Do disk caching on background thread

This commit is contained in:
baldurk
2024-12-02 13:39:28 +00:00
parent 514e0b0d78
commit aa242a5be7
3 changed files with 150 additions and 49 deletions
+1 -2
View File
@@ -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);
+137 -25
View File
@@ -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<void()> 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<void()> 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;
}
+12 -22
View File
@@ -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<std::function<bool()>> &callbacks);
void TickASManagement();
void PushDiskCacheTask(std::function<void()> 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<std::function<void()>> m_ASCacheTasks;
ID3D12QueryHeap *m_TimerQueryHeap = NULL;
D3D12GpuBuffer *m_TimerReadbackBuffer = NULL;
uint64_t *m_Timestamps = NULL;