From 081345fccf9a18b3003dd9f784264a296f36a9e2 Mon Sep 17 00:00:00 2001 From: baldurk Date: Thu, 21 Nov 2024 17:50:56 +0000 Subject: [PATCH] Add verification of TLAS build inputs --- .../driver/d3d12/d3d12_command_list4_wrap.cpp | 70 ++++++++++++--- .../driver/d3d12/d3d12_command_queue_wrap.cpp | 85 ++++++++++++++++++- renderdoc/driver/d3d12/d3d12_commands.h | 5 +- renderdoc/driver/d3d12/d3d12_resources.h | 2 + 4 files changed, 148 insertions(+), 14 deletions(-) diff --git a/renderdoc/driver/d3d12/d3d12_command_list4_wrap.cpp b/renderdoc/driver/d3d12/d3d12_command_list4_wrap.cpp index 91da40a86..ab0cf6a4f 100644 --- a/renderdoc/driver/d3d12/d3d12_command_list4_wrap.cpp +++ b/renderdoc/driver/d3d12/d3d12_command_list4_wrap.cpp @@ -847,7 +847,7 @@ bool WrappedID3D12GraphicsCommandList::PatchAccStructBlasAddress( resBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; resBarrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; resBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; - resBarrier.Transition.pResource = patchRaytracing->m_patchedInstanceBuffer->Resource(); + resBarrier.Transition.pResource = patchRaytracing->patchedInstanceBuffer->Resource(); resBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_UNORDERED_ACCESS; resBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST; resBarriers.push_back(resBarrier); @@ -866,7 +866,7 @@ bool WrappedID3D12GraphicsCommandList::PatchAccStructBlasAddress( // unroll the instances list into a flat array (which will then get patched below in-place) D3D12GpuBuffer *tempBuffer = rtManager->UnrollBLASInstancesList( unwrappedList, accStructInput.Inputs, addressPairResAddress, addressCount, - patchRaytracing->m_patchedInstanceBuffer); + patchRaytracing->patchedInstanceBuffer); accStructInput.Inputs.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY; @@ -880,9 +880,20 @@ bool WrappedID3D12GraphicsCommandList::PatchAccStructBlasAddress( } else { - unwrappedList->CopyBufferRegion(patchRaytracing->m_patchedInstanceBuffer->Resource(), - patchRaytracing->m_patchedInstanceBuffer->Offset(), + unwrappedList->CopyBufferRegion(patchRaytracing->patchedInstanceBuffer->Resource(), + patchRaytracing->patchedInstanceBuffer->Offset(), instanceResource, instanceResOffset, totalInstancesSize); + + if(D3D12_Debug_RTAuditing()) + { + GetResourceManager()->GetGPUBufferAllocator().Alloc( + D3D12GpuBufferHeapType::ReadBackHeap, D3D12GpuBufferHeapMemoryFlag::Default, + totalInstancesSize, 256, &patchRaytracing->unpatchedInstanceBufferReadback); + + unwrappedList->CopyBufferRegion(patchRaytracing->unpatchedInstanceBufferReadback->Resource(), + patchRaytracing->unpatchedInstanceBufferReadback->Offset(), + instanceResource, instanceResOffset, totalInstancesSize); + } } D3D12AccStructPatchInfo patchInfo = rtManager->GetAccStructPatchInfo(); @@ -894,7 +905,7 @@ bool WrappedID3D12GraphicsCommandList::PatchAccStructBlasAddress( resBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; resBarrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; resBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; - resBarrier.Transition.pResource = patchRaytracing->m_patchedInstanceBuffer->Resource(); + resBarrier.Transition.pResource = patchRaytracing->patchedInstanceBuffer->Resource(); resBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST; resBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_UNORDERED_ACCESS; resBarriers.push_back(resBarrier); @@ -928,7 +939,7 @@ bool WrappedID3D12GraphicsCommandList::PatchAccStructBlasAddress( D3D12_RESOURCE_BARRIER resBarrier; resBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV; resBarrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; - resBarrier.UAV.pResource = patchRaytracing->m_patchedInstanceBuffer->Resource(); + resBarrier.UAV.pResource = patchRaytracing->patchedInstanceBuffer->Resource(); unwrappedList->ResourceBarrier(1, &resBarrier); } @@ -940,14 +951,15 @@ bool WrappedID3D12GraphicsCommandList::PatchAccStructBlasAddress( (UINT)D3D12PatchTLASBuildParam::RootAddressPairSrv, addressPairResAddress); unwrappedList->SetComputeRootUnorderedAccessView( (UINT)D3D12PatchTLASBuildParam::RootPatchedAddressUav, - patchRaytracing->m_patchedInstanceBuffer->Address()); + patchRaytracing->patchedInstanceBuffer->Address()); + unwrappedList->Dispatch(accStructInput.Inputs.NumDescs, 1, 1); { D3D12_RESOURCE_BARRIER resBarrier; resBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV; resBarrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; - resBarrier.UAV.pResource = patchRaytracing->m_patchedInstanceBuffer->Resource(); + resBarrier.UAV.pResource = patchRaytracing->patchedInstanceBuffer->Resource(); unwrappedList->ResourceBarrier(1, &resBarrier); } @@ -956,7 +968,7 @@ bool WrappedID3D12GraphicsCommandList::PatchAccStructBlasAddress( resBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; resBarrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; resBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; - resBarrier.Transition.pResource = patchRaytracing->m_patchedInstanceBuffer->Resource(); + resBarrier.Transition.pResource = patchRaytracing->patchedInstanceBuffer->Resource(); resBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_UNORDERED_ACCESS; resBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE; unwrappedList->ResourceBarrier(1, &resBarrier); @@ -993,6 +1005,22 @@ bool WrappedID3D12GraphicsCommandList::Serialise_BuildRaytracingAccelerationStru BakedCmdListInfo::PatchRaytracing &patchInfo = bakedCmdInfo.m_patchRaytracingInfo[bakedCmdInfo.curEventID]; + D3D12AccelerationStructure *accStructAtDstOffset = NULL; + + if(D3D12_Debug_RTAuditing()) + { + ResourceId destASBId; + D3D12BufferOffset destASBOffset; + + WrappedID3D12Resource::GetResIDFromAddr(AccStructDesc.DestAccelerationStructureData, + destASBId, destASBOffset); + + WrappedID3D12Resource *destASB = + GetResourceManager()->GetCurrentAs(destASBId); + + RDCASSERT(destASB->GetAccStructIfExist(destASBOffset, &accStructAtDstOffset)); + } + if(IsActiveReplaying(m_State)) { if(m_Cmd->InRerecordRange(m_Cmd->m_LastCmdListID)) @@ -1004,9 +1032,15 @@ bool WrappedID3D12GraphicsCommandList::Serialise_BuildRaytracingAccelerationStru { patchInfo.m_patched = false; PatchAccStructBlasAddress(AccStructDesc, Unwrap4(list), &patchInfo); + + // the destination AS *will* be present by definition, but we only fetch it and store it + // here for auditing so the pointer may be NULL. + patchInfo.destinationAS = + accStructAtDstOffset ? accStructAtDstOffset->GetResourceID() : ResourceId(); + if(patchInfo.m_patched) { - AccStructDesc.Inputs.InstanceDescs = patchInfo.m_patchedInstanceBuffer->Address(); + AccStructDesc.Inputs.InstanceDescs = patchInfo.patchedInstanceBuffer->Address(); } else { @@ -1017,6 +1051,13 @@ bool WrappedID3D12GraphicsCommandList::Serialise_BuildRaytracingAccelerationStru // Switch back to previous state bakedCmdInfo.state.ApplyState(m_pDevice, list); } + else if(AccStructDesc.Inputs.Type == D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL) + { + // the destination AS *will* be present by definition, but we only fetch it and store it + // here for auditing so the pointer may be NULL. + patchInfo.destinationAS = + accStructAtDstOffset ? accStructAtDstOffset->GetResourceID() : ResourceId(); + } if(!D3D12_Debug_RTAuditing()) { @@ -1039,13 +1080,18 @@ bool WrappedID3D12GraphicsCommandList::Serialise_BuildRaytracingAccelerationStru if(GetResourceManager()->GetGPUBufferAllocator().Alloc( D3D12GpuBufferHeapType::DefaultHeapWithUav, D3D12GpuBufferHeapMemoryFlag::Default, totalInstancesSize, D3D12_RAYTRACING_INSTANCE_DESCS_BYTE_ALIGNMENT, - &patchInfo.m_patchedInstanceBuffer)) + &patchInfo.patchedInstanceBuffer)) { PatchAccStructBlasAddress(AccStructDesc, Unwrap4(pCommandList), &patchInfo); + // the destination AS *will* be present by definition, but we only fetch it and store it + // here for auditing so the pointer may be NULL. + patchInfo.destinationAS = + accStructAtDstOffset ? accStructAtDstOffset->GetResourceID() : ResourceId(); + if(patchInfo.m_patched) { - AccStructDesc.Inputs.InstanceDescs = patchInfo.m_patchedInstanceBuffer->Address(); + AccStructDesc.Inputs.InstanceDescs = patchInfo.patchedInstanceBuffer->Address(); } // Switch back to previous state diff --git a/renderdoc/driver/d3d12/d3d12_command_queue_wrap.cpp b/renderdoc/driver/d3d12/d3d12_command_queue_wrap.cpp index e35831305..deda10aa0 100644 --- a/renderdoc/driver/d3d12/d3d12_command_queue_wrap.cpp +++ b/renderdoc/driver/d3d12/d3d12_command_queue_wrap.cpp @@ -28,6 +28,7 @@ #include "d3d12_resources.h" RDOC_EXTERN_CONFIG(bool, D3D12_Debug_SingleSubmitFlushing); +RDOC_EXTERN_CONFIG(bool, D3D12_Debug_RTAuditing); template bool WrappedID3D12CommandQueue::Serialise_UpdateTileMappings( @@ -491,11 +492,93 @@ bool WrappedID3D12CommandQueue::Serialise_ExecuteCommandLists(SerialiserType &se ID3D12CommandList *list = Unwrap(ppCommandLists[i]); real->ExecuteCommandLists(1, &list); - if(D3D12_Debug_SingleSubmitFlushing()) + if(D3D12_Debug_SingleSubmitFlushing() || D3D12_Debug_RTAuditing()) m_pDevice->GPUSync(); BakedCmdListInfo &info = m_Cmd.m_BakedCmdListInfo[cmd]; + if(D3D12_Debug_RTAuditing()) + { + for(auto it = info.m_patchRaytracingInfo.begin(); it != info.m_patchRaytracingInfo.end(); + ++it) + { + if(!it->second.unpatchedInstanceBufferReadback) + { + if(it->second.destinationAS != ResourceId()) + { + D3D12AccelerationStructure *as = + (D3D12AccelerationStructure *)GetResourceManager()->GetLiveResource( + it->second.destinationAS); + as->seenReplayBuild = true; + } + + continue; + } + + const D3D12_RAYTRACING_INSTANCE_DESC *instances = + (const D3D12_RAYTRACING_INSTANCE_DESC *) + it->second.unpatchedInstanceBufferReadback->Map(); + + ResourceId id = it->second.destinationAS; + + RDCLOG("Verifying TLAS build of %s at relative %u in %s", ToStr(id).c_str(), it->first, + ToStr(cmd).c_str()); + + for(UINT desc = 0; desc < it->second.numDescs; desc++) + { + // silently ignore NULL BLASs + if(instances[desc].AccelerationStructure == 0) + continue; + + ResourceId blasId; + UINT64 blasOffs; + m_pDevice->GetResIDFromOrigAddr(instances[desc].AccelerationStructure, blasId, + blasOffs); + + WrappedID3D12Resource *blas = + GetResourceManager()->GetLiveAs(blasId); + + D3D12AccelerationStructure *blasCheck = NULL; + rdcstr invalid; + + if(blasId == ResourceId() || blas == NULL) + invalid = StringFormat::Fmt("Address references non-existant buffer"); + else if(!blas->GetAccStructIfExist(blasOffs, &blasCheck)) + invalid = StringFormat::Fmt("No valid AS known at buffer location"); + else if(blasCheck->Type() == D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL) + invalid = StringFormat::Fmt("TLAS referenced by TLAS"); + + if(!invalid.empty()) + { + RDCERR("%s[%u]: %s", ToStr(id).c_str(), desc, invalid.c_str()); + continue; + } + + if(id < GetResourceManager()->GetOriginalID(blasCheck->GetResourceID())) + { + RDCERR("%s[%u]: BLAS referenced by TLAS is newer than TLAS", ToStr(id).c_str(), desc); + continue; + } + + if(!blasCheck->seenReplayBuild) + { + RDCERR("%s[%u]: BLAS referenced by TLAS has not been built yet on replay", + ToStr(id).c_str(), desc); + continue; + } + + RDCLOG("%s[%u]: valid BLAS referenced at %llx (%llx on replay)", ToStr(id).c_str(), + desc, instances[desc].AccelerationStructure, + blas->GetGPUVirtualAddress() + blasOffs); + + RDCASSERTEQUAL(blasCheck->GetVirtualAddress(), blas->GetGPUVirtualAddress() + blasOffs); + } + + it->second.unpatchedInstanceBufferReadback->Unmap(); + SAFE_RELEASE(it->second.unpatchedInstanceBufferReadback); + } + } + if(!info.executeEvents.empty()) { // ensure all GPU work has finished for readback of arguments diff --git a/renderdoc/driver/d3d12/d3d12_commands.h b/renderdoc/driver/d3d12/d3d12_commands.h index 60eb2bb5e..0c1b7e9cc 100644 --- a/renderdoc/driver/d3d12/d3d12_commands.h +++ b/renderdoc/driver/d3d12/d3d12_commands.h @@ -205,7 +205,10 @@ struct BakedCmdListInfo struct PatchRaytracing { bool m_patched = false; - D3D12GpuBuffer *m_patchedInstanceBuffer; + uint32_t numDescs = 0; + D3D12GpuBuffer *patchedInstanceBuffer = NULL; + D3D12GpuBuffer *unpatchedInstanceBufferReadback = NULL; + ResourceId destinationAS; }; rdcflatmap m_patchRaytracingInfo; diff --git a/renderdoc/driver/d3d12/d3d12_resources.h b/renderdoc/driver/d3d12/d3d12_resources.h index ff09b6a93..73f8ddd6c 100644 --- a/renderdoc/driver/d3d12/d3d12_resources.h +++ b/renderdoc/driver/d3d12/d3d12_resources.h @@ -1684,6 +1684,8 @@ public: ASBuildData *buildData = NULL; + // only valid on replay - for auditing + bool seenReplayBuild = false; private: WrappedID3D12Resource *m_asbWrappedResource; D3D12BufferOffset m_asbWrappedResourceBufferOffset;