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;