Skip to content

Commit c65c3ab

Browse files
committed
Move push constants promotion to "PipelineLayoutVk::Create" , PipelineLayoutVk should be completely responsible for initializing the layout. (DiligentGraphics#747)
1 parent c2ed69c commit c65c3ab

File tree

4 files changed

+62
-91
lines changed

4 files changed

+62
-91
lines changed

Graphics/GraphicsEngineVulkan/include/PipelineLayoutVk.hpp

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,6 @@ namespace Diligent
4040
class RenderDeviceVkImpl;
4141
class PipelineResourceSignatureVkImpl;
4242

43-
/// Push constant information extracted from shaders or selected from inline constants
44-
struct PushConstantInfoVk
45-
{
46-
VkShaderStageFlags StageFlags = 0;
47-
Uint32 Size = 0;
48-
Uint32 SignatureIndex = ~0u; // Index of the signature containing the push constant (~0u if none)
49-
Uint32 ResourceIndex = ~0u; // Resource index within the signature (~0u if none)
50-
};
51-
5243
static constexpr Uint32 INVALID_PUSH_CONSTANT_INDEX = ~0u;
5344

5445
/// Implementation of the Diligent::PipelineLayoutVk class
@@ -60,8 +51,7 @@ class PipelineLayoutVk
6051

6152
void Create(RenderDeviceVkImpl* pDeviceVk,
6253
RefCntAutoPtr<PipelineResourceSignatureVkImpl> ppSignatures[],
63-
Uint32 SignatureCount,
64-
const PushConstantInfoVk& PushConstant = {}) noexcept(false);
54+
Uint32 SignatureCount) noexcept(false);
6555
void Release(RenderDeviceVkImpl* pDeviceVkImpl, Uint64 CommandQueueMask);
6656

6757
VkPipelineLayout GetVkPipelineLayout() const { return m_VkPipelineLayout; }

Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,11 +134,9 @@ class PipelineStateVkImpl final : public PipelineStateBase<EngineVkImplTraits>
134134
void InitializePipeline(const ComputePipelineStateCreateInfo& CreateInfo);
135135
void InitializePipeline(const RayTracingPipelineStateCreateInfo& CreateInfo);
136136

137-
bool InitPushConstantInfoFromSignatures(PushConstantInfoVk& PushConstant,
138-
TShaderStages& ShaderStages) const noexcept(false);
137+
void ValidateShaderPushConstants(const TShaderStages& ShaderStages) const noexcept(false);
139138

140-
void PatchShaderConvertUniformBufferToPushConstant(const PushConstantInfoVk& PushConstant,
141-
TShaderStages& ShaderStages) const noexcept(false);
139+
void PatchShaderConvertUniformBufferToPushConstant(TShaderStages& ShaderStages) const noexcept(false);
142140

143141
// TPipelineStateBase::Construct needs access to InitializePipeline
144142
friend TPipelineStateBase;

