Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Resources/Shaders/g_buffer.frag
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ void main()

if (material.AlbedoMap < INVALID_MAP_HANDLE)
{
uint texId = uint(material.AlbedoMap);
uint texId = material.AlbedoMap;
vec4 albedoSample = texture(sampler2D(TextureArray[nonuniformEXT(texId)], LinearWrapSampler), TexCoord);
albedo = albedoSample.rgb;
alpha = albedoSample.a;
Expand All @@ -37,15 +37,15 @@ void main()

if (material.NormalMap < INVALID_MAP_HANDLE)
{
uint texId = uint(material.NormalMap);
uint texId = material.NormalMap;
vec3 normalSample = texture(sampler2D(TextureArray[nonuniformEXT(texId)], LinearWrapSampler), TexCoord).rgb;
normal = perturbNormal(normalize(WorldNormal), WorldPos, normalSample, TexCoord);
}

if (material.SpecularMap < INVALID_MAP_HANDLE)
{
// glTF metallicRoughnessTexture: R=occlusion, G=roughness, B=metallic
uint texId = uint(material.SpecularMap);
uint texId = material.SpecularMap;
vec3 orm = texture(sampler2D(TextureArray[nonuniformEXT(texId)], LinearWrapSampler), TexCoord).rgb;
ao = orm.r;
roughness = orm.g;
Expand All @@ -54,7 +54,7 @@ void main()

if (material.EmissiveMap < INVALID_MAP_HANDLE)
{
uint texId = uint(material.EmissiveMap);
uint texId = material.EmissiveMap;
emissive = texture(sampler2D(TextureArray[nonuniformEXT(texId)], LinearWrapSampler), TexCoord).r;
}

Expand Down
1 change: 0 additions & 1 deletion Resources/Shaders/material.glsl
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Material bindings and accessor — fragment-stage only.
// Declares set=1 (TextureArray, LinearWrapSampler) and MatSB.
// Does NOT pull in geometry buffers so no duplicate-binding conflicts with vertex stage.
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : enable

#include "material_types.glsl"
#include "texture_bindings.glsl"
Expand Down
28 changes: 13 additions & 15 deletions Resources/Shaders/material_types.glsl
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
// Material data structure.
// No descriptor bindings — include wherever MaterialData is needed.
// Note: MaterialData uses uint64_t; include material.glsl (not this file directly)
// in shader stages that need the full material pipeline — it enables Int64 there.

// Texture map fields use uint (32-bit bindless indices) — no shaderInt64 required.
struct MaterialData
{
vec4 Ambient;
vec4 Emissive;
vec4 Albedo;
vec4 Specular;
vec4 Roughness;
vec4 Factors; // {x : transparency, y : Metallic, z : AlphaTest, w : _padding}
vec4 Ambient;
vec4 Emissive;
vec4 Albedo;
vec4 Specular;
vec4 Roughness;
vec4 Factors; // {x : transparency, y : Metallic, z : AlphaTest, w : _padding}

uint64_t EmissiveMap;
uint64_t AlbedoMap;
uint64_t SpecularMap;
uint64_t NormalMap;
uint64_t OpacityMap;
uint64_t _padding;
uint EmissiveMap;
uint AlbedoMap;
uint SpecularMap;
uint NormalMap;
uint OpacityMap;
uint _pad;
};
126 changes: 86 additions & 40 deletions Tetragrama/Panels/ViewportPanel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -265,52 +265,98 @@ namespace Tetragrama::Panels
if (!ZEngine::Importers::AssetCodec::ReadAssetMeshFileHeader(native_path, header))
return;

// The registry's auto-ingest path is dead code (see #755) — ingest
// explicitly instead, matching every other real consumer. Idempotent.
{
auto scratch = ZGetScratch(&ctx->AssetArena);
ZEngine::Importers::AssetMesh mesh_data{};
ZEngine::Importers::AssetNodeHierarchy hier_data{};
ZEngine::Importers::AssetCodec::DeserializeMeshAssetFile(scratch.Arena, native_path, mesh_data, hier_data);

// IngestMesh only loads geometry/hierarchy — ingest each submesh's material
// (and, transitively, its textures) first, before mesh_data is moved below.
for (uint32_t i = 0; i < mesh_data.SubMeshes.size(); ++i)
{
const auto& mat_uuid = mesh_data.SubMeshes[i].MaterialUUID;
if (!mat_uuid.is_nil())
ZEngine::Managers::AssetManager::IngestMaterialFromUUID(scratch.Arena, mat_uuid);
}
std::string mesh_path = native_path;
std::string drop_path = m_pending_mesh_drop;
auto* layer_ptr = m_layer;

ZEngine::Managers::AssetManager::IngestMesh(std::move(mesh_data), std::move(hier_data));
ZReleaseScratch(scratch);
}
// Deserialize + material ingest on a worker thread — both are synchronous file
// reads that block the render loop. A dedicated arena (4× file size + 8 MB)
// outlives the lambda; the main-thread callback owns and shuts it down after use.

char iname[256] = {};
auto pr = VFSPath::Parse(m_pending_mesh_drop);
if (pr.Succeeded())
uint64_t file_bytes = 0;
if (FILE* f = fopen(mesh_path.c_str(), "rb"))
{
auto s = pr.Value().Stem();
snprintf(iname, sizeof(iname), "%.*s", (int) s.Length, s.Data);
fseek(f, 0, SEEK_END);
file_bytes = static_cast<uint64_t>(ftell(f));
fclose(f);
}

uint32_t render_id = scene->AddMeshInstance(header.Id, iname);

using namespace ZEngine::ECS::Components;
ZEngine::ECS::ActorHandle handle = ctx->ActorManager->Create();
ZEngine::ECS::Actor* actor = ctx->ActorManager->Access(handle);
if (actor)
struct MeshPayload
{
NameComponent nc = {};
secure_strncpy(nc.Value, sizeof(nc.Value), iname, secure_strlen(iname));
actor->AddComponent<NameComponent>(nc);
actor->AddComponent<TransformComponent>({});

MeshComponent mc = {};
mc.MeshUUID = header.Id;
mc.RenderInstanceId = render_id;
actor->AddComponent<MeshComponent>(mc);
}
ZEngine::Core::Memory::ArenaAllocator* Arena = nullptr;
ZEngine::Importers::AssetMesh Mesh = {};
ZEngine::Importers::AssetNodeHierarchy Hierarchy = {};
uuids::uuid MeshId = {};
std::string DropPath = {};
void* Layer = nullptr;
};

auto* payload = new MeshPayload();
payload->Arena = new ZEngine::Core::Memory::ArenaAllocator{};
payload->Arena->Initialize(file_bytes * 4 + (8u << 20), 0);
payload->MeshId = header.Id;
payload->DropPath = drop_path;
payload->Layer = layer_ptr;

ZEngine::Helpers::ThreadPoolHelper::Submit([payload, mesh_path]() mutable {
ZEngine::Importers::AssetCodec::DeserializeMeshAssetFile(payload->Arena, mesh_path.c_str(), payload->Mesh, payload->Hierarchy);

// IngestMaterialFromUUID is also file I/O — keep it on the worker.
auto* ctx = ZEngine::Engine::GetContext();
if (ctx)
{
auto scratch = ZGetScratch(&ctx->AssetArena);
for (uint32_t i = 0; i < payload->Mesh.SubMeshes.size(); ++i)
{
const auto& mat_uuid = payload->Mesh.SubMeshes[i].MaterialUUID;
if (!mat_uuid.is_nil())
ZEngine::Managers::AssetManager::IngestMaterialFromUUID(scratch.Arena, mat_uuid);
}
ZReleaseScratch(scratch);
}

ZEngine::Core::MainThreadScheduler::Post(payload, [](void* raw) {
auto* p = static_cast<MeshPayload*>(raw);
auto* ctx = ZEngine::Engine::GetContext();
auto* app = p->Layer ? reinterpret_cast<EditorPtr>(reinterpret_cast<Tetragrama::Layers::ZUILayer*>(p->Layer)->CurrentApp) : nullptr;
auto* scene = app ? reinterpret_cast<EditorScenePtr>(app->CurrentScene) : nullptr;

if (ctx && scene && ctx->ActorManager)
{
ZEngine::Managers::AssetManager::IngestMesh(std::move(p->Mesh), std::move(p->Hierarchy));

char iname[256] = {};
auto pr = VFSPath::Parse(p->DropPath.c_str());
if (pr.Succeeded())
{
auto s = pr.Value().Stem();
snprintf(iname, sizeof(iname), "%.*s", (int) s.Length, s.Data);
}

uint32_t render_id = scene->AddMeshInstance(p->MeshId, iname);

using namespace ZEngine::ECS::Components;
ZEngine::ECS::ActorHandle handle = ctx->ActorManager->Create();
ZEngine::ECS::Actor* actor = ctx->ActorManager->Access(handle);
if (actor)
{
NameComponent nc = {};
secure_strncpy(nc.Value, sizeof(nc.Value), iname, secure_strlen(iname));
actor->AddComponent<NameComponent>(nc);
actor->AddComponent<TransformComponent>({});

MeshComponent mc = {};
mc.MeshUUID = p->MeshId;
mc.RenderInstanceId = render_id;
actor->AddComponent<MeshComponent>(mc);
}
}

p->Arena->Shutdown();
delete p->Arena;
delete p;
});
});
}

