From aa242a5be7deef34998fe074240ce72f0d27202f Mon Sep 17 00:00:00 2001 From: baldurk Date: Mon, 25 Nov 2024 16:16:59 +0000 Subject: [PATCH] Do disk caching on background thread --- renderdoc/driver/d3d12/d3d12_device.cpp | 3 +- renderdoc/driver/d3d12/d3d12_manager.cpp | 162 +++++++++++++++++++---- renderdoc/driver/d3d12/d3d12_manager.h | 34 ++--- 3 files changed, 150 insertions(+), 49 deletions(-) diff --git a/renderdoc/driver/d3d12/d3d12_device.cpp b/renderdoc/driver/d3d12/d3d12_device.cpp index 7e1631b0a..c3deeea88 100644 --- a/renderdoc/driver/d3d12/d3d12_device.cpp +++ b/renderdoc/driver/d3d12/d3d12_device.cpp @@ -2726,6 +2726,7 @@ void WrappedID3D12Device::StartFrameCapture(DeviceOwnedWindow devWnd) // wait until we've synced all queues to check for these GetResourceManager()->GetRTManager()->TickASManagement(); + GetResourceManager()->GetRTManager()->FlushDiskCacheThread(); GetResourceManager()->PrepareInitialContents(); @@ -4423,8 +4424,6 @@ void WrappedID3D12Device::CreateInternalResources() } } - GetResourceManager()->GetRTManager()->CreateInternalResources(); - // we don't want replay-only shaders added in WrappedID3D12Shader to pollute the list of resources WrappedID3D12Shader::InternalResources(true); diff --git a/renderdoc/driver/d3d12/d3d12_manager.cpp b/renderdoc/driver/d3d12/d3d12_manager.cpp index 417c08dba..e698e3804 100644 --- a/renderdoc/driver/d3d12/d3d12_manager.cpp +++ b/renderdoc/driver/d3d12/d3d12_manager.cpp @@ -715,24 +715,46 @@ D3D12RTManager::D3D12RTManager(WrappedID3D12Device *device, { } -void D3D12RTManager::CreateInternalResources() +D3D12RTManager::~D3D12RTManager() { - if(m_wrappedDevice) - { - m_GPUBufferAllocator.Alloc(D3D12GpuBufferHeapType::CustomHeapWithUavCpuAccess, - D3D12GpuBufferHeapMemoryFlag::Default, 16, 256, &ASQueryBuffer); + SAFE_RELEASE(ASSerialiseBuffer); + SAFE_RELEASE(m_accStructPatchInfo.m_rootSignature); + SAFE_RELEASE(m_accStructPatchInfo.m_pipeline); + SAFE_RELEASE(m_TLASCopyingData.ArgsBuffer); + SAFE_RELEASE(m_TLASCopyingData.PreparePipe); + SAFE_RELEASE(m_TLASCopyingData.CopyPipe); + SAFE_RELEASE(m_TLASCopyingData.RootSig); + SAFE_RELEASE(m_TLASCopyingData.IndirectSig); + SAFE_RELEASE(m_RayPatchingData.descPatchRootSig); + SAFE_RELEASE(m_RayPatchingData.descPatchPipe); + SAFE_RELEASE(m_RayPatchingData.indirectComSig); + SAFE_RELEASE(m_RayPatchingData.indirectPrepPipe); + SAFE_RELEASE(m_RayPatchingData.indirectPrepRootSig); + SAFE_RELEASE(m_TimerQueryHeap); - if(D3D12_Debug_RT_Auditing()) - { - m_GPUBufferAllocator.Alloc(D3D12GpuBufferHeapType::ReadBackHeap, - D3D12GpuBufferHeapMemoryFlag::Default, 256, 256, - &PostbuildReadbackBuffer); - } + if(m_ASCacheThread) + { + Atomic::Dec32(&m_ASCacheThreadRunning); + // wake the thread so it can notice that the running flag is zero'd + m_ASCacheThreadSemaphore->Wake(1); + Threading::JoinThread(m_ASCacheThread); + Threading::CloseThread(m_ASCacheThread); + m_ASCacheThreadSemaphore->Destroy(); } } void D3D12RTManager::InitInternalResources() { + m_GPUBufferAllocator.Alloc(D3D12GpuBufferHeapType::CustomHeapWithUavCpuAccess, + D3D12GpuBufferHeapMemoryFlag::Default, 16, 256, &ASQueryBuffer); + + if(D3D12_Debug_RT_Auditing()) + { + m_GPUBufferAllocator.Alloc(D3D12GpuBufferHeapType::ReadBackHeap, + D3D12GpuBufferHeapMemoryFlag::Default, 256, 256, + &PostbuildReadbackBuffer); + } + if(IsReplayMode(m_wrappedDevice->GetState())) { InitReplayBlasPatchingResources(); @@ -1011,6 +1033,75 @@ void D3D12RTManager::TickASManagement() CheckASCaching(); } +void D3D12RTManager::PushDiskCacheTask(std::function task) +{ + { + SCOPED_LOCK(m_ASCacheThreadLock); + + // create thread lazily if we need it and it's not already here + if(!m_ASCacheThread) + { + m_ASCacheThreadRunning = 1; + m_ASCacheThreadSemaphore = Threading::Semaphore::Create(); + + m_ASCacheThread = Threading::CreateThread([this]() { + // loop until we shut down + while(Atomic::CmpExch32(&m_ASCacheThreadRunning, 1, 1) == 1) + { + // wait until there's a work item (may be multiple, we process one per loop and may not + // actually block here if there's more work waiting) + m_ASCacheThreadSemaphore->WaitForWake(); + + // grab a task + std::function task; + { + SCOPED_LOCK(m_ASCacheThreadLock); + if(!m_ASCacheTasks.empty()) + { + task = m_ASCacheTasks.front(); + m_ASCacheTasks.erase(0); + + // consider ourselves active as soon as we have the task before releasing the lock so + // there is no period where both the queue is empty and we're not active, but a task is in flight + Atomic::Inc32(&m_ASCacheThreadActive); + } + } + + // run it if we got one + if(task) + { + task(); + Atomic::Dec32(&m_ASCacheThreadActive); + } + } + }); + } + + // push a task + m_ASCacheTasks.push_back(task); + } + + // wake the thread to do one bit of work + m_ASCacheThreadSemaphore->Wake(1); +} + +void D3D12RTManager::FlushDiskCacheThread() +{ + if(!m_ASCacheThread) + return; + + m_ASCacheThreadSemaphore->Wake(1); + + // just spin until the thread is done + while(true) + { + SCOPED_LOCK(m_ASCacheThreadLock); + // there may be a task running on the thread even if the queue is empty, ensure the active flag is not set + if(m_ASCacheTasks.empty() && Atomic::CmpExch32(&m_ASCacheThreadActive, 0, 0) == 0) + return; + } +} + void D3D12RTManager::CheckASCaching() { double now = m_Timestamp.GetMilliseconds(); @@ -1079,25 +1170,40 @@ void D3D12RTManager::CheckASCaching() { ASBuildData *buildData = m_InMemASBuildDatas[i]; - if(D3D12_Debug_RT_Auditing()) - { - RDCDEBUG("Flushing AS build data of size %llu to disk", buildData->buffer->Size()); - } - - // de-interleave positions in geoms here if their stride is greater than vertex format? - buildData->filename = StringFormat::Fmt( + // grab parameters for the task + D3D12GpuBuffer *buf = buildData->buffer; + rdcstr filename = StringFormat::Fmt( "%s/rdoc_as_%llu_%llu.bin", get_dirname(RenderDoc::Inst().GetCaptureFileTemplate()).c_str(), Timing::GetTick(), Threading::GetCurrentID()); - buildData->bytesOnDisk = buildData->buffer->Size(); - FileIO::CreateParentDirectory(buildData->filename); + // immediately update the build data as if the cache has completed. We flush the thread + buildData->bytesOnDisk = buildData->buffer->Size(); + buildData->buffer = NULL; + buildData->filename = filename; + + if(D3D12_Debug_RT_Auditing()) { - StreamWriter writer(FileIO::fopen(buildData->filename, FileIO::WriteBinary), Ownership::Stream); - writer.Write(buildData->buffer->Map(), buildData->buffer->Size()); + RDCDEBUG("Flushing AS build data of size %llu to disk", buf->Size()); } - buildData->buffer->Unmap(); - SAFE_RELEASE(buildData->buffer); + PushDiskCacheTask([buf, filename]() { + FILE *f = FileIO::fopen(filename, FileIO::WriteBinary); + + if(!f) + { + FileIO::CreateParentDirectory(filename); + f = FileIO::fopen(filename, FileIO::WriteBinary); + } + + { + StreamWriter writer(f, Ownership::Stream); + // de-interleave positions in geoms here if their stride is greater than vertex format to save space? + writer.Write(buf->Map(), buf->Size()); + } + + buf->Unmap(); + buf->Release(); + }); m_DiskCachedASBuildDatas.push_back(buildData); } @@ -3374,7 +3480,13 @@ void ASBuildData::Release() SAFE_RELEASE(buffer); if(!filename.empty()) - FileIO::Delete(filename); + { + if(rtManager) + { + rdcstr fileToDelete = filename; + rtManager->PushDiskCacheTask([fileToDelete]() { FileIO::Delete(fileToDelete); }); + } + } delete this; } diff --git a/renderdoc/driver/d3d12/d3d12_manager.h b/renderdoc/driver/d3d12/d3d12_manager.h index de8c107db..dd96b41f4 100644 --- a/renderdoc/driver/d3d12/d3d12_manager.h +++ b/renderdoc/driver/d3d12/d3d12_manager.h @@ -1213,31 +1213,12 @@ class D3D12RTManager { public: D3D12RTManager(WrappedID3D12Device *device, D3D12GpuBufferAllocator &gpuBufferAllocator); - - void CreateInternalResources(); - - D3D12AccStructPatchInfo GetAccStructPatchInfo() const { return m_accStructPatchInfo; } - - ~D3D12RTManager() - { - SAFE_RELEASE(ASSerialiseBuffer); - SAFE_RELEASE(m_accStructPatchInfo.m_rootSignature); - SAFE_RELEASE(m_accStructPatchInfo.m_pipeline); - SAFE_RELEASE(m_TLASCopyingData.ArgsBuffer); - SAFE_RELEASE(m_TLASCopyingData.PreparePipe); - SAFE_RELEASE(m_TLASCopyingData.CopyPipe); - SAFE_RELEASE(m_TLASCopyingData.RootSig); - SAFE_RELEASE(m_TLASCopyingData.IndirectSig); - SAFE_RELEASE(m_RayPatchingData.descPatchRootSig); - SAFE_RELEASE(m_RayPatchingData.descPatchPipe); - SAFE_RELEASE(m_RayPatchingData.indirectComSig); - SAFE_RELEASE(m_RayPatchingData.indirectPrepPipe); - SAFE_RELEASE(m_RayPatchingData.indirectPrepRootSig); - SAFE_RELEASE(m_TimerQueryHeap); - } + ~D3D12RTManager(); void InitInternalResources(); + D3D12AccStructPatchInfo GetAccStructPatchInfo() const { return m_accStructPatchInfo; } + uint32_t RegisterLocalRootSig(const D3D12RootSignature &sig); void RegisterExportDatabase(D3D12ShaderExportDatabase *db); @@ -1277,6 +1258,8 @@ public: void AddPendingASBuilds(ID3D12Fence *fence, UINT64 waitValue, const rdcarray> &callbacks); void TickASManagement(); + void PushDiskCacheTask(std::function task); + void FlushDiskCacheThread(); void ResizeSerialisationBuffer(UINT64 ScratchDataSizeInBytes); @@ -1359,6 +1342,13 @@ private: ID3D12CommandSignature *indirectComSig = NULL; } m_RayPatchingData; + Threading::CriticalSection m_ASCacheThreadLock; + int32_t m_ASCacheThreadRunning = 0; + int32_t m_ASCacheThreadActive = 0; + Threading::Semaphore *m_ASCacheThreadSemaphore = NULL; + Threading::ThreadHandle m_ASCacheThread = {}; + rdcarray> m_ASCacheTasks; + ID3D12QueryHeap *m_TimerQueryHeap = NULL; D3D12GpuBuffer *m_TimerReadbackBuffer = NULL; uint64_t *m_Timestamps = NULL;