From f256218e17d20fa81caab5be84a2d9a644b1bdaf Mon Sep 17 00:00:00 2001 From: baldurk Date: Wed, 3 Apr 2019 11:53:13 +0100 Subject: [PATCH] Pass bindless feedback data to UI through vulkan pipeline state * Each binding element within an arrayed descriptor has a bool indicating if it's dynamically used or not (which will be set to true if the feedback isn't available). Each descriptor has a uint32_t indicating how many elements are dynamically used - which is useful for the UI to hide the root of an array that has no used elements, or to heuristically decide whether to expand or elide the contents. --- .../VulkanPipelineStateViewer.cpp | 21 +++++- qrenderdoc/Windows/TextureViewer.cpp | 31 +++++--- renderdoc/api/replay/common_pipestate.h | 20 +++++- renderdoc/api/replay/pipestate.inl | 6 ++ renderdoc/api/replay/vk_pipestate.h | 47 ++++++++---- renderdoc/core/image_viewer.cpp | 2 +- renderdoc/core/replay_proxy.cpp | 12 ++-- renderdoc/core/replay_proxy.h | 2 +- renderdoc/driver/d3d11/d3d11_replay.cpp | 2 +- renderdoc/driver/d3d11/d3d11_replay.h | 2 +- renderdoc/driver/d3d12/d3d12_replay.cpp | 2 +- renderdoc/driver/d3d12/d3d12_replay.h | 2 +- renderdoc/driver/gl/gl_replay.cpp | 2 +- renderdoc/driver/gl/gl_replay.h | 2 +- renderdoc/driver/vulkan/vk_replay.cpp | 72 ++++++++++++++++++- renderdoc/driver/vulkan/vk_replay.h | 2 +- renderdoc/replay/replay_controller.cpp | 10 +-- renderdoc/replay/replay_controller.h | 4 +- renderdoc/replay/replay_driver.h | 2 +- 19 files changed, 191 insertions(+), 52 deletions(-) diff --git a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp index 8e848cd4d..8324c528f 100644 --- a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp @@ -744,7 +744,8 @@ QVariantList VulkanPipelineStateViewer::makeSampler(const QString &bindset, cons // arrange like either UVW: WRAP or UV: WRAP, W: CLAMP for(int a = 0; a < 3; a++) { - QString prefix = QString(QLatin1Char("UVW"[a])); + const char *uvw = "UVW"; + QString prefix = QString(QLatin1Char(uvw[a])); if(a == 0 || addr[a] == addr[a - 1]) { @@ -937,10 +938,12 @@ void VulkanPipelineStateViewer::addResourceRow(ShaderReflection *shaderDetails, BindType bindType = BindType::Unknown; ShaderStageMask stageBits = ShaderStageMask::Unknown; bool pushDescriptor = false; + uint32_t dynamicallyUsedCount = 1; if(bindset < pipe.descriptorSets.count() && bind < pipe.descriptorSets[bindset].bindings.count()) { pushDescriptor = pipe.descriptorSets[bindset].pushDescriptor; + dynamicallyUsedCount = pipe.descriptorSets[bindset].bindings[bind].dynamicallyUsedCount; slotBinds = &pipe.descriptorSets[bindset].bindings[bind].binds; bindType = pipe.descriptorSets[bindset].bindings[bind].type; stageBits = pipe.descriptorSets[bindset].bindings[bind].stageFlags; @@ -955,7 +958,7 @@ void VulkanPipelineStateViewer::addResourceRow(ShaderReflection *shaderDetails, bindType = isrw ? BindType::ReadWriteImage : BindType::ReadOnlyImage; } - bool usedSlot = bindMap != NULL && bindMap->used; + bool usedSlot = bindMap != NULL && bindMap->used && dynamicallyUsedCount > 0; bool stageBitsIncluded = bool(stageBits & MaskForStage(stage.stage)); // skip descriptors that aren't for this shader stage @@ -1025,8 +1028,13 @@ void VulkanPipelineStateViewer::addResourceRow(ShaderReflection *shaderDetails, { const VKPipe::BindingElement *descriptorBind = NULL; if(slotBinds != NULL) + { descriptorBind = &(*slotBinds)[idx]; + if(!showNode(usedSlot && descriptorBind->dynamicallyUsed, filledSlot)) + continue; + } + if(arrayLength > 1) { if(shaderRes && !shaderRes->name.isEmpty()) @@ -1315,18 +1323,20 @@ void VulkanPipelineStateViewer::addConstantBlockRow(ShaderReflection *shaderDeta const rdcarray *slotBinds = NULL; BindType bindType = BindType::ConstantBuffer; ShaderStageMask stageBits = ShaderStageMask::Unknown; + uint32_t dynamicallyUsedCount = 1; bool pushDescriptor = false; if(bindset < pipe.descriptorSets.count() && bind < pipe.descriptorSets[bindset].bindings.count()) { pushDescriptor = pipe.descriptorSets[bindset].pushDescriptor; + dynamicallyUsedCount = pipe.descriptorSets[bindset].bindings[bind].dynamicallyUsedCount; slotBinds = &pipe.descriptorSets[bindset].bindings[bind].binds; bindType = pipe.descriptorSets[bindset].bindings[bind].type; stageBits = pipe.descriptorSets[bindset].bindings[bind].stageFlags; } - bool usedSlot = bindMap != NULL && bindMap->used; + bool usedSlot = bindMap != NULL && bindMap->used && dynamicallyUsedCount > 0; bool stageBitsIncluded = bool(stageBits & MaskForStage(stage.stage)); // skip descriptors that aren't for this shader stage @@ -1385,8 +1395,13 @@ void VulkanPipelineStateViewer::addConstantBlockRow(ShaderReflection *shaderDeta { const VKPipe::BindingElement *descriptorBind = NULL; if(slotBinds != NULL) + { descriptorBind = &(*slotBinds)[idx]; + if(!showNode(usedSlot && descriptorBind->dynamicallyUsed, filledSlot)) + continue; + } + if(arrayLength > 1) { if(cblock != NULL && !cblock->name.isEmpty()) diff --git a/qrenderdoc/Windows/TextureViewer.cpp b/qrenderdoc/Windows/TextureViewer.cpp index da64ce5b0..5d36b013b 100644 --- a/qrenderdoc/Windows/TextureViewer.cpp +++ b/qrenderdoc/Windows/TextureViewer.cpp @@ -2095,20 +2095,24 @@ void TextureViewer::InitStageResourcePreviews(ShaderStage stage, const Bindpoint &key = mapping[idx]; const rdcarray *resArray = NULL; + uint32_t dynamicallyUsedResCount = 1; int residx = ResList.indexOf(key); if(residx >= 0) - resArray = &ResList[residx].resources; - - int arrayLen = resArray != NULL ? resArray->count() : 1; - - // it's too expensive in bindless-type cases to create a thumbnail for every array element, so - // for now we create one just for the first element and add an array size indicator to the slot - // name - // for(int arrayIdx = 0; arrayIdx < arrayLen; arrayIdx++) - int arrayIdx = 0; - { + resArray = &ResList[residx].resources; + dynamicallyUsedResCount = ResList[residx].dynamicallyUsedCount; + } + + const bool collapseArray = dynamicallyUsedResCount > 20; + + const int arrayLen = resArray != NULL ? resArray->count() : 1; + + for(int arrayIdx = 0; arrayIdx < arrayLen; arrayIdx++) + { + if(resArray && !resArray->at(arrayIdx).dynamicallyUsed) + continue; + ResourceId id = resArray != NULL ? resArray->at(arrayIdx).resourceId : ResourceId(); CompType typeHint = resArray != NULL ? resArray->at(arrayIdx).typeHint : CompType::Typeless; @@ -2137,8 +2141,10 @@ void TextureViewer::InitStageResourcePreviews(ShaderStage stage, .arg(rw ? lit("RW ") : lit("")) .arg(idx); - if(arrayLen > 1) + if(collapseArray) slotName += QFormatStr(" Arr[%1]").arg(arrayLen); + else + slotName += QFormatStr("[%1]").arg(arrayIdx); if(copy) slotName = tr("SRC"); @@ -2177,6 +2183,9 @@ void TextureViewer::InitStageResourcePreviews(ShaderStage stage, prevIndex++; InitResourcePreview(prev, show ? id : ResourceId(), typeHint, show, follow, bindName, slotName); + + if(collapseArray) + break; } } } diff --git a/renderdoc/api/replay/common_pipestate.h b/renderdoc/api/replay/common_pipestate.h index 36468c939..d99f402e5 100644 --- a/renderdoc/api/replay/common_pipestate.h +++ b/renderdoc/api/replay/common_pipestate.h @@ -232,6 +232,7 @@ struct BoundResource BoundResource() { resourceId = ResourceId(); + dynamicallyUsed = true; firstMip = -1; firstSlice = -1; typeHint = CompType::Typeless; @@ -239,6 +240,7 @@ struct BoundResource BoundResource(ResourceId id) { resourceId = id; + dynamicallyUsed = true; firstMip = -1; firstSlice = -1; typeHint = CompType::Typeless; @@ -264,6 +266,12 @@ struct BoundResource } DOCUMENT("A :class:`~renderdoc.ResourceId` identifying the bound resource."); ResourceId resourceId; + DOCUMENT(R"(``True`` if this binding element is dynamically used. + +Some APIs provide fine-grained usage based on dynamic shader feedback, to support 'bindless' +scenarios where only a small sparse subset of bound resources are actually used. +)"); + bool dynamicallyUsed = true; DOCUMENT("For textures, the highest mip level available on this binding, or -1 for all mips"); int firstMip; DOCUMENT("For textures, the first array slice available on this binding. or -1 for all slices."); @@ -285,7 +293,10 @@ struct BoundResourceArray BoundResourceArray() = default; BoundResourceArray(const BoundResourceArray &) = default; BoundResourceArray(Bindpoint b) : bindPoint(b) {} - BoundResourceArray(Bindpoint b, const rdcarray &r) : bindPoint(b), resources(r) {} + BoundResourceArray(Bindpoint b, const rdcarray &r) : bindPoint(b), resources(r) + { + dynamicallyUsedCount = (uint32_t)r.size(); + } // for convenience for searching the array, we compare only using the BindPoint bool operator==(const BoundResourceArray &o) const { return bindPoint == o.bindPoint; } bool operator!=(const BoundResourceArray &o) const { return !(bindPoint == o.bindPoint); } @@ -295,6 +306,13 @@ struct BoundResourceArray DOCUMENT("The resources at this bind point"); rdcarray resources; + + DOCUMENT(R"(Lists how many bindings in :data:`resources` are dynamically used. + +Some APIs provide fine-grained usage based on dynamic shader feedback, to support 'bindless' +scenarios where only a small sparse subset of bound resources are actually used. +)"); + uint32_t dynamicallyUsedCount = 0; }; DECLARE_REFLECTION_STRUCT(BoundResourceArray); diff --git a/renderdoc/api/replay/pipestate.inl b/renderdoc/api/replay/pipestate.inl index d1785b248..e610a8a36 100644 --- a/renderdoc/api/replay/pipestate.inl +++ b/renderdoc/api/replay/pipestate.inl @@ -1115,9 +1115,12 @@ rdcarray PipeState::GetReadOnlyResources(ShaderStage stage) rdcarray &val = ret.back().resources; val.resize(bind.descriptorCount); + ret.back().dynamicallyUsedCount = bind.dynamicallyUsedCount; + for(uint32_t i = 0; i < bind.descriptorCount; i++) { val[i].resourceId = bind.binds[i].resourceResourceId; + val[i].dynamicallyUsed = bind.binds[i].dynamicallyUsed; val[i].firstMip = (int)bind.binds[i].firstMip; val[i].firstSlice = (int)bind.binds[i].firstSlice; val[i].typeHint = bind.binds[i].viewFormat.compType; @@ -1266,9 +1269,12 @@ rdcarray PipeState::GetReadWriteResources(ShaderStage stage) rdcarray &val = ret.back().resources; val.resize(bind.descriptorCount); + ret.back().dynamicallyUsedCount = bind.dynamicallyUsedCount; + for(uint32_t i = 0; i < bind.descriptorCount; i++) { val[i].resourceId = bind.binds[i].resourceResourceId; + val[i].dynamicallyUsed = bind.binds[i].dynamicallyUsed; val[i].firstMip = (int)bind.binds[i].firstMip; val[i].firstSlice = (int)bind.binds[i].firstSlice; val[i].typeHint = bind.binds[i].viewFormat.compType; diff --git a/renderdoc/api/replay/vk_pipestate.h b/renderdoc/api/replay/vk_pipestate.h index 992536c62..c453107c3 100644 --- a/renderdoc/api/replay/vk_pipestate.h +++ b/renderdoc/api/replay/vk_pipestate.h @@ -37,21 +37,23 @@ struct BindingElement bool operator==(const BindingElement &o) const { - return viewResourceId == o.viewResourceId && resourceResourceId == o.resourceResourceId && - samplerResourceId == o.samplerResourceId && immutableSampler == o.immutableSampler && - viewFormat == o.viewFormat && swizzle[0] == o.swizzle[0] && swizzle[1] == o.swizzle[1] && - swizzle[2] == o.swizzle[2] && swizzle[3] == o.swizzle[3] && firstMip == o.firstMip && - firstSlice == o.firstSlice && numMips == o.numMips && numSlices == o.numSlices && - byteOffset == o.byteOffset && byteSize == o.byteSize && filter == o.filter && - addressU == o.addressU && addressV == o.addressV && addressW == o.addressW && - mipBias == o.mipBias && maxAnisotropy == o.maxAnisotropy && - compareFunction == o.compareFunction && minLOD == o.minLOD && maxLOD == o.maxLOD && - borderColor[0] == o.borderColor[0] && borderColor[1] == o.borderColor[1] && - borderColor[2] == o.borderColor[2] && borderColor[3] == o.borderColor[3] && - unnormalized == o.unnormalized; + return dynamicallyUsed == o.dynamicallyUsed && viewResourceId == o.viewResourceId && + resourceResourceId == o.resourceResourceId && samplerResourceId == o.samplerResourceId && + immutableSampler == o.immutableSampler && viewFormat == o.viewFormat && + swizzle[0] == o.swizzle[0] && swizzle[1] == o.swizzle[1] && swizzle[2] == o.swizzle[2] && + swizzle[3] == o.swizzle[3] && firstMip == o.firstMip && firstSlice == o.firstSlice && + numMips == o.numMips && numSlices == o.numSlices && byteOffset == o.byteOffset && + byteSize == o.byteSize && filter == o.filter && addressU == o.addressU && + addressV == o.addressV && addressW == o.addressW && mipBias == o.mipBias && + maxAnisotropy == o.maxAnisotropy && compareFunction == o.compareFunction && + minLOD == o.minLOD && maxLOD == o.maxLOD && borderColor[0] == o.borderColor[0] && + borderColor[1] == o.borderColor[1] && borderColor[2] == o.borderColor[2] && + borderColor[3] == o.borderColor[3] && unnormalized == o.unnormalized; } bool operator<(const BindingElement &o) const { + if(!(dynamicallyUsed == o.dynamicallyUsed)) + return dynamicallyUsed < o.dynamicallyUsed; if(!(viewResourceId == o.viewResourceId)) return viewResourceId < o.viewResourceId; if(!(resourceResourceId == o.resourceResourceId)) @@ -121,6 +123,15 @@ struct BindingElement ResourceId samplerResourceId; DOCUMENT("``True`` if this is an immutable sampler binding."); bool immutableSampler = false; + DOCUMENT(R"(``True`` if this binding element is dynamically used. + +If set to ``False`` this means that the binding was available to the shader but during execution it +was not referenced. The data gathered for setting this variable is conservative, meaning that only +accesses through arrays will have this calculated to reduce the required feedback bandwidth - single +non-arrayed descriptors may have this value set to ``True`` even if the shader did not use them, +since single descriptors may only be dynamically skipped by control flow. +)"); + bool dynamicallyUsed = true; DOCUMENT("The :class:`ResourceFormat` that the view uses."); ResourceFormat viewFormat; @@ -209,13 +220,15 @@ struct DescriptorBinding bool operator==(const DescriptorBinding &o) const { - return descriptorCount == o.descriptorCount && type == o.type && stageFlags == o.stageFlags && - binds == o.binds; + return descriptorCount == o.descriptorCount && dynamicallyUsedCount == o.dynamicallyUsedCount && + type == o.type && stageFlags == o.stageFlags && binds == o.binds; } bool operator<(const DescriptorBinding &o) const { if(!(descriptorCount == o.descriptorCount)) return descriptorCount < o.descriptorCount; + if(!(dynamicallyUsedCount == o.dynamicallyUsedCount)) + return dynamicallyUsedCount < o.dynamicallyUsedCount; if(!(type == o.type)) return type < o.type; if(!(stageFlags == o.stageFlags)) @@ -228,6 +241,12 @@ struct DescriptorBinding If this binding is empty/non-existant this value will be ``0``. )"); uint32_t descriptorCount = 0; + DOCUMENT(R"(Lists how many bindings in :data:`binds` are dynamically used. Useful to avoid +redundant iteration to determine whether any bindings are present. + +For more information see :data:`VKBindingElement.dynamicallyUsed`. +)"); + uint32_t dynamicallyUsedCount = 0; DOCUMENT("The :class:`BindType` of this binding."); BindType type = BindType::Unknown; DOCUMENT("The :class:`ShaderStageMask` where this binding is visible."); diff --git a/renderdoc/core/image_viewer.cpp b/renderdoc/core/image_viewer.cpp index 82f4faee5..0f07f6d0e 100644 --- a/renderdoc/core/image_viewer.cpp +++ b/renderdoc/core/image_viewer.cpp @@ -176,7 +176,7 @@ public: RDCEraseEl(ret); return ret; } - void SavePipelineState() {} + void SavePipelineState(uint32_t eventId) {} DriverInformation GetDriverInfo() { DriverInformation ret = {}; diff --git a/renderdoc/core/replay_proxy.cpp b/renderdoc/core/replay_proxy.cpp index 02c01e290..ec10d85c2 100644 --- a/renderdoc/core/replay_proxy.cpp +++ b/renderdoc/core/replay_proxy.cpp @@ -1524,13 +1524,15 @@ ShaderDebugTrace ReplayProxy::DebugThread(uint32_t eventId, const uint32_t group } template -void ReplayProxy::Proxied_SavePipelineState(ParamSerialiser ¶mser, ReturnSerialiser &retser) +void ReplayProxy::Proxied_SavePipelineState(ParamSerialiser ¶mser, ReturnSerialiser &retser, + uint32_t eventId) { const ReplayProxyPacket expectedPacket = eReplayProxy_SavePipelineState; ReplayProxyPacket packet = eReplayProxy_SavePipelineState; { BEGIN_PARAMS(); + SERIALISE_ELEMENT(eventId); END_PARAMS(); } @@ -1538,7 +1540,7 @@ void ReplayProxy::Proxied_SavePipelineState(ParamSerialiser ¶mser, ReturnSer REMOTE_EXECUTION(); if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored) { - m_Remote->SavePipelineState(); + m_Remote->SavePipelineState(eventId); if(m_APIProps.pipelineType == GraphicsAPI::D3D11) m_D3D11PipelineState = *m_Remote->GetD3D11PipelineState(); @@ -1636,9 +1638,9 @@ void ReplayProxy::Proxied_SavePipelineState(ParamSerialiser ¶mser, ReturnSer CheckError(packet, expectedPacket); } -void ReplayProxy::SavePipelineState() +void ReplayProxy::SavePipelineState(uint32_t eventId) { - PROXY_FUNCTION(SavePipelineState); + PROXY_FUNCTION(SavePipelineState, eventId); } template @@ -2581,7 +2583,7 @@ bool ReplayProxy::Tick(int type) GetTextureData(ResourceId(), 0, 0, GetTextureDataParams(), dummy); break; } - case eReplayProxy_SavePipelineState: SavePipelineState(); break; + case eReplayProxy_SavePipelineState: SavePipelineState(0); 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 7e6402420..ec828e3bf 100644 --- a/renderdoc/core/replay_proxy.h +++ b/renderdoc/core/replay_proxy.h @@ -453,7 +453,7 @@ public: IMPLEMENT_FUNCTION_PROXIED(std::vector, GetDebugMessages); - IMPLEMENT_FUNCTION_PROXIED(void, SavePipelineState); + IMPLEMENT_FUNCTION_PROXIED(void, SavePipelineState, uint32_t eventId); IMPLEMENT_FUNCTION_PROXIED(void, ReplayLog, uint32_t endEventID, ReplayLogType replayType); IMPLEMENT_FUNCTION_PROXIED(std::vector, GetPassEvents, uint32_t eventId); diff --git a/renderdoc/driver/d3d11/d3d11_replay.cpp b/renderdoc/driver/d3d11/d3d11_replay.cpp index c39631300..9763d67c3 100644 --- a/renderdoc/driver/d3d11/d3d11_replay.cpp +++ b/renderdoc/driver/d3d11/d3d11_replay.cpp @@ -638,7 +638,7 @@ std::vector D3D11Replay::GetTextures() return ret; } -void D3D11Replay::SavePipelineState() +void D3D11Replay::SavePipelineState(uint32_t eventId) { D3D11RenderState *rs = m_pDevice->GetImmediateContext()->GetCurrentPipelineState(); diff --git a/renderdoc/driver/d3d11/d3d11_replay.h b/renderdoc/driver/d3d11/d3d11_replay.h index ea14c67b9..2d3afc18e 100644 --- a/renderdoc/driver/d3d11/d3d11_replay.h +++ b/renderdoc/driver/d3d11/d3d11_replay.h @@ -128,7 +128,7 @@ public: FrameRecord GetFrameRecord(); - void SavePipelineState(); + void SavePipelineState(uint32_t eventId); const D3D11Pipe::State *GetD3D11PipelineState() { return &m_CurPipelineState; } const D3D12Pipe::State *GetD3D12PipelineState() { return NULL; } const GLPipe::State *GetGLPipelineState() { return NULL; } diff --git a/renderdoc/driver/d3d12/d3d12_replay.cpp b/renderdoc/driver/d3d12/d3d12_replay.cpp index ec2d1d82f..57b830856 100644 --- a/renderdoc/driver/d3d12/d3d12_replay.cpp +++ b/renderdoc/driver/d3d12/d3d12_replay.cpp @@ -1220,7 +1220,7 @@ void D3D12Replay::FillRegisterSpaces(const D3D12RenderState::RootSignature &root } } -void D3D12Replay::SavePipelineState() +void D3D12Replay::SavePipelineState(uint32_t eventId) { const D3D12RenderState &rs = m_pDevice->GetQueue()->GetCommandData()->m_RenderState; diff --git a/renderdoc/driver/d3d12/d3d12_replay.h b/renderdoc/driver/d3d12/d3d12_replay.h index a953679ea..9c7d86a11 100644 --- a/renderdoc/driver/d3d12/d3d12_replay.h +++ b/renderdoc/driver/d3d12/d3d12_replay.h @@ -87,7 +87,7 @@ public: FrameRecord GetFrameRecord(); - void SavePipelineState(); + void SavePipelineState(uint32_t eventId); const D3D11Pipe::State *GetD3D11PipelineState() { return NULL; } const D3D12Pipe::State *GetD3D12PipelineState() { return &m_PipelineState; } const GLPipe::State *GetGLPipelineState() { return NULL; } diff --git a/renderdoc/driver/gl/gl_replay.cpp b/renderdoc/driver/gl/gl_replay.cpp index a0d4dbf73..f26a97300 100644 --- a/renderdoc/driver/gl/gl_replay.cpp +++ b/renderdoc/driver/gl/gl_replay.cpp @@ -757,7 +757,7 @@ string GLReplay::DisassembleShader(ResourceId pipeline, const ShaderReflection * return StringFormat::Fmt("; Invalid disassembly target %s", target.c_str()); } -void GLReplay::SavePipelineState() +void GLReplay::SavePipelineState(uint32_t eventId) { GLPipe::State &pipe = m_CurPipelineState; WrappedOpenGL &drv = *m_pDriver; diff --git a/renderdoc/driver/gl/gl_replay.h b/renderdoc/driver/gl/gl_replay.h index 914531099..066af254b 100644 --- a/renderdoc/driver/gl/gl_replay.h +++ b/renderdoc/driver/gl/gl_replay.h @@ -117,7 +117,7 @@ public: FrameRecord GetFrameRecord(); - void SavePipelineState(); + void SavePipelineState(uint32_t eventId); const D3D11Pipe::State *GetD3D11PipelineState() { return NULL; } const D3D12Pipe::State *GetD3D12PipelineState() { return NULL; } const GLPipe::State *GetGLPipelineState() { return &m_CurPipelineState; } diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp index 95e0c3007..30843c81f 100644 --- a/renderdoc/driver/vulkan/vk_replay.cpp +++ b/renderdoc/driver/vulkan/vk_replay.cpp @@ -864,13 +864,19 @@ void VulkanReplay::SetDriverInformation(const VkPhysicalDeviceProperties &props) memcpy(m_DriverInfo.version, versionString.c_str(), versionString.size()); } -void VulkanReplay::SavePipelineState() +void VulkanReplay::SavePipelineState(uint32_t eventId) { const VulkanRenderState &state = m_pDriver->m_RenderState; VulkanCreationInfo &c = m_pDriver->m_CreationInfo; VulkanResourceManager *rm = m_pDriver->GetResourceManager(); + VkMarkerRegion::Begin(StringFormat::Fmt("FetchShaderFeedback for %u", eventId)); + + FetchShaderFeedback(eventId); + + VkMarkerRegion::End(); + m_VulkanPipelineState = VKPipe::State(); m_VulkanPipelineState.pushconsts.resize(state.pushConstSize); @@ -1355,11 +1361,30 @@ void VulkanReplay::SavePipelineState() for(size_t p = 0; p < ARRAY_COUNT(srcs); p++) { + bool hasUsedBinds = false; + const BindIdx *usedBindsData = NULL; + size_t usedBindsSize = 0; + + { + const DynamicUsedBinds &usage = m_BindlessFeedback.Usage[eventId]; + bool curCompute = (p == 1); + if(usage.valid && usage.compute == curCompute) + { + hasUsedBinds = true; + usedBindsData = usage.used.data(); + usedBindsSize = usage.used.size(); + } + } + + BindIdx curBind; + for(size_t i = 0; i < srcs[p]->size(); i++) { ResourceId src = (*srcs[p])[i].descSet; VKPipe::DescriptorSet &dst = (*dsts[p])[i]; + curBind.set = (uint32_t)i; + ResourceId layoutId = m_pDriver->m_DescriptorSetState[src].layout; // push descriptors don't have a real descriptor set backing them @@ -1381,6 +1406,8 @@ void VulkanReplay::SavePipelineState() DescriptorSetSlot *info = m_pDriver->m_DescriptorSetState[src].currentBindings[b]; const DescSetLayout::Binding &layoutBind = c.m_DescSetLayout[layoutId].bindings[b]; + curBind.bind = (uint32_t)b; + bool dynamicOffset = false; dst.bindings[b].descriptorCount = layoutBind.descriptorCount; @@ -1426,6 +1453,49 @@ void VulkanReplay::SavePipelineState() dst.bindings[b].binds.resize(layoutBind.descriptorCount); for(uint32_t a = 0; a < layoutBind.descriptorCount; a++) { + VKPipe::BindingElement &dstel = dst.bindings[b].binds[a]; + + curBind.arrayidx = a; + + // if we have a list of used binds, and this is an array descriptor (so would be + // expected to be in the list), check it for dynamic usage. + if(layoutBind.descriptorCount > 1 && hasUsedBinds) + { + // if we exhausted the list, all other elements are unused + if(usedBindsSize == 0) + { + dstel.dynamicallyUsed = false; + } + else + { + // we never saw the current value of usedBindsData (which is odd, we should have + // when iterating over all descriptors. This could only happen if there's some + // layout mismatch or a feedback bug that lead to an invalid entry in the list). + // Keep advancing until we get to one that is >= our current bind + while(curBind > *usedBindsData && usedBindsSize) + { + usedBindsData++; + usedBindsSize--; + } + + // the next used bind is equal to this one. Mark it as dynamically used, and consume + if(curBind == *usedBindsData) + { + dstel.dynamicallyUsed = true; + usedBindsData++; + usedBindsSize--; + } + // the next used bind is after the current one, this is not used. + else if(curBind < *usedBindsData) + { + dstel.dynamicallyUsed = false; + } + } + } + + if(dstel.dynamicallyUsed) + dst.bindings[b].dynamicallyUsedCount++; + if(layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER || layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) { diff --git a/renderdoc/driver/vulkan/vk_replay.h b/renderdoc/driver/vulkan/vk_replay.h index 403b5193c..7c83bdd2a 100644 --- a/renderdoc/driver/vulkan/vk_replay.h +++ b/renderdoc/driver/vulkan/vk_replay.h @@ -254,7 +254,7 @@ public: FrameRecord GetFrameRecord(); vector GetDebugMessages(); - void SavePipelineState(); + void SavePipelineState(uint32_t eventId); const D3D11Pipe::State *GetD3D11PipelineState() { return NULL; } const D3D12Pipe::State *GetD3D12PipelineState() { return NULL; } const GLPipe::State *GetGLPipelineState() { return NULL; } diff --git a/renderdoc/replay/replay_controller.cpp b/renderdoc/replay/replay_controller.cpp index ae6e5ebb0..bb5e32cd3 100644 --- a/renderdoc/replay/replay_controller.cpp +++ b/renderdoc/replay/replay_controller.cpp @@ -226,7 +226,7 @@ void ReplayController::SetFrameEvent(uint32_t eventId, bool force) m_pDevice->ReplayLog(eventId, eReplay_OnlyDraw); - FetchPipelineState(); + FetchPipelineState(eventId); } } @@ -2060,8 +2060,6 @@ ReplayStatus ReplayController::PostCreateInit(IReplayDriver *device, RDCFile *rd if(status != ReplayStatus::Succeeded) return status; - FetchPipelineState(); - m_APIProps = m_pDevice->GetAPIProperties(); // fetch GCN ISA targets @@ -2095,6 +2093,8 @@ ReplayStatus ReplayController::PostCreateInit(IReplayDriver *device, RDCFile *rd m_Drawcalls.clear(); SetupDrawcallPointers(m_Drawcalls, m_FrameRecord.drawcallList); + FetchPipelineState(m_Drawcalls.back()->eventId); + return ReplayStatus::Succeeded; } @@ -2112,11 +2112,11 @@ APIProperties ReplayController::GetAPIProperties() return m_pDevice->GetAPIProperties(); } -void ReplayController::FetchPipelineState() +void ReplayController::FetchPipelineState(uint32_t eventId) { CHECK_REPLAY_THREAD(); - m_pDevice->SavePipelineState(); + m_pDevice->SavePipelineState(eventId); m_D3D11PipelineState = m_pDevice->GetD3D11PipelineState(); m_D3D12PipelineState = m_pDevice->GetD3D12PipelineState(); diff --git a/renderdoc/replay/replay_controller.h b/renderdoc/replay/replay_controller.h index ac017416b..5c7b54437 100644 --- a/renderdoc/replay/replay_controller.h +++ b/renderdoc/replay/replay_controller.h @@ -139,8 +139,6 @@ public: void SetFrameEvent(uint32_t eventId, bool force); - void FetchPipelineState(); - const D3D11Pipe::State *GetD3D11PipelineState(); const D3D12Pipe::State *GetD3D12PipelineState(); const GLPipe::State *GetGLPipelineState(); @@ -215,6 +213,8 @@ public: private: ReplayStatus PostCreateInit(IReplayDriver *device, RDCFile *rdc); + void FetchPipelineState(uint32_t eventId); + DrawcallDescription *GetDrawcallByEID(uint32_t eventId); bool ContainsMarker(const rdcarray &draws); bool PassEquivalent(const DrawcallDescription &a, const DrawcallDescription &b); diff --git a/renderdoc/replay/replay_driver.h b/renderdoc/replay/replay_driver.h index 03ee268b0..c3023fcf6 100644 --- a/renderdoc/replay/replay_driver.h +++ b/renderdoc/replay/replay_driver.h @@ -111,7 +111,7 @@ public: virtual vector GetUsage(ResourceId id) = 0; - virtual void SavePipelineState() = 0; + virtual void SavePipelineState(uint32_t eventId) = 0; virtual const D3D11Pipe::State *GetD3D11PipelineState() = 0; virtual const D3D12Pipe::State *GetD3D12PipelineState() = 0; virtual const GLPipe::State *GetGLPipelineState() = 0;