// OpenDroppedScene (main-thread only)
Expand Down
15 changes: 8 additions & 7 deletions ZEngine/ZEngine/Applications/AppRenderPipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,15 +145,16 @@ namespace ZEngine::Applications
void AppRenderPipeline::EndFrame()
{
if (Device->RRM)
{
auto* rrm = static_cast<Rendering::RenderResourceManager*>(Device->RRM);
rrm->EndFrame();
rrm->SubmitAsyncUploads();
}
static_cast<Rendering::RenderResourceManager*>(Device->RRM)->EndFrame();

Device->CommandBufferMgr->EnqueueBuffer(CurrentCmdBuf);
Device->CommandBufferMgr->EndEnqueuedBuffers();

// Present before SubmitAsyncUploads: texture upload ops go into the deferred
// queues and are waited on by the next frame's submit_1, not the current one.
Device->SwapchainPtr->Present();
if (Device->RRM)
static_cast<Rendering::RenderResourceManager*>(Device->RRM)->SubmitAsyncUploads();
}

void AppRenderPipeline::RenderScene(Rendering::Cameras::CameraPtr camera, Rendering::Scenes::RenderScenePtr scene)
Expand Down Expand Up @@ -239,8 +240,8 @@ namespace ZEngine::Applications
for (uint32_t sub_i = 0; sub_i < static_cast<uint32_t>(mesh->SubMeshes.size()); ++sub_i)
{
const auto& sub = mesh->SubMeshes[sub_i];
auto* mat = Managers::AssetManager::GetAsset<Importers::AssetMaterial>(sub.MaterialUUID);
uint32_t mat_idx = mat ? static_cast<uint32_t>(mat - mgr->Materials.data()) : 0;
uint32_t* mat_slot = mgr ? mgr->UUIDToMaterialSlot.find(sub.MaterialUUID) : nullptr;
uint32_t mat_idx = mat_slot ? *mat_slot : 0;
uint32_t draw_idx = static_cast<uint32_t>(allocs.size());

Rendering::Meshes::SubMeshAllocation alloc = {};
Expand Down
2 changes: 1 addition & 1 deletion ZEngine/ZEngine/Hardwares/AsyncUploadQueue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ namespace ZEngine::Hardwares
// real failure, and the device may already be unsafe to keep calling into.
if (!m_device->QueueSubmit(job.Buffer, job.Timeline, job.WaitFlag, job.SignalValue, job.WaitValue, job.WaitTimeline))
break;
m_device->EnqueueAsyncGPUOperation({job.WaitFlag, job.SignalValue, job.Timeline});
m_device->EnqueueDeferredAsyncGPUOperation({job.WaitFlag, job.SignalValue, job.Timeline});
}
}

Expand Down
3 changes: 2 additions & 1 deletion ZEngine/ZEngine/Hardwares/AsyncUploadQueue.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once
#include <ZEngine/Core/Containers/SPSCQueue.h>
#include <vulkan/vulkan.h>
#include <cstdint>

namespace ZEngine::Rendering::Primitives
Expand All @@ -18,7 +19,7 @@ namespace ZEngine::Hardwares
CommandBuffer* Buffer = nullptr;
Rendering::Primitives::Semaphore* Timeline = nullptr;
Rendering::Primitives::Semaphore* WaitTimeline = nullptr;
uint32_t WaitFlag = 0;
VkPipelineStageFlags2 WaitFlag = 0;
uint64_t SignalValue = 0;
uint64_t WaitValue = UINT64_MAX;
};
Expand Down
Loading
Loading