From 742e3de252c6846d18b0dc09c227fc9654392f8e Mon Sep 17 00:00:00 2001 From: baldurk Date: Tue, 8 Nov 2022 17:42:18 +0000 Subject: [PATCH] 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. --- renderdoc/api/replay/d3d12_pipestate.h | 27 ++-- renderdoc/api/replay/replay_enums.h | 2 +- renderdoc/driver/d3d12/d3d12_device_wrap.cpp | 4 + renderdoc/driver/d3d12/d3d12_replay.cpp | 128 ++++++++++--------- renderdoc/driver/d3d12/d3d12_resources.cpp | 71 ++++++++++ renderdoc/driver/d3d12/d3d12_resources.h | 8 ++ renderdoc/replay/renderdoc_serialise.inl | 6 +- 7 files changed, 169 insertions(+), 77 deletions(-) diff --git a/renderdoc/api/replay/d3d12_pipestate.h b/renderdoc/api/replay/d3d12_pipestate.h index 13a003da9..87c368e5a 100644 --- a/renderdoc/api/replay/d3d12_pipestate.h +++ b/renderdoc/api/replay/d3d12_pipestate.h @@ -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; }; diff --git a/renderdoc/api/replay/replay_enums.h b/renderdoc/api/replay/replay_enums.h index 25b526bcd..9b49315aa 100644 --- a/renderdoc/api/replay/replay_enums.h +++ b/renderdoc/api/replay/replay_enums.h @@ -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, diff --git a/renderdoc/driver/d3d12/d3d12_device_wrap.cpp b/renderdoc/driver/d3d12/d3d12_device_wrap.cpp index 740392244..2354d9744 100644 --- a/renderdoc/driver/d3d12/d3d12_device_wrap.cpp +++ b/renderdoc/driver/d3d12/d3d12_device_wrap.cpp @@ -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 © : DescriptorCopies) + { CopyDescriptorsSimple(1, *copy.dst, *copy.src, copy.type); + copy.dst->GetHeap()->MarkMutableView(copy.dst->GetHeapIndex()); + } } return true; diff --git a/renderdoc/driver/d3d12/d3d12_replay.cpp b/renderdoc/driver/d3d12/d3d12_replay.cpp index 26c5793e2..3158e8df0 100644 --- a/renderdoc/driver/d3d12/d3d12_replay.cpp +++ b/renderdoc/driver/d3d12/d3d12_replay.cpp @@ -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(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) diff --git a/renderdoc/driver/d3d12/d3d12_resources.cpp b/renderdoc/driver/d3d12/d3d12_resources.cpp index 6b079765e..15b53c6d5 100644 --- a/renderdoc/driver/d3d12/d3d12_resources.cpp +++ b/renderdoc/driver/d3d12/d3d12_resources.cpp @@ -350,6 +350,57 @@ rdcarray 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() diff --git a/renderdoc/driver/d3d12/d3d12_resources.h b/renderdoc/driver/d3d12/d3d12_resources.h index 0a7da6db7..756d1f98a 100644 --- a/renderdoc/driver/d3d12/d3d12_resources.h +++ b/renderdoc/driver/d3d12/d3d12_resources.h @@ -396,6 +396,9 @@ class WrappedID3D12DescriptorHeap : public WrappedDeviceChild12 @@ -1565,7 +1565,7 @@ void DoSerialise(SerialiserType &ser, D3D12Pipe::OM &el) SERIALISE_MEMBER(multiSampleCount); SERIALISE_MEMBER(multiSampleQuality); - SIZE_CHECK(264); + SIZE_CHECK(240); } template @@ -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