Skip unmapped subresources when serialising sparse init states on D3D12

This commit is contained in:
baldurk
2022-10-26 17:09:05 +01:00
parent 4612d71df5
commit 10d4c69433
7 changed files with 215 additions and 23 deletions
+100
View File
@@ -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<uint32_t, Coord> 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)
+24
View File
@@ -70,6 +70,23 @@ struct PageRangeMapping
// the memory mappings per-page if there are different mappings per-page
rdcarray<Page> 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
{
+1 -1
View File
@@ -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))
{
+4
View File
@@ -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;
}
+2 -2
View File
@@ -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; }
+82 -20
View File
@@ -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<D3D12_PLACED_SUBRESOURCE_FOOTPRINT> copyLayouts;
rdcarray<uint32_t> 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<UINT64>(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<uint32_t> 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<UINT64>(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
+2
View File
@@ -671,6 +671,8 @@ struct D3D12InitialContents
byte *srcData;
size_t dataSize;
rdcarray<uint32_t> 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