Cache expanded views from large immutable D3D12 descriptor heaps

* The principle here is that on D3D12 it's common to have huge descriptor heaps
  in which only a handful of descriptors are changed. By caching the expanded
  D3D12Pipe::View results from these unchanging descriptors we don't have to do
  that over and over. Any descriptors written in the captured frame may have
  different contents depending on where we've replayed up to, so we don't cache
  them to keep things simple rather than invalidating the cache.
This commit is contained in:
baldurk
2022-11-08 17:42:18 +00:00
parent ffe09f1cb0
commit 742e3de252
7 changed files with 169 additions and 77 deletions
+12 -15
View File
@@ -269,6 +269,10 @@ struct View
DOCUMENT("The :class:`ResourceId` of the underlying resource the view refers to.");
ResourceId resourceId;
DOCUMENT("The :class:`ResourceId` of the resource where the hidden buffer counter is stored.");
ResourceId counterResourceId;
DOCUMENT("The byte offset in :data:`counterResourceId` where the counter is stored.");
uint32_t counterByteOffset = 0;
DOCUMENT("The :class:`TextureType` of the view type.");
TextureType type;
DOCUMENT(R"(The format cast that the view uses.
@@ -293,6 +297,14 @@ since single descriptors may only be dynamically skipped by control flow.
bool dynamicallyUsed = true;
DOCUMENT("The :class:`D3DBufferViewFlags` set for the buffer.");
D3DBufferViewFlags bufferFlags = D3DBufferViewFlags::NoFlags;
DOCUMENT("Valid for textures - the highest mip that is available through the view.");
uint8_t firstMip = 0;
DOCUMENT("Valid for textures - the number of mip levels in the view.");
uint8_t numMips = 1;
DOCUMENT("Valid for texture arrays or 3D textures - the first slice available through the view.");
uint16_t firstSlice = 0;
DOCUMENT("Valid for texture arrays or 3D textures - the number of slices in the view.");
uint16_t numSlices = 1;
DOCUMENT("If the view has a hidden counter, this stores the current value of the counter.");
uint32_t bufferStructCount = 0;
DOCUMENT(R"(The byte size of a single element in the view. Either the byte size of
@@ -304,21 +316,6 @@ since single descriptors may only be dynamically skipped by control flow.
DOCUMENT("Valid for buffers - the number of elements to be used in the view.");
uint32_t numElements = 1;
DOCUMENT("The :class:`ResourceId` of the resource where the hidden buffer counter is stored.");
ResourceId counterResourceId;
DOCUMENT("The byte offset in :data:`counterResourceId` where the counter is stored.");
uint64_t counterByteOffset = 0;
DOCUMENT("Valid for textures - the highest mip that is available through the view.");
uint32_t firstMip = 0;
DOCUMENT("Valid for textures - the number of mip levels in the view.");
uint32_t numMips = 1;
DOCUMENT("Valid for texture arrays or 3D textures - the first slice available through the view.");
uint32_t firstSlice = 0;
DOCUMENT("Valid for texture arrays or 3D textures - the number of slices in the view.");
uint32_t numSlices = 1;
DOCUMENT("The minimum mip-level clamp applied when sampling this texture.");
float minLODClamp = 0.0f;
};
+1 -1
View File
@@ -734,7 +734,7 @@ DOCUMENT(R"(The dimensionality of a texture binding.
A Cubemap texture array.
)");
enum class TextureType : uint32_t
enum class TextureType : uint16_t
{
Unknown,
First = Unknown,
@@ -1176,6 +1176,7 @@ bool WrappedID3D12Device::Serialise_DynamicDescriptorWrite(SerialiserType &ser,
// be undefined
RDCASSERT(desc.GetType() != D3D12DescriptorType::Undefined);
desc.Create(D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES, this, *handle);
handle->GetHeap()->MarkMutableView(handle->GetHeapIndex());
}
}
@@ -2552,7 +2553,10 @@ bool WrappedID3D12Device::Serialise_DynamicDescriptorCopies(
{
// not optimal, but simple for now. Do a wrapped copy so that internal tracking is also updated
for(const DynamicDescriptorCopy &copy : DescriptorCopies)
{
CopyDescriptorsSimple(1, *copy.dst, *copy.src, copy.type);
copy.dst->GetHeap()->MarkMutableView(copy.dst->GetHeapIndex());
}
}
return true;
+70 -58
View File
@@ -660,10 +660,19 @@ void D3D12Replay::FillResourceView(D3D12Pipe::View &view, const D3D12Descriptor
return;
}
if(desc->GetHeap()->HasValidViewCache(desc->GetHeapIndex()))
{
desc->GetHeap()->GetFromViewCache(desc->GetHeapIndex(), view);
return;
}
view.resourceId = rm->GetOriginalID(desc->GetResResourceId());
if(view.resourceId == ResourceId())
{
desc->GetHeap()->SetToViewCache(desc->GetHeapIndex(), view);
return;
}
D3D12_RESOURCE_DESC res;
@@ -690,30 +699,30 @@ void D3D12Replay::FillResourceView(D3D12Pipe::View &view, const D3D12Descriptor
}
else if(rtv.ViewDimension == D3D12_RTV_DIMENSION_TEXTURE1D)
{
view.firstMip = rtv.Texture1D.MipSlice;
view.firstMip = rtv.Texture1D.MipSlice & 0xff;
view.numMips = 1;
view.firstSlice = 0;
view.numSlices = 1;
}
else if(rtv.ViewDimension == D3D12_RTV_DIMENSION_TEXTURE1DARRAY)
{
view.firstMip = rtv.Texture1DArray.MipSlice;
view.firstMip = rtv.Texture1DArray.MipSlice & 0xff;
view.numMips = 1;
view.numSlices = rtv.Texture1DArray.ArraySize;
view.firstSlice = rtv.Texture1DArray.FirstArraySlice;
view.numSlices = rtv.Texture1DArray.ArraySize & 0xffff;
view.firstSlice = rtv.Texture1DArray.FirstArraySlice & 0xffff;
}
else if(rtv.ViewDimension == D3D12_RTV_DIMENSION_TEXTURE2D)
{
view.firstMip = rtv.Texture2D.MipSlice;
view.firstMip = rtv.Texture2D.MipSlice & 0xff;
view.numMips = 1;
view.firstSlice = 0;
view.numSlices = 1;
}
else if(rtv.ViewDimension == D3D12_RTV_DIMENSION_TEXTURE2DARRAY)
{
view.numSlices = rtv.Texture2DArray.ArraySize;
view.firstSlice = rtv.Texture2DArray.FirstArraySlice;
view.firstMip = rtv.Texture2DArray.MipSlice;
view.numSlices = rtv.Texture2DArray.ArraySize & 0xffff;
view.firstSlice = rtv.Texture2DArray.FirstArraySlice & 0xffff;
view.firstMip = rtv.Texture2DArray.MipSlice & 0xff;
view.numMips = 1;
}
else if(rtv.ViewDimension == D3D12_RTV_DIMENSION_TEXTURE2DMS)
@@ -727,14 +736,14 @@ void D3D12Replay::FillResourceView(D3D12Pipe::View &view, const D3D12Descriptor
{
view.firstMip = 0;
view.numMips = 1;
view.numSlices = rtv.Texture2DMSArray.ArraySize;
view.firstSlice = rtv.Texture2DMSArray.FirstArraySlice;
view.numSlices = rtv.Texture2DMSArray.ArraySize & 0xffff;
view.firstSlice = rtv.Texture2DMSArray.FirstArraySlice & 0xffff;
}
else if(rtv.ViewDimension == D3D12_RTV_DIMENSION_TEXTURE3D)
{
view.numSlices = rtv.Texture3D.WSize;
view.firstSlice = rtv.Texture3D.FirstWSlice;
view.firstMip = rtv.Texture3D.MipSlice;
view.numSlices = rtv.Texture3D.WSize & 0xffff;
view.firstSlice = rtv.Texture3D.FirstWSlice & 0xffff;
view.firstMip = rtv.Texture3D.MipSlice & 0xff;
view.numMips = 1;
}
}
@@ -748,31 +757,31 @@ void D3D12Replay::FillResourceView(D3D12Pipe::View &view, const D3D12Descriptor
if(dsv.ViewDimension == D3D12_DSV_DIMENSION_TEXTURE1D)
{
view.firstMip = dsv.Texture1D.MipSlice;
view.firstMip = dsv.Texture1D.MipSlice & 0xff;
}
else if(dsv.ViewDimension == D3D12_DSV_DIMENSION_TEXTURE1DARRAY)
{
view.numSlices = dsv.Texture1DArray.ArraySize;
view.firstSlice = dsv.Texture1DArray.FirstArraySlice;
view.firstMip = dsv.Texture1DArray.MipSlice;
view.numSlices = dsv.Texture1DArray.ArraySize & 0xffff;
view.firstSlice = dsv.Texture1DArray.FirstArraySlice & 0xffff;
view.firstMip = dsv.Texture1DArray.MipSlice & 0xff;
}
else if(dsv.ViewDimension == D3D12_DSV_DIMENSION_TEXTURE2D)
{
view.firstMip = dsv.Texture2D.MipSlice;
view.firstMip = dsv.Texture2D.MipSlice & 0xff;
}
else if(dsv.ViewDimension == D3D12_DSV_DIMENSION_TEXTURE2DARRAY)
{
view.numSlices = dsv.Texture2DArray.ArraySize;
view.firstSlice = dsv.Texture2DArray.FirstArraySlice;
view.firstMip = dsv.Texture2DArray.MipSlice;
view.numSlices = dsv.Texture2DArray.ArraySize & 0xffff;
view.firstSlice = dsv.Texture2DArray.FirstArraySlice & 0xffff;
view.firstMip = dsv.Texture2DArray.MipSlice & 0xff;
}
else if(dsv.ViewDimension == D3D12_DSV_DIMENSION_TEXTURE2DMS)
{
}
else if(dsv.ViewDimension == D3D12_DSV_DIMENSION_TEXTURE2DMSARRAY)
{
view.numSlices = dsv.Texture2DMSArray.ArraySize;
view.firstSlice = dsv.Texture2DMSArray.FirstArraySlice;
view.numSlices = dsv.Texture2DMSArray.ArraySize & 0xffff;
view.firstSlice = dsv.Texture2DMSArray.FirstArraySlice & 0xffff;
}
}
else if(desc->GetType() == D3D12DescriptorType::SRV)
@@ -806,30 +815,30 @@ void D3D12Replay::FillResourceView(D3D12Pipe::View &view, const D3D12Descriptor
}
else if(srv.ViewDimension == D3D12_SRV_DIMENSION_TEXTURE1D)
{
view.firstMip = srv.Texture1D.MostDetailedMip;
view.numMips = srv.Texture1D.MipLevels;
view.firstMip = srv.Texture1D.MostDetailedMip & 0xff;
view.numMips = srv.Texture1D.MipLevels & 0xff;
view.minLODClamp = srv.Texture1D.ResourceMinLODClamp;
}
else if(srv.ViewDimension == D3D12_SRV_DIMENSION_TEXTURE1DARRAY)
{
view.numSlices = srv.Texture1DArray.ArraySize;
view.firstSlice = srv.Texture1DArray.FirstArraySlice;
view.firstMip = srv.Texture1DArray.MostDetailedMip;
view.numMips = srv.Texture1DArray.MipLevels;
view.numSlices = srv.Texture1DArray.ArraySize & 0xffff;
view.firstSlice = srv.Texture1DArray.FirstArraySlice & 0xffff;
view.firstMip = srv.Texture1DArray.MostDetailedMip & 0xff;
view.numMips = srv.Texture1DArray.MipLevels & 0xff;
view.minLODClamp = srv.Texture1DArray.ResourceMinLODClamp;
}
else if(srv.ViewDimension == D3D12_SRV_DIMENSION_TEXTURE2D)
{
view.firstMip = srv.Texture2D.MostDetailedMip;
view.numMips = srv.Texture2D.MipLevels;
view.firstMip = srv.Texture2D.MostDetailedMip & 0xff;
view.numMips = srv.Texture2D.MipLevels & 0xff;
view.minLODClamp = srv.Texture2D.ResourceMinLODClamp;
}
else if(srv.ViewDimension == D3D12_SRV_DIMENSION_TEXTURE2DARRAY)
{
view.numSlices = srv.Texture2DArray.ArraySize;
view.firstSlice = srv.Texture2DArray.FirstArraySlice;
view.firstMip = srv.Texture2DArray.MostDetailedMip;
view.numMips = srv.Texture2DArray.MipLevels;
view.numSlices = srv.Texture2DArray.ArraySize & 0xffff;
view.firstSlice = srv.Texture2DArray.FirstArraySlice & 0xffff;
view.firstMip = srv.Texture2DArray.MostDetailedMip & 0xff;
view.numMips = srv.Texture2DArray.MipLevels & 0xff;
view.minLODClamp = srv.Texture2DArray.ResourceMinLODClamp;
}
else if(srv.ViewDimension == D3D12_SRV_DIMENSION_TEXTURE2DMS)
@@ -837,28 +846,28 @@ void D3D12Replay::FillResourceView(D3D12Pipe::View &view, const D3D12Descriptor
}
else if(srv.ViewDimension == D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY)
{
view.numSlices = srv.Texture2DMSArray.ArraySize;
view.firstSlice = srv.Texture2DMSArray.FirstArraySlice;
view.numSlices = srv.Texture2DMSArray.ArraySize & 0xffff;
view.firstSlice = srv.Texture2DMSArray.FirstArraySlice & 0xffff;
}
else if(srv.ViewDimension == D3D12_SRV_DIMENSION_TEXTURE3D)
{
view.firstMip = srv.Texture3D.MostDetailedMip;
view.numMips = srv.Texture3D.MipLevels;
view.firstMip = srv.Texture3D.MostDetailedMip & 0xff;
view.numMips = srv.Texture3D.MipLevels & 0xff;
view.minLODClamp = srv.Texture3D.ResourceMinLODClamp;
}
else if(srv.ViewDimension == D3D12_SRV_DIMENSION_TEXTURECUBE)
{
view.numSlices = 6;
view.firstMip = srv.TextureCube.MostDetailedMip;
view.numMips = srv.TextureCube.MipLevels;
view.firstMip = srv.TextureCube.MostDetailedMip & 0xff;
view.numMips = srv.TextureCube.MipLevels & 0xff;
view.minLODClamp = srv.TextureCube.ResourceMinLODClamp;
}
else if(srv.ViewDimension == D3D12_SRV_DIMENSION_TEXTURECUBEARRAY)
{
view.numSlices = srv.TextureCubeArray.NumCubes * 6;
view.firstSlice = srv.TextureCubeArray.First2DArrayFace;
view.firstMip = srv.TextureCubeArray.MostDetailedMip;
view.numMips = srv.TextureCubeArray.MipLevels;
view.numSlices = (srv.TextureCubeArray.NumCubes * 6) & 0xffff;
view.firstSlice = srv.TextureCubeArray.First2DArrayFace & 0xffff;
view.firstMip = srv.TextureCubeArray.MostDetailedMip & 0xff;
view.numMips = srv.TextureCubeArray.MipLevels & 0xff;
view.minLODClamp = srv.TextureCube.ResourceMinLODClamp;
}
}
@@ -884,43 +893,44 @@ void D3D12Replay::FillResourceView(D3D12Pipe::View &view, const D3D12Descriptor
if(uav.Buffer.StructureByteStride > 0)
view.elementByteSize = uav.Buffer.StructureByteStride;
view.counterByteOffset = uav.Buffer.CounterOffsetInBytes;
view.counterByteOffset = uav.Buffer.CounterOffsetInBytes & 0xffffffff;
RDCASSERT(uav.Buffer.CounterOffsetInBytes < 0xffffffff);
if(view.counterResourceId != ResourceId())
{
bytebuf counterVal;
GetDebugManager()->GetBufferData(
rm->GetCurrentAs<ID3D12Resource>(desc->GetCounterResourceId()),
view.counterByteOffset, 4, counterVal);
uav.Buffer.CounterOffsetInBytes, 4, counterVal);
uint32_t *val = (uint32_t *)&counterVal[0];
view.bufferStructCount = *val;
}
}
else if(uav.ViewDimension == D3D12_UAV_DIMENSION_TEXTURE1D)
{
view.firstMip = uav.Texture1D.MipSlice;
view.firstMip = uav.Texture1D.MipSlice & 0xff;
}
else if(uav.ViewDimension == D3D12_UAV_DIMENSION_TEXTURE1DARRAY)
{
view.numSlices = uav.Texture1DArray.ArraySize;
view.firstSlice = uav.Texture1DArray.FirstArraySlice;
view.firstMip = uav.Texture1DArray.MipSlice;
view.numSlices = uav.Texture1DArray.ArraySize & 0xffff;
view.firstSlice = uav.Texture1DArray.FirstArraySlice & 0xffff;
view.firstMip = uav.Texture1DArray.MipSlice & 0xff;
}
else if(uav.ViewDimension == D3D12_UAV_DIMENSION_TEXTURE2D)
{
view.firstMip = uav.Texture2D.MipSlice;
view.firstMip = uav.Texture2D.MipSlice & 0xff;
}
else if(uav.ViewDimension == D3D12_UAV_DIMENSION_TEXTURE2DARRAY)
{
view.numSlices = uav.Texture2DArray.ArraySize;
view.firstSlice = uav.Texture2DArray.FirstArraySlice;
view.firstMip = uav.Texture2DArray.MipSlice;
view.numSlices = uav.Texture2DArray.ArraySize & 0xffff;
view.firstSlice = uav.Texture2DArray.FirstArraySlice & 0xffff;
view.firstMip = uav.Texture2DArray.MipSlice & 0xff;
}
else if(uav.ViewDimension == D3D12_UAV_DIMENSION_TEXTURE3D)
{
view.numSlices = uav.Texture3D.WSize;
view.firstSlice = uav.Texture3D.FirstWSlice;
view.firstMip = uav.Texture3D.MipSlice;
view.numSlices = uav.Texture3D.WSize & 0xffff;
view.firstSlice = uav.Texture3D.FirstWSlice & 0xffff;
view.firstMip = uav.Texture3D.MipSlice & 0xff;
}
}
@@ -932,6 +942,8 @@ void D3D12Replay::FillResourceView(D3D12Pipe::View &view, const D3D12Descriptor
view.viewFormat = MakeResourceFormat(fmt);
}
desc->GetHeap()->SetToViewCache(desc->GetHeapIndex(), view);
}
void D3D12Replay::FillSampler(D3D12Pipe::Sampler &samp, const D3D12_SAMPLER_DESC &sampDesc)
@@ -350,6 +350,57 @@ rdcarray<ID3D12Resource *> WrappedID3D12Resource::AddRefBuffersBeforeCapture(D3D
return ret;
}
bool WrappedID3D12DescriptorHeap::HasValidViewCache(uint32_t index)
{
if(!mutableViewBitmask)
return false;
// don't cache mutable views. In theory we could but we'd need to know which ones were modified
// mid-frame, to mark the cache as stale when initial contents are re-applied. This optimisation
// is aimed at the assumption of a huge number of descriptors that don't change so we just don't
// cache ones that change mid-frame
if((mutableViewBitmask[index / 64] & (1ULL << (index % 64))) != 0)
return false;
// anything that's not mutable is valid once it's been set at least once. Since we
// zero-initialise, we use bind as a flag (it isn't retrieved from the cache since it depends on
// the binding)
return cachedViews[index].bind == 1;
}
void WrappedID3D12DescriptorHeap::MarkMutableView(uint32_t index)
{
if(!mutableViewBitmask)
return;
mutableViewBitmask[index / 64] |= (1ULL << (index % 64));
}
void WrappedID3D12DescriptorHeap::GetFromViewCache(uint32_t index, D3D12Pipe::View &view)
{
if(!mutableViewBitmask)
return;
bool dynamicallyUsed = view.dynamicallyUsed;
uint32_t bind = view.bind;
uint32_t tableIndex = view.tableIndex;
view = cachedViews[index];
view.dynamicallyUsed = dynamicallyUsed;
view.bind = bind;
view.tableIndex = tableIndex;
}
void WrappedID3D12DescriptorHeap::SetToViewCache(uint32_t index, const D3D12Pipe::View &view)
{
if(!mutableViewBitmask)
return;
cachedViews[index] = view;
// we re-use bind as the indicator that this view is valid
cachedViews[index].bind = 1;
mutableViewBitmask[index / 64] |= (1ULL << (index % 64));
}
WrappedID3D12DescriptorHeap::WrappedID3D12DescriptorHeap(ID3D12DescriptorHeap *real,
WrappedID3D12Device *device,
const D3D12_DESCRIPTOR_HEAP_DESC &desc,
@@ -368,12 +419,32 @@ WrappedID3D12DescriptorHeap::WrappedID3D12DescriptorHeap(ID3D12DescriptorHeap *r
RDCEraseMem(descriptors, sizeof(D3D12Descriptor) * desc.NumDescriptors);
for(UINT i = 0; i < desc.NumDescriptors; i++)
descriptors[i].Setup(this, i);
// only cache views for "large" descriptor heaps where we expect few will actually change
// mid-frame
if(IsReplayMode(device->GetState()) && desc.NumDescriptors > 1024)
{
size_t bitmaskSize = AlignUp(desc.NumDescriptors, 64U) / 64;
cachedViews = new D3D12Pipe::View[desc.NumDescriptors];
RDCEraseMem(cachedViews, sizeof(D3D12Pipe::View) * desc.NumDescriptors);
mutableViewBitmask = new uint64_t[bitmaskSize];
RDCEraseMem(mutableViewBitmask, sizeof(uint64_t) * bitmaskSize);
}
else
{
cachedViews = NULL;
mutableViewBitmask = NULL;
}
}
WrappedID3D12DescriptorHeap::~WrappedID3D12DescriptorHeap()
{
Shutdown();
SAFE_DELETE_ARRAY(descriptors);
SAFE_DELETE_ARRAY(cachedViews);
SAFE_DELETE_ARRAY(mutableViewBitmask);
}
void WrappedID3D12PipelineState::ShaderEntry::BuildReflection()
+8
View File
@@ -396,6 +396,9 @@ class WrappedID3D12DescriptorHeap : public WrappedDeviceChild12<ID3D12Descriptor
D3D12Descriptor *descriptors;
D3D12Pipe::View *cachedViews;
uint64_t *mutableViewBitmask;
public:
ALLOCATE_WITH_WRAPPED_POOL(WrappedID3D12DescriptorHeap);
@@ -410,6 +413,11 @@ public:
D3D12Descriptor *GetDescriptors() { return descriptors; }
UINT GetNumDescriptors() { return numDescriptors; }
bool HasValidViewCache(uint32_t index);
void MarkMutableView(uint32_t index);
void GetFromViewCache(uint32_t index, D3D12Pipe::View &view);
void SetToViewCache(uint32_t index, const D3D12Pipe::View &view);
//////////////////////////////
// implement ID3D12DescriptorHeap
+3 -3
View File
@@ -1424,7 +1424,7 @@ void DoSerialise(SerialiserType &ser, D3D12Pipe::View &el)
SERIALISE_MEMBER(minLODClamp);
SIZE_CHECK(96);
SIZE_CHECK(72);
}
template <typename SerialiserType>
@@ -1565,7 +1565,7 @@ void DoSerialise(SerialiserType &ser, D3D12Pipe::OM &el)
SERIALISE_MEMBER(multiSampleCount);
SERIALISE_MEMBER(multiSampleQuality);
SIZE_CHECK(264);
SIZE_CHECK(240);
}
template <typename SerialiserType>
@@ -1609,7 +1609,7 @@ void DoSerialise(SerialiserType &ser, D3D12Pipe::State &el)
SERIALISE_MEMBER(resourceStates);
SIZE_CHECK(1424);
SIZE_CHECK(1400);
}
#pragma endregion D3D12 pipeline state