From 2a41eb54dd9a0f1ca15743a53c5d4de883f96bdc Mon Sep 17 00:00:00 2001 From: baldurk Date: Mon, 18 Mar 2024 10:24:07 +0000 Subject: [PATCH] Add new common pipeline state accessors for descriptor-based accesses * These are temporarily given separate names, to allow them to exist in parallel with the existing helpers, but in future these will be renamed when the older helpers are removed. --- qrenderdoc/Code/pyrenderdoc/renderdoc.i | 1 + renderdoc/api/replay/common_pipestate.h | 53 ++++ renderdoc/api/replay/pipestate.h | 100 +++++++ renderdoc/api/replay/pipestate.inl | 355 ++++++++++++++++++++++++ renderdoc/api/replay/renderdoc_replay.h | 2 +- renderdoc/replay/replay_controller.cpp | 51 +++- renderdoc/replay/replay_controller.h | 2 +- 7 files changed, 560 insertions(+), 4 deletions(-) diff --git a/qrenderdoc/Code/pyrenderdoc/renderdoc.i b/qrenderdoc/Code/pyrenderdoc/renderdoc.i index 5692333da..923380347 100644 --- a/qrenderdoc/Code/pyrenderdoc/renderdoc.i +++ b/qrenderdoc/Code/pyrenderdoc/renderdoc.i @@ -417,6 +417,7 @@ TEMPLATE_ARRAY_INSTANTIATE(rdcarray, Descriptor) TEMPLATE_ARRAY_INSTANTIATE(rdcarray, SamplerDescriptor) TEMPLATE_ARRAY_INSTANTIATE(rdcarray, DescriptorAccess) TEMPLATE_ARRAY_INSTANTIATE(rdcarray, DescriptorLogicalLocation) +TEMPLATE_ARRAY_INSTANTIATE(rdcarray, UsedDescriptor) 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 208fe6b85..9143af811 100644 --- a/renderdoc/api/replay/common_pipestate.h +++ b/renderdoc/api/replay/common_pipestate.h @@ -954,6 +954,59 @@ first, and fall back to this name if no reflection information is available in t DECLARE_REFLECTION_STRUCT(DescriptorLogicalLocation); +DOCUMENT(R"(Combined information about a single descriptor that has been used, both the information +about its access and its contents. + +This is a helper struct for the common pipeline state abstraction to trade off simplicity of access +against optimal access. +)"); +struct UsedDescriptor +{ + DOCUMENT(""); + UsedDescriptor() = default; + UsedDescriptor(const UsedDescriptor &) = default; + UsedDescriptor &operator=(const UsedDescriptor &) = default; + + bool operator==(const UsedDescriptor &o) const + { + return access == o.access && descriptor == o.descriptor && sampler == o.sampler; + } + bool operator<(const UsedDescriptor &o) const + { + if(!(access == o.access)) + return access < o.access; + if(!(descriptor == o.descriptor)) + return descriptor < o.descriptor; + if(!(sampler == o.sampler)) + return sampler < o.sampler; + return false; + } + + DOCUMENT(R"(The access information of which shader reflection object accessed which descriptor. + +:type: DescriptorAccess +)"); + DescriptorAccess access; + + DOCUMENT(R"(The contents of the accessed descriptor, if it is a normal non-sampler descriptor. + +For sampler descriptors this is empty. + +:type: Descriptor +)"); + Descriptor descriptor; + + DOCUMENT(R"(The contents of the accessed descriptor, if it is a sampler descriptor. + +For normal descriptors this is empty. + +:type: SamplerDescriptor +)"); + SamplerDescriptor sampler; +}; + +DECLARE_REFLECTION_STRUCT(UsedDescriptor); + DOCUMENT("Information about a single constant buffer binding."); struct BoundCBuffer { diff --git a/renderdoc/api/replay/pipestate.h b/renderdoc/api/replay/pipestate.h index 1243deae4..d32c5c4f3 100644 --- a/renderdoc/api/replay/pipestate.h +++ b/renderdoc/api/replay/pipestate.h @@ -77,6 +77,14 @@ public: m_GL = NULL; m_Vulkan = vk; } + + void SetDescriptorAccess(rdcarray &&descriptorAccess, + rdcarray &&descriptors, rdcarray &&samplers) + { + m_Access = descriptorAccess; + m_Descriptors = descriptors; + m_SamplerDescriptors = samplers; + } #endif DOCUMENT(R"(Determines whether or not a capture is currently loaded. @@ -381,6 +389,71 @@ For some APIs that don't distinguish by entry point, this may be empty. )"); rdcarray GetReadWriteResources(ShaderStage stage, bool onlyUsed = false) const; + DOCUMENT(R"(Retrieves the current list of descriptor accesses, as cached from a call to +:meth:`ReplayController.GetDescriptorAccess`. The return value is identical, this is here for +convenience of access. + +:return: The descriptor accesses. +:rtype: List[DescriptorAccess] +)"); + const rdcarray &GetDescriptorAccess() const { return m_Access; } + + DOCUMENT(R"(Retrieves all descriptor information for all descriptors accessed at the current event. + +:param bool onlyUsed: Omit descriptors bound or declared but not accessed. +:return: All descriptors accessed at the current event. +:rtype: List[UsedDescriptor] +)"); + rdcarray GetAllUsedDescriptors(bool onlyUsed = false) const; + + DOCUMENT(R"(Retrieves the constant block at a given binding. + +:param ShaderStage stage: The shader stage to fetch from. +:param int index: The index in the shader's ConstantBlocks array to look up. +:param int arrayIdx: For APIs that support arrays of constant buffers in a single binding, the index + in that array to look up. +:return: The constant buffer at the specified binding. +:rtype: UsedDescriptor +)"); + UsedDescriptor GetConstantBlockDescriptor(ShaderStage stage, uint32_t index, + uint32_t arrayIdx) const; + + DOCUMENT(R"(Retrieves the constant blocks used by a particular shader stage. + +:param ShaderStage stage: The shader stage to fetch from. +:param bool onlyUsed: Omit descriptors bound or declared but not accessed. +:return: The currently bound constant blocks. +:rtype: List[UsedDescriptor] +)"); + rdcarray GetConstantBlockDescriptors(ShaderStage stage, bool onlyUsed = false) const; + + DOCUMENT(R"(Retrieves the read-only resources used by a particular shader stage. + +:param ShaderStage stage: The shader stage to fetch from. +:param bool onlyUsed: Omit descriptors bound or declared but not accessed. +:return: The currently bound read-only resources. +:rtype: List[UsedDescriptor] +)"); + rdcarray GetReadOnlyDescriptors(ShaderStage stage, bool onlyUsed = false) const; + + DOCUMENT(R"(Retrieves the samplers bound to a particular shader stage. + +:param ShaderStage stage: The shader stage to fetch from. +:param bool onlyUsed: Omit descriptors bound or declared but not accessed. +:return: The currently bound sampler resources. +:rtype: List[UsedDescriptor] +)"); + rdcarray GetSamplerDescriptors(ShaderStage stage, bool onlyUsed = false) const; + + DOCUMENT(R"(Retrieves the read/write resources used by a particular shader stage. + +:param ShaderStage stage: The shader stage to fetch from. +:param bool onlyUsed: Omit descriptors bound or declared but not accessed. +:return: The currently bound read/write resources. +:rtype: List[UsedDescriptor] +)"); + rdcarray GetReadWriteDescriptors(ShaderStage stage, bool onlyUsed = false) const; + DOCUMENT(R"(Retrieves the read/write resources bound to the depth-stencil output. :return: The currently bound depth-stencil resource. @@ -402,6 +475,27 @@ For some APIs that don't distinguish by entry point, this may be empty. )"); rdcarray GetOutputTargets() const; + DOCUMENT(R"(Retrieves the read/write resources bound to the depth-stencil output. + +:return: The currently bound depth-stencil resource. +:rtype: Descriptor +)"); + Descriptor GetDepthTargetDescriptor() const; + + DOCUMENT(R"(Retrieves the read/write resources bound to the depth-stencil resolve output. + +:return: The currently bound depth-stencil resolve resource. +:rtype: Descriptor +)"); + Descriptor GetDepthResolveTargetDescriptor() const; + + DOCUMENT(R"(Retrieves the resources bound to the color outputs. + +:return: The currently bound output targets. +:rtype: List[Descriptor] +)"); + rdcarray GetOutputTargetDescriptors() const; + DOCUMENT(R"(Retrieves the current color blending states, per target. :return: The currently color blend states. @@ -446,4 +540,10 @@ private: bool IsD3D12Stage(ShaderStage stage) const; bool IsGLStage(ShaderStage stage) const; bool IsVulkanStage(ShaderStage stage) const; + + rdcarray m_Access; + rdcarray m_Descriptors; + rdcarray m_SamplerDescriptors; + + void ApplyVulkanDynamicOffsets(UsedDescriptor &used) const; }; diff --git a/renderdoc/api/replay/pipestate.inl b/renderdoc/api/replay/pipestate.inl index 1724d378a..1f8496a5d 100644 --- a/renderdoc/api/replay/pipestate.inl +++ b/renderdoc/api/replay/pipestate.inl @@ -1849,6 +1849,142 @@ rdcarray PipeState::GetReadWriteResources(ShaderStage stage, return ret; } +rdcarray PipeState::GetAllUsedDescriptors(bool onlyUsed) const +{ + rdcarray ret; + ret.reserve(m_Access.size()); + + for(size_t i = 0; i < m_Access.size(); i++) + { + if(onlyUsed == false || !m_Access[i].staticallyUnused) + { + if(i < m_Descriptors.size()) + ret.push_back({m_Access[i], m_Descriptors[i], m_SamplerDescriptors[i]}); + } + } + + return ret; +} + +void PipeState::ApplyVulkanDynamicOffsets(UsedDescriptor &used) const +{ + if(IsCaptureVK()) + { + const rdcarray &sets = used.access.stage == ShaderStage::Compute + ? m_Vulkan->compute.descriptorSets + : m_Vulkan->graphics.descriptorSets; + for(const VKPipe::DescriptorSet &set : sets) + { + for(const VKPipe::DynamicOffset &offs : set.dynamicOffsets) + { + if(set.descriptorSetResourceId == used.access.descriptorStore && + offs.descriptorByteOffset == used.access.byteOffset) + { + used.descriptor.byteOffset += offs.dynamicBufferByteOffset; + } + } + } + } +} + +UsedDescriptor PipeState::GetConstantBlockDescriptor(ShaderStage stage, uint32_t index, + uint32_t arrayIdx) const +{ + for(size_t i = 0; i < m_Access.size(); i++) + { + if(m_Access[i].stage == stage && IsConstantBlockDescriptor(m_Access[i].type) && + m_Access[i].index == index && m_Access[i].arrayElement == arrayIdx) + { + if(i < m_Descriptors.size()) + { + UsedDescriptor ret = {m_Access[i], m_Descriptors[i], SamplerDescriptor()}; + ApplyVulkanDynamicOffsets(ret); + return ret; + } + + break; + } + } + + return UsedDescriptor(); +} + +rdcarray PipeState::GetConstantBlockDescriptors(ShaderStage stage, bool onlyUsed) const +{ + rdcarray ret; + + for(size_t i = 0; i < m_Access.size(); i++) + { + if(m_Access[i].stage == stage && IsConstantBlockDescriptor(m_Access[i].type) && + (onlyUsed == false || !m_Access[i].staticallyUnused)) + { + if(i < m_Descriptors.size()) + { + ret.push_back({m_Access[i], m_Descriptors[i], SamplerDescriptor()}); + ApplyVulkanDynamicOffsets(ret.back()); + } + } + } + + return ret; +} + +rdcarray PipeState::GetReadOnlyDescriptors(ShaderStage stage, bool onlyUsed) const +{ + rdcarray ret; + + for(size_t i = 0; i < m_Access.size(); i++) + { + if(m_Access[i].stage == stage && IsReadOnlyDescriptor(m_Access[i].type) && + (onlyUsed == false || !m_Access[i].staticallyUnused)) + { + if(i < m_Descriptors.size() && i < m_SamplerDescriptors.size()) + { + ret.push_back({m_Access[i], m_Descriptors[i], m_SamplerDescriptors[i]}); + ApplyVulkanDynamicOffsets(ret.back()); + } + } + } + + return ret; +} + +rdcarray PipeState::GetSamplerDescriptors(ShaderStage stage, bool onlyUsed) const +{ + rdcarray ret; + + for(size_t i = 0; i < m_Access.size(); i++) + { + if(m_Access[i].stage == stage && IsSamplerDescriptor(m_Access[i].type) && + (onlyUsed == false || !m_Access[i].staticallyUnused)) + { + if(i < m_Descriptors.size()) + { + ret.push_back({m_Access[i], Descriptor(), m_SamplerDescriptors[i]}); + ApplyVulkanDynamicOffsets(ret.back()); + } + } + } + + return ret; +} + +rdcarray PipeState::GetReadWriteDescriptors(ShaderStage stage, bool onlyUsed) const +{ + rdcarray ret; + + for(size_t i = 0; i < m_Access.size(); i++) + { + if(m_Access[i].stage == stage && IsReadWriteDescriptor(m_Access[i].type) && + (onlyUsed == false || !m_Access[i].staticallyUnused)) + { + if(i < m_SamplerDescriptors.size()) + ret.push_back({m_Access[i], m_Descriptors[i], SamplerDescriptor()}); + } + } + + return ret; +} BoundResource PipeState::GetDepthTarget() const { if(IsCaptureLoaded()) @@ -2011,6 +2147,225 @@ rdcarray PipeState::GetOutputTargets() const return ret; } +Descriptor PipeState::GetDepthTargetDescriptor() const +{ + Descriptor ret; + ret.type = DescriptorType::ReadWriteImage; + + if(IsCaptureLoaded()) + { + if(IsCaptureD3D11()) + { + const D3D11Pipe::View &depthTarget = m_D3D11->outputMerger.depthTarget; + + ret.resource = depthTarget.resourceResourceId; + ret.view = depthTarget.viewResourceId; + ret.firstMip = depthTarget.firstMip & 0xff; + ret.numMips = depthTarget.numMips & 0xff; + ret.firstSlice = depthTarget.firstSlice & 0xffff; + ret.numSlices = depthTarget.numSlices & 0xffff; + ret.format = depthTarget.viewFormat; + ret.textureType = depthTarget.type; + } + else if(IsCaptureD3D12()) + { + const D3D12Pipe::View &depthTarget = m_D3D12->outputMerger.depthTarget; + + ret.resource = depthTarget.resourceId; + ret.firstMip = depthTarget.firstMip & 0xff; + ret.numMips = depthTarget.numMips & 0xff; + ret.firstSlice = depthTarget.firstSlice & 0xffff; + ret.numSlices = depthTarget.numSlices & 0xffff; + ret.format = depthTarget.viewFormat; + ret.textureType = depthTarget.type; + } + else if(IsCaptureGL()) + { + const GLPipe::Attachment &depthAttachment = m_GL->framebuffer.drawFBO.depthAttachment; + + ret.resource = depthAttachment.resourceId; + ret.firstMip = depthAttachment.mipLevel & 0xff; + ret.numMips = 1; + ret.firstSlice = depthAttachment.slice & 0xffff; + ret.numSlices = depthAttachment.numSlices & 0xffff; + ret.swizzle = depthAttachment.swizzle; + ret.textureType = ret.numSlices > 1 ? TextureType::Texture2DArray : TextureType::Texture2D; + } + else if(IsCaptureVK()) + { + const VKPipe::RenderPass &rp = m_Vulkan->currentPass.renderpass; + const VKPipe::Framebuffer &fb = m_Vulkan->currentPass.framebuffer; + + if(rp.depthstencilAttachment >= 0 && rp.depthstencilAttachment < fb.attachments.count()) + { + const VKPipe::Attachment &depthAttachment = fb.attachments[rp.depthstencilAttachment]; + + ret.resource = depthAttachment.imageResourceId; + ret.view = depthAttachment.viewResourceId; + ret.firstMip = depthAttachment.firstMip & 0xff; + ret.numMips = depthAttachment.numMips & 0xff; + ret.firstSlice = depthAttachment.firstSlice & 0xffff; + ret.numSlices = depthAttachment.numSlices & 0xffff; + ret.format = depthAttachment.viewFormat; + ret.swizzle = depthAttachment.swizzle; + ret.textureType = ret.numSlices > 1 ? TextureType::Texture2DArray : TextureType::Texture2D; + } + } + } + + return ret; +} + +Descriptor PipeState::GetDepthResolveTargetDescriptor() const +{ + Descriptor ret; + ret.type = DescriptorType::ReadWriteImage; + + if(IsCaptureLoaded()) + { + if(IsCaptureVK()) + { + const VKPipe::RenderPass &rp = m_Vulkan->currentPass.renderpass; + const VKPipe::Framebuffer &fb = m_Vulkan->currentPass.framebuffer; + + if(rp.depthstencilResolveAttachment >= 0 && + rp.depthstencilResolveAttachment < fb.attachments.count()) + { + const VKPipe::Attachment &depthResolveAttachment = + fb.attachments[rp.depthstencilResolveAttachment]; + + ret.resource = depthResolveAttachment.imageResourceId; + ret.view = depthResolveAttachment.viewResourceId; + ret.firstMip = depthResolveAttachment.firstMip & 0xff; + ret.numMips = depthResolveAttachment.numMips & 0xff; + ret.firstSlice = depthResolveAttachment.firstSlice & 0xffff; + ret.numSlices = depthResolveAttachment.numSlices & 0xffff; + ret.format = depthResolveAttachment.viewFormat; + ret.swizzle = depthResolveAttachment.swizzle; + ret.textureType = ret.numSlices > 1 ? TextureType::Texture2DArray : TextureType::Texture2D; + } + } + } + + return ret; +} + +rdcarray PipeState::GetOutputTargetDescriptors() const +{ + rdcarray ret; + + if(IsCaptureLoaded()) + { + if(IsCaptureD3D11()) + { + ret.resize(m_D3D11->outputMerger.renderTargets.count()); + for(int i = 0; i < m_D3D11->outputMerger.renderTargets.count(); i++) + { + const D3D11Pipe::View &rt = m_D3D11->outputMerger.renderTargets[i]; + + ret[i].resource = rt.resourceResourceId; + ret[i].view = rt.viewResourceId; + ret[i].firstMip = rt.firstMip & 0xff; + ret[i].numMips = rt.numMips & 0xff; + ret[i].firstSlice = rt.firstSlice & 0xffff; + ret[i].numSlices = rt.numSlices & 0xffff; + ret[i].format = rt.viewFormat; + ret[i].textureType = rt.type; + } + } + else if(IsCaptureD3D12()) + { + ret.resize(m_D3D12->outputMerger.renderTargets.count()); + for(int i = 0; i < m_D3D12->outputMerger.renderTargets.count(); i++) + { + const D3D12Pipe::View &rt = m_D3D12->outputMerger.renderTargets[i]; + + ret[i].resource = rt.resourceId; + ret[i].firstMip = rt.firstMip & 0xff; + ret[i].numMips = rt.numMips & 0xff; + ret[i].firstSlice = rt.firstSlice & 0xffff; + ret[i].numSlices = rt.numSlices & 0xffff; + ret[i].format = rt.viewFormat; + ret[i].textureType = rt.type; + } + } + else if(IsCaptureGL()) + { + ret.resize(m_GL->framebuffer.drawFBO.drawBuffers.count()); + for(int i = 0; i < m_GL->framebuffer.drawFBO.drawBuffers.count(); i++) + { + int db = m_GL->framebuffer.drawFBO.drawBuffers[i]; + + if(db >= 0) + { + const GLPipe::Attachment &col = m_GL->framebuffer.drawFBO.colorAttachments[db]; + + ret[i].resource = col.resourceId; + ret[i].firstMip = col.mipLevel & 0xff; + ret[i].numMips = 1; + ret[i].firstSlice = col.slice & 0xffff; + ret[i].numSlices = col.numSlices & 0xffff; + ret[i].swizzle = col.swizzle; + ret[i].textureType = + ret[i].numSlices > 1 ? TextureType::Texture2DArray : TextureType::Texture2D; + } + } + } + else if(IsCaptureVK()) + { + const VKPipe::RenderPass &rp = m_Vulkan->currentPass.renderpass; + const VKPipe::Framebuffer &fb = m_Vulkan->currentPass.framebuffer; + + int idx = 0; + + ret.resize(rp.colorAttachments.count() + rp.resolveAttachments.count()); + for(int i = 0; i < rp.colorAttachments.count(); i++) + { + if(rp.colorAttachments[i] < (uint32_t)fb.attachments.count()) + { + const VKPipe::Attachment &col = fb.attachments[rp.colorAttachments[i]]; + + ret[idx].resource = col.imageResourceId; + ret[idx].view = col.viewResourceId; + ret[idx].firstMip = col.firstMip & 0xff; + ret[idx].numMips = col.numMips & 0xff; + ret[idx].firstSlice = col.firstSlice & 0xffff; + ret[idx].numSlices = col.numSlices & 0xffff; + ret[idx].format = col.viewFormat; + ret[idx].swizzle = col.swizzle; + ret[idx].textureType = + ret[idx].numSlices > 1 ? TextureType::Texture2DArray : TextureType::Texture2D; + } + + idx++; + } + + for(int i = 0; i < rp.resolveAttachments.count(); i++) + { + if(rp.resolveAttachments[i] < (uint32_t)fb.attachments.count()) + { + const VKPipe::Attachment &resolve = fb.attachments[rp.resolveAttachments[i]]; + + ret[idx].resource = resolve.imageResourceId; + ret[idx].view = resolve.viewResourceId; + ret[idx].firstMip = resolve.firstMip & 0xff; + ret[idx].numMips = resolve.numMips & 0xff; + ret[idx].firstSlice = resolve.firstSlice & 0xffff; + ret[idx].numSlices = resolve.numSlices & 0xffff; + ret[idx].format = resolve.viewFormat; + ret[idx].swizzle = resolve.swizzle; + ret[idx].textureType = + ret[idx].numSlices > 1 ? TextureType::Texture2DArray : TextureType::Texture2D; + } + + idx++; + } + } + } + + return ret; +} + rdcarray PipeState::GetColorBlends() const { if(IsCaptureLoaded()) diff --git a/renderdoc/api/replay/renderdoc_replay.h b/renderdoc/api/replay/renderdoc_replay.h index 968c0ac5f..118a31031 100644 --- a/renderdoc/api/replay/renderdoc_replay.h +++ b/renderdoc/api/replay/renderdoc_replay.h @@ -593,7 +593,7 @@ Multiple ranges within the store can be queried at once, and are returned in a c :return: The descriptor accesses. :rtype: List[DescriptorAccess] )"); - virtual rdcarray GetDescriptorAccess() = 0; + virtual const rdcarray &GetDescriptorAccess() = 0; DOCUMENT(R"(Retrieve the logical locations for descriptors in a given descriptor store. diff --git a/renderdoc/replay/replay_controller.cpp b/renderdoc/replay/replay_controller.cpp index fbe1eb014..2a32d768f 100644 --- a/renderdoc/replay/replay_controller.cpp +++ b/renderdoc/replay/replay_controller.cpp @@ -131,11 +131,11 @@ rdcarray ReplayController::GetDescriptors(ResourceId descriptorStore return m_pDevice->GetDescriptors(m_pDevice->GetLiveID(descriptorStore), ranges); } -rdcarray ReplayController::GetDescriptorAccess() +const rdcarray &ReplayController::GetDescriptorAccess() { CHECK_REPLAY_THREAD(); - return m_pDevice->GetDescriptorAccess(m_EventID); + return m_PipeState.GetDescriptorAccess(); } rdcarray ReplayController::GetDescriptorLocations( @@ -2257,4 +2257,51 @@ void ReplayController::FetchPipelineState(uint32_t eventId) m_PipeState.SetState(&m_GLPipelineState); else if(m_APIProps.pipelineType == GraphicsAPI::Vulkan) m_PipeState.SetState(&m_VulkanPipelineState); + + rdcarray access = m_pDevice->GetDescriptorAccess(eventId); + rdcarray descs; + rdcarray samps; + descs.reserve(access.size()); + samps.reserve(access.size()); + + // we could collate ranges by descriptor store, but in practice we don't expect descriptors to be + // scattered across multiple stores. So to keep the code simple for now we do a linear sweep + ResourceId store; + rdcarray ranges; + + for(const DescriptorAccess &acc : access) + { + if(acc.descriptorStore != store) + { + if(store != ResourceId()) + { + descs.append(m_pDevice->GetDescriptors(store, ranges)); + samps.append(m_pDevice->GetSamplerDescriptors(store, ranges)); + } + + store = m_pDevice->GetLiveID(acc.descriptorStore); + ranges.clear(); + } + + // if the last range is contiguous with this access, append this access as a new range to query + if(!ranges.empty() && ranges.back().descriptorSize == acc.byteSize && + ranges.back().offset + ranges.back().descriptorSize == acc.byteOffset) + { + ranges.back().count++; + continue; + } + + DescriptorRange range; + range.offset = acc.byteOffset; + range.descriptorSize = acc.byteSize; + ranges.push_back(range); + } + + if(store != ResourceId()) + { + descs.append(m_pDevice->GetDescriptors(store, ranges)); + samps.append(m_pDevice->GetSamplerDescriptors(store, ranges)); + } + + m_PipeState.SetDescriptorAccess(std::move(access), std::move(descs), std::move(samps)); } diff --git a/renderdoc/replay/replay_controller.h b/renderdoc/replay/replay_controller.h index 221e001d2..a9a9c5fed 100644 --- a/renderdoc/replay/replay_controller.h +++ b/renderdoc/replay/replay_controller.h @@ -154,7 +154,7 @@ public: const rdcarray &ranges); rdcarray GetSamplerDescriptors(ResourceId descriptorStore, const rdcarray &ranges); - rdcarray GetDescriptorAccess(); + const rdcarray &GetDescriptorAccess(); rdcarray GetDescriptorLocations(ResourceId descriptorStore, const rdcarray &ranges);