Skip to content

Commit feed4cd

Browse files
committed
Add stuffs for vk push constants
1 parent 437a562 commit feed4cd

File tree

15 files changed

+569
-125
lines changed

15 files changed

+569
-125
lines changed

Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1637,7 +1637,7 @@ String GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAGS Flags, bool GetFul
16371637

16381638
PIPELINE_RESOURCE_FLAGS Flag = ExtractLSB(Flags);
16391639

1640-
static_assert(PIPELINE_RESOURCE_FLAG_LAST == (1u << 5), "Please update the switch below to handle the new pipeline resource flag.");
1640+
static_assert(PIPELINE_RESOURCE_FLAG_LAST == (1u << 6), "Please update the switch below to handle the new pipeline resource flag.");
16411641
switch (Flag)
16421642
{
16431643
case PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS:
@@ -1664,6 +1664,10 @@ String GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAGS Flags, bool GetFul
16641664
Str.append(GetFullName ? "PIPELINE_RESOURCE_FLAG_GENERAL_INPUT_ATTACHMENT" : "GENERAL_INPUT_ATTACHMENT");
16651665
break;
16661666

1667+
case PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT:
1668+
Str.append(GetFullName ? "PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT" : "VULKAN_PUSH_CONSTANT");
1669+
break;
1670+
16671671
default:
16681672
UNEXPECTED("Unexpected pipeline resource flag");
16691673
}
@@ -1812,7 +1816,7 @@ PIPELINE_RESOURCE_FLAGS GetValidPipelineResourceFlags(SHADER_RESOURCE_TYPE Resou
18121816
switch (ResourceType)
18131817
{
18141818
case SHADER_RESOURCE_TYPE_CONSTANT_BUFFER:
1815-
return PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS | PIPELINE_RESOURCE_FLAG_RUNTIME_ARRAY | PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS;
1819+
return PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS | PIPELINE_RESOURCE_FLAG_RUNTIME_ARRAY | PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS | PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT;
18161820

18171821
case SHADER_RESOURCE_TYPE_TEXTURE_SRV:
18181822
return PIPELINE_RESOURCE_FLAG_COMBINED_SAMPLER | PIPELINE_RESOURCE_FLAG_RUNTIME_ARRAY;

Graphics/GraphicsEngine/interface/PipelineResourceSignature.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,19 @@ DILIGENT_TYPED_ENUM(PIPELINE_RESOURCE_FLAGS, Uint8)
148148
/// \note This flag is only valid in Vulkan.
149149
PIPELINE_RESOURCE_FLAG_GENERAL_INPUT_ATTACHMENT = 1u << 5,
150150

151-
PIPELINE_RESOURCE_FLAG_LAST = PIPELINE_RESOURCE_FLAG_GENERAL_INPUT_ATTACHMENT
151+
/// Internal flag indicating that this inline constant is a Vulkan push constant
152+
/// (declared with push_constant storage class in SPIR-V).
153+
/// This flag is automatically set by the engine during shader reflection and should not
154+
/// be set manually by the application.
155+
///
156+
/// When this flag is set along with PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS, the engine
157+
/// will use vkCmdPushConstants instead of a dynamic uniform buffer to update the constants.
158+
///
159+
/// \note This flag is only valid in Vulkan and is always combined with
160+
/// PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS.
161+
PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT = 1u << 6,
162+
163+
PIPELINE_RESOURCE_FLAG_LAST = PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT
152164
};
153165
DEFINE_FLAG_ENUM_OPERATORS(PIPELINE_RESOURCE_FLAGS);
154166

Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,10 @@ void ValidatePipelineResourceSignatureDesc(const PipelineResourceSignatureDesc&
113113

114114
if ((Res.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0)
115115
{
116-
if (Res.Flags != PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS)
116+
if ((Res.Flags & (PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS | PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT)) != Res.Flags)
117117
{
118118
LOG_PRS_ERROR_AND_THROW("Incorrect Desc.Resources[", i, "].Flags (", GetPipelineResourceFlagsString(Res.Flags),
119-
"). INLINE_CONSTANTS flag cannot be combined with other flags.");
119+
"). INLINE_CONSTANTS and VULKAN_PUSH_CONSTANT flags cannot be combined with other flags.");
120120
}
121121

122122
if (Res.ArraySize > 64)

Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,9 @@ class DeviceContextVkImpl final : public DeviceContextNextGenBase<EngineVkImplTr
321321
/// Implementation of IDeviceContextVk::GetVkCommandBuffer().
322322
virtual VkCommandBuffer DILIGENT_CALL_TYPE GetVkCommandBuffer() override final;
323323

324+
/// Implementation of IDeviceContextVk::SetPushConstants().
325+
virtual void DILIGENT_CALL_TYPE SetPushConstants(const void* pData, Uint32 Offset, Uint32 Size) override final;
326+
324327
// Transitions BLAS state from OldState to NewState, and optionally updates internal state.
325328
// If OldState == RESOURCE_STATE_UNKNOWN, internal BLAS state is used as old state.
326329
void TransitionBLASState(BottomLevelASVkImpl& BLAS,
@@ -514,11 +517,19 @@ class DeviceContextVkImpl final : public DeviceContextNextGenBase<EngineVkImplTr
514517
/// Current graphics PSO uses no depth/render targets.
515518
bool NullRenderTargets = false;
516519

520+
/// Flag indicating if push constants have been updated since last draw/dispatch
521+
bool PushConstantsDirty = false;
522+
517523
Uint32 NumCommands = 0;
518524

519525
VkPipelineBindPoint vkPipelineBindPoint = VK_PIPELINE_BIND_POINT_MAX_ENUM;
520526
} m_State;
521527

528+
// Push constants data storage (max size is typically 128-256 bytes, use 256 for safety)
529+
static constexpr Uint32 MAX_PUSH_CONSTANTS_SIZE = 256;
530+
std::array<Uint8, MAX_PUSH_CONSTANTS_SIZE> m_PushConstantsData = {};
531+
Uint32 m_PushConstantsDataSize = 0; // Actual size of valid push constants data
532+
522533
// Graphics/mesh, compute, ray tracing
523534
static constexpr Uint32 NUM_PIPELINE_BIND_POINTS = 3;
524535

@@ -558,6 +569,9 @@ class DeviceContextVkImpl final : public DeviceContextNextGenBase<EngineVkImplTr
558569

559570
void UpdateInlineConstantBuffers(ResourceBindInfo& BindInfo);
560571

572+
// Commits push constants to the command buffer if they are dirty
573+
void CommitPushConstants();
574+
561575
#ifdef DILIGENT_DEVELOPMENT
562576
void DvpValidateCommittedShaderResources(ResourceBindInfo& BindInfo);
563577
#endif

Graphics/GraphicsEngineVulkan/include/PipelineLayoutVk.hpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,24 @@ namespace Diligent
4040
class RenderDeviceVkImpl;
4141
class PipelineResourceSignatureVkImpl;
4242

43+
/// Push constant information extracted from shaders
44+
struct PushConstantInfoVk
45+
{
46+
VkShaderStageFlags StageFlags = 0;
47+
Uint32 Size = 0;
48+
};
49+
4350
/// Implementation of the Diligent::PipelineLayoutVk class
4451
class PipelineLayoutVk
4552
{
4653
public:
4754
PipelineLayoutVk();
4855
~PipelineLayoutVk();
4956

50-
void Create(RenderDeviceVkImpl* pDeviceVk, RefCntAutoPtr<PipelineResourceSignatureVkImpl> ppSignatures[], Uint32 SignatureCount) noexcept(false);
57+
void Create(RenderDeviceVkImpl* pDeviceVk,
58+
RefCntAutoPtr<PipelineResourceSignatureVkImpl> ppSignatures[],
59+
Uint32 SignatureCount,
60+
const PushConstantInfoVk& PushConstant = {}) noexcept(false);
5161
void Release(RenderDeviceVkImpl* pDeviceVkImpl, Uint64 CommandQueueMask);
5262

5363
VkPipelineLayout GetVkPipelineLayout() const { return m_VkPipelineLayout; }
@@ -59,6 +69,15 @@ class PipelineLayoutVk
5969
return m_FirstDescrSetIndex[Index];
6070
}
6171

72+
// Returns true if this pipeline layout has push constants
73+
bool HasPushConstants() const { return m_PushConstantSize > 0; }
74+
75+
// Returns the size of push constants in bytes
76+
Uint32 GetPushConstantSize() const { return m_PushConstantSize; }
77+
78+
// Returns the shader stage flags for push constants
79+
VkShaderStageFlags GetPushConstantStageFlags() const { return m_PushConstantStageFlags; }
80+
6281
private:
6382
VulkanUtilities::PipelineLayoutWrapper m_VkPipelineLayout;
6483

@@ -70,6 +89,12 @@ class PipelineLayoutVk
7089
// (Maximum is MAX_RESOURCE_SIGNATURES * 2)
7190
Uint8 m_DescrSetCount = 0;
7291

92+
// Push constant size in bytes
93+
Uint32 m_PushConstantSize = 0;
94+
95+
// Shader stages that use push constants
96+
VkShaderStageFlags m_PushConstantStageFlags = 0;
97+
7398
#ifdef DILIGENT_DEBUG
7499
Uint32 m_DbgMaxBindIndex = 0;
75100
#endif

Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,16 @@ struct ImmutableSamplerAttribsVk
6060
ASSERT_SIZEOF(ImmutableSamplerAttribsVk, 8, "The struct is used in serialization and must be tightly packed");
6161

6262
/// Inline constant buffer attributes for Vulkan backend.
63-
/// Since Vulkan only supports one push constant block per pipeline, inline constants
64-
/// are emulated using dynamic uniform buffers (similar to D3D11 backend).
63+
/// Inline constants can be either:
64+
/// 1. True push constants (from SPIR-V push_constant storage class) - use vkCmdPushConstants
65+
/// 2. Emulated inline constants - use dynamic uniform buffers (similar to D3D11 backend)
6566
struct InlineConstantBufferAttribsVk
6667
{
67-
Uint32 DescrSet = 0; // Descriptor set index
68-
Uint32 BindingIndex = 0; // Binding index within the descriptor set
69-
Uint32 NumConstants = 0; // Number of 32-bit constants
70-
RefCntAutoPtr<BufferVkImpl> pBuffer; // Internal dynamic uniform buffer
68+
Uint32 DescrSet = 0; // Descriptor set index
69+
Uint32 BindingIndex = 0; // Binding index within the descriptor set
70+
Uint32 NumConstants = 0; // Number of 32-bit constants
71+
bool IsPushConstant = false; // True if this is a Vulkan push constant (not emulated)
72+
RefCntAutoPtr<BufferVkImpl> pBuffer; // Internal dynamic uniform buffer (null for true push constants)
7173
};
7274

7375
struct PipelineResourceSignatureInternalDataVk : PipelineResourceSignatureInternalData<PipelineResourceAttribsVk, ImmutableSamplerAttribsVk>
@@ -156,6 +158,11 @@ class PipelineResourceSignatureVkImpl final : public PipelineResourceSignatureBa
156158
// Returns the inline constant buffer attributes
157159
const InlineConstantBufferAttribsVk* GetInlineConstantBuffers() const { return m_InlineConstantBuffers.get(); }
158160

161+
// Adds PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT flag to a resource if it has
162+
// PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS but is missing the Vulkan push constant flag.
163+
// This is called during pipeline state creation when shader reflection detects a push constant.
164+
void UpdatePushConstantFlags(Uint32 ResIndex);
165+
159166
#ifdef DILIGENT_DEVELOPMENT
160167
/// Verifies committed resource using the SPIRV resource attributes from the PSO.
161168
bool DvpValidateCommittedResource(const DeviceContextVkImpl* pDeviceCtx,

Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ class PipelineStateVkImpl final : public PipelineStateBase<EngineVkImplTraits>
133133
void InitializePipeline(const ComputePipelineStateCreateInfo& CreateInfo);
134134
void InitializePipeline(const RayTracingPipelineStateCreateInfo& CreateInfo);
135135

136+
void InitPushConstantInfo(const TShaderStages& ShaderStages, PushConstantInfoVk& PushConstant) noexcept(false);
137+
136138
// TPipelineStateBase::Construct needs access to InitializePipeline
137139
friend TPipelineStateBase;
138140

Graphics/GraphicsEngineVulkan/include/VulkanUtilities/CommandBuffer.hpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,18 @@ class CommandBuffer
504504
vkCmdBindDescriptorSets(m_VkCmdBuffer, pipelineBindPoint, layout, firstSet, descriptorSetCount, pDescriptorSets, dynamicOffsetCount, pDynamicOffsets);
505505
}
506506

507+
__forceinline void PushConstants(VkPipelineLayout layout,
508+
VkShaderStageFlags stageFlags,
509+
uint32_t offset,
510+
uint32_t size,
511+
const void* pValues)
512+
{
513+
VERIFY_EXPR(m_VkCmdBuffer != VK_NULL_HANDLE);
514+
VERIFY_EXPR(pValues != nullptr);
515+
VERIFY_EXPR(size > 0);
516+
vkCmdPushConstants(m_VkCmdBuffer, layout, stageFlags, offset, size, pValues);
517+
}
518+
507519
__forceinline void CopyBuffer(VkBuffer srcBuffer,
508520
VkBuffer dstBuffer,
509521
uint32_t regionCount,

Graphics/GraphicsEngineVulkan/interface/DeviceContextVk.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,25 @@ DILIGENT_BEGIN_INTERFACE(IDeviceContextVk, IDeviceContext)
8484
/// calling IDeviceContext::InvalidateState() and then manually restore all required states via
8585
/// appropriate Diligent API calls.
8686
VIRTUAL VkCommandBuffer METHOD(GetVkCommandBuffer)(THIS) PURE;
87+
88+
/// Sets push constant data for the currently bound pipeline.
89+
90+
/// \param [in] pData - Pointer to the push constant data.
91+
/// \param [in] Offset - Offset in bytes from the start of the push constant block.
92+
/// \param [in] Size - Size of the data to set in bytes.
93+
///
94+
/// \remarks This method sets push constant data that will be used in subsequent draw or dispatch calls.
95+
/// The pipeline state must be set before calling this method.
96+
/// Push constants are a Vulkan feature that allows small amounts of data to be passed to
97+
/// shaders efficiently without using descriptor sets. In shaders, push constants are declared
98+
/// using the layout(push_constant) qualifier in GLSL or [[vk::push_constant]] attribute in HLSL.
99+
///
100+
/// \note The total size of push constants is limited by the device (typically 128-256 bytes).
101+
/// Check VkPhysicalDeviceLimits::maxPushConstantsSize for the exact limit.
102+
VIRTUAL void METHOD(SetPushConstants)(THIS_
103+
const void* pData,
104+
Uint32 Offset,
105+
Uint32 Size) PURE;
87106
};
88107
DILIGENT_END_INTERFACE
89108

@@ -95,6 +114,7 @@ DILIGENT_END_INTERFACE
95114

96115
# define IDeviceContextVk_TransitionImageLayout(This, ...) CALL_IFACE_METHOD(DeviceContextVk, TransitionImageLayout, This, __VA_ARGS__)
97116
# define IDeviceContextVk_BufferMemoryBarrier(This, ...) CALL_IFACE_METHOD(DeviceContextVk, BufferMemoryBarrier, This, __VA_ARGS__)
117+
# define IDeviceContextVk_SetPushConstants(This, ...) CALL_IFACE_METHOD(DeviceContextVk, SetPushConstants, This, __VA_ARGS__)
98118

99119
// clang-format on
100120

Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,16 @@ void DeviceContextVkImpl::SetPipelineState(IPipelineState* pPipelineState)
390390

391391
// Reserve space to store all dynamic buffer offsets
392392
m_DynamicBufferOffsets.resize(TotalDynamicOffsetCount);
393+
394+
// Mark push constants as dirty when PSO changes, since the new PSO might have
395+
// a different pipeline layout. If the new PSO has push constants, they need to
396+
// be re-committed even if the data hasn't changed.
397+
if (Layout.HasPushConstants())
398+
{
399+
m_State.PushConstantsDirty = true;
400+
// Ensure we have initialized push constants data for the new PSO
401+
// (user should call SetPushConstants before draw/dispatch)
402+
}
393403
}
394404

395405
DeviceContextVkImpl::ResourceBindInfo& DeviceContextVkImpl::GetBindInfo(PIPELINE_TYPE Type)
@@ -433,6 +443,54 @@ void DeviceContextVkImpl::UpdateInlineConstantBuffers(ResourceBindInfo& BindInfo
433443
}
434444
}
435445

446+
void DeviceContextVkImpl::SetPushConstants(const void* pData, Uint32 Offset, Uint32 Size)
447+
{
448+
DEV_CHECK_ERR(pData != nullptr || Size == 0, "pData must not be null when Size is non-zero");
449+
DEV_CHECK_ERR(Offset + Size <= MAX_PUSH_CONSTANTS_SIZE,
450+
"Push constant data range [", Offset, ", ", Offset + Size, ") exceeds the maximum supported size (", MAX_PUSH_CONSTANTS_SIZE, ")");
451+
452+
// Note: This function may be called from UpdateInlineConstantBuffers during draw preparation,
453+
// at which point the pipeline state is guaranteed to be bound. When called from user code,
454+
// we validate that the pipeline has push constants.
455+
if (m_pPipelineState != nullptr)
456+
{
457+
const PipelineLayoutVk& Layout = m_pPipelineState->GetPipelineLayout();
458+
if (Layout.HasPushConstants())
459+
{
460+
DEV_CHECK_ERR(Offset + Size <= Layout.GetPushConstantSize(),
461+
"Push constant data range [", Offset, ", ", Offset + Size, ") exceeds the push constant block size (", Layout.GetPushConstantSize(), ")");
462+
}
463+
}
464+
465+
if (Size > 0)
466+
{
467+
memcpy(m_PushConstantsData.data() + Offset, pData, Size);
468+
m_PushConstantsDataSize = std::max(m_PushConstantsDataSize, Offset + Size);
469+
m_State.PushConstantsDirty = true;
470+
}
471+
}
472+
473+
void DeviceContextVkImpl::CommitPushConstants()
474+
{
475+
if (!m_State.PushConstantsDirty)
476+
return;
477+
478+
VERIFY_EXPR(m_pPipelineState != nullptr);
479+
const PipelineLayoutVk& Layout = m_pPipelineState->GetPipelineLayout();
480+
481+
if (!Layout.HasPushConstants())
482+
return;
483+
484+
const Uint32 Size = Layout.GetPushConstantSize();
485+
const VkShaderStageFlags StageFlags = Layout.GetPushConstantStageFlags();
486+
const VkPipelineLayout vkLayout = Layout.GetVkPipelineLayout();
487+
488+
VERIFY_EXPR(Size <= m_PushConstantsData.size());
489+
490+
m_CommandBuffer.PushConstants(vkLayout, StageFlags, 0, Size, m_PushConstantsData.data());
491+
m_State.PushConstantsDirty = false;
492+
}
493+
436494
void DeviceContextVkImpl::CommitDescriptorSets(ResourceBindInfo& BindInfo, Uint32 CommitSRBMask)
437495
{
438496
VERIFY(CommitSRBMask != 0, "This method should not be called when there is nothing to commit");
@@ -791,6 +849,10 @@ void DeviceContextVkImpl::PrepareForDraw(DRAW_FLAGS Flags)
791849
{
792850
CommitDescriptorSets(BindInfo, CommitMask);
793851
}
852+
853+
// Commit push constants if dirty
854+
CommitPushConstants();
855+
794856
#ifdef DILIGENT_DEVELOPMENT
795857
// Must be called after CommitDescriptorSets as it needs SetInfo.BaseInd
796858
DvpValidateCommittedShaderResources(BindInfo);
@@ -1094,6 +1156,9 @@ void DeviceContextVkImpl::PrepareForDispatchCompute()
10941156
CommitDescriptorSets(BindInfo, CommitMask);
10951157
}
10961158

1159+
// Commit push constants if dirty
1160+
CommitPushConstants();
1161+
10971162
#ifdef DILIGENT_DEVELOPMENT
10981163
// Must be called after CommitDescriptorSets as it needs SetInfo.BaseInd
10991164
DvpValidateCommittedShaderResources(BindInfo);
@@ -1114,6 +1179,9 @@ void DeviceContextVkImpl::PrepareForRayTracing()
11141179
CommitDescriptorSets(BindInfo, CommitMask);
11151180
}
11161181

1182+
// Commit push constants if dirty
1183+
CommitPushConstants();
1184+
11171185
#ifdef DILIGENT_DEVELOPMENT
11181186
// Must be called after CommitDescriptorSets as it needs SetInfo.BaseInd
11191187
DvpValidateCommittedShaderResources(BindInfo);

0 commit comments

Comments
 (0)