Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2025 Diligent Graphics LLC
* Copyright 2019-2026 Diligent Graphics LLC
* Copyright 2015-2019 Egor Yusov
*
* Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -555,6 +555,9 @@ class DeviceContextVkImpl final : public DeviceContextNextGenBase<EngineVkImplTr
__forceinline ResourceBindInfo& GetBindInfo(PIPELINE_TYPE Type);

__forceinline void CommitDescriptorSets(ResourceBindInfo& BindInfo, Uint32 CommitSRBMask);

void CommitInlineConstants(ResourceBindInfo& BindInfo);

#ifdef DILIGENT_DEVELOPMENT
void DvpValidateCommittedShaderResources(ResourceBindInfo& BindInfo);
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,11 @@ ASSERT_SIZEOF(ImmutableSamplerAttribsVk, 8, "The struct is used in serialization
/// one inline constant is selected to be the native push constant block.
struct InlineConstantBufferAttribsVk
{
Uint32 ResIndex = 0; // Resource index in the signature
Uint32 DescrSet = 0; // Descriptor set index
Uint32 BindingIndex = 0; // Binding index within the descriptor set
Uint32 NumConstants = 0; // Number of 32-bit constants
Uint32 ResIndex = 0; // Resource index in the signature
Uint32 DescrSet = 0; // Descriptor set index
Uint32 BindingIndex = 0; // Binding index within the descriptor set
Uint32 NumConstants = 0; // Number of 32-bit constants
Uint32 SRBCacheOffset = 0; // Offset in the SRB resource cache

// Shared buffer created in the Signature.
// All SRBs reference this same buffer to reduce memory usage.
Expand Down Expand Up @@ -154,11 +155,17 @@ class PipelineResourceSignatureVkImpl final : public PipelineResourceSignatureBa
void CommitDynamicResources(const ShaderResourceCacheVk& ResourceCache,
VkDescriptorSet vkDynamicDescriptorSet) const;

// Returns the number of inline constant buffers
Uint32 GetNumInlineConstantBuffers() const { return m_NumInlineConstantBuffers; }

// Returns the inline constant buffer attributes
const InlineConstantBufferAttribsVk* GetInlineConstantBuffers() const { return m_InlineConstantBuffers.get(); }
struct CommitInlineConstantsAttribs
{
DeviceContextVkImpl& Ctx;
VkPipelineLayout vkPipelineLayout = VK_NULL_HANDLE;
VkPushConstantRange vkPushConstRange = {};
const ShaderResourceCacheVk* pResourceCache = nullptr;
Uint32 PushConstantResIndex = ~0u;
};
// Commits inline constants from the ResourceCache using push constants or
// by mapping and updating the inline constant buffer.
void CommitInlineConstants(const CommitInlineConstantsAttribs& Attribs) const;

#ifdef DILIGENT_DEVELOPMENT
/// Verifies committed resource using the SPIRV resource attributes from the PSO.
Expand Down
59 changes: 58 additions & 1 deletion Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,45 @@ DeviceContextVkImpl::ResourceBindInfo& DeviceContextVkImpl::GetBindInfo(PIPELINE
return m_BindInfo[Indices[Uint32{Type}]];
}

void DeviceContextVkImpl::CommitInlineConstants(ResourceBindInfo& BindInfo)
{
const PipelineLayoutVk& Layout = m_pPipelineState->GetPipelineLayout();
const PipelineLayoutVk::PushConstantInfo& PCInfo = Layout.GetPushConstantInfo();
const Uint32 SignCount = m_pPipelineState->GetResourceSignatureCount();

PipelineResourceSignatureVkImpl::CommitInlineConstantsAttribs CommitAttribs{
*this,
Layout.GetVkPipelineLayout(),
PCInfo.vkRange,
};
for (Uint32 i = 0; i < SignCount; ++i)
{
const PipelineResourceSignatureVkImpl* pSign = m_pPipelineState->GetResourceSignature(i);
if (pSign == nullptr || !pSign->HasInlineConstants())
continue;

CommitAttribs.pResourceCache = BindInfo.ResourceCaches[i];
if (CommitAttribs.pResourceCache == nullptr)
{
// Each signature with inline constants must have a bound SRB with a valid resource cache
DEV_CHECK_ERR(false, "Signature '", pSign->GetDesc().Name, "' has inline constants but no SRB is bound. "
"Did you call CommitShaderResources()?");
continue;
}

if (!CommitAttribs.pResourceCache->HasInlineConstants())
continue;

// Determine which resource (if any) in this signature should use push constant path
// If this signature contains the selected push constant, pass its resource index
// Otherwise pass ~0u to use emulated buffer path for all
CommitAttribs.PushConstantResIndex = (i == PCInfo.SignatureIndex) ? PCInfo.ResourceIndex : ~0u;

// Update inline constant buffers
pSign->CommitInlineConstants(CommitAttribs);
}
}

void DeviceContextVkImpl::CommitDescriptorSets(ResourceBindInfo& BindInfo, Uint32 CommitSRBMask)
{
VERIFY(CommitSRBMask != 0, "This method should not be called when there is nothing to commit");
Expand Down Expand Up @@ -758,13 +797,23 @@ void DeviceContextVkImpl::PrepareForDraw(DRAW_FLAGS Flags)
#endif

ResourceBindInfo& BindInfo = GetBindInfo(PIPELINE_TYPE_GRAPHICS);

// Update inline constant buffers (emulated path) before binding descriptor sets
const bool DynamicBuffersIntact = (Flags & DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT) != 0;
const bool InlineConstantsIntact = (Flags & DRAW_FLAG_INLINE_CONSTANTS_INTACT) != 0;
if (!InlineConstantsIntact)
{
CommitInlineConstants(BindInfo);
}

// First time we must always bind descriptor sets with dynamic offsets as SRBs are stale.
// If there are no dynamic buffers bound in the resource cache, for all subsequent
// calls we do not need to bind the sets again.
if (Uint32 CommitMask = BindInfo.GetCommitMask(Flags & DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT, Flags & DRAW_FLAG_INLINE_CONSTANTS_INTACT))
if (Uint32 CommitMask = BindInfo.GetCommitMask(DynamicBuffersIntact, InlineConstantsIntact))
{
CommitDescriptorSets(BindInfo, CommitMask);
}

#ifdef DILIGENT_DEVELOPMENT
// Must be called after CommitDescriptorSets as it needs SetInfo.BaseInd
DvpValidateCommittedShaderResources(BindInfo);
Expand Down Expand Up @@ -1059,6 +1108,10 @@ void DeviceContextVkImpl::PrepareForDispatchCompute()
EndRenderScope();

ResourceBindInfo& BindInfo = GetBindInfo(PIPELINE_TYPE_COMPUTE);

// Update inline constant buffers before binding descriptor sets
CommitInlineConstants(BindInfo);

if (Uint32 CommitMask = BindInfo.GetCommitMask())
{
CommitDescriptorSets(BindInfo, CommitMask);
Expand All @@ -1075,6 +1128,10 @@ void DeviceContextVkImpl::PrepareForRayTracing()
EnsureVkCmdBuffer();

ResourceBindInfo& BindInfo = GetBindInfo(PIPELINE_TYPE_RAY_TRACING);

// Update inline constant buffers before binding descriptor sets
CommitInlineConstants(BindInfo);

if (Uint32 CommitMask = BindInfo.GetCommitMask())
{
CommitDescriptorSets(BindInfo, CommitMask);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,7 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized)
InlineCBAttribs.DescrSet = pAttribs->DescrSet;
InlineCBAttribs.BindingIndex = pAttribs->BindingIndex;
InlineCBAttribs.NumConstants = ResDesc.ArraySize; // For inline constants, ArraySize is the number of 32-bit constants
InlineCBAttribs.SRBCacheOffset = pAttribs->SRBCacheOffset;

// Create a shared buffer in the Signature for all inline constants.
// All SRBs will reference this same buffer.
Expand Down Expand Up @@ -1000,7 +1001,13 @@ bool PipelineResourceSignatureVkImpl::DvpValidateCommittedResource(const DeviceC
// is bound. It will be null if the type is incorrect.
if (const BufferVkImpl* pBufferVk = Res.pObject.RawPtr<BufferVkImpl>())
{
pDeviceCtx->DvpVerifyDynamicAllocation(pBufferVk);
// Skip dynamic allocation verification for inline constant buffers.
// These are internal buffers managed by the signature and are updated
// via UpdateInlineConstantBuffers() before each draw/dispatch.
if ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) == 0)
{
pDeviceCtx->DvpVerifyDynamicAllocation(pBufferVk);
}

if ((pBufferVk->GetDesc().Size < SPIRVAttribs.BufferStaticSize) &&
(GetDevice()->GetValidationFlags() & VALIDATION_FLAG_CHECK_SHADER_BUFFER_SIZE) != 0)
Expand Down Expand Up @@ -1115,4 +1122,37 @@ PipelineResourceSignatureInternalDataVk PipelineResourceSignatureVkImpl::GetInte
return InternalData;
}

void PipelineResourceSignatureVkImpl::CommitInlineConstants(const CommitInlineConstantsAttribs& Attribs) const
{
const ShaderResourceCacheVk& ResourceCache = *Attribs.pResourceCache;
VERIFY(ResourceCache.GetContentType() == ResourceCacheContentType::SRB,
"Inline constant buffers can be updated only in SRB resource caches");
for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i)
{
const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i];
const Uint32 DataSize = InlineCBAttr.NumConstants * sizeof(Uint32);
const void* pInlineConstantData = ResourceCache.GetInlineConstantData(InlineCBAttr.DescrSet, InlineCBAttr.SRBCacheOffset);
VERIFY_EXPR(pInlineConstantData != nullptr);

if (InlineCBAttr.ResIndex == Attribs.PushConstantResIndex)
{
VulkanUtilities::CommandBuffer& CmdBuffer = Attribs.Ctx.GetCommandBuffer();
VERIFY(Attribs.vkPushConstRange.size == DataSize,
"Push constant range size (", Attribs.vkPushConstRange.size,
") does not match the inline constant buffer data size (", DataSize, ")");
CmdBuffer.PushConstants(Attribs.vkPipelineLayout, Attribs.vkPushConstRange, pInlineConstantData);
}
else
{
VERIFY_EXPR(InlineCBAttr.pBuffer);

// Map the shared buffer and copy the data
void* pMappedData = nullptr;
Attribs.Ctx.MapBuffer(InlineCBAttr.pBuffer, MAP_WRITE, MAP_FLAG_DISCARD, pMappedData);
memcpy(pMappedData, pInlineConstantData, DataSize);
Attribs.Ctx.UnmapBuffer(InlineCBAttr.pBuffer, MAP_WRITE);
}
}
}

} // namespace Diligent
13 changes: 9 additions & 4 deletions Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ class InlineConstants : public ::testing::Test
GPUTestingEnvironment* pEnv = GPUTestingEnvironment::GetInstance();
IRenderDevice* pDevice = pEnv->GetDevice();

