From d88aad8fc22a0c36f6347fa5a4da1f7e46ae3f7c Mon Sep 17 00:00:00 2001 From: baldurk Date: Thu, 14 Mar 2024 15:44:43 +0000 Subject: [PATCH] Add a query to determine logical identifiers for descriptors * This is a consideration for any cases where binding numbers are relevant - primarily D3D11 and GL - where the offset into an arbitrary (and possibly fake) descriptor storage is not helpful but knowing the register binding definitely is. * If someone wants to look at the raw descriptor contents without respect to a particular shader access they can use this query to determine a more useful 'name' for any given descriptor. On D3D11 and GL this gives the register number, on Vulkan it gives the binding number (and array element). On D3D12 it just repeats the offset effectively. --- qrenderdoc/Code/pyrenderdoc/renderdoc.i | 1 + renderdoc/api/replay/common_pipestate.h | 105 +++++++++++++++++++ renderdoc/api/replay/renderdoc_replay.h | 10 ++ renderdoc/api/replay/replay_enums.h | 2 +- renderdoc/core/image_viewer.cpp | 5 + renderdoc/core/replay_proxy.cpp | 35 +++++++ renderdoc/core/replay_proxy.h | 3 + renderdoc/driver/d3d11/d3d11_replay.cpp | 58 +++++++++++ renderdoc/driver/d3d11/d3d11_replay.h | 2 + renderdoc/driver/d3d12/d3d12_replay.cpp | 123 +++++++++++++++++++++++ renderdoc/driver/d3d12/d3d12_replay.h | 2 + renderdoc/driver/gl/gl_replay.cpp | 110 ++++++++++++++++++++ renderdoc/driver/gl/gl_replay.h | 2 + renderdoc/driver/vulkan/vk_info.h | 7 ++ renderdoc/driver/vulkan/vk_replay.cpp | 122 ++++++++++++++++++++++ renderdoc/driver/vulkan/vk_replay.h | 2 + renderdoc/replay/dummy_driver.cpp | 6 ++ renderdoc/replay/dummy_driver.h | 2 + renderdoc/replay/renderdoc_serialise.inl | 12 +++ renderdoc/replay/replay_controller.cpp | 8 ++ renderdoc/replay/replay_controller.h | 2 + renderdoc/replay/replay_driver.h | 2 + 22 files changed, 620 insertions(+), 1 deletion(-) diff --git a/qrenderdoc/Code/pyrenderdoc/renderdoc.i b/qrenderdoc/Code/pyrenderdoc/renderdoc.i index 5bcb13ec8..5692333da 100644 --- a/qrenderdoc/Code/pyrenderdoc/renderdoc.i +++ b/qrenderdoc/Code/pyrenderdoc/renderdoc.i @@ -416,6 +416,7 @@ TEMPLATE_ARRAY_INSTANTIATE(rdcarray, DescriptorRange) TEMPLATE_ARRAY_INSTANTIATE(rdcarray, Descriptor) TEMPLATE_ARRAY_INSTANTIATE(rdcarray, SamplerDescriptor) TEMPLATE_ARRAY_INSTANTIATE(rdcarray, DescriptorAccess) +TEMPLATE_ARRAY_INSTANTIATE(rdcarray, DescriptorLogicalLocation) TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, VKPipe, Attachment) TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, VKPipe, BindingElement) TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, VKPipe, DescriptorBinding) diff --git a/renderdoc/api/replay/common_pipestate.h b/renderdoc/api/replay/common_pipestate.h index 2a3011b24..208fe6b85 100644 --- a/renderdoc/api/replay/common_pipestate.h +++ b/renderdoc/api/replay/common_pipestate.h @@ -849,6 +849,111 @@ does not necessarily guarantee that the descriptor was accessed on the GPU durin DECLARE_REFLECTION_STRUCT(DescriptorAccess); +DOCUMENT(R"(In many cases there may be a logical location or fixed binding point for a particular +descriptor which is not conveyed with a simple byte offset into a descriptor store. +This is particularly true for any descriptor stores that are not equivalent to a buffer of bytes +but actually have an API structure - for example D3D11 and GL with fixed binding points, or Vulkan +with descriptor sets. + +In some cases on APIs with explicit descriptor storage this may convey information about virtualised +descriptors that are not explicitly backed with real storage. + +This structure describes such a location queried for a given descriptor. + +For example on D3D11 this would give the register number of the binding, and on GL it would give the +unit index. Both cases would be able to query the type and shader stage visibility of descriptors +that are not accessed or even bound. + +On Vulkan this would give the set, binding, and visibility. In most cases this information will be +available for all descriptors but in some cases the type of descriptor may not be available if it +is unused and has not been initialised. + +On D3D12 this would only give the index into the heap, as no other information is available purely +by the descriptor itself. + +.. note:: + + This information may not be fully present on all APIs so the returned structures may be empty or + partially filled out, depending on what information is relevant per API. +)"); +struct DescriptorLogicalLocation +{ + DOCUMENT(""); + DescriptorLogicalLocation() = default; + DescriptorLogicalLocation(const DescriptorLogicalLocation &) = default; + DescriptorLogicalLocation &operator=(const DescriptorLogicalLocation &) = default; + + bool operator==(const DescriptorLogicalLocation &o) const + { + return fixedBindNumber == o.fixedBindNumber && stageMask == o.stageMask && + category == o.category && logicalBindName == o.logicalBindName; + } + bool operator<(const DescriptorLogicalLocation &o) const + { + // note there are two different conflicting sorts that would be sensible here: + // stage > category > fixed bind would work best for D3D11/GL + // fixed bind first works better for Vulkan/D3D12. + // + // We assume users will access or sort D3D11/GL descriptors directly by binds if needed so are + // less likely to rely on this sort behaviour. + if(fixedBindNumber != o.fixedBindNumber) + return fixedBindNumber < o.fixedBindNumber; + if(stageMask != o.stageMask) + return stageMask < o.stageMask; + if(category != o.category) + return category < o.category; + if(logicalBindName != o.logicalBindName) + return logicalBindName < o.logicalBindName; + return false; + } + + DOCUMENT(R"(The set of shader stages that this descriptor is intrinsically available to. This is +primarily relevant for D3D11 with its fixed per-stage register binding points. + +Note this *only* shows if a descriptor itself can only ever be accessed by some shader stages by +definition, not if a descriptor is generally available but happened to only be accessed by one or +more stage. That information is available directly in the :class:`DescriptorAccess` itself. + +:type: ShaderStageMask +)"); + ShaderStageMask stageMask = ShaderStageMask::All; + + DOCUMENT(R"(The general category of a descriptor stored. This may not be available for +uninitialised descriptors on all APIs. + +:type: DescriptorCategory +)"); + DescriptorCategory category = DescriptorCategory::Unknown; + + DOCUMENT(R"(The fixed binding number for this descriptor. The interpretation of this is +API-specific and it is provided purely for informational purposes and has no bearing on how data +is accessed or described. + +Generally speaking sorting by this number will give a reasonable ordering by binding if it exists. + +.. note:: + Because this number is API-specific, there is no guarantee that it will be unique across all + descriptors. It should be used only within contexts that can interpret it API-specifically, or + else for purely informational/non-semantic purposes like sorting. + +:type: int +)"); + uint32_t fixedBindNumber = 0; + + DOCUMENT(R"(The logical binding name, as suitable for displaying to a user when displaying +the contents of a descriptor queried directly from a heap. + +Depending on the API, this name may be identical or less specific than the one obtained from +shader reflection. Generally speaking it's preferred to use any information from shader reflection +first, and fall back to this name if no reflection information is available in the context. + +:type: str +)"); + rdcinflexiblestr logicalBindName; +}; + +DECLARE_REFLECTION_STRUCT(DescriptorLogicalLocation); + DOCUMENT("Information about a single constant buffer binding."); struct BoundCBuffer { diff --git a/renderdoc/api/replay/renderdoc_replay.h b/renderdoc/api/replay/renderdoc_replay.h index fb0024900..968c0ac5f 100644 --- a/renderdoc/api/replay/renderdoc_replay.h +++ b/renderdoc/api/replay/renderdoc_replay.h @@ -595,6 +595,16 @@ Multiple ranges within the store can be queried at once, and are returned in a c )"); virtual rdcarray GetDescriptorAccess() = 0; + DOCUMENT(R"(Retrieve the logical locations for descriptors in a given descriptor store. + +:param ResourceId descriptorStore: The descriptor store to be queried from. +:param List[DescriptorRange] ranges: The descriptor ranges to query. +:return: The descriptor logical locations. +:rtype: List[DescriptorLogicalLocation] +)"); + virtual rdcarray GetDescriptorLocations( + ResourceId descriptorStore, const rdcarray &ranges) = 0; + DOCUMENT(R"(Retrieve the list of possible disassembly targets for :meth:`DisassembleShader`. The values are implementation dependent but will always include a default target first which is the native disassembly of the shader. Further options may be available for additional diassembly views diff --git a/renderdoc/api/replay/replay_enums.h b/renderdoc/api/replay/replay_enums.h index 94c151610..8559b5b23 100644 --- a/renderdoc/api/replay/replay_enums.h +++ b/renderdoc/api/replay/replay_enums.h @@ -4771,7 +4771,7 @@ DOCUMENT(R"(A set of flags for ``ShaderStage`` stages A shorthand version with flags set for all stages together. )"); -enum class ShaderStageMask : uint32_t +enum class ShaderStageMask : uint16_t { Unknown = 0, Vertex = 1 << uint32_t(ShaderStage::Vertex), diff --git a/renderdoc/core/image_viewer.cpp b/renderdoc/core/image_viewer.cpp index 825f65cae..e6614d26b 100644 --- a/renderdoc/core/image_viewer.cpp +++ b/renderdoc/core/image_viewer.cpp @@ -280,6 +280,11 @@ public: return ret; } rdcarray GetDescriptorAccess(uint32_t eventId) { return {}; } + rdcarray GetDescriptorLocations(ResourceId descriptorStore, + const rdcarray &ranges) + { + return {}; + } DriverInformation GetDriverInfo() { DriverInformation ret = {}; diff --git a/renderdoc/core/replay_proxy.cpp b/renderdoc/core/replay_proxy.cpp index ac345a214..1d344d937 100644 --- a/renderdoc/core/replay_proxy.cpp +++ b/renderdoc/core/replay_proxy.cpp @@ -101,6 +101,7 @@ rdcstr DoStringise(const ReplayProxyPacket &el) STRINGISE_ENUM_NAMED(eReplayProxy_GetDescriptors, "GetDescriptors"); STRINGISE_ENUM_NAMED(eReplayProxy_GetSamplerDescriptors, "GetSamplerDescriptors"); STRINGISE_ENUM_NAMED(eReplayProxy_GetDescriptorAccess, "GetDescriptorAccess"); + STRINGISE_ENUM_NAMED(eReplayProxy_GetDescriptorLocations, "GetDescriptorLocations"); } END_ENUM_STRINGISE(); } @@ -1938,6 +1939,39 @@ rdcarray ReplayProxy::GetDescriptorAccess(uint32_t eventId) PROXY_FUNCTION(GetDescriptorAccess, eventId); } +template +rdcarray ReplayProxy::Proxied_GetDescriptorLocations( + ParamSerialiser ¶mser, ReturnSerialiser &retser, ResourceId descriptorStore, + const rdcarray &ranges) +{ + const ReplayProxyPacket expectedPacket = eReplayProxy_GetDescriptorLocations; + ReplayProxyPacket packet = eReplayProxy_GetDescriptorLocations; + rdcarray ret; + + { + BEGIN_PARAMS(); + SERIALISE_ELEMENT(descriptorStore); + SERIALISE_ELEMENT(ranges); + END_PARAMS(); + } + + { + REMOTE_EXECUTION(); + if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored) + ret = m_Remote->GetDescriptorLocations(descriptorStore, ranges); + } + + SERIALISE_RETURN(ret); + + return ret; +} + +rdcarray ReplayProxy::GetDescriptorLocations( + ResourceId descriptorStore, const rdcarray &ranges) +{ + PROXY_FUNCTION(GetDescriptorLocations, descriptorStore, ranges); +} + template void ReplayProxy::Proxied_ReplayLog(ParamSerialiser ¶mser, ReturnSerialiser &retser, uint32_t endEventID, ReplayLogType replayType) @@ -3011,6 +3045,7 @@ bool ReplayProxy::Tick(int type) case eReplayProxy_GetDescriptors: GetDescriptors(ResourceId(), {}); break; case eReplayProxy_GetSamplerDescriptors: GetSamplerDescriptors(ResourceId(), {}); break; case eReplayProxy_GetDescriptorAccess: GetDescriptorAccess(0); break; + case eReplayProxy_GetDescriptorLocations: GetDescriptorLocations(ResourceId(), {}); break; case eReplayProxy_GetUsage: GetUsage(ResourceId()); break; case eReplayProxy_GetLiveID: GetLiveID(ResourceId()); break; case eReplayProxy_GetFrameRecord: GetFrameRecord(); break; diff --git a/renderdoc/core/replay_proxy.h b/renderdoc/core/replay_proxy.h index 5f92e1a9e..38412f8e5 100644 --- a/renderdoc/core/replay_proxy.h +++ b/renderdoc/core/replay_proxy.h @@ -109,6 +109,7 @@ enum ReplayProxyPacket eReplayProxy_GetDescriptors, eReplayProxy_GetSamplerDescriptors, eReplayProxy_GetDescriptorAccess, + eReplayProxy_GetDescriptorLocations, }; DECLARE_REFLECTION_ENUM(ReplayProxyPacket); @@ -487,6 +488,8 @@ public: IMPLEMENT_FUNCTION_PROXIED(rdcarray, GetSamplerDescriptors, ResourceId descriptorStore, const rdcarray &ranges); IMPLEMENT_FUNCTION_PROXIED(rdcarray, GetDescriptorAccess, uint32_t eventId); + IMPLEMENT_FUNCTION_PROXIED(rdcarray, GetDescriptorLocations, + ResourceId descriptorStore, const rdcarray &ranges); IMPLEMENT_FUNCTION_PROXIED(rdcarray, GetPassEvents, uint32_t eventId); diff --git a/renderdoc/driver/d3d11/d3d11_replay.cpp b/renderdoc/driver/d3d11/d3d11_replay.cpp index 80079b96e..dcb168f42 100644 --- a/renderdoc/driver/d3d11/d3d11_replay.cpp +++ b/renderdoc/driver/d3d11/d3d11_replay.cpp @@ -2003,6 +2003,64 @@ rdcarray D3D11Replay::GetDescriptorAccess(uint32_t eventId) return ret; } +rdcarray D3D11Replay::GetDescriptorLocations( + ResourceId descriptorStore, const rdcarray &ranges) +{ + rdcarray ret; + + if(descriptorStore != m_pImmediateContext->GetDescriptorsID()) + { + RDCERR("Descriptors query for invalid descriptor store on fixed bindings API (D3D11)"); + return ret; + } + + size_t count = 0; + for(const DescriptorRange &r : ranges) + count += r.count; + ret.resize(count); + + size_t dst = 0; + for(const DescriptorRange &r : ranges) + { + uint32_t descriptorByteOffset = r.offset; + + for(uint32_t i = 0; i < r.count; i++, dst++, descriptorByteOffset++) + { + DescriptorLogicalLocation &dstLoc = ret[dst]; + D3D11DescriptorLocation srcLoc = DecodeD3D11DescriptorIndex(descriptorByteOffset); + + dstLoc.stageMask = MaskForStage(srcLoc.stage); + char typePrefix = '?'; + switch(srcLoc.type) + { + case D3D11DescriptorMapping::CBs: + typePrefix = 'b'; + dstLoc.category = DescriptorCategory::ConstantBlock; + break; + case D3D11DescriptorMapping::Samplers: + typePrefix = 's'; + dstLoc.category = DescriptorCategory::Sampler; + break; + case D3D11DescriptorMapping::SRVs: + typePrefix = 't'; + dstLoc.category = DescriptorCategory::ReadOnlyResource; + break; + case D3D11DescriptorMapping::UAVs: + typePrefix = 'u'; + dstLoc.category = DescriptorCategory::ReadWriteResource; + break; + case D3D11DescriptorMapping::Count: + case D3D11DescriptorMapping::Invalid: dstLoc.category = DescriptorCategory::Unknown; break; + } + dstLoc.fixedBindNumber = srcLoc.idx; + + dstLoc.logicalBindName = StringFormat::Fmt("%c%u", typePrefix, srcLoc.idx); + } + } + + return ret; +} + RDResult D3D11Replay::ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers) { return m_pDevice->ReadLogInitialisation(rdc, storeStructuredBuffers); diff --git a/renderdoc/driver/d3d11/d3d11_replay.h b/renderdoc/driver/d3d11/d3d11_replay.h index e9a9fe97f..1b59e961f 100644 --- a/renderdoc/driver/d3d11/d3d11_replay.h +++ b/renderdoc/driver/d3d11/d3d11_replay.h @@ -187,6 +187,8 @@ public: rdcarray GetSamplerDescriptors(ResourceId descriptorStore, const rdcarray &ranges); rdcarray GetDescriptorAccess(uint32_t eventId); + rdcarray GetDescriptorLocations(ResourceId descriptorStore, + const rdcarray &ranges); void FreeTargetResource(ResourceId id); void FreeCustomShader(ResourceId id); diff --git a/renderdoc/driver/d3d12/d3d12_replay.cpp b/renderdoc/driver/d3d12/d3d12_replay.cpp index 4bbb28a89..acef8b03b 100644 --- a/renderdoc/driver/d3d12/d3d12_replay.cpp +++ b/renderdoc/driver/d3d12/d3d12_replay.cpp @@ -2810,6 +2810,129 @@ rdcarray D3D12Replay::GetDescriptorAccess(uint32_t eventId) return ret; } +rdcarray D3D12Replay::GetDescriptorLocations( + ResourceId descriptorStore, const rdcarray &ranges) +{ + rdcarray ret; + + D3D12ResourceManager *rm = m_pDevice->GetResourceManager(); + + ID3D12DeviceChild *res = rm->GetCurrentAs(descriptorStore); + + size_t count = 0; + for(const DescriptorRange &r : ranges) + count += r.count; + ret.resize(count); + + // for sort keys we have the top 32-bits be the lower 32-bits of the store ResourceID, or 1 or 2 + // for static samplers and root constants. Then the lower 32-bits are an index + + if(WrappedID3D12RootSignature::IsAlloc(res)) + { + WrappedID3D12RootSignature *sig = (WrappedID3D12RootSignature *)res; + size_t dst = 0; + for(const DescriptorRange &r : ranges) + { + uint32_t staticIdx = r.offset; + + for(uint32_t i = 0; i < r.count; i++) + { + if(staticIdx >= sig->sig.StaticSamplers.size()) + { + // silently drop out of bounds descriptor reads + } + else + { + ret[dst].fixedBindNumber = ~0U - 2048 + staticIdx; + ret[dst].stageMask = ToShaderStageMask(sig->sig.StaticSamplers[staticIdx].ShaderVisibility); + ret[dst].category = DescriptorCategory::Sampler; + ret[dst].logicalBindName = StringFormat::Fmt("Static #%u", staticIdx); + } + + dst++; + staticIdx++; + } + } + return ret; + } + + if(WrappedID3D12PipelineState::IsAlloc(res)) + { + WrappedID3D12PipelineState *pipe = (WrappedID3D12PipelineState *)res; + + WrappedID3D12RootSignature *sig = + (WrappedID3D12RootSignature *)(pipe->IsGraphics() ? pipe->graphics->pRootSignature + : pipe->compute->pRootSignature); + + // root constants + size_t dst = 0; + for(const DescriptorRange &r : ranges) + { + uint32_t rootIndex = r.offset; + + for(uint32_t i = 0; i < r.count; i++, rootIndex++, dst++) + { + const D3D12RootSignatureParameter ¶m = sig->sig.Parameters[rootIndex]; + + DescriptorLogicalLocation &l = ret[dst]; + + l.fixedBindNumber = ~0U - 2048 - 64 + rootIndex; + l.stageMask = ToShaderStageMask(param.ShaderVisibility); + + if(param.ParameterType == D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS) + { + l.category = DescriptorCategory::ConstantBlock; + l.logicalBindName = StringFormat::Fmt("Consts #", rootIndex); + } + else if(param.ParameterType == D3D12_ROOT_PARAMETER_TYPE_CBV) + { + l.category = DescriptorCategory::ConstantBlock; + l.logicalBindName = StringFormat::Fmt("Root CB #", rootIndex); + } + else if(param.ParameterType == D3D12_ROOT_PARAMETER_TYPE_SRV) + { + l.category = DescriptorCategory::ReadOnlyResource; + l.logicalBindName = StringFormat::Fmt("Root SRV #", rootIndex); + } + else if(param.ParameterType == D3D12_ROOT_PARAMETER_TYPE_UAV) + { + l.category = DescriptorCategory::ReadWriteResource; + l.logicalBindName = StringFormat::Fmt("Root UAV #", rootIndex); + } + } + } + + return ret; + } + + if(!WrappedID3D12DescriptorHeap::IsAlloc(res)) + { + RDCERR("Invalid/unrecognised descriptor store %s", ToStr(descriptorStore).c_str()); + return ret; + } + + WrappedID3D12DescriptorHeap *heap = (WrappedID3D12DescriptorHeap *)res; + const bool sampler = (heap->GetDesc().Type == D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); + + size_t dst = 0; + for(const DescriptorRange &r : ranges) + { + uint32_t descriptorId = r.offset; + + for(uint32_t i = 0; i < r.count; i++, dst++, descriptorId++) + { + // can't set anything except the "bind number" which we just set as the offset. + ret[dst].fixedBindNumber = descriptorId; + if(sampler) + ret[dst].logicalBindName = StringFormat::Fmt("SamplerDescriptorHeap[%u]", descriptorId); + else + ret[dst].logicalBindName = StringFormat::Fmt("ResourceDescriptorHeap[%u]", descriptorId); + } + } + + return ret; +} + void D3D12Replay::RenderHighlightBox(float w, float h, float scale) { OutputWindow &outw = m_OutputWindows[m_CurrentOutputWindow]; diff --git a/renderdoc/driver/d3d12/d3d12_replay.h b/renderdoc/driver/d3d12/d3d12_replay.h index f7435d8e1..083a581e1 100644 --- a/renderdoc/driver/d3d12/d3d12_replay.h +++ b/renderdoc/driver/d3d12/d3d12_replay.h @@ -143,6 +143,8 @@ public: rdcarray GetSamplerDescriptors(ResourceId descriptorStore, const rdcarray &ranges); rdcarray GetDescriptorAccess(uint32_t eventId); + rdcarray GetDescriptorLocations(ResourceId descriptorStore, + const rdcarray &ranges); void FreeTargetResource(ResourceId id); void FreeCustomShader(ResourceId id); diff --git a/renderdoc/driver/gl/gl_replay.cpp b/renderdoc/driver/gl/gl_replay.cpp index 13df14432..4342e2ae0 100644 --- a/renderdoc/driver/gl/gl_replay.cpp +++ b/renderdoc/driver/gl/gl_replay.cpp @@ -2865,6 +2865,116 @@ rdcarray GLReplay::GetDescriptorAccess(uint32_t eventId) return m_Access; } +rdcarray GLReplay::GetDescriptorLocations( + ResourceId descriptorStore, const rdcarray &ranges) +{ + rdcarray ret; + + if(descriptorStore != m_pDriver->m_DescriptorsID) + { + RDCERR("Descriptors query for invalid descriptor store on fixed bindings API (OpenGL)"); + return ret; + } + + size_t count = 0; + for(const DescriptorRange &r : ranges) + count += r.count; + ret.resize(count); + + size_t dst = 0; + for(const DescriptorRange &r : ranges) + { + uint32_t descriptorByteOffset = r.offset; + + for(uint32_t i = 0; i < r.count; i++, dst++, descriptorByteOffset++) + { + DescriptorLogicalLocation &dstLoc = ret[dst]; + GLDescriptorLocation srcLoc = DecodeGLDescriptorIndex(descriptorByteOffset); + + const char *prefix = "Unknown"; + dstLoc.stageMask = ShaderStageMask::All; + switch(srcLoc.type) + { + case GLDescriptorMapping::BareUniforms: + prefix = "Uniforms"; + dstLoc.category = DescriptorCategory::ConstantBlock; + break; + case GLDescriptorMapping::UniformBinding: + prefix = "UBO"; + dstLoc.category = DescriptorCategory::ConstantBlock; + break; + case GLDescriptorMapping::Tex1D: + prefix = "Tex1D"; + dstLoc.category = DescriptorCategory::ReadOnlyResource; + break; + case GLDescriptorMapping::Tex2D: + prefix = "Tex2D"; + dstLoc.category = DescriptorCategory::ReadOnlyResource; + break; + case GLDescriptorMapping::Tex3D: + prefix = "Tex3D"; + dstLoc.category = DescriptorCategory::ReadOnlyResource; + break; + case GLDescriptorMapping::Tex1DArray: + prefix = "Tex1DArray"; + dstLoc.category = DescriptorCategory::ReadOnlyResource; + break; + case GLDescriptorMapping::Tex2DArray: + prefix = "Tex2DArray"; + dstLoc.category = DescriptorCategory::ReadOnlyResource; + break; + case GLDescriptorMapping::TexCubeArray: + prefix = "TexCubeArray"; + dstLoc.category = DescriptorCategory::ReadOnlyResource; + break; + case GLDescriptorMapping::TexRect: + prefix = "TexRect"; + dstLoc.category = DescriptorCategory::ReadOnlyResource; + break; + case GLDescriptorMapping::TexBuffer: + prefix = "TexBuffer"; + dstLoc.category = DescriptorCategory::ReadOnlyResource; + break; + case GLDescriptorMapping::TexCube: + prefix = "TexCube"; + dstLoc.category = DescriptorCategory::ReadOnlyResource; + break; + case GLDescriptorMapping::Tex2DMS: + prefix = "Tex2DMS"; + dstLoc.category = DescriptorCategory::ReadOnlyResource; + break; + case GLDescriptorMapping::Tex2DMSArray: + prefix = "Tex2DMSArray"; + dstLoc.category = DescriptorCategory::ReadOnlyResource; + break; + case GLDescriptorMapping::Images: + prefix = "Image"; + dstLoc.category = DescriptorCategory::ReadWriteResource; + break; + case GLDescriptorMapping::AtomicCounter: + prefix = "Atomic"; + dstLoc.category = DescriptorCategory::ReadWriteResource; + break; + case GLDescriptorMapping::ShaderStorage: + prefix = "SSBO"; + dstLoc.category = DescriptorCategory::ReadWriteResource; + break; + case GLDescriptorMapping::Count: + case GLDescriptorMapping::Invalid: dstLoc.category = DescriptorCategory::Unknown; break; + } + dstLoc.fixedBindNumber = srcLoc.idx; + + if(srcLoc.type == GLDescriptorMapping::BareUniforms) + dstLoc.logicalBindName = + StringFormat::Fmt("%s %s", prefix, ToStr(ShaderStage(srcLoc.idx)).c_str()); + else + dstLoc.logicalBindName = StringFormat::Fmt("%s %u", prefix, srcLoc.idx); + } + } + + return ret; +} + void GLReplay::OpenGLFillCBufferVariables(ResourceId shader, GLuint prog, bool bufferBacked, rdcstr prefix, const rdcarray &variables, rdcarray &outvars, diff --git a/renderdoc/driver/gl/gl_replay.h b/renderdoc/driver/gl/gl_replay.h index 52c7173ac..b91ad0db7 100644 --- a/renderdoc/driver/gl/gl_replay.h +++ b/renderdoc/driver/gl/gl_replay.h @@ -165,6 +165,8 @@ public: rdcarray GetSamplerDescriptors(ResourceId descriptorStore, const rdcarray &ranges); rdcarray GetDescriptorAccess(uint32_t eventId); + rdcarray GetDescriptorLocations(ResourceId descriptorStore, + const rdcarray &ranges); void FreeTargetResource(ResourceId id); RDResult ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); diff --git a/renderdoc/driver/vulkan/vk_info.h b/renderdoc/driver/vulkan/vk_info.h index fab6fe480..7ece69c36 100644 --- a/renderdoc/driver/vulkan/vk_info.h +++ b/renderdoc/driver/vulkan/vk_info.h @@ -109,6 +109,13 @@ struct DescSetLayout VkShaderStageFlags stageFlags : 31; uint32_t variableSize : 1; ResourceId *immutableSampler; + + inline uint32_t GetDescriptorCount(uint32_t varDescriptorSize) const + { + if(variableSize) + return varDescriptorSize; + return descriptorCount; + } }; rdcarray bindings; diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp index 269372f10..abc28c37d 100644 --- a/renderdoc/driver/vulkan/vk_replay.cpp +++ b/renderdoc/driver/vulkan/vk_replay.cpp @@ -3007,6 +3007,128 @@ rdcarray VulkanReplay::GetDescriptorAccess(uint32_t eventId) return ret; } +rdcarray VulkanReplay::GetDescriptorLocations( + ResourceId descriptorStore, const rdcarray &ranges) +{ + rdcarray ret; + + size_t count = 0; + for(const DescriptorRange &r : ranges) + count += r.count; + ret.resize(count); + + // specialisation constants 'descriptor' stored in a pipeline + auto pipe = m_pDriver->m_CreationInfo.m_Pipeline.find(descriptorStore); + if(pipe != m_pDriver->m_CreationInfo.m_Pipeline.end()) + { + // should only be one descriptor referred here, but just munge them all to be the same + for(DescriptorLogicalLocation &d : ret) + { + d.category = DescriptorCategory::ConstantBlock; + d.fixedBindNumber = ~0U - 2; + d.logicalBindName = "Specialization"; + } + + return ret; + } + + VulkanResourceManager *rm = m_pDriver->GetResourceManager(); + + // push constants 'descriptor' stored in a command buffer + if(WrappedVkCommandBuffer::IsAlloc(rm->GetCurrentResource(descriptorStore))) + { + // should only be one descriptor referred here, but just munge them all to be the same + for(DescriptorLogicalLocation &d : ret) + { + d.category = DescriptorCategory::ConstantBlock; + d.fixedBindNumber = ~0U - 1; + d.logicalBindName = "Push constants"; + } + + return ret; + } + + auto descit = m_pDriver->m_DescriptorSetState.find(descriptorStore); + if(descit == m_pDriver->m_DescriptorSetState.end()) + { + RDCERR("Invalid/unrecognised descriptor store %s", ToStr(descriptorStore).c_str()); + return ret; + } + + const WrappedVulkan::DescriptorSetInfo &descState = descit->second; + uint32_t varDescCount = descState.data.variableDescriptorCount; + + const DescSetLayout &descLayout = m_pDriver->m_CreationInfo.m_DescSetLayout[descState.layout]; + + size_t dst = 0; + for(const DescriptorRange &r : ranges) + { + uint32_t descriptorOffset = r.offset; + + const DescSetLayout::Binding *bind = descLayout.bindings.data(); + const DescSetLayout::Binding *firstBind = bind; + const DescSetLayout::Binding *lastBind = bind + descLayout.bindings.size(); + + for(uint32_t i = 0; i < r.count; i++, dst++, descriptorOffset++) + { + while(bind < lastBind && + descLayout.inlineByteSize + bind->elemOffset + bind->GetDescriptorCount(varDescCount) <= + descriptorOffset) + bind++; + + if(bind >= lastBind) + { + RDCERR("Ran off end of descriptor layout looking for matching offset"); + break; + } + + DescriptorLogicalLocation &d = ret[dst]; + + const DescriptorSetSlot *slot = descState.data.binds[0] + descriptorOffset; + + switch(slot->type) + { + case DescriptorSlotType::Sampler: d.category = DescriptorCategory::Sampler; break; + case DescriptorSlotType::UniformBuffer: + case DescriptorSlotType::InlineBlock: + case DescriptorSlotType::UniformBufferDynamic: + case DescriptorSlotType::SampledImage: + case DescriptorSlotType::CombinedImageSampler: + case DescriptorSlotType::UniformTexelBuffer: + case DescriptorSlotType::InputAttachment: + case DescriptorSlotType::AccelerationStructure: + d.category = DescriptorCategory::ReadOnlyResource; + break; + case DescriptorSlotType::StorageBuffer: + case DescriptorSlotType::StorageBufferDynamic: + case DescriptorSlotType::StorageImage: + case DescriptorSlotType::StorageTexelBuffer: + d.category = DescriptorCategory::ReadWriteResource; + break; + case DescriptorSlotType::Unwritten: + case DescriptorSlotType::Count: d.category = DescriptorCategory::Unknown; break; + } + + if(bind->stageFlags == VK_SHADER_STAGE_ALL) + d.stageMask = ShaderStageMask::All; + else + d.stageMask = (ShaderStageMask)bind->stageFlags; + // we only have one bind number, for simplicity, so we put the bind here and omit the array + // element entirely. Users that want to decode this are expected to either be aware of arrays + // and determine that contiguous identical bind numbers are arrays, or display with the + // logical name string below + d.fixedBindNumber = uint32_t(bind - firstBind); + if(bind->descriptorCount > 1 && bind->layoutDescType != VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) + d.logicalBindName = StringFormat::Fmt("%zu[%u]", size_t(bind - firstBind), + descriptorOffset - bind->elemOffset); + else + d.logicalBindName = StringFormat::Fmt("%zu", size_t(bind - firstBind)); + } + } + + return ret; +} + void VulkanReplay::FillCBufferVariables(ResourceId pipeline, ResourceId shader, ShaderStage stage, rdcstr entryPoint, uint32_t cbufSlot, rdcarray &outvars, const bytebuf &data) diff --git a/renderdoc/driver/vulkan/vk_replay.h b/renderdoc/driver/vulkan/vk_replay.h index 3192a7738..5a3611170 100644 --- a/renderdoc/driver/vulkan/vk_replay.h +++ b/renderdoc/driver/vulkan/vk_replay.h @@ -354,6 +354,8 @@ public: rdcarray GetSamplerDescriptors(ResourceId descriptorStore, const rdcarray &ranges); rdcarray GetDescriptorAccess(uint32_t eventId); + rdcarray GetDescriptorLocations(ResourceId descriptorStore, + const rdcarray &ranges); void FreeTargetResource(ResourceId id); RDResult ReadLogInitialisation(RDCFile *rdc, bool storeStructuredBuffers); diff --git a/renderdoc/replay/dummy_driver.cpp b/renderdoc/replay/dummy_driver.cpp index 4fa469138..98eb28b30 100644 --- a/renderdoc/replay/dummy_driver.cpp +++ b/renderdoc/replay/dummy_driver.cpp @@ -170,6 +170,12 @@ rdcarray DummyDriver::GetDescriptorAccess(uint32_t eventId) return {}; } +rdcarray DummyDriver::GetDescriptorLocations( + ResourceId descriptorStore, const rdcarray &ranges) +{ + return {}; +} + FrameRecord DummyDriver::GetFrameRecord() { return m_FrameRecord; diff --git a/renderdoc/replay/dummy_driver.h b/renderdoc/replay/dummy_driver.h index 1149b5e7b..e98b7e5bb 100644 --- a/renderdoc/replay/dummy_driver.h +++ b/renderdoc/replay/dummy_driver.h @@ -65,6 +65,8 @@ public: rdcarray GetSamplerDescriptors(ResourceId descriptorStore, const rdcarray &ranges); rdcarray GetDescriptorAccess(uint32_t eventId); + rdcarray GetDescriptorLocations(ResourceId descriptorStore, + const rdcarray &ranges); FrameRecord GetFrameRecord(); diff --git a/renderdoc/replay/renderdoc_serialise.inl b/renderdoc/replay/renderdoc_serialise.inl index 42e658291..561932d52 100644 --- a/renderdoc/replay/renderdoc_serialise.inl +++ b/renderdoc/replay/renderdoc_serialise.inl @@ -1138,6 +1138,17 @@ void DoSerialise(SerialiserType &ser, DescriptorAccess &el) SIZE_CHECK(32); } +template +void DoSerialise(SerialiserType &ser, DescriptorLogicalLocation &el) +{ + SERIALISE_MEMBER(stageMask); + SERIALISE_MEMBER(category); + SERIALISE_MEMBER(fixedBindNumber); + SERIALISE_MEMBER(logicalBindName); + + SIZE_CHECK(16); +} + template void DoSerialise(SerialiserType &ser, StencilFace &el) { @@ -2604,6 +2615,7 @@ INSTANTIATE_SERIALISE_TYPE(DescriptorRange) INSTANTIATE_SERIALISE_TYPE(Descriptor) INSTANTIATE_SERIALISE_TYPE(SamplerDescriptor) INSTANTIATE_SERIALISE_TYPE(DescriptorAccess) +INSTANTIATE_SERIALISE_TYPE(DescriptorLogicalLocation) INSTANTIATE_SERIALISE_TYPE(D3D11Pipe::Layout) INSTANTIATE_SERIALISE_TYPE(D3D11Pipe::InputAssembly) INSTANTIATE_SERIALISE_TYPE(D3D11Pipe::View) diff --git a/renderdoc/replay/replay_controller.cpp b/renderdoc/replay/replay_controller.cpp index e2875401c..fbe1eb014 100644 --- a/renderdoc/replay/replay_controller.cpp +++ b/renderdoc/replay/replay_controller.cpp @@ -138,6 +138,14 @@ rdcarray ReplayController::GetDescriptorAccess() return m_pDevice->GetDescriptorAccess(m_EventID); } +rdcarray ReplayController::GetDescriptorLocations( + ResourceId descriptorStore, const rdcarray &ranges) +{ + CHECK_REPLAY_THREAD(); + + return m_pDevice->GetDescriptorLocations(m_pDevice->GetLiveID(descriptorStore), ranges); +} + rdcarray ReplayController::GetSamplerDescriptors( ResourceId descriptorStore, const rdcarray &ranges) { diff --git a/renderdoc/replay/replay_controller.h b/renderdoc/replay/replay_controller.h index 5553c4bb0..221e001d2 100644 --- a/renderdoc/replay/replay_controller.h +++ b/renderdoc/replay/replay_controller.h @@ -155,6 +155,8 @@ public: rdcarray GetSamplerDescriptors(ResourceId descriptorStore, const rdcarray &ranges); rdcarray GetDescriptorAccess(); + rdcarray GetDescriptorLocations(ResourceId descriptorStore, + const rdcarray &ranges); rdcarray GetDisassemblyTargets(bool withPipeline); rdcstr DisassembleShader(ResourceId pipeline, const ShaderReflection *refl, const rdcstr &target); diff --git a/renderdoc/replay/replay_driver.h b/renderdoc/replay/replay_driver.h index 845ef4656..a90cb6cde 100644 --- a/renderdoc/replay/replay_driver.h +++ b/renderdoc/replay/replay_driver.h @@ -167,6 +167,8 @@ public: virtual rdcarray GetSamplerDescriptors( ResourceId descriptorStore, const rdcarray &ranges) = 0; virtual rdcarray GetDescriptorAccess(uint32_t eventId) = 0; + virtual rdcarray GetDescriptorLocations( + ResourceId descriptorStore, const rdcarray &ranges) = 0; virtual FrameRecord GetFrameRecord() = 0;