Skip to content

Commit f63da22

Browse files
committed
Add PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT to PRS so we don't to have to deal with SPIRVShaderResourceAttribs stuffs in PRS creation.
1 parent feed4cd commit f63da22

File tree

9 files changed

+402
-183
lines changed

9 files changed

+402
-183
lines changed

Graphics/GraphicsEngine/interface/PipelineResourceSignature.h

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -148,13 +148,12 @@ 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-
/// Internal flag indicating that this inline constant is a Vulkan push constant
151+
/// Indicates that this inline constant is a Vulkan push constant
152152
/// (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.
153+
/// This flag is must be explicitly set when creating PipelineResourceSignature
154+
/// And must be used together with PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS
155+
/// When this flag is set, the engine will use vkCmdPushConstants
156+
/// instead of a dynamic uniform buffer to update the constants.
158157
///
159158
/// \note This flag is only valid in Vulkan and is always combined with
160159
/// PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS.

Graphics/GraphicsEngine/src/PipelineStateBase.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -849,6 +849,15 @@ void ValidatePipelineResourceCompatibility(const PipelineResourceDesc& ResDesc,
849849
GetShaderResourceTypeLiteralName(ResDesc.ResourceType), "' specified in pipeline resource signature '", SignatureName, "'.");
850850
}
851851

852+
if ((ResourceFlags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT))
853+
{
854+
LOG_ERROR_AND_THROW("Shader '", ShaderName, "' contains resource '", ResDesc.Name,
855+
"' that is", ((ResourceFlags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) ? "" : " not"),
856+
" labeled as vulkan's push constant, while the same resource specified by the pipeline resource signature '",
857+
SignatureName, "' is", ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) ? "" : " not"),
858+
" labeled as such.");
859+
}
860+
852861
if ((ResourceFlags & PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER) != (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER))
853862
{
854863
LOG_ERROR_AND_THROW("Shader '", ShaderName, "' contains resource '", ResDesc.Name,

Graphics/GraphicsEngineVulkan/include/PipelineResourceAttribsVk.hpp

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,13 @@ struct PipelineResourceAttribsVk
6868
private:
6969
static constexpr Uint32 _BindingIndexBits = 16;
7070
static constexpr Uint32 _SamplerIndBits = 16;
71-
static constexpr Uint32 _ArraySizeBits = 25;
71+
static constexpr Uint32 _ArraySizeBits = 24;
7272
static constexpr Uint32 _DescrTypeBits = 5;
7373
static constexpr Uint32 _DescrSetBits = 1;
7474
static constexpr Uint32 _SamplerAssignedBits = 1;
75+
static constexpr Uint32 _IsPushConstantBits = 1;
7576

76-
static_assert((_BindingIndexBits + _ArraySizeBits + _SamplerIndBits + _DescrTypeBits + _DescrSetBits + _SamplerAssignedBits) % 32 == 0, "Bits are not optimally packed");
77+
static_assert((_BindingIndexBits + _ArraySizeBits + _SamplerIndBits + _DescrTypeBits + _DescrSetBits + _SamplerAssignedBits + _IsPushConstantBits) % 32 == 0, "Bits are not optimally packed");
7778

7879
// clang-format off
7980
static_assert((1u << _DescrTypeBits) > static_cast<Uint32>(DescriptorType::Count), "Not enough bits to store DescriptorType values");
@@ -88,10 +89,11 @@ struct PipelineResourceAttribsVk
8889
// clang-format off
8990
const Uint32 BindingIndex : _BindingIndexBits; // Binding in the descriptor set
9091
const Uint32 SamplerInd : _SamplerIndBits; // Index of the assigned sampler in m_Desc.Resources and m_pPipelineResourceAttribsVk
91-
const Uint32 ArraySize : _ArraySizeBits; // Array size
92+
const Uint32 ArraySize : _ArraySizeBits; // Array size (for push constants, this is the number of 32-bit constants)
9293
const Uint32 DescrType : _DescrTypeBits; // Descriptor type (DescriptorType)
9394
const Uint32 DescrSet : _DescrSetBits; // Descriptor set (0 or 1)
9495
const Uint32 ImtblSamplerAssigned : _SamplerAssignedBits; // Immutable sampler flag
96+
const Uint32 IsPushConstant : _IsPushConstantBits; // True if this is a Vulkan push constant (not using descriptor sets)
9597

9698
const Uint32 SRBCacheOffset; // Offset in the SRB resource cache
9799
const Uint32 StaticCacheOffset; // Offset in the static resource cache
@@ -103,6 +105,7 @@ struct PipelineResourceAttribsVk
103105
DescriptorType _DescrType,
104106
Uint32 _DescrSet,
105107
bool _ImtblSamplerAssigned,
108+
bool _IsPushConstant,
106109
Uint32 _SRBCacheOffset,
107110
Uint32 _StaticCacheOffset) noexcept :
108111
// clang-format off
@@ -112,6 +115,7 @@ struct PipelineResourceAttribsVk
112115
DescrType {static_cast<Uint32>(_DescrType)},
113116
DescrSet {_DescrSet },
114117
ImtblSamplerAssigned {_ImtblSamplerAssigned ? 1u : 0u},
118+
IsPushConstant {_IsPushConstant ? 1u : 0u },
115119
SRBCacheOffset {_SRBCacheOffset },
116120
StaticCacheOffset {_StaticCacheOffset }
117121
// clang-format on
@@ -127,7 +131,7 @@ struct PipelineResourceAttribsVk
127131

128132
// Only for serialization
129133
PipelineResourceAttribsVk() noexcept :
130-
PipelineResourceAttribsVk{0, 0, 0, DescriptorType::Unknown, 0, false, 0, 0}
134+
PipelineResourceAttribsVk{0, 0, 0, DescriptorType::Unknown, 0, false, false, 0, 0}
131135
{}
132136

133137

@@ -151,6 +155,11 @@ struct PipelineResourceAttribsVk
151155
return SamplerInd != InvalidSamplerInd;
152156
}
153157

