From cbcaf23df18af88f6e48dad830e8fb8dfdb6092e Mon Sep 17 00:00:00 2001 From: hydrogendeuteride Date: Sat, 22 Nov 2025 22:33:19 +0900 Subject: [PATCH] object creation&deletion completed(RT error) --- src/core/asset_manager.cpp | 28 +++++++++--- src/core/vk_raytracing.cpp | 93 ++++++++++++++++++++------------------ src/core/vk_raytracing.h | 5 +- src/render/rg_graph.cpp | 19 ++++++-- src/scene/vk_loader.cpp | 18 ++++++-- 5 files changed, 101 insertions(+), 62 deletions(-) diff --git a/src/core/asset_manager.cpp b/src/core/asset_manager.cpp index 44a76a6..ac177f6 100644 --- a/src/core/asset_manager.cpp +++ b/src/core/asset_manager.cpp @@ -15,6 +15,7 @@ #include #include #include +#include using std::filesystem::path; @@ -83,11 +84,28 @@ std::optional > AssetManager::loadGLTF(std::string_v if (auto it = _gltfCacheByPath.find(key); it != _gltfCacheByPath.end()) { - if (auto sp = it->second.lock()) return sp; + if (auto sp = it->second.lock()) + { + fmt::println("[AssetManager] loadGLTF cache hit key='{}' path='{}' ptr={}", key, resolved, + static_cast(sp.get())); + return sp; + } + fmt::println("[AssetManager] loadGLTF cache expired key='{}' path='{}' (reloading)", key, resolved); } auto loaded = loadGltf(_engine, resolved); if (!loaded.has_value()) return {}; + + if (loaded.value()) + { + fmt::println("[AssetManager] loadGLTF loaded new scene key='{}' path='{}' ptr={}", key, resolved, + static_cast(loaded.value().get())); + } + else + { + fmt::println("[AssetManager] loadGLTF got empty scene for key='{}' path='{}'", key, resolved); + } + _gltfCacheByPath[key] = loaded.value(); return loaded; } @@ -519,11 +537,9 @@ std::shared_ptr AssetManager::createMesh(const std::string &name, auto mesh = std::make_shared(); mesh->name = name; mesh->meshBuffers = _engine->_resourceManager->uploadMesh(indices, vertices); - // Build BLAS for the mesh if ray tracing manager is available - if (_engine->_rayManager) - { - _engine->_rayManager->getOrBuildBLAS(mesh); - } + // BLAS for this mesh is built lazily when TLAS is constructed from the draw + // context (RayTracingManager::buildTLASFromDrawContext). This keeps RT work + // centralized and avoids redundant builds on load. GeoSurface surf{}; surf.startIndex = 0; diff --git a/src/core/vk_raytracing.cpp b/src/core/vk_raytracing.cpp index c8fcc2a..e747ce2 100644 --- a/src/core/vk_raytracing.cpp +++ b/src/core/vk_raytracing.cpp @@ -5,6 +5,7 @@ #include "scene/vk_loader.h" #include "scene/vk_scene.h" #include +#include void RayTracingManager::init(DeviceManager *dev, ResourceManager *res) { @@ -51,24 +52,28 @@ void RayTracingManager::cleanup() _tlasInstanceBuffer = {}; _tlasInstanceCapacity = 0; } - for (auto &kv: _blasByVB) + + // Destroy any remaining cached BLAS that weren't queued for deferred destroy. + for (auto &kv : _blasByMesh) { - if (kv.second.handle) + const AccelStructureHandle &as = kv.second; + if (as.handle) { - _vkDestroyAccelerationStructureKHR(dv, kv.second.handle, nullptr); + _vkDestroyAccelerationStructureKHR(dv, as.handle, nullptr); } - if (kv.second.storage.buffer) + if (as.storage.buffer) { - _resources->destroy_buffer(kv.second.storage); + _resources->destroy_buffer(as.storage); } } - _blasByVB.clear(); _blasByMesh.clear(); } void RayTracingManager::flushPendingDeletes() { if (_pendingBlasDestroy.empty()) return; + + fmt::println("[RT] flushPendingDeletes: destroying {} BLAS handles", _pendingBlasDestroy.size()); VkDevice dv = _device->device(); for (auto &as : _pendingBlasDestroy) { @@ -94,13 +99,10 @@ static VkDeviceAddress get_buffer_address(VkDevice dev, VkBuffer buf) AccelStructureHandle RayTracingManager::getOrBuildBLAS(const std::shared_ptr &mesh) { if (!mesh) return {}; - VkBuffer vb = mesh->meshBuffers.vertexBuffer.buffer; - if (auto it = _blasByVB.find(vb); it != _blasByVB.end()) - { - return it->second; - } if (auto it = _blasByMesh.find(mesh.get()); it != _blasByMesh.end()) { + fmt::println("[RT] getOrBuildBLAS reuse by mesh mesh='{}' handle={}", mesh->name, + static_cast(it->second.handle)); return it->second; } @@ -113,6 +115,10 @@ AccelStructureHandle RayTracingManager::getOrBuildBLAS(const std::shared_ptrmeshBuffers.vertexBufferAddress; VkDeviceAddress iaddr = mesh->meshBuffers.indexBufferAddress; const uint32_t vcount = mesh->meshBuffers.vertexCount; + VkBuffer vb = mesh->meshBuffers.vertexBuffer.buffer; + + fmt::println("[RT] getOrBuildBLAS build mesh='{}' surfaces={} vcount={}", mesh->name, + mesh->surfaces.size(), vcount); for (const auto &s: mesh->surfaces) { @@ -199,6 +205,12 @@ AccelStructureHandle RayTracingManager::getOrBuildBLAS(const std::shared_ptrimmediate_submit([&](VkCommandBuffer cmd) { // ppBuildRangeInfos is an array of infoCount pointers; we have 1 build info + fmt::println("[RT] building BLAS for mesh='{}' geoms={} primsTotal={} storageSize={} scratchSize={}", + mesh->name, + geoms.size(), + maxPrim.empty() ? 0u : std::accumulate(maxPrim.begin(), maxPrim.end(), 0u), + sizes.accelerationStructureSize, + sizes.buildScratchSize); _vkCmdBuildAccelerationStructuresKHR(cmd, 1, &buildInfo, &pRange); }); @@ -210,7 +222,6 @@ AccelStructureHandle RayTracingManager::getOrBuildBLAS(const std::shared_ptrdevice(), &dai); - _blasByVB.emplace(vb, blas); _blasByMesh.emplace(mesh.get(), blas); return blas; } @@ -222,6 +233,10 @@ void RayTracingManager::ensure_tlas_storage(VkDeviceSize requiredASSize, VkDevic if (_tlas.handle || _tlas.storage.buffer) { AccelStructureHandle old = _tlas; + fmt::println("[RT] ensure_tlas_storage: scheduling old TLAS destroy handle={} buffer={} size={}", + static_cast(old.handle), + static_cast(old.storage.buffer), + old.storage.info.size); dq.push_function([this, old]() { if (old.handle) _vkDestroyAccelerationStructureKHR(_device->device(), old.handle, nullptr); @@ -240,6 +255,11 @@ void RayTracingManager::ensure_tlas_storage(VkDeviceSize requiredASSize, VkDevic asci.buffer = _tlas.storage.buffer; asci.size = requiredASSize; VK_CHECK(_vkCreateAccelerationStructureKHR(_device->device(), &asci, nullptr, &_tlas.handle)); + + fmt::println("[RT] ensure_tlas_storage: created TLAS handle={} buffer={} size={}", + static_cast(_tlas.handle), + static_cast(_tlas.storage.buffer), + requiredASSize); } VkAccelerationStructureKHR RayTracingManager::buildTLASFromDrawContext(const DrawContext &dc, DeletionQueue& dq) @@ -248,16 +268,17 @@ VkAccelerationStructureKHR RayTracingManager::buildTLASFromDrawContext(const Dra std::vector instances; instances.reserve(dc.OpaqueSurfaces.size()); + fmt::println("[RT] buildTLASFromDrawContext: opaqueSurfaces={} current TLAS handle={} buffer={}", + dc.OpaqueSurfaces.size(), + static_cast(_tlas.handle), + static_cast(_tlas.storage.buffer)); + for (const auto &r: dc.OpaqueSurfaces) { - // Find mesh BLAS by vertex buffer, then by mesh pointer (if available). + // Find or lazily build BLAS by mesh pointer. We require sourceMesh + // for ray tracing; objects without it are skipped from TLAS. AccelStructureHandle blas{}; - auto it = _blasByVB.find(r.vertexBuffer); - if (it != _blasByVB.end()) - { - blas = it->second; - } - else if (r.sourceMesh) + if (r.sourceMesh) { auto itMesh = _blasByMesh.find(r.sourceMesh); if (itMesh != _blasByMesh.end()) @@ -392,50 +413,32 @@ VkAccelerationStructureKHR RayTracingManager::buildTLASFromDrawContext(const Dra void RayTracingManager::removeBLASForBuffer(VkBuffer vertexBuffer) { if (!vertexBuffer) return; - VkDevice dv = _device->device(); - auto it = _blasByVB.find(vertexBuffer); - if (it == _blasByVB.end()) return; - // Defer destruction until after the next fence wait to avoid racing in-flight traces. - _pendingBlasDestroy.push_back(it->second); - - // Also erase corresponding mesh-keyed entry if present - for (auto mit = _blasByMesh.begin(); mit != _blasByMesh.end(); ) + // Find any mesh whose vertex buffer matches and evict its BLAS. + for (auto it = _blasByMesh.begin(); it != _blasByMesh.end(); ) { - if (mit->second.handle == it->second.handle) + const MeshAsset *mesh = it->first; + if (mesh && mesh->meshBuffers.vertexBuffer.buffer == vertexBuffer) { - mit = _blasByMesh.erase(mit); + // Defer destruction until after the next fence wait to avoid racing in-flight traces. + _pendingBlasDestroy.push_back(it->second); + it = _blasByMesh.erase(it); } else { - ++mit; + ++it; } } - _blasByVB.erase(it); } void RayTracingManager::removeBLASForMesh(const MeshAsset *mesh) { if (!mesh) return; - VkDevice dv = _device->device(); auto it = _blasByMesh.find(mesh); if (it == _blasByMesh.end()) return; // Defer destruction until after the next fence wait to avoid racing in-flight traces. _pendingBlasDestroy.push_back(it->second); - // Remove any VB-keyed entries that point to the same BLAS - for (auto vbit = _blasByVB.begin(); vbit != _blasByVB.end(); ) - { - if (vbit->second.handle == it->second.handle) - { - vbit = _blasByVB.erase(vbit); - } - else - { - ++vbit; - } - } - _blasByMesh.erase(it); } diff --git a/src/core/vk_raytracing.h b/src/core/vk_raytracing.h index 776f82f..4c96fc4 100644 --- a/src/core/vk_raytracing.h +++ b/src/core/vk_raytracing.h @@ -51,8 +51,9 @@ private: DeviceManager* _device{nullptr}; ResourceManager* _resources{nullptr}; - // BLAS cache by vertex buffer handle (legacy) and by mesh pointer (preferred) - std::unordered_map _blasByVB; + // BLAS cache per mesh. BLAS lifetime is tied to MeshAsset lifetime; + // when a mesh is destroyed or its GPU buffers are freed, the owning code + // must call removeBLASForMesh/removeBLASForBuffer to drop the cached BLAS. std::unordered_map _blasByMesh; // TLAS + scratch / instance buffer (rebuilt per frame) diff --git a/src/render/rg_graph.cpp b/src/render/rg_graph.cpp index 22a1a72..62f89e1 100644 --- a/src/render/rg_graph.cpp +++ b/src/render/rg_graph.cpp @@ -565,9 +565,9 @@ bool RenderGraph::compile() ? prev.access : _resources.initial_access(RGBufferHandle{id}); - bool needBarrier = !prev.initialized - || prev.stage != desired.stage - || prev.access != desired.access; + bool needBarrier = !prev.initialized + || prev.stage != desired.stage + || prev.access != desired.access; if (needBarrier) { @@ -582,8 +582,17 @@ bool RenderGraph::compile() const RGBufferRecord *rec = _resources.get_buffer(RGBufferHandle{id}); barrier.buffer = rec ? rec->buffer : VK_NULL_HANDLE; barrier.offset = 0; - // If size is unknown or 0 for imported buffers, use WHOLE_SIZE to satisfy VUID 01188 - barrier.size = (rec && rec->size > 0) ? rec->size : VK_WHOLE_SIZE; + // For imported buffers we don't always know the exact VkBuffer size, so use WHOLE_SIZE + // to avoid violating VUID-VkBufferMemoryBarrier2-size-01189. For transient buffers + // created by the graph, we track the exact size. + if (rec && !rec->imported && rec->size > 0) + { + barrier.size = rec->size; + } + else + { + barrier.size = VK_WHOLE_SIZE; + } pass.preBufferBarriers.push_back(barrier); if (rec && !rec->imported) diff --git a/src/scene/vk_loader.cpp b/src/scene/vk_loader.cpp index b9eac3b..210ee67 100644 --- a/src/scene/vk_loader.cpp +++ b/src/scene/vk_loader.cpp @@ -218,6 +218,12 @@ std::optional > loadGltf(VulkanEngine *engine, std:: //< load_1 //> load_2 // we can stimate the descriptors we will need accurately + fmt::println("[GLTF] loadGltf: materials={} meshes={} images={} samplers={} (creating descriptor pool)", + gltf.materials.size(), + gltf.meshes.size(), + gltf.images.size(), + gltf.samplers.size()); + std::vector sizes = { {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 3}, {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 3}, @@ -225,6 +231,10 @@ std::optional > loadGltf(VulkanEngine *engine, std:: }; file.descriptorPool.init(engine->_deviceManager->device(), gltf.materials.size(), sizes); + + fmt::println("[GLTF] loadGltf: descriptor pool initialized for '{}' (materials={})", + filePath, + gltf.materials.size()); //< load_2 //> load_samplers @@ -613,6 +623,10 @@ std::optional > loadGltf(VulkanEngine *engine, std:: } newmesh->meshBuffers = engine->_resourceManager->uploadMesh(indices, vertices); + // BLAS for this mesh will be built lazily from RayTracingManager::buildTLASFromDrawContext() + // when ray-traced shadows are enabled. This avoids redundant builds and concentrates + // RT work in one place. + // If CPU vectors ballooned for this mesh, release capacity back to the OS auto shrink_if_huge = [](auto &vec, size_t elemSizeBytes) { const size_t capBytes = vec.capacity() * elemSizeBytes; @@ -626,10 +640,6 @@ std::optional > loadGltf(VulkanEngine *engine, std:: }; shrink_if_huge(indices, sizeof(uint32_t)); shrink_if_huge(vertices, sizeof(Vertex)); - if (engine->_rayManager) - { - engine->_rayManager->getOrBuildBLAS(newmesh); - } } //> load_nodes // load all nodes and their meshes