Skip to content

Commit d444f82

Browse files
hzqstTheMostDiligent
authored andcommitted
DeviceContextVk: enable inline constants
1 parent 68bd7c6 commit d444f82

5 files changed

Lines changed: 186 additions & 6 deletions

File tree

Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,12 @@ class DeviceContextVkImpl final : public DeviceContextNextGenBase<EngineVkImplTr
555555
__forceinline ResourceBindInfo& GetBindInfo(PIPELINE_TYPE Type);
556556

557557
__forceinline void CommitDescriptorSets(ResourceBindInfo& BindInfo, Uint32 CommitSRBMask);
558+
559+
void UpdateInlineConstantBuffers(ResourceBindInfo& BindInfo);
560+
561+
// Commits push constants to the command buffer directly from SRB cache
562+
void CommitPushConstants(ResourceBindInfo& BindInfo);
563+
558564
#ifdef DILIGENT_DEVELOPMENT
559565
void DvpValidateCommittedShaderResources(ResourceBindInfo& BindInfo);
560566
#endif

Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,17 @@ class PipelineResourceSignatureVkImpl final : public PipelineResourceSignatureBa
160160
// Returns the inline constant buffer attributes
161161
const InlineConstantBufferAttribsVk* GetInlineConstantBuffers() const { return m_InlineConstantBuffers.get(); }
162162

