From eb504cf6e2f8ca8dc760ba829334d4f0029cae3f Mon Sep 17 00:00:00 2001 From: Cam Mannett Date: Sat, 14 Sep 2024 16:26:18 +0100 Subject: [PATCH] Vulkan AS rebuild-on-replay: Copy AS input buffers on build When vkCmdBuildAccelerationStructuresKHR is called, the input buffers used for the build are copied into host-accessible memory, so when initial state serialisation is triggers it can be copied into the capture file. To unify the behaviour of the different geometry types, there is only one readback buffer per AS, and in the case of triangle data it can contain the vertex, index, and transform data concatenated into a single contiguous block. The buffer data along with build arguments are stored in a ref-counted VkAccelerationStructureInfo object held by the AS's VkResourceRecord. Once the info object is destroyed, then the copied buffer mem is also freed. Serialisation of the initial state will follow in later patches. Caveats: * vkCmdBuildAccelerationStructuresIndirectKHR is not handled * VkAccelerationStructureGeometryInstancesDataKHR::arrayOfPointers mode is not handled --- .../vulkan/vk_acceleration_structure.cpp | 532 ++++++++++++++++++ .../driver/vulkan/vk_acceleration_structure.h | 82 ++- renderdoc/driver/vulkan/vk_core.h | 2 + renderdoc/driver/vulkan/vk_memory.cpp | 5 + renderdoc/driver/vulkan/vk_resources.cpp | 4 +- .../driver/vulkan/wrappers/vk_cmd_funcs.cpp | 3 + .../driver/vulkan/wrappers/vk_get_funcs.cpp | 3 +- .../vulkan/wrappers/vk_resource_funcs.cpp | 1 - 8 files changed, 626 insertions(+), 6 deletions(-) diff --git a/renderdoc/driver/vulkan/vk_acceleration_structure.cpp b/renderdoc/driver/vulkan/vk_acceleration_structure.cpp index 71f68d51dd..6085dab6fe 100644 --- a/renderdoc/driver/vulkan/vk_acceleration_structure.cpp +++ b/renderdoc/driver/vulkan/vk_acceleration_structure.cpp @@ -25,6 +25,7 @@ #include "vk_acceleration_structure.h" #include "core/settings.h" #include "vk_core.h" +#include "vk_manager.h" RDOC_EXTERN_CONFIG(bool, Vulkan_Debug_SingleSubmitFlushing); @@ -37,6 +38,34 @@ constexpr VkDeviceSize handleCountSize = 8; // Spec says VkCopyAccelerationStructureToMemoryInfoKHR::dst::deviceAddress must be 256 bytes aligned constexpr VkDeviceSize asBufferAlignment = 256; + +VkDeviceSize IndexTypeSize(VkIndexType type) +{ + switch(type) + { + case VK_INDEX_TYPE_UINT32: return 4; + case VK_INDEX_TYPE_UINT16: return 2; + case VK_INDEX_TYPE_UINT8_KHR: return 1; + default: return 0; + } +} +} + +VkAccelerationStructureInfo::~VkAccelerationStructureInfo() +{ + for(const GeometryData &geoData : geometryData) + { + if(geoData.readbackMem != VK_NULL_HANDLE) + ObjDisp(device)->FreeMemory(Unwrap(device), geoData.readbackMem, NULL); + } +} + +void VkAccelerationStructureInfo::Release() +{ + const int32_t ref = Atomic::Dec32(&refCount); + RDCASSERT(ref >= 0); + if(ref <= 0) + delete this; } VulkanAccelerationStructureManager::VulkanAccelerationStructureManager(WrappedVulkan *driver) @@ -44,6 +73,403 @@ VulkanAccelerationStructureManager::VulkanAccelerationStructureManager(WrappedVu { } +void VulkanAccelerationStructureManager::CopyInputBuffers( + VkCommandBuffer commandBuffer, const VkAccelerationStructureBuildGeometryInfoKHR &info, + const VkAccelerationStructureBuildRangeInfoKHR *buildRange, CaptureState state) +{ + VkResourceRecord *cmdRecord = GetRecord(commandBuffer); + VkDevice device = cmdRecord->cmdInfo->device; + + VkResourceRecord *asRecord = GetRecord(info.dstAccelerationStructure); + RDCASSERT(asRecord); + if(!asRecord) + return; + + // If this is an update then replace the existing and safely delete it + VkAccelerationStructureInfo *metadata = asRecord->accelerationStructureInfo; + if(!metadata->geometryData.empty()) + { + DeletePreviousInfo(commandBuffer, metadata, state); + metadata = asRecord->accelerationStructureInfo = new VkAccelerationStructureInfo(); + } + + metadata->device = device; + metadata->type = info.type; + metadata->flags = info.flags; + + metadata->geometryData.reserve(info.geometryCount); + + struct BufferData + { + BufferData() = default; + BufferData(RecordAndOffset r) : rao(r) + { + if(rao.record != NULL) + buf = ToUnwrappedHandle(rao.record->Resource); + } + + operator bool() const { return buf != VK_NULL_HANDLE; } + bool operator!() const { return buf == VK_NULL_HANDLE; } + + VkBufferMemoryBarrier Barrier() const + { + return { + VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, + NULL, + 0, + 0, + VK_QUEUE_FAMILY_IGNORED, + VK_QUEUE_FAMILY_IGNORED, + buf, + GetReadPosition(), + size, + }; + } + + void SetReadPosition(VkDeviceSize startFrom) { start = startFrom; } + VkDeviceSize GetReadPosition() const { return rao.offset + start; } + + RecordAndOffset rao; + VkBuffer buf = VK_NULL_HANDLE; + + VkDeviceSize alignment = 0; + VkDeviceSize size = 0; + + private: + VkDeviceSize start = 0; + }; + + for(uint32_t i = 0; i < info.geometryCount; ++i) + { + // Work out the buffer size needed for each geometry type + const VkAccelerationStructureGeometryKHR &geometry = + info.pGeometries != NULL ? info.pGeometries[i] : *(info.ppGeometries[i]); + const VkAccelerationStructureBuildRangeInfoKHR &rangeInfo = buildRange[i]; + + Allocation readbackmem; + rdcarray barriers; + barriers.reserve(3); + + switch(geometry.geometryType) + { + case VK_GEOMETRY_TYPE_TRIANGLES_KHR: + { + const VkAccelerationStructureGeometryTrianglesDataKHR &triInfo = geometry.geometry.triangles; + + // Find the associated VkBuffers + BufferData vertexData = GetDeviceAddressData(triInfo.vertexData.deviceAddress); + if(!vertexData) + { + RDCERR("Unable to find VkBuffer for vertex data at 0x%llx", + triInfo.vertexData.deviceAddress); + continue; + } + + BufferData indexData; + if(triInfo.indexType != VK_INDEX_TYPE_NONE_KHR) + { + indexData = GetDeviceAddressData(triInfo.indexData.deviceAddress); + if(!indexData) + { + RDCERR("Unable to find VkBuffer for index data at 0x%llx", + triInfo.indexData.deviceAddress); + continue; + } + } + + BufferData transformData; + if(triInfo.transformData.deviceAddress != 0x0) + { + transformData = GetDeviceAddressData(triInfo.transformData.deviceAddress); + if(!transformData) + { + RDCERR("Unable to find VkBuffer for transform data at 0x%llx", + triInfo.transformData.deviceAddress); + continue; + } + } + + // Find the alignment requirements for each type + { + VkMemoryRequirements mrq = {}; + + // Vertex buffer. The complexity here is that the rangeInfo members are interpreted + // differently depending on whether or not index buffers are used + ObjDisp(device)->GetBufferMemoryRequirements(Unwrap(device), vertexData.buf, &mrq); + vertexData.alignment = mrq.alignment; + + if(indexData) + { + // If we're using an index buffer, we don't know the maximum vertex used without + // reading the buffer itself. As that's a heavy operation we'll just take up the + // maxVertex value instead + vertexData.size = RDCMIN(vertexData.rao.record->memSize - vertexData.rao.offset, + triInfo.maxVertex * triInfo.vertexStride); + vertexData.SetReadPosition(0); + } + else + { + vertexData.size = rangeInfo.primitiveCount * 3 * triInfo.vertexStride; + vertexData.SetReadPosition(rangeInfo.primitiveOffset + + (triInfo.vertexStride * rangeInfo.firstVertex)); + } + + // Index buffer + if(indexData) + { + ObjDisp(device)->GetBufferMemoryRequirements(Unwrap(device), indexData.buf, &mrq); + indexData.alignment = mrq.alignment; + indexData.size = rangeInfo.primitiveCount * 3 * IndexTypeSize(triInfo.indexType); + indexData.SetReadPosition(rangeInfo.primitiveOffset); + } + + // Transform buffer + if(transformData) + { + ObjDisp(device)->GetBufferMemoryRequirements(Unwrap(device), transformData.buf, &mrq); + transformData.alignment = mrq.alignment; + transformData.size = sizeof(VkTransformMatrixKHR); + transformData.SetReadPosition(rangeInfo.transformOffset); + } + } + const VkDeviceSize maxAlignment = + RDCMAX(RDCMAX(vertexData.alignment, indexData.alignment), transformData.alignment); + + // We want to copy the input buffers into one big block so sum the sizes up together + const VkDeviceSize totalMemSize = AlignUp(vertexData.size, vertexData.alignment) + + AlignUp(indexData.size, indexData.alignment) + + AlignUp(transformData.size, transformData.alignment); + + readbackmem = CreateReadBackMemory(device, totalMemSize, maxAlignment); + if(readbackmem.mem == VK_NULL_HANDLE) + { + RDCERR("Unable to allocate AS triangle input buffer readback memory (size: %u bytes)", + totalMemSize); + continue; + } + + // Insert copy commands + VkBufferCopy region = { + vertexData.GetReadPosition(), + 0, + vertexData.size, + }; + ObjDisp(device)->CmdCopyBuffer(Unwrap(commandBuffer), vertexData.buf, readbackmem.buf, 1, + ®ion); + + if(indexData) + { + region = { + indexData.GetReadPosition(), + AlignUp(vertexData.size, vertexData.alignment), + indexData.size, + }; + ObjDisp(device)->CmdCopyBuffer(Unwrap(commandBuffer), indexData.buf, readbackmem.buf, 1, + ®ion); + } + + if(transformData) + { + region = { + transformData.GetReadPosition(), + AlignUp(vertexData.size, vertexData.alignment) + + AlignUp(indexData.size, indexData.alignment), + transformData.size, + }; + ObjDisp(device)->CmdCopyBuffer(Unwrap(commandBuffer), transformData.buf, readbackmem.buf, + 1, ®ion); + } + + // Prepare the barriers + barriers = {vertexData.Barrier()}; + if(indexData) + barriers.push_back(indexData.Barrier()); + if(transformData) + barriers.push_back(transformData.Barrier()); + + // Store the metadata + VkAccelerationStructureInfo::GeometryData geoData; + geoData.geometryType = geometry.geometryType; + geoData.flags = geometry.flags; + geoData.readbackMem = readbackmem.mem; + geoData.memSize = readbackmem.size; + + geoData.tris.vertexFormat = geometry.geometry.triangles.vertexFormat; + geoData.tris.vertexStride = geometry.geometry.triangles.vertexStride; + geoData.tris.maxVertex = geometry.geometry.triangles.maxVertex; + geoData.tris.indexType = geometry.geometry.triangles.indexType; + geoData.tris.hasTransformData = transformData; + + // Frustratingly rangeInfo.primitiveOffset represents either the offset into the index or + // vertex buffer depending if indices are in use or not + VkAccelerationStructureBuildRangeInfoKHR &buildData = geoData.buildRangeInfo; + buildData.primitiveCount = rangeInfo.primitiveCount; + buildData.primitiveOffset = 0; + buildData.firstVertex = 0; + buildData.transformOffset = 0; + + if(indexData) + { + buildData.primitiveOffset = (uint32_t)AlignUp(vertexData.size, vertexData.alignment); + buildData.firstVertex = rangeInfo.firstVertex; + } + if(transformData) + buildData.transformOffset = (uint32_t)(AlignUp(vertexData.size, vertexData.alignment) + + AlignUp(indexData.size, indexData.alignment)); + + metadata->geometryData.push_back(geoData); + + break; + } + case VK_GEOMETRY_TYPE_AABBS_KHR: + { + const VkAccelerationStructureGeometryAabbsDataKHR &aabbInfo = geometry.geometry.aabbs; + + // Find the associated VkBuffer + BufferData data = GetDeviceAddressData(aabbInfo.data.deviceAddress); + if(!data) + { + RDCERR("Unable to find VkBuffer for AABB data at 0x%llx", aabbInfo.data.deviceAddress); + continue; + } + + data.size = rangeInfo.primitiveCount * sizeof(VkAabbPositionsKHR); + data.SetReadPosition(rangeInfo.primitiveOffset); + + // Get the alignment + VkMemoryRequirements mrq = {}; + ObjDisp(device)->GetBufferMemoryRequirements(Unwrap(device), data.buf, &mrq); + + // Allocate copy buffer + readbackmem = CreateReadBackMemory(device, data.size, mrq.alignment); + if(readbackmem.mem == VK_NULL_HANDLE) + { + RDCERR("Unable to allocate AS AABB input buffer readback memory (size: %u bytes)", + mrq.size); + continue; + } + + // Insert copy commands + VkBufferCopy region = { + data.GetReadPosition(), + 0, + data.size, + }; + ObjDisp(device)->CmdCopyBuffer(Unwrap(commandBuffer), data.buf, readbackmem.buf, 1, ®ion); + + // Prepare barrier + barriers = {data.Barrier()}; + + // Store the metadata + VkAccelerationStructureInfo::GeometryData geoData; + geoData.geometryType = geometry.geometryType; + geoData.flags = geometry.flags; + geoData.readbackMem = readbackmem.mem; + geoData.memSize = readbackmem.size; + + geoData.aabbs.stride = aabbInfo.stride; + + geoData.buildRangeInfo = rangeInfo; + geoData.buildRangeInfo.primitiveOffset = 0; + + metadata->geometryData.push_back(geoData); + + break; + } + case VK_GEOMETRY_TYPE_INSTANCES_KHR: + { + const VkAccelerationStructureGeometryInstancesDataKHR &instanceInfo = + geometry.geometry.instances; + + // Find the associated VkBuffer + BufferData data = GetDeviceAddressData(instanceInfo.data.deviceAddress); + if(!data) + { + RDCERR("Unable to find VkBuffer for instance data at 0x%llx", + instanceInfo.data.deviceAddress); + continue; + } + + data.size = rangeInfo.primitiveCount * sizeof(VkAccelerationStructureInstanceKHR); + data.SetReadPosition(rangeInfo.primitiveOffset); + + // Get the alignment + VkMemoryRequirements mrq = {}; + ObjDisp(device)->GetBufferMemoryRequirements(Unwrap(device), data.buf, &mrq); + + // Allocate copy buffer + readbackmem = CreateReadBackMemory(device, data.size, mrq.alignment); + if(readbackmem.mem == VK_NULL_HANDLE) + { + RDCERR("Unable to allocate AS instance input buffer readback memory (size: %u bytes)", + data.size); + continue; + } + + // Insert copy commands + VkBufferCopy region = { + data.GetReadPosition(), + 0, + data.size, + }; + ObjDisp(device)->CmdCopyBuffer(Unwrap(commandBuffer), data.buf, readbackmem.buf, 1, ®ion); + + // Prepare barrier + barriers = {data.Barrier()}; + + // Store the metadata + VkAccelerationStructureInfo::GeometryData geoData; + geoData.geometryType = geometry.geometryType; + geoData.flags = geometry.flags; + geoData.readbackMem = readbackmem.mem; + geoData.memSize = readbackmem.size; + + geoData.buildRangeInfo = rangeInfo; + geoData.buildRangeInfo.primitiveOffset = 0; + + metadata->geometryData.push_back(geoData); + + break; + } + default: RDCERR("Unhandled geometry type: %d", geometry.geometryType); return; + } + + // Insert barriers to block any other commands until the buffers are copied + if(!barriers.empty()) + { + ObjDisp(device)->CmdPipelineBarrier(Unwrap(commandBuffer), VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, VK_NULL_HANDLE, + static_cast(barriers.size()), barriers.data(), + 0, VK_NULL_HANDLE); + } + + // We can schedule buffer deletion now as it isn't needed anymore + if(readbackmem.buf != VK_NULL_HANDLE) + cmdRecord->cmdInfo->pendingSubmissionCompleteCallbacks->callbacks.push_back( + [device, buffer = readbackmem.buf]() { + ObjDisp(device)->DestroyBuffer(Unwrap(device), buffer, NULL); + }); + } +} + +void VulkanAccelerationStructureManager::CopyAccelerationStructure( + VkCommandBuffer commandBuffer, const VkCopyAccelerationStructureInfoKHR &pInfo, CaptureState state) +{ + VkResourceRecord *srcRecord = GetRecord(pInfo.src); + RDCASSERT(srcRecord->accelerationStructureInfo != NULL); + + // Delete any previous data associated with AS + VkResourceRecord *dstRecord = GetRecord(pInfo.dst); + VkAccelerationStructureInfo *info = dstRecord->accelerationStructureInfo; + if(!info->geometryData.empty()) + DeletePreviousInfo(commandBuffer, info, state); + + // Rather than copy the backing mem, we can just increase the ref count. If there is an update + // build to the AS then the ref will be replaced in the record, so there's no risk of aliasing + dstRecord->accelerationStructureInfo = srcRecord->accelerationStructureInfo; + dstRecord->accelerationStructureInfo->AddRef(); +} + bool VulkanAccelerationStructureManager::Prepare(VkAccelerationStructureKHR unwrappedAs, const rdcarray &queueFamilyIndices, ASMemory &result) @@ -360,6 +786,112 @@ void VulkanAccelerationStructureManager::Apply(ResourceId id, const VkInitialCon } } +VulkanAccelerationStructureManager::Allocation VulkanAccelerationStructureManager::CreateReadBackMemory( + VkDevice device, VkDeviceSize size, VkDeviceSize alignment) +{ + VkBufferCreateInfo bufInfo = { + VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + NULL, + 0, + size, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + }; + + // we make the buffer concurrently accessible by all queue families to not invalidate the + // contents of the memory we're reading back from. + bufInfo.sharingMode = VK_SHARING_MODE_CONCURRENT; + bufInfo.queueFamilyIndexCount = (uint32_t)m_pDriver->GetQueueFamilyIndices().size(); + bufInfo.pQueueFamilyIndices = m_pDriver->GetQueueFamilyIndices().data(); + + // spec requires that CONCURRENT must specify more than one queue family. If there is only one + // queue family, we can safely use exclusive. + if(bufInfo.queueFamilyIndexCount == 1) + bufInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + Allocation readbackmem; + VkResult vkr = ObjDisp(device)->CreateBuffer(Unwrap(device), &bufInfo, NULL, &readbackmem.buf); + if(vkr != VK_SUCCESS) + { + RDCERR("Failed to create readback buffer"); + return {}; + } + + VkMemoryRequirements mrq = {}; + ObjDisp(device)->GetBufferMemoryRequirements(Unwrap(device), readbackmem.buf, &mrq); + + if(alignment != 0) + mrq.alignment = RDCMAX(mrq.alignment, alignment); + + readbackmem.size = AlignUp(mrq.size, mrq.alignment); + readbackmem.size = + AlignUp(readbackmem.size, m_pDriver->GetDeviceProps().limits.nonCoherentAtomSize); + + VkMemoryAllocateFlagsInfo flagsInfo = { + VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO, + NULL, + VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT, + }; + VkMemoryAllocateInfo info = { + VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + &flagsInfo, + readbackmem.size, + m_pDriver->GetReadbackMemoryIndex(mrq.memoryTypeBits), + }; + + vkr = ObjDisp(device)->AllocateMemory(Unwrap(device), &info, NULL, &readbackmem.mem); + if(vkr != VK_SUCCESS) + { + RDCERR("Failed to allocate readback memory"); + return {}; + } + + vkr = ObjDisp(device)->BindBufferMemory(Unwrap(device), readbackmem.buf, readbackmem.mem, 0); + if(vkr != VK_SUCCESS) + { + RDCERR("Failed to bind readback memory"); + return {}; + } + + return readbackmem; +} + +VulkanAccelerationStructureManager::RecordAndOffset VulkanAccelerationStructureManager::GetDeviceAddressData( + VkDeviceAddress address) const +{ + RecordAndOffset result; + + ResourceId id; + m_pDriver->GetResIDFromAddr(address, id, result.offset); + + // No match + if(id == ResourceId()) + return {}; + + // Convert the ID to a resource record + result.record = m_pDriver->GetResourceManager()->GetResourceRecord(id); + RDCASSERTMSG("Unable to find record", result.record, id); + if(!result.record) + return {}; + + result.address = address - result.offset; + return result; +} + +template +void VulkanAccelerationStructureManager::DeletePreviousInfo(VkCommandBuffer commandBuffer, T *info, + CaptureState state) +{ + VkResourceRecord *cmdRecord = GetRecord(commandBuffer); + cmdRecord->cmdInfo->pendingSubmissionCompleteCallbacks->callbacks.push_back( + [info]() { info->Release(); }); +} + +// OMM suport todo +template void VulkanAccelerationStructureManager::DeletePreviousInfo(VkCommandBuffer commandBuffer, + VkAccelerationStructureInfo *info, + CaptureState state); + VkDeviceSize VulkanAccelerationStructureManager::SerialisedASSize(VkAccelerationStructureKHR unwrappedAs) { VkDevice d = m_pDriver->GetDev(); diff --git a/renderdoc/driver/vulkan/vk_acceleration_structure.h b/renderdoc/driver/vulkan/vk_acceleration_structure.h index bdce6bb6b1..b810b3363a 100644 --- a/renderdoc/driver/vulkan/vk_acceleration_structure.h +++ b/renderdoc/driver/vulkan/vk_acceleration_structure.h @@ -24,14 +24,58 @@ #pragma once -#include "vk_manager.h" +#include "vk_resources.h" class WrappedVulkan; +struct VkInitialContents; -// Just holds the built flag, will eventually hold all the AS build data struct VkAccelerationStructureInfo { + struct GeometryData + { + struct Triangles + { + VkFormat vertexFormat; + VkDeviceSize vertexStride; + uint32_t maxVertex; + VkIndexType indexType; + bool hasTransformData; + }; + + struct Aabbs + { + VkDeviceSize stride; + }; + + VkGeometryTypeKHR geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR; + VkGeometryFlagsKHR flags; + + VkDeviceMemory readbackMem; + VkDeviceSize memSize; + + Triangles tris; + Aabbs aabbs; + + VkAccelerationStructureBuildRangeInfoKHR buildRangeInfo; + }; + + ~VkAccelerationStructureInfo(); + + void AddRef() { Atomic::Inc32(&refCount); } + void Release(); + + VkDevice device = VK_NULL_HANDLE; + + VkAccelerationStructureTypeKHR type = + VkAccelerationStructureTypeKHR::VK_ACCELERATION_STRUCTURE_TYPE_GENERIC_KHR; + VkBuildAccelerationStructureFlagsKHR flags = 0; + + rdcarray geometryData; + bool accelerationStructureBuilt = false; + +private: + int32_t refCount = 1; }; class VulkanAccelerationStructureManager @@ -43,8 +87,35 @@ class VulkanAccelerationStructureManager bool isTLAS; }; + struct Allocation + { + VkDeviceMemory mem = VK_NULL_HANDLE; + VkDeviceSize size = 0; + VkBuffer buf = VK_NULL_HANDLE; + }; + + struct RecordAndOffset + { + VkResourceRecord *record = NULL; + VkDeviceAddress address = 0x0; + VkDeviceSize offset = 0; + }; + explicit VulkanAccelerationStructureManager(WrappedVulkan *driver); + // Allocates readback mem and injects commands into the command buffer so that the input buffers + // are copied. + void CopyInputBuffers(VkCommandBuffer commandBuffer, + const VkAccelerationStructureBuildGeometryInfoKHR &info, + const VkAccelerationStructureBuildRangeInfoKHR *buildRange, + CaptureState state); + + // Copies the metadata from src to dst, the input buffers are identical so don't need to be + // duplicated. Compaction is ignored but the copy is still performed so the dst handle is valid + // on replay + void CopyAccelerationStructure(VkCommandBuffer commandBuffer, + const VkCopyAccelerationStructureInfoKHR &pInfo, CaptureState state); + // Called when the initial state is prepared. Any TLAS and BLAS data is copied into temporary // buffers and the handles for that memory and the buffers is stored in the init state bool Prepare(VkAccelerationStructureKHR unwrappedAs, const rdcarray &queueFamilyIndices, @@ -59,6 +130,13 @@ class VulkanAccelerationStructureManager void Apply(ResourceId id, const VkInitialContents &initial); private: + Allocation CreateReadBackMemory(VkDevice device, VkDeviceSize size, VkDeviceSize alignment = 0); + + RecordAndOffset GetDeviceAddressData(VkDeviceAddress address) const; + + template + void DeletePreviousInfo(VkCommandBuffer commandBuffer, T *info, CaptureState state); + VkDeviceSize SerialisedASSize(VkAccelerationStructureKHR unwrappedAs); WrappedVulkan *m_pDriver; diff --git a/renderdoc/driver/vulkan/vk_core.h b/renderdoc/driver/vulkan/vk_core.h index 76ca8d741b..83d6b3332f 100644 --- a/renderdoc/driver/vulkan/vk_core.h +++ b/renderdoc/driver/vulkan/vk_core.h @@ -1225,6 +1225,7 @@ class WrappedVulkan : public IFrameCapturer void RemapQueueFamilyIndices(uint32_t &srcQueueFamily, uint32_t &dstQueueFamily); uint32_t GetQueueFamilyIndex() const { return m_QueueFamilyIdx; } bool ReleaseResource(WrappedVkRes *res); + const rdcarray &GetQueueFamilyIndices() const { return m_QueueFamilyIndices; } void AddDebugMessage(MessageCategory c, MessageSeverity sv, MessageSource src, rdcstr d); @@ -1275,6 +1276,7 @@ class WrappedVulkan : public IFrameCapturer void TrackBufferAddress(VkDevice device, VkBuffer buffer); void UntrackBufferAddress(VkDevice device, VkBuffer buffer); + void GetResIDFromAddr(GPUAddressRange::Address addr, ResourceId &id, uint64_t &offs); EventFlags GetEventFlags(uint32_t eid) { return m_EventFlags[eid]; } rdcarray GetUsage(ResourceId id) { return m_ResourceUses[id]; } diff --git a/renderdoc/driver/vulkan/vk_memory.cpp b/renderdoc/driver/vulkan/vk_memory.cpp index d899803edc..24c3a5a626 100644 --- a/renderdoc/driver/vulkan/vk_memory.cpp +++ b/renderdoc/driver/vulkan/vk_memory.cpp @@ -77,6 +77,11 @@ void WrappedVulkan::UntrackBufferAddress(VkDevice device, VkBuffer buffer) m_AddressTracker.RemoveFrom(rng); } +void WrappedVulkan::GetResIDFromAddr(GPUAddressRange::Address addr, ResourceId &id, uint64_t &offs) +{ + m_AddressTracker.GetResIDFromAddr(addr, id, offs); +} + void WrappedVulkan::ChooseMemoryIndices() { // we need to do this little dance because Get*MemoryIndex checks to see if the existing diff --git a/renderdoc/driver/vulkan/vk_resources.cpp b/renderdoc/driver/vulkan/vk_resources.cpp index 243ce48621..3f0a51fb67 100644 --- a/renderdoc/driver/vulkan/vk_resources.cpp +++ b/renderdoc/driver/vulkan/vk_resources.cpp @@ -3996,8 +3996,8 @@ VkResourceRecord::~VkResourceRecord() if(resType == eResCommandPool) SAFE_DELETE(cmdPoolInfo); - if(resType == eResAccelerationStructureKHR) - SAFE_DELETE(accelerationStructureInfo); + if(resType == eResAccelerationStructureKHR && accelerationStructureInfo) + accelerationStructureInfo->Release(); } void VkResourceRecord::MarkImageFrameReferenced(VkResourceRecord *img, const ImageRange &range, diff --git a/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp index 97087c0150..83cf838b85 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp @@ -7873,6 +7873,9 @@ void WrappedVulkan::vkCmdBuildAccelerationStructuresKHR( // Add to the command buffer metadata, so we can know when it has been submitted record->cmdInfo->accelerationStructures.push_back(GetRecord(geomInfo.dstAccelerationStructure)); + + GetAccelerationStructureManager()->CopyInputBuffers(commandBuffer, geomInfo, + ppBuildRangeInfos[i], m_State); } } } diff --git a/renderdoc/driver/vulkan/wrappers/vk_get_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_get_funcs.cpp index 5bc3b5a53e..3db681c281 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_get_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_get_funcs.cpp @@ -860,8 +860,9 @@ void WrappedVulkan::vkGetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice if(accStruct && accStruct->accelerationStructureHostCommands) { - RDCWARN("Disabling support for acceleration structure host commands"); + RDCWARN("Disabling support for acceleration structure host commands and indirect builds"); accStruct->accelerationStructureHostCommands = VK_FALSE; + accStruct->accelerationStructureIndirectBuild = VK_FALSE; } } diff --git a/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp index b03e4be901..2eb28a16d9 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp @@ -3389,7 +3389,6 @@ VkResult WrappedVulkan::vkCreateAccelerationStructureKHR( record->baseResource = bufferRecord->GetResourceID(); record->baseResourceMem = bufferRecord->baseResource; record->dedicated = bufferRecord->dedicated; - record->resInfo = bufferRecord->resInfo; record->storable = bufferRecord->storable; record->memOffset = bufferRecord->memOffset + pCreateInfo->offset; record->memSize = pCreateInfo->size;