if (!pDevice->GetDeviceInfo().IsD3DDevice())
if (!pDevice->GetDeviceInfo().IsD3DDevice() && !pDevice->GetDeviceInfo().IsVulkanDevice())
{
GTEST_SKIP();
}
Expand Down Expand Up @@ -342,7 +342,7 @@ void InlineConstants::TestSignatures(Uint32 NumSignatures)

PipelineResourceSignatureDescX SignDesc{"Inline constants test"};
SignDesc
.AddResource(SHADER_TYPE_VERTEX, "cb0_stat", 1u, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_STATIC)
.AddResource(SHADER_TYPE_VERTEX, "cb0_stat", 1u, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_STATIC, PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS)
.AddResource(SHADER_TYPE_VERTEX, "cb0_mut", 1u, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE)
.AddResource(SHADER_TYPE_VERTEX, "cb0_dyn", 1u, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC)
.AddResource(SHADER_TYPE_VERTEX, "tex0_stat", SHADER_RESOURCE_TYPE_TEXTURE_SRV, SHADER_RESOURCE_VARIABLE_TYPE_STATIC)
Expand All @@ -363,7 +363,7 @@ void InlineConstants::TestSignatures(Uint32 NumSignatures)
SignDesc.BindingIndex = 1;
}

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

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