163+
// Updates inline constant buffers by mapping the internal buffers and copying data from the resource cache
164+
// ResourceCache must be valid - each SRB has its own copy of inline constant data stored in the cache
165+
// PushConstantResIndex: Resource index of the inline constant selected as push constant by PSO
166+
// Pass ~0u if no push constant is selected from this signature
167+
void UpdateInlineConstantBuffers(const ShaderResourceCacheVk& ResourceCache,
168+
DeviceContextVkImpl& Ctx,
169+
Uint32 PushConstantResIndex) const;
170+
171+
// Returns push constant data from the resource cache for the specified resource index
172+
const void* GetPushConstantData(const ShaderResourceCacheVk& ResourceCache, Uint32 ResIndex) const;
173+
163174
#ifdef DILIGENT_DEVELOPMENT
164175
/// Verifies committed resource using the SPIRV resource attributes from the PSO.
165176
bool DvpValidateCommittedResource(const DeviceContextVkImpl* pDeviceCtx,

Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,71 @@ DeviceContextVkImpl::ResourceBindInfo& DeviceContextVkImpl::GetBindInfo(PIPELINE
415415
return m_BindInfo[Indices[Uint32{Type}]];
416416
}
417417

418+
void DeviceContextVkImpl::UpdateInlineConstantBuffers(ResourceBindInfo& BindInfo)
419+
{
420+
const PipelineLayoutVk& Layout = m_pPipelineState->GetPipelineLayout();
421+
const auto& PushConstantInfo = Layout.GetPushConstantInfo();
422+
const Uint32 SignCount = m_pPipelineState->GetResourceSignatureCount();
423+
424+
for (Uint32 i = 0; i < SignCount; ++i)
425+
{
426+
const PipelineResourceSignatureVkImpl* pSign = m_pPipelineState->GetResourceSignature(i);
427+
if (pSign == nullptr || pSign->GetNumInlineConstantBuffers() == 0)
428+
continue;
429+
430+
const ShaderResourceCacheVk* pResourceCache = BindInfo.ResourceCaches[i];
431+
if (pResourceCache == nullptr)
432+
{
433+
// Each signature with inline constants must have a bound SRB with a valid resource cache
434+
DEV_CHECK_ERR(false, "Signature '", pSign->GetDesc().Name, "' has inline constants but no SRB is bound. "
435+
"Did you call CommitShaderResources()?");
436+
continue;
437+
}
438+
439+
if (!pResourceCache->HasInlineConstants())
440+
continue;
441+
442+
// Determine which resource (if any) in this signature should use push constant path
443+
// If this signature contains the selected push constant, pass its resource index
444+
// Otherwise pass ~0u to use emulated buffer path for all
445+
const Uint32 PushConstantResIndex = (i == PushConstantInfo.SignatureIndex) ? PushConstantInfo.ResourceIndex : ~0u;
446+
447+
// Update inline constant buffers
448+
pSign->UpdateInlineConstantBuffers(*pResourceCache, *this, PushConstantResIndex);
449+
}
450+
}
451+
452+
void DeviceContextVkImpl::CommitPushConstants(ResourceBindInfo& BindInfo)
453+
{
454+
VERIFY_EXPR(m_pPipelineState != nullptr);
455+
const PipelineLayoutVk& Layout = m_pPipelineState->GetPipelineLayout();
456+
const auto& PushConstantInfo = Layout.GetPushConstantInfo();
457+
458+
if (!PushConstantInfo)
459+
return;
460+
461+
const PipelineResourceSignatureVkImpl* pSign = m_pPipelineState->GetResourceSignature(PushConstantInfo.SignatureIndex);
462+
VERIFY_EXPR(pSign != nullptr);
463+
464+
const ShaderResourceCacheVk* pResourceCache = BindInfo.ResourceCaches[PushConstantInfo.SignatureIndex];
465+
if (pResourceCache == nullptr)
466+
{
467+
DEV_CHECK_ERR(false, "Signature '", pSign->GetDesc().Name, "' has push constants but no SRB is bound. "
468+
"Did you call CommitShaderResources()?");
469+
return;
470+
}
471+
472+
// Get inline constant data directly from the resource cache
473+
const void* pPushConstantData = pSign->GetPushConstantData(*pResourceCache, PushConstantInfo.ResourceIndex);
474+
if (pPushConstantData == nullptr)
475+
{
476+
DEV_CHECK_ERR(false, "Push constant data is null in signature '", pSign->GetDesc().Name, "'");
477+
return;
478+
}
479+
480+
m_CommandBuffer.PushConstants(Layout.GetVkPipelineLayout(), PushConstantInfo.vkRange, pPushConstantData);
481+
}
482+
418483
void DeviceContextVkImpl::CommitDescriptorSets(ResourceBindInfo& BindInfo, Uint32 CommitSRBMask)
419484
{
420485
VERIFY(CommitSRBMask != 0, "This method should not be called when there is nothing to commit");
@@ -758,13 +823,28 @@ void DeviceContextVkImpl::PrepareForDraw(DRAW_FLAGS Flags)
758823
#endif
759824

760825
ResourceBindInfo& BindInfo = GetBindInfo(PIPELINE_TYPE_GRAPHICS);
826+
827+
// Update inline constant buffers (emulated path) before binding descriptor sets
828+
const bool DynamicBuffersIntact = (Flags & DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT) != 0;
829+
const bool InlineConstantsIntact = (Flags & DRAW_FLAG_INLINE_CONSTANTS_INTACT) != 0;
830+
if (!InlineConstantsIntact)
831+
{
832+
UpdateInlineConstantBuffers(BindInfo);
833+
}
834+
761835
// First time we must always bind descriptor sets with dynamic offsets as SRBs are stale.
762836
// If there are no dynamic buffers bound in the resource cache, for all subsequent
763837
// calls we do not need to bind the sets again.
764-
if (Uint32 CommitMask = BindInfo.GetCommitMask(Flags & DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT, Flags & DRAW_FLAG_INLINE_CONSTANTS_INTACT))
838+
if (Uint32 CommitMask = BindInfo.GetCommitMask(DynamicBuffersIntact, InlineConstantsIntact))
765839
{
766840
CommitDescriptorSets(BindInfo, CommitMask);
767841
}
842+
843+
// Commit push constants directly from SRB cache.
844+
// Push constants are always submitted before draw (similar to D3D11/D3D12) because
845+
// inline constants are designed to change every draw call.
846+
CommitPushConstants(BindInfo);
847+
768848
#ifdef DILIGENT_DEVELOPMENT
769849
// Must be called after CommitDescriptorSets as it needs SetInfo.BaseInd
770850
DvpValidateCommittedShaderResources(BindInfo);
@@ -1059,11 +1139,18 @@ void DeviceContextVkImpl::PrepareForDispatchCompute()
10591139
EndRenderScope();
10601140

10611141
ResourceBindInfo& BindInfo = GetBindInfo(PIPELINE_TYPE_COMPUTE);
1142+
1143+
// Update inline constant buffers before binding descriptor sets
1144+
UpdateInlineConstantBuffers(BindInfo);
1145+
10621146
if (Uint32 CommitMask = BindInfo.GetCommitMask())
10631147
{
10641148
CommitDescriptorSets(BindInfo, CommitMask);
10651149
}
10661150

1151+
// Commit push constants directly from SRB cache
1152+
CommitPushConstants(BindInfo);
1153+
10671154
#ifdef DILIGENT_DEVELOPMENT
10681155
// Must be called after CommitDescriptorSets as it needs SetInfo.BaseInd
10691156
DvpValidateCommittedShaderResources(BindInfo);
@@ -1075,11 +1162,18 @@ void DeviceContextVkImpl::PrepareForRayTracing()
10751162
EnsureVkCmdBuffer();
10761163

10771164
ResourceBindInfo& BindInfo = GetBindInfo(PIPELINE_TYPE_RAY_TRACING);
1165+
1166+
// Update inline constant buffers before binding descriptor sets
1167+
UpdateInlineConstantBuffers(BindInfo);
1168+
10781169
if (Uint32 CommitMask = BindInfo.GetCommitMask())
10791170
{
10801171
CommitDescriptorSets(BindInfo, CommitMask);
10811172
}
10821173

1174+
// Commit push constants directly from SRB cache
1175+
CommitPushConstants(BindInfo);
1176+
10831177
#ifdef DILIGENT_DEVELOPMENT
10841178
// Must be called after CommitDescriptorSets as it needs SetInfo.BaseInd
10851179
DvpValidateCommittedShaderResources(BindInfo);

Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,8 @@ bool PipelineResourceSignatureVkImpl::DvpValidateCommittedResource(const DeviceC
956956

957957
VERIFY_EXPR(SPIRVAttribs.ArraySize <= ResAttribs.ArraySize);
958958

959+
// TODO: verify how this works for push constants
960+
959961
bool BindingsOK = true;
960962
for (Uint32 ArrIndex = 0; ArrIndex < SPIRVAttribs.ArraySize; ++ArrIndex)
961963
{
@@ -1000,7 +1002,16 @@ bool PipelineResourceSignatureVkImpl::DvpValidateCommittedResource(const DeviceC
10001002
// is bound. It will be null if the type is incorrect.
10011003
if (const BufferVkImpl* pBufferVk = Res.pObject.RawPtr<BufferVkImpl>())
10021004
{
1003-
pDeviceCtx->DvpVerifyDynamicAllocation(pBufferVk);
1005+
// Skip dynamic allocation verification for inline constant buffers.
1006+
// These are internal buffers managed by the signature and are updated
1007+
// via UpdateInlineConstantBuffers() before each draw/dispatch.
1008+
// The Dynamic Buffer ID may be reused after PSO recreation (e.g., from cache),
1009+
// which would cause false validation failures.
1010+
const bool IsInlineConstantBuffer = (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0;
1011+
if (!IsInlineConstantBuffer)
1012+
{
1013+
pDeviceCtx->DvpVerifyDynamicAllocation(pBufferVk);
1014+
}
10041015

10051016
if ((pBufferVk->GetDesc().Size < SPIRVAttribs.BufferStaticSize) &&
10061017
(GetDevice()->GetValidationFlags() & VALIDATION_FLAG_CHECK_SHADER_BUFFER_SIZE) != 0)
@@ -1115,4 +1126,57 @@ PipelineResourceSignatureInternalDataVk PipelineResourceSignatureVkImpl::GetInte
11151126
return InternalData;
11161127
}
11171128

1129+
void PipelineResourceSignatureVkImpl::UpdateInlineConstantBuffers(const ShaderResourceCacheVk& ResourceCache,
1130+
DeviceContextVkImpl& Ctx,
1131+
Uint32 PushConstantResIndex) const
1132+
{
1133+
// Determine the cache type based on the resource cache content
1134+
// SRB caches use SRBCacheOffset, static caches use StaticCacheOffset
1135+
const ResourceCacheContentType CacheType = ResourceCache.GetContentType();
1136+
1137+
for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i)
1138+
{
1139+
const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i];
1140+
const Uint32 DataSize = InlineCBAttr.NumConstants * sizeof(Uint32);
1141+
const ResourceAttribs& Attr = GetResourceAttribs(InlineCBAttr.ResIndex);
1142+
const Uint32 CacheOffset = Attr.CacheOffset(CacheType);
1143+
const void* pInlineConstantData = ResourceCache.GetInlineConstantData(Attr.DescrSet, CacheOffset);
1144+
VERIFY_EXPR(pInlineConstantData != nullptr);
1145+
1146+
// Skip push constants - they are handled directly by CommitPushConstants
1147+
if (InlineCBAttr.ResIndex == PushConstantResIndex)
1148+
continue;
1149+
1150+
// For emulated inline constants, get the shared buffer from the Signature
1151+
// All SRBs share this buffer (similar to D3D11 backend)
1152+
BufferVkImpl* pBuffer = InlineCBAttr.pBuffer.RawPtr();
1153+
1154+
if (pBuffer)
1155+
{
1156+
// Map the shared buffer and copy the data
1157+
void* pMappedData = nullptr;
1158+
Ctx.MapBuffer(pBuffer, MAP_WRITE, MAP_FLAG_DISCARD, pMappedData);
1159+
memcpy(pMappedData, pInlineConstantData, DataSize);
1160+
Ctx.UnmapBuffer(pBuffer, MAP_WRITE);
1161+
}
1162+
}
1163+
}
1164+
1165+
const void* PipelineResourceSignatureVkImpl::GetPushConstantData(const ShaderResourceCacheVk& ResourceCache, Uint32 ResIndex) const
1166+
{
1167+
// Find the inline constant buffer with the specified resource index
1168+
for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i)
1169+
{
1170+
const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i];
1171+
if (InlineCBAttr.ResIndex == ResIndex)
1172+
{
1173+
const ResourceCacheContentType CacheType = ResourceCache.GetContentType();
1174+
const ResourceAttribs& Attr = GetResourceAttribs(InlineCBAttr.ResIndex);
1175+
const Uint32 CacheOffset = Attr.CacheOffset(CacheType);
1176+
return ResourceCache.GetInlineConstantData(Attr.DescrSet, CacheOffset);
1177+
}
1178+
}
1179+
return nullptr;
1180+
}
1181+
11181182
} // namespace Diligent

Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ class InlineConstants : public ::testing::Test
129129
GPUTestingEnvironment* pEnv = GPUTestingEnvironment::GetInstance();
130130
IRenderDevice* pDevice = pEnv->GetDevice();
131131

132-
if (!pDevice->GetDeviceInfo().IsD3DDevice())
132+
if (!pDevice->GetDeviceInfo().IsD3DDevice() && !pDevice->GetDeviceInfo().IsVulkanDevice())
133133
{
134134
GTEST_SKIP();
135135
}
@@ -342,7 +342,7 @@ void InlineConstants::TestSignatures(Uint32 NumSignatures)
342342

343343
PipelineResourceSignatureDescX SignDesc{"Inline constants test"};
344344
SignDesc
345-
.AddResource(SHADER_TYPE_VERTEX, "cb0_stat", 1u, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_STATIC)
345+
.AddResource(SHADER_TYPE_VERTEX, "cb0_stat", 1u, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_STATIC, PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS)
346346
.AddResource(SHADER_TYPE_VERTEX, "cb0_mut", 1u, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE)
347347
.AddResource(SHADER_TYPE_VERTEX, "cb0_dyn", 1u, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC)
348348
.AddResource(SHADER_TYPE_VERTEX, "tex0_stat", SHADER_RESOURCE_TYPE_TEXTURE_SRV, SHADER_RESOURCE_VARIABLE_TYPE_STATIC)
@@ -363,7 +363,7 @@ void InlineConstants::TestSignatures(Uint32 NumSignatures)
363363
SignDesc.BindingIndex = 1;
364364
}
365365

