diff --git a/renderdoc/driver/d3d12/d3d12_command_queue_wrap.cpp b/renderdoc/driver/d3d12/d3d12_command_queue_wrap.cpp index 83c2dea29..c885dcd96 100644 --- a/renderdoc/driver/d3d12/d3d12_command_queue_wrap.cpp +++ b/renderdoc/driver/d3d12/d3d12_command_queue_wrap.cpp @@ -439,7 +439,60 @@ void STDMETHODCALLTYPE WrappedID3D12CommandQueue::ExecuteCommandLists( if(capframe) { - // flush coherent maps + vector maps = m_pDevice->GetMaps(); + + for(auto it = maps.begin(); it != maps.end(); ++it) + { + WrappedID3D12Resource *res = it->res; + UINT subres = it->subres; + size_t size = (size_t)it->totalSize; + + // only need to flush memory that could affect this submitted batch of work + if(refdIDs.find(res->GetResourceID()) == refdIDs.end()) + { + RDCDEBUG("Map of memory %llu not referenced in this queue - not flushing", + res->GetResourceID()); + continue; + } + + size_t diffStart = 0, diffEnd = 0; + bool found = true; + + byte *ref = res->GetShadow(subres); + byte *data = res->GetMap(subres); + + if(ref) + found = FindDiffRange(data, ref, size, diffStart, diffEnd); + else + diffEnd = size; + + if(found) + { + RDCLOG("Persistent map flush forced for %llu (%llu -> %llu)", res->GetResourceID(), + (uint64_t)diffStart, (uint64_t)diffEnd); + + D3D12_RANGE range = {diffStart, diffEnd}; + + m_pDevice->MapDataWrite(res, subres, data, range); + + if(ref == NULL) + { + res->AllocShadow(subres, size); + + ref = res->GetShadow(subres); + } + + // update comparison shadow for next time + memcpy(ref, res->GetMap(subres), size); + + GetResourceManager()->MarkPendingDirty(res->GetResourceID()); + } + else + { + RDCDEBUG("Persistent map flush not needed for %llu", res->GetResourceID()); + } + } + for(UINT i = 0; i < NumCommandLists; i++) { SCOPED_SERIALISE_CONTEXT(EXECUTE_CMD_LISTS); diff --git a/renderdoc/driver/d3d12/d3d12_commands.cpp b/renderdoc/driver/d3d12/d3d12_commands.cpp index c38a3009d..1d60ee617 100644 --- a/renderdoc/driver/d3d12/d3d12_commands.cpp +++ b/renderdoc/driver/d3d12/d3d12_commands.cpp @@ -264,6 +264,13 @@ void WrappedID3D12CommandQueue::ProcessChunk(uint64_t offset, D3D12ChunkType chu case RESOURCE_BARRIER: m_ReplayList->Serialise_ResourceBarrier(0, NULL); break; + case MAP_DATA_WRITE: + m_pDevice->Serialise_MapDataWrite(m_pSerialiser, NULL, 0, NULL, D3D12_RANGE()); + break; + case WRITE_TO_SUB: + m_pDevice->Serialise_WriteToSubresource(m_pSerialiser, NULL, 0, NULL, NULL, 0, 0); + break; + case BEGIN_QUERY: m_ReplayList->Serialise_BeginQuery(NULL, D3D12_QUERY_TYPE_OCCLUSION, 0); break; diff --git a/renderdoc/driver/d3d12/d3d12_common.h b/renderdoc/driver/d3d12/d3d12_common.h index 93f6b8974..fb253b7ad 100644 --- a/renderdoc/driver/d3d12/d3d12_common.h +++ b/renderdoc/driver/d3d12/d3d12_common.h @@ -279,6 +279,9 @@ void Serialiser::Serialise(const char *name, D3D12Descriptor &el); \ D3D12_CHUNK_MACRO(RESOURCE_BARRIER, "ID3D12GraphicsCommandList::ResourceBarrier") \ \ + D3D12_CHUNK_MACRO(MAP_DATA_WRITE, "ID3D12Resource::Unmap") \ + D3D12_CHUNK_MACRO(WRITE_TO_SUB, "ID3D12Resource::WriteToSubresource") \ + \ D3D12_CHUNK_MACRO(BEGIN_QUERY, "ID3D12GraphicsCommandList::BeginQuery") \ D3D12_CHUNK_MACRO(END_QUERY, "ID3D12GraphicsCommandList::EndQuery") \ D3D12_CHUNK_MACRO(RESOLVE_QUERY, "ID3D12GraphicsCommandList::ResolveQueryData") \ diff --git a/renderdoc/driver/d3d12/d3d12_device.cpp b/renderdoc/driver/d3d12/d3d12_device.cpp index bce825692..f94f1159b 100644 --- a/renderdoc/driver/d3d12/d3d12_device.cpp +++ b/renderdoc/driver/d3d12/d3d12_device.cpp @@ -702,6 +702,195 @@ IUnknown *WrappedID3D12Device::WrapSwapchainBuffer(WrappedIDXGISwapChain3 *swap, return pRes; } +void WrappedID3D12Device::Map(WrappedID3D12Resource *Resource, UINT Subresource) +{ + MapState map; + map.res = Resource; + map.subres = Subresource; + + D3D12_RESOURCE_DESC desc = Resource->GetDesc(); + + m_pDevice->GetCopyableFootprints(&desc, Subresource, 1, 0, NULL, NULL, NULL, &map.totalSize); + + { + SCOPED_LOCK(m_MapsLock); + m_Maps.push_back(map); + } +} + +void WrappedID3D12Device::Unmap(WrappedID3D12Resource *Resource, UINT Subresource, byte *mapPtr, + const D3D12_RANGE *pWrittenRange) +{ + MapState map = {}; + { + SCOPED_LOCK(m_MapsLock); + for(auto it = m_Maps.begin(); it != m_Maps.end(); ++it) + { + if(it->res == Resource && it->subres == Subresource) + { + map = *it; + m_Maps.erase(it); + break; + } + } + } + + if(map.res == NULL) + return; + + bool capframe = false; + { + SCOPED_LOCK(m_CapTransitionLock); + capframe = (m_State == WRITING_CAPFRAME); + } + + D3D12_RANGE range = {0, (SIZE_T)map.totalSize}; + + if(pWrittenRange) + range = *pWrittenRange; + + if(capframe) + MapDataWrite(Resource, Subresource, mapPtr, range); +} + +bool WrappedID3D12Device::Serialise_MapDataWrite(Serialiser *localSerialiser, + WrappedID3D12Resource *Resource, UINT Subresource, + byte *mapPtr, D3D12_RANGE range) +{ + SERIALISE_ELEMENT(ResourceId, res, GetResID(Resource)); + SERIALISE_ELEMENT(UINT, sub, Subresource); + SERIALISE_ELEMENT_BUF(byte *, data, mapPtr, range.End - range.Begin); + SERIALISE_ELEMENT(uint64_t, begin, (uint64_t)range.Begin); + SERIALISE_ELEMENT(uint64_t, end, (uint64_t)range.End); + + if(m_State < WRITING && GetResourceManager()->HasLiveResource(res)) + { + ID3D12Resource *r = GetResourceManager()->GetLiveAs(res); + + D3D12_RANGE range = {}; + + byte *mapPtr = NULL; + HRESULT hr = r->Map(sub, &range, (void **)&mapPtr); + + if(SUCCEEDED(hr)) + { + memcpy(mapPtr + begin, data, end - begin); + + range.Begin = begin; + range.End = end; + + r->Unmap(sub, &range); + } + else + { + RDCERR("Failed to map resource on replay %08x", hr); + } + + SAFE_DELETE_ARRAY(data); + } + + return true; +} + +void WrappedID3D12Device::MapDataWrite(WrappedID3D12Resource *Resource, UINT Subresource, + byte *mapPtr, D3D12_RANGE range) +{ + CACHE_THREAD_SERIALISER(); + + SCOPED_SERIALISE_CONTEXT(MAP_DATA_WRITE); + Serialise_MapDataWrite(localSerialiser, Resource, Subresource, mapPtr, range); + + m_FrameCaptureRecord->AddChunk(scope.Get()); + + GetResourceManager()->MarkResourceFrameReferenced(Resource->GetResourceID(), eFrameRef_Write); +} + +bool WrappedID3D12Device::Serialise_WriteToSubresource(Serialiser *localSerialiser, + WrappedID3D12Resource *Resource, + UINT Subresource, const D3D12_BOX *pDstBox, + const void *pSrcData, UINT SrcRowPitch, + UINT SrcDepthPitch) +{ + SERIALISE_ELEMENT(ResourceId, res, GetResID(Resource)); + SERIALISE_ELEMENT(UINT, sub, Subresource); + SERIALISE_ELEMENT(bool, HasBox, pDstBox != NULL); + SERIALISE_ELEMENT_OPT(D3D12_BOX, box, *pDstBox, HasBox); + SERIALISE_ELEMENT(UINT, rowPitch, SrcRowPitch); + SERIALISE_ELEMENT(UINT, depthPitch, SrcDepthPitch); + + size_t dataSize = 0; + + if(m_State >= WRITING) + { + // if we have a box, calculate how much data from user's pitch + if(HasBox) + { + UINT numSlicesBeforeLast = pDstBox->back - pDstBox->front - 1; + UINT numRows = pDstBox->bottom - pDstBox->top; + + dataSize = depthPitch * numSlicesBeforeLast + rowPitch * numRows; + } + else + { + D3D12_RESOURCE_DESC desc = Resource->GetDesc(); + UINT64 totalBytes = 0; + + // otherwise fetch the whole resource size + m_pDevice->GetCopyableFootprints(&desc, sub, 1, 0, NULL, NULL, NULL, &totalBytes); + + dataSize = (size_t)totalBytes; + } + } + + SERIALISE_ELEMENT_BUF(byte *, data, pSrcData, dataSize); + + if(m_State < WRITING && GetResourceManager()->HasLiveResource(res)) + { + ID3D12Resource *r = GetResourceManager()->GetLiveAs(res); + + HRESULT hr = r->Map(sub, NULL, NULL); + + if(SUCCEEDED(hr)) + { + r->WriteToSubresource(sub, HasBox ? &box : NULL, data, rowPitch, depthPitch); + + r->Unmap(sub, NULL); + } + else + { + RDCERR("Failed to map resource on replay %08x", hr); + } + + SAFE_DELETE_ARRAY(data); + } + + return true; +} + +void WrappedID3D12Device::WriteToSubresource(WrappedID3D12Resource *Resource, UINT Subresource, + const D3D12_BOX *pDstBox, const void *pSrcData, + UINT SrcRowPitch, UINT SrcDepthPitch) +{ + bool capframe = false; + { + SCOPED_LOCK(m_CapTransitionLock); + capframe = (m_State == WRITING_CAPFRAME); + } + + if(capframe) + { + CACHE_THREAD_SERIALISER(); + + SCOPED_SERIALISE_CONTEXT(WRITE_TO_SUB); + Serialise_WriteToSubresource(localSerialiser, Resource, Subresource, pDstBox, pSrcData, + SrcRowPitch, SrcDepthPitch); + + m_FrameCaptureRecord->AddChunk(scope.Get()); + + GetResourceManager()->MarkResourceFrameReferenced(Resource->GetResourceID(), eFrameRef_Write); + } +} + HRESULT WrappedID3D12Device::Present(WrappedIDXGISwapChain3 *swap, UINT SyncInterval, UINT Flags) { if((Flags & DXGI_PRESENT_TEST) != 0) @@ -980,6 +1169,12 @@ bool WrappedID3D12Device::EndFrameCapture(void *dev, void *wnd) m_State = WRITING_IDLE; GPUSync(); + + { + SCOPED_LOCK(m_MapsLock); + for(auto it = m_Maps.begin(); it != m_Maps.end(); ++it) + it->res->FreeShadow(); + } } byte *thpixels = NULL; diff --git a/renderdoc/driver/d3d12/d3d12_device.h b/renderdoc/driver/d3d12/d3d12_device.h index 2912e54d2..c33215f4d 100644 --- a/renderdoc/driver/d3d12/d3d12_device.h +++ b/renderdoc/driver/d3d12/d3d12_device.h @@ -52,6 +52,7 @@ struct D3D12InitParams : public RDCInitParams }; class WrappedID3D12Device; +class WrappedID3D12Resource; // give every impression of working but do nothing. // Just allow the user to call functions so that they don't @@ -242,6 +243,9 @@ private: D3D12Replay m_Replay; D3D12DebugManager *m_DebugManager; + Threading::CriticalSection m_MapsLock; + vector m_Maps; + void ProcessChunk(uint64_t offset, D3D12ChunkType context); unsigned int m_InternalRefcount; @@ -423,6 +427,26 @@ public: void NewSwapchainBuffer(IUnknown *backbuffer) {} void ReleaseSwapchainResources(WrappedIDXGISwapChain3 *swap); + void Map(WrappedID3D12Resource *Resource, UINT Subresource); + void Unmap(WrappedID3D12Resource *Resource, UINT Subresource, byte *mapPtr, + const D3D12_RANGE *pWrittenRange); + + IMPLEMENT_FUNCTION_THREAD_SERIALISED(void, MapDataWrite, WrappedID3D12Resource *Resource, + UINT Subresource, byte *mapPtr, D3D12_RANGE range); + IMPLEMENT_FUNCTION_THREAD_SERIALISED(void, WriteToSubresource, WrappedID3D12Resource *Resource, + UINT Subresource, const D3D12_BOX *pDstBox, + const void *pSrcData, UINT SrcRowPitch, UINT SrcDepthPitch); + + vector GetMaps() + { + vector ret; + { + SCOPED_LOCK(m_MapsLock); + ret = m_Maps; + } + return ret; + } + void InternalRef() { InterlockedIncrement(&m_InternalRefcount); } void InternalRelease() { InterlockedDecrement(&m_InternalRefcount); } void SoftRef() { m_SoftRefCounter.AddRef(); } diff --git a/renderdoc/driver/d3d12/d3d12_manager.h b/renderdoc/driver/d3d12/d3d12_manager.h index 13c593267..ad4620f6a 100644 --- a/renderdoc/driver/d3d12/d3d12_manager.h +++ b/renderdoc/driver/d3d12/d3d12_manager.h @@ -279,6 +279,15 @@ struct CmdListRecordingInfo vector bundles; }; +class WrappedID3D12Resource; + +struct MapState +{ + WrappedID3D12Resource *res; + UINT subres; + UINT64 totalSize; +}; + struct D3D12ResourceRecord : public ResourceRecord { enum @@ -322,6 +331,14 @@ struct D3D12ResourceRecord : public ResourceRecord D3D12ResourceType type; D3D12ResourceRecord *bakedCommands; CmdListRecordingInfo *cmdInfo; + + struct MapData + { + byte *realPtr; + byte *shadowPtr; + }; + + vector m_Map; }; typedef vector SubresourceStateVector; diff --git a/renderdoc/driver/d3d12/d3d12_resources.cpp b/renderdoc/driver/d3d12/d3d12_resources.cpp index dec9b8d0d..4f6738db3 100644 --- a/renderdoc/driver/d3d12/d3d12_resources.cpp +++ b/renderdoc/driver/d3d12/d3d12_resources.cpp @@ -29,6 +29,7 @@ std::vector WrappedID3D12Resource::m_Addresses; std::map WrappedID3D12Resource::m_List; + std::map WrappedID3D12PipelineState::m_Shaders; @@ -169,7 +170,7 @@ D3D12ResourceType IdentifyTypeByPtr(ID3D12DeviceChild *ptr) return Resource_Unknown; } -TrackedResource *GetTracked(ID3D12DeviceChild *ptr) +TrackedResource12 *GetTracked(ID3D12DeviceChild *ptr) { if(ptr == NULL) return NULL; @@ -177,12 +178,12 @@ TrackedResource *GetTracked(ID3D12DeviceChild *ptr) #undef D3D12_TYPE_MACRO #define D3D12_TYPE_MACRO(iface) \ if(UnwrapHelper::IsAlloc(ptr)) \ - return (TrackedResource *)GetWrapped((iface *)ptr); + return (TrackedResource12 *)GetWrapped((iface *)ptr); ALL_D3D12_TYPES; if(WrappedID3D12PipelineState::ShaderEntry::IsAlloc(ptr)) - return (TrackedResource *)(WrappedID3D12PipelineState::ShaderEntry *)ptr; + return (TrackedResource12 *)(WrappedID3D12PipelineState::ShaderEntry *)ptr; return NULL; } @@ -216,7 +217,7 @@ ResourceId GetResID(ID3D12DeviceChild *ptr) if(ptr == NULL) return ResourceId(); - TrackedResource *res = GetTracked(ptr); + TrackedResource12 *res = GetTracked(ptr); if(res == NULL) { @@ -239,7 +240,7 @@ D3D12ResourceRecord *GetRecord(ID3D12DeviceChild *ptr) if(ptr == NULL) return NULL; - TrackedResource *res = GetTracked(ptr); + TrackedResource12 *res = GetTracked(ptr); if(res == NULL) { @@ -256,6 +257,122 @@ D3D12ResourceRecord *GetRecord(ID3D12DeviceChild *ptr) return res->GetResourceRecord(); } +byte *WrappedID3D12Resource::GetMap(UINT Subresource) +{ + vector &map = GetResourceRecord()->m_Map; + + if(Subresource < map.size()) + return map[Subresource].realPtr; + + return NULL; +} + +byte *WrappedID3D12Resource::GetShadow(UINT Subresource) +{ + vector &map = GetResourceRecord()->m_Map; + + if(Subresource >= map.size()) + map.resize(Subresource + 1); + + return map[Subresource].shadowPtr; +} + +void WrappedID3D12Resource::AllocShadow(UINT Subresource, size_t size) +{ + vector &map = GetResourceRecord()->m_Map; + + if(Subresource >= map.size()) + map.resize(Subresource + 1); + + if(map[Subresource].shadowPtr == NULL) + map[Subresource].shadowPtr = Serialiser::AllocAlignedBuffer(size); +} + +void WrappedID3D12Resource::FreeShadow() +{ + vector &map = GetResourceRecord()->m_Map; + + for(size_t i = 0; i < map.size(); i++) + { + Serialiser::FreeAlignedBuffer(map[i].shadowPtr); + map[i].shadowPtr = NULL; + } +} + +HRESULT STDMETHODCALLTYPE WrappedID3D12Resource::Map(UINT Subresource, + const D3D12_RANGE *pReadRange, void **ppData) +{ + // don't care about maps without returned pointers - we'll just intercept the WriteToSubresource + // calls + if(ppData == NULL) + return m_pReal->Map(Subresource, pReadRange, ppData); + + void *mapPtr = NULL; + + // pass a NULL range as we might want to read from the whole range + HRESULT hr = m_pReal->Map(Subresource, NULL, &mapPtr); + + if(ppData) + *ppData = mapPtr; + + if(SUCCEEDED(hr) && GetResourceRecord()) + { + vector &map = GetResourceRecord()->m_Map; + + if(Subresource >= map.size()) + map.resize(Subresource + 1); + map[Subresource].realPtr = (byte *)mapPtr; + + // need to register this so we can flush any updates in case it's left persistant + m_pDevice->Map(this, Subresource); + } + + return hr; +} + +void STDMETHODCALLTYPE WrappedID3D12Resource::Unmap(UINT Subresource, const D3D12_RANGE *pWrittenRange) +{ + if(GetResourceRecord()) + { + vector &map = GetResourceRecord()->m_Map; + + // may not have a map if e.g. no pointer was requested + if(Subresource < map.size()) + { + m_pDevice->Unmap(this, Subresource, map[Subresource].realPtr, pWrittenRange); + + map[Subresource].realPtr = NULL; + Serialiser::FreeAlignedBuffer(map[Subresource].shadowPtr); + } + } + + return m_pReal->Unmap(Subresource, pWrittenRange); +} + +HRESULT STDMETHODCALLTYPE WrappedID3D12Resource::WriteToSubresource(UINT DstSubresource, + const D3D12_BOX *pDstBox, + const void *pSrcData, + UINT SrcRowPitch, + UINT SrcDepthPitch) +{ + if(GetResourceRecord()) + { + vector &map = GetResourceRecord()->m_Map; + + if(DstSubresource < map.size()) + { + m_pDevice->WriteToSubresource(this, DstSubresource, pDstBox, pSrcData, SrcDepthPitch, + SrcDepthPitch); + } + else + { + RDCERR("WriteToSubresource without matching map!"); + } + } + + return m_pReal->WriteToSubresource(DstSubresource, pDstBox, pSrcData, SrcRowPitch, SrcDepthPitch); +} + WrappedID3D12DescriptorHeap::WrappedID3D12DescriptorHeap(ID3D12DescriptorHeap *real, WrappedID3D12Device *device, const D3D12_DESCRIPTOR_HEAP_DESC &desc) diff --git a/renderdoc/driver/d3d12/d3d12_resources.h b/renderdoc/driver/d3d12/d3d12_resources.h index cdb31a937..68dc3c283 100644 --- a/renderdoc/driver/d3d12/d3d12_resources.h +++ b/renderdoc/driver/d3d12/d3d12_resources.h @@ -29,10 +29,10 @@ #include "d3d12_device.h" #include "d3d12_manager.h" -class TrackedResource +class TrackedResource12 { public: - TrackedResource() + TrackedResource12() { m_ID = ResourceIDGen::GetNewUniqueID(); m_pRecord = NULL; @@ -41,8 +41,8 @@ public: D3D12ResourceRecord *GetResourceRecord() { return m_pRecord; } void SetResourceRecord(D3D12ResourceRecord *record) { m_pRecord = record; } private: - TrackedResource(const TrackedResource &); - TrackedResource &operator=(const TrackedResource &); + TrackedResource12(const TrackedResource12 &); + TrackedResource12 &operator=(const TrackedResource12 &); ResourceId m_ID; D3D12ResourceRecord *m_pRecord; @@ -51,7 +51,9 @@ private: extern const GUID RENDERDOC_ID3D12ShaderGUID_ShaderDebugMagicValue; template -class WrappedDeviceChild12 : public RefCounter12, public NestedType2, public TrackedResource +class WrappedDeviceChild12 : public RefCounter12, + public NestedType2, + public TrackedResource12 { protected: WrappedID3D12Device *m_pDevice; @@ -737,6 +739,11 @@ public: bool Resident() { return resident; } void SetResident(bool r) { resident = r; } + byte *GetMap(UINT Subresource); + byte *GetShadow(UINT Subresource); + void AllocShadow(UINT Subresource, size_t size); + void FreeShadow(); + ////////////////////////////// // implement ID3D12Resource @@ -752,31 +759,17 @@ public: return m_pReal->GetHeapProperties(pHeapProperties, pHeapFlags); } - virtual HRESULT STDMETHODCALLTYPE Map(UINT Subresource, const D3D12_RANGE *pReadRange, void **ppData) - { - D3D12NOTIMP("Resource mapping"); - return m_pReal->Map(Subresource, pReadRange, ppData); - } - - virtual void STDMETHODCALLTYPE Unmap(UINT Subresource, const D3D12_RANGE *pWrittenRange) - { - D3D12NOTIMP("Resource mapping"); - return m_pReal->Unmap(Subresource, pWrittenRange); - } - + virtual HRESULT STDMETHODCALLTYPE Map(UINT Subresource, const D3D12_RANGE *pReadRange, + void **ppData); + virtual void STDMETHODCALLTYPE Unmap(UINT Subresource, const D3D12_RANGE *pWrittenRange); virtual HRESULT STDMETHODCALLTYPE WriteToSubresource(UINT DstSubresource, const D3D12_BOX *pDstBox, const void *pSrcData, UINT SrcRowPitch, - UINT SrcDepthPitch) - { - D3D12NOTIMP("Resource mapping"); - return m_pReal->WriteToSubresource(DstSubresource, pDstBox, pSrcData, SrcRowPitch, SrcDepthPitch); - } - + UINT SrcDepthPitch); virtual HRESULT STDMETHODCALLTYPE ReadFromSubresource(void *pDstData, UINT DstRowPitch, UINT DstDepthPitch, UINT SrcSubresource, const D3D12_BOX *pSrcBox) { - D3D12NOTIMP("Resource mapping"); + // don't have to do anything here return m_pReal->ReadFromSubresource(pDstData, DstRowPitch, DstDepthPitch, SrcSubresource, pSrcBox); }