.AddResource(SHADER_TYPE_VERTEX, "cb2_stat", 1u, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_STATIC)
.AddResource(SHADER_TYPE_VERTEX, "cb2_stat", 1u, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_STATIC, PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS)
.AddResource(SHADER_TYPE_VERTEX, "cb2_mut", 1u, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE)
.AddResource(SHADER_TYPE_VERTEX, "cb2_dyn", 1u, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC)
.AddResource(SHADER_TYPE_VERTEX, "tex2_stat", SHADER_RESOURCE_TYPE_TEXTURE_SRV, SHADER_RESOURCE_VARIABLE_TYPE_STATIC)
Expand Down Expand Up @@ -753,6 +753,11 @@ TEST_F(InlineConstants, RenderStateCache)
PresentInCache = true;
}
#endif
if (pDevice->GetDeviceInfo().IsVulkanDevice())
{
// For some reason, pUncachedVS and pVS1 got same hash computation result on Vulkan.
PresentInCache = true;
}
CreatePSOFromCache(pCache, PresentInCache, pUncachedVS, pUncachedPS, &pPSO2);
ASSERT_NE(pPSO2, nullptr);
ASSERT_EQ(pPSO2->GetStatus(), PIPELINE_STATE_STATUS_READY);
Expand Down
Loading