diff --git a/renderdoc/driver/d3d12/d3d12_command_queue_wrap.cpp b/renderdoc/driver/d3d12/d3d12_command_queue_wrap.cpp index 1bf5922b4..a8a4f5abe 100644 --- a/renderdoc/driver/d3d12/d3d12_command_queue_wrap.cpp +++ b/renderdoc/driver/d3d12/d3d12_command_queue_wrap.cpp @@ -886,6 +886,12 @@ void WrappedID3D12CommandQueue::ExecuteCommandListsInternal(UINT NumCommandLists { rdcarray maps = m_pDevice->GetMaps(); + // get the Mappable referenced IDs. With the case of placed resources the resource that's + // mapped may not be the one that was bound but they may overlap, so we use the heap as + // reference for non-committed resource. + std::unordered_set mappableIDs; + WrappedID3D12Resource::GetMappableIDs(GetResourceManager(), refdIDs, mappableIDs); + for(auto it = maps.begin(); it != maps.end(); ++it) { WrappedID3D12Resource *res = GetWrapped(it->res); @@ -893,10 +899,10 @@ void WrappedID3D12CommandQueue::ExecuteCommandListsInternal(UINT NumCommandLists 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()) + if(mappableIDs.find(res->GetMappableID()) == mappableIDs.end()) { - RDCDEBUG("Map of memory %s not referenced in this queue - not flushing", - ToStr(res->GetResourceID()).c_str()); + RDCDEBUG("Map of memory %s (mappable ID %s) not referenced in this queue - not flushing", + ToStr(res->GetResourceID()).c_str(), ToStr(res->GetMappableID()).c_str()); continue; } diff --git a/renderdoc/driver/d3d12/d3d12_resources.cpp b/renderdoc/driver/d3d12/d3d12_resources.cpp index 0711062aa..b56b47721 100644 --- a/renderdoc/driver/d3d12/d3d12_resources.cpp +++ b/renderdoc/driver/d3d12/d3d12_resources.cpp @@ -327,6 +327,22 @@ void WrappedID3D12Resource::RefBuffers(D3D12ResourceManager *rm) rm->MarkResourceFrameReferenced(m_Addresses.addresses[i].id, eFrameRef_Read); } +void WrappedID3D12Resource::GetMappableIDs(D3D12ResourceManager *rm, + const std::unordered_set &refdIDs, + std::unordered_set &mappableIDs) +{ + SCOPED_READLOCK(m_Addresses.addressLock); + for(size_t i = 0; i < m_Addresses.addresses.size(); i++) + { + if(refdIDs.find(m_Addresses.addresses[i].id) != refdIDs.end()) + { + WrappedID3D12Resource *resource = + (WrappedID3D12Resource *)rm->GetCurrentResource(m_Addresses.addresses[i].id); + mappableIDs.insert(resource->GetMappableID()); + } + } +} + rdcarray WrappedID3D12Resource::AddRefBuffersBeforeCapture(D3D12ResourceManager *rm) { rdcarray ret; diff --git a/renderdoc/driver/d3d12/d3d12_resources.h b/renderdoc/driver/d3d12/d3d12_resources.h index 32e410751..b40df8689 100644 --- a/renderdoc/driver/d3d12/d3d12_resources.h +++ b/renderdoc/driver/d3d12/d3d12_resources.h @@ -946,12 +946,21 @@ public: return this->GetReal(); } + ResourceId GetMappableID() + { + if(m_Heap) + return m_Heap->GetResourceID(); + return this->GetResourceID(); + } + void SetHeap(ID3D12Heap *heap) { m_Heap = (WrappedID3D12Heap *)heap; SAFE_ADDREF(m_Heap); } static void RefBuffers(D3D12ResourceManager *rm); + static void GetMappableIDs(D3D12ResourceManager *rm, const std::unordered_set &refdIDs, + std::unordered_set &mappableIDs); static rdcarray AddRefBuffersBeforeCapture(D3D12ResourceManager *rm); diff --git a/util/test/demos/d3d12/d3d12_map_placed_alias.cpp b/util/test/demos/d3d12/d3d12_map_placed_alias.cpp new file mode 100644 index 000000000..f8c788ae7 --- /dev/null +++ b/util/test/demos/d3d12/d3d12_map_placed_alias.cpp @@ -0,0 +1,143 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2019-2023 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include "d3d12_test.h" + +RD_TEST(D3D12_Map_PlacedAlias, D3D12GraphicsTest) +{ + static constexpr const char *Description = + "Check that mapped data is still saved even if the mapped resource is not the one used in " + "rendering."; + + int main() + { + // initialise, create window, create device, etc + if(!Init()) + return 3; + + ID3DBlobPtr vsblob = Compile(D3DDefaultVertex, "main", "vs_4_0"); + ID3DBlobPtr psblob = Compile(D3DDefaultPixel, "main", "ps_4_0"); + + UINT heapSize = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT * 3; + + D3D12_HEAP_DESC heapDesc; + heapDesc.SizeInBytes = heapSize; + heapDesc.Flags = D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS; + heapDesc.Alignment = 0; + heapDesc.Properties.Type = D3D12_HEAP_TYPE_UPLOAD; + heapDesc.Properties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapDesc.Properties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapDesc.Properties.CreationNodeMask = 1; + heapDesc.Properties.VisibleNodeMask = 1; + + D3D12_RESOURCE_DESC resDesc; + resDesc.Alignment = 0; + resDesc.DepthOrArraySize = 1; + resDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + resDesc.Flags = D3D12_RESOURCE_FLAG_NONE; + resDesc.Format = DXGI_FORMAT_UNKNOWN; + resDesc.Height = 1; + resDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + resDesc.Width = sizeof(DefaultTri); + resDesc.MipLevels = 1; + resDesc.SampleDesc.Count = 1; + resDesc.SampleDesc.Quality = 0; + + ID3D12HeapPtr vbHeap; + CHECK_HR(dev->CreateHeap(&heapDesc, __uuidof(ID3D12Heap), (void **)&vbHeap)); + + ID3D12ResourcePtr vb; + CHECK_HR(dev->CreatePlacedResource(vbHeap, D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT, &resDesc, + D3D12_RESOURCE_STATE_GENERIC_READ, NULL, + __uuidof(ID3D12Resource), (void **)&vb)); + + ID3D12ResourcePtr vb2; + CHECK_HR(dev->CreatePlacedResource(vbHeap, D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT * 2, + &resDesc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL, + __uuidof(ID3D12Resource), (void **)&vb2)); + + resDesc.Width = heapSize; + ID3D12ResourcePtr mapBuffer; + CHECK_HR(dev->CreatePlacedResource(vbHeap, 0, &resDesc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL, + __uuidof(ID3D12Resource), (void **)&mapBuffer)); + + ID3D12RootSignaturePtr sig = MakeSig({}); + + ID3D12PipelineStatePtr pso = MakePSO().RootSig(sig).InputLayout().VS(vsblob).PS(psblob); + + byte *mapptr = NULL; + mapBuffer->Map(0, NULL, (void **)&mapptr); + + // clear the buffer before capturing, and we'll do so at the end after submitting, to ensure + // data can only come from detected map writes. + memset(mapptr, 0xfe, heapSize); + + while(Running()) + { + ID3D12GraphicsCommandListPtr cmd = GetCommandBuffer(); + + Reset(cmd); + + ID3D12ResourcePtr bb = StartUsingBackbuffer(cmd, D3D12_RESOURCE_STATE_RENDER_TARGET); + + D3D12_CPU_DESCRIPTOR_HANDLE rtv = + MakeRTV(bb).Format(DXGI_FORMAT_R8G8B8A8_UNORM_SRGB).CreateCPU(0); + + ClearRenderTargetView(cmd, rtv, {0.2f, 0.2f, 0.2f, 1.0f}); + + cmd->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + + IASetVertexBuffer(cmd, vb, sizeof(DefaultA2V), 0); + cmd->SetPipelineState(pso); + cmd->SetGraphicsRootSignature(sig); + + RSSetViewport(cmd, {0.0f, 0.0f, (float)screenWidth, (float)screenHeight, 0.0f, 1.0f}); + RSSetScissorRect(cmd, {0, 0, screenWidth, screenHeight}); + + OMSetRenderTargets(cmd, {rtv}, {}); + + cmd->DrawInstanced(3, 1, 0, 0); + + FinishUsingBackbuffer(cmd, D3D12_RESOURCE_STATE_RENDER_TARGET); + + cmd->Close(); + + memcpy(mapptr + D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT, DefaultTri, sizeof(DefaultTri)); + + Submit({cmd}); + + GPUSync(); + + memset(mapptr, 0xfe, heapSize); + + Present(); + } + + mapBuffer->Unmap(0, NULL); + + return 0; + } +}; + +REGISTER_TEST(); diff --git a/util/test/demos/demos.vcxproj b/util/test/demos/demos.vcxproj index 3dcdded5d..b4dd874ad 100644 --- a/util/test/demos/demos.vcxproj +++ b/util/test/demos/demos.vcxproj @@ -191,6 +191,7 @@ + diff --git a/util/test/demos/demos.vcxproj.filters b/util/test/demos/demos.vcxproj.filters index 0c9037950..8bb026568 100644 --- a/util/test/demos/demos.vcxproj.filters +++ b/util/test/demos/demos.vcxproj.filters @@ -646,6 +646,9 @@ OpenGL\demos + + D3D12\demos + diff --git a/util/test/tests/D3D12/D3D12_Map_PlacedAlias.py b/util/test/tests/D3D12/D3D12_Map_PlacedAlias.py new file mode 100644 index 000000000..20b0a27c6 --- /dev/null +++ b/util/test/tests/D3D12/D3D12_Map_PlacedAlias.py @@ -0,0 +1,13 @@ +import renderdoc as rd +import rdtest + + +class D3D12_Map_PlacedAlias(rdtest.TestCase): + demos_test_name = 'D3D12_Map_PlacedAlias' + + def check_capture(self): + action = self.find_action("Draw") + + self.controller.SetFrameEvent(action.eventId, True) + + self.check_triangle() \ No newline at end of file