158+
bool IsPushConstantBuffer() const
159+
{
160+
return IsPushConstant != 0;
161+
}
162+
154163
bool IsCompatibleWith(const PipelineResourceAttribsVk& rhs) const
155164
{
156165
// Ignore sampler index and cache offsets.
@@ -159,13 +168,14 @@ struct PipelineResourceAttribsVk
159168
ArraySize == rhs.ArraySize &&
160169
DescrType == rhs.DescrType &&
161170
DescrSet == rhs.DescrSet &&
162-
ImtblSamplerAssigned == rhs.ImtblSamplerAssigned;
171+
ImtblSamplerAssigned == rhs.ImtblSamplerAssigned &&
172+
IsPushConstant == rhs.IsPushConstant;
163173
// clang-format on
164174
}
165175

166176
size_t GetHash() const
167177
{
168-
return ComputeHash(BindingIndex, ArraySize, DescrType, DescrSet, ImtblSamplerAssigned);
178+
return ComputeHash(BindingIndex, ArraySize, DescrType, DescrSet, ImtblSamplerAssigned, IsPushConstant);
169179
}
170180
};
171181
ASSERT_SIZEOF(PipelineResourceAttribsVk, 16, "The struct is used in serialization and must be tightly packed");

Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,13 @@ ASSERT_SIZEOF(ImmutableSamplerAttribsVk, 8, "The struct is used in serialization
6565
/// 2. Emulated inline constants - use dynamic uniform buffers (similar to D3D11 backend)
6666
struct InlineConstantBufferAttribsVk
6767
{
68-
Uint32 DescrSet = 0; // Descriptor set index
68+
Uint32 ResIndex = 0; // Resource index in the signature (used for matching)
69+
Uint32 DescrSet = 0; // Descriptor set index (0 for push constants as placeholder)
6970
Uint32 BindingIndex = 0; // Binding index within the descriptor set
7071
Uint32 NumConstants = 0; // Number of 32-bit constants
71-
bool IsPushConstant = false; // True if this is a Vulkan push constant (not emulated)
72+
bool IsPushConstant = false; // True if this is a Vulkan push constant (not emulated)
7273
RefCntAutoPtr<BufferVkImpl> pBuffer; // Internal dynamic uniform buffer (null for true push constants)
74+
void* pPushConstantData = nullptr; // CPU-side staging buffer for push constants (only used when IsPushConstant is true)
7375
};
7476

7577
struct PipelineResourceSignatureInternalDataVk : PipelineResourceSignatureInternalData<PipelineResourceAttribsVk, ImmutableSamplerAttribsVk>
@@ -149,6 +151,7 @@ class PipelineResourceSignatureVkImpl final : public PipelineResourceSignatureBa
149151
VkDescriptorSet vkDynamicDescriptorSet) const;
150152

