Implement reserved (sparse) resources on D3D12. Closes #2203

This commit is contained in:
baldurk
2021-03-12 18:08:23 +00:00
parent e5e5b064d8
commit aeaf26930c
14 changed files with 1014 additions and 62 deletions
+1 -1
View File
@@ -477,7 +477,7 @@ struct ResourceRecord
return MarkResourceFrameReferenced(id, refType, ComposeFrameRefs);
}
void AddResourceReferences(ResourceRecordHandler *mgr);
void AddReferencedIDs(std::set<ResourceId> &ids)
void AddReferencedIDs(std::unordered_set<ResourceId> &ids)
{
for(auto it = m_FrameRefs.begin(); it != m_FrameRefs.end(); ++it)
ids.insert(it->first);
@@ -5351,7 +5351,79 @@ bool WrappedID3D12GraphicsCommandList::Serialise_CopyTiles(
const D3D12_TILE_REGION_SIZE *pTileRegionSize, ID3D12Resource *pBuffer,
UINT64 BufferStartOffsetInBytes, D3D12_TILE_COPY_FLAGS Flags)
{
D3D12NOTIMP("Tiled Resources");
ID3D12GraphicsCommandList *pCommandList = this;
SERIALISE_ELEMENT(pCommandList);
SERIALISE_ELEMENT(pTiledResource);
SERIALISE_ELEMENT_LOCAL(TileRegionStartCoordinate, *pTileRegionStartCoordinate);
SERIALISE_ELEMENT_LOCAL(TileRegionSize, *pTileRegionSize);
SERIALISE_ELEMENT(pBuffer);
SERIALISE_ELEMENT(BufferStartOffsetInBytes);
SERIALISE_ELEMENT(Flags);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
m_Cmd->m_LastCmdListID = GetResourceManager()->GetOriginalID(GetResID(pCommandList));
if(IsActiveReplaying(m_State))
{
if(m_Cmd->InRerecordRange(m_Cmd->m_LastCmdListID))
{
ID3D12GraphicsCommandListX *list = m_Cmd->RerecordCmdList(m_Cmd->m_LastCmdListID);
Unwrap(list)->CopyTiles(Unwrap(pTiledResource), &TileRegionStartCoordinate, &TileRegionSize,
Unwrap(pBuffer), BufferStartOffsetInBytes, Flags);
}
}
else
{
Unwrap(pCommandList)
->CopyTiles(Unwrap(pTiledResource), &TileRegionStartCoordinate, &TileRegionSize,
Unwrap(pBuffer), BufferStartOffsetInBytes, Flags);
GetCrackedList()->CopyTiles(Unwrap(pTiledResource), &TileRegionStartCoordinate,
&TileRegionSize, Unwrap(pBuffer), BufferStartOffsetInBytes, Flags);
{
m_Cmd->AddEvent();
ResourceId liveSrc = GetResID(pBuffer);
ResourceId liveDst = GetResID(pTiledResource);
if(Flags & D3D12_TILE_COPY_FLAG_SWIZZLED_TILED_RESOURCE_TO_LINEAR_BUFFER)
std::swap(liveSrc, liveDst);
ResourceId origSrc = GetResourceManager()->GetOriginalID(liveSrc);
ResourceId origDst = GetResourceManager()->GetOriginalID(liveDst);
DrawcallDescription draw;
draw.name = StringFormat::Fmt("CopyTiles(src=%s, dst=%s)", ToStr(origDst).c_str(),
ToStr(origSrc).c_str());
draw.flags |= DrawFlags::Copy;
draw.copySource = origSrc;
draw.copyDestination = origDst;
Subresource tileSub = Subresource(
GetMipForSubresource(pTiledResource, TileRegionStartCoordinate.Subresource),
GetSliceForSubresource(pTiledResource, TileRegionStartCoordinate.Subresource));
if(Flags & D3D12_TILE_COPY_FLAG_SWIZZLED_TILED_RESOURCE_TO_LINEAR_BUFFER)
draw.copySourceSubresource = tileSub;
else
draw.copyDestinationSubresource = tileSub;
m_Cmd->AddDrawcall(draw, true);
D3D12DrawcallTreeNode &drawNode = m_Cmd->GetDrawcallStack().back()->children.back();
drawNode.resourceUsage.push_back(
make_rdcpair(liveSrc, EventUsage(drawNode.draw.eventId, ResourceUsage::CopySrc)));
drawNode.resourceUsage.push_back(
make_rdcpair(liveDst, EventUsage(drawNode.draw.eventId, ResourceUsage::CopyDst)));
}
}
}
return true;
}
@@ -5360,9 +5432,22 @@ void WrappedID3D12GraphicsCommandList::CopyTiles(
const D3D12_TILE_REGION_SIZE *pTileRegionSize, ID3D12Resource *pBuffer,
UINT64 BufferStartOffsetInBytes, D3D12_TILE_COPY_FLAGS Flags)
{
D3D12NOTIMP("Tiled Resources");
m_pList->CopyTiles(Unwrap(pTiledResource), pTileRegionStartCoordinate, pTileRegionSize,
Unwrap(pBuffer), BufferStartOffsetInBytes, Flags);
SERIALISE_TIME_CALL(m_pList->CopyTiles(Unwrap(pTiledResource), pTileRegionStartCoordinate,
pTileRegionSize, Unwrap(pBuffer), BufferStartOffsetInBytes,
Flags));
if(IsCaptureMode(m_State))
{
CACHE_THREAD_SERIALISER();
ser.SetDrawChunk();
SCOPED_SERIALISE_CHUNK(D3D12Chunk::List_CopyTiles);
Serialise_CopyTiles(ser, pTiledResource, pTileRegionStartCoordinate, pTileRegionSize, pBuffer,
BufferStartOffsetInBytes, Flags);
m_ListRecord->AddChunk(scope.Get(m_ListRecord->cmdInfo->alloc));
m_ListRecord->MarkResourceFrameReferenced(GetResID(pTiledResource), eFrameRef_PartialWrite);
m_ListRecord->MarkResourceFrameReferenced(GetResID(pBuffer), eFrameRef_Read);
}
}
#pragma endregion Copies
@@ -156,6 +156,8 @@ class WrappedID3D12CommandQueue : public ID3D12CommandQueue,
rdcarray<D3D12ResourceRecord *> m_CmdListRecords;
std::unordered_set<ResourceId> m_SparseBindResources;
// D3D12 guarantees that queues are thread-safe
Threading::CriticalSection m_Lock;
@@ -196,6 +198,11 @@ public:
uint32_t GetMaxEID() { return m_Cmd.m_Events.back().eventId; }
void ClearAfterCapture();
bool IsSparseUpdatedResource(ResourceId id) const
{
return m_SparseBindResources.find(id) != m_SparseBindResources.end();
}
ReplayStatus ReplayLog(CaptureState readType, uint32_t startEventID, uint32_t endEventID,
bool partial);
void SetFrameReader(StreamReader *reader) { m_FrameReader = reader; }
@@ -38,7 +38,33 @@ bool WrappedID3D12CommandQueue::Serialise_UpdateTileMappings(
const D3D12_TILE_RANGE_FLAGS *pRangeFlags, const UINT *pHeapRangeStartOffsets,
const UINT *pRangeTileCounts, D3D12_TILE_MAPPING_FLAGS Flags)
{
D3D12NOTIMP("Tiled Resources");
ID3D12CommandQueue *pQueue = this;
SERIALISE_ELEMENT(pQueue);
SERIALISE_ELEMENT(pResource);
SERIALISE_ELEMENT(NumResourceRegions);
SERIALISE_ELEMENT_ARRAY(pResourceRegionStartCoordinates, NumResourceRegions);
SERIALISE_ELEMENT_ARRAY(pResourceRegionSizes, NumResourceRegions);
SERIALISE_ELEMENT(pHeap);
SERIALISE_ELEMENT(NumRanges);
SERIALISE_ELEMENT_ARRAY(pRangeFlags, NumRanges);
SERIALISE_ELEMENT_ARRAY(pHeapRangeStartOffsets, NumRanges);
SERIALISE_ELEMENT_ARRAY(pRangeTileCounts, NumRanges);
SERIALISE_ELEMENT(Flags);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
if(IsLoading(m_State))
m_SparseBindResources.insert(GetResID(pResource));
// don't replay with NO_HAZARD
m_pReal->UpdateTileMappings(Unwrap(pResource), NumResourceRegions,
pResourceRegionStartCoordinates, pResourceRegionSizes,
Unwrap(pHeap), NumRanges, pRangeFlags, pHeapRangeStartOffsets,
pRangeTileCounts, Flags & ~D3D12_TILE_MAPPING_FLAG_NO_HAZARD);
}
return true;
}
@@ -49,10 +75,257 @@ void STDMETHODCALLTYPE WrappedID3D12CommandQueue::UpdateTileMappings(
const D3D12_TILE_RANGE_FLAGS *pRangeFlags, const UINT *pHeapRangeStartOffsets,
const UINT *pRangeTileCounts, D3D12_TILE_MAPPING_FLAGS Flags)
{
D3D12NOTIMP("Tiled Resources");
m_pReal->UpdateTileMappings(Unwrap(pResource), NumResourceRegions, pResourceRegionStartCoordinates,
pResourceRegionSizes, Unwrap(pHeap), NumRanges, pRangeFlags,
pHeapRangeStartOffsets, pRangeTileCounts, Flags);
SERIALISE_TIME_CALL(m_pReal->UpdateTileMappings(
Unwrap(pResource), NumResourceRegions, pResourceRegionStartCoordinates, pResourceRegionSizes,
Unwrap(pHeap), NumRanges, pRangeFlags, pHeapRangeStartOffsets, pRangeTileCounts, Flags));
if(IsActiveCapturing(m_State))
{
CACHE_THREAD_SERIALISER();
ser.SetDrawChunk();
SCOPED_SERIALISE_CHUNK(D3D12Chunk::Queue_UpdateTileMappings);
Serialise_UpdateTileMappings(ser, pResource, NumResourceRegions, pResourceRegionStartCoordinates,
pResourceRegionSizes, pHeap, NumRanges, pRangeFlags,
pHeapRangeStartOffsets, pRangeTileCounts, Flags);
m_QueueRecord->AddChunk(scope.Get());
GetResourceManager()->MarkResourceFrameReferenced(GetResID(pResource), eFrameRef_Read);
GetResourceManager()->MarkResourceFrameReferenced(GetResID(pHeap), eFrameRef_Read);
}
// update our internal page tables
if(IsCaptureMode(m_State))
{
Sparse::PageTable &pageTable = *GetRecord(pResource)->sparseTable;
ResourceId memoryId = GetResID(pHeap);
// register this heap as having been used for sparse binding
m_pDevice->AddSparseHeap(GetResID(pHeap));
// define macros to help provide the defaults for NULL arrays
#define REGION_START(i) \
(pResourceRegionStartCoordinates ? pResourceRegionStartCoordinates[i] \
: D3D12_TILED_RESOURCE_COORDINATE({0, 0, 0, 0}))
// The default for size depends on whether a co-ordinate is set (ughhhh). If we do have co-ordinates
// then the sizes are all 1 tile. If we don't, then the size is the whole resource. Ideally we'd
// provide the exact number of tiles, but instead we just set ~0U and the sparse table interprets
// this as 'unbounded tiles'
#define REGION_SIZE(i) \
(pResourceRegionSizes \
? pResourceRegionSizes[i] \
: (pResourceRegionStartCoordinates ? D3D12_TILE_REGION_SIZE({1, FALSE, 1, 1, 1}) \
: D3D12_TILE_REGION_SIZE({~0U, FALSE, 1, 1, 1})))
#define RANGE_FLAGS(i) (pRangeFlags ? pRangeFlags[i] : D3D12_TILE_RANGE_FLAG_NONE)
// don't think there is any default for this one, but we just return 0 for consistency and safety
// since the array CAN be NULL when it's ignored
#define RANGE_OFFSET(i) (pHeapRangeStartOffsets ? pHeapRangeStartOffsets[i] : 0)
#define RANGE_SIZE(i) (pRangeTileCounts ? pRangeTileCounts[i] : ~0U)
const UINT pageSize = 64 * 1024;
const Sparse::Coord texelShape = pageTable.getPageTexelSize();
// this persists from loop to loop. The effective offset is rangeBaseOffset +
// curRelativeRangeOffset. That allows us to partially use a range in one region then another.
// This goes from 0 to whatever rangeSize is
UINT curRelativeRangeOffset = 0;
// iterate region at a time
UINT curRange = 0;
for(UINT curRegion = 0; curRegion < NumResourceRegions && curRange < NumRanges; curRegion++)
{
D3D12_TILED_RESOURCE_COORDINATE regionStart = REGION_START(curRegion);
D3D12_TILE_REGION_SIZE regionSize = REGION_SIZE(curRegion);
// sanitise the region size according to the dimensions of the texture
// clamp inputs that may be invalid for buffers or 2D to sensible values
regionSize.Width = RDCCLAMP(1U, regionSize.Width, pageTable.getResourceSize().x);
regionSize.Height =
(uint16_t)RDCCLAMP(1U, (uint32_t)regionSize.Height, pageTable.getResourceSize().y);
regionSize.Depth =
(uint16_t)RDCCLAMP(1U, (uint32_t)regionSize.Depth, pageTable.getResourceSize().z);
UINT rangeBaseOffset = RANGE_OFFSET(0);
UINT rangeSize = RANGE_SIZE(curRange);
D3D12_TILE_RANGE_FLAGS rangeFlags = RANGE_FLAGS(curRange);
// get the memory ID, respecting the NULL flag
ResourceId memId = memoryId;
if(rangeFlags & D3D12_TILE_RANGE_FLAG_NULL)
memId = ResourceId();
// store if we're skipping for this range
bool skip = false;
if(rangeFlags & D3D12_TILE_RANGE_FLAG_SKIP)
skip = true;
// take the current range offset (which might be partway into the current range even at
// the start of a region). Unless we're re-using a single tile in which case it's always the
// start of the region
bool singlePage = false;
uint32_t memoryOffsetInTiles = rangeBaseOffset + curRelativeRangeOffset;
if(rangeFlags & D3D12_TILE_RANGE_FLAG_REUSE_SINGLE_TILE)
{
memoryOffsetInTiles = RANGE_OFFSET(curRange);
singlePage = true;
}
// if the region is a box region, contained within a subresource
if(regionSize.UseBox)
{
// if this region is entirely within the current range, set it as one
if(regionSize.NumTiles <= rangeSize)
{
if(skip)
{
// do no binding if we're skipping, because this whole range covers the region
}
else
{
pageTable.setImageBoxRange(
regionStart.Subresource, {regionStart.X * texelShape.x, regionStart.Y * texelShape.y,
regionStart.Z * texelShape.z},
{regionSize.Width * texelShape.x, regionSize.Height * texelShape.y,
regionSize.Depth * texelShape.z},
memId, memoryOffsetInTiles * pageSize, singlePage);
}
// consume the number of tiles in the range, which might not be all of them
curRelativeRangeOffset += regionSize.NumTiles;
// however if it is, then move to the next range. We don't need to reset most range
// parameters because they'll be refreshed on the next region, however the exception is
// the range offset which is persistent region-to-region because we might use only part of
// a range on one region.
if(curRelativeRangeOffset >= rangeSize)
{
curRange++;
curRelativeRangeOffset = 0;
if(curRange < NumRanges)
rangeBaseOffset = RANGE_OFFSET(curRange);
}
// we're done with this region, we'll loop around now
}
// if the region isn't contained within a single range, iterate tile-by-tile
else
{
// the region spans multiple ranges. Fall back to tile-by-tile setting
for(UINT z = 0; z < regionSize.Depth; z++)
{
for(UINT y = 0; y < regionSize.Height; y++)
{
for(UINT x = 0; x < regionSize.Width; x++)
{
if(skip)
{
// do nothing
}
else
{
pageTable.setImageBoxRange(
regionStart.Subresource,
{(regionStart.X + x) * texelShape.x, (regionStart.Y + y) * texelShape.y,
(regionStart.Z + z) * texelShape.z},
texelShape, memId, memoryOffsetInTiles * pageSize, singlePage);
}
// consume one tile, and also advance the memory offset if we're not in single page
// mode
curRelativeRangeOffset += 1;
if(!singlePage)
memoryOffsetInTiles += 1;
// if we've consumed everything in the current range, move to the next one
if(curRelativeRangeOffset >= rangeSize)
{
curRange++;
curRelativeRangeOffset = 0;
if(curRange < NumRanges)
{
rangeBaseOffset = RANGE_OFFSET(curRange);
rangeFlags = RANGE_FLAGS(curRange);
rangeSize = RANGE_SIZE(curRange);
skip = false;
if(rangeFlags & D3D12_TILE_RANGE_FLAG_SKIP)
skip = true;
memId = memoryId;
if(rangeFlags & D3D12_TILE_RANGE_FLAG_NULL)
memId = ResourceId();
memoryOffsetInTiles = rangeBaseOffset + curRelativeRangeOffset;
singlePage = false;
if(rangeFlags & D3D12_TILE_RANGE_FLAG_REUSE_SINGLE_TILE)
singlePage = true;
}
}
}
}
}
// done with the x,y,z loop. Continue to the next region. We handled any range wrapping in
// the innermost loop so we don't have to do anything here
}
}
// the region isn't a box region, so it can wrap
else
{
// set up the starting co-ord. setImageWrappedRange will help us iterate from here
rdcpair<uint32_t, Sparse::Coord> curCoord = {
regionStart.Subresource,
{regionStart.X * texelShape.x, regionStart.Y * texelShape.y,
regionStart.Z * texelShape.z}};
// consume a region at a time setting it. The page table will handle detecting any
// whole-subresource sets
for(UINT i = 0; i < regionSize.NumTiles;)
{
// we consume either the rest of the range or the rest of the region, whichever is least
UINT tilesToConsume = RDCMIN(regionSize.NumTiles - i, rangeSize - curRelativeRangeOffset);
RDCASSERT(tilesToConsume > 0);
curCoord = pageTable.setImageWrappedRange(
curCoord.first, curCoord.second, tilesToConsume * pageSize, memId,
memoryOffsetInTiles * pageSize, singlePage, !skip);
// consume the number of tiles from the region and range
i += tilesToConsume;
curRelativeRangeOffset += tilesToConsume;
// if we've consumed everything in the current range, move to the next one
if(curRelativeRangeOffset >= rangeSize)
{
curRange++;
curRelativeRangeOffset = 0;
if(curRange < NumRanges)
{
rangeBaseOffset = RANGE_OFFSET(curRange);
rangeFlags = RANGE_FLAGS(curRange);
rangeSize = RANGE_SIZE(curRange);
skip = false;
if(rangeFlags & D3D12_TILE_RANGE_FLAG_SKIP)
skip = true;
memId = memoryId;
if(rangeFlags & D3D12_TILE_RANGE_FLAG_NULL)
memId = ResourceId();
memoryOffsetInTiles = rangeBaseOffset + curRelativeRangeOffset;
singlePage = false;
if(rangeFlags & D3D12_TILE_RANGE_FLAG_REUSE_SINGLE_TILE)
singlePage = true;
}
}
}
}
}
}
}
template <typename SerialiserType>
@@ -62,7 +335,28 @@ bool WrappedID3D12CommandQueue::Serialise_CopyTileMappings(
const D3D12_TILED_RESOURCE_COORDINATE *pSrcRegionStartCoordinate,
const D3D12_TILE_REGION_SIZE *pRegionSize, D3D12_TILE_MAPPING_FLAGS Flags)
{
D3D12NOTIMP("Tiled Resources");
ID3D12CommandQueue *pQueue = this;
SERIALISE_ELEMENT(pQueue);
SERIALISE_ELEMENT(pDstResource);
SERIALISE_ELEMENT_LOCAL(DstRegionStartCoordinate, *pDstRegionStartCoordinate);
SERIALISE_ELEMENT(pSrcResource);
SERIALISE_ELEMENT_LOCAL(SrcRegionStartCoordinate, *pSrcRegionStartCoordinate);
SERIALISE_ELEMENT_LOCAL(RegionSize, *pRegionSize);
SERIALISE_ELEMENT(Flags);
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
if(IsLoading(m_State))
m_SparseBindResources.insert(GetResID(pDstResource));
// don't replay with NO_HAZARD
m_pReal->CopyTileMappings(Unwrap(pDstResource), &DstRegionStartCoordinate, Unwrap(pSrcResource),
&SrcRegionStartCoordinate, &RegionSize,
Flags & ~D3D12_TILE_MAPPING_FLAG_NO_HAZARD);
}
return true;
}
@@ -71,9 +365,69 @@ void STDMETHODCALLTYPE WrappedID3D12CommandQueue::CopyTileMappings(
ID3D12Resource *pSrcResource, const D3D12_TILED_RESOURCE_COORDINATE *pSrcRegionStartCoordinate,
const D3D12_TILE_REGION_SIZE *pRegionSize, D3D12_TILE_MAPPING_FLAGS Flags)
{
D3D12NOTIMP("Tiled Resources");
m_pReal->CopyTileMappings(Unwrap(pDstResource), pDstRegionStartCoordinate, Unwrap(pSrcResource),
pSrcRegionStartCoordinate, pRegionSize, Flags);
SERIALISE_TIME_CALL(m_pReal->CopyTileMappings(Unwrap(pDstResource), pDstRegionStartCoordinate,
Unwrap(pSrcResource), pSrcRegionStartCoordinate,
pRegionSize, Flags));
if(IsActiveCapturing(m_State))
{
CACHE_THREAD_SERIALISER();
ser.SetDrawChunk();
SCOPED_SERIALISE_CHUNK(D3D12Chunk::Queue_CopyTileMappings);
Serialise_CopyTileMappings(ser, pDstResource, pDstRegionStartCoordinate, pSrcResource,
pSrcRegionStartCoordinate, pRegionSize, Flags);
m_QueueRecord->AddChunk(scope.Get());
GetResourceManager()->MarkResourceFrameReferenced(GetResID(pDstResource), eFrameRef_Read);
GetResourceManager()->MarkResourceFrameReferenced(GetResID(pSrcResource), eFrameRef_Read);
}
// update our internal page tables
if(IsCaptureMode(m_State))
{
Sparse::PageTable tmp;
// if we're moving within a subresource the regions can overlap. Take a temporary copy for the
// source
if(pSrcResource == pDstResource)
tmp = *GetRecord(pSrcResource)->sparseTable;
const Sparse::PageTable &srcPageTable =
(pSrcResource == pDstResource) ? tmp : *GetRecord(pSrcResource)->sparseTable;
Sparse::PageTable &dstPageTable = *GetRecord(pDstResource)->sparseTable;
const UINT srcSub = pSrcRegionStartCoordinate->Subresource;
const UINT dstSub = pDstRegionStartCoordinate->Subresource;
if(pRegionSize->UseBox)
{
D3D12_TILE_REGION_SIZE size = *pRegionSize;
if(pRegionSize->Width == 0)
return;
// clamp inputs that may be invalid for buffers or 2D to sensible values
size.Width = RDCCLAMP(1U, pRegionSize->Width, dstPageTable.getResourceSize().x);
size.Height =
(uint16_t)RDCCLAMP(1U, (uint32_t)pRegionSize->Height, dstPageTable.getResourceSize().y);
size.Depth =
(uint16_t)RDCCLAMP(1U, (uint32_t)pRegionSize->Depth, dstPageTable.getResourceSize().z);
dstPageTable.copyImageBoxRange(
dstSub,
{pDstRegionStartCoordinate->X, pDstRegionStartCoordinate->Y, pDstRegionStartCoordinate->Z},
{size.Width, size.Height, size.Depth}, srcPageTable, srcSub,
{pSrcRegionStartCoordinate->X, pSrcRegionStartCoordinate->Y, pSrcRegionStartCoordinate->Z});
}
else
{
dstPageTable.copyImageWrappedRange(
dstSub,
{pDstRegionStartCoordinate->X, pDstRegionStartCoordinate->Y, pDstRegionStartCoordinate->Z},
pRegionSize->NumTiles * 64 * 1024, srcPageTable, srcSub,
{pSrcRegionStartCoordinate->X, pSrcRegionStartCoordinate->Y, pSrcRegionStartCoordinate->Z});
}
}
}
template <typename SerialiserType>
@@ -387,7 +741,7 @@ void WrappedID3D12CommandQueue::ExecuteCommandListsInternal(UINT NumCommandLists
m_Lock.Lock();
bool capframe = IsActiveCapturing(m_State);
std::set<ResourceId> refdIDs;
std::unordered_set<ResourceId> refdIDs;
for(UINT i = 0; i < NumCommandLists; i++)
{
@@ -531,7 +885,8 @@ void WrappedID3D12CommandQueue::ExecuteCommandListsInternal(UINT NumCommandLists
byte *ref = res->GetShadow(subres);
byte *data = res->GetMap(subres);
// check we actually have map data. It's possible that over the course of the loop iteration
// check we actually have map data. It's possible that over the course of the loop
// iteration
// the resource has been unmapped on another thread before we got here.
if(data)
{
@@ -570,6 +925,76 @@ void WrappedID3D12CommandQueue::ExecuteCommandListsInternal(UINT NumCommandLists
res->UnlockMaps();
}
std::unordered_set<ResourceId> sparsePageHeaps;
std::unordered_set<ResourceId> sparseResources;
// this returns the list of current live sparse resources, and the list of heaps *that have
// ever been used for sparse binding*. The latter list may be way too big, in which case we
// look at the referenced sparse resources and pull in the heaps they are currently using.
// However many applications may use only a few large heaps for sparse binding so if the
// list
// is small enough then we just use it directly even if technically some heaps may not be
// used
// by any resources we are referencing.
m_pDevice->GetSparseResources(sparseResources, sparsePageHeaps);
if(sparsePageHeaps.size() > refdIDs.size() || sparsePageHeaps.size() > sparseResources.size())
{
// intersect sparse resources with ref'd IDs, and pull in the referenced heaps from its
// current page table
const std::unordered_set<ResourceId> &smaller =
sparseResources.size() < refdIDs.size() ? sparseResources : refdIDs;
const std::unordered_set<ResourceId> &larger =
sparseResources.size() >= refdIDs.size() ? sparseResources : refdIDs;
for(const ResourceId id : smaller)
{
if(larger.find(id) != larger.end())
{
D3D12ResourceRecord *record = GetResourceManager()->GetResourceRecord(id);
RDCASSERT(record->sparseTable);
const Sparse::PageTable &table = *record->sparseTable;
for(uint32_t sub = 0; sub < RDCMAX(1U, table.getNumSubresources());)
{
const Sparse::PageRangeMapping &mapping = table.isSubresourceInMipTail(sub)
? table.getMipTailMapping(sub)
: table.getSubresource(sub);
if(mapping.hasSingleMapping())
{
if(mapping.singleMapping.memory != ResourceId())
sparsePageHeaps.insert(mapping.singleMapping.memory);
}
else
{
// this is a huge perf cliff as we've lost any batching and we perform as badly as
// if every page was mapped to a different resource, so we hope applications don't
// hit this often.
for(const Sparse::Page &page : mapping.pages)
{
sparsePageHeaps.insert(page.memory);
}
}
if(table.isSubresourceInMipTail(sub))
{
// move to the next subresource after the miptail, since we handle the miptail all
// at once
sub = ((sub / table.getMipCount()) + 1) * table.getMipCount();
}
else
{
sub++;
}
}
}
}
}
for(const ResourceId id : sparsePageHeaps)
GetResourceManager()->MarkResourceFrameReferenced(id, eFrameRef_Read);
{
WriteSerialiser &ser = GetThreadSerialiser();
ser.SetDrawChunk();
@@ -858,7 +1283,8 @@ HRESULT STDMETHODCALLTYPE WrappedID3D12CommandQueue::Present(
if(m_pPresentHWND != NULL)
{
// don't let the device actually release any refs on the resource, just make it release internal
// don't let the device actually release any refs on the resource, just make it release
// internal
// resources
m_pPresentSource->AddRef();
m_pDevice->ReleaseSwapchainResources(this, 0, NULL, NULL);
+4
View File
@@ -223,6 +223,10 @@ bool D3D12InitParams::IsSupportedVersion(uint64_t ver)
if(ver == 0x9)
return true;
// 0xA -> 0xB - Added support for sparse/reserved/tiled resources
if(ver == 0x9)
return true;
return false;
}
+3
View File
@@ -665,6 +665,9 @@ DECLARE_REFLECTION_ENUM(D3D12_SHADER_VISIBILITY);
DECLARE_REFLECTION_ENUM(D3D12_STATIC_BORDER_COLOR);
DECLARE_REFLECTION_ENUM(D3D12_DESCRIPTOR_RANGE_TYPE);
DECLARE_REFLECTION_ENUM(D3D12_DESCRIPTOR_RANGE_FLAGS);
DECLARE_REFLECTION_ENUM(D3D12_TILE_COPY_FLAGS);
DECLARE_REFLECTION_ENUM(D3D12_TILE_RANGE_FLAGS);
DECLARE_REFLECTION_ENUM(D3D12_TILE_MAPPING_FLAGS);
DECLARE_REFLECTION_STRUCT(D3D12_RESOURCE_DESC);
DECLARE_REFLECTION_STRUCT(D3D12_COMMAND_QUEUE_DESC);
+4 -2
View File
@@ -1222,8 +1222,10 @@ void D3D12DebugManager::GetBufferData(ID3D12Resource *buffer, uint64_t offset, u
return;
D3D12_RESOURCE_DESC desc = buffer->GetDesc();
D3D12_HEAP_PROPERTIES heapProps;
buffer->GetHeapProperties(&heapProps, NULL);
D3D12_HEAP_PROPERTIES heapProps = {};
// can't call GetHeapProperties on sparse resources
if(!m_pDevice->IsSparseResource(GetResID(buffer)))
buffer->GetHeapProperties(&heapProps, NULL);
if(offset >= desc.Width)
{
+6
View File
@@ -2626,6 +2626,12 @@ void WrappedID3D12Device::ReleaseResource(ID3D12DeviceChild *res)
m_ResourceStates.erase(id);
}
{
SCOPED_LOCK(m_SparseLock);
m_SparseResources.erase(id);
m_SparseHeaps.erase(id);
}
D3D12ResourceRecord *record = GetRecord(res);
if(record)
+24 -1
View File
@@ -52,7 +52,7 @@ struct D3D12InitParams
uint32_t VendorUAVSpace = ~0U;
// check if a frame capture section version is supported
static const uint64_t CurrentVersion = 0xA;
static const uint64_t CurrentVersion = 0xB;
static bool IsSupportedVersion(uint64_t ver);
};
@@ -612,6 +612,10 @@ private:
Threading::CriticalSection m_MapsLock;
rdcarray<MapState> m_Maps;
Threading::CriticalSection m_SparseLock;
std::unordered_set<ResourceId> m_SparseResources;
std::unordered_set<ResourceId> m_SparseHeaps;
Threading::CriticalSection m_WrapDeduplicateLock;
bool ProcessChunk(ReadSerialiser &ser, D3D12Chunk context);
@@ -1049,6 +1053,25 @@ public:
}
void CheckForDeath();
void GetSparseResources(std::unordered_set<ResourceId> &resources,
std::unordered_set<ResourceId> &heaps)
{
SCOPED_LOCK(m_SparseLock);
resources = m_SparseResources;
heaps = m_SparseHeaps;
}
bool IsSparseResource(ResourceId id)
{
SCOPED_LOCK_OPTIONAL(m_SparseLock, IsCaptureMode(m_State));
return m_SparseResources.find(id) != m_SparseResources.end();
}
void AddSparseHeap(ResourceId heap)
{
SCOPED_LOCK(m_SparseLock);
m_SparseHeaps.insert(heap);
}
void ReleaseResource(ID3D12DeviceChild *pResource);
// helper function that takes an expanded descriptor, but downcasts it to the regular descriptor
+215 -23
View File
@@ -1817,8 +1817,102 @@ bool WrappedID3D12Device::Serialise_CreateReservedResource(
SerialiserType &ser, const D3D12_RESOURCE_DESC *pDesc, D3D12_RESOURCE_STATES InitialState,
const D3D12_CLEAR_VALUE *pOptimizedClearValue, REFIID riid, void **ppvResource)
{
D3D12NOTIMP("Tiled Resources");
APIProps.SparseResources = true;
SERIALISE_ELEMENT_LOCAL(Descriptor, *pDesc).Named("pDesc"_lit);
SERIALISE_ELEMENT(InitialState);
SERIALISE_ELEMENT_OPT(pOptimizedClearValue);
SERIALISE_ELEMENT_LOCAL(guid, riid).Named("riid"_lit);
SERIALISE_ELEMENT_LOCAL(pResource, ((WrappedID3D12Resource *)*ppvResource)->GetResourceID())
.TypedAs("ID3D12Resource *"_lit);
SERIALISE_ELEMENT_LOCAL(gpuAddress,
((WrappedID3D12Resource *)*ppvResource)->GetGPUVirtualAddressIfBuffer())
.Hidden();
SERIALISE_CHECK_READ_ERRORS();
if(IsReplayingAndReading())
{
APIProps.SparseResources = true;
if(Descriptor.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)
{
GPUAddressRange range;
range.start = gpuAddress;
range.end = gpuAddress + Descriptor.Width;
range.id = pResource;
m_GPUAddresses.AddTo(range);
}
APIProps.YUVTextures |= IsYUVFormat(Descriptor.Format);
// always allow SRVs on replay so we can inspect resources
Descriptor.Flags &= ~D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE;
ID3D12Resource *ret = NULL;
HRESULT hr = m_pDevice->CreateReservedResource(&Descriptor, InitialState, pOptimizedClearValue,
guid, (void **)&ret);
if(FAILED(hr))
{
RDCERR("Failed on resource serialise-creation, HRESULT: %s", ToStr(hr).c_str());
return false;
}
else
{
SetObjName(ret,
StringFormat::Fmt("Reserved Resource %s %s", ToStr(Descriptor.Dimension).c_str(),
ToStr(pResource).c_str()));
ret = new WrappedID3D12Resource(ret, this);
GetResourceManager()->AddLiveResource(pResource, ret);
SubresourceStateVector &states = m_ResourceStates[GetResID(ret)];
states.fill(GetNumSubresources(m_pDevice, &Descriptor), InitialState);
}
m_SparseResources.insert(GetResID(ret));
ResourceType type = ResourceType::Texture;
const char *prefix = "Texture";
if(Descriptor.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)
{
type = ResourceType::Buffer;
prefix = "Buffer";
}
else if(Descriptor.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE1D)
{
prefix = Descriptor.DepthOrArraySize > 1 ? "1D TextureArray" : "1D Texture";
if(Descriptor.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET)
prefix = "1D Render Target";
else if(Descriptor.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)
prefix = "1D Depth Target";
}
else if(Descriptor.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D)
{
prefix = Descriptor.DepthOrArraySize > 1 ? "2D TextureArray" : "2D Texture";
if(Descriptor.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET)
prefix = "2D Render Target";
else if(Descriptor.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)
prefix = "2D Depth Target";
}
else if(Descriptor.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D)
{
prefix = "3D Texture";
if(Descriptor.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET)
prefix = "3D Render Target";
else if(Descriptor.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)
prefix = "3D Depth Target";
}
AddResource(pResource, type, prefix);
}
return true;
}
@@ -1827,8 +1921,125 @@ HRESULT WrappedID3D12Device::CreateReservedResource(const D3D12_RESOURCE_DESC *p
const D3D12_CLEAR_VALUE *pOptimizedClearValue,
REFIID riid, void **ppvResource)
{
RDCERR("Tiled Resources are not currently implemented on D3D12");
return E_NOINTERFACE;
if(ppvResource == NULL)
return m_pDevice->CreateReservedResource(pDesc, InitialState, pOptimizedClearValue, riid, NULL);
if(riid != __uuidof(ID3D12Resource) && riid != __uuidof(ID3D12Resource1) &&
riid != __uuidof(ID3D12Resource2))
return E_NOINTERFACE;
const D3D12_RESOURCE_DESC *pCreateDesc = pDesc;
D3D12_RESOURCE_DESC localDesc;
if(pDesc && pDesc->Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D && pDesc->SampleDesc.Count > 1)
{
localDesc = *pDesc;
// need to be able to create SRVs of MSAA textures to copy out their contents
localDesc.Flags &= ~D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE;
pCreateDesc = &localDesc;
}
ID3D12Resource *real = NULL;
HRESULT ret;
SERIALISE_TIME_CALL(
ret = m_pDevice->CreateReservedResource(pCreateDesc, InitialState, pOptimizedClearValue,
__uuidof(ID3D12Resource), (void **)&real));
if(SUCCEEDED(ret))
{
WrappedID3D12Resource *wrapped = new WrappedID3D12Resource(real, this);
if(IsCaptureMode(m_State))
{
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CHUNK(D3D12Chunk::Device_CreateReservedResource);
Serialise_CreateReservedResource(ser, pDesc, InitialState, pOptimizedClearValue, riid,
(void **)&wrapped);
D3D12ResourceRecord *record = GetResourceManager()->AddResourceRecord(wrapped->GetResourceID());
record->type = Resource_Resource;
record->Length = 0;
wrapped->SetResourceRecord(record);
record->m_MapsCount = GetNumSubresources(this, pDesc);
record->m_Maps = new D3D12ResourceRecord::MapData[record->m_MapsCount];
const UINT pageSize = 64 * 1024;
if(pDesc->Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)
{
record->sparseTable = new Sparse::PageTable;
record->sparseTable->Initialise(pDesc->Width, pageSize);
}
else
{
D3D12_PACKED_MIP_INFO mipTail = {};
D3D12_TILE_SHAPE tileShape = {};
m_pDevice->GetResourceTiling(wrapped->GetReal(), NULL, &mipTail, &tileShape, NULL, 0, NULL);
UINT texDepth = 1;
UINT texSlices = pDesc->DepthOrArraySize;
if(pDesc->Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D)
{
texDepth = pDesc->DepthOrArraySize;
texSlices = 1;
}
RDCASSERT(mipTail.NumStandardMips + mipTail.NumPackedMips == pDesc->MipLevels,
mipTail.NumStandardMips, mipTail.NumPackedMips, pDesc->MipLevels);
record->sparseTable = new Sparse::PageTable;
record->sparseTable->Initialise(
{(uint32_t)pDesc->Width, pDesc->Height, texDepth}, pDesc->MipLevels, texSlices,
pageSize, {tileShape.WidthInTexels, tileShape.HeightInTexels, tileShape.DepthInTexels},
mipTail.NumStandardMips, mipTail.StartTileIndexInOverallResource * pageSize,
(mipTail.StartTileIndexInOverallResource + mipTail.NumTilesForPackedMips) * pageSize,
mipTail.NumTilesForPackedMips * pageSize * texSlices);
}
{
SCOPED_LOCK(m_SparseLock);
m_SparseResources.insert(wrapped->GetResourceID());
}
record->AddChunk(scope.Get());
GetResourceManager()->MarkDirtyResource(wrapped->GetResourceID());
}
else
{
GetResourceManager()->AddLiveResource(wrapped->GetResourceID(), wrapped);
}
{
SCOPED_LOCK(m_ResourceStatesLock);
SubresourceStateVector &states = m_ResourceStates[wrapped->GetResourceID()];
states.fill(GetNumSubresources(m_pDevice, pDesc), InitialState);
}
if(riid == __uuidof(ID3D12Resource))
*ppvResource = (ID3D12Resource *)wrapped;
else if(riid == __uuidof(ID3D12Resource1))
*ppvResource = (ID3D12Resource1 *)wrapped;
else if(riid == __uuidof(ID3D12Resource2))
*ppvResource = (ID3D12Resource2 *)wrapped;
// while actively capturing we keep all buffers around to prevent the address lookup from
// losing addresses we might need (or the manageable but annoying problem of an address being
// re-used)
{
SCOPED_READLOCK(m_CapTransitionLock);
if(IsActiveCapturing(m_State))
{
wrapped->AddRef();
m_RefBuffers.push_back(wrapped);
}
}
}
return ret;
}
template <typename SerialiserType>
@@ -2955,26 +3166,7 @@ HRESULT WrappedID3D12Device::CheckFeatureSupport(D3D12_FEATURE Feature, void *pF
return hr;
}
else if(Feature == D3D12_FEATURE_D3D12_OPTIONS)
{
HRESULT hr = m_pDevice->CheckFeatureSupport(Feature, pFeatureSupportData, FeatureSupportDataSize);
if(SUCCEEDED(hr))
{
D3D12_FEATURE_DATA_D3D12_OPTIONS *opts =
(D3D12_FEATURE_DATA_D3D12_OPTIONS *)pFeatureSupportData;
if(FeatureSupportDataSize != sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS))
return E_INVALIDARG;
// renderdoc doesn't support tiled resources (calls to CreateReservedResource will fail), so
// don't report it as supported
opts->TiledResourcesTier = D3D12_TILED_RESOURCES_TIER_NOT_SUPPORTED;
return S_OK;
}
return hr;
}
return m_pDevice->CheckFeatureSupport(Feature, pFeatureSupportData, FeatureSupportDataSize);
}
+171 -11
View File
@@ -59,10 +59,14 @@ bool D3D12ResourceManager::Prepare_InitialState(ID3D12DeviceChild *res)
D3D12_RESOURCE_DESC desc = r->GetDesc();
D3D12InitialContents initContents;
if(desc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)
{
D3D12_HEAP_PROPERTIES heapProps;
r->GetHeapProperties(&heapProps, NULL);
D3D12_HEAP_PROPERTIES heapProps = {};
if(GetRecord(r)->sparseTable == NULL)
r->GetHeapProperties(&heapProps, NULL);
HRESULT hr = S_OK;
@@ -167,8 +171,7 @@ bool D3D12ResourceManager::Prepare_InitialState(ID3D12DeviceChild *res)
#endif
}
SetInitialContents(GetResID(r), D3D12InitialContents(copyDst));
return true;
initContents = D3D12InitialContents(copyDst);
}
else
{
@@ -364,9 +367,16 @@ bool D3D12ResourceManager::Prepare_InitialState(ID3D12DeviceChild *res)
SAFE_RELEASE(arrayTexture);
SAFE_DELETE_ARRAY(layouts);
SetInitialContents(GetResID(r), D3D12InitialContents(copyDst));
return true;
initContents = D3D12InitialContents(copyDst);
}
if(GetRecord(r)->sparseTable)
{
initContents.sparseTable = new Sparse::PageTable(*GetRecord(r)->sparseTable);
}
SetInitialContents(GetResID(r), initContents);
return true;
}
else
{
@@ -394,7 +404,13 @@ uint64_t D3D12ResourceManager::GetSize_InitialState(ResourceId id, const D3D12In
if(data.tag == D3D12InitialContents::MapDirect)
return WriteSerialiser::GetChunkAlignment() + 16 + uint64_t(data.dataSize);
return WriteSerialiser::GetChunkAlignment() + 16 + uint64_t(buf ? buf->GetDesc().Width : 0);
uint64_t ret =
WriteSerialiser::GetChunkAlignment() + 16 + uint64_t(buf ? buf->GetDesc().Width : 0);
if(data.sparseTable)
ret += 16 + data.sparseTable->GetSerialiseSize();
return ret;
}
else
{
@@ -404,6 +420,121 @@ uint64_t D3D12ResourceManager::GetSize_InitialState(ResourceId id, const D3D12In
return 16;
}
SparseBinds::SparseBinds(const Sparse::PageTable &table)
{
const uint32_t pageSize = 64 * 1024;
// in theory some of these subresources may share a single binding but we don't try to extract
// that out again. If we can get one bind per subresource and avoid falling down to per-page
// mappings we're happy
for(uint32_t sub = 0; sub < RDCMAX(1U, table.getNumSubresources());)
{
const Sparse::PageRangeMapping &mapping =
table.isSubresourceInMipTail(sub) ? table.getMipTailMapping(sub) : table.getSubresource(sub);
if(mapping.hasSingleMapping())
{
Bind bind;
bind.heap = mapping.singleMapping.memory;
bind.rangeOffset = uint32_t(mapping.singleMapping.offset / pageSize);
bind.rangeCount = uint32_t(table.isSubresourceInMipTail(sub)
? table.getMipTailSliceSize() / pageSize
: table.getSubresourceByteSize(sub) / pageSize);
bind.regionStart = {0, 0, 0, sub};
bind.regionSize = {bind.rangeCount, FALSE, bind.rangeCount, 1, 1};
bind.rangeFlag = D3D12_TILE_RANGE_FLAG_NONE;
if(bind.heap == ResourceId())
bind.rangeFlag = D3D12_TILE_RANGE_FLAG_NULL;
else if(mapping.singlePageReused)
bind.rangeFlag = D3D12_TILE_RANGE_FLAG_REUSE_SINGLE_TILE;
binds.push_back(bind);
}
else
{
Sparse::Coord texelShape = table.calcSubresourcePageDim(sub);
// march the pages for this subresource in linear order
for(uint32_t page = 0; page < mapping.pages.size(); page++)
{
Bind bind;
bind.heap = mapping.pages[page].memory;
bind.rangeOffset = uint32_t(mapping.pages[page].offset / pageSize);
// do simple coalescing. If the previous bind was in the same heap, one tile back, make it
// cover this tile
if(page > 0 && binds.back().heap == bind.heap &&
binds.back().rangeOffset + binds.back().rangeCount == bind.rangeOffset)
{
binds.back().regionSize.NumTiles++;
binds.back().regionSize.Width++;
binds.back().rangeCount++;
continue;
}
// otherwise add a new bind
if(table.isSubresourceInMipTail(sub))
{
bind.regionStart = {page, 0, 0, sub};
}
else
{
bind.regionStart.Subresource = sub;
// set the starting co-ord as appropriate for this page
bind.regionStart.X = page % texelShape.x;
bind.regionStart.Y = (page / texelShape.x) % texelShape.y;
bind.regionStart.Z = page / (texelShape.x * texelShape.y);
}
bind.rangeCount = 1;
bind.regionSize = {1, FALSE, 1, 1, 1};
bind.rangeFlag = D3D12_TILE_RANGE_FLAG_NONE;
if(bind.heap == ResourceId())
bind.rangeFlag = D3D12_TILE_RANGE_FLAG_NULL;
binds.push_back(bind);
}
}
if(table.isSubresourceInMipTail(sub))
{
// move to the next subresource after the miptail, since we handle the miptail all at once
sub = ((sub / table.getMipCount()) + 1) * table.getMipCount();
}
else
{
sub++;
}
}
}
SparseBinds::SparseBinds(int)
{
null = true;
}
void SparseBinds::Apply(WrappedID3D12Device *device, ID3D12Resource *resource)
{
if(null)
{
D3D12_TILE_RANGE_FLAGS rangeFlags = D3D12_TILE_RANGE_FLAG_NULL;
// do a single whole-resource bind of NULL
device->GetQueue()->UpdateTileMappings(Unwrap(resource), 1, NULL, NULL, NULL, 1, &rangeFlags,
NULL, NULL, D3D12_TILE_MAPPING_FLAG_NONE);
}
else
{
D3D12ResourceManager *rm = device->GetResourceManager();
for(const Bind &bind : binds)
{
device->GetQueue()->UpdateTileMappings(
resource, 1, &bind.regionStart, &bind.regionSize,
bind.heap == ResourceId() ? NULL : (ID3D12Heap *)rm->GetLiveResource(bind.heap), 1,
&bind.rangeFlag, &bind.rangeOffset, &bind.rangeCount, D3D12_TILE_MAPPING_FLAG_NONE);
}
}
}
template <typename SerialiserType>
bool D3D12ResourceManager::Serialise_InitialState(SerialiserType &ser, ResourceId id,
D3D12ResourceRecord *record,
@@ -494,6 +625,18 @@ bool D3D12ResourceManager::Serialise_InitialState(SerialiserType &ser, ResourceI
liveRes = (ID3D12Resource *)GetLiveResource(id);
}
SparseBinds *sparseBinds = NULL;
if(ser.VersionAtLeast(0xB))
{
Sparse::PageTable *sparseTable = initial ? initial->sparseTable : NULL;
SERIALISE_ELEMENT_OPT(sparseTable);
if(sparseTable)
sparseBinds = new SparseBinds(*sparseTable);
}
if(ser.IsWriting())
{
m_Device->ExecuteLists(NULL, true);
@@ -536,7 +679,8 @@ bool D3D12ResourceManager::Serialise_InitialState(SerialiserType &ser, ResourceI
D3D12_RESOURCE_DESC resDesc = liveRes->GetDesc();
D3D12_HEAP_PROPERTIES heapProps = {};
liveRes->GetHeapProperties(&heapProps, NULL);
if(!m_Device->IsSparseResource(GetResID(liveRes)))
liveRes->GetHeapProperties(&heapProps, NULL);
if(heapProps.Type == D3D12_HEAP_TYPE_UPLOAD)
{
@@ -624,6 +768,8 @@ bool D3D12ResourceManager::Serialise_InitialState(SerialiserType &ser, ResourceI
initContents.resourceType = Resource_Resource;
initContents.resource = mappedBuffer;
initContents.sparseBinds = sparseBinds;
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
@@ -639,7 +785,8 @@ bool D3D12ResourceManager::Serialise_InitialState(SerialiserType &ser, ResourceI
else
{
D3D12_HEAP_PROPERTIES heapProps = {};
liveRes->GetHeapProperties(&heapProps, NULL);
if(!m_Device->IsSparseResource(GetResID(liveRes)))
liveRes->GetHeapProperties(&heapProps, NULL);
ID3D12GraphicsCommandList *list = Unwrap(m_Device->GetInitialStateList());
@@ -806,7 +953,8 @@ void D3D12ResourceManager::Create_InitialState(ResourceId id, ID3D12DeviceChild
D3D12_RESOURCE_DESC resDesc = res->GetDesc();
D3D12_HEAP_PROPERTIES heapProps = {};
res->GetHeapProperties(&heapProps, NULL);
if(!m_Device->IsSparseResource(GetResID(live)))
res->GetHeapProperties(&heapProps, NULL);
if(heapProps.Type == D3D12_HEAP_TYPE_UPLOAD)
{
@@ -855,6 +1003,10 @@ void D3D12ResourceManager::Create_InitialState(ResourceId id, ID3D12DeviceChild
D3D12InitialContents initContents(D3D12InitialContents::ForceCopy, type);
initContents.resourceType = Resource_Resource;
initContents.resource = copy;
if(m_Device->IsSparseResource(GetResID(live)))
initContents.sparseBinds = new SparseBinds(0);
SetInitialContents(id, initContents);
}
}
@@ -896,7 +1048,15 @@ void D3D12ResourceManager::Apply_InitialState(ID3D12DeviceChild *live,
}
D3D12_HEAP_PROPERTIES heapProps = {};
copyDst->GetHeapProperties(&heapProps, NULL);
if(data.sparseBinds)
{
if(IsLoading(m_State) || m_Device->GetQueue()->IsSparseUpdatedResource(GetResID(live)))
data.sparseBinds->Apply(m_Device, (ID3D12Resource *)live);
}
else
{
copyDst->GetHeapProperties(&heapProps, NULL);
}
// if destination is on the upload heap, it's impossible to copy via the device,
// so we have to CPU copy. We assume that we detected this case above and never uploaded a
+51 -6
View File
@@ -27,6 +27,7 @@
#include "common/wrapped_pool.h"
#include "core/core.h"
#include "core/resource_manager.h"
#include "core/sparse_page_table.h"
#include "driver/d3d12/d3d12_common.h"
#include "serialise/serialiser.h"
@@ -493,6 +494,7 @@ struct D3D12ResourceRecord : public ResourceRecord
type(Resource_Unknown),
ContainsExecuteIndirect(false),
cmdInfo(NULL),
sparseTable(NULL),
m_Maps(NULL),
m_MapsCount(0),
bakedCommands(NULL)
@@ -501,6 +503,7 @@ struct D3D12ResourceRecord : public ResourceRecord
~D3D12ResourceRecord()
{
SAFE_DELETE(cmdInfo);
SAFE_DELETE(sparseTable);
SAFE_DELETE_ARRAY(m_Maps);
}
void Bake()
@@ -517,6 +520,7 @@ struct D3D12ResourceRecord : public ResourceRecord
bool ContainsExecuteIndirect;
D3D12ResourceRecord *bakedCommands;
CmdListRecordingInfo *cmdInfo;
Sparse::PageTable *sparseTable;
struct MapData
{
@@ -533,6 +537,29 @@ struct D3D12ResourceRecord : public ResourceRecord
typedef rdcarray<D3D12_RESOURCE_STATES> SubresourceStateVector;
struct SparseBinds
{
SparseBinds(const Sparse::PageTable &table);
// tagged constructor meaning 'null binds everywhere'
SparseBinds(int);
void Apply(WrappedID3D12Device *device, ID3D12Resource *resource);
private:
bool null = false;
struct Bind
{
ResourceId heap;
D3D12_TILED_RESOURCE_COORDINATE regionStart;
D3D12_TILE_REGION_SIZE regionSize;
D3D12_TILE_RANGE_FLAGS rangeFlag;
UINT rangeOffset;
UINT rangeCount;
};
rdcarray<Bind> binds;
};
struct D3D12InitialContents
{
enum Tag
@@ -551,7 +578,9 @@ struct D3D12InitialContents
numDescriptors(n),
resource(NULL),
srcData(NULL),
dataSize(0)
dataSize(0),
sparseTable(NULL),
sparseBinds(NULL)
{
}
D3D12InitialContents(ID3D12DescriptorHeap *r)
@@ -561,7 +590,9 @@ struct D3D12InitialContents
numDescriptors(0),
resource(r),
srcData(NULL),
dataSize(0)
dataSize(0),
sparseTable(NULL),
sparseBinds(NULL)
{
}
D3D12InitialContents(ID3D12Resource *r)
@@ -571,7 +602,9 @@ struct D3D12InitialContents
numDescriptors(0),
resource(r),
srcData(NULL),
dataSize(0)
dataSize(0),
sparseTable(NULL),
sparseBinds(NULL)
{
}
D3D12InitialContents(byte *data, size_t size)
@@ -581,7 +614,9 @@ struct D3D12InitialContents
numDescriptors(0),
resource(NULL),
srcData(data),
dataSize(size)
dataSize(size),
sparseTable(NULL),
sparseBinds(NULL)
{
}
D3D12InitialContents(Tag tg, D3D12ResourceType type)
@@ -591,7 +626,9 @@ struct D3D12InitialContents
numDescriptors(0),
resource(NULL),
srcData(NULL),
dataSize(0)
dataSize(0),
sparseTable(NULL),
sparseBinds(NULL)
{
}
D3D12InitialContents()
@@ -601,13 +638,16 @@ struct D3D12InitialContents
numDescriptors(0),
resource(NULL),
srcData(NULL),
dataSize(0)
dataSize(0),
sparseTable(NULL),
sparseBinds(NULL)
{
}
template <typename Configuration>
void Free(ResourceManager<Configuration> *rm)
{
SAFE_DELETE_ARRAY(descriptors);
SAFE_DELETE(sparseTable);
SAFE_RELEASE(resource);
FreeAlignedBuffer(srcData);
}
@@ -619,6 +659,11 @@ struct D3D12InitialContents
ID3D12DeviceChild *resource;
byte *srcData;
size_t dataSize;
// 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
SparseBinds *sparseBinds;
};
struct D3D12ResourceManagerConfiguration
-1
View File
@@ -1005,7 +1005,6 @@ public:
// replay interface
bool Prepare_InitialState(WrappedVkRes *res);
uint64_t GetSize_InitialState(ResourceId id, const VkInitialContents &initial);
uint64_t GetSize_SparseInitialState(ResourceId id, const VkInitialContents &initial);
template <typename SerialiserType>
bool Serialise_InitialState(SerialiserType &ser, ResourceId id, VkResourceRecord *record,
const VkInitialContents *initial);
@@ -840,7 +840,7 @@ void WrappedVulkan::CaptureQueueSubmit(VkQueue queue,
bool capframe = IsActiveCapturing(m_State);
bool backframe = IsBackgroundCapturing(m_State);
std::set<ResourceId> refdIDs;
std::unordered_set<ResourceId> refdIDs;
std::set<VkDescriptorSet> descriptorSets;