Graphics/GraphicsEngineVulkan/src/PipelineLayoutVk.cpp

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,7 @@ void PipelineLayoutVk::Release(RenderDeviceVkImpl* pDeviceVk, Uint64 CommandQueu
6161

6262
void PipelineLayoutVk::Create(RenderDeviceVkImpl* pDeviceVk,
6363
RefCntAutoPtr<PipelineResourceSignatureVkImpl> ppSignatures[],
64-
Uint32 SignatureCount,
65-
const PushConstantInfoVk& PushConstant) noexcept(false)
64+
Uint32 SignatureCount) noexcept(false)
6665
{
6766
VERIFY(m_DescrSetCount == 0 && !m_VkPipelineLayout, "This pipeline layout is already initialized");
6867

@@ -72,6 +71,12 @@ void PipelineLayoutVk::Create(RenderDeviceVkImpl* pDe
7271
Uint32 DynamicUniformBufferCount = 0;
7372
Uint32 DynamicStorageBufferCount = 0;
7473

74+
// Extract push constant info from signatures.
75+
// Vulkan allows only one push constant range per pipeline layout.
76+
// DiligentCore allows multiple inline constant resources, so we promote only the first inline constant
77+
// from resource signatures to push constants. Other inline constants remain uniform buffers.
78+
const PipelineResourceDesc* pPushConstantResDesc = nullptr;
79+
7580
for (Uint32 BindInd = 0; BindInd < SignatureCount; ++BindInd)
7681
{
7782
// Signatures are arranged by binding index by PipelineStateBase::CopyResourceSignatures
@@ -95,6 +100,29 @@ void PipelineLayoutVk::Create(RenderDeviceVkImpl* pDe
95100
#ifdef DILIGENT_DEBUG
96101
m_DbgMaxBindIndex = std::max(m_DbgMaxBindIndex, Uint32{pSignature->GetDesc().BindingIndex});
97102
#endif
103+
104+
// Find the first inline constant resource (only if not already found)
105+
if (pPushConstantResDesc == nullptr)
106+
{
107+
for (Uint32 r = 0; r < pSignature->GetTotalResourceCount(); ++r)
108+
{
109+
const PipelineResourceDesc& ResDesc = pSignature->GetResourceDesc(r);
110+
if (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS)
111+
{
112+
pPushConstantResDesc = &ResDesc;
113+
m_PushConstantSignatureIndex = BindInd;
114+
m_PushConstantResourceIndex = r;
115+
// For inline constants, ArraySize contains the number of 32-bit constants.
116+
m_PushConstantSize = ResDesc.ArraySize * sizeof(Uint32);
117+
for (SHADER_TYPE ShaderTypes = ResDesc.ShaderStages; ShaderTypes != SHADER_TYPE_UNKNOWN;)
118+
{
119+
const SHADER_TYPE ShaderType = ExtractLSB(ShaderTypes);
120+
m_PushConstantStageFlags |= ShaderTypeToVkShaderStageFlagBit(ShaderType);
121+
}
122+
break;
123+
}
124+
}
125+
}
98126
}
99127
VERIFY_EXPR(DescSetLayoutCount <= MAX_RESOURCE_SIGNATURES * 2);
100128

@@ -118,11 +146,11 @@ void PipelineLayoutVk::Create(RenderDeviceVkImpl* pDe
118146
}
119147

120148
// Validate push constant size against device limits
121-
if (PushConstant.Size > 0)
149+
if (m_PushConstantSize > 0)
122150
{
123-
if (PushConstant.Size > Limits.maxPushConstantsSize)
151+
if (m_PushConstantSize > Limits.maxPushConstantsSize)
124152
{
125-
LOG_ERROR_AND_THROW("Push constant size (", PushConstant.Size,
153+
LOG_ERROR_AND_THROW("Push constant size (", m_PushConstantSize,
126154
" bytes) exceeds device limit (", Limits.maxPushConstantsSize, " bytes)");
127155
}
128156
}
@@ -140,19 +168,14 @@ void PipelineLayoutVk::Create(RenderDeviceVkImpl* pDe
140168

141169
// Set up push constant range if present
142170
VkPushConstantRange PushConstantRange = {};
143-
if (PushConstant.Size > 0 && PushConstant.StageFlags != 0)
171+
if (m_PushConstantSize > 0 && m_PushConstantStageFlags != 0)
144172
{
145-
PushConstantRange.stageFlags = PushConstant.StageFlags;
173+
PushConstantRange.stageFlags = m_PushConstantStageFlags;
146174
PushConstantRange.offset = 0;
147-
PushConstantRange.size = PushConstant.Size;
175+
PushConstantRange.size = m_PushConstantSize;
148176

149177
PipelineLayoutCI.pushConstantRangeCount = 1;
150178
PipelineLayoutCI.pPushConstantRanges = &PushConstantRange;
151-
152-
m_PushConstantSize = PushConstant.Size;
153-
m_PushConstantStageFlags = PushConstant.StageFlags;
154-
m_PushConstantSignatureIndex = PushConstant.SignatureIndex;
155-
m_PushConstantResourceIndex = PushConstant.ResourceIndex;
156179
}
157180
else
158181
{

Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp

Lines changed: 23 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -821,41 +821,18 @@ void PipelineStateVkImpl::RemapOrVerifyShaderResources(
821821
}
822822
}
823823

824-
bool PipelineStateVkImpl::InitPushConstantInfoFromSignatures(PushConstantInfoVk& PushConstant,
825-
TShaderStages& ShaderStages) const noexcept(false)
824+
void PipelineStateVkImpl::ValidateShaderPushConstants(const TShaderStages& ShaderStages) const noexcept(false)
826825
{
827-
// Vulkan allows only one push constant range per pipeline layout.
828-
// DiligentCore allows multiple inline constant resources, so we promote only the first inline constant
829-
// from resource signatures to push constants. Other inline constants remain uniform buffers.
830-
const PipelineResourceDesc* pResDesc = nullptr;
831-
const char* PushConstantName = nullptr;
832-
Uint32 PushConstantSize = 0;
833-
Uint32 PushConstantSignIdx = INVALID_PUSH_CONSTANT_INDEX;
834-
Uint32 PushConstantResIdx = INVALID_PUSH_CONSTANT_INDEX;
826+
const Uint32 PushConstantSignIdx = m_PipelineLayout.GetPushConstantSignatureIndex();
827+
const Uint32 PushConstantResIdx = m_PipelineLayout.GetPushConstantResourceIndex();
828+
const Uint32 PushConstantSize = m_PipelineLayout.GetPushConstantSize();
835829

836-
for (Uint32 s = 0; s < m_SignatureCount; ++s)
830+
const char* PushConstantName = nullptr;
831+
if (PushConstantSignIdx != INVALID_PUSH_CONSTANT_INDEX && PushConstantResIdx != INVALID_PUSH_CONSTANT_INDEX)
837832
{
838-
const PipelineResourceSignatureVkImpl* pSignature = m_Signatures[s];
839-
if (pSignature == nullptr)
840-
continue;
841-
842-
for (Uint32 r = 0; r < pSignature->GetTotalResourceCount(); ++r)
843-
{
844-
const PipelineResourceDesc& ResDesc = pSignature->GetResourceDesc(r);
845-
if ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) == 0)
846-
continue;
847-
848-
// For inline constants, ArraySize contains the number of 32-bit constants.
849-
PushConstantSize = ResDesc.ArraySize * sizeof(Uint32);
850-
PushConstantSignIdx = s;
851-
PushConstantResIdx = r;
852-
PushConstantName = ResDesc.Name;
853-
pResDesc = &ResDesc;
854-
break;
855-
}
856-
857-
if (pResDesc != nullptr)
858-
break;
833+
const PipelineResourceSignatureVkImpl* pSignature = m_Signatures[PushConstantSignIdx];
834+
if (pSignature != nullptr)
835+
PushConstantName = pSignature->GetResourceDesc(PushConstantResIdx).Name;
859836
}
860837

861838
// Validate shader-declared push constants against the selected inline constant (if any).
@@ -871,7 +848,7 @@ bool PipelineStateVkImpl::InitPushConstantInfoFromSignatures(PushConstantInfoVk&
871848
{
872849
const SPIRVShaderResourceAttribs& PCAttribs = pShaderResources->GetPushConstant(pc);
873850

874-
if (pResDesc == nullptr)
851+
if (PushConstantName == nullptr)
875852
{
876853
LOG_ERROR_AND_THROW("Shader '", pShader->GetDesc().Name, "' defines push constants block '", PCAttribs.Name,
877854
"', but the pipeline resource signatures define no inline constant resource to promote to push constants.");
@@ -893,24 +870,6 @@ bool PipelineStateVkImpl::InitPushConstantInfoFromSignatures(PushConstantInfoVk&
893870
}
894871
}
895872
}
896-
897-
if (pResDesc == nullptr)
898-
{
899-
// No inline constants found - PushConstant remains empty.
900-
return false;
901-
}
902-
903-
PushConstant.Size = PushConstantSize;
904-
PushConstant.SignatureIndex = PushConstantSignIdx;
905-
PushConstant.ResourceIndex = PushConstantResIdx;
906-
907-
for (SHADER_TYPE ShaderTypes = pResDesc->ShaderStages; ShaderTypes != SHADER_TYPE_UNKNOWN;)
908-
{
909-
const SHADER_TYPE ShaderType = ExtractLSB(ShaderTypes);
910-
PushConstant.StageFlags |= ShaderTypeToVkShaderStageFlagBit(ShaderType);
911-
}
912-
913-
return true;
914873
}
915874

916875
void PipelineStateVkImpl::InitPipelineLayout(const PipelineStateCreateInfo& CreateInfo, TShaderStages& ShaderStages) noexcept(false)
@@ -927,16 +886,15 @@ void PipelineStateVkImpl::InitPipelineLayout(const PipelineStateCreateInfo& Crea
927886
DvpValidateResourceLimits();
928887
#endif
929888

930-
// Vulkan allows only one push constant block per pipeline, but it can be accessed from multiple shader stages.
931-
// Promote the first inline constant from signatures to push constants (and validate any shader-declared push constants).
932-
PushConstantInfoVk PushConstant;
933-
InitPushConstantInfoFromSignatures(PushConstant, ShaderStages);
889+
// Create the pipeline layout - this also extracts push constant info from signatures
890+
m_PipelineLayout.Create(GetDevice(), m_Signatures, m_SignatureCount);
934891

935-
m_PipelineLayout.Create(GetDevice(), m_Signatures, m_SignatureCount, PushConstant);
892+
// Validate shader-declared push constants against the selected inline constant (if any)
893+
ValidateShaderPushConstants(ShaderStages);
936894

937895
// If we promoted an inline constant as push constant (not an existing SPIR-V push constant),
938896
// convert the uniform buffer to push constant in SPIRV bytecode.
939-
PatchShaderConvertUniformBufferToPushConstant(PushConstant, ShaderStages);
897+
PatchShaderConvertUniformBufferToPushConstant(ShaderStages);
940898

941899
const bool RemapResources = (CreateInfo.Flags & PSO_CREATE_FLAG_DONT_REMAP_SHADER_RESOURCES) == 0;
942900
const bool VerifyBindings = !RemapResources && ((InternalFlags & PSO_CREATE_INTERNAL_FLAG_NO_SHADER_REFLECTION) == 0);
@@ -964,20 +922,22 @@ void PipelineStateVkImpl::InitPipelineLayout(const PipelineStateCreateInfo& Crea
964922
}
965923
}
966924

967-
void PipelineStateVkImpl::PatchShaderConvertUniformBufferToPushConstant(const PushConstantInfoVk& PushConstantInfo,
968-
TShaderStages& ShaderStages) const noexcept(false)
925+
void PipelineStateVkImpl::PatchShaderConvertUniformBufferToPushConstant(TShaderStages& ShaderStages) const noexcept(false)
969926
{
927+
const Uint32 PushConstantSignIdx = m_PipelineLayout.GetPushConstantSignatureIndex();
928+
const Uint32 PushConstantResIdx = m_PipelineLayout.GetPushConstantResourceIndex();
929+
970930
// If no push constant was selected, no patching needed
971-
if (PushConstantInfo.SignatureIndex == INVALID_PUSH_CONSTANT_INDEX ||
972-
PushConstantInfo.ResourceIndex == INVALID_PUSH_CONSTANT_INDEX)
931+
if (PushConstantSignIdx == INVALID_PUSH_CONSTANT_INDEX ||
932+
PushConstantResIdx == INVALID_PUSH_CONSTANT_INDEX)
973933
return;
974934

975935
// Get the name of the selected push constant resource
976-
const PipelineResourceSignatureVkImpl* pSignature = m_Signatures[PushConstantInfo.SignatureIndex];
936+
const PipelineResourceSignatureVkImpl* pSignature = m_Signatures[PushConstantSignIdx];
977937
if (pSignature == nullptr)
978938
return;
979939

980-
const PipelineResourceDesc& ResDesc = pSignature->GetResourceDesc(PushConstantInfo.ResourceIndex);
940+
const PipelineResourceDesc& ResDesc = pSignature->GetResourceDesc(PushConstantResIdx);
981941
const std::string PushConstantName = ResDesc.Name;
982942

983943
// For each shader stage, check if the uniform buffer needs to be patched

0 commit comments

Comments
 (0)