151153
// Updates inline constant buffers by mapping the internal buffers and copying data from the resource cache
154+
// ResourceCache must be valid - each SRB has its own copy of push constant data stored in the cache
152155
void UpdateInlineConstantBuffers(const ShaderResourceCacheVk& ResourceCache,
153156
DeviceContextVkImpl& Ctx) const;
154157

Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,37 @@ class ShaderResourceCacheVk : public ShaderResourceCacheBase
274274
STDDeleter<void, IMemoryAllocator>(Allocator)};
275275
}
276276

277+
// Sets the push constant data pointer for the given index
278+
// This is used to store per-SRB push constant data for STATIC push constants
279+
void SetPushConstantDataPtr(Uint32 Index, void* pData)
280+
{
281+
VERIFY_EXPR(Index < m_NumPushConstantBuffers);
282+
m_pPushConstantDataPtrs[Index] = pData;
283+
}
284+
285+
// Gets the push constant data pointer for the given index
286+
void* GetPushConstantDataPtr(Uint32 Index) const
287+
{
288+
VERIFY_EXPR(Index < m_NumPushConstantBuffers);
289+
return m_pPushConstantDataPtrs[Index];
290+
}
291+
292+
// Initializes push constant data pointers array
293+
void InitializePushConstantDataPtrs(Uint32 NumPushConstants)
294+
{
295+
m_NumPushConstantBuffers = static_cast<Uint16>(NumPushConstants);
296+
if (NumPushConstants > 0)
297+
{
298+
m_pPushConstantDataPtrs = std::make_unique<void*[]>(NumPushConstants);
299+
for (Uint32 i = 0; i < NumPushConstants; ++i)
300+
m_pPushConstantDataPtrs[i] = nullptr;
301+
// Mark that this cache has inline constants (push constants are a type of inline constants)
302+
m_HasInlineConstants = 1;
303+
}
304+
}
305+
306+
Uint32 GetNumPushConstantBuffers() const { return m_NumPushConstantBuffers; }
307+
277308
Uint32 GetNumDescriptorSets() const { return m_NumSets; }
278309
bool HasDynamicResources() const { return m_NumDynamicBuffers > 0; }
279310

@@ -318,7 +349,12 @@ class ShaderResourceCacheVk : public ShaderResourceCacheBase
318349
// Memory for inline constant data (allocated separately, freed in destructor)
319350
std::unique_ptr<void, STDDeleter<void, IMemoryAllocator>> m_pInlineConstantMemory;
320351

321-
Uint16 m_NumSets = 0;
352+
// Array of push constant data pointers (one per push constant buffer)
353+
// Each pointer points to memory within m_pInlineConstantMemory
354+
std::unique_ptr<void*[]> m_pPushConstantDataPtrs;
355+
356+
Uint16 m_NumSets = 0;
357+
Uint16 m_NumPushConstantBuffers = 0;
322358

323359
// Total actual number of dynamic buffers (that were created with USAGE_DYNAMIC) bound in the resource cache
324360
// regardless of the variable type. Note this variable is not equal to dynamic offsets count, which is constant.

Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp

Lines changed: 56 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,15 @@ void DeviceContextVkImpl::UpdateInlineConstantBuffers(ResourceBindInfo& BindInfo
435435
continue;
436436

437437
const ShaderResourceCacheVk* pResourceCache = BindInfo.ResourceCaches[i];
438-
if (pResourceCache == nullptr || !pResourceCache->HasInlineConstants())
438+
if (pResourceCache == nullptr)
439+
{
440+
// Each signature with inline constants must have a bound SRB with a valid resource cache
441+
DEV_CHECK_ERR(false, "Signature '", pSign->GetDesc().Name, "' has inline constants but no SRB is bound. "
442+
"Did you call CommitShaderResources()?");
443+
continue;
444+
}
445+
446+
if (!pResourceCache->HasInlineConstants())
439447
continue;
440448

441449
// Update inline constant buffers
@@ -461,13 +469,14 @@ void DeviceContextVkImpl::SetPushConstants(const void* pData, Uint32 Offset, Uin
461469
"Push constant data range [", Offset, ", ", Offset + Size, ") exceeds the push constant block size (", Layout.GetPushConstantSize(), ")");
462470
}
463471
}
464-
465-
if (Size > 0)
472+
else
466473
{
467-
memcpy(m_PushConstantsData.data() + Offset, pData, Size);
468-
m_PushConstantsDataSize = std::max(m_PushConstantsDataSize, Offset + Size);
469-
m_State.PushConstantsDirty = true;
474+
DEV_ERROR("A valid PipelineState must be bound before calling SetPushConstants!");
470475
}
476+
477+
memcpy(m_PushConstantsData.data() + Offset, pData, Size);
478+
m_PushConstantsDataSize = std::max(m_PushConstantsDataSize, Offset + Size);
479+
m_State.PushConstantsDirty = true;
471480
}
472481

