4343#include " EngineMemory.h"
4444#include " StringTools.hpp"
4545
46- #if !DILIGENT_NO_HLSL
47- # include " SPIRVTools.hpp"
48- #endif
46+ #include " SPIRVTools.hpp"
4947
5048namespace Diligent
5149{
@@ -854,33 +852,43 @@ void PipelineStateVkImpl::RemapOrVerifyShaderResources(
854852 }
855853}
856854
857- void PipelineStateVkImpl::InitPushConstantInfo ( const TShaderStages& ShaderStages, PushConstantInfoVk& PushConstant) noexcept (false )
855+ void PipelineStateVkImpl::InitPushConstantInfoFromSignatures ( PushConstantInfoVk& PushConstant) const noexcept (false )
858856{
859- for (const ShaderStageInfo& Stage : ShaderStages)
857+ // Iterate through all signatures to find resources marked as VULKAN_PUSH_CONSTANT
858+ for (Uint32 s = 0 ; s < m_SignatureCount; ++s)
860859 {
861- for (const ShaderVkImpl* pShader : Stage.Shaders )
860+ const PipelineResourceSignatureVkImpl* pSignature = m_Signatures[s];
861+ if (pSignature == nullptr )
862+ continue ;
863+
864+ for (Uint32 r = 0 ; r < pSignature->GetTotalResourceCount (); ++r)
862865 {
863- const auto & pShaderResources = pShader->GetShaderResources ();
864- if (pShaderResources && pShaderResources->GetNumPushConstants () > 0 )
866+ const PipelineResourceDesc& ResDesc = pSignature->GetResourceDesc (r);
867+
868+ // Check if this resource is marked as a Vulkan push constant
869+ if ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != 0 )
865870 {
866- // There should be at most one push constant block per shader
867- const SPIRVShaderResourceAttribs& PCAttribs = pShaderResources->GetPushConstant (0 );
868- const Uint32 PCSize = PCAttribs.BufferStaticSize ;
871+ // For push constants, ArraySize contains the number of 32-bit constants
872+ const Uint32 PCSize = ResDesc.ArraySize * sizeof (Uint32);
869873
870874 if (PushConstant.Size == 0 )
871875 {
872- // First shader with push constants - record the size
876+ // First push constant resource - record the size
873877 PushConstant.Size = PCSize;
874878 }
875879 else if (PushConstant.Size != PCSize)
876880 {
877- // Multiple shaders with different push constant sizes
878- // This is allowed in Vulkan - take the maximum size
881+ // Multiple push constant resources with different sizes
882+ // Take the maximum size ( Vulkan allows only one push constant block per pipeline)
879883 PushConstant.Size = std::max (PushConstant.Size , PCSize);
880884 }
881885
882- // Add this shader stage to the stage flags
883- PushConstant.StageFlags |= ShaderTypeToVkShaderStageFlagBit (Stage.Type );
886+ // Add shader stages to the stage flags
887+ for (SHADER_TYPE ShaderStages = ResDesc.ShaderStages ; ShaderStages != SHADER_TYPE_UNKNOWN;)
888+ {
889+ const SHADER_TYPE ShaderType = ExtractLSB (ShaderStages);
890+ PushConstant.StageFlags |= ShaderTypeToVkShaderStageFlagBit (ShaderType);
891+ }
884892 }
885893 }
886894 }
@@ -900,13 +908,19 @@ void PipelineStateVkImpl::InitPipelineLayout(const PipelineStateCreateInfo& Crea
900908 DvpValidateResourceLimits ();
901909#endif
902910
903- // Extract push constant information from shaders
904- // Vulkan allows only one push constant block per pipeline, but it can be accessed from multiple shader stages
911+ // Extract push constant information from signatures
912+ // Vulkan allows only one push constant block per pipeline, but it can be accessed from multiple shader stages.
913+ // We use resource attributes from m_Signatures instead of from shader stages because we may patch SPIRV
914+ // later according to m_Signatures definitions (e.g., converting uniform buffers to push constants).
905915 PushConstantInfoVk PushConstant;
906- InitPushConstantInfo (ShaderStages, PushConstant);
916+ InitPushConstantInfoFromSignatures ( PushConstant);
907917
908918 m_PipelineLayout.Create (GetDevice (), m_Signatures, m_SignatureCount, PushConstant);
909919
920+ // Check if any resource in shader is a uniform buffer but marked as VULKAN_PUSH_CONSTANT in signatures.
921+ // If so, convert the uniform buffer to push constant in SPIRV bytecode.
922+ PatchShaderConvertUniformBufferToPushConstant (ShaderStages);
923+
910924 const bool RemapResources = (CreateInfo.Flags & PSO_CREATE_FLAG_DONT_REMAP_SHADER_RESOURCES) == 0 ;
911925 const bool VerifyBindings = !RemapResources && ((InternalFlags & PSO_CREATE_INTERNAL_FLAG_NO_SHADER_REFLECTION) == 0 );
912926 if (RemapResources || VerifyBindings)
@@ -933,6 +947,87 @@ void PipelineStateVkImpl::InitPipelineLayout(const PipelineStateCreateInfo& Crea
933947 }
934948}
935949
950+ void PipelineStateVkImpl::PatchShaderConvertUniformBufferToPushConstant (TShaderStages& ShaderStages) const noexcept (false )
951+ {
952+ // Build a set of resource names that need to be converted to push constants
953+ std::unordered_set<std::string> PushConstantNames;
954+ for (Uint32 s = 0 ; s < m_SignatureCount; ++s)
955+ {
956+ const PipelineResourceSignatureVkImpl* pSignature = m_Signatures[s];
957+ if (pSignature == nullptr )
958+ continue ;
959+
960+ for (Uint32 r = 0 ; r < pSignature->GetTotalResourceCount (); ++r)
961+ {
962+ const PipelineResourceDesc& ResDesc = pSignature->GetResourceDesc (r);
963+ if ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != 0 )
964+ {
965+ PushConstantNames.insert (ResDesc.Name );
966+ }
967+ }
968+ }
969+
970+ if (PushConstantNames.empty ())
971+ return ;
972+
973+ // For each shader stage, check if any uniform buffer needs to be patched
974+ for (ShaderStageInfo& Stage : ShaderStages)
975+ {
976+ for (size_t i = 0 ; i < Stage.Shaders .size (); ++i)
977+ {
978+ ShaderVkImpl* pShader = const_cast <ShaderVkImpl*>(Stage.Shaders [i]);
979+
980+ bool ShouldPatchUniformBuffer = false ;
981+ std::string PatchingUniformBufferName;
982+ {
983+ const SPIRVShaderResources* pRes = pShader->GetShaderResources ().get ();
984+
985+ if (pRes == nullptr )
986+ continue ;
987+
988+ // Check each uniform buffer in the shader
989+ for (Uint32 ub = 0 ; ub < pRes->GetNumUBs (); ++ub)
990+ {
991+ const SPIRVShaderResourceAttribs& UBAttribs = pRes->GetUB (ub);
992+
993+ // If this uniform buffer is marked as push constant in the signature,
994+ // convert it in the SPIRV bytecode
995+ if (PushConstantNames.count (UBAttribs.Name ) > 0 )
996+ {
997+ PatchingUniformBufferName = UBAttribs.Name ;
998+ ShouldPatchUniformBuffer = true ;
999+ break ;
1000+ }
1001+ }
1002+ }
1003+
1004+ if (ShouldPatchUniformBuffer)
1005+ {
1006+ const std::vector<uint32_t >& SPIRV = Stage.SPIRVs [i];
1007+ std::vector<uint32_t > ConvertedSPIRV = PatchSPIRVConvertUniformBufferToPushConstant (
1008+ SPIRV,
1009+ SPV_ENV_MAX, // Auto-detect target environment
1010+ PatchingUniformBufferName);
1011+
1012+ if (!ConvertedSPIRV.empty ())
1013+ {
1014+ Stage.SPIRVs [i] = ConvertedSPIRV;
1015+
1016+ pShader->SetSPIRV (ConvertedSPIRV);
1017+
1018+ // Reconstruct shader resources from SPIRV
1019+ pShader->CreateSPIRVShaderResources ();
1020+ }
1021+ else
1022+ {
1023+ LOG_ERROR_MESSAGE (" Failed to convert uniform buffer '" , PatchingUniformBufferName,
1024+ " ' to push constant in shader '" , pShader->GetDesc ().Name , " '" );
1025+ }
1026+ }
1027+ }
1028+ }
1029+ }
1030+
9361031template <typename PSOCreateInfoType>
9371032PipelineStateVkImpl::TShaderStages PipelineStateVkImpl::InitInternalObjects (
9381033 const PSOCreateInfoType& CreateInfo,
0 commit comments