diff --git a/qrenderdoc/Code/pyrenderdoc/renderdoc.i b/qrenderdoc/Code/pyrenderdoc/renderdoc.i index 86971ccc4..4014971f6 100644 --- a/qrenderdoc/Code/pyrenderdoc/renderdoc.i +++ b/qrenderdoc/Code/pyrenderdoc/renderdoc.i @@ -393,7 +393,6 @@ TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, VKPipe, DescriptorSet) TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, VKPipe, ImageData) TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, VKPipe, ImageLayout) TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, VKPipe, RenderArea) -TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, VKPipe, SpecializationConstant) TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, VKPipe, XFBBuffer) TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, VKPipe, VertexBuffer) TEMPLATE_NAMESPACE_ARRAY_INSTANTIATE(rdcarray, VKPipe, VertexAttribute) diff --git a/qrenderdoc/Windows/ConstantBufferPreviewer.cpp b/qrenderdoc/Windows/ConstantBufferPreviewer.cpp index 8602c1ad2..8e518fa53 100644 --- a/qrenderdoc/Windows/ConstantBufferPreviewer.cpp +++ b/qrenderdoc/Windows/ConstantBufferPreviewer.cpp @@ -107,6 +107,7 @@ void ConstantBufferPreviewer::OnEventChanged(uint32_t eventId) m_cbuffer = cb.resourceId; uint64_t offset = cb.byteOffset; uint64_t size = cb.byteSize; + bytebuf inlineData = cb.inlineData; ResourceId prevShader = m_shader; @@ -136,26 +137,24 @@ void ConstantBufferPreviewer::OnEventChanged(uint32_t eventId) if(!m_formatOverride.type.members.empty()) { - m_Ctx.Replay().AsyncInvoke([this, offset, size, wasEmpty](IReplayController *r) { - bytebuf data; - if(size > 0) - data = r->GetBufferData(m_cbuffer, offset, size); - rdcarray vars = applyFormatOverride(data); - GUIInvoke::call(this, [this, vars, wasEmpty] { - RDTreeViewExpansionState state; - ui->variables->saveExpansion(state, 0); - setVariables(vars); - if(wasEmpty) - { - // Expand before resizing so that collapsed data will already be visible when expanded - ui->variables->expandAll(); - for(int i = 0; i < 3; i++) - ui->variables->resizeColumnToContents(i); - ui->variables->collapseAll(); - } - ui->variables->applyExpansion(state, 0); + if(!inlineData.empty() && m_cbuffer == ResourceId()) + { + setVariablesPreserveExpansion(applyFormatOverride(inlineData), wasEmpty); + } + else + { + m_Ctx.Replay().AsyncInvoke([this, offset, size, wasEmpty](IReplayController *r) { + bytebuf data; + + if(size > 0 && m_cbuffer != ResourceId()) + data = r->GetBufferData(m_cbuffer, offset, size); + + rdcarray vars = applyFormatOverride(data); + + GUIInvoke::call(this, + [this, vars, wasEmpty] { setVariablesPreserveExpansion(vars, wasEmpty); }); }); - }); + } } else { @@ -327,6 +326,26 @@ void ConstantBufferPreviewer::setVariables(const rdcarray &vars) ui->variables->endUpdate(); } +void ConstantBufferPreviewer::setVariablesPreserveExpansion(const rdcarray &vars, + bool wasEmpty) +{ + RDTreeViewExpansionState state; + ui->variables->saveExpansion(state, 0); + + setVariables(vars); + + if(wasEmpty) + { + // Expand before resizing so that collapsed data will already be visible when expanded + ui->variables->expandAll(); + for(int i = 0; i < 3; i++) + ui->variables->resizeColumnToContents(i); + ui->variables->collapseAll(); + } + + ui->variables->applyExpansion(state, 0); +} + void ConstantBufferPreviewer::updateLabels() { QString bufName = m_Ctx.GetResourceName(m_cbuffer); diff --git a/qrenderdoc/Windows/ConstantBufferPreviewer.h b/qrenderdoc/Windows/ConstantBufferPreviewer.h index 2c3d33bd6..a917ae1fc 100644 --- a/qrenderdoc/Windows/ConstantBufferPreviewer.h +++ b/qrenderdoc/Windows/ConstantBufferPreviewer.h @@ -84,6 +84,7 @@ private: void addVariables(RDTreeWidgetItem *root, const rdcarray &vars); void setVariables(const rdcarray &vars); + void setVariablesPreserveExpansion(const rdcarray &vars, bool wasEmpty); void updateLabels(); diff --git a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp index 852f0ba8e..0ea4e7ef0 100644 --- a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp @@ -1626,12 +1626,28 @@ void VulkanPipelineStateViewer::addConstantBlockRow(ShaderReflection *shaderDeta { setname = QString(); slotname = cblock->name; - name = tr("Push constants"); + if(cblock->compileConstants) + name = tr("Specialization constants"); + else if(descriptorBind && descriptorBind->inlineBlock) + name = tr("Inline uniforms"); + else + name = tr("Push constants"); + vecrange = QString(); sizestr = tr("%1 Variables").arg(numvars); - // could maybe get range from ShaderVariable.reg if it's filled out - // from SPIR-V side. + if(descriptorBind && descriptorBind->inlineBlock) + { + vecrange = QFormatStr("%1 - %2") + .arg(descriptorBind->byteOffset) + .arg(descriptorBind->byteOffset + descriptorBind->byteSize); + } + else if(!cblock->compileConstants) + { + vecrange = QFormatStr("%1 - %2") + .arg(stage.pushConstantRangeByteOffset) + .arg(stage.pushConstantRangeByteOffset + stage.pushConstantRangeByteSize); + } } else { @@ -1850,16 +1866,9 @@ void VulkanPipelineStateViewer::setShaderState(const VKPipe::Shader &stage, ConstantBlock &cblock = shaderDetails->constantBlocks[cb]; if(cblock.bufferBacked == false) { - // could maybe get range from ShaderVariable.reg if it's filled out - // from SPIR-V side. - - RDTreeWidgetItem *node = - new RDTreeWidgetItem({QString(), QString(), cblock.name, tr("Push constants"), QString(), - tr("%1 Variables").arg(cblock.variables.count()), QString()}); - - node->setTag(QVariant::fromValue(VulkanCBufferTag(cb))); - - ubos->addTopLevelItem(node); + addConstantBlockRow( + shaderDetails, stage, stage.bindpointMapping.constantBlocks[cblock.bindPoint].bindset, + stage.bindpointMapping.constantBlocks[cblock.bindPoint].bind, pipe, ubos); } } } @@ -3225,18 +3234,42 @@ void VulkanPipelineStateViewer::exportHTML(QXmlStreamWriter &xml, const VKPipe:: // push constants if(!b.bufferBacked) { + const VKPipe::BindingElement *descriptorBind = NULL; + + if(bindMap.bindset < pipeline.descriptorSets.count() && + bindMap.bind < pipeline.descriptorSets[bindMap.bindset].bindings.count()) + descriptorBind = &pipeline.descriptorSets[bindMap.bindset].bindings[bindMap.bind].binds[0]; + + QString name; + if(b.compileConstants) + name = tr("Specialization constants"); + else if(descriptorBind->inlineBlock) + name = tr("Inline uniforms"); + else + name = tr("Push constants"); + + qulonglong offset = 0, size = 0; + + if(descriptorBind->inlineBlock) + { + offset = descriptorBind->byteOffset; + size = descriptorBind->byteSize; + } + else if(!b.compileConstants) + { + offset = sh.pushConstantRangeByteOffset; + size = sh.pushConstantRangeByteSize; + } + // could maybe get range/size from ShaderVariable.reg if it's filled out // from SPIR-V side. - rows.push_back({QString(), b.name, tr("Push constants"), (qulonglong)0, (qulonglong)0, - b.variables.count(), b.byteSize}); + rows.push_back({QString(), b.name, name, offset, size, b.variables.count(), b.byteSize}); continue; } - const VKPipe::DescriptorSet &set = - pipeline.descriptorSets[sh.bindpointMapping.constantBlocks[i].bindset]; - const VKPipe::DescriptorBinding &bind = - set.bindings[sh.bindpointMapping.constantBlocks[i].bind]; + const VKPipe::DescriptorSet &set = pipeline.descriptorSets[bindMap.bindset]; + const VKPipe::DescriptorBinding &bind = set.bindings[bindMap.bind]; QString setname = QString::number(bindMap.bindset); diff --git a/renderdoc/api/replay/common_pipestate.h b/renderdoc/api/replay/common_pipestate.h index 7cff9039d..f731051cf 100644 --- a/renderdoc/api/replay/common_pipestate.h +++ b/renderdoc/api/replay/common_pipestate.h @@ -409,6 +409,13 @@ struct BoundCBuffer uint64_t byteOffset = 0; DOCUMENT("The size in bytes for the constant buffer. Access outside this size returns 0."); uint64_t byteSize = 0; + + DOCUMENT(R"(The inline byte data for this constant buffer, if this binding is not backed by a +typical buffer. + +:type: bytes +)"); + bytebuf inlineData; }; DECLARE_REFLECTION_STRUCT(BoundCBuffer); diff --git a/renderdoc/api/replay/pipestate.inl b/renderdoc/api/replay/pipestate.inl index 77c779480..cbd7c070d 100644 --- a/renderdoc/api/replay/pipestate.inl +++ b/renderdoc/api/replay/pipestate.inl @@ -905,9 +905,7 @@ rdcarray PipeState::GetVertexInputs() const BoundCBuffer PipeState::GetConstantBuffer(ShaderStage stage, uint32_t BufIdx, uint32_t ArrayIdx) const { - ResourceId buf; - uint64_t ByteOffset = 0; - uint64_t ByteSize = 0; + BoundCBuffer ret; if(IsCaptureLoaded()) { @@ -925,9 +923,9 @@ BoundCBuffer PipeState::GetConstantBuffer(ShaderStage stage, uint32_t BufIdx, ui const D3D11Pipe::ConstantBuffer &descriptor = s.constantBuffers[bind.bind]; - buf = descriptor.resourceId; - ByteOffset = descriptor.vecOffset * 4 * sizeof(float); - ByteSize = descriptor.vecCount * 4 * sizeof(float); + ret.resourceId = descriptor.resourceId; + ret.byteOffset = descriptor.vecOffset * 4 * sizeof(float); + ret.byteSize = descriptor.vecCount * 4 * sizeof(float); } } else if(IsCaptureD3D12()) @@ -956,10 +954,16 @@ BoundCBuffer PipeState::GetConstantBuffer(ShaderStage stage, uint32_t BufIdx, ui const D3D12Pipe::ConstantBuffer &cb = element.constantBuffers[j]; if(cb.bind == (uint32_t)shaderReg) { - buf = cb.resourceId; - ByteOffset = cb.byteOffset; - ByteSize = cb.byteSize; found = true; + + ret.resourceId = cb.resourceId; + ret.byteOffset = cb.byteOffset; + ret.byteSize = cb.byteSize; + if(element.immediate) + { + ret.inlineData.resize(cb.rootValues.byteSize()); + memcpy(ret.inlineData.data(), cb.rootValues.data(), ret.inlineData.size()); + } break; } } @@ -981,12 +985,12 @@ BoundCBuffer PipeState::GetConstantBuffer(ShaderStage stage, uint32_t BufIdx, ui { const GLPipe::Buffer &b = m_GL->uniformBuffers[uboIdx]; - buf = b.resourceId; - ByteOffset = b.byteOffset; - ByteSize = b.byteSize; + ret.resourceId = b.resourceId; + ret.byteOffset = b.byteOffset; + ret.byteSize = b.byteSize; - if(ByteSize == 0) - ByteSize = ~0ULL; + if(ret.byteSize == 0) + ret.byteSize = ~0ULL; } } } @@ -1004,33 +1008,95 @@ BoundCBuffer PipeState::GetConstantBuffer(ShaderStage stage, uint32_t BufIdx, ui if(s.reflection->constantBlocks[BufIdx].bufferBacked == false) { - BoundCBuffer ret; - // dummy value, it would be nice to fetch this properly - ret.byteSize = 1024; + if(s.reflection->constantBlocks[BufIdx].compileConstants) + { + ret.inlineData = s.specializationData; + ret.byteSize = ret.inlineData.size(); + } + else + { + ret.inlineData.resize((size_t)ret.byteSize); + + const bytebuf *src = NULL; + + // push constants have a magic bindset value higher than any descriptor set + if(bind.bindset < pipe.descriptorSets.count()) + { + if(bind.bind >= pipe.descriptorSets[bind.bindset].bindings.count() || + ArrayIdx >= pipe.descriptorSets[bind.bindset].bindings[bind.bind].binds.size()) + return BoundCBuffer(); + + src = &pipe.descriptorSets[bind.bindset].inlineData; + + const VKPipe::BindingElement &descriptorBind = + pipe.descriptorSets[bind.bindset].bindings[bind.bind].binds[ArrayIdx]; + + ret.byteOffset = descriptorBind.byteOffset; + ret.byteSize = descriptorBind.byteSize; + } + else + { + src = &m_Vulkan->pushconsts; + + switch(stage) + { + case ShaderStage::Vertex: + ret.byteOffset = m_Vulkan->vertexShader.pushConstantRangeByteOffset; + ret.byteSize = m_Vulkan->vertexShader.pushConstantRangeByteSize; + break; + case ShaderStage::Tess_Control: + ret.byteOffset = m_Vulkan->tessControlShader.pushConstantRangeByteOffset; + ret.byteSize = m_Vulkan->tessControlShader.pushConstantRangeByteSize; + break; + case ShaderStage::Tess_Eval: + ret.byteOffset = m_Vulkan->tessEvalShader.pushConstantRangeByteOffset; + ret.byteSize = m_Vulkan->tessEvalShader.pushConstantRangeByteSize; + break; + case ShaderStage::Geometry: + ret.byteOffset = m_Vulkan->geometryShader.pushConstantRangeByteOffset; + ret.byteSize = m_Vulkan->geometryShader.pushConstantRangeByteSize; + break; + case ShaderStage::Fragment: + ret.byteOffset = m_Vulkan->fragmentShader.pushConstantRangeByteOffset; + ret.byteSize = m_Vulkan->fragmentShader.pushConstantRangeByteSize; + break; + case ShaderStage::Compute: + ret.byteOffset = m_Vulkan->computeShader.pushConstantRangeByteOffset; + ret.byteSize = m_Vulkan->computeShader.pushConstantRangeByteSize; + break; + default: break; + } + } + + if(ret.byteOffset > src->size()) + ret.byteSize = 0; + else if(ret.byteOffset + ret.byteSize > src->size()) + ret.byteSize = src->size() - (ret.byteOffset + ret.byteSize); + + // consume the byteoffset here when copying data from the source data + ret.inlineData.resize((size_t)ret.byteSize); + memcpy(ret.inlineData.data(), src->data() + ret.byteOffset, (size_t)ret.byteSize); + ret.byteOffset = 0; + } + return ret; } if(bind.bindset >= pipe.descriptorSets.count() || bind.bind >= pipe.descriptorSets[bind.bindset].bindings.count() || - ArrayIdx > pipe.descriptorSets[bind.bindset].bindings[bind.bind].binds.size()) + ArrayIdx >= pipe.descriptorSets[bind.bindset].bindings[bind.bind].binds.size()) return BoundCBuffer(); const VKPipe::BindingElement &descriptorBind = pipe.descriptorSets[bind.bindset].bindings[bind.bind].binds[ArrayIdx]; - buf = descriptorBind.resourceResourceId; - ByteOffset = descriptorBind.byteOffset; - ByteSize = descriptorBind.byteSize; + ret.resourceId = descriptorBind.resourceResourceId; + ret.byteOffset = descriptorBind.byteOffset; + ret.byteSize = descriptorBind.byteSize; } } } - BoundCBuffer ret; - - ret.resourceId = buf; - ret.byteOffset = ByteOffset; - ret.byteSize = ByteSize; - return ret; } diff --git a/renderdoc/api/replay/shader_types.h b/renderdoc/api/replay/shader_types.h index d885939cd..9b0f554d7 100644 --- a/renderdoc/api/replay/shader_types.h +++ b/renderdoc/api/replay/shader_types.h @@ -1025,7 +1025,8 @@ struct ConstantBlock bool operator==(const ConstantBlock &o) const { return name == o.name && variables == o.variables && bindPoint == o.bindPoint && - byteSize == o.byteSize && bufferBacked == o.bufferBacked; + byteSize == o.byteSize && bufferBacked == o.bufferBacked && + compileConstants == o.compileConstants; } bool operator<(const ConstantBlock &o) const { @@ -1039,6 +1040,8 @@ struct ConstantBlock return byteSize < o.byteSize; if(!(bufferBacked == o.bufferBacked)) return bufferBacked < o.bufferBacked; + if(!(compileConstants == o.compileConstants)) + return compileConstants < o.compileConstants; return false; } DOCUMENT("The name of this constant block, may be empty on some APIs."); @@ -1051,14 +1054,16 @@ struct ConstantBlock DOCUMENT(R"(The bindpoint for this block. This is an index in the :data:`ShaderBindpointMapping.constantBlocks` list. )"); - int32_t bindPoint; + int32_t bindPoint = 0; DOCUMENT("The total number of bytes consumed by all of the constants contained in this block."); - uint32_t byteSize; + uint32_t byteSize = 0; DOCUMENT(R"(``True`` if the contents are stored in a buffer of memory. If not then they are set by some other API-specific method, such as direct function calls or they may be compile-time specialisation constants. )"); - bool bufferBacked; + bool bufferBacked = true; + DOCUMENT("``True`` if this is a virtual buffer listing compile-time specialisation constants."); + bool compileConstants = false; }; DECLARE_REFLECTION_STRUCT(ConstantBlock); diff --git a/renderdoc/api/replay/vk_pipestate.h b/renderdoc/api/replay/vk_pipestate.h index 34627d925..0f553450e 100644 --- a/renderdoc/api/replay/vk_pipestate.h +++ b/renderdoc/api/replay/vk_pipestate.h @@ -144,7 +144,11 @@ since single descriptors may only be dynamically skipped by control flow. DOCUMENT("For 3D textures and texture arrays - the number of array slices in the view."); uint32_t numSlices = 0; - DOCUMENT("For buffers - the byte offset where the buffer view starts in the underlying buffer."); + DOCUMENT(R"(For buffers - the byte offset where the buffer view starts in the underlying buffer. + +For inline block uniforms (see :data:`inlineBlock`) this is the byte offset into the descriptor +set's inline block data. +)"); uint64_t byteOffset = 0; DOCUMENT("For buffers - how many bytes are in this buffer view."); uint64_t byteSize = 0; @@ -294,7 +298,8 @@ struct DescriptorSet { return layoutResourceId == o.layoutResourceId && descriptorSetResourceId == o.descriptorSetResourceId && - pushDescriptor == o.pushDescriptor && bindings == o.bindings; + pushDescriptor == o.pushDescriptor && bindings == o.bindings && + inlineData == o.inlineData; } bool operator<(const DescriptorSet &o) const { @@ -306,6 +311,8 @@ struct DescriptorSet return pushDescriptor < o.pushDescriptor; if(!(bindings == o.bindings)) return bindings < o.bindings; + if(!(inlineData == o.inlineData)) + return inlineData < o.inlineData; return false; } DOCUMENT("The :class:`ResourceId` of the descriptor set layout that matches this set."); @@ -321,6 +328,13 @@ This list is indexed by the binding, so it may be sparse (some entries do not co :type: List[VKDescriptorBinding] )"); rdcarray bindings; + + DOCUMENT(R"(The inline byte data within this descriptor set. Individual bindings will have an +offset and size into this buffer. + +:type: bytes +)"); + bytebuf inlineData; }; DOCUMENT("Describes the object and descriptor set bindings of a Vulkan pipeline object."); @@ -526,32 +540,6 @@ struct VertexInput rdcarray vertexBuffers; }; -DOCUMENT("The provided value for a specialization constant."); -struct SpecializationConstant -{ - DOCUMENT(""); - SpecializationConstant() = default; - SpecializationConstant(const SpecializationConstant &) = default; - SpecializationConstant &operator=(const SpecializationConstant &) = default; - - bool operator==(const SpecializationConstant &o) const - { - return specializationId == o.specializationId && data == o.data; - } - bool operator<(const SpecializationConstant &o) const - { - if(!(specializationId == o.specializationId)) - return specializationId < o.specializationId; - if(!(data == o.data)) - return data < o.data; - return false; - } - DOCUMENT("The specialization ID"); - uint32_t specializationId = 0; - DOCUMENT("A ``bytes`` with the contents of the constant."); - bytebuf data; -}; - DOCUMENT("Describes a Vulkan shader stage."); struct Shader { @@ -579,11 +567,20 @@ struct Shader DOCUMENT("A :class:`ShaderStage` identifying which stage this shader is bound to."); ShaderStage stage = ShaderStage::Vertex; - DOCUMENT(R"(The provided specialization constants. + DOCUMENT("The byte offset into the push constant data that is visible to this shader."); + uint32_t pushConstantRangeByteOffset = 0; -:type: List[VKSpecializationConstant] + DOCUMENT("The number of bytes in the push constant data that is visible to this shader."); + uint32_t pushConstantRangeByteSize = 0; + + DOCUMENT(R"(The provided specialization constant data. Shader constants store the byte offset into +this buffer as their byteOffset. This data includes the applied specialization constants over the +top of the default values, so it is safe to read any constant from here and get the correct current +value. + +:type: bytes )"); - rdcarray specialization; + bytebuf specializationData; }; DOCUMENT("Describes the state of the fixed-function tessellator."); @@ -1319,7 +1316,6 @@ DECLARE_REFLECTION_STRUCT(VKPipe::VertexAttribute); DECLARE_REFLECTION_STRUCT(VKPipe::VertexBinding); DECLARE_REFLECTION_STRUCT(VKPipe::VertexBuffer); DECLARE_REFLECTION_STRUCT(VKPipe::VertexInput); -DECLARE_REFLECTION_STRUCT(VKPipe::SpecializationConstant); DECLARE_REFLECTION_STRUCT(VKPipe::Shader); DECLARE_REFLECTION_STRUCT(VKPipe::Tessellation); DECLARE_REFLECTION_STRUCT(VKPipe::XFBBuffer); diff --git a/renderdoc/driver/d3d12/d3d12_replay.cpp b/renderdoc/driver/d3d12/d3d12_replay.cpp index 4dc3793c8..7ec762846 100644 --- a/renderdoc/driver/d3d12/d3d12_replay.cpp +++ b/renderdoc/driver/d3d12/d3d12_replay.cpp @@ -954,6 +954,8 @@ void D3D12Replay::FillRootElements(const D3D12RenderState::RootSignature &rootSi element.constantBuffers.push_back(D3D12Pipe::ConstantBuffer(p.Constants.ShaderRegister)); D3D12Pipe::ConstantBuffer &cb = element.constantBuffers.back(); + cb.resourceId = ResourceId(); + cb.byteOffset = 0; cb.byteSize = uint32_t(sizeof(uint32_t) * p.Constants.Num32BitValues); if(rootEl < rootSig.sigelems.size()) diff --git a/renderdoc/driver/shaders/spirv/spirv_reflect.cpp b/renderdoc/driver/shaders/spirv/spirv_reflect.cpp index 0743630bf..f236bdc81 100644 --- a/renderdoc/driver/shaders/spirv/spirv_reflect.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_reflect.cpp @@ -46,7 +46,7 @@ void FillSpecConstantVariables(ResourceId shader, const rdcarray { for(size_t v = 0; v < invars.size() && v < outvars.size(); v++) { - if(specInfo[i].specID == invars[v].byteOffset) + if(specInfo[i].specID * sizeof(uint64_t) == invars[v].byteOffset) { outvars[v].value.u64v[0] = specInfo[i].value; } @@ -1115,7 +1115,7 @@ void Reflector::MakeReflection(const GraphicsAPI sourceAPI, const ShaderStage st ShaderConstant spec; MakeConstantBlockVariable(spec, pointerTypes, dataTypes[c.type], name, decorations[c.id], specInfo); - spec.byteOffset = decorations[c.id].specID; + spec.byteOffset = decorations[c.id].specID * sizeof(uint64_t); spec.defaultValue = c.value.value.u64v[0]; specblock.variables.push_back(spec); } @@ -1125,6 +1125,7 @@ void Reflector::MakeReflection(const GraphicsAPI sourceAPI, const ShaderStage st { specblock.name = "Specialization Constants"; specblock.bufferBacked = false; + specblock.compileConstants = true; specblock.byteSize = 0; Bindpoint bindmap; diff --git a/renderdoc/driver/vulkan/vk_common.cpp b/renderdoc/driver/vulkan/vk_common.cpp index 3a1fffce4..b9daf2ff1 100644 --- a/renderdoc/driver/vulkan/vk_common.cpp +++ b/renderdoc/driver/vulkan/vk_common.cpp @@ -521,6 +521,25 @@ int StageIndex(VkShaderStageFlagBits stageFlag) return 0; } +VkShaderStageFlags ShaderMaskFromIndex(size_t index) +{ + VkShaderStageFlagBits mask[] = { + VK_SHADER_STAGE_VERTEX_BIT, + VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, + VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, + VK_SHADER_STAGE_GEOMETRY_BIT, + VK_SHADER_STAGE_FRAGMENT_BIT, + VK_SHADER_STAGE_COMPUTE_BIT, + }; + + if(index < ARRAY_COUNT(mask)) + return mask[index]; + + RDCERR("Unrecognised shader stage index %d", index); + + return 0; +} + void DoPipelineBarrier(VkCommandBuffer cmd, size_t count, const VkImageMemoryBarrier *barriers) { RDCASSERT(cmd != VK_NULL_HANDLE); diff --git a/renderdoc/driver/vulkan/vk_common.h b/renderdoc/driver/vulkan/vk_common.h index 59df4a1d7..c09fe6a93 100644 --- a/renderdoc/driver/vulkan/vk_common.h +++ b/renderdoc/driver/vulkan/vk_common.h @@ -102,6 +102,7 @@ void DoPipelineBarrier(VkCommandBuffer cmd, size_t count, const VkMemoryBarrier int SampleCount(VkSampleCountFlagBits countFlag); int SampleIndex(VkSampleCountFlagBits countFlag); int StageIndex(VkShaderStageFlagBits stageFlag); +VkShaderStageFlags ShaderMaskFromIndex(size_t index); struct PackedWindowHandle { diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp index 36591d0eb..c6ef01c21 100644 --- a/renderdoc/driver/vulkan/vk_replay.cpp +++ b/renderdoc/driver/vulkan/vk_replay.cpp @@ -1059,6 +1059,8 @@ void VulkanReplay::SavePipelineState(uint32_t eventId) m_VulkanPipelineState.compute.pipelineLayoutResourceId = rm->GetOriginalID(p.layout); + const VulkanCreationInfo::PipelineLayout &pl = c.m_PipelineLayout[p.layout]; + m_VulkanPipelineState.compute.flags = p.flags; VKPipe::Shader &stage = m_VulkanPipelineState.computeShader; @@ -1074,13 +1076,43 @@ void VulkanReplay::SavePipelineState(uint32_t eventId) if(p.shaders[i].refl) stage.reflection = p.shaders[i].refl; - stage.specialization.resize(p.shaders[i].specialization.size()); - for(size_t s = 0; s < p.shaders[i].specialization.size(); s++) + stage.pushConstantRangeByteOffset = stage.pushConstantRangeByteSize = 0; + for(const VkPushConstantRange &pr : pl.pushRanges) { - const SpecConstant &spec = p.shaders[i].specialization[s]; - stage.specialization[s].specializationId = spec.specID; - stage.specialization[s].data.resize(spec.dataSize); - memcpy(stage.specialization[s].data.data(), &spec.value, spec.dataSize); + if(pr.stageFlags & VK_SHADER_STAGE_COMPUTE_BIT) + { + stage.pushConstantRangeByteOffset = pr.offset; + stage.pushConstantRangeByteSize = pr.size; + break; + } + } + + stage.specializationData.clear(); + + // set up the defaults + if(p.shaders[i].mapping && p.shaders[i].refl) + { + for(size_t cb = 0; cb < p.shaders[i].mapping->constantBlocks.size(); cb++) + { + if(p.shaders[i].mapping->constantBlocks[cb].bindset == SpecializationConstantBindSet) + { + for(const ShaderConstant &sc : p.shaders[i].refl->constantBlocks[cb].variables) + { + stage.specializationData.resize_for_index(sc.byteOffset + sizeof(uint64_t)); + memcpy(stage.specializationData.data() + sc.byteOffset, &sc.defaultValue, + sizeof(uint64_t)); + } + break; + } + } + } + + // apply any specializations + for(const SpecConstant &s : p.shaders[i].specialization) + { + size_t offs = s.specID * sizeof(uint64_t); + stage.specializationData.resize_for_index(offs + sizeof(uint64_t)); + memcpy(stage.specializationData.data() + offs, &s.value, s.dataSize); } } } @@ -1097,6 +1129,8 @@ void VulkanReplay::SavePipelineState(uint32_t eventId) m_VulkanPipelineState.graphics.pipelineLayoutResourceId = rm->GetOriginalID(p.layout); + const VulkanCreationInfo::PipelineLayout &pl = c.m_PipelineLayout[p.layout]; + m_VulkanPipelineState.graphics.flags = p.flags; // Input Assembly @@ -1157,13 +1191,43 @@ void VulkanReplay::SavePipelineState(uint32_t eventId) if(p.shaders[i].refl) stages[i]->reflection = p.shaders[i].refl; - stages[i]->specialization.resize(p.shaders[i].specialization.size()); - for(size_t s = 0; s < p.shaders[i].specialization.size(); s++) + stages[i]->pushConstantRangeByteOffset = stages[i]->pushConstantRangeByteSize = 0; + for(const VkPushConstantRange &pr : pl.pushRanges) { - const SpecConstant &spec = p.shaders[i].specialization[s]; - stages[i]->specialization[s].specializationId = spec.specID; - stages[i]->specialization[s].data.resize(spec.dataSize); - memcpy(stages[i]->specialization[s].data.data(), &spec.value, spec.dataSize); + if(pr.stageFlags & ShaderMaskFromIndex(i)) + { + stages[i]->pushConstantRangeByteOffset = pr.offset; + stages[i]->pushConstantRangeByteSize = pr.size; + break; + } + } + + stages[i]->specializationData.clear(); + + // set up the defaults + if(p.shaders[i].mapping && p.shaders[i].refl) + { + for(size_t cb = 0; cb < p.shaders[i].mapping->constantBlocks.size(); cb++) + { + if(p.shaders[i].mapping->constantBlocks[cb].bindset == SpecializationConstantBindSet) + { + for(const ShaderConstant &sc : p.shaders[i].refl->constantBlocks[cb].variables) + { + stages[i]->specializationData.resize_for_index(sc.byteOffset + sizeof(uint64_t)); + memcpy(stages[i]->specializationData.data() + sc.byteOffset, &sc.defaultValue, + sizeof(uint64_t)); + } + break; + } + } + } + + // apply any specializations + for(const SpecConstant &s : p.shaders[i].specialization) + { + size_t offs = s.specID * sizeof(uint64_t); + stages[i]->specializationData.resize_for_index(offs + sizeof(uint64_t)); + memcpy(stages[i]->specializationData.data() + offs, &s.value, s.dataSize); } } @@ -1600,6 +1664,8 @@ void VulkanReplay::SavePipelineState(uint32_t eventId) const uint32_t *srcOffset = (*srcs[p])[i].offsets.begin(); VKPipe::DescriptorSet &dst = (*dsts[p])[i]; + dst.inlineData = m_pDriver->m_DescriptorSetState[src].data.inlineBytes; + curBind.bindset = (uint32_t)i; ResourceId layoutId = m_pDriver->m_DescriptorSetState[src].layout; @@ -1869,7 +1935,7 @@ void VulkanReplay::SavePipelineState(uint32_t eventId) dst.bindings[b].binds[a].viewResourceId = ResourceId(); dst.bindings[b].binds[a].resourceResourceId = ResourceId(); dst.bindings[b].binds[a].inlineBlock = true; - dst.bindings[b].binds[a].byteOffset = 0; + dst.bindings[b].binds[a].byteOffset = info[a].inlineOffset; dst.bindings[b].binds[a].byteSize = descriptorCount; } else if(layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER || @@ -2034,7 +2100,7 @@ void VulkanReplay::FillCBufferVariables(ResourceId pipeline, ResourceId shader, if(pipeIt != m_pDriver->m_CreationInfo.m_Pipeline.end()) { - auto specInfo = + const rdcarray &specInfo = pipeIt->second.shaders[it->second.GetReflection(entryPoint, pipeline).stageIndex].specialization; FillSpecConstantVariables(refl.resourceId, c.variables, outvars, specInfo); diff --git a/renderdoc/replay/renderdoc_serialise.inl b/renderdoc/replay/renderdoc_serialise.inl index ddaaad8d7..0c5cb8733 100644 --- a/renderdoc/replay/renderdoc_serialise.inl +++ b/renderdoc/replay/renderdoc_serialise.inl @@ -215,6 +215,7 @@ void DoSerialise(SerialiserType &ser, ConstantBlock &el) SERIALISE_MEMBER(bindPoint); SERIALISE_MEMBER(byteSize); SERIALISE_MEMBER(bufferBacked); + SERIALISE_MEMBER(compileConstants); SIZE_CHECK(64); } @@ -1920,7 +1921,9 @@ void DoSerialise(SerialiserType &ser, VKPipe::DescriptorSet &el) SERIALISE_MEMBER(bindings); - SIZE_CHECK(48); + SERIALISE_MEMBER(inlineData); + + SIZE_CHECK(72); } template @@ -1997,15 +2000,6 @@ void DoSerialise(SerialiserType &ser, VKPipe::VertexInput &el) SIZE_CHECK(72); } -template -void DoSerialise(SerialiserType &ser, VKPipe::SpecializationConstant &el) -{ - SERIALISE_MEMBER(specializationId); - SERIALISE_MEMBER(data); - - SIZE_CHECK(32); -} - template void DoSerialise(SerialiserType &ser, VKPipe::Shader &el) { @@ -2017,9 +2011,11 @@ void DoSerialise(SerialiserType &ser, VKPipe::Shader &el) SERIALISE_MEMBER(bindpointMapping); SERIALISE_MEMBER(stage); - SERIALISE_MEMBER(specialization); + SERIALISE_MEMBER(pushConstantRangeByteOffset); + SERIALISE_MEMBER(pushConstantRangeByteSize); + SERIALISE_MEMBER(specializationData); - SIZE_CHECK(192); + SIZE_CHECK(200); } template @@ -2278,7 +2274,7 @@ void DoSerialise(SerialiserType &ser, VKPipe::State &el) SERIALISE_MEMBER(conditionalRendering); - SIZE_CHECK(1928); + SIZE_CHECK(1976); } #pragma endregion Vulkan pipeline state @@ -2380,7 +2376,6 @@ INSTANTIATE_SERIALISE_TYPE(VKPipe::DescriptorSet) INSTANTIATE_SERIALISE_TYPE(VKPipe::Pipeline) INSTANTIATE_SERIALISE_TYPE(VKPipe::VertexAttribute) INSTANTIATE_SERIALISE_TYPE(VKPipe::VertexInput) -INSTANTIATE_SERIALISE_TYPE(VKPipe::SpecializationConstant) INSTANTIATE_SERIALISE_TYPE(VKPipe::Shader) INSTANTIATE_SERIALISE_TYPE(VKPipe::ViewState) INSTANTIATE_SERIALISE_TYPE(VKPipe::ColorBlendState)