From 7291567fffabf26a5754828b8abec225adb3404b Mon Sep 17 00:00:00 2001 From: baldurk Date: Fri, 11 Nov 2022 15:25:51 +0000 Subject: [PATCH] Implement GPU-pushed readback for memory when optimal on D3D12 --- .../driver/d3d12/d3d12_command_queue_wrap.cpp | 49 ++++++++++++ renderdoc/driver/d3d12/d3d12_device.cpp | 76 ++++++++++++++++++- renderdoc/driver/d3d12/d3d12_device.h | 16 ++++ 3 files changed, 139 insertions(+), 2 deletions(-) diff --git a/renderdoc/driver/d3d12/d3d12_command_queue_wrap.cpp b/renderdoc/driver/d3d12/d3d12_command_queue_wrap.cpp index eaed42f37..99de9056f 100644 --- a/renderdoc/driver/d3d12/d3d12_command_queue_wrap.cpp +++ b/renderdoc/driver/d3d12/d3d12_command_queue_wrap.cpp @@ -916,6 +916,52 @@ void WrappedID3D12CommandQueue::ExecuteCommandListsInternal(UINT NumCommandLists // the resource has been unmapped on another thread before we got here. if(data) { + QueueReadbackData &queueReadback = m_pDevice->GetQueueReadbackData(); + + D3D12_COMMAND_LIST_TYPE type = GetDesc().Type; + + if(type >= ARRAY_COUNT(queueReadback.lists)) + { + RDCERR("Unexpected invalid queue type %s", ToStr(type).c_str()); + } + else if(1) + { + } + else + { + ID3D12GraphicsCommandList *list = queueReadback.lists[type]; + ID3D12CommandAllocator *alloc = queueReadback.allocs[type]; + + RDCLOG("Doing GPU readback of mapped memory"); + + D3D12_HEAP_PROPERTIES heapProps; + res->GetHeapProperties(&heapProps, NULL); + + if(heapProps.Type == D3D12_HEAP_TYPE_UPLOAD || + heapProps.CPUPageProperty == D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE) + { + if(!list) + { + RDCERR("No readback list prepared for queue type %s", ToStr(type).c_str()); + } + else + { + queueReadback.lock.Lock(); + + queueReadback.Resize(size); + + list->Reset(alloc, NULL); + list->CopyBufferRegion(queueReadback.readbackBuf, 0, res, 0, size); + list->Close(); + ID3D12CommandList *listptr = Unwrap(list); + m_pReal->ExecuteCommandLists(1, &listptr); + m_pDevice->GPUSync(this); + + data = queueReadback.readbackMapped; + } + } + } + if(ref) found = FindDiffRange(data, ref, size, diffStart, diffEnd); else @@ -945,6 +991,9 @@ void WrappedID3D12CommandQueue::ExecuteCommandListsInternal(UINT NumCommandLists { RDCDEBUG("Persistent map flush not needed for %s", ToStr(res->GetResourceID()).c_str()); } + + if(data == queueReadback.readbackMapped) + queueReadback.lock.Unlock(); } res->UnlockMaps(); diff --git a/renderdoc/driver/d3d12/d3d12_device.cpp b/renderdoc/driver/d3d12/d3d12_device.cpp index 6347d03fb..becb3ac95 100644 --- a/renderdoc/driver/d3d12/d3d12_device.cpp +++ b/renderdoc/driver/d3d12/d3d12_device.cpp @@ -724,10 +724,9 @@ WrappedID3D12Device::WrappedID3D12Device(ID3D12Device *realDevice, D3D12InitPara m_pInfoQueue->ClearStoredMessages(); + m_pInfoQueue->SetMuteDebugOutput(false); if(RenderDoc::Inst().IsReplayApp()) { - m_pInfoQueue->SetMuteDebugOutput(false); - D3D12_MESSAGE_ID mute[] = { // the runtime cries foul when you use normal APIs in expected ways (for simple markers) D3D12_MESSAGE_ID_CORRUPTED_PARAMETER2, @@ -3473,6 +3472,54 @@ void WrappedID3D12Device::FreeRTV(D3D12_CPU_DESCRIPTOR_HANDLE handle) } } +void QueueReadbackData::Resize(uint64_t size) +{ + size = AlignUp(size, 4096ULL); + + if(readbackSize >= size && size != 0) + return; + + if(readbackBuf) + { + Unwrap(readbackBuf)->Unmap(0, NULL); + SAFE_RELEASE(readbackBuf); + readbackMapped = NULL; + } + + readbackSize = size; + + if(size == 0) + return; + + RDCLOG("Resizing GPU readback window to %llu", size); + + D3D12_RESOURCE_DESC readbackDesc; + readbackDesc.Alignment = 0; + readbackDesc.DepthOrArraySize = 1; + readbackDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + readbackDesc.Flags = D3D12_RESOURCE_FLAG_NONE; + readbackDesc.Format = DXGI_FORMAT_UNKNOWN; + readbackDesc.Height = 1; + readbackDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + readbackDesc.MipLevels = 1; + readbackDesc.SampleDesc.Count = 1; + readbackDesc.SampleDesc.Quality = 0; + readbackDesc.Width = size; + + D3D12_HEAP_PROPERTIES heapProps; + heapProps.Type = D3D12_HEAP_TYPE_READBACK; + heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapProps.CreationNodeMask = 1; + heapProps.VisibleNodeMask = 1; + + device->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &readbackDesc, + D3D12_RESOURCE_STATE_COPY_DEST, NULL, __uuidof(ID3D12Resource), + (void **)&readbackBuf); + // don't intercept the map + Unwrap(readbackBuf)->Map(0, NULL, (void **)&readbackMapped); +} + void WrappedID3D12Device::CreateInternalResources() { if(IsReplayMode(m_State)) @@ -3498,6 +3545,25 @@ void WrappedID3D12Device::CreateInternalResources() // we don't want replay-only shaders added in WrappedID3D12Shader to pollute the list of resources WrappedID3D12Shader::InternalResources(true); + { + m_QueueReadbackData.device = this; + m_QueueReadbackData.Resize(4 * 1024 * 1024); + InternalRef(); + + for(D3D12_COMMAND_LIST_TYPE type : + {D3D12_COMMAND_LIST_TYPE_DIRECT, D3D12_COMMAND_LIST_TYPE_COMPUTE, + D3D12_COMMAND_LIST_TYPE_COPY}) + { + CreateCommandAllocator(type, __uuidof(ID3D12CommandAllocator), + (void **)&m_QueueReadbackData.allocs[type]); + InternalRef(); + CreateCommandList(0, type, m_QueueReadbackData.allocs[type], NULL, + __uuidof(ID3D12GraphicsCommandList), + (void **)&m_QueueReadbackData.lists[type]); + InternalRef(); + } + } + CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, __uuidof(ID3D12CommandAllocator), (void **)&m_Alloc); InternalRef(); @@ -3587,6 +3653,12 @@ void WrappedID3D12Device::DestroyInternalResources() SAFE_RELEASE(m_DataUploadList[i]); SAFE_RELEASE(m_DataUploadAlloc); + for(size_t i = 0; i < ARRAY_COUNT(m_QueueReadbackData.allocs); i++) + SAFE_RELEASE(m_QueueReadbackData.allocs[i]); + for(size_t i = 0; i < ARRAY_COUNT(m_QueueReadbackData.lists); i++) + SAFE_RELEASE(m_QueueReadbackData.lists[i]); + m_QueueReadbackData.Resize(0); + SAFE_RELEASE(m_Alloc); SAFE_RELEASE(m_GPUSyncFence); CloseHandle(m_GPUSyncHandle); diff --git a/renderdoc/driver/d3d12/d3d12_device.h b/renderdoc/driver/d3d12/d3d12_device.h index 665330092..dd6c57b6d 100644 --- a/renderdoc/driver/d3d12/d3d12_device.h +++ b/renderdoc/driver/d3d12/d3d12_device.h @@ -61,6 +61,20 @@ struct D3D12InitParams DECLARE_REFLECTION_STRUCT(D3D12InitParams); +struct QueueReadbackData +{ + Threading::CriticalSection lock; + ID3D12Resource *readbackBuf = NULL; + byte *readbackMapped = NULL; + uint64_t readbackSize = 0; + ID3D12GraphicsCommandList *lists[6] = {}; + ID3D12CommandAllocator *allocs[6] = {}; + + void Resize(uint64_t size); + + WrappedID3D12Device *device; +}; + class WrappedID3D12Device; class WrappedID3D12Resource; class WrappedID3D12PipelineState; @@ -588,6 +602,7 @@ private: WrappedID3D12CommandQueue *m_Queue; ID3D12CommandAllocator *m_Alloc = NULL, *m_DataUploadAlloc = NULL; + QueueReadbackData m_QueueReadbackData; ID3D12GraphicsCommandList *m_DataUploadList[64] = {}; size_t m_CurDataUpload = 0; ID3D12DescriptorHeap *m_RTVHeap = NULL; @@ -806,6 +821,7 @@ public: void FirstFrame(IDXGISwapper *swapper); const ActionDescription *GetAction(uint32_t eventId); + QueueReadbackData &GetQueueReadbackData() { return m_QueueReadbackData; } bool IsBindlessResourceUseActive() const { return m_BindlessResourceUseActive; } ResourceId GetFrameCaptureResourceId() { return m_FrameCaptureRecord->GetResourceID(); } void AddDebugMessage(MessageCategory c, MessageSeverity sv, MessageSource src, rdcstr d);