mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-15 19:16:32 +00:00
Implement reserved (sparse) resources on D3D12. Closes #2203
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user