diff --git a/renderdoc/core/sparse_page_table.cpp b/renderdoc/core/sparse_page_table.cpp index 5f42c8caf..b2a02c361 100644 --- a/renderdoc/core/sparse_page_table.cpp +++ b/renderdoc/core/sparse_page_table.cpp @@ -208,6 +208,8 @@ uint64_t PageTable::setMipTailRange(uint64_t resourceByteOffset, ResourceId memo memoryByteOffset += m_PageByteSize; } + mapping.simplifyUnmapped(); + // return how much of the mip tail we consumed, clamped to the size. Note resourceByteOffset has // been remapped to be mip-tail relative here return m_MipTail.byteOffset + @@ -287,6 +289,9 @@ uint64_t PageTable::setMipTailRange(uint64_t resourceByteOffset, ResourceId memo // if we don't have enough remaining to hit the stride, we just zero-out the number of bytes // remaining byteSize -= RDCMIN(byteSize, m_MipTail.byteStride - mipTailSubresourceByteSize); + + // simplify current mapping before moving on to next subresource + mapping.simplifyUnmapped(); sub++; } else @@ -299,6 +304,11 @@ uint64_t PageTable::setMipTailRange(uint64_t resourceByteOffset, ResourceId memo } } + // simplify the last tail that we touched, as long as it's still valid. If we just incremented + // past the last, we will have simplified above where sub is incremented + if(sub < m_MipTail.mappings.size()) + m_MipTail.mappings[sub].simplifyUnmapped(); + if(byteSize > 0) RDCERR("Unclaimed bytes being assigned to image after iterating over all subresources"); @@ -383,6 +393,8 @@ void PageTable::setImageBoxRange(uint32_t subresource, const Sparse::Coord &coor } } } + + sub.simplifyUnmapped(); } } @@ -509,6 +521,9 @@ rdcpair PageTable::setImageWrappedRange(uint32_t subresource, byteSize -= m_PageByteSize; } + if(updateMappings) + sub.simplifyUnmapped(); + // if we consumed all bytes and didn't get to the end of the subresource, calculate where we // ended up if(byteSize == 0 && startingPage + numPages < numSubresourcePages) @@ -667,6 +682,8 @@ void PageTable::copyImageBoxRange(uint32_t dstSubresource, const Coord &coordInT } } } + + dstSub.simplifyUnmapped(); } void PageTable::copyImageWrappedRange(uint32_t dstSubresource, const Coord &coordInTiles, @@ -802,12 +819,17 @@ void PageTable::copyImageWrappedRange(uint32_t dstSubresource, const Coord &coor dstSubresource++; } + // simplify the mapping before we move on + dstMapping->simplifyUnmapped(); + dstSubSize = calcSubresourcePageDim(dstSubresource); dstMapping = &m_Subresources[dstSubresource]; } if(srcSubresource >= srcPageTable.getNumSubresources()) { + dstMapping->simplifyUnmapped(); + RDCERR( "Number of tiles %u in image wrapped range copy exceeded source page table subresource " "count %u", @@ -817,6 +839,8 @@ void PageTable::copyImageWrappedRange(uint32_t dstSubresource, const Coord &coor if(dstSubresource >= getNumSubresources()) { + dstMapping->simplifyUnmapped(); + RDCERR( "Number of tiles %u in image wrapped range copy exceeded dest page table subresource " "count %u", @@ -824,6 +848,10 @@ void PageTable::copyImageWrappedRange(uint32_t dstSubresource, const Coord &coor return; } } + + // simplify the last mapping we were working on + if(dstMapping < m_Subresources.end()) + dstMapping->simplifyUnmapped(); } Coord PageTable::calcSubresourcePageDim(uint32_t subresource) const @@ -2723,6 +2751,78 @@ TEST_CASE("Test sparse page table mapping", "[sparse]") }; }; }; + + SECTION("Test unmapped checks") + { + // create a 256x256 texture with 32x32 pages, 6 mips (the last two are in the mip tail) + pageTable.Initialise({256, 256, 1}, 6, 1, 64, {32, 32, 1}, 4, 0, 0, 8192); + + CHECK_FALSE(pageTable.getSubresource(0).isMapped()); + CHECK_FALSE(pageTable.getSubresource(1).isMapped()); + CHECK_FALSE(pageTable.getSubresource(2).isMapped()); + CHECK_FALSE(pageTable.getSubresource(3).isMapped()); + CHECK_FALSE(pageTable.getMipTail().mappings[0].isMapped()); + }; + + SECTION("Test unmapped simplification") + { + // create a 256x256 texture with 32x32 pages, 6 mips (the last two are in the mip tail) + pageTable.Initialise({256, 256, 1}, 6, 1, 64, {32, 32, 1}, 4, 0, 0, 8192); + + CHECK_FALSE(pageTable.getSubresource(0).isMapped()); + CHECK_FALSE(pageTable.getSubresource(1).isMapped()); + CHECK_FALSE(pageTable.getSubresource(2).isMapped()); + CHECK_FALSE(pageTable.getSubresource(3).isMapped()); + CHECK_FALSE(pageTable.getMipTail().mappings[0].isMapped()); + + ResourceId mem = ResourceIDGen::GetNewUniqueID(); + + pageTable.setMipTailRange(0, mem, 128, 256, false); + + CHECK_FALSE(pageTable.getSubresource(0).isMapped()); + CHECK_FALSE(pageTable.getSubresource(1).isMapped()); + CHECK_FALSE(pageTable.getSubresource(2).isMapped()); + CHECK_FALSE(pageTable.getSubresource(3).isMapped()); + CHECK(pageTable.getMipTail().mappings[0].isMapped()); + + pageTable.setMipTailRange(0, ResourceId(), 128, 256, false); + + CHECK_FALSE(pageTable.getSubresource(0).isMapped()); + CHECK_FALSE(pageTable.getSubresource(1).isMapped()); + CHECK_FALSE(pageTable.getSubresource(2).isMapped()); + CHECK_FALSE(pageTable.getSubresource(3).isMapped()); + CHECK_FALSE(pageTable.getMipTail().mappings[0].isMapped()); + + pageTable.setImageWrappedRange(0, {0, 0, 0}, ~0U, mem, 0, false); + + CHECK(pageTable.getSubresource(0).isMapped()); + CHECK(pageTable.getSubresource(1).isMapped()); + CHECK(pageTable.getSubresource(2).isMapped()); + CHECK(pageTable.getSubresource(3).isMapped()); + CHECK(pageTable.getMipTail().mappings[0].isMapped()); + + ResourceId mem2 = ResourceIDGen::GetNewUniqueID(); + + pageTable.setImageBoxRange(0, {0, 0, 0}, {32, 32, 1}, mem2, 1024, false); + + CHECK(pageTable.getSubresource(0).isMapped()); + + pageTable.setImageBoxRange(0, {64, 0, 0}, {32, 32, 1}, ResourceId(), 1024, false); + + CHECK(pageTable.getSubresource(0).isMapped()); + + pageTable.setImageBoxRange(0, {0, 0, 0}, {32, 32, 1}, ResourceId(), 1024, false); + + CHECK(pageTable.getSubresource(0).isMapped()); + + pageTable.setImageBoxRange(0, {0, 0, 0}, {256, 192, 1}, ResourceId(), 1024, false); + + CHECK(pageTable.getSubresource(0).isMapped()); + + pageTable.setImageBoxRange(0, {0, 192, 0}, {256, 64, 1}, ResourceId(), 1024, false); + + CHECK_FALSE(pageTable.getSubresource(0).isMapped()); + }; }; #endif // ENABLED(ENABLE_UNIT_TESTS) diff --git a/renderdoc/core/sparse_page_table.h b/renderdoc/core/sparse_page_table.h index bf04a4ee7..bdc7861b4 100644 --- a/renderdoc/core/sparse_page_table.h +++ b/renderdoc/core/sparse_page_table.h @@ -70,6 +70,23 @@ struct PageRangeMapping // the memory mappings per-page if there are different mappings per-page rdcarray pages; + bool isMapped() const { return !pages.empty() || singleMapping.memory != ResourceId(); } + void simplifyUnmapped() + { + // if we're already using singleMapping, don't check anything + if(pages.empty()) + return; + + // if we find a single page with memory mapped, we're not entirely unmapped + for(size_t i = 0; i < pages.size(); i++) + if(pages[i].memory != ResourceId()) + return; + + // we're entirely unmapped - revert back to a single page mapping + pages.clear(); + singleMapping = Page(); + singlePageReused = false; + } Page getPage(uint32_t idx, uint32_t pageSize) const { if(pages.empty()) @@ -175,6 +192,13 @@ public: { return m_Subresources[subresource]; } + const PageRangeMapping &getPageRangeMapping(uint32_t subresource) const + { + if(isSubresourceInMipTail(subresource)) + return getMipTailMapping(subresource); + else + return getSubresource(subresource); + } const MipTail &getMipTail() const { return m_MipTail; } const PageRangeMapping &getMipTailMapping(uint32_t subresource) const { diff --git a/renderdoc/driver/d3d12/d3d12_commands.cpp b/renderdoc/driver/d3d12/d3d12_commands.cpp index ee42afb9d..c22c4d4b8 100644 --- a/renderdoc/driver/d3d12/d3d12_commands.cpp +++ b/renderdoc/driver/d3d12/d3d12_commands.cpp @@ -922,7 +922,7 @@ RDResult WrappedID3D12CommandQueue::ReplayLog(CaptureState readType, uint32_t st ser.SetStringDatabase(&m_StringDB); ser.SetUserData(GetResourceManager()); - ser.SetVersion(m_pDevice->GetLogVersion()); + ser.SetVersion(m_pDevice->GetCaptureVersion()); if(IsLoading(m_State) || IsStructuredExporting(m_State)) { diff --git a/renderdoc/driver/d3d12/d3d12_common.cpp b/renderdoc/driver/d3d12/d3d12_common.cpp index 087434f3a..579ce1a0a 100644 --- a/renderdoc/driver/d3d12/d3d12_common.cpp +++ b/renderdoc/driver/d3d12/d3d12_common.cpp @@ -235,6 +235,10 @@ bool D3D12InitParams::IsSupportedVersion(uint64_t ver) if(ver == 0xC) return true; + // 0xD -> 0xE - Initial contents of sparse resources only serialise subresources with mapped pages + if(ver == 0xD) + return true; + return false; } diff --git a/renderdoc/driver/d3d12/d3d12_device.h b/renderdoc/driver/d3d12/d3d12_device.h index bb1249988..75c5c1fe6 100644 --- a/renderdoc/driver/d3d12/d3d12_device.h +++ b/renderdoc/driver/d3d12/d3d12_device.h @@ -54,7 +54,7 @@ struct D3D12InitParams UINT SDKVersion = 0; // check if a frame capture section version is supported - static const uint64_t CurrentVersion = 0xD; + static const uint64_t CurrentVersion = 0xE; static bool IsSupportedVersion(uint64_t ver); }; @@ -850,7 +850,7 @@ public: m_ReplayAGS = ags; } const ReplayOptions &GetReplayOptions() { return m_ReplayOptions; } - uint64_t GetLogVersion() { return m_SectionVersion; } + uint64_t GetCaptureVersion() { return m_SectionVersion; } CaptureState GetState() { return m_State; } D3D12Replay *GetReplay() { return m_Replay; } WrappedID3D12CommandQueue *GetQueue() { return m_Queue; } diff --git a/renderdoc/driver/d3d12/d3d12_initstate.cpp b/renderdoc/driver/d3d12/d3d12_initstate.cpp index 6d825fd71..9ebead111 100644 --- a/renderdoc/driver/d3d12/d3d12_initstate.cpp +++ b/renderdoc/driver/d3d12/d3d12_initstate.cpp @@ -61,11 +61,16 @@ bool D3D12ResourceManager::Prepare_InitialState(ID3D12DeviceChild *res) D3D12InitialContents initContents; + Sparse::PageTable *sparseTable = NULL; + + if(GetRecord(r)->sparseTable) + sparseTable = new Sparse::PageTable(*GetRecord(r)->sparseTable); + if(desc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER) { D3D12_HEAP_PROPERTIES heapProps = {}; - if(GetRecord(r)->sparseTable == NULL) + if(sparseTable == NULL) r->GetHeapProperties(&heapProps, NULL); HRESULT hr = S_OK; @@ -185,6 +190,8 @@ bool D3D12ResourceManager::Prepare_InitialState(ID3D12DeviceChild *res) bool isDepth = IsDepthFormat(desc.Format) || (desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) != 0; + bool isMSAA = false; + if(desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D && desc.SampleDesc.Count > 1) { desc.Alignment = 0; @@ -212,6 +219,7 @@ bool D3D12ResourceManager::Prepare_InitialState(ID3D12DeviceChild *res) RDCASSERTEQUAL(hr, S_OK); destState = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + isMSAA = true; } ID3D12GraphicsCommandList *list = Unwrap(m_Device->GetInitialStateList()); @@ -288,7 +296,7 @@ bool D3D12ResourceManager::Prepare_InitialState(ID3D12DeviceChild *res) bufDesc.MipLevels = 1; bufDesc.SampleDesc.Count = 1; bufDesc.SampleDesc.Quality = 0; - bufDesc.Width = 1; + bufDesc.Width = 0; UINT numSubresources = desc.MipLevels; if(desc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE3D) @@ -305,11 +313,33 @@ bool D3D12ResourceManager::Prepare_InitialState(ID3D12DeviceChild *res) numSubresources *= planes; } - D3D12_PLACED_SUBRESOURCE_FOOTPRINT *layouts = - new D3D12_PLACED_SUBRESOURCE_FOOTPRINT[numSubresources]; + D3D12_PLACED_SUBRESOURCE_FOOTPRINT layout = {}; - m_Device->GetCopyableFootprints(&desc, 0, numSubresources, 0, layouts, NULL, NULL, - &bufDesc.Width); + rdcarray copyLayouts; + rdcarray subresources; + + for(UINT i = 0; i < numSubresources; i++) + { + // skip non-MSAA sparse subresources that are not mapped at all + if(!isMSAA && sparseTable && !sparseTable->getPageRangeMapping(i).isMapped()) + continue; + + UINT64 subSize = 0; + m_Device->GetCopyableFootprints(&desc, i, 1, bufDesc.Width, &layout, NULL, NULL, &subSize); + + copyLayouts.push_back(layout); + subresources.push_back(i); + bufDesc.Width += subSize; + bufDesc.Width = AlignUp(bufDesc.Width, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT); + } + + if(bufDesc.Width == 0) + bufDesc.Width = 1U; + + // If we're not a sparse single-sampled texture, we copy the whole resource with all + // subresources. + if(isMSAA || sparseTable == NULL) + subresources = {~0U}; ID3D12Resource *copyDst = NULL; HRESULT hr = m_Device->GetReal()->CreateCommittedResource( @@ -318,7 +348,7 @@ bool D3D12ResourceManager::Prepare_InitialState(ID3D12DeviceChild *res) if(SUCCEEDED(hr)) { - for(UINT i = 0; i < numSubresources; i++) + for(UINT i = 0; i < copyLayouts.size(); i++) { D3D12_TEXTURE_COPY_LOCATION dst, src; @@ -328,7 +358,7 @@ bool D3D12ResourceManager::Prepare_InitialState(ID3D12DeviceChild *res) dst.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; dst.pResource = copyDst; - dst.PlacedFootprint = layouts[i]; + dst.PlacedFootprint = copyLayouts[i]; list->CopyTextureRegion(&dst, 0, 0, 0, &src, NULL); } @@ -365,15 +395,12 @@ bool D3D12ResourceManager::Prepare_InitialState(ID3D12DeviceChild *res) } SAFE_RELEASE(arrayTexture); - SAFE_DELETE_ARRAY(layouts); initContents = D3D12InitialContents(copyDst); + initContents.subresources = subresources; } - if(GetRecord(r)->sparseTable) - { - initContents.sparseTable = new Sparse::PageTable(*GetRecord(r)->sparseTable); - } + initContents.sparseTable = sparseTable; SetInitialContents(GetResID(r), initContents); return true; @@ -628,6 +655,21 @@ bool D3D12ResourceManager::Serialise_InitialState(SerialiserType &ser, ResourceI } SparseBinds *sparseBinds = NULL; + bool skipUnmapped = false; + rdcarray subresourcesIncluded; + if(initial) + subresourcesIncluded = initial->subresources; + + // default to {~0U} if this isn't present, which means 'all subresources serialised', since an + // empty array is valid and means NO subresources were serialised. + if(ser.VersionAtLeast(0xE)) + { + SERIALISE_ELEMENT(subresourcesIncluded); + } + else + { + subresourcesIncluded = {~0U}; + } if(ser.VersionAtLeast(0xB)) { @@ -773,6 +815,8 @@ bool D3D12ResourceManager::Serialise_InitialState(SerialiserType &ser, ResourceI initContents.sparseBinds = sparseBinds; + initContents.subresources = subresourcesIncluded; + D3D12_RESOURCE_DESC resDesc = liveRes->GetDesc(); // for MSAA textures we upload to an MSAA texture here so we're ready to copy the image in @@ -1006,6 +1050,7 @@ void D3D12ResourceManager::Create_InitialState(ResourceId id, ID3D12DeviceChild if(FAILED(hr)) { RDCERR("Couldn't create initial state copy: %s", ToStr(hr).c_str()); + m_Device->CheckHRESULT(hr); } else { @@ -1229,7 +1274,7 @@ void D3D12ResourceManager::Apply_InitialState(ID3D12DeviceChild *live, // we only accounted for planes in version 0x6, before then we only copied the first plane // so the buffer won't have enough data - if(m_Device->GetLogVersion() >= 0x6) + if(m_Device->GetCaptureVersion() >= 0x6) { D3D12_FEATURE_DATA_FORMAT_INFO formatInfo = {}; formatInfo.Format = desc.Format; @@ -1240,13 +1285,19 @@ void D3D12ResourceManager::Apply_InitialState(ID3D12DeviceChild *live, numSubresources *= planes; } - D3D12_PLACED_SUBRESOURCE_FOOTPRINT *layouts = - new D3D12_PLACED_SUBRESOURCE_FOOTPRINT[numSubresources]; + const uint32_t *nextIncludedSubresource = data.subresources.begin(); + if(data.subresources.empty() || *nextIncludedSubresource == ~0U) + nextIncludedSubresource = NULL; - m_Device->GetCopyableFootprints(&desc, 0, numSubresources, 0, layouts, NULL, NULL, NULL); + UINT64 offset = 0; + UINT64 subSize = 0; for(UINT i = 0; i < numSubresources; i++) { + // if we have a list of subresources included, only copy those + if(nextIncludedSubresource && *nextIncludedSubresource != i) + continue; + D3D12_TEXTURE_COPY_LOCATION dst, src; dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; @@ -1255,12 +1306,23 @@ void D3D12ResourceManager::Apply_InitialState(ID3D12DeviceChild *live, src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; src.pResource = Unwrap(copySrc); - src.PlacedFootprint = layouts[i]; + + m_Device->GetCopyableFootprints(&desc, i, 1, offset, &src.PlacedFootprint, NULL, NULL, + &subSize); list->CopyTextureRegion(&dst, 0, 0, 0, &src, NULL); - } - delete[] layouts; + offset += subSize; + offset = AlignUp(offset, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT); + + if(nextIncludedSubresource) + { + nextIncludedSubresource++; + // no more subresource after this one were included, even if they exist + if(nextIncludedSubresource >= data.subresources.end()) + break; + } + } } // transition back to whatever it was before diff --git a/renderdoc/driver/d3d12/d3d12_manager.h b/renderdoc/driver/d3d12/d3d12_manager.h index a6beba3f0..a31a3ebdf 100644 --- a/renderdoc/driver/d3d12/d3d12_manager.h +++ b/renderdoc/driver/d3d12/d3d12_manager.h @@ -671,6 +671,8 @@ struct D3D12InitialContents byte *srcData; size_t dataSize; + rdcarray subresources; + // only valid on capture - the snapshotted table at prepare time Sparse::PageTable *sparseTable; // only valid on replay, the table above converted into a set of binds