object creation&deletion completed(RT error)
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
#include <fastgltf/parser.hpp>
|
||||
#include <fastgltf/util.hpp>
|
||||
#include <fastgltf/tools.hpp>
|
||||
#include <fmt/core.h>
|
||||
|
||||
using std::filesystem::path;
|
||||
|
||||
@@ -83,11 +84,28 @@ std::optional<std::shared_ptr<LoadedGLTF> > 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<const void *>(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<const void *>(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<MeshAsset> AssetManager::createMesh(const std::string &name,
|
||||
auto mesh = std::make_shared<MeshAsset>();
|
||||
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;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "scene/vk_loader.h"
|
||||
#include "scene/vk_scene.h"
|
||||
#include <cstring>
|
||||
#include <numeric>
|
||||
|
||||
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<MeshAsset> &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<const void *>(it->second.handle));
|
||||
return it->second;
|
||||
}
|
||||
|
||||
@@ -113,6 +115,10 @@ AccelStructureHandle RayTracingManager::getOrBuildBLAS(const std::shared_ptr<Mes
|
||||
VkDeviceAddress vaddr = mesh->meshBuffers.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_ptr<Mes
|
||||
const VkAccelerationStructureBuildRangeInfoKHR* pRange = ranges.data();
|
||||
_resources->immediate_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_ptr<Mes
|
||||
dai.accelerationStructure = blas.handle;
|
||||
blas.deviceAddress = _vkGetAccelerationStructureDeviceAddressKHR(_device->device(), &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<const void *>(old.handle),
|
||||
static_cast<const void *>(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<const void *>(_tlas.handle),
|
||||
static_cast<const void *>(_tlas.storage.buffer),
|
||||
requiredASSize);
|
||||
}
|
||||
|
||||
VkAccelerationStructureKHR RayTracingManager::buildTLASFromDrawContext(const DrawContext &dc, DeletionQueue& dq)
|
||||
@@ -248,16 +268,17 @@ VkAccelerationStructureKHR RayTracingManager::buildTLASFromDrawContext(const Dra
|
||||
std::vector<VkAccelerationStructureInstanceKHR> instances;
|
||||
instances.reserve(dc.OpaqueSurfaces.size());
|
||||
|
||||
fmt::println("[RT] buildTLASFromDrawContext: opaqueSurfaces={} current TLAS handle={} buffer={}",
|
||||
dc.OpaqueSurfaces.size(),
|
||||
static_cast<const void *>(_tlas.handle),
|
||||
static_cast<const void *>(_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);
|
||||
}
|
||||
|
||||
@@ -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<VkBuffer, AccelStructureHandle> _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<const MeshAsset*, AccelStructureHandle> _blasByMesh;
|
||||
|
||||
// TLAS + scratch / instance buffer (rebuilt per frame)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -218,6 +218,12 @@ std::optional<std::shared_ptr<LoadedGLTF> > 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<DescriptorAllocatorGrowable::PoolSizeRatio> sizes = {
|
||||
{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 3},
|
||||
{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 3},
|
||||
@@ -225,6 +231,10 @@ std::optional<std::shared_ptr<LoadedGLTF> > 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<std::shared_ptr<LoadedGLTF> > 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<std::shared_ptr<LoadedGLTF> > 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
|
||||
|
||||
Reference in New Issue
Block a user