366-
SignDesc.AddResource(SHADER_TYPE_VERTEX, "cb1_stat", 1u, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_STATIC)
366+
SignDesc.AddResource(SHADER_TYPE_VERTEX, "cb1_stat", 1u, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_STATIC, PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS)
367367
.AddResource(SHADER_TYPE_VERTEX, "cb1_mut", 1u, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE)
368368
.AddResource(SHADER_TYPE_VERTEX, "cb1_dyn", 1u, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC)
369369
.AddResource(SHADER_TYPE_VERTEX, "tex1_stat", SHADER_RESOURCE_TYPE_TEXTURE_SRV, SHADER_RESOURCE_VARIABLE_TYPE_STATIC)
@@ -372,7 +372,7 @@ void InlineConstants::TestSignatures(Uint32 NumSignatures)
372372

373373
.AddResource(SHADER_TYPE_VS_PS, "cbInlineColors", kNumColConstants, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, ColType, PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS)
374374

375-
.AddResource(SHADER_TYPE_VERTEX, "cb2_stat", 1u, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_STATIC)
375+
.AddResource(SHADER_TYPE_VERTEX, "cb2_stat", 1u, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_STATIC, PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS)
376376
.AddResource(SHADER_TYPE_VERTEX, "cb2_mut", 1u, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE)
377377
.AddResource(SHADER_TYPE_VERTEX, "cb2_dyn", 1u, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC)
378378
.AddResource(SHADER_TYPE_VERTEX, "tex2_stat", SHADER_RESOURCE_TYPE_TEXTURE_SRV, SHADER_RESOURCE_VARIABLE_TYPE_STATIC)
@@ -753,6 +753,11 @@ TEST_F(InlineConstants, RenderStateCache)
753753
PresentInCache = true;
754754
}
755755
#endif
756+
if (pDevice->GetDeviceInfo().IsVulkanDevice())
757+
{
758+
// For some reason, pUncachedVS and pVS1 got same hash computation result on Vulkan.
759+
PresentInCache = true;
760+
}
756761
CreatePSOFromCache(pCache, PresentInCache, pUncachedVS, pUncachedPS, &pPSO2);
757762
ASSERT_NE(pPSO2, nullptr);
758763
ASSERT_EQ(pPSO2->GetStatus(), PIPELINE_STATE_STATUS_READY);

0 commit comments

Comments
 (0)