473482
void DeviceContextVkImpl::CommitPushConstants()
@@ -495,6 +504,27 @@ void DeviceContextVkImpl::CommitDescriptorSets(ResourceBindInfo& BindInfo, Uint3
495504
{
496505
VERIFY(CommitSRBMask != 0, "This method should not be called when there is nothing to commit");
497506

507+
// Filter out SRBs that only have push constants (no descriptor sets)
508+
// These SRBs are included in CommitSRBMask due to InlineConstantsSRBMask, but they don't need descriptor set binding
509+
Uint32 FilteredCommitMask = CommitSRBMask;
510+
Uint32 ResourceSignaturesCount = m_pPipelineState->GetResourceSignatureCount();
511+
for (Uint32 sign = 0; sign < ResourceSignaturesCount; ++sign)
512+
{
513+
if ((CommitSRBMask & (1u << sign)) != 0)
514+
{
515+
const ShaderResourceCacheVk* pResourceCache = BindInfo.ResourceCaches[sign];
516+
if (pResourceCache != nullptr && pResourceCache->GetNumDescriptorSets() == 0)
517+
{
518+
// This SRB only has push constants, no descriptor sets to commit
519+
FilteredCommitMask &= ~(1u << sign);
520+
}
521+
}
522+
}
523+
524+
// If all SRBs were filtered out (only push constants), nothing to commit
525+
if (FilteredCommitMask == 0)
526+
return;
527+
498528
const Uint32 FirstSign = PlatformMisc::GetLSB(CommitSRBMask);
499529
const Uint32 LastSign = PlatformMisc::GetMSB(CommitSRBMask);
500530
VERIFY_EXPR(LastSign < m_pPipelineState->GetResourceSignatureCount());
@@ -563,6 +593,8 @@ void DeviceContextVkImpl::DvpValidateCommittedShaderResources(ResourceBindInfo&
563593
if (BindInfo.ResourcesValidated)
564594
return;
565595

596+
// Use custom signature getter to skip signatures that have no resources at all
597+
// Note: Signatures with only push constants still need SRB to store push constant data
566598
DvpVerifySRBCompatibility(BindInfo);
567599

568600
const Uint32 SignCount = m_pPipelineState->GetResourceSignatureCount();
@@ -612,9 +644,24 @@ void DeviceContextVkImpl::CommitShaderResources(IShaderResourceBinding* pShaderR
612644

613645
ShaderResourceBindingVkImpl* pResBindingVkImpl = ClassPtrCast<ShaderResourceBindingVkImpl>(pShaderResourceBinding);
614646
ShaderResourceCacheVk& ResourceCache = pResBindingVkImpl->GetResourceCache();
647+
648+
const Uint32 SRBIndex = pResBindingVkImpl->GetBindingIndex();
649+
const PipelineResourceSignatureVkImpl* pSignature = pResBindingVkImpl->GetSignature();
650+
ResourceBindInfo& BindInfo = GetBindInfo(pResBindingVkImpl->GetPipelineType());
651+
ResourceBindInfo::DescriptorSetInfo& SetInfo = BindInfo.SetInfo[SRBIndex];
652+
653+
// Always bind the SRB, even if it has no descriptor sets (e.g., only push constants)
654+
// The resource cache is needed to store push constant data
655+
BindInfo.Set(SRBIndex, pResBindingVkImpl);
656+
657+
// If there are no descriptor sets, we're done (SRB only contains push constants)
658+
// Clear the stale flag since there are no descriptor sets to commit
615659
if (ResourceCache.GetNumDescriptorSets() == 0)
616660
{
617-
// Ignore SRBs that contain no resources
661+
// Clear the stale bit for this SRB since it has no descriptor sets to commit
662+
// The SRB is still bound and ResourceCaches[SRBIndex] is set, so push constants can be accessed
663+
const auto SRBBit = static_cast<ResourceBindInfo::SRBMaskType>(1u << SRBIndex);
664+
BindInfo.StaleSRBMask &= ~SRBBit;
618665
return;
619666
}
620667

@@ -633,12 +680,6 @@ void DeviceContextVkImpl::CommitShaderResources(IShaderResourceBinding* pShaderR
633680
}
634681
#endif
635682

636-
const Uint32 SRBIndex = pResBindingVkImpl->GetBindingIndex();
637-
const PipelineResourceSignatureVkImpl* pSignature = pResBindingVkImpl->GetSignature();
638-
ResourceBindInfo& BindInfo = GetBindInfo(pResBindingVkImpl->GetPipelineType());
639-
ResourceBindInfo::DescriptorSetInfo& SetInfo = BindInfo.SetInfo[SRBIndex];
640-
641-
BindInfo.Set(SRBIndex, pResBindingVkImpl);
642683
// We must not clear entire ResInfo as DescriptorSetBaseInd and DynamicOffsetCount
643684
// are set by SetPipelineState().
644685
SetInfo.vkSets = {};
@@ -836,6 +877,7 @@ void DeviceContextVkImpl::PrepareForDraw(DRAW_FLAGS Flags)
836877
ResourceBindInfo& BindInfo = GetBindInfo(PIPELINE_TYPE_GRAPHICS);
837878

838879
// Update inline constant buffers before binding descriptor sets
880+
const bool DynamicBuffersIntact = (Flags & DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT) != 0;
839881
const bool InlineConstantsIntact = (Flags & DRAW_FLAG_INLINE_CONSTANTS_INTACT) != 0;
840882
if (!InlineConstantsIntact)
841883
{
@@ -845,7 +887,7 @@ void DeviceContextVkImpl::PrepareForDraw(DRAW_FLAGS Flags)
845887
// First time we must always bind descriptor sets with dynamic offsets as SRBs are stale.
846888
// If there are no dynamic buffers bound in the resource cache, for all subsequent
847889
// calls we do not need to bind the sets again.
848-
if (Uint32 CommitMask = BindInfo.GetCommitMask(Flags & DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT, Flags & DRAW_FLAG_INLINE_CONSTANTS_INTACT))
890+
if (Uint32 CommitMask = BindInfo.GetCommitMask(DynamicBuffersIntact, InlineConstantsIntact))
849891
{
850892
CommitDescriptorSets(BindInfo, CommitMask);
851893
}

0 commit comments

Comments
 (0)