From 61f2358c5ad93814ed2818db576635a137b522c1 Mon Sep 17 00:00:00 2001 From: assiduous Date: Sun, 19 Oct 2025 10:13:43 -0700 Subject: [PATCH 001/104] Added SHADER_RESOURCE_TYPE_INLINE_CONSTANTS enum value (#672) --- .../src/GraphicsAccessories.cpp | 8 ++++-- Graphics/GraphicsEngine/interface/Shader.h | 10 +++++++ .../PipelineResourceSignatureD3D11Impl.cpp | 19 ++++++------- .../src/ShaderVariableManagerD3D11.cpp | 10 +++++-- .../src/D3D12TypeConversions.cpp | 27 +++++++++++-------- .../PipelineResourceSignatureD3D12Impl.cpp | 2 +- .../src/ShaderResourceCacheD3D12.cpp | 6 +++-- .../src/ShaderVariableManagerD3D12.cpp | 6 ++++- .../src/PipelineResourceSignatureGLImpl.cpp | 3 ++- Graphics/ShaderTools/src/DXBCUtils.cpp | 3 ++- Graphics/ShaderTools/src/DXCompiler.cpp | 3 ++- 11 files changed, 66 insertions(+), 31 deletions(-) diff --git a/Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp b/Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp index e1321b2853..66b7d6bb7b 100644 --- a/Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp +++ b/Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp @@ -862,7 +862,7 @@ const Char* GetShaderVariableTypeLiteralName(SHADER_RESOURCE_VARIABLE_TYPE VarTy const Char* GetShaderResourceTypeLiteralName(SHADER_RESOURCE_TYPE ResourceType, bool bGetFullName) { - static_assert(SHADER_RESOURCE_TYPE_LAST == 8, "Please update the switch below to handle the new shader resource type"); + static_assert(SHADER_RESOURCE_TYPE_LAST == 9, "Please update the switch below to handle the new shader resource type"); switch (ResourceType) { // clang-format off @@ -874,6 +874,7 @@ const Char* GetShaderResourceTypeLiteralName(SHADER_RESOURCE_TYPE ResourceType, case SHADER_RESOURCE_TYPE_BUFFER_UAV: return bGetFullName ? "SHADER_RESOURCE_TYPE_BUFFER_UAV" : "buffer UAV"; case SHADER_RESOURCE_TYPE_SAMPLER: return bGetFullName ? "SHADER_RESOURCE_TYPE_SAMPLER" : "sampler"; case SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT: return bGetFullName ? "SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT" : "input attachment"; + case SHADER_RESOURCE_TYPE_INLINE_CONSTANTS: return bGetFullName ? "SHADER_RESOURCE_TYPE_INLINE_CONSTANTS" : "inline constants"; case SHADER_RESOURCE_TYPE_ACCEL_STRUCT: return bGetFullName ? "SHADER_RESOURCE_TYPE_ACCEL_STRUCT" : "acceleration structure"; // clang-format on default: @@ -1804,7 +1805,7 @@ String GetLayoutElementString(const LayoutElement& Element) PIPELINE_RESOURCE_FLAGS GetValidPipelineResourceFlags(SHADER_RESOURCE_TYPE ResourceType) { - static_assert(SHADER_RESOURCE_TYPE_LAST == 8, "Please update the switch below to handle the new shader resource type"); + static_assert(SHADER_RESOURCE_TYPE_LAST == 9, "Please update the switch below to handle the new shader resource type"); switch (ResourceType) { case SHADER_RESOURCE_TYPE_CONSTANT_BUFFER: @@ -1828,6 +1829,9 @@ PIPELINE_RESOURCE_FLAGS GetValidPipelineResourceFlags(SHADER_RESOURCE_TYPE Resou case SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT: return PIPELINE_RESOURCE_FLAG_GENERAL_INPUT_ATTACHMENT; + case SHADER_RESOURCE_TYPE_INLINE_CONSTANTS: + return PIPELINE_RESOURCE_FLAG_NONE; + case SHADER_RESOURCE_TYPE_ACCEL_STRUCT: return PIPELINE_RESOURCE_FLAG_RUNTIME_ARRAY; diff --git a/Graphics/GraphicsEngine/interface/Shader.h b/Graphics/GraphicsEngine/interface/Shader.h index 7909272f6f..b0236d7c6f 100644 --- a/Graphics/GraphicsEngine/interface/Shader.h +++ b/Graphics/GraphicsEngine/interface/Shader.h @@ -723,6 +723,16 @@ DILIGENT_TYPED_ENUM(SHADER_RESOURCE_TYPE, Uint8) /// Input attachment in a render pass SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT, + /// Inline constants (aka push constants in Vulkan or root constants in Direct3D12) + + /// In Vulkan/Direct3D12, inline constants are not bound via descriptor sets or root + /// signatures but are set directly in command buffers / command lists. As such, they + /// are very cheap to set and are intended for small pieces of frequently changing data. + /// + /// In legacy APIs (Direct3D11/OpenGL), inline constants are emulated using regular + /// constant buffers. + SHADER_RESOURCE_TYPE_INLINE_CONSTANTS, + /// Acceleration structure SHADER_RESOURCE_TYPE_ACCEL_STRUCT, diff --git a/Graphics/GraphicsEngineD3D11/src/PipelineResourceSignatureD3D11Impl.cpp b/Graphics/GraphicsEngineD3D11/src/PipelineResourceSignatureD3D11Impl.cpp index edd339f46b..17f29de84b 100644 --- a/Graphics/GraphicsEngineD3D11/src/PipelineResourceSignatureD3D11Impl.cpp +++ b/Graphics/GraphicsEngineD3D11/src/PipelineResourceSignatureD3D11Impl.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2024 Diligent Graphics LLC + * Copyright 2019-2025 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -90,17 +90,18 @@ PipelineResourceSignatureD3D11Impl::PipelineResourceSignatureD3D11Impl(IReferenc D3D11_RESOURCE_RANGE PipelineResourceSignatureD3D11Impl::ShaderResourceTypeToRange(SHADER_RESOURCE_TYPE Type) { - static_assert(SHADER_RESOURCE_TYPE_LAST == 8, "Please update the switch below to handle the new shader resource type"); + static_assert(SHADER_RESOURCE_TYPE_LAST == 9, "Please update the switch below to handle the new shader resource type"); switch (Type) { // clang-format off - case SHADER_RESOURCE_TYPE_CONSTANT_BUFFER: return D3D11_RESOURCE_RANGE_CBV; - case SHADER_RESOURCE_TYPE_TEXTURE_SRV: return D3D11_RESOURCE_RANGE_SRV; - case SHADER_RESOURCE_TYPE_BUFFER_SRV: return D3D11_RESOURCE_RANGE_SRV; - case SHADER_RESOURCE_TYPE_TEXTURE_UAV: return D3D11_RESOURCE_RANGE_UAV; - case SHADER_RESOURCE_TYPE_BUFFER_UAV: return D3D11_RESOURCE_RANGE_UAV; - case SHADER_RESOURCE_TYPE_SAMPLER: return D3D11_RESOURCE_RANGE_SAMPLER; - case SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT: return D3D11_RESOURCE_RANGE_SRV; + case SHADER_RESOURCE_TYPE_CONSTANT_BUFFER: return D3D11_RESOURCE_RANGE_CBV; + case SHADER_RESOURCE_TYPE_TEXTURE_SRV: return D3D11_RESOURCE_RANGE_SRV; + case SHADER_RESOURCE_TYPE_BUFFER_SRV: return D3D11_RESOURCE_RANGE_SRV; + case SHADER_RESOURCE_TYPE_TEXTURE_UAV: return D3D11_RESOURCE_RANGE_UAV; + case SHADER_RESOURCE_TYPE_BUFFER_UAV: return D3D11_RESOURCE_RANGE_UAV; + case SHADER_RESOURCE_TYPE_SAMPLER: return D3D11_RESOURCE_RANGE_SAMPLER; + case SHADER_RESOURCE_TYPE_INLINE_CONSTANTS: return D3D11_RESOURCE_RANGE_CBV; + case SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT: return D3D11_RESOURCE_RANGE_SRV; // clang-format on default: UNEXPECTED("Unsupported resource type"); diff --git a/Graphics/GraphicsEngineD3D11/src/ShaderVariableManagerD3D11.cpp b/Graphics/GraphicsEngineD3D11/src/ShaderVariableManagerD3D11.cpp index 87762b641b..c70d81d689 100644 --- a/Graphics/GraphicsEngineD3D11/src/ShaderVariableManagerD3D11.cpp +++ b/Graphics/GraphicsEngineD3D11/src/ShaderVariableManagerD3D11.cpp @@ -121,7 +121,7 @@ D3DShaderResourceCounters ShaderVariableManagerD3D11::CountResources( [&](Uint32 Index) // { const PipelineResourceDesc& ResDesc = Signature.GetResourceDesc(Index); - static_assert(SHADER_RESOURCE_TYPE_LAST == 8, "Please update the switch below to handle the new shader resource range"); + static_assert(SHADER_RESOURCE_TYPE_LAST == 9, "Please update the switch below to handle the new shader resource range"); switch (ResDesc.ResourceType) { // clang-format off @@ -132,6 +132,7 @@ D3DShaderResourceCounters ShaderVariableManagerD3D11::CountResources( case SHADER_RESOURCE_TYPE_BUFFER_UAV: ++Counters.NumBufUAVs; break; case SHADER_RESOURCE_TYPE_SAMPLER: ++Counters.NumSamplers; break; case SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT: ++Counters.NumTexSRVs; break; + case SHADER_RESOURCE_TYPE_INLINE_CONSTANTS: ++Counters.NumCBs; break; // clang-format on default: UNEXPECTED("Unsupported resource type."); @@ -215,7 +216,7 @@ void ShaderVariableManagerD3D11::Initialize(const PipelineResourceSignatureD3D11 [&](Uint32 Index) // { const PipelineResourceDesc& ResDesc = Signature.GetResourceDesc(Index); - static_assert(SHADER_RESOURCE_TYPE_LAST == 8, "Please update the switch below to handle the new shader resource range"); + static_assert(SHADER_RESOURCE_TYPE_LAST == 9, "Please update the switch below to handle the new shader resource range"); switch (ResDesc.ResourceType) { case SHADER_RESOURCE_TYPE_CONSTANT_BUFFER: @@ -249,6 +250,11 @@ void ShaderVariableManagerD3D11::Initialize(const PipelineResourceSignatureD3D11 new (&GetResource(sam++)) SamplerBindInfo{*this, Index}; break; + case SHADER_RESOURCE_TYPE_INLINE_CONSTANTS: + // Initialize CB for inline constants in place, increment CB counter + new (&GetResource(cb++)) ConstBuffBindInfo{*this, Index}; + break; + default: UNEXPECTED("Unsupported resource type."); } diff --git a/Graphics/GraphicsEngineD3D12/src/D3D12TypeConversions.cpp b/Graphics/GraphicsEngineD3D12/src/D3D12TypeConversions.cpp index 9fada8f72d..7f2fac2e07 100644 --- a/Graphics/GraphicsEngineD3D12/src/D3D12TypeConversions.cpp +++ b/Graphics/GraphicsEngineD3D12/src/D3D12TypeConversions.cpp @@ -849,20 +849,25 @@ DXGI_FORMAT TypeToRayTracingVertexFormat(VALUE_TYPE ValueType, Uint32 ComponentC D3D12_DESCRIPTOR_RANGE_TYPE ResourceTypeToD3D12DescriptorRangeType(SHADER_RESOURCE_TYPE ResType) { - static_assert(SHADER_RESOURCE_TYPE_LAST == 8, "Please update the switch below to handle the new resource type"); + static_assert(SHADER_RESOURCE_TYPE_LAST == 9, "Please update the switch below to handle the new resource type"); switch (ResType) { - // clang-format off - case SHADER_RESOURCE_TYPE_CONSTANT_BUFFER: return D3D12_DESCRIPTOR_RANGE_TYPE_CBV; - case SHADER_RESOURCE_TYPE_TEXTURE_SRV: return D3D12_DESCRIPTOR_RANGE_TYPE_SRV; - case SHADER_RESOURCE_TYPE_BUFFER_SRV: return D3D12_DESCRIPTOR_RANGE_TYPE_SRV; - case SHADER_RESOURCE_TYPE_TEXTURE_UAV: return D3D12_DESCRIPTOR_RANGE_TYPE_UAV; - case SHADER_RESOURCE_TYPE_BUFFER_UAV: return D3D12_DESCRIPTOR_RANGE_TYPE_UAV; - case SHADER_RESOURCE_TYPE_SAMPLER: return D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER; - case SHADER_RESOURCE_TYPE_ACCEL_STRUCT: return D3D12_DESCRIPTOR_RANGE_TYPE_SRV; - case SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT:return D3D12_DESCRIPTOR_RANGE_TYPE_SRV; - // clang-format on + // clang-format off + case SHADER_RESOURCE_TYPE_CONSTANT_BUFFER: return D3D12_DESCRIPTOR_RANGE_TYPE_CBV; + case SHADER_RESOURCE_TYPE_TEXTURE_SRV: return D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + case SHADER_RESOURCE_TYPE_BUFFER_SRV: return D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + case SHADER_RESOURCE_TYPE_TEXTURE_UAV: return D3D12_DESCRIPTOR_RANGE_TYPE_UAV; + case SHADER_RESOURCE_TYPE_BUFFER_UAV: return D3D12_DESCRIPTOR_RANGE_TYPE_UAV; + case SHADER_RESOURCE_TYPE_SAMPLER: return D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER; + case SHADER_RESOURCE_TYPE_ACCEL_STRUCT: return D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + case SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT: return D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + // clang-format on + + case SHADER_RESOURCE_TYPE_INLINE_CONSTANTS: + UNEXPECTED("Inline constants do not have a descriptor range type"); + return static_cast(-1); + default: UNEXPECTED("Unknown resource type"); return static_cast(-1); diff --git a/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp index c61bc1bee6..2353983f35 100644 --- a/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp @@ -773,7 +773,7 @@ bool PipelineResourceSignatureD3D12Impl::DvpValidateCommittedResource(const Devi } } - static_assert(SHADER_RESOURCE_TYPE_LAST == 8, "Please update the switch below to handle the new shader resource type"); + static_assert(SHADER_RESOURCE_TYPE_LAST == 9, "Please update the switch below to handle the new shader resource type"); switch (ResDesc.ResourceType) { case SHADER_RESOURCE_TYPE_TEXTURE_SRV: diff --git a/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp b/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp index 5ae744f8cf..2eff2c8cca 100644 --- a/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp +++ b/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp @@ -485,7 +485,7 @@ void ShaderResourceCacheD3D12::DbgValidateDynamicBuffersMask() const void ShaderResourceCacheD3D12::Resource::TransitionResource(CommandContext& Ctx) { - static_assert(SHADER_RESOURCE_TYPE_LAST == 8, "Please update this function to handle the new resource type"); + static_assert(SHADER_RESOURCE_TYPE_LAST == 9, "Please update this function to handle the new resource type"); switch (Type) { case SHADER_RESOURCE_TYPE_CONSTANT_BUFFER: @@ -543,6 +543,7 @@ void ShaderResourceCacheD3D12::Resource::TransitionResource(CommandContext& Ctx) break; case SHADER_RESOURCE_TYPE_SAMPLER: + case SHADER_RESOURCE_TYPE_INLINE_CONSTANTS: // Nothing to transition break; @@ -569,7 +570,7 @@ void ShaderResourceCacheD3D12::Resource::TransitionResource(CommandContext& Ctx) #ifdef DILIGENT_DEVELOPMENT void ShaderResourceCacheD3D12::Resource::DvpVerifyResourceState() { - static_assert(SHADER_RESOURCE_TYPE_LAST == 8, "Please update this function to handle the new resource type"); + static_assert(SHADER_RESOURCE_TYPE_LAST == 9, "Please update this function to handle the new resource type"); switch (Type) { case SHADER_RESOURCE_TYPE_CONSTANT_BUFFER: @@ -657,6 +658,7 @@ void ShaderResourceCacheD3D12::Resource::DvpVerifyResourceState() break; case SHADER_RESOURCE_TYPE_SAMPLER: + case SHADER_RESOURCE_TYPE_INLINE_CONSTANTS: // No resource break; diff --git a/Graphics/GraphicsEngineD3D12/src/ShaderVariableManagerD3D12.cpp b/Graphics/GraphicsEngineD3D12/src/ShaderVariableManagerD3D12.cpp index 98be7eb388..16f64ceb01 100644 --- a/Graphics/GraphicsEngineD3D12/src/ShaderVariableManagerD3D12.cpp +++ b/Graphics/GraphicsEngineD3D12/src/ShaderVariableManagerD3D12.cpp @@ -570,7 +570,7 @@ void BindResourceHelper::operator()(const BindResourceInfo& BindInfo) const VERIFY_EXPR(m_ArrayIndex == BindInfo.ArrayIndex); if (BindInfo.pObject != nullptr) { - static_assert(SHADER_RESOURCE_TYPE_LAST == 8, "Please update this function to handle the new resource type"); + static_assert(SHADER_RESOURCE_TYPE_LAST == 9, "Please update this function to handle the new resource type"); switch (m_ResDesc.ResourceType) { case SHADER_RESOURCE_TYPE_CONSTANT_BUFFER: @@ -598,6 +598,10 @@ void BindResourceHelper::operator()(const BindResourceInfo& BindInfo) const CacheSampler(BindInfo); break; + case SHADER_RESOURCE_TYPE_INLINE_CONSTANTS: + UNSUPPORTED("Not yet implemented"); + break; + case SHADER_RESOURCE_TYPE_ACCEL_STRUCT: CacheAccelStruct(BindInfo); break; diff --git a/Graphics/GraphicsEngineOpenGL/src/PipelineResourceSignatureGLImpl.cpp b/Graphics/GraphicsEngineOpenGL/src/PipelineResourceSignatureGLImpl.cpp index b100a2b8bb..4152dbaadb 100644 --- a/Graphics/GraphicsEngineOpenGL/src/PipelineResourceSignatureGLImpl.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/PipelineResourceSignatureGLImpl.cpp @@ -55,7 +55,7 @@ const char* GetBindingRangeName(BINDING_RANGE Range) BINDING_RANGE PipelineResourceToBindingRange(const PipelineResourceDesc& Desc) { - static_assert(SHADER_RESOURCE_TYPE_LAST == 8, "Please update the switch below to handle the new shader resource type"); + static_assert(SHADER_RESOURCE_TYPE_LAST == 9, "Please update the switch below to handle the new shader resource type"); switch (Desc.ResourceType) { // clang-format off @@ -65,6 +65,7 @@ BINDING_RANGE PipelineResourceToBindingRange(const PipelineResourceDesc& Desc) case SHADER_RESOURCE_TYPE_TEXTURE_UAV: return BINDING_RANGE_IMAGE; case SHADER_RESOURCE_TYPE_BUFFER_UAV: return (Desc.Flags & PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER) ? BINDING_RANGE_IMAGE : BINDING_RANGE_STORAGE_BUFFER; case SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT: return BINDING_RANGE_TEXTURE; + case SHADER_RESOURCE_TYPE_INLINE_CONSTANTS: return BINDING_RANGE_UNIFORM_BUFFER; // clang-format on case SHADER_RESOURCE_TYPE_SAMPLER: case SHADER_RESOURCE_TYPE_ACCEL_STRUCT: diff --git a/Graphics/ShaderTools/src/DXBCUtils.cpp b/Graphics/ShaderTools/src/DXBCUtils.cpp index ee946889e3..3cac44c482 100644 --- a/Graphics/ShaderTools/src/DXBCUtils.cpp +++ b/Graphics/ShaderTools/src/DXBCUtils.cpp @@ -939,7 +939,7 @@ void RemapShaderResources(const DXBCUtils::TResourceBindingMap& ResourceMap, con Iter->second.ArraySize >= Res.BindCount); #ifdef DILIGENT_DEBUG - static_assert(SHADER_RESOURCE_TYPE_LAST == 8, "Please update the switch below to handle the new shader resource type"); + static_assert(SHADER_RESOURCE_TYPE_LAST == 9, "Please update the switch below to handle the new shader resource type"); switch (Iter->second.ResType) { // clang-format off @@ -950,6 +950,7 @@ void RemapShaderResources(const DXBCUtils::TResourceBindingMap& ResourceMap, con case SHADER_RESOURCE_TYPE_BUFFER_UAV: VERIFY_EXPR(ResType == RES_TYPE_UAV); break; case SHADER_RESOURCE_TYPE_SAMPLER: VERIFY_EXPR(ResType == RES_TYPE_SAMPLER); break; case SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT: VERIFY_EXPR(ResType == RES_TYPE_SRV); break; + case SHADER_RESOURCE_TYPE_INLINE_CONSTANTS: VERIFY_EXPR(ResType == RES_TYPE_CBV); break; // clang-format on default: UNEXPECTED("Unsupported shader resource type."); } diff --git a/Graphics/ShaderTools/src/DXCompiler.cpp b/Graphics/ShaderTools/src/DXCompiler.cpp index e8768e2641..a024833b77 100644 --- a/Graphics/ShaderTools/src/DXCompiler.cpp +++ b/Graphics/ShaderTools/src/DXCompiler.cpp @@ -926,7 +926,7 @@ bool DXCompilerImpl::RemapResourceBindings(const TResourceBindingMap& ResourceMa # ifdef DILIGENT_DEVELOPMENT { - static_assert(SHADER_RESOURCE_TYPE_LAST == 8, "Please update the switch below to handle the new shader resource type"); + static_assert(SHADER_RESOURCE_TYPE_LAST == 9, "Please update the switch below to handle the new shader resource type"); RES_TYPE ExpectedResType = RES_TYPE_COUNT; switch (NameAndBinding.second.ResType) { @@ -938,6 +938,7 @@ bool DXCompilerImpl::RemapResourceBindings(const TResourceBindingMap& ResourceMa case SHADER_RESOURCE_TYPE_BUFFER_UAV: ExpectedResType = RES_TYPE_UAV; break; case SHADER_RESOURCE_TYPE_SAMPLER: ExpectedResType = RES_TYPE_SAMPLER; break; case SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT: ExpectedResType = RES_TYPE_SRV; break; + case SHADER_RESOURCE_TYPE_INLINE_CONSTANTS: ExpectedResType = RES_TYPE_CBV; break; case SHADER_RESOURCE_TYPE_ACCEL_STRUCT: ExpectedResType = RES_TYPE_SRV; break; // clang-format on default: UNEXPECTED("Unsupported shader resource type."); From aea9cac00be699217d9e5270bdb5ae7d322acee7 Mon Sep 17 00:00:00 2001 From: assiduous Date: Sun, 19 Oct 2025 12:50:34 -0700 Subject: [PATCH 002/104] D3D12/RootParamsManager: add support for root constants (#672) --- .../PipelineResourceSignatureD3D12Impl.hpp | 7 +- .../include/RootParamsManager.hpp | 33 +++++- .../src/RootParamsManager.cpp | 107 ++++++++++++++---- .../GraphicsEngineD3D12/src/RootSignature.cpp | 20 +++- 4 files changed, 138 insertions(+), 29 deletions(-) diff --git a/Graphics/GraphicsEngineD3D12/include/PipelineResourceSignatureD3D12Impl.hpp b/Graphics/GraphicsEngineD3D12/include/PipelineResourceSignatureD3D12Impl.hpp index e76c7beef5..9fb6731f6b 100644 --- a/Graphics/GraphicsEngineD3D12/include/PipelineResourceSignatureD3D12Impl.hpp +++ b/Graphics/GraphicsEngineD3D12/include/PipelineResourceSignatureD3D12Impl.hpp @@ -125,7 +125,7 @@ class PipelineResourceSignatureD3D12Impl final : public PipelineResourceSignatur Uint32 GetTotalRootParamsCount() const { - return m_RootParams.GetNumRootTables() + m_RootParams.GetNumRootViews(); + return m_RootParams.GetNumRootTables() + m_RootParams.GetNumRootViews() + m_RootParams.GetNumRootConstants(); } Uint32 GetNumRootTables() const @@ -138,6 +138,11 @@ class PipelineResourceSignatureD3D12Impl final : public PipelineResourceSignatur return m_RootParams.GetNumRootViews(); } + Uint32 GetNumRootConstants() const + { + return m_RootParams.GetNumRootConstants(); + } + void InitSRBResourceCache(ShaderResourceCacheD3D12& ResourceCache); void CopyStaticResources(ShaderResourceCacheD3D12& ResourceCache) const; diff --git a/Graphics/GraphicsEngineD3D12/include/RootParamsManager.hpp b/Graphics/GraphicsEngineD3D12/include/RootParamsManager.hpp index 379838a6d0..368c476e17 100644 --- a/Graphics/GraphicsEngineD3D12/include/RootParamsManager.hpp +++ b/Graphics/GraphicsEngineD3D12/include/RootParamsManager.hpp @@ -139,6 +139,7 @@ class RootParamsManager Uint32 GetNumRootTables() const { return m_NumRootTables; } Uint32 GetNumRootViews() const { return m_NumRootViews; } + Uint32 GetNumRootConstants() const { return m_NumRootConstants; } const RootParameter& GetRootTable(Uint32 TableInd) const { @@ -152,6 +153,12 @@ class RootParamsManager return m_pRootViews[ViewInd]; } + const RootParameter& GetRootConstants(Uint32 ConstInd) const + { + VERIFY_EXPR(ConstInd < m_NumRootConstants); + return m_pRootConstants[ConstInd]; + } + // Returns the total number of resources in a given parameter group and descriptor heap type Uint32 GetParameterGroupSize(D3D12_DESCRIPTOR_HEAP_TYPE d3d12HeapType, ROOT_PARAMETER_GROUP Group) const { @@ -169,11 +176,18 @@ class RootParamsManager std::unique_ptr> m_pMemory; + // The number of root tables Uint32 m_NumRootTables = 0; - Uint32 m_NumRootViews = 0; - const RootParameter* m_pRootTables = nullptr; - const RootParameter* m_pRootViews = nullptr; + // The number of root views + Uint32 m_NumRootViews = 0; + + // The number of root constant parameters (not the number of 32-bit constants) + Uint32 m_NumRootConstants = 0; + + const RootParameter* m_pRootTables = nullptr; + const RootParameter* m_pRootViews = nullptr; + const RootParameter* m_pRootConstants = nullptr; // The total number of resources placed in descriptor tables for each heap type and parameter group type std::array, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER + 1> m_ParameterGroupSizes{}; @@ -206,6 +220,14 @@ class RootParamsBuilder D3D12_SHADER_VISIBILITY Visibility, ROOT_PARAMETER_GROUP RootType); + // Adds a new root constants parameter and returns the reference to it. + RootParameter& AddRootConstants(Uint32 RootIndex, + UINT Register, + UINT RegisterSpace, + UINT Num32BitValues, + D3D12_SHADER_VISIBILITY Visibility, + ROOT_PARAMETER_GROUP RootType); + struct RootTableData; // Adds a new root table parameter and returns the reference to it. RootTableData& AddRootTable(Uint32 RootIndex, @@ -214,6 +236,10 @@ class RootParamsBuilder Uint32 NumRangesInNewTable = 1); +#ifdef DILIGENT_DEBUG + void DbgCheckRootIndexUniqueness(Uint32 RootIndex) const; +#endif + private: struct RootTableData { @@ -231,6 +257,7 @@ class RootParamsBuilder }; std::vector m_RootTables; std::vector m_RootViews; + std::vector m_RootConstants; static constexpr int InvalidRootTableIndex = -1; diff --git a/Graphics/GraphicsEngineD3D12/src/RootParamsManager.cpp b/Graphics/GraphicsEngineD3D12/src/RootParamsManager.cpp index 81611b13cb..b04c927d73 100644 --- a/Graphics/GraphicsEngineD3D12/src/RootParamsManager.cpp +++ b/Graphics/GraphicsEngineD3D12/src/RootParamsManager.cpp @@ -144,13 +144,14 @@ size_t RootParameter::GetHash() const RootParamsManager::~RootParamsManager() { - static_assert(std::is_trivially_destructible::value, "Destructors for m_pRootTables and m_pRootViews are required"); + static_assert(std::is_trivially_destructible::value, "m_pRootTables, m_pRootViews and m_pRootConstants must be manually destroyed"); } bool RootParamsManager::operator==(const RootParamsManager& RootParams) const noexcept { if (m_NumRootTables != RootParams.m_NumRootTables || - m_NumRootViews != RootParams.m_NumRootViews) + m_NumRootViews != RootParams.m_NumRootViews || + m_NumRootConstants != RootParams.m_NumRootConstants) return false; for (Uint32 rv = 0; rv < m_NumRootViews; ++rv) @@ -169,6 +170,14 @@ bool RootParamsManager::operator==(const RootParamsManager& RootParams) const no return false; } + for (Uint32 rc = 0; rc < m_NumRootConstants; ++rc) + { + const RootParameter& RC0 = GetRootConstants(rc); + const RootParameter& RC1 = RootParams.GetRootConstants(rc); + if (RC0 != RC1) + return false; + } + return true; } @@ -230,6 +239,13 @@ void RootParamsManager::Validate() const VERIFY(RootView.TableOffsetInGroupAllocation == RootParameter::InvalidTableOffsetInGroupAllocation, "Root views must not be assigned to descriptor table allocations."); } + + for (Uint32 i = 0; i < GetNumRootConstants(); ++i) + { + const RootParameter& RootConst = GetRootConstants(i); + VERIFY(RootConst.TableOffsetInGroupAllocation == RootParameter::InvalidTableOffsetInGroupAllocation, + "Root constants must not be assigned to descriptor table allocations."); + } } #endif @@ -243,6 +259,18 @@ RootParamsBuilder::RootParamsBuilder() Map.fill(InvalidRootTableIndex); } +#ifdef DILIGENT_DEBUG +void RootParamsBuilder::DbgCheckRootIndexUniqueness(Uint32 RootIndex) const +{ + for (const RootTableData& RootTbl : m_RootTables) + VERIFY(RootTbl.RootIndex != RootIndex, "Index ", RootIndex, " is already used by another root table"); + for (const RootParameter& RootView : m_RootViews) + VERIFY(RootView.RootIndex != RootIndex, "Index ", RootIndex, " is already used by another root view"); + for (const RootParameter& RootConst : m_RootConstants) + VERIFY(RootConst.RootIndex != RootIndex, "Index ", RootIndex, " is already used by another root constant"); +} +#endif + RootParameter& RootParamsBuilder::AddRootView(D3D12_ROOT_PARAMETER_TYPE ParameterType, Uint32 RootIndex, UINT Register, @@ -255,11 +283,7 @@ RootParameter& RootParamsBuilder::AddRootView(D3D12_ROOT_PARAMETER_TYPE Paramete ParameterType == D3D12_ROOT_PARAMETER_TYPE_SRV || ParameterType == D3D12_ROOT_PARAMETER_TYPE_UAV), "Unexpected parameter type SBV, SRV or UAV is expected"); - - for (const RootTableData& RootTbl : m_RootTables) - VERIFY(RootTbl.RootIndex != RootIndex, "Index ", RootIndex, " is already used by another root table"); - for (const RootParameter& RootView : m_RootViews) - VERIFY(RootView.RootIndex != RootIndex, "Index ", RootIndex, " is already used by another root view"); + DbgCheckRootIndexUniqueness(RootIndex); #endif D3D12_ROOT_PARAMETER d3d12RootParam{ParameterType, {}, Visibility}; @@ -270,6 +294,26 @@ RootParameter& RootParamsBuilder::AddRootView(D3D12_ROOT_PARAMETER_TYPE Paramete return m_RootViews.back(); } +RootParameter& RootParamsBuilder::AddRootConstants(Uint32 RootIndex, + UINT Register, + UINT RegisterSpace, + UINT Num32BitValues, + D3D12_SHADER_VISIBILITY Visibility, + ROOT_PARAMETER_GROUP RootType) +{ +#ifdef DILIGENT_DEBUG + DbgCheckRootIndexUniqueness(RootIndex); +#endif + + D3D12_ROOT_PARAMETER d3d12RootParam{D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS, {}, Visibility}; + d3d12RootParam.Constants.ShaderRegister = Register; + d3d12RootParam.Constants.RegisterSpace = RegisterSpace; + d3d12RootParam.Constants.Num32BitValues = Num32BitValues; + m_RootConstants.emplace_back(RootIndex, RootType, d3d12RootParam); + + return m_RootConstants.back(); +} + RootParamsBuilder::RootTableData::RootTableData(Uint32 _RootIndex, D3D12_SHADER_VISIBILITY _Visibility, ROOT_PARAMETER_GROUP _Group, @@ -305,10 +349,7 @@ RootParamsBuilder::RootTableData& RootParamsBuilder::AddRootTable(Uint32 Uint32 NumRangesInNewTable) { #ifdef DILIGENT_DEBUG - for (const RootTableData& RootTbl : m_RootTables) - VERIFY(RootTbl.RootIndex != RootIndex, "Index ", RootIndex, " is already used by another root table"); - for (const RootParameter& RootView : m_RootViews) - VERIFY(RootView.RootIndex != RootIndex, "Index ", RootIndex, " is already used by another root view"); + DbgCheckRootIndexUniqueness(RootIndex); #endif m_RootTables.emplace_back(RootIndex, Visibility, Group, NumRangesInNewTable); @@ -330,8 +371,8 @@ void RootParamsBuilder::AllocateResourceSlot(SHADER_TYPE Shade const D3D12_SHADER_VISIBILITY ShaderVisibility = ShaderStagesToD3D12ShaderVisibility(ShaderStages); const ROOT_PARAMETER_GROUP ParameterGroup = VariableTypeToRootParameterGroup(VariableType); - // Get the next available root index past all allocated tables and root views - RootIndex = static_cast(m_RootTables.size() + m_RootViews.size()); + // Get the next available root index past all allocated tables, root views and root constants + RootIndex = static_cast(m_RootTables.size() + m_RootViews.size() + m_RootConstants.size()); if (RootParameterType == D3D12_ROOT_PARAMETER_TYPE_CBV || RootParameterType == D3D12_ROOT_PARAMETER_TYPE_SRV || @@ -345,6 +386,13 @@ void RootParamsBuilder::AllocateResourceSlot(SHADER_TYPE Shade // Add new root view to existing root parameters AddRootView(RootParameterType, RootIndex, Register, Space, ShaderVisibility, ParameterGroup); } + else if (RootParameterType == D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS) + { + OffsetFromTableStart = 0; + + // Add new 32-bit constants parameter to existing root parameters + AddRootConstants(RootIndex, Register, Space, ArraySize, ShaderVisibility, ParameterGroup); + } else if (RootParameterType == D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE) { const bool IsSampler = (RangeType == D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER); @@ -406,15 +454,17 @@ void RootParamsBuilder::InitializeMgr(IMemoryAllocator& MemAllocator, RootParams { VERIFY(!ParamsMgr.m_pMemory, "Params manager has already been initialized!"); - Uint32& NumRootTables = ParamsMgr.m_NumRootTables; - Uint32& NumRootViews = ParamsMgr.m_NumRootViews; + Uint32& NumRootTables = ParamsMgr.m_NumRootTables; + Uint32& NumRootViews = ParamsMgr.m_NumRootViews; + Uint32& NumRootConstants = ParamsMgr.m_NumRootConstants; - NumRootTables = static_cast(m_RootTables.size()); - NumRootViews = static_cast(m_RootViews.size()); - if (NumRootTables == 0 && NumRootViews == 0) + NumRootTables = static_cast(m_RootTables.size()); + NumRootViews = static_cast(m_RootViews.size()); + NumRootConstants = static_cast(m_RootConstants.size()); + if (NumRootTables == 0 && NumRootViews == 0 && NumRootConstants == 0) return; - const size_t TotalRootParamsCount = m_RootTables.size() + m_RootViews.size(); + const size_t TotalRootParamsCount = m_RootTables.size() + m_RootViews.size() + m_RootConstants.size(); size_t TotalRangesCount = 0; for (RootTableData& Tbl : m_RootTables) @@ -437,7 +487,8 @@ void RootParamsBuilder::InitializeMgr(IMemoryAllocator& MemAllocator, RootParams // Note: this order is more efficient than views->tables->ranges RootParameter* const pRootTables = reinterpret_cast(ParamsMgr.m_pMemory.get()); RootParameter* const pRootViews = pRootTables + NumRootTables; - D3D12_DESCRIPTOR_RANGE* const pDescriptorRanges = reinterpret_cast(pRootViews + NumRootViews); + RootParameter* const pRootConstants = pRootViews + NumRootViews; + D3D12_DESCRIPTOR_RANGE* const pDescriptorRanges = reinterpret_cast(pRootConstants + NumRootConstants); // Copy descriptor tables D3D12_DESCRIPTOR_RANGE* pCurrDescrRangePtr = pDescriptorRanges; @@ -484,8 +535,20 @@ void RootParamsBuilder::InitializeMgr(IMemoryAllocator& MemAllocator, RootParams "Unexpected parameter type: SBV, SRV or UAV is expected"); new (pRootViews + rv) RootParameter{SrcView.RootIndex, SrcView.Group, d3d12RootParam}; } - ParamsMgr.m_pRootTables = NumRootTables != 0 ? pRootTables : nullptr; - ParamsMgr.m_pRootViews = NumRootViews != 0 ? pRootViews : nullptr; + + // Copy root constants + for (Uint32 rc = 0; rc < NumRootConstants; ++rc) + { + const RootParameter& SrcConst = m_RootConstants[rc]; + const D3D12_ROOT_PARAMETER& d3d12RootParam = SrcConst.d3d12RootParam; + VERIFY(d3d12RootParam.ParameterType == D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS, + "Unexpected parameter type: 32-bit constants is expected"); + new (pRootConstants + rc) RootParameter{SrcConst.RootIndex, SrcConst.Group, d3d12RootParam}; + } + + ParamsMgr.m_pRootTables = NumRootTables != 0 ? pRootTables : nullptr; + ParamsMgr.m_pRootViews = NumRootViews != 0 ? pRootViews : nullptr; + ParamsMgr.m_pRootConstants = NumRootConstants != 0 ? pRootConstants : nullptr; #ifdef DILIGENT_DEBUG ParamsMgr.Validate(); diff --git a/Graphics/GraphicsEngineD3D12/src/RootSignature.cpp b/Graphics/GraphicsEngineD3D12/src/RootSignature.cpp index 54c1d04f9e..7231b5f415 100644 --- a/Graphics/GraphicsEngineD3D12/src/RootSignature.cpp +++ b/Graphics/GraphicsEngineD3D12/src/RootSignature.cpp @@ -78,7 +78,7 @@ RootSignatureD3D12::RootSignatureD3D12(IReferenceCounters* const RootParamsManager& RootParams = pSignature->GetRootParams(); SignInfo.BaseRootIndex = TotalParams; - TotalParams += RootParams.GetNumRootTables() + RootParams.GetNumRootViews(); + TotalParams += RootParams.GetNumRootTables() + RootParams.GetNumRootViews() + RootParams.GetNumRootConstants(); for (Uint32 rt = 0; rt < RootParams.GetNumRootTables(); ++rt) { @@ -166,6 +166,20 @@ RootSignatureD3D12::RootSignatureD3D12(IReferenceCounters* d3d12Parameters[RootIndex].Descriptor.RegisterSpace += BaseRegisterSpace; } + for (Uint32 rc = 0; rc < RootParams.GetNumRootConstants(); ++rc) + { + const RootParameter& RootConsts = RootParams.GetRootConstants(rc); + const D3D12_ROOT_PARAMETER& d3d12SrcParam = RootConsts.d3d12RootParam; + const Uint32 RootIndex = SignInfo.BaseRootIndex + RootConsts.RootIndex; + VERIFY(d3d12SrcParam.ParameterType == D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS, "Root 32-bit constants is expected"); + + MaxSpaceUsed = std::max(MaxSpaceUsed, d3d12SrcParam.Constants.RegisterSpace); + + d3d12Parameters[RootIndex] = d3d12SrcParam; + // Offset register space value by the base register space of the current resource signature. + d3d12Parameters[RootIndex].Constants.RegisterSpace += BaseRegisterSpace; + } + for (Uint32 samp = 0, SampCount = pSignature->GetImmutableSamplerCount(); samp < SampCount; ++samp) { const ImmutableSamplerAttribsD3D12& SampAttr = pSignature->GetImmutableSamplerAttribs(samp); @@ -191,8 +205,8 @@ RootSignatureD3D12::RootSignatureD3D12(IReferenceCounters* SamDesc.MaxLOD, SampAttr.ShaderRegister + ArrInd, SampAttr.RegisterSpace + BaseRegisterSpace, - ShaderVisibility // - } // + ShaderVisibility, + } // ); } } From d729b38b79ebd19360c2c73db9786c31aca213d4 Mon Sep 17 00:00:00 2001 From: assiduous Date: Sun, 19 Oct 2025 15:08:50 -0700 Subject: [PATCH 003/104] ResourceSignatureD3D12: implement CommitRootConstants method (#672) --- .../include/DeviceContextBase.hpp | 18 ++++++++- .../include/ShaderResourceCacheD3D11.hpp | 5 +++ .../PipelineResourceSignatureD3D12Impl.hpp | 3 ++ .../include/ShaderResourceCacheD3D12.hpp | 9 +++++ .../PipelineResourceSignatureD3D12Impl.cpp | 39 +++++++++++++++++++ .../include/ShaderResourceCacheGL.hpp | 5 +++ .../include/ShaderResourceCacheVk.hpp | 5 +++ .../include/ShaderResourceCacheWebGPU.hpp | 5 +++ 8 files changed, 88 insertions(+), 1 deletion(-) diff --git a/Graphics/GraphicsEngine/include/DeviceContextBase.hpp b/Graphics/GraphicsEngine/include/DeviceContextBase.hpp index 972e92d641..e4ee3d13b3 100644 --- a/Graphics/GraphicsEngine/include/DeviceContextBase.hpp +++ b/Graphics/GraphicsEngine/include/DeviceContextBase.hpp @@ -381,6 +381,9 @@ class DeviceContextBase : public ObjectBaseHasInlineConstants()) + InlineConstantsSRBMask |= SRBBit; + else + InlineConstantsSRBMask &= ~SRBBit; + #ifdef DILIGENT_DEVELOPMENT SRBs[Index] = pSRB; if (pSRB != nullptr) @@ -412,7 +420,8 @@ class DeviceContextBase : public ObjectBaseGetRootTable(RootInd); + const Uint32& BaseRootIndex = CommitAttribs.BaseRootIndex; + + VERIFY_EXPR(CacheTbl.GetSize() == 1); + const ShaderResourceCacheD3D12::Resource& Res = CacheTbl.GetResource(0); + VERIFY_EXPR(Res.Type == SHADER_RESOURCE_TYPE_INLINE_CONSTANTS); + VERIFY(Res.IsNull(), "There should be no resource bound for root constants as they contain raw data."); + + const Uint32* pConstants = reinterpret_cast(Res.CPUDescriptorHandle.ptr); + VERIFY(pConstants != nullptr, "Resources used to store root constants must have valid pointer to the data."); + // The number of root constants is stored in BufferRangeSize + const Uint32 NumConstants = static_cast(Res.BufferRangeSize); + + ID3D12GraphicsCommandList* const pd3d12CmdList = CommitAttribs.CmdCtx.GetCommandList(); + if (CommitAttribs.IsCompute) + { + for (Uint32 i = 0; i < NumConstants; ++i) + pd3d12CmdList->SetComputeRoot32BitConstant(BaseRootIndex + RootInd, pConstants[i], i); + } + else + { + for (Uint32 i = 0; i < NumConstants; ++i) + pd3d12CmdList->SetGraphicsRoot32BitConstant(BaseRootIndex + RootInd, pConstants[i], i); + } + } +} + void PipelineResourceSignatureD3D12Impl::CommitRootTables(const CommitCacheResourcesAttribs& CommitAttribs) const { VERIFY_EXPR(CommitAttribs.pResourceCache != nullptr); diff --git a/Graphics/GraphicsEngineOpenGL/include/ShaderResourceCacheGL.hpp b/Graphics/GraphicsEngineOpenGL/include/ShaderResourceCacheGL.hpp index e0e4190a7d..f7e83adcf0 100644 --- a/Graphics/GraphicsEngineOpenGL/include/ShaderResourceCacheGL.hpp +++ b/Graphics/GraphicsEngineOpenGL/include/ShaderResourceCacheGL.hpp @@ -345,6 +345,11 @@ class ShaderResourceCacheGL : public ShaderResourceCacheBase return m_DynamicUBOMask != 0 || m_DynamicSSBOMask != 0; } + bool HasInlineConstants() const + { + return false; + } + #ifdef DILIGENT_DEBUG void DbgVerifyDynamicBufferMasks() const; #endif diff --git a/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp b/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp index 0e3b376d8c..26bf336e4d 100644 --- a/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp +++ b/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp @@ -249,6 +249,11 @@ class ShaderResourceCacheVk : public ShaderResourceCacheBase Uint32 GetNumDescriptorSets() const { return m_NumSets; } bool HasDynamicResources() const { return m_NumDynamicBuffers > 0; } + bool HasInlineConstants() const + { + return false; + } + ResourceCacheContentType GetContentType() const { return static_cast(m_ContentType); } #ifdef DILIGENT_DEBUG diff --git a/Graphics/GraphicsEngineWebGPU/include/ShaderResourceCacheWebGPU.hpp b/Graphics/GraphicsEngineWebGPU/include/ShaderResourceCacheWebGPU.hpp index 4c8067eb59..206fe5926b 100644 --- a/Graphics/GraphicsEngineWebGPU/include/ShaderResourceCacheWebGPU.hpp +++ b/Graphics/GraphicsEngineWebGPU/include/ShaderResourceCacheWebGPU.hpp @@ -176,6 +176,11 @@ class ShaderResourceCacheWebGPU : public ShaderResourceCacheBase Uint32 GetNumBindGroups() const { return m_NumBindGroups; } bool HasDynamicResources() const { return m_NumDynamicBuffers > 0; } + bool HasInlineConstants() const + { + return false; + } + ResourceCacheContentType GetContentType() const { return static_cast(m_ContentType); } WGPUBindGroup UpdateBindGroup(WGPUDevice wgpuDevice, Uint32 GroupIndex, WGPUBindGroupLayout wgpuGroupLayout); From 4a20552bfb0605d533d1b9b3841400b048e06d6a Mon Sep 17 00:00:00 2001 From: assiduous Date: Sun, 19 Oct 2025 15:30:26 -0700 Subject: [PATCH 004/104] Add DRAW_FLAG_INLINE_CONSTANTS_INTACT flag (#672) --- .../GraphicsEngine/interface/DeviceContext.h | 22 ++++++++++++++++++- .../src/DeviceContextD3D11Impl.cpp | 2 +- .../src/DeviceContextD3D12Impl.cpp | 2 +- .../src/DeviceContextGLImpl.cpp | 2 +- .../src/DeviceContextVkImpl.cpp | 2 +- .../src/DeviceContextWebGPUImpl.cpp | 2 +- 6 files changed, 26 insertions(+), 6 deletions(-) diff --git a/Graphics/GraphicsEngine/interface/DeviceContext.h b/Graphics/GraphicsEngine/interface/DeviceContext.h index d7ee0c448b..263d1da145 100644 --- a/Graphics/GraphicsEngine/interface/DeviceContext.h +++ b/Graphics/GraphicsEngine/interface/DeviceContext.h @@ -213,7 +213,27 @@ DILIGENT_TYPED_ENUM(DRAW_FLAGS, Uint8) /// (see RootSignature::CommitRootViews). When `DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT` is set, root views are only bound /// by the first draw command that uses the PSO + SRB pair. The flag avoids setting the same GPU virtual addresses when /// they stay unchanged. - DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT = 1u << 2u + /// + /// \see DRAW_FLAG_INLINE_CONSTANTS_INTACT, DRAW_FLAG_DYNAMIC_BUFFERS_AND_INLINE_CONSTANTS_INTACT + DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT = 1u << 2u, + + /// Indicates that none of the inline constants used by the draw command + /// have been modified by the CPU since the last command. + /// + /// This flag should be used to improve performance when an application issues a + /// series of draw commands that use the same pipeline state and shader resources and + /// no inline constants are updated between the commands. + /// + /// \see DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT, DRAW_FLAG_DYNAMIC_BUFFERS_AND_INLINE_CONSTANTS_INTACT + DRAW_FLAG_INLINE_CONSTANTS_INTACT = 1u << 3u, + + /// Indicates that neither the dynamic resource buffers nor the inline constants used by + /// the draw command have been modified by the CPU since the previous command. + /// + /// \see DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT, DRAW_FLAG_INLINE_CONSTANTS_INTACT + DRAW_FLAG_DYNAMIC_BUFFERS_AND_INLINE_CONSTANTS_INTACT = + DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT | + DRAW_FLAG_INLINE_CONSTANTS_INTACT }; DEFINE_FLAG_ENUM_OPERATORS(DRAW_FLAGS) diff --git a/Graphics/GraphicsEngineD3D11/src/DeviceContextD3D11Impl.cpp b/Graphics/GraphicsEngineD3D11/src/DeviceContextD3D11Impl.cpp index fccc955464..b1f725ac77 100755 --- a/Graphics/GraphicsEngineD3D11/src/DeviceContextD3D11Impl.cpp +++ b/Graphics/GraphicsEngineD3D11/src/DeviceContextD3D11Impl.cpp @@ -672,7 +672,7 @@ void DeviceContextD3D11Impl::PrepareForDraw(DRAW_FLAGS Flags) CommitD3D11VertexBuffers(m_pPipelineState); } - if (Uint32 BindSRBMask = m_BindInfo.GetCommitMask(Flags & DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT)) + if (Uint32 BindSRBMask = m_BindInfo.GetCommitMask(Flags & DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT, Flags & DRAW_FLAG_INLINE_CONSTANTS_INTACT)) { BindShaderResources(BindSRBMask); } diff --git a/Graphics/GraphicsEngineD3D12/src/DeviceContextD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/DeviceContextD3D12Impl.cpp index 6f49c65d14..e99e4bbef6 100644 --- a/Graphics/GraphicsEngineD3D12/src/DeviceContextD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/DeviceContextD3D12Impl.cpp @@ -629,7 +629,7 @@ void DeviceContextD3D12Impl::PrepareForDraw(GraphicsContext& GraphCtx, DRAW_FLAG #ifdef DILIGENT_DEVELOPMENT DvpValidateCommittedShaderResources(RootInfo); #endif - if (Uint32 CommitSRBMask = RootInfo.GetCommitMask(Flags & DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT)) + if (Uint32 CommitSRBMask = RootInfo.GetCommitMask(Flags & DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT, Flags & DRAW_FLAG_INLINE_CONSTANTS_INTACT)) { CommitRootTablesAndViews(RootInfo, CommitSRBMask, GraphCtx); } diff --git a/Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp b/Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp index ddb50122c9..7121283829 100644 --- a/Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp @@ -800,7 +800,7 @@ void DeviceContextGLImpl::PrepareForDraw(DRAW_FLAGS Flags, bool IsIndexed, GLenu // The program might have changed since the last SetPipelineState call if a shader was // created after the call (ShaderResourcesGL needs to bind a program to load uniforms). m_pPipelineState->CommitProgram(m_ContextState); - if (Uint32 BindSRBMask = m_BindInfo.GetCommitMask(Flags & DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT)) + if (Uint32 BindSRBMask = m_BindInfo.GetCommitMask(Flags & DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT, Flags & DRAW_FLAG_INLINE_CONSTANTS_INTACT)) { BindProgramResources(BindSRBMask); } diff --git a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp index df035cdb4b..140acab84c 100644 --- a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp @@ -761,7 +761,7 @@ void DeviceContextVkImpl::PrepareForDraw(DRAW_FLAGS Flags) // 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)) + if (Uint32 CommitMask = BindInfo.GetCommitMask(Flags & DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT, Flags & DRAW_FLAG_INLINE_CONSTANTS_INTACT)) { CommitDescriptorSets(BindInfo, CommitMask); } diff --git a/Graphics/GraphicsEngineWebGPU/src/DeviceContextWebGPUImpl.cpp b/Graphics/GraphicsEngineWebGPU/src/DeviceContextWebGPUImpl.cpp index 2ce32b2b19..e0231c747e 100644 --- a/Graphics/GraphicsEngineWebGPU/src/DeviceContextWebGPUImpl.cpp +++ b/Graphics/GraphicsEngineWebGPU/src/DeviceContextWebGPUImpl.cpp @@ -2097,7 +2097,7 @@ WGPURenderPassEncoder DeviceContextWebGPUImpl::PrepareForDraw(DRAW_FLAGS Flags) m_EncoderState.SetUpToDate(WebGPUEncoderState::CMD_ENCODER_STATE_STENCIL_REF); } - if (CommittedShaderResources::SRBMaskType CommitSRBMask = m_BindInfo.GetCommitMask(Flags & DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT)) + if (CommittedShaderResources::SRBMaskType CommitSRBMask = m_BindInfo.GetCommitMask(Flags & DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT, Flags & DRAW_FLAG_INLINE_CONSTANTS_INTACT)) CommitBindGroups(wgpuRenderCmdEncoder, CommitSRBMask); return wgpuRenderCmdEncoder; From 0c612092abc93a723c76ca112700b7886bc0a605 Mon Sep 17 00:00:00 2001 From: assiduous Date: Sun, 19 Oct 2025 16:29:17 -0700 Subject: [PATCH 005/104] IShaderResourceVariable: add SetInlineConstants method plumbing (#672) --- .../include/ShaderResourceVariableBase.hpp | 15 +++++++++++++++ .../interface/ShaderResourceVariable.h | 11 +++++++++++ .../include/ShaderVariableManagerD3D11.hpp | 7 +++++++ .../src/ShaderVariableManagerD3D11.cpp | 5 +++++ .../include/ShaderVariableManagerD3D12.hpp | 14 +++++++++++++- .../src/ShaderVariableManagerD3D12.cpp | 8 ++++++++ .../include/ShaderVariableManagerGL.hpp | 7 +++++++ .../src/ShaderVariableManagerGL.cpp | 4 ++++ .../include/ShaderVariableManagerVk.hpp | 14 +++++++++++++- .../src/ShaderVariableManagerVk.cpp | 8 ++++++++ .../include/ShaderVariableManagerWebGPU.hpp | 14 +++++++++++++- .../src/ShaderVariableManagerWebGPU.cpp | 7 +++++++ 12 files changed, 111 insertions(+), 3 deletions(-) diff --git a/Graphics/GraphicsEngine/include/ShaderResourceVariableBase.hpp b/Graphics/GraphicsEngine/include/ShaderResourceVariableBase.hpp index 266f9ffa68..97497329a7 100644 --- a/Graphics/GraphicsEngine/include/ShaderResourceVariableBase.hpp +++ b/Graphics/GraphicsEngine/include/ShaderResourceVariableBase.hpp @@ -713,6 +713,21 @@ struct ShaderVariableBase : public ResourceVariableBaseInterface } + virtual void DILIGENT_CALL_TYPE SetInlineConstants(const void* pConstants, + Uint32 FirstConstant, + Uint32 NumConstants) override final + { +#ifdef DILIGENT_DEVELOPMENT + { + const PipelineResourceDesc& Desc = GetDesc(); + DEV_CHECK_ERR(Desc.ResourceType == SHADER_RESOURCE_TYPE_INLINE_CONSTANTS, + "SetInlineConstants() is only allowed for inline constant variables."); + } +#endif + + static_cast(this)->SetConstants(pConstants, FirstConstant, NumConstants); + } + virtual SHADER_RESOURCE_VARIABLE_TYPE DILIGENT_CALL_TYPE GetType() const override final { return GetDesc().VarType; diff --git a/Graphics/GraphicsEngine/interface/ShaderResourceVariable.h b/Graphics/GraphicsEngine/interface/ShaderResourceVariable.h index 50e6d94b35..ba87a85404 100644 --- a/Graphics/GraphicsEngine/interface/ShaderResourceVariable.h +++ b/Graphics/GraphicsEngine/interface/ShaderResourceVariable.h @@ -250,6 +250,17 @@ DILIGENT_BEGIN_INTERFACE(IShaderResourceVariable, IObject) Uint32 ArrayIndex DEFAULT_VALUE(0)) PURE; + /// For inline constant variables, sets the constant values + + /// \param [in] pConstants - pointer to the array of 32-bit constant values. + /// \param [in] FirstConstant - index of the first 32-bit constant to set. + /// \param [in] NumConstants - number of 32-bit constants to set. + VIRTUAL void METHOD(SetInlineConstants)(THIS_ + const void* pConstants, + Uint32 FirstConstant, + Uint32 NumConstants) PURE; + + /// Returns the shader resource variable type VIRTUAL SHADER_RESOURCE_VARIABLE_TYPE METHOD(GetType)(THIS) CONST PURE; diff --git a/Graphics/GraphicsEngineD3D11/include/ShaderVariableManagerD3D11.hpp b/Graphics/GraphicsEngineD3D11/include/ShaderVariableManagerD3D11.hpp index a800eee65a..e297101f42 100644 --- a/Graphics/GraphicsEngineD3D11/include/ShaderVariableManagerD3D11.hpp +++ b/Graphics/GraphicsEngineD3D11/include/ShaderVariableManagerD3D11.hpp @@ -130,6 +130,11 @@ class ShaderVariableManagerD3D11 : ShaderVariableManagerBase @@ -141,6 +146,8 @@ class ShaderVariableManagerD3D11 : ShaderVariableManagerBase diff --git a/Graphics/GraphicsEngineD3D11/src/ShaderVariableManagerD3D11.cpp b/Graphics/GraphicsEngineD3D11/src/ShaderVariableManagerD3D11.cpp index c70d81d689..924bda26f0 100644 --- a/Graphics/GraphicsEngineD3D11/src/ShaderVariableManagerD3D11.cpp +++ b/Graphics/GraphicsEngineD3D11/src/ShaderVariableManagerD3D11.cpp @@ -306,6 +306,11 @@ void ShaderVariableManagerD3D11::ConstBuffBindInfo::SetDynamicOffset(Uint32 Arra m_ParentManager.m_ResourceCache.SetDynamicCBOffset(Attr.BindPoints + ArrayIndex, Offset); } +void ShaderVariableManagerD3D11::ConstBuffBindInfo::SetConstants(const void* pConstants, Uint32 FirstConstant, Uint32 NumConstants) +{ + UNSUPPORTED("Not yet implemented"); +} + void ShaderVariableManagerD3D11::TexSRVBindInfo::BindResource(const BindResourceInfo& BindInfo) { const PipelineResourceDesc& Desc = GetDesc(); diff --git a/Graphics/GraphicsEngineD3D12/include/ShaderVariableManagerD3D12.hpp b/Graphics/GraphicsEngineD3D12/include/ShaderVariableManagerD3D12.hpp index 44e5e31fd7..21aeccf209 100644 --- a/Graphics/GraphicsEngineD3D12/include/ShaderVariableManagerD3D12.hpp +++ b/Graphics/GraphicsEngineD3D12/include/ShaderVariableManagerD3D12.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2024 Diligent Graphics LLC + * Copyright 2019-2025 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -104,6 +104,11 @@ class ShaderVariableManagerD3D12 : public ShaderVariableManagerBase Date: Sun, 19 Oct 2025 16:36:54 -0700 Subject: [PATCH 006/104] ResourceSignatureD3D12: set all root constants at once (#672) --- .../src/PipelineResourceSignatureD3D12Impl.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp index d2c965c111..e48dfa5c30 100644 --- a/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp @@ -550,13 +550,11 @@ void PipelineResourceSignatureD3D12Impl::CommitRootConstants(const CommitCacheRe ID3D12GraphicsCommandList* const pd3d12CmdList = CommitAttribs.CmdCtx.GetCommandList(); if (CommitAttribs.IsCompute) { - for (Uint32 i = 0; i < NumConstants; ++i) - pd3d12CmdList->SetComputeRoot32BitConstant(BaseRootIndex + RootInd, pConstants[i], i); + pd3d12CmdList->SetComputeRoot32BitConstants(BaseRootIndex + RootInd, NumConstants, pConstants, 0); } else { - for (Uint32 i = 0; i < NumConstants; ++i) - pd3d12CmdList->SetGraphicsRoot32BitConstant(BaseRootIndex + RootInd, pConstants[i], i); + pd3d12CmdList->SetGraphicsRoot32BitConstants(BaseRootIndex + RootInd, NumConstants, pConstants, 0); } } } From 2a86179813a664f2a06ef53cb6efe758826da8b7 Mon Sep 17 00:00:00 2001 From: assiduous Date: Sun, 19 Oct 2025 17:11:58 -0700 Subject: [PATCH 007/104] ResourceCacheD3D12: implement SetInlineConstants (#672) --- .../include/ShaderResourceVariableBase.hpp | 34 +++++++++++++++++++ .../include/ShaderResourceCacheD3D12.hpp | 5 +++ .../src/ShaderResourceCacheD3D12.cpp | 16 +++++++++ .../src/ShaderVariableManagerD3D12.cpp | 14 +++++++- 4 files changed, 68 insertions(+), 1 deletion(-) diff --git a/Graphics/GraphicsEngine/include/ShaderResourceVariableBase.hpp b/Graphics/GraphicsEngine/include/ShaderResourceVariableBase.hpp index 97497329a7..9d895d217b 100644 --- a/Graphics/GraphicsEngine/include/ShaderResourceVariableBase.hpp +++ b/Graphics/GraphicsEngine/include/ShaderResourceVariableBase.hpp @@ -165,6 +165,12 @@ bool VerifyResourceBinding(const char* ExpectedResourceTypeName, const IDeviceObject* pCachedObject, // Object already set in the cache const char* SignatureName) { + if (ResDesc.ResourceType == SHADER_RESOURCE_TYPE_INLINE_CONSTANTS) + { + RESOURCE_VALIDATION_FAILURE("Cannot bind resources to inline constants variable '", GetShaderResourcePrintName(ResDesc, BindInfo.ArrayIndex), '\''); + return false; + } + if (BindInfo.pObject != nullptr && pResourceImpl == nullptr) { std::stringstream ss; @@ -602,6 +608,34 @@ bool VerifyDynamicBufferOffset(const PipelineResourceDesc& ResDesc, } +inline bool VerifyInlineConstants(const PipelineResourceDesc& ResDesc, + const void* pConstants, + Uint32 FirstConstant, + Uint32 NumConstants) +{ + bool ParamsOK = true; + if (ResDesc.ResourceType != SHADER_RESOURCE_TYPE_INLINE_CONSTANTS) + { + RESOURCE_VALIDATION_FAILURE("SetInlineConstants() is only allowed for inline constant variables."); + ParamsOK = false; + } + + if (pConstants == nullptr) + { + RESOURCE_VALIDATION_FAILURE("Pointer to inline constants is null."); + ParamsOK = false; + } + + if (FirstConstant + NumConstants > ResDesc.ArraySize) + { + RESOURCE_VALIDATION_FAILURE("Inline constant range (", FirstConstant, " .. ", FirstConstant + NumConstants - 1, + ") is out of bounds for variable '", ResDesc.Name, "' of size ", ResDesc.ArraySize, " constants."); + ParamsOK = false; + } + + return ParamsOK; +} + #undef RESOURCE_VALIDATION_FAILURE template diff --git a/Graphics/GraphicsEngineD3D12/include/ShaderResourceCacheD3D12.hpp b/Graphics/GraphicsEngineD3D12/include/ShaderResourceCacheD3D12.hpp index f3b42995e4..0658c0f3a1 100644 --- a/Graphics/GraphicsEngineD3D12/include/ShaderResourceCacheD3D12.hpp +++ b/Graphics/GraphicsEngineD3D12/include/ShaderResourceCacheD3D12.hpp @@ -281,6 +281,11 @@ class ShaderResourceCacheD3D12 : public ShaderResourceCacheBase Uint32 OffsetFromTableStart, Uint32 BufferDynamicOffset); + void SetInlineConstants(Uint32 RootIndex, + const void* pConstants, + Uint32 FirstConstant, + Uint32 NumConstants); + const RootTable& GetRootTable(Uint32 RootIndex) const { VERIFY_EXPR(RootIndex < m_NumTables); diff --git a/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp b/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp index 2eff2c8cca..68da48e28e 100644 --- a/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp +++ b/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp @@ -386,6 +386,22 @@ void ShaderResourceCacheD3D12::SetBufferDynamicOffset(Uint32 RootIndex, Res.BufferDynamicOffset = BufferDynamicOffset; } +void ShaderResourceCacheD3D12::SetInlineConstants(Uint32 RootIndex, + const void* pConstants, + Uint32 FirstConstant, + Uint32 NumConstants) +{ + RootTable& Tbl = GetRootTable(RootIndex); + Resource& Res = Tbl.GetResource(0); + VERIFY_EXPR(Res.Type == SHADER_RESOURCE_TYPE_INLINE_CONSTANTS); + VERIFY(Res.IsNull(), "There should be no resource bound for root constants as they contain raw data."); + VERIFY(Res.CPUDescriptorHandle.ptr != 0, "Resources used to store root constants must have valid pointer to the data."); + VERIFY(FirstConstant + NumConstants <= Res.BufferRangeSize, + "Too many constants (", FirstConstant + NumConstants, ") for the allocated space (", Res.BufferRangeSize, ")"); + Uint32* pDstConstants = reinterpret_cast(Res.CPUDescriptorHandle.ptr); + memcpy(pDstConstants + FirstConstant, pConstants, NumConstants * sizeof(Uint32)); +} + const ShaderResourceCacheD3D12::Resource& ShaderResourceCacheD3D12::CopyResource(ID3D12Device* pd3d12Device, Uint32 RootIndex, Uint32 OffsetFromTableStart, diff --git a/Graphics/GraphicsEngineD3D12/src/ShaderVariableManagerD3D12.cpp b/Graphics/GraphicsEngineD3D12/src/ShaderVariableManagerD3D12.cpp index 0a53386381..d7b8289739 100644 --- a/Graphics/GraphicsEngineD3D12/src/ShaderVariableManagerD3D12.cpp +++ b/Graphics/GraphicsEngineD3D12/src/ShaderVariableManagerD3D12.cpp @@ -678,7 +678,19 @@ void ShaderVariableManagerD3D12::SetInlineConstants(Uint32 ResIndex, Uint32 FirstConstant, Uint32 NumConstants) { - UNSUPPORTED("Not implemented yet"); + const ResourceAttribs& Attribs = m_pSignature->GetResourceAttribs(ResIndex); + const ResourceCacheContentType CacheType = m_ResourceCache.GetContentType(); + const Uint32 RootIndex = Attribs.RootIndex(CacheType); + VERIFY_EXPR(Attribs.OffsetFromTableStart(CacheType) == 0); + +#ifdef DILIGENT_DEVELOPMENT + { + const PipelineResourceDesc& ResDesc = m_pSignature->GetResourceDesc(ResIndex); + VerifyInlineConstants(ResDesc, pConstants, FirstConstant, NumConstants); + } +#endif + + m_ResourceCache.SetInlineConstants(RootIndex, pConstants, FirstConstant, NumConstants); } IDeviceObject* ShaderVariableManagerD3D12::Get(Uint32 ArrayIndex, From 5e188f019c869a7a558b0b71f4085feebbbe9de9 Mon Sep 17 00:00:00 2001 From: assiduous Date: Sun, 19 Oct 2025 19:50:58 -0700 Subject: [PATCH 008/104] Replace SHADER_RESOURCE_TYPE_INLINE_CONSTANTS type with PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS flag (#672) --- .../src/GraphicsAccessories.cpp | 16 ++++---- .../include/ShaderResourceVariableBase.hpp | 37 ++++++++++++++----- .../interface/PipelineResourceSignature.h | 18 ++++++++- Graphics/GraphicsEngine/interface/Shader.h | 10 ----- .../interface/ShaderResourceVariable.h | 17 +++++---- .../src/PipelineResourceSignatureBase.cpp | 6 +++ .../PipelineResourceSignatureD3D11Impl.cpp | 3 +- .../src/ShaderVariableManagerD3D11.cpp | 10 +---- .../src/D3D12TypeConversions.cpp | 6 +-- .../PipelineResourceSignatureD3D12Impl.cpp | 4 +- .../src/ShaderResourceCacheD3D12.cpp | 8 ++-- .../src/ShaderVariableManagerD3D12.cpp | 6 +-- .../src/PipelineResourceSignatureGLImpl.cpp | 3 +- Graphics/ShaderTools/src/DXBCUtils.cpp | 3 +- Graphics/ShaderTools/src/DXCompiler.cpp | 3 +- .../GraphicsAccessoriesTest.cpp | 4 +- 16 files changed, 83 insertions(+), 71 deletions(-) diff --git a/Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp b/Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp index 66b7d6bb7b..9300fccf59 100644 --- a/Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp +++ b/Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp @@ -862,7 +862,7 @@ const Char* GetShaderVariableTypeLiteralName(SHADER_RESOURCE_VARIABLE_TYPE VarTy const Char* GetShaderResourceTypeLiteralName(SHADER_RESOURCE_TYPE ResourceType, bool bGetFullName) { - static_assert(SHADER_RESOURCE_TYPE_LAST == 9, "Please update the switch below to handle the new shader resource type"); + static_assert(SHADER_RESOURCE_TYPE_LAST == 8, "Please update the switch below to handle the new shader resource type"); switch (ResourceType) { // clang-format off @@ -874,7 +874,6 @@ const Char* GetShaderResourceTypeLiteralName(SHADER_RESOURCE_TYPE ResourceType, case SHADER_RESOURCE_TYPE_BUFFER_UAV: return bGetFullName ? "SHADER_RESOURCE_TYPE_BUFFER_UAV" : "buffer UAV"; case SHADER_RESOURCE_TYPE_SAMPLER: return bGetFullName ? "SHADER_RESOURCE_TYPE_SAMPLER" : "sampler"; case SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT: return bGetFullName ? "SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT" : "input attachment"; - case SHADER_RESOURCE_TYPE_INLINE_CONSTANTS: return bGetFullName ? "SHADER_RESOURCE_TYPE_INLINE_CONSTANTS" : "inline constants"; case SHADER_RESOURCE_TYPE_ACCEL_STRUCT: return bGetFullName ? "SHADER_RESOURCE_TYPE_ACCEL_STRUCT" : "acceleration structure"; // clang-format on default: @@ -1638,7 +1637,7 @@ String GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAGS Flags, bool GetFul PIPELINE_RESOURCE_FLAGS Flag = ExtractLSB(Flags); - static_assert(PIPELINE_RESOURCE_FLAG_LAST == (1u << 4), "Please update the switch below to handle the new pipeline resource flag."); + static_assert(PIPELINE_RESOURCE_FLAG_LAST == (1u << 5), "Please update the switch below to handle the new pipeline resource flag."); switch (Flag) { case PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS: @@ -1661,6 +1660,10 @@ String GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAGS Flags, bool GetFul Str.append(GetFullName ? "PIPELINE_RESOURCE_FLAG_GENERAL_INPUT_ATTACHMENT" : "GENERAL_INPUT_ATTACHMENT"); break; + case PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS: + Str.append(GetFullName ? "PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS" : "INLINE_CONSTANTS"); + break; + default: UNEXPECTED("Unexpected pipeline resource flag"); } @@ -1805,11 +1808,11 @@ String GetLayoutElementString(const LayoutElement& Element) PIPELINE_RESOURCE_FLAGS GetValidPipelineResourceFlags(SHADER_RESOURCE_TYPE ResourceType) { - static_assert(SHADER_RESOURCE_TYPE_LAST == 9, "Please update the switch below to handle the new shader resource type"); + static_assert(SHADER_RESOURCE_TYPE_LAST == 8, "Please update the switch below to handle the new shader resource type"); switch (ResourceType) { case SHADER_RESOURCE_TYPE_CONSTANT_BUFFER: - return PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS | PIPELINE_RESOURCE_FLAG_RUNTIME_ARRAY; + return PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS | PIPELINE_RESOURCE_FLAG_RUNTIME_ARRAY | PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS; case SHADER_RESOURCE_TYPE_TEXTURE_SRV: return PIPELINE_RESOURCE_FLAG_COMBINED_SAMPLER | PIPELINE_RESOURCE_FLAG_RUNTIME_ARRAY; @@ -1829,9 +1832,6 @@ PIPELINE_RESOURCE_FLAGS GetValidPipelineResourceFlags(SHADER_RESOURCE_TYPE Resou case SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT: return PIPELINE_RESOURCE_FLAG_GENERAL_INPUT_ATTACHMENT; - case SHADER_RESOURCE_TYPE_INLINE_CONSTANTS: - return PIPELINE_RESOURCE_FLAG_NONE; - case SHADER_RESOURCE_TYPE_ACCEL_STRUCT: return PIPELINE_RESOURCE_FLAG_RUNTIME_ARRAY; diff --git a/Graphics/GraphicsEngine/include/ShaderResourceVariableBase.hpp b/Graphics/GraphicsEngine/include/ShaderResourceVariableBase.hpp index 9d895d217b..fcb6055153 100644 --- a/Graphics/GraphicsEngine/include/ShaderResourceVariableBase.hpp +++ b/Graphics/GraphicsEngine/include/ShaderResourceVariableBase.hpp @@ -165,12 +165,6 @@ bool VerifyResourceBinding(const char* ExpectedResourceTypeName, const IDeviceObject* pCachedObject, // Object already set in the cache const char* SignatureName) { - if (ResDesc.ResourceType == SHADER_RESOURCE_TYPE_INLINE_CONSTANTS) - { - RESOURCE_VALIDATION_FAILURE("Cannot bind resources to inline constants variable '", GetShaderResourcePrintName(ResDesc, BindInfo.ArrayIndex), '\''); - return false; - } - if (BindInfo.pObject != nullptr && pResourceImpl == nullptr) { std::stringstream ss; @@ -185,6 +179,23 @@ bool VerifyResourceBinding(const char* ExpectedResourceTypeName, return false; } + if (ResDesc.ResourceType == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER && (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0) + { + if (pResourceImpl != nullptr) + { + std::stringstream ss; + ss << "Failed to bind " << ExpectedResourceTypeName << " '" << pResourceImpl->GetDesc().Name << "' to variable '" + << GetShaderResourcePrintName(ResDesc, BindInfo.ArrayIndex) << '\''; + if (SignatureName != nullptr) + { + ss << " defined by signature '" << SignatureName << '\''; + } + ss << ". Inline constants must be set using IShaderResourceVariable::SetInlineConstants() method."; + RESOURCE_VALIDATION_FAILURE(ss.str()); + return false; + } + } + if (ResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC && (BindInfo.Flags & SET_SHADER_RESOURCE_FLAG_ALLOW_OVERWRITE) == 0 && pCachedObject != nullptr && pCachedObject != pResourceImpl) @@ -614,9 +625,9 @@ inline bool VerifyInlineConstants(const PipelineResourceDesc& ResDesc, Uint32 NumConstants) { bool ParamsOK = true; - if (ResDesc.ResourceType != SHADER_RESOURCE_TYPE_INLINE_CONSTANTS) + if (ResDesc.ResourceType != SHADER_RESOURCE_TYPE_CONSTANT_BUFFER) { - RESOURCE_VALIDATION_FAILURE("SetInlineConstants() is only allowed for inline constant variables."); + RESOURCE_VALIDATION_FAILURE("SetInlineConstants() is only allowed for constant buffer resources."); ParamsOK = false; } @@ -754,8 +765,14 @@ struct ShaderVariableBase : public ResourceVariableBaseInterface #ifdef DILIGENT_DEVELOPMENT { const PipelineResourceDesc& Desc = GetDesc(); - DEV_CHECK_ERR(Desc.ResourceType == SHADER_RESOURCE_TYPE_INLINE_CONSTANTS, - "SetInlineConstants() is only allowed for inline constant variables."); + DEV_CHECK_ERR(Desc.ResourceType == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, + "SetInlineConstants() is only allowed for constant buffer resources."); + DEV_CHECK_ERR(Desc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS, + "SetInlineConstants() is only allowed for inline constant buffers."); + DEV_CHECK_ERR(pConstants != nullptr, "Pointer to inline constants is null."); + DEV_CHECK_ERR(FirstConstant + NumConstants <= Desc.ArraySize, + "Inline constant range (", FirstConstant, " .. ", FirstConstant + NumConstants - 1, + ") is out of bounds for variable '", Desc.Name, "' of size ", Desc.ArraySize, " constants."); } #endif diff --git a/Graphics/GraphicsEngine/interface/PipelineResourceSignature.h b/Graphics/GraphicsEngine/interface/PipelineResourceSignature.h index 83ee6293a4..8222397d99 100644 --- a/Graphics/GraphicsEngine/interface/PipelineResourceSignature.h +++ b/Graphics/GraphicsEngine/interface/PipelineResourceSignature.h @@ -129,7 +129,23 @@ DILIGENT_TYPED_ENUM(PIPELINE_RESOURCE_FLAGS, Uint8) /// \note This flag is only valid in Vulkan. PIPELINE_RESOURCE_FLAG_GENERAL_INPUT_ATTACHMENT = 1u << 4, - PIPELINE_RESOURCE_FLAG_LAST = PIPELINE_RESOURCE_FLAG_GENERAL_INPUT_ATTACHMENT + /// Indicates that the resource consists of inline constants (also known as push constants in Vulkan or root constants in Direct3D12). + + /// Applies to SHADER_RESOURCE_TYPE_CONSTANT_BUFFER only. + /// + /// Inline constants are set directly using IShaderResourceVariable::SetInlineConstants. + /// + /// This flag cannot be combined with any other flags. + /// + /// In Vulkan and Direct3D12, inline constants are not bound via descriptor sets or root + /// signatures, but are set directly in command buffers or command lists. As such, they + /// are very cheap to set and are intended for small pieces of frequently changing data. + /// + /// In legacy APIs (Direct3D11 and OpenGL), inline constants are emulated using regular + /// constant buffers and thus have higher overhead. + PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS = 1u << 5, + + PIPELINE_RESOURCE_FLAG_LAST = PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS }; DEFINE_FLAG_ENUM_OPERATORS(PIPELINE_RESOURCE_FLAGS); diff --git a/Graphics/GraphicsEngine/interface/Shader.h b/Graphics/GraphicsEngine/interface/Shader.h index b0236d7c6f..7909272f6f 100644 --- a/Graphics/GraphicsEngine/interface/Shader.h +++ b/Graphics/GraphicsEngine/interface/Shader.h @@ -723,16 +723,6 @@ DILIGENT_TYPED_ENUM(SHADER_RESOURCE_TYPE, Uint8) /// Input attachment in a render pass SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT, - /// Inline constants (aka push constants in Vulkan or root constants in Direct3D12) - - /// In Vulkan/Direct3D12, inline constants are not bound via descriptor sets or root - /// signatures but are set directly in command buffers / command lists. As such, they - /// are very cheap to set and are intended for small pieces of frequently changing data. - /// - /// In legacy APIs (Direct3D11/OpenGL), inline constants are emulated using regular - /// constant buffers. - SHADER_RESOURCE_TYPE_INLINE_CONSTANTS, - /// Acceleration structure SHADER_RESOURCE_TYPE_ACCEL_STRUCT, diff --git a/Graphics/GraphicsEngine/interface/ShaderResourceVariable.h b/Graphics/GraphicsEngine/interface/ShaderResourceVariable.h index ba87a85404..346b1e806f 100644 --- a/Graphics/GraphicsEngine/interface/ShaderResourceVariable.h +++ b/Graphics/GraphicsEngine/interface/ShaderResourceVariable.h @@ -289,14 +289,15 @@ DILIGENT_END_INTERFACE // clang-format off -# define IShaderResourceVariable_Set(This, ...) CALL_IFACE_METHOD(ShaderResourceVariable, Set, This, __VA_ARGS__) -# define IShaderResourceVariable_SetArray(This, ...) CALL_IFACE_METHOD(ShaderResourceVariable, SetArray, This, __VA_ARGS__) -# define IShaderResourceVariable_SetBufferRange(This, ...) CALL_IFACE_METHOD(ShaderResourceVariable, SetBufferRange, This, __VA_ARGS__) -# define IShaderResourceVariable_SetBufferOffset(This, ...) CALL_IFACE_METHOD(ShaderResourceVariable, SetBufferOffset, This, __VA_ARGS__) -# define IShaderResourceVariable_GetType(This) CALL_IFACE_METHOD(ShaderResourceVariable, GetType, This) -# define IShaderResourceVariable_GetResourceDesc(This, ...) CALL_IFACE_METHOD(ShaderResourceVariable, GetResourceDesc, This, __VA_ARGS__) -# define IShaderResourceVariable_GetIndex(This) CALL_IFACE_METHOD(ShaderResourceVariable, GetIndex, This) -# define IShaderResourceVariable_Get(This, ...) CALL_IFACE_METHOD(ShaderResourceVariable, Get, This, __VA_ARGS__) +# define IShaderResourceVariable_Set(This, ...) CALL_IFACE_METHOD(ShaderResourceVariable, Set, This, __VA_ARGS__) +# define IShaderResourceVariable_SetArray(This, ...) CALL_IFACE_METHOD(ShaderResourceVariable, SetArray, This, __VA_ARGS__) +# define IShaderResourceVariable_SetBufferRange(This, ...) CALL_IFACE_METHOD(ShaderResourceVariable, SetBufferRange, This, __VA_ARGS__) +# define IShaderResourceVariable_SetBufferOffset(This, ...) CALL_IFACE_METHOD(ShaderResourceVariable, SetBufferOffset, This, __VA_ARGS__) +# define IShaderResourceVariable_SetInlineConstants(This,...)CALL_IFACE_METHOD(ShaderResourceVariable, SetInlineConstants, This, __VA_ARGS__) +# define IShaderResourceVariable_GetType(This) CALL_IFACE_METHOD(ShaderResourceVariable, GetType, This) +# define IShaderResourceVariable_GetResourceDesc(This, ...) CALL_IFACE_METHOD(ShaderResourceVariable, GetResourceDesc, This, __VA_ARGS__) +# define IShaderResourceVariable_GetIndex(This) CALL_IFACE_METHOD(ShaderResourceVariable, GetIndex, This) +# define IShaderResourceVariable_Get(This, ...) CALL_IFACE_METHOD(ShaderResourceVariable, Get, This, __VA_ARGS__) // clang-format on diff --git a/Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp b/Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp index cc5ecfd543..dec112807f 100644 --- a/Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp +++ b/Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp @@ -111,6 +111,12 @@ void ValidatePipelineResourceSignatureDesc(const PipelineResourceSignatureDesc& ": ", GetPipelineResourceFlagsString(AllowedResourceFlags, false, ", "), "."); } + if ((Res.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0 && Res.Flags != PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) + { + LOG_PRS_ERROR_AND_THROW("Incorrect Desc.Resources[", i, "].Flags (", GetPipelineResourceFlagsString(Res.Flags), + "). INLINE_CONSTANTS flag cannot be combined with other flags."); + } + if ((Res.Flags & PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER) != 0 && Features.FormattedBuffers == DEVICE_FEATURE_STATE_DISABLED) { LOG_PRS_ERROR_AND_THROW("Incorrect Desc.Resources[", i, "].Flags (FORMATTED_BUFFER). The flag can only be used if FormattedBuffers device feature is enabled."); diff --git a/Graphics/GraphicsEngineD3D11/src/PipelineResourceSignatureD3D11Impl.cpp b/Graphics/GraphicsEngineD3D11/src/PipelineResourceSignatureD3D11Impl.cpp index 17f29de84b..1736bc7ceb 100644 --- a/Graphics/GraphicsEngineD3D11/src/PipelineResourceSignatureD3D11Impl.cpp +++ b/Graphics/GraphicsEngineD3D11/src/PipelineResourceSignatureD3D11Impl.cpp @@ -90,7 +90,7 @@ PipelineResourceSignatureD3D11Impl::PipelineResourceSignatureD3D11Impl(IReferenc D3D11_RESOURCE_RANGE PipelineResourceSignatureD3D11Impl::ShaderResourceTypeToRange(SHADER_RESOURCE_TYPE Type) { - static_assert(SHADER_RESOURCE_TYPE_LAST == 9, "Please update the switch below to handle the new shader resource type"); + static_assert(SHADER_RESOURCE_TYPE_LAST == 8, "Please update the switch below to handle the new shader resource type"); switch (Type) { // clang-format off @@ -100,7 +100,6 @@ D3D11_RESOURCE_RANGE PipelineResourceSignatureD3D11Impl::ShaderResourceTypeToRan case SHADER_RESOURCE_TYPE_TEXTURE_UAV: return D3D11_RESOURCE_RANGE_UAV; case SHADER_RESOURCE_TYPE_BUFFER_UAV: return D3D11_RESOURCE_RANGE_UAV; case SHADER_RESOURCE_TYPE_SAMPLER: return D3D11_RESOURCE_RANGE_SAMPLER; - case SHADER_RESOURCE_TYPE_INLINE_CONSTANTS: return D3D11_RESOURCE_RANGE_CBV; case SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT: return D3D11_RESOURCE_RANGE_SRV; // clang-format on default: diff --git a/Graphics/GraphicsEngineD3D11/src/ShaderVariableManagerD3D11.cpp b/Graphics/GraphicsEngineD3D11/src/ShaderVariableManagerD3D11.cpp index 924bda26f0..7f44560531 100644 --- a/Graphics/GraphicsEngineD3D11/src/ShaderVariableManagerD3D11.cpp +++ b/Graphics/GraphicsEngineD3D11/src/ShaderVariableManagerD3D11.cpp @@ -121,7 +121,7 @@ D3DShaderResourceCounters ShaderVariableManagerD3D11::CountResources( [&](Uint32 Index) // { const PipelineResourceDesc& ResDesc = Signature.GetResourceDesc(Index); - static_assert(SHADER_RESOURCE_TYPE_LAST == 9, "Please update the switch below to handle the new shader resource range"); + static_assert(SHADER_RESOURCE_TYPE_LAST == 8, "Please update the switch below to handle the new shader resource range"); switch (ResDesc.ResourceType) { // clang-format off @@ -132,7 +132,6 @@ D3DShaderResourceCounters ShaderVariableManagerD3D11::CountResources( case SHADER_RESOURCE_TYPE_BUFFER_UAV: ++Counters.NumBufUAVs; break; case SHADER_RESOURCE_TYPE_SAMPLER: ++Counters.NumSamplers; break; case SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT: ++Counters.NumTexSRVs; break; - case SHADER_RESOURCE_TYPE_INLINE_CONSTANTS: ++Counters.NumCBs; break; // clang-format on default: UNEXPECTED("Unsupported resource type."); @@ -216,7 +215,7 @@ void ShaderVariableManagerD3D11::Initialize(const PipelineResourceSignatureD3D11 [&](Uint32 Index) // { const PipelineResourceDesc& ResDesc = Signature.GetResourceDesc(Index); - static_assert(SHADER_RESOURCE_TYPE_LAST == 9, "Please update the switch below to handle the new shader resource range"); + static_assert(SHADER_RESOURCE_TYPE_LAST == 8, "Please update the switch below to handle the new shader resource range"); switch (ResDesc.ResourceType) { case SHADER_RESOURCE_TYPE_CONSTANT_BUFFER: @@ -250,11 +249,6 @@ void ShaderVariableManagerD3D11::Initialize(const PipelineResourceSignatureD3D11 new (&GetResource(sam++)) SamplerBindInfo{*this, Index}; break; - case SHADER_RESOURCE_TYPE_INLINE_CONSTANTS: - // Initialize CB for inline constants in place, increment CB counter - new (&GetResource(cb++)) ConstBuffBindInfo{*this, Index}; - break; - default: UNEXPECTED("Unsupported resource type."); } diff --git a/Graphics/GraphicsEngineD3D12/src/D3D12TypeConversions.cpp b/Graphics/GraphicsEngineD3D12/src/D3D12TypeConversions.cpp index 7f2fac2e07..fd5da92f35 100644 --- a/Graphics/GraphicsEngineD3D12/src/D3D12TypeConversions.cpp +++ b/Graphics/GraphicsEngineD3D12/src/D3D12TypeConversions.cpp @@ -849,7 +849,7 @@ DXGI_FORMAT TypeToRayTracingVertexFormat(VALUE_TYPE ValueType, Uint32 ComponentC D3D12_DESCRIPTOR_RANGE_TYPE ResourceTypeToD3D12DescriptorRangeType(SHADER_RESOURCE_TYPE ResType) { - static_assert(SHADER_RESOURCE_TYPE_LAST == 9, "Please update the switch below to handle the new resource type"); + static_assert(SHADER_RESOURCE_TYPE_LAST == 8, "Please update the switch below to handle the new resource type"); switch (ResType) { @@ -864,10 +864,6 @@ D3D12_DESCRIPTOR_RANGE_TYPE ResourceTypeToD3D12DescriptorRangeType(SHADER_RESOUR case SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT: return D3D12_DESCRIPTOR_RANGE_TYPE_SRV; // clang-format on - case SHADER_RESOURCE_TYPE_INLINE_CONSTANTS: - UNEXPECTED("Inline constants do not have a descriptor range type"); - return static_cast(-1); - default: UNEXPECTED("Unknown resource type"); return static_cast(-1); diff --git a/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp index e48dfa5c30..8dc74e97fe 100644 --- a/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp @@ -539,7 +539,7 @@ void PipelineResourceSignatureD3D12Impl::CommitRootConstants(const CommitCacheRe VERIFY_EXPR(CacheTbl.GetSize() == 1); const ShaderResourceCacheD3D12::Resource& Res = CacheTbl.GetResource(0); - VERIFY_EXPR(Res.Type == SHADER_RESOURCE_TYPE_INLINE_CONSTANTS); + VERIFY_EXPR(Res.Type == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER); VERIFY(Res.IsNull(), "There should be no resource bound for root constants as they contain raw data."); const Uint32* pConstants = reinterpret_cast(Res.CPUDescriptorHandle.ptr); @@ -810,7 +810,7 @@ bool PipelineResourceSignatureD3D12Impl::DvpValidateCommittedResource(const Devi } } - static_assert(SHADER_RESOURCE_TYPE_LAST == 9, "Please update the switch below to handle the new shader resource type"); + static_assert(SHADER_RESOURCE_TYPE_LAST == 8, "Please update the switch below to handle the new shader resource type"); switch (ResDesc.ResourceType) { case SHADER_RESOURCE_TYPE_TEXTURE_SRV: diff --git a/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp b/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp index 68da48e28e..dbad0da638 100644 --- a/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp +++ b/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp @@ -393,7 +393,7 @@ void ShaderResourceCacheD3D12::SetInlineConstants(Uint32 RootIndex, { RootTable& Tbl = GetRootTable(RootIndex); Resource& Res = Tbl.GetResource(0); - VERIFY_EXPR(Res.Type == SHADER_RESOURCE_TYPE_INLINE_CONSTANTS); + VERIFY_EXPR(Res.Type == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER); VERIFY(Res.IsNull(), "There should be no resource bound for root constants as they contain raw data."); VERIFY(Res.CPUDescriptorHandle.ptr != 0, "Resources used to store root constants must have valid pointer to the data."); VERIFY(FirstConstant + NumConstants <= Res.BufferRangeSize, @@ -501,7 +501,7 @@ void ShaderResourceCacheD3D12::DbgValidateDynamicBuffersMask() const void ShaderResourceCacheD3D12::Resource::TransitionResource(CommandContext& Ctx) { - static_assert(SHADER_RESOURCE_TYPE_LAST == 9, "Please update this function to handle the new resource type"); + static_assert(SHADER_RESOURCE_TYPE_LAST == 8, "Please update this function to handle the new resource type"); switch (Type) { case SHADER_RESOURCE_TYPE_CONSTANT_BUFFER: @@ -559,7 +559,6 @@ void ShaderResourceCacheD3D12::Resource::TransitionResource(CommandContext& Ctx) break; case SHADER_RESOURCE_TYPE_SAMPLER: - case SHADER_RESOURCE_TYPE_INLINE_CONSTANTS: // Nothing to transition break; @@ -586,7 +585,7 @@ void ShaderResourceCacheD3D12::Resource::TransitionResource(CommandContext& Ctx) #ifdef DILIGENT_DEVELOPMENT void ShaderResourceCacheD3D12::Resource::DvpVerifyResourceState() { - static_assert(SHADER_RESOURCE_TYPE_LAST == 9, "Please update this function to handle the new resource type"); + static_assert(SHADER_RESOURCE_TYPE_LAST == 8, "Please update this function to handle the new resource type"); switch (Type) { case SHADER_RESOURCE_TYPE_CONSTANT_BUFFER: @@ -674,7 +673,6 @@ void ShaderResourceCacheD3D12::Resource::DvpVerifyResourceState() break; case SHADER_RESOURCE_TYPE_SAMPLER: - case SHADER_RESOURCE_TYPE_INLINE_CONSTANTS: // No resource break; diff --git a/Graphics/GraphicsEngineD3D12/src/ShaderVariableManagerD3D12.cpp b/Graphics/GraphicsEngineD3D12/src/ShaderVariableManagerD3D12.cpp index d7b8289739..398f62badb 100644 --- a/Graphics/GraphicsEngineD3D12/src/ShaderVariableManagerD3D12.cpp +++ b/Graphics/GraphicsEngineD3D12/src/ShaderVariableManagerD3D12.cpp @@ -570,7 +570,7 @@ void BindResourceHelper::operator()(const BindResourceInfo& BindInfo) const VERIFY_EXPR(m_ArrayIndex == BindInfo.ArrayIndex); if (BindInfo.pObject != nullptr) { - static_assert(SHADER_RESOURCE_TYPE_LAST == 9, "Please update this function to handle the new resource type"); + static_assert(SHADER_RESOURCE_TYPE_LAST == 8, "Please update this function to handle the new resource type"); switch (m_ResDesc.ResourceType) { case SHADER_RESOURCE_TYPE_CONSTANT_BUFFER: @@ -598,10 +598,6 @@ void BindResourceHelper::operator()(const BindResourceInfo& BindInfo) const CacheSampler(BindInfo); break; - case SHADER_RESOURCE_TYPE_INLINE_CONSTANTS: - UNSUPPORTED("Not yet implemented"); - break; - case SHADER_RESOURCE_TYPE_ACCEL_STRUCT: CacheAccelStruct(BindInfo); break; diff --git a/Graphics/GraphicsEngineOpenGL/src/PipelineResourceSignatureGLImpl.cpp b/Graphics/GraphicsEngineOpenGL/src/PipelineResourceSignatureGLImpl.cpp index 4152dbaadb..b100a2b8bb 100644 --- a/Graphics/GraphicsEngineOpenGL/src/PipelineResourceSignatureGLImpl.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/PipelineResourceSignatureGLImpl.cpp @@ -55,7 +55,7 @@ const char* GetBindingRangeName(BINDING_RANGE Range) BINDING_RANGE PipelineResourceToBindingRange(const PipelineResourceDesc& Desc) { - static_assert(SHADER_RESOURCE_TYPE_LAST == 9, "Please update the switch below to handle the new shader resource type"); + static_assert(SHADER_RESOURCE_TYPE_LAST == 8, "Please update the switch below to handle the new shader resource type"); switch (Desc.ResourceType) { // clang-format off @@ -65,7 +65,6 @@ BINDING_RANGE PipelineResourceToBindingRange(const PipelineResourceDesc& Desc) case SHADER_RESOURCE_TYPE_TEXTURE_UAV: return BINDING_RANGE_IMAGE; case SHADER_RESOURCE_TYPE_BUFFER_UAV: return (Desc.Flags & PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER) ? BINDING_RANGE_IMAGE : BINDING_RANGE_STORAGE_BUFFER; case SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT: return BINDING_RANGE_TEXTURE; - case SHADER_RESOURCE_TYPE_INLINE_CONSTANTS: return BINDING_RANGE_UNIFORM_BUFFER; // clang-format on case SHADER_RESOURCE_TYPE_SAMPLER: case SHADER_RESOURCE_TYPE_ACCEL_STRUCT: diff --git a/Graphics/ShaderTools/src/DXBCUtils.cpp b/Graphics/ShaderTools/src/DXBCUtils.cpp index 3cac44c482..ee946889e3 100644 --- a/Graphics/ShaderTools/src/DXBCUtils.cpp +++ b/Graphics/ShaderTools/src/DXBCUtils.cpp @@ -939,7 +939,7 @@ void RemapShaderResources(const DXBCUtils::TResourceBindingMap& ResourceMap, con Iter->second.ArraySize >= Res.BindCount); #ifdef DILIGENT_DEBUG - static_assert(SHADER_RESOURCE_TYPE_LAST == 9, "Please update the switch below to handle the new shader resource type"); + static_assert(SHADER_RESOURCE_TYPE_LAST == 8, "Please update the switch below to handle the new shader resource type"); switch (Iter->second.ResType) { // clang-format off @@ -950,7 +950,6 @@ void RemapShaderResources(const DXBCUtils::TResourceBindingMap& ResourceMap, con case SHADER_RESOURCE_TYPE_BUFFER_UAV: VERIFY_EXPR(ResType == RES_TYPE_UAV); break; case SHADER_RESOURCE_TYPE_SAMPLER: VERIFY_EXPR(ResType == RES_TYPE_SAMPLER); break; case SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT: VERIFY_EXPR(ResType == RES_TYPE_SRV); break; - case SHADER_RESOURCE_TYPE_INLINE_CONSTANTS: VERIFY_EXPR(ResType == RES_TYPE_CBV); break; // clang-format on default: UNEXPECTED("Unsupported shader resource type."); } diff --git a/Graphics/ShaderTools/src/DXCompiler.cpp b/Graphics/ShaderTools/src/DXCompiler.cpp index a024833b77..e8768e2641 100644 --- a/Graphics/ShaderTools/src/DXCompiler.cpp +++ b/Graphics/ShaderTools/src/DXCompiler.cpp @@ -926,7 +926,7 @@ bool DXCompilerImpl::RemapResourceBindings(const TResourceBindingMap& ResourceMa # ifdef DILIGENT_DEVELOPMENT { - static_assert(SHADER_RESOURCE_TYPE_LAST == 9, "Please update the switch below to handle the new shader resource type"); + static_assert(SHADER_RESOURCE_TYPE_LAST == 8, "Please update the switch below to handle the new shader resource type"); RES_TYPE ExpectedResType = RES_TYPE_COUNT; switch (NameAndBinding.second.ResType) { @@ -938,7 +938,6 @@ bool DXCompilerImpl::RemapResourceBindings(const TResourceBindingMap& ResourceMa case SHADER_RESOURCE_TYPE_BUFFER_UAV: ExpectedResType = RES_TYPE_UAV; break; case SHADER_RESOURCE_TYPE_SAMPLER: ExpectedResType = RES_TYPE_SAMPLER; break; case SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT: ExpectedResType = RES_TYPE_SRV; break; - case SHADER_RESOURCE_TYPE_INLINE_CONSTANTS: ExpectedResType = RES_TYPE_CBV; break; case SHADER_RESOURCE_TYPE_ACCEL_STRUCT: ExpectedResType = RES_TYPE_SRV; break; // clang-format on default: UNEXPECTED("Unsupported shader resource type."); diff --git a/Tests/DiligentCoreTest/src/GraphicsAccessories/GraphicsAccessoriesTest.cpp b/Tests/DiligentCoreTest/src/GraphicsAccessories/GraphicsAccessoriesTest.cpp index 1cf0238d7b..5b4c9f342f 100644 --- a/Tests/DiligentCoreTest/src/GraphicsAccessories/GraphicsAccessoriesTest.cpp +++ b/Tests/DiligentCoreTest/src/GraphicsAccessories/GraphicsAccessoriesTest.cpp @@ -705,7 +705,7 @@ TEST(GraphicsAccessories_GraphicsAccessories, PipelineTypeFromShaderStages) TEST(GraphicsAccessories_GraphicsAccessories, GetPipelineResourceFlagsString) { - static_assert(PIPELINE_RESOURCE_FLAG_LAST == (1u << 4), "Please add a test for the new flag here"); + static_assert(PIPELINE_RESOURCE_FLAG_LAST == (1u << 5), "Please add a test for the new flag here"); EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_NONE, true).c_str(), "PIPELINE_RESOURCE_FLAG_NONE"); EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_NONE).c_str(), "UNKNOWN"); @@ -714,11 +714,13 @@ TEST(GraphicsAccessories_GraphicsAccessories, GetPipelineResourceFlagsString) EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_COMBINED_SAMPLER, true).c_str(), "PIPELINE_RESOURCE_FLAG_COMBINED_SAMPLER"); EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER, true).c_str(), "PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER"); EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_GENERAL_INPUT_ATTACHMENT, true).c_str(), "PIPELINE_RESOURCE_FLAG_GENERAL_INPUT_ATTACHMENT"); + EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS, true).c_str(), "PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS"); EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS).c_str(), "NO_DYNAMIC_BUFFERS"); EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_COMBINED_SAMPLER).c_str(), "COMBINED_SAMPLER"); EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER).c_str(), "FORMATTED_BUFFER"); EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_GENERAL_INPUT_ATTACHMENT).c_str(), "GENERAL_INPUT_ATTACHMENT"); + EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS).c_str(), "INLINE_CONSTANTS"); EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS | PIPELINE_RESOURCE_FLAG_COMBINED_SAMPLER, true).c_str(), "PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS|PIPELINE_RESOURCE_FLAG_COMBINED_SAMPLER"); From 2d41af286c20b8cec4eddd6cc09e153928b180c0 Mon Sep 17 00:00:00 2001 From: assiduous Date: Sun, 19 Oct 2025 20:30:02 -0700 Subject: [PATCH 009/104] ResourceSignatureD3D12: initialize root constants (#672) --- .../src/PipelineResourceSignatureD3D12Impl.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp index 8dc74e97fe..a16cfe0cc6 100644 --- a/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp @@ -267,7 +267,9 @@ void PipelineResourceSignatureD3D12Impl::AllocateRootParameters(const bool IsSer // Normal resources go into space 0. Space = 0; Register = NumResources[d3d12DescriptorRangeType]; - NumResources[d3d12DescriptorRangeType] += ResDesc.ArraySize; + NumResources[d3d12DescriptorRangeType] += (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0 ? + 1 : // For inline constants, count only one resource as the ArraySize is the number of 4-byte constants. + ResDesc.ArraySize; } const PIPELINE_RESOURCE_FLAGS dbgValidResourceFlags = GetValidPipelineResourceFlags(ResDesc.ResourceType); @@ -283,7 +285,15 @@ void PipelineResourceSignatureD3D12Impl::AllocateRootParameters(const bool IsSer { case SHADER_RESOURCE_TYPE_CONSTANT_BUFFER: VERIFY(!IsFormattedBuffer, "Constant buffers can't be labeled as formatted. This error should've been caught by ValidatePipelineResourceSignatureDesc()."); - d3d12RootParamType = UseDynamicOffset && !IsArray ? D3D12_ROOT_PARAMETER_TYPE_CBV : D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + if ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0) + { + VERIFY(UseDynamicOffset, "NO_DYNAMIC_BUFFERS flag is not compatible with INLINE_CONSTANTS. This error should've been caught by ValidatePipelineResourceSignatureDesc()."); + d3d12RootParamType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; + } + else + { + d3d12RootParamType = UseDynamicOffset && !IsArray ? D3D12_ROOT_PARAMETER_TYPE_CBV : D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + } break; case SHADER_RESOURCE_TYPE_BUFFER_SRV: From 69d2f734b28ad67a3a17874b2ebb196963d81e89 Mon Sep 17 00:00:00 2001 From: assiduous Date: Tue, 21 Oct 2025 19:32:50 -0700 Subject: [PATCH 010/104] ShaderResourceCacheD3D12: allocate space for inline constant values (#672) --- .../include/ShaderResourceCacheD3D12.hpp | 61 ++++++++++--- .../PipelineResourceSignatureD3D12Impl.cpp | 23 ++++- .../src/ShaderResourceCacheD3D12.cpp | 90 +++++++++++++++---- 3 files changed, 145 insertions(+), 29 deletions(-) diff --git a/Graphics/GraphicsEngineD3D12/include/ShaderResourceCacheD3D12.hpp b/Graphics/GraphicsEngineD3D12/include/ShaderResourceCacheD3D12.hpp index 0658c0f3a1..29d9653a76 100644 --- a/Graphics/GraphicsEngineD3D12/include/ShaderResourceCacheD3D12.hpp +++ b/Graphics/GraphicsEngineD3D12/include/ShaderResourceCacheD3D12.hpp @@ -38,7 +38,7 @@ // m_pMemory | m_pResources, m_NumResources == m | // | | | // V | V -// | RootTable[0] | .... | RootTable[Nrt-1] | Res[0] | ... | Res[n-1] | .... | Res[0] | ... | Res[m-1] | DescriptorHeapAllocation[0] | ... +// | RootTable[0] | .... | RootTable[Nrt-1] | Res[0] | ... | Res[n-1] | .... | Res[0] | ... | Res[m-1] | Descriptor Heap Allocations | Inline constant values | // | A \ // | | \ // |________________________________________________| \RefCntAutoPtr @@ -70,6 +70,7 @@ // DESCRIPTOR_HEAP_TYPE_SAMPLER // // +// Root constant parameters use CPUDescriptorHandle.ptr to store pointer to the raw data. // // The allocation is inexed by the offset from the beginning of the root table. // Each root table is assigned the space to store exactly m_NumResources resources. @@ -138,18 +139,29 @@ class ShaderResourceCacheD3D12 : public ShaderResourceCacheBase struct MemoryRequirements { - Uint32 NumTables = 0; - Uint32 TotalResources = 0; - Uint32 NumDescriptorAllocations = 0; - size_t TotalSize = 0; + Uint32 NumTables = 0; + Uint32 TotalResources = 0; + Uint32 NumDescriptorAllocations = 0; + Uint32 TotalInlineConstantValues = 0; + size_t TotalSize = 0; }; static MemoryRequirements GetMemoryRequirements(const RootParamsManager& RootParams); + + struct InlineConstantParamInfo + { + Uint32 RootIndex = 0; + Uint32 OffsetFromTableStart = 0; + Uint32 NumValues = 0; + }; + using InlineConstantInfoVectorType = std::vector; + // Initializes resource cache to hold the given number of root tables, no descriptor space // is allocated (this is used to initialize the cache for a pipeline resource signature). - void Initialize(IMemoryAllocator& MemAllocator, - Uint32 NumTables, - const Uint32 TableSizes[]); + void Initialize(IMemoryAllocator& MemAllocator, + Uint32 NumTables, + const Uint32 TableSizes[], + const InlineConstantInfoVectorType& InlineConstants); // Initializes resource cache to hold the resources of a root parameters manager // (this is used to initialize the cache for an SRB). @@ -192,6 +204,7 @@ class ShaderResourceCacheD3D12 : public ShaderResourceCacheBase // CPU descriptor handle of a cached resource in CPU-only descriptor heap. // This handle may be null for CBVs that address the buffer range. + // For root constant parameters, ptr stores pointer to the raw data. D3D12_CPU_DESCRIPTOR_HANDLE CPUDescriptorHandle = {}; RefCntAutoPtr pObject; @@ -289,7 +302,7 @@ class ShaderResourceCacheD3D12 : public ShaderResourceCacheBase const RootTable& GetRootTable(Uint32 RootIndex) const { VERIFY_EXPR(RootIndex < m_NumTables); - return reinterpret_cast(m_pMemory.get())[RootIndex]; + return GetRootTableStorage()[RootIndex]; } Uint32 GetNumRootTables() const { return m_NumTables; } @@ -362,7 +375,7 @@ class ShaderResourceCacheD3D12 : public ShaderResourceCacheBase RootTable& GetRootTable(Uint32 RootIndex) { VERIFY_EXPR(RootIndex < m_NumTables); - return reinterpret_cast(m_pMemory.get())[RootIndex]; + return GetRootTableStorage()[RootIndex]; } Resource& GetResource(Uint32 Idx) @@ -371,7 +384,33 @@ class ShaderResourceCacheD3D12 : public ShaderResourceCacheBase return reinterpret_cast(reinterpret_cast(m_pMemory.get()) + m_NumTables)[Idx]; } - size_t AllocateMemory(IMemoryAllocator& MemAllocator); + size_t AllocateMemory(IMemoryAllocator& MemAllocator, + Uint32 TotalInlineConstantValues); + + // Memory layout: + // + // | Root tables | Resources | DescriptorHeapAllocations | Inline constant values | + // + RootTable* GetRootTableStorage() + { + return reinterpret_cast(m_pMemory.get()); + } + const RootTable* GetRootTableStorage() const + { + return reinterpret_cast(m_pMemory.get()); + } + Resource* GetResourceStorage() + { + return reinterpret_cast(GetRootTableStorage() + m_NumTables); + } + DescriptorHeapAllocation* GetDescriptorAllocationStorage() + { + return reinterpret_cast(GetResourceStorage() + m_TotalResourceCount); + } + Uint32* GetInlineConstantStorage() + { + return reinterpret_cast(GetDescriptorAllocationStorage() + m_NumDescriptorAllocations); + } private: static constexpr Uint32 MaxRootTables = 64; diff --git a/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp index a16cfe0cc6..d223fe6a94 100644 --- a/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp @@ -193,6 +193,8 @@ void PipelineResourceSignatureD3D12Impl::AllocateRootParameters(const bool IsSer // Cache table sizes for static resources std::array StaticResCacheTblSizes{}; + ShaderResourceCacheD3D12::InlineConstantInfoVectorType StaticResInlineConstInfo; + // Allocate registers for immutable samplers first for (Uint32 i = 0; i < m_Desc.NumImmutableSamplers; ++i) { @@ -253,7 +255,22 @@ void PipelineResourceSignatureD3D12Impl::AllocateRootParameters(const bool IsSer // Samplers at root index D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER (3) SigRootIndex = d3d12DescriptorRangeType; SigOffsetFromTableStart = StaticResCacheTblSizes[SigRootIndex]; - StaticResCacheTblSizes[SigRootIndex] += ResDesc.ArraySize; + + if ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0) + { + ShaderResourceCacheD3D12::InlineConstantParamInfo InlineConstInfo; + InlineConstInfo.RootIndex = SigRootIndex; + InlineConstInfo.OffsetFromTableStart = SigOffsetFromTableStart; + InlineConstInfo.NumValues = ResDesc.ArraySize; + StaticResInlineConstInfo.push_back(InlineConstInfo); + + // For inline constants, count only one resource as the ArraySize is the number of 4-byte constants + StaticResCacheTblSizes[SigRootIndex] += 1; + } + else + { + StaticResCacheTblSizes[SigRootIndex] += ResDesc.ArraySize; + } } if (IsRTSizedArray) @@ -337,7 +354,7 @@ void PipelineResourceSignatureD3D12Impl::AllocateRootParameters(const bool IsSer SigRootIndex, SigOffsetFromTableStart, SrcImmutableSamplerInd != InvalidImmutableSamplerIndex, - d3d12RootParamType // + d3d12RootParamType, }; } else @@ -366,7 +383,7 @@ void PipelineResourceSignatureD3D12Impl::AllocateRootParameters(const bool IsSer if (GetNumStaticResStages() > 0) { - m_pStaticResCache->Initialize(GetRawAllocator(), static_cast(StaticResCacheTblSizes.size()), StaticResCacheTblSizes.data()); + m_pStaticResCache->Initialize(GetRawAllocator(), static_cast(StaticResCacheTblSizes.size()), StaticResCacheTblSizes.data(), StaticResInlineConstInfo); } else { diff --git a/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp b/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp index dbad0da638..0d2c388795 100644 --- a/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp +++ b/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp @@ -45,6 +45,7 @@ ShaderResourceCacheD3D12::MemoryRequirements ShaderResourceCacheD3D12::GetMemory { const Uint32 NumRootTables = RootParams.GetNumRootTables(); const Uint32 NumRootViews = RootParams.GetNumRootViews(); + const Uint32 NumRootConsts = RootParams.GetNumRootConstants(); MemoryRequirements MemReqs; @@ -61,17 +62,26 @@ ShaderResourceCacheD3D12::MemoryRequirements ShaderResourceCacheD3D12::GetMemory } } } - // Root views' resources are stored in one-descriptor tables + // Root views and root constants resources are stored in one-descriptor tables MemReqs.TotalResources += NumRootViews; + MemReqs.TotalResources += NumRootConsts; static_assert(sizeof(RootTable) % sizeof(void*) == 0, "sizeof(RootTable) is not aligned by the sizeof(void*)"); static_assert(sizeof(Resource) % sizeof(void*) == 0, "sizeof(Resource) is not aligned by the sizeof(void*)"); - MemReqs.NumTables = NumRootTables + NumRootViews; + MemReqs.NumTables = NumRootTables + NumRootViews + NumRootConsts; + + MemReqs.TotalInlineConstantValues = 0; + for (Uint32 i = 0; i < NumRootConsts; ++i) + { + const RootParameter& RootConsts = RootParams.GetRootConstants(i); + MemReqs.TotalInlineConstantValues += RootConsts.d3d12RootParam.Constants.Num32BitValues; + } MemReqs.TotalSize = (MemReqs.NumTables * sizeof(RootTable) + MemReqs.TotalResources * sizeof(Resource) + - MemReqs.NumDescriptorAllocations * sizeof(DescriptorHeapAllocation)); + MemReqs.NumDescriptorAllocations * sizeof(DescriptorHeapAllocation) + + MemReqs.TotalInlineConstantValues * sizeof(Uint32)); return MemReqs; } @@ -82,21 +92,23 @@ ShaderResourceCacheD3D12::MemoryRequirements ShaderResourceCacheD3D12::GetMemory // m_pMemory | m_pResources, m_NumResources | // | | | // V | V -// | RootTable[0] | .... | RootTable[Nrt-1] | Res[0] | ... | Res[n-1] | .... | Res[0] | ... | Res[m-1] | DescriptorHeapAllocation[0] | ... +// | RootTable[0] | .... | RootTable[Nrt-1] | Res[0] | ... | Res[n-1] | .... | Res[0] | ... | Res[m-1] | Descriptor Heap Allocations | Inline cnstant values | // | A // | | // |________________________________________________| // m_pResources, m_NumResources // -size_t ShaderResourceCacheD3D12::AllocateMemory(IMemoryAllocator& MemAllocator) +size_t ShaderResourceCacheD3D12::AllocateMemory(IMemoryAllocator& MemAllocator, + Uint32 TotalInlineConstantValues) { VERIFY(!m_pMemory, "Memory has already been allocated"); const size_t MemorySize = (m_NumTables * sizeof(RootTable) + m_TotalResourceCount * sizeof(Resource) + - m_NumDescriptorAllocations * sizeof(DescriptorHeapAllocation)); + m_NumDescriptorAllocations * sizeof(DescriptorHeapAllocation) + + TotalInlineConstantValues * sizeof(Uint32)); if (MemorySize > 0) { @@ -105,12 +117,13 @@ size_t ShaderResourceCacheD3D12::AllocateMemory(IMemoryAllocator& MemAllocator) STDDeleter(MemAllocator) // }; - RootTable* const pTables = reinterpret_cast(m_pMemory.get()); - Resource* const pResources = reinterpret_cast(pTables + m_NumTables); - m_DescriptorAllocations = m_NumDescriptorAllocations > 0 ? reinterpret_cast(pResources + m_TotalResourceCount) : nullptr; + Resource* const pResources = GetResourceStorage(); + m_DescriptorAllocations = m_NumDescriptorAllocations > 0 ? GetDescriptorAllocationStorage() : nullptr; for (Uint32 res = 0; res < m_TotalResourceCount; ++res) + { new (pResources + res) Resource{}; + } for (Uint32 i = 0; i < m_NumDescriptorAllocations; ++i) new (m_DescriptorAllocations + i) DescriptorHeapAllocation{}; @@ -119,9 +132,10 @@ size_t ShaderResourceCacheD3D12::AllocateMemory(IMemoryAllocator& MemAllocator) return MemorySize; } -void ShaderResourceCacheD3D12::Initialize(IMemoryAllocator& MemAllocator, - Uint32 NumTables, - const Uint32 TableSizes[]) +void ShaderResourceCacheD3D12::Initialize(IMemoryAllocator& MemAllocator, + Uint32 NumTables, + const Uint32 TableSizes[], + const InlineConstantInfoVectorType& InlineConstants) { VERIFY(GetContentType() == ResourceCacheContentType::Signature, "This method should be called to initialize the cache to store resources of a pipeline resource signature"); @@ -137,7 +151,13 @@ void ShaderResourceCacheD3D12::Initialize(IMemoryAllocator& MemAllocator, m_DescriptorAllocations = 0; - AllocateMemory(MemAllocator); + Uint32 TotalInlineConstantValues = 0; + for (const InlineConstantParamInfo& InlineConstInfo : InlineConstants) + { + TotalInlineConstantValues += InlineConstInfo.NumValues; + } + + AllocateMemory(MemAllocator, TotalInlineConstantValues); Uint32 ResIdx = 0; for (Uint32 t = 0; t < m_NumTables; ++t) @@ -146,6 +166,15 @@ void ShaderResourceCacheD3D12::Initialize(IMemoryAllocator& MemAllocator, ResIdx += TableSizes[t]; } VERIFY_EXPR(ResIdx == m_TotalResourceCount); + + Uint32* pCurrInlineConstValueStorage = GetInlineConstantStorage(); + for (const InlineConstantParamInfo& InlineConstInfo : InlineConstants) + { + Resource& Res = GetRootTable(InlineConstInfo.RootIndex).GetResource(InlineConstInfo.OffsetFromTableStart); + Res.CPUDescriptorHandle.ptr = reinterpret_cast(pCurrInlineConstValueStorage); + pCurrInlineConstValueStorage += InlineConstInfo.NumValues; + } + VERIFY_EXPR(pCurrInlineConstValueStorage == GetInlineConstantStorage() + TotalInlineConstantValues); } @@ -168,7 +197,7 @@ void ShaderResourceCacheD3D12::Initialize(IMemoryAllocator& MemAllocator, m_NumDescriptorAllocations = static_cast(MemReq.NumDescriptorAllocations); VERIFY_EXPR(m_NumDescriptorAllocations == MemReq.NumDescriptorAllocations); - const size_t MemSize = AllocateMemory(MemAllocator); + const size_t MemSize = AllocateMemory(MemAllocator, MemReq.TotalInlineConstantValues); VERIFY_EXPR(MemSize == MemReq.TotalSize); #ifdef DILIGENT_DEBUG @@ -177,7 +206,7 @@ void ShaderResourceCacheD3D12::Initialize(IMemoryAllocator& MemAllocator, Uint32 ResIdx = 0; - // Initialize root tables + // Initialize root tables. for (Uint32 i = 0; i < RootParams.GetNumRootTables(); ++i) { const RootParameter& RootTbl = RootParams.GetRootTable(i); @@ -220,6 +249,37 @@ void ShaderResourceCacheD3D12::Initialize(IMemoryAllocator& MemAllocator, RootTableInitFlags[RootView.RootIndex] = true; #endif } + + if (Uint32 NumRootConstants = RootParams.GetNumRootConstants()) + { + Uint32* pCurrInlineConstValueStorage = GetInlineConstantStorage(); + + // Initialize one-descriptor tables for root constants + for (Uint32 i = 0; i < NumRootConstants; ++i) + { + const RootParameter& RootConsts = RootParams.GetRootConstants(i); + VERIFY(!RootTableInitFlags[RootConsts.RootIndex], "Root table at index ", RootConsts.RootIndex, " has already been initialized."); + VERIFY_EXPR(RootConsts.TableOffsetInGroupAllocation == RootParameter::InvalidTableOffsetInGroupAllocation); + VERIFY_EXPR(RootConsts.d3d12RootParam.ParameterType == D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS); + + GetResource(ResIdx).CPUDescriptorHandle.ptr = reinterpret_cast(pCurrInlineConstValueStorage); + pCurrInlineConstValueStorage += RootConsts.d3d12RootParam.Constants.Num32BitValues; + + new (&GetRootTable(RootConsts.RootIndex)) RootTable{ + 1, + &GetResource(ResIdx), + false //IsRootView + }; + ++ResIdx; + +#ifdef DILIGENT_DEBUG + RootTableInitFlags[RootConsts.RootIndex] = true; +#endif + } + + VERIFY_EXPR(pCurrInlineConstValueStorage == GetInlineConstantStorage() + MemReq.TotalInlineConstantValues); + } + VERIFY_EXPR(ResIdx == m_TotalResourceCount); #ifdef DILIGENT_DEBUG From 1c4be138f8c133a33de9e63bff0b3617489db335 Mon Sep 17 00:00:00 2001 From: assiduous Date: Tue, 21 Oct 2025 20:34:00 -0700 Subject: [PATCH 011/104] ResourceSignatureD3D12: copy inline constants from static resources (#672) --- .../PipelineResourceSignatureD3D12Impl.cpp | 54 +++++++++++++------ .../src/ShaderResourceCacheD3D12.cpp | 16 +++++- 2 files changed, 51 insertions(+), 19 deletions(-) diff --git a/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp index d223fe6a94..424b482461 100644 --- a/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp @@ -443,27 +443,47 @@ void PipelineResourceSignatureD3D12Impl::CopyStaticResources(ShaderResourceCache Uint32 SrcCacheOffset = Attr.OffsetFromTableStart(SrcCacheType); Uint32 DstCacheOffset = Attr.OffsetFromTableStart(DstCacheType); - for (Uint32 ArrInd = 0; ArrInd < ResDesc.ArraySize; ++ArrInd, ++SrcCacheOffset, ++DstCacheOffset) + if (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) { - const ShaderResourceCacheD3D12::Resource& SrcRes = SrcRootTable.GetResource(SrcCacheOffset); - if (!SrcRes.pObject) - { - if (DstCacheType == ResourceCacheContentType::SRB) - LOG_ERROR_MESSAGE("No resource is assigned to static shader variable '", GetShaderResourcePrintName(ResDesc, ArrInd), "' in pipeline resource signature '", m_Desc.Name, "'."); - continue; - } + VERIFY(ResDesc.ResourceType == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, "Only constant buffers can be marked as INLINE_CONSTANTS."); + const ShaderResourceCacheD3D12::Resource& SrcRes = SrcRootTable.GetResource(SrcCacheOffset); const ShaderResourceCacheD3D12::Resource& DstRes = DstRootTable.GetResource(DstCacheOffset); - if (DstRes.pObject != SrcRes.pObject) - { - DEV_CHECK_ERR(DstRes.pObject == nullptr, "Static resource has already been initialized, and the new resource does not match previously assigned resource."); - DstResourceCache.CopyResource(d3d12Device, DstRootIndex, DstCacheOffset, SrcRes); - } - else + VERIFY(SrcRes.CPUDescriptorHandle.ptr != 0, "Inline constant resource must have valid CPU descriptor handle."); + VERIFY(DstRes.CPUDescriptorHandle.ptr != 0, "Inline constant resource must have valid CPU descriptor handle."); + + // For inline constants, array size is the number of 4-byte constant values + const Uint32 NumConstantValues = ResDesc.ArraySize; + // Copy the actual constant values. + // For inline constants, CPUDescriptorHandle.ptr stores the pointer to the constant values buffer. + memcpy(reinterpret_cast(DstRes.CPUDescriptorHandle.ptr), + reinterpret_cast(SrcRes.CPUDescriptorHandle.ptr), + NumConstantValues * sizeof(Uint32)); + } + else + { + for (Uint32 ArrInd = 0; ArrInd < ResDesc.ArraySize; ++ArrInd, ++SrcCacheOffset, ++DstCacheOffset) { - VERIFY_EXPR(DstRes.pObject == SrcRes.pObject); - VERIFY_EXPR(DstRes.Type == SrcRes.Type); - VERIFY_EXPR(DstRes.CPUDescriptorHandle.ptr == SrcRes.CPUDescriptorHandle.ptr); + const ShaderResourceCacheD3D12::Resource& SrcRes = SrcRootTable.GetResource(SrcCacheOffset); + if (!SrcRes.pObject) + { + if (DstCacheType == ResourceCacheContentType::SRB) + LOG_ERROR_MESSAGE("No resource is assigned to static shader variable '", GetShaderResourcePrintName(ResDesc, ArrInd), "' in pipeline resource signature '", m_Desc.Name, "'."); + continue; + } + + const ShaderResourceCacheD3D12::Resource& DstRes = DstRootTable.GetResource(DstCacheOffset); + if (DstRes.pObject != SrcRes.pObject) + { + DEV_CHECK_ERR(DstRes.pObject == nullptr, "Static resource has already been initialized, and the new resource does not match previously assigned resource."); + DstResourceCache.CopyResource(d3d12Device, DstRootIndex, DstCacheOffset, SrcRes); + } + else + { + VERIFY_EXPR(DstRes.pObject == SrcRes.pObject); + VERIFY_EXPR(DstRes.Type == SrcRes.Type); + VERIFY_EXPR(DstRes.CPUDescriptorHandle.ptr == SrcRes.CPUDescriptorHandle.ptr); + } } } } diff --git a/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp b/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp index 0d2c388795..81efd0f860 100644 --- a/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp +++ b/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp @@ -172,6 +172,11 @@ void ShaderResourceCacheD3D12::Initialize(IMemoryAllocator& Me { Resource& Res = GetRootTable(InlineConstInfo.RootIndex).GetResource(InlineConstInfo.OffsetFromTableStart); Res.CPUDescriptorHandle.ptr = reinterpret_cast(pCurrInlineConstValueStorage); + // Note that normally resource type is set when a resource is bound to the cache. + // For inline constants, we set it here during tinitialization. + Res.Type = SHADER_RESOURCE_TYPE_CONSTANT_BUFFER; + // Store the number of constant values in BufferRangeSize + Res.BufferRangeSize = InlineConstInfo.NumValues; pCurrInlineConstValueStorage += InlineConstInfo.NumValues; } VERIFY_EXPR(pCurrInlineConstValueStorage == GetInlineConstantStorage() + TotalInlineConstantValues); @@ -262,12 +267,19 @@ void ShaderResourceCacheD3D12::Initialize(IMemoryAllocator& MemAllocator, VERIFY_EXPR(RootConsts.TableOffsetInGroupAllocation == RootParameter::InvalidTableOffsetInGroupAllocation); VERIFY_EXPR(RootConsts.d3d12RootParam.ParameterType == D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS); - GetResource(ResIdx).CPUDescriptorHandle.ptr = reinterpret_cast(pCurrInlineConstValueStorage); + Resource& Res = GetResource(ResIdx); + Res.CPUDescriptorHandle.ptr = reinterpret_cast(pCurrInlineConstValueStorage); + // Note that normally resource type is set when a resource is bound to the cache. + // For inline constants, we set it here during tinitialization. + Res.Type = SHADER_RESOURCE_TYPE_CONSTANT_BUFFER; + // Store the number of constant values in BufferRangeSize + Res.BufferRangeSize = RootConsts.d3d12RootParam.Constants.Num32BitValues; + pCurrInlineConstValueStorage += RootConsts.d3d12RootParam.Constants.Num32BitValues; new (&GetRootTable(RootConsts.RootIndex)) RootTable{ 1, - &GetResource(ResIdx), + &Res, false //IsRootView }; ++ResIdx; From 7f033896f6e19378630077728546d80b8089306f Mon Sep 17 00:00:00 2001 From: assiduous Date: Tue, 21 Oct 2025 20:47:28 -0700 Subject: [PATCH 012/104] ShaderResourceCacheD3D12: do not transition/validate null resources --- .../GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp b/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp index 81efd0f860..c097d4a43e 100644 --- a/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp +++ b/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp @@ -573,6 +573,9 @@ void ShaderResourceCacheD3D12::DbgValidateDynamicBuffersMask() const void ShaderResourceCacheD3D12::Resource::TransitionResource(CommandContext& Ctx) { + if (IsNull()) + return; + static_assert(SHADER_RESOURCE_TYPE_LAST == 8, "Please update this function to handle the new resource type"); switch (Type) { @@ -657,6 +660,9 @@ void ShaderResourceCacheD3D12::Resource::TransitionResource(CommandContext& Ctx) #ifdef DILIGENT_DEVELOPMENT void ShaderResourceCacheD3D12::Resource::DvpVerifyResourceState() { + if (IsNull()) + return; + static_assert(SHADER_RESOURCE_TYPE_LAST == 8, "Please update this function to handle the new resource type"); switch (Type) { From 24e9fddc187993a05aee577cf4a873f2205d57ae Mon Sep 17 00:00:00 2001 From: assiduous Date: Wed, 22 Oct 2025 07:47:18 -0700 Subject: [PATCH 013/104] ShaderVarManagerD3D12: properly handle offset from table start for inline constants (#672) --- .../include/ShaderResourceCacheD3D12.hpp | 1 + .../src/ShaderResourceCacheD3D12.cpp | 3 ++- .../src/ShaderVariableManagerD3D12.cpp | 10 +++++----- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Graphics/GraphicsEngineD3D12/include/ShaderResourceCacheD3D12.hpp b/Graphics/GraphicsEngineD3D12/include/ShaderResourceCacheD3D12.hpp index 29d9653a76..173146b639 100644 --- a/Graphics/GraphicsEngineD3D12/include/ShaderResourceCacheD3D12.hpp +++ b/Graphics/GraphicsEngineD3D12/include/ShaderResourceCacheD3D12.hpp @@ -295,6 +295,7 @@ class ShaderResourceCacheD3D12 : public ShaderResourceCacheBase Uint32 BufferDynamicOffset); void SetInlineConstants(Uint32 RootIndex, + Uint32 OffsetFromTableStart, const void* pConstants, Uint32 FirstConstant, Uint32 NumConstants); diff --git a/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp b/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp index c097d4a43e..f0ad062773 100644 --- a/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp +++ b/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp @@ -459,12 +459,13 @@ void ShaderResourceCacheD3D12::SetBufferDynamicOffset(Uint32 RootIndex, } void ShaderResourceCacheD3D12::SetInlineConstants(Uint32 RootIndex, + Uint32 OffsetFromTableStart, const void* pConstants, Uint32 FirstConstant, Uint32 NumConstants) { RootTable& Tbl = GetRootTable(RootIndex); - Resource& Res = Tbl.GetResource(0); + Resource& Res = Tbl.GetResource(OffsetFromTableStart); VERIFY_EXPR(Res.Type == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER); VERIFY(Res.IsNull(), "There should be no resource bound for root constants as they contain raw data."); VERIFY(Res.CPUDescriptorHandle.ptr != 0, "Resources used to store root constants must have valid pointer to the data."); diff --git a/Graphics/GraphicsEngineD3D12/src/ShaderVariableManagerD3D12.cpp b/Graphics/GraphicsEngineD3D12/src/ShaderVariableManagerD3D12.cpp index 398f62badb..ab636fc359 100644 --- a/Graphics/GraphicsEngineD3D12/src/ShaderVariableManagerD3D12.cpp +++ b/Graphics/GraphicsEngineD3D12/src/ShaderVariableManagerD3D12.cpp @@ -674,10 +674,10 @@ void ShaderVariableManagerD3D12::SetInlineConstants(Uint32 ResIndex, Uint32 FirstConstant, Uint32 NumConstants) { - const ResourceAttribs& Attribs = m_pSignature->GetResourceAttribs(ResIndex); - const ResourceCacheContentType CacheType = m_ResourceCache.GetContentType(); - const Uint32 RootIndex = Attribs.RootIndex(CacheType); - VERIFY_EXPR(Attribs.OffsetFromTableStart(CacheType) == 0); + const ResourceAttribs& Attribs = m_pSignature->GetResourceAttribs(ResIndex); + const ResourceCacheContentType CacheType = m_ResourceCache.GetContentType(); + const Uint32 RootIndex = Attribs.RootIndex(CacheType); + const Uint32 OffsetFromTableStart = Attribs.OffsetFromTableStart(CacheType); #ifdef DILIGENT_DEVELOPMENT { @@ -686,7 +686,7 @@ void ShaderVariableManagerD3D12::SetInlineConstants(Uint32 ResIndex, } #endif - m_ResourceCache.SetInlineConstants(RootIndex, pConstants, FirstConstant, NumConstants); + m_ResourceCache.SetInlineConstants(RootIndex, OffsetFromTableStart, pConstants, FirstConstant, NumConstants); } IDeviceObject* ShaderVariableManagerD3D12::Get(Uint32 ArrayIndex, From 98c8cda3d6d0d8a3ac5279fae2c142ae5c03088b Mon Sep 17 00:00:00 2001 From: assiduous Date: Wed, 22 Oct 2025 17:39:51 -0700 Subject: [PATCH 014/104] ResourceSignatureD3D12: use correct array size for inline constants when remapping resources (#672) --- .../interface/PipelineResourceSignature.h | 3 +++ .../src/PipelineResourceSignatureD3D12Impl.cpp | 10 ++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Graphics/GraphicsEngine/interface/PipelineResourceSignature.h b/Graphics/GraphicsEngine/interface/PipelineResourceSignature.h index 8222397d99..b73df18f75 100644 --- a/Graphics/GraphicsEngine/interface/PipelineResourceSignature.h +++ b/Graphics/GraphicsEngine/interface/PipelineResourceSignature.h @@ -285,6 +285,9 @@ struct PipelineResourceDesc SHADER_TYPE ShaderStages DEFAULT_INITIALIZER(SHADER_TYPE_UNKNOWN); /// Resource array size (must be 1 for non-array resources). + /// + /// For inline constants (see PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS), + /// this member specifies the number of 4-byte values. Uint32 ArraySize DEFAULT_INITIALIZER(1); /// Resource type, see Diligent::SHADER_RESOURCE_TYPE. diff --git a/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp index 424b482461..1309de946f 100644 --- a/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp @@ -730,12 +730,18 @@ void PipelineResourceSignatureD3D12Impl::UpdateShaderResourceBindingMap(Resource if ((ResDesc.ShaderStages & ShaderStage) != 0) { + VERIFY((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) == 0 || ResDesc.ResourceType == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, + "Only constant buffers can be marked as INLINE_CONSTANTS. This error should've been caught by ValidatePipelineResourceSignatureDesc()."); + const Uint32 ArraySize = (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) ? + 1 : // For inline constants, ArraySize is the number of 4-byte constants + ResDesc.ArraySize; + ResourceBinding::BindInfo BindInfo // { Attribs.Register, Attribs.Space + BaseRegisterSpace, - ResDesc.ArraySize, - ResDesc.ResourceType // + ArraySize, + ResDesc.ResourceType, }; bool IsUnique = ResourceMap.emplace(HashMapStringKey{ResDesc.Name}, BindInfo).second; VERIFY(IsUnique, "Shader resource '", ResDesc.Name, From 36006c17bf2368035dbb79fd155471292a206215 Mon Sep 17 00:00:00 2001 From: assiduous Date: Wed, 22 Oct 2025 18:57:05 -0700 Subject: [PATCH 015/104] D3D12: commit root constants; add inline constants test (#672) --- .../src/DeviceContextD3D12Impl.cpp | 16 ++ .../PipelineResourceSignatureD3D12Impl.cpp | 8 + .../src/ShaderResourceCacheD3D12.cpp | 4 + .../src/InlineConstantsTest.cpp | 251 ++++++++++++++++++ 4 files changed, 279 insertions(+) create mode 100644 Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp diff --git a/Graphics/GraphicsEngineD3D12/src/DeviceContextD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/DeviceContextD3D12Impl.cpp index e99e4bbef6..0b49ec90aa 100644 --- a/Graphics/GraphicsEngineD3D12/src/DeviceContextD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/DeviceContextD3D12Impl.cpp @@ -418,6 +418,22 @@ void DeviceContextD3D12Impl::CommitRootTablesAndViews(RootTableInfo& RootInfo, U "There are no dynamic root buffers in the cache, but the bit in DynamicSRBMask is set. This may indicate that resources " "in the cache have changed, but the SRB has not been committed before the draw/dispatch command."); } + + if (Uint64 RootConstantsMask = pResourceCache->GetRootConstantsMask()) + { + VERIFY((RootInfo.InlineConstantsSRBMask & SignBit) != 0, + "There are root constants in the cache, but the bit in InlineConstantsSRBMask is not set. " + "This may be a bug because root constants mask in the cache never changes after SRB creation, " + "while RootInfo.InlineConstantsSRBMask is initialized when SRB is committed."); + pSignature->CommitRootConstants(CommitAttribs, RootConstantsMask); + } + else + { + VERIFY((RootInfo.InlineConstantsSRBMask & SignBit) == 0, + "There are no root constants in the cache, but the bit in InlineConstantsSRBMask is set. " + "This may be a bug because root constants mask in the cache never changes after SRB creation, " + "while RootInfo.InlineConstantsSRBMask is initialized when SRB is committed."); + } } VERIFY_EXPR((CommitSRBMask & RootInfo.ActiveSRBMask) == 0); diff --git a/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp index 1309de946f..a00275e153 100644 --- a/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp @@ -830,6 +830,14 @@ bool PipelineResourceSignatureD3D12Impl::DvpValidateCommittedResource(const Devi for (Uint32 ArrIndex = 0; ArrIndex < D3DAttribs.BindCount; ++ArrIndex) { const ShaderResourceCacheD3D12::Resource& CachedRes = RootTable.GetResource(OffsetFromTableStart + ArrIndex); + if (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) + { + VERIFY(CachedRes.IsNull(), "Inline constants should not have any resource bound to them."); + VERIFY(CachedRes.CPUDescriptorHandle.ptr != 0, "Inline constant resource must have valid CPU descriptor handle."); + // Inline constants are not actual resources + continue; + } + if (CachedRes.IsNull()) { LOG_ERROR_MESSAGE("No resource is bound to variable '", GetShaderResourcePrintName(D3DAttribs.Name, D3DAttribs.BindCount, ArrIndex), diff --git a/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp b/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp index f0ad062773..48822b723e 100644 --- a/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp +++ b/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp @@ -178,6 +178,8 @@ void ShaderResourceCacheD3D12::Initialize(IMemoryAllocator& Me // Store the number of constant values in BufferRangeSize Res.BufferRangeSize = InlineConstInfo.NumValues; pCurrInlineConstValueStorage += InlineConstInfo.NumValues; + + m_RootConstantsMask |= (Uint64{1} << Uint64{InlineConstInfo.RootIndex}); } VERIFY_EXPR(pCurrInlineConstValueStorage == GetInlineConstantStorage() + TotalInlineConstantValues); } @@ -284,6 +286,8 @@ void ShaderResourceCacheD3D12::Initialize(IMemoryAllocator& MemAllocator, }; ++ResIdx; + m_RootConstantsMask |= (Uint64{1} << Uint64{RootConsts.RootIndex}); + #ifdef DILIGENT_DEBUG RootTableInitFlags[RootConsts.RootIndex] = true; #endif diff --git a/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp b/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp new file mode 100644 index 0000000000..4e87de8ee4 --- /dev/null +++ b/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp @@ -0,0 +1,251 @@ +/* + * Copyright 2025 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "GPUTestingEnvironment.hpp" + +#include "gtest/gtest.h" + +#include "GraphicsTypesX.hpp" +#include "FastRand.hpp" + + +namespace Diligent +{ +namespace Testing +{ +void RenderDrawCommandReference(ISwapChain* pSwapChain, const float* pClearColor = nullptr); +} +} // namespace Diligent + + +using namespace Diligent; +using namespace Diligent::Testing; + +#include "InlineShaders/DrawCommandTestHLSL.h" + + +namespace +{ + +namespace HLSL +{ + +const std::string InlineConstantsTest_VS{ + R"( +cbuffer cbInlinePositions +{ + float4 g_Positions[6]; +} + +cbuffer cbInlineColors +{ + float4 g_Colors[3]; +} + +struct PSInput +{ + float4 Pos : SV_POSITION; + float3 Color : COLOR; +}; + +void main(uint VertexId : SV_VertexId, + out PSInput PSIn) +{ + PSIn.Pos = g_Positions[VertexId]; + PSIn.Color = g_Colors[VertexId % 3].rgb; +} +)"}; + +} + +float4 g_Positions[] = { + float4{-1.0f, -0.5f, 0.f, 1.f}, + float4{-0.5f, +0.5f, 0.f, 1.f}, + float4{0.0f, -0.5f, 0.f, 1.f}, + + float4{+0.0f, -0.5f, 0.f, 1.f}, + float4{+0.5f, +0.5f, 0.f, 1.f}, + float4{+1.0f, -0.5f, 0.f, 1.f}, +}; + +float4 g_Colors[] = { + float4{1.f, 0.f, 0.f, 1.f}, + float4{0.f, 1.f, 0.f, 1.f}, + float4{0.f, 0.f, 1.f, 1.f}, +}; + +class InlineConstants : public ::testing::Test +{ +protected: + static void SetUpTestSuite() + { + GPUTestingEnvironment* pEnv = GPUTestingEnvironment::GetInstance(); + IRenderDevice* pDevice = pEnv->GetDevice(); + + ShaderCreateInfo ShaderCI; + ShaderCI.SourceLanguage = SHADER_SOURCE_LANGUAGE_HLSL; + ShaderCI.ShaderCompiler = pEnv->GetDefaultCompiler(ShaderCI.SourceLanguage); + + { + ShaderCI.Desc = {"Inline constants test", SHADER_TYPE_VERTEX, true}; + ShaderCI.EntryPoint = "main"; + ShaderCI.Source = HLSL::InlineConstantsTest_VS.c_str(); + pDevice->CreateShader(ShaderCI, &sm_Res.pVS); + ASSERT_NE(sm_Res.pVS, nullptr); + } + + { + ShaderCI.Desc = {"Inline constants test", SHADER_TYPE_PIXEL, true}; + ShaderCI.EntryPoint = "main"; + ShaderCI.Source = HLSL::DrawTest_PS.c_str(); + pDevice->CreateShader(ShaderCI, &sm_Res.pPS); + ASSERT_NE(sm_Res.pPS, nullptr); + } + } + + static void TearDownTestSuite() + { + sm_Res = {}; + + GPUTestingEnvironment* pEnv = GPUTestingEnvironment::GetInstance(); + pEnv->Reset(); + } + + static void Present() + { + GPUTestingEnvironment* pEnv = GPUTestingEnvironment::GetInstance(); + ISwapChain* pSwapChain = pEnv->GetSwapChain(); + IDeviceContext* pContext = pEnv->GetDeviceContext(); + + pSwapChain->Present(); + + pContext->Flush(); + pContext->InvalidateState(); + } + + struct Resources + { + RefCntAutoPtr pVS; + RefCntAutoPtr pPS; + }; + static Resources sm_Res; + + static FastRandFloat sm_Rnd; +}; + +InlineConstants::Resources InlineConstants::sm_Res; +FastRandFloat InlineConstants::sm_Rnd{0, 0.f, 1.f}; + + +TEST_F(InlineConstants, ResourceSignature) +{ + GPUTestingEnvironment* pEnv = GPUTestingEnvironment::GetInstance(); + IRenderDevice* pDevice = pEnv->GetDevice(); + IDeviceContext* pContext = pEnv->GetDeviceContext(); + ISwapChain* pSwapChain = pEnv->GetSwapChain(); + if (pDevice->GetDeviceInfo().Type != RENDER_DEVICE_TYPE_D3D12) + { + GTEST_SKIP(); + } + + for (Uint32 pos_type = 0; pos_type < SHADER_RESOURCE_VARIABLE_TYPE_NUM_TYPES; ++pos_type) + { + for (Uint32 col_type = 0; col_type < SHADER_RESOURCE_VARIABLE_TYPE_NUM_TYPES; ++col_type) + { + const float ClearColor[] = {sm_Rnd(), sm_Rnd(), sm_Rnd(), sm_Rnd()}; + RenderDrawCommandReference(pSwapChain, ClearColor); + + SHADER_RESOURCE_VARIABLE_TYPE PosType = static_cast(pos_type); + SHADER_RESOURCE_VARIABLE_TYPE ColType = static_cast(col_type); + + PipelineResourceSignatureDescX SignDesc; + SignDesc + .AddResource(SHADER_TYPE_VERTEX, "cbInlinePositions", Uint32{sizeof(g_Positions) / 4}, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, PosType, PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) + .AddResource(SHADER_TYPE_VERTEX, "cbInlineColors", Uint32{sizeof(g_Colors) / 4}, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, ColType, PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS); + RefCntAutoPtr pSign; + pDevice->CreatePipelineResourceSignature(SignDesc, &pSign); + ASSERT_TRUE(pSign); + + GraphicsPipelineStateCreateInfoX PsoCI{"Inline constants test"}; + PsoCI + .AddRenderTarget(pSwapChain->GetDesc().ColorBufferFormat) + .SetPrimitiveTopology(PRIMITIVE_TOPOLOGY_TRIANGLE_LIST) + .AddShader(sm_Res.pVS) + .AddShader(sm_Res.pPS) + .AddSignature(pSign); + PsoCI.GraphicsPipeline.DepthStencilDesc.DepthEnable = False; + + RefCntAutoPtr pPSO; + pDevice->CreateGraphicsPipelineState(PsoCI, &pPSO); + ASSERT_TRUE(pPSO); + + if (PosType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) + { + IShaderResourceVariable* pVar = pSign->GetStaticVariableByName(SHADER_TYPE_VERTEX, "cbInlinePositions"); + ASSERT_TRUE(pVar); + pVar->SetInlineConstants(g_Positions, 0, sizeof(g_Positions) / 4); + } + + if (ColType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) + { + IShaderResourceVariable* pVar = pSign->GetStaticVariableByName(SHADER_TYPE_VERTEX, "cbInlineColors"); + ASSERT_TRUE(pVar); + pVar->SetInlineConstants(g_Colors, 0, sizeof(g_Colors) / 4); + } + + RefCntAutoPtr pSRB; + pSign->CreateShaderResourceBinding(&pSRB, true); + ASSERT_TRUE(pSRB); + + if (PosType != SHADER_RESOURCE_VARIABLE_TYPE_STATIC) + { + IShaderResourceVariable* pVar = pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cbInlinePositions"); + ASSERT_TRUE(pVar); + pVar->SetInlineConstants(g_Positions, 0, sizeof(g_Positions) / 4); + } + + if (ColType != SHADER_RESOURCE_VARIABLE_TYPE_STATIC) + { + IShaderResourceVariable* pVar = pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cbInlineColors"); + ASSERT_TRUE(pVar); + pVar->SetInlineConstants(g_Colors, 0, sizeof(g_Colors) / 4); + } + + ITextureView* pRTVs[] = {pSwapChain->GetCurrentBackBufferRTV()}; + pContext->SetRenderTargets(1, pRTVs, nullptr, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); + pContext->ClearRenderTarget(pRTVs[0], ClearColor, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); + + pContext->CommitShaderResources(pSRB, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); + pContext->CommitShaderResources(pSRB, RESOURCE_STATE_TRANSITION_MODE_VERIFY); + pContext->SetPipelineState(pPSO); + pContext->Draw({6, DRAW_FLAG_VERIFY_ALL}); + + Present(); + } + } +} + +} // namespace From 2dacc9dcef1dd409300c209aa63933476fd6b998 Mon Sep 17 00:00:00 2001 From: assiduous Date: Wed, 22 Oct 2025 19:49:02 -0700 Subject: [PATCH 016/104] Improve inline constants test --- .../src/InlineConstantsTest.cpp | 107 ++++++++++++++++-- 1 file changed, 96 insertions(+), 11 deletions(-) diff --git a/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp b/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp index 4e87de8ee4..a641a2b226 100644 --- a/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp +++ b/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp @@ -97,6 +97,9 @@ float4 g_Colors[] = { float4{0.f, 0.f, 1.f, 1.f}, }; +constexpr Uint32 kNumPosConstants = sizeof(g_Positions) / 4; +constexpr Uint32 kNumColConstants = sizeof(g_Colors) / 4; + class InlineConstants : public ::testing::Test { protected: @@ -171,6 +174,13 @@ TEST_F(InlineConstants, ResourceSignature) GTEST_SKIP(); } + RefCntAutoPtr pConstBuffer = pEnv->CreateBuffer({"InlineConstants - dummy const buffer", 256, BIND_UNIFORM_BUFFER}); + ASSERT_TRUE(pConstBuffer); + RefCntAutoPtr pTexture = pEnv->CreateTexture("InlineConstants - dummy texture", TEX_FORMAT_RGBA8_UNORM, BIND_SHADER_RESOURCE, 64, 64); + ASSERT_TRUE(pTexture); + ITextureView* pTexSRV = pTexture->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE); + ASSERT_TRUE(pTexSRV); + for (Uint32 pos_type = 0; pos_type < SHADER_RESOURCE_VARIABLE_TYPE_NUM_TYPES; ++pos_type) { for (Uint32 col_type = 0; col_type < SHADER_RESOURCE_VARIABLE_TYPE_NUM_TYPES; ++col_type) @@ -183,12 +193,41 @@ TEST_F(InlineConstants, ResourceSignature) PipelineResourceSignatureDescX SignDesc; SignDesc - .AddResource(SHADER_TYPE_VERTEX, "cbInlinePositions", Uint32{sizeof(g_Positions) / 4}, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, PosType, PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) - .AddResource(SHADER_TYPE_VERTEX, "cbInlineColors", Uint32{sizeof(g_Colors) / 4}, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, ColType, PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS); + .AddResource(SHADER_TYPE_VERTEX, "cb0_stat", 1u, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_STATIC) + .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) + .AddResource(SHADER_TYPE_VERTEX, "tex0_mut", SHADER_RESOURCE_TYPE_TEXTURE_SRV, SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE) + .AddResource(SHADER_TYPE_VERTEX, "tex0_dyn", SHADER_RESOURCE_TYPE_TEXTURE_SRV, SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) + + .AddResource(SHADER_TYPE_VERTEX, "cbInlinePositions", kNumPosConstants, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, PosType, PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) + + .AddResource(SHADER_TYPE_VERTEX, "cb1_stat", 1u, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_STATIC) + .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) + .AddResource(SHADER_TYPE_VERTEX, "tex1_mut", SHADER_RESOURCE_TYPE_TEXTURE_SRV, SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE) + .AddResource(SHADER_TYPE_VERTEX, "tex1_dyn", SHADER_RESOURCE_TYPE_TEXTURE_SRV, SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) + + .AddResource(SHADER_TYPE_VERTEX, "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_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) + .AddResource(SHADER_TYPE_VERTEX, "tex2_mut", SHADER_RESOURCE_TYPE_TEXTURE_SRV, SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE) + .AddResource(SHADER_TYPE_VERTEX, "tex2_dyn", SHADER_RESOURCE_TYPE_TEXTURE_SRV, SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC); RefCntAutoPtr pSign; pDevice->CreatePipelineResourceSignature(SignDesc, &pSign); ASSERT_TRUE(pSign); + pSign->GetStaticVariableByName(SHADER_TYPE_VERTEX, "cb0_stat")->Set(pConstBuffer); + pSign->GetStaticVariableByName(SHADER_TYPE_VERTEX, "tex0_stat")->Set(pTexSRV); + pSign->GetStaticVariableByName(SHADER_TYPE_VERTEX, "cb1_stat")->Set(pConstBuffer); + pSign->GetStaticVariableByName(SHADER_TYPE_VERTEX, "tex1_stat")->Set(pTexSRV); + pSign->GetStaticVariableByName(SHADER_TYPE_VERTEX, "cb2_stat")->Set(pConstBuffer); + pSign->GetStaticVariableByName(SHADER_TYPE_VERTEX, "tex2_stat")->Set(pTexSRV); + GraphicsPipelineStateCreateInfoX PsoCI{"Inline constants test"}; PsoCI .AddRenderTarget(pSwapChain->GetDesc().ColorBufferFormat) @@ -206,44 +245,90 @@ TEST_F(InlineConstants, ResourceSignature) { IShaderResourceVariable* pVar = pSign->GetStaticVariableByName(SHADER_TYPE_VERTEX, "cbInlinePositions"); ASSERT_TRUE(pVar); - pVar->SetInlineConstants(g_Positions, 0, sizeof(g_Positions) / 4); + pVar->SetInlineConstants(g_Positions, 0, kNumPosConstants); } if (ColType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) { IShaderResourceVariable* pVar = pSign->GetStaticVariableByName(SHADER_TYPE_VERTEX, "cbInlineColors"); ASSERT_TRUE(pVar); - pVar->SetInlineConstants(g_Colors, 0, sizeof(g_Colors) / 4); + pVar->SetInlineConstants(g_Colors, 0, kNumColConstants); } RefCntAutoPtr pSRB; pSign->CreateShaderResourceBinding(&pSRB, true); ASSERT_TRUE(pSRB); + pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cb0_mut")->Set(pConstBuffer); + pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "tex0_mut")->Set(pTexSRV); + pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cb1_mut")->Set(pConstBuffer); + pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "tex1_mut")->Set(pTexSRV); + pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cb2_mut")->Set(pConstBuffer); + pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "tex2_mut")->Set(pTexSRV); + pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cb0_dyn")->Set(pConstBuffer); + pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "tex0_dyn")->Set(pTexSRV); + pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cb1_dyn")->Set(pConstBuffer); + pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "tex1_dyn")->Set(pTexSRV); + pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cb2_dyn")->Set(pConstBuffer); + pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "tex2_dyn")->Set(pTexSRV); + + IShaderResourceVariable* pPosVar = nullptr; if (PosType != SHADER_RESOURCE_VARIABLE_TYPE_STATIC) { - IShaderResourceVariable* pVar = pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cbInlinePositions"); - ASSERT_TRUE(pVar); - pVar->SetInlineConstants(g_Positions, 0, sizeof(g_Positions) / 4); + pPosVar = pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cbInlinePositions"); + ASSERT_TRUE(pPosVar); } + IShaderResourceVariable* pColVar = nullptr; if (ColType != SHADER_RESOURCE_VARIABLE_TYPE_STATIC) { - IShaderResourceVariable* pVar = pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cbInlineColors"); - ASSERT_TRUE(pVar); - pVar->SetInlineConstants(g_Colors, 0, sizeof(g_Colors) / 4); + pColVar = pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cbInlineColors"); + ASSERT_TRUE(pColVar); } ITextureView* pRTVs[] = {pSwapChain->GetCurrentBackBufferRTV()}; pContext->SetRenderTargets(1, pRTVs, nullptr, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); pContext->ClearRenderTarget(pRTVs[0], ClearColor, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); + + if (pColVar != nullptr) + { + // Set first half of color constants before committing SRB + pColVar->SetInlineConstants(g_Colors, 0, kNumColConstants / 2); + } + pContext->CommitShaderResources(pSRB, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); pContext->CommitShaderResources(pSRB, RESOURCE_STATE_TRANSITION_MODE_VERIFY); + + if (pColVar != nullptr) + { + // Set second half of color constants after committing SRB + pColVar->SetInlineConstants(g_Colors[0].Data() + kNumColConstants / 2, kNumColConstants / 2, kNumColConstants / 2); + } + pContext->SetPipelineState(pPSO); - pContext->Draw({6, DRAW_FLAG_VERIFY_ALL}); + + if (pPosVar == nullptr) + { + // Draw both triangles as positions are static + pContext->Draw({6, DRAW_FLAG_VERIFY_ALL}); + } + else + { + // Draw first triangle + pPosVar->SetInlineConstants(g_Positions, 0, kNumPosConstants / 2); + pContext->Draw({3, DRAW_FLAG_VERIFY_ALL}); + + // Draw second triangle + pPosVar->SetInlineConstants(g_Positions[0].Data() + kNumPosConstants / 2, 0, kNumPosConstants / 2); + pContext->Draw({3, DRAW_FLAG_VERIFY_ALL}); + } Present(); + + std::cout << TestingEnvironment::GetCurrentTestStatusString() << ' ' + << " Pos " << GetShaderVariableTypeLiteralName(PosType) << ',' + << " Col " << GetShaderVariableTypeLiteralName(ColType) << std::endl; } } } From d8ddfb7b3b515f2fff684ea0d570e908fd0ba6c6 Mon Sep 17 00:00:00 2001 From: assiduous Date: Wed, 22 Oct 2025 20:08:36 -0700 Subject: [PATCH 017/104] Improved inline constant validation (#672) --- .../include/ShaderResourceVariableBase.hpp | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/Graphics/GraphicsEngine/include/ShaderResourceVariableBase.hpp b/Graphics/GraphicsEngine/include/ShaderResourceVariableBase.hpp index fcb6055153..0469d518f3 100644 --- a/Graphics/GraphicsEngine/include/ShaderResourceVariableBase.hpp +++ b/Graphics/GraphicsEngine/include/ShaderResourceVariableBase.hpp @@ -179,23 +179,6 @@ bool VerifyResourceBinding(const char* ExpectedResourceTypeName, return false; } - if (ResDesc.ResourceType == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER && (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0) - { - if (pResourceImpl != nullptr) - { - std::stringstream ss; - ss << "Failed to bind " << ExpectedResourceTypeName << " '" << pResourceImpl->GetDesc().Name << "' to variable '" - << GetShaderResourcePrintName(ResDesc, BindInfo.ArrayIndex) << '\''; - if (SignatureName != nullptr) - { - ss << " defined by signature '" << SignatureName << '\''; - } - ss << ". Inline constants must be set using IShaderResourceVariable::SetInlineConstants() method."; - RESOURCE_VALIDATION_FAILURE(ss.str()); - return false; - } - } - if (ResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC && (BindInfo.Flags & SET_SHADER_RESOURCE_FLAG_ALLOW_OVERWRITE) == 0 && pCachedObject != nullptr && pCachedObject != pResourceImpl) @@ -248,6 +231,20 @@ bool VerifyConstantBufferBinding(const PipelineResourceDesc& ResDesc, Uint64 CachedRangeSize, const char* SignatureName) { + if ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0) + { + std::stringstream ss; + ss << "Error binding buffer to variable '" << GetShaderResourcePrintName(ResDesc, BindInfo.ArrayIndex) << '\''; + if (SignatureName != nullptr) + { + ss << " defined by signature '" << SignatureName << '\''; + } + ss << ". The variable is marked as inline constants and cannot be bound to a buffer. " + "Use IShaderResourceVariable::SetInlineConstants() method to set the inline constant values."; + RESOURCE_VALIDATION_FAILURE(ss.str()); + return false; + } + bool BindingOK = VerifyResourceBinding("buffer", ResDesc, BindInfo, pBufferImpl, pCachedBuffer, SignatureName); if (pBufferImpl != nullptr) @@ -557,6 +554,12 @@ bool VerifyDynamicBufferOffset(const PipelineResourceDesc& ResDesc, { bool BindingOK = true; + if ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0) + { + RESOURCE_VALIDATION_FAILURE("Error setting dynamic buffer offset for variable '", ResDesc.Name, "': inline constants do not support dynamic offsets."); + BindingOK = false; + } + if ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS) != 0) { RESOURCE_VALIDATION_FAILURE("Error setting dynamic buffer offset for variable '", ResDesc.Name, "': the variable was created with PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS flag."); From 72bb17776fb33ea27b14788c96ff18468508b578 Mon Sep 17 00:00:00 2001 From: assiduous Date: Wed, 22 Oct 2025 20:28:51 -0700 Subject: [PATCH 018/104] Improve inline constants resource description validation (#672) --- .../src/PipelineResourceSignatureBase.cpp | 20 ++++++++++++++++--- .../src/InlineConstantsTest.cpp | 2 +- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp b/Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp index dec112807f..8104d3eed3 100644 --- a/Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp +++ b/Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp @@ -111,10 +111,24 @@ void ValidatePipelineResourceSignatureDesc(const PipelineResourceSignatureDesc& ": ", GetPipelineResourceFlagsString(AllowedResourceFlags, false, ", "), "."); } - if ((Res.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0 && Res.Flags != PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) + if ((Res.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0) { - LOG_PRS_ERROR_AND_THROW("Incorrect Desc.Resources[", i, "].Flags (", GetPipelineResourceFlagsString(Res.Flags), - "). INLINE_CONSTANTS flag cannot be combined with other flags."); + if (Res.Flags != PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) + { + LOG_PRS_ERROR_AND_THROW("Incorrect Desc.Resources[", i, "].Flags (", GetPipelineResourceFlagsString(Res.Flags), + "). INLINE_CONSTANTS flag cannot be combined with other flags."); + } + + if (Res.ArraySize == 0) + { + LOG_PRS_ERROR_AND_THROW("Desc.Resources[", i, "].ArraySize must be greater than 0 for inline constants."); + } + + if (Res.ArraySize > 64) + { + LOG_PRS_ERROR_AND_THROW("Desc.Resources[", i, "].ArraySize (", Res.ArraySize, + ") exceeds the maximum allowed value (64) for inline constants."); + } } if ((Res.Flags & PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER) != 0 && Features.FormattedBuffers == DEVICE_FEATURE_STATE_DISABLED) diff --git a/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp b/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp index a641a2b226..1cb09f31a4 100644 --- a/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp +++ b/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp @@ -191,7 +191,7 @@ TEST_F(InlineConstants, ResourceSignature) SHADER_RESOURCE_VARIABLE_TYPE PosType = static_cast(pos_type); SHADER_RESOURCE_VARIABLE_TYPE ColType = static_cast(col_type); - PipelineResourceSignatureDescX SignDesc; + 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_mut", 1u, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE) From 93391d1fc5ef456abda7d9b7ac4988cd942c2bfc Mon Sep 17 00:00:00 2001 From: assiduous Date: Thu, 23 Oct 2025 08:02:56 -0700 Subject: [PATCH 019/104] Add inline constants test with two resource signatures (#672) --- .../src/InlineConstantsTest.cpp | 128 +++++++++++++----- 1 file changed, 93 insertions(+), 35 deletions(-) diff --git a/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp b/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp index 1cb09f31a4..5adfa494d1 100644 --- a/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp +++ b/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp @@ -137,6 +137,8 @@ class InlineConstants : public ::testing::Test pEnv->Reset(); } + static void TestSignatures(Uint32 NumSignatures); + static void Present() { GPUTestingEnvironment* pEnv = GPUTestingEnvironment::GetInstance(); @@ -163,7 +165,7 @@ InlineConstants::Resources InlineConstants::sm_Res; FastRandFloat InlineConstants::sm_Rnd{0, 0.f, 1.f}; -TEST_F(InlineConstants, ResourceSignature) +void InlineConstants::TestSignatures(Uint32 NumSignatures) { GPUTestingEnvironment* pEnv = GPUTestingEnvironment::GetInstance(); IRenderDevice* pDevice = pEnv->GetDevice(); @@ -191,6 +193,9 @@ TEST_F(InlineConstants, ResourceSignature) SHADER_RESOURCE_VARIABLE_TYPE PosType = static_cast(pos_type); SHADER_RESOURCE_VARIABLE_TYPE ColType = static_cast(col_type); + RefCntAutoPtr pPosSign; + RefCntAutoPtr pColSign; + PipelineResourceSignatureDescX SignDesc{"Inline constants test"}; SignDesc .AddResource(SHADER_TYPE_VERTEX, "cb0_stat", 1u, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_STATIC) @@ -200,9 +205,19 @@ TEST_F(InlineConstants, ResourceSignature) .AddResource(SHADER_TYPE_VERTEX, "tex0_mut", SHADER_RESOURCE_TYPE_TEXTURE_SRV, SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE) .AddResource(SHADER_TYPE_VERTEX, "tex0_dyn", SHADER_RESOURCE_TYPE_TEXTURE_SRV, SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) - .AddResource(SHADER_TYPE_VERTEX, "cbInlinePositions", kNumPosConstants, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, PosType, PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) + .AddResource(SHADER_TYPE_VERTEX, "cbInlinePositions", kNumPosConstants, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, PosType, PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS); + + if (NumSignatures == 2) + { + pDevice->CreatePipelineResourceSignature(SignDesc, &pPosSign); + ASSERT_TRUE(pPosSign); - .AddResource(SHADER_TYPE_VERTEX, "cb1_stat", 1u, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_STATIC) + SignDesc.Clear(); + SignDesc.Name = "Inline constants test 2"; + SignDesc.BindingIndex = 1; + } + + SignDesc.AddResource(SHADER_TYPE_VERTEX, "cb1_stat", 1u, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_STATIC) .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) @@ -217,16 +232,29 @@ TEST_F(InlineConstants, ResourceSignature) .AddResource(SHADER_TYPE_VERTEX, "tex2_stat", SHADER_RESOURCE_TYPE_TEXTURE_SRV, SHADER_RESOURCE_VARIABLE_TYPE_STATIC) .AddResource(SHADER_TYPE_VERTEX, "tex2_mut", SHADER_RESOURCE_TYPE_TEXTURE_SRV, SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE) .AddResource(SHADER_TYPE_VERTEX, "tex2_dyn", SHADER_RESOURCE_TYPE_TEXTURE_SRV, SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC); - RefCntAutoPtr pSign; - pDevice->CreatePipelineResourceSignature(SignDesc, &pSign); - ASSERT_TRUE(pSign); - pSign->GetStaticVariableByName(SHADER_TYPE_VERTEX, "cb0_stat")->Set(pConstBuffer); - pSign->GetStaticVariableByName(SHADER_TYPE_VERTEX, "tex0_stat")->Set(pTexSRV); - pSign->GetStaticVariableByName(SHADER_TYPE_VERTEX, "cb1_stat")->Set(pConstBuffer); - pSign->GetStaticVariableByName(SHADER_TYPE_VERTEX, "tex1_stat")->Set(pTexSRV); - pSign->GetStaticVariableByName(SHADER_TYPE_VERTEX, "cb2_stat")->Set(pConstBuffer); - pSign->GetStaticVariableByName(SHADER_TYPE_VERTEX, "tex2_stat")->Set(pTexSRV); + if (NumSignatures == 1) + { + pDevice->CreatePipelineResourceSignature(SignDesc, &pPosSign); + ASSERT_TRUE(pPosSign); + pColSign = pPosSign; + } + else if (NumSignatures == 2) + { + pDevice->CreatePipelineResourceSignature(SignDesc, &pColSign); + ASSERT_TRUE(pColSign); + } + else + { + GTEST_FAIL() << "Invalid number of signatures: " << NumSignatures; + } + + pPosSign->GetStaticVariableByName(SHADER_TYPE_VERTEX, "cb0_stat")->Set(pConstBuffer); + pPosSign->GetStaticVariableByName(SHADER_TYPE_VERTEX, "tex0_stat")->Set(pTexSRV); + pColSign->GetStaticVariableByName(SHADER_TYPE_VERTEX, "cb1_stat")->Set(pConstBuffer); + pColSign->GetStaticVariableByName(SHADER_TYPE_VERTEX, "tex1_stat")->Set(pTexSRV); + pColSign->GetStaticVariableByName(SHADER_TYPE_VERTEX, "cb2_stat")->Set(pConstBuffer); + pColSign->GetStaticVariableByName(SHADER_TYPE_VERTEX, "tex2_stat")->Set(pTexSRV); GraphicsPipelineStateCreateInfoX PsoCI{"Inline constants test"}; PsoCI @@ -234,7 +262,11 @@ TEST_F(InlineConstants, ResourceSignature) .SetPrimitiveTopology(PRIMITIVE_TOPOLOGY_TRIANGLE_LIST) .AddShader(sm_Res.pVS) .AddShader(sm_Res.pPS) - .AddSignature(pSign); + .AddSignature(pPosSign); + if (NumSignatures == 2) + { + PsoCI.AddSignature(pColSign); + } PsoCI.GraphicsPipeline.DepthStencilDesc.DepthEnable = False; RefCntAutoPtr pPSO; @@ -243,46 +275,57 @@ TEST_F(InlineConstants, ResourceSignature) if (PosType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) { - IShaderResourceVariable* pVar = pSign->GetStaticVariableByName(SHADER_TYPE_VERTEX, "cbInlinePositions"); + IShaderResourceVariable* pVar = pPosSign->GetStaticVariableByName(SHADER_TYPE_VERTEX, "cbInlinePositions"); ASSERT_TRUE(pVar); pVar->SetInlineConstants(g_Positions, 0, kNumPosConstants); } if (ColType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) { - IShaderResourceVariable* pVar = pSign->GetStaticVariableByName(SHADER_TYPE_VERTEX, "cbInlineColors"); + IShaderResourceVariable* pVar = pColSign->GetStaticVariableByName(SHADER_TYPE_VERTEX, "cbInlineColors"); ASSERT_TRUE(pVar); pVar->SetInlineConstants(g_Colors, 0, kNumColConstants); } - RefCntAutoPtr pSRB; - pSign->CreateShaderResourceBinding(&pSRB, true); - ASSERT_TRUE(pSRB); - - pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cb0_mut")->Set(pConstBuffer); - pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "tex0_mut")->Set(pTexSRV); - pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cb1_mut")->Set(pConstBuffer); - pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "tex1_mut")->Set(pTexSRV); - pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cb2_mut")->Set(pConstBuffer); - pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "tex2_mut")->Set(pTexSRV); - pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cb0_dyn")->Set(pConstBuffer); - pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "tex0_dyn")->Set(pTexSRV); - pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cb1_dyn")->Set(pConstBuffer); - pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "tex1_dyn")->Set(pTexSRV); - pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cb2_dyn")->Set(pConstBuffer); - pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "tex2_dyn")->Set(pTexSRV); + RefCntAutoPtr pPosSRB; + pPosSign->CreateShaderResourceBinding(&pPosSRB, true); + ASSERT_TRUE(pPosSRB); + + RefCntAutoPtr pColSRB; + if (NumSignatures == 1) + { + pColSRB = pPosSRB; + } + else if (NumSignatures == 2) + { + pColSign->CreateShaderResourceBinding(&pColSRB, true); + ASSERT_TRUE(pColSRB); + } + + pPosSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cb0_mut")->Set(pConstBuffer); + pPosSRB->GetVariableByName(SHADER_TYPE_VERTEX, "tex0_mut")->Set(pTexSRV); + pColSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cb1_mut")->Set(pConstBuffer); + pColSRB->GetVariableByName(SHADER_TYPE_VERTEX, "tex1_mut")->Set(pTexSRV); + pColSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cb2_mut")->Set(pConstBuffer); + pColSRB->GetVariableByName(SHADER_TYPE_VERTEX, "tex2_mut")->Set(pTexSRV); + pPosSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cb0_dyn")->Set(pConstBuffer); + pPosSRB->GetVariableByName(SHADER_TYPE_VERTEX, "tex0_dyn")->Set(pTexSRV); + pColSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cb1_dyn")->Set(pConstBuffer); + pColSRB->GetVariableByName(SHADER_TYPE_VERTEX, "tex1_dyn")->Set(pTexSRV); + pColSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cb2_dyn")->Set(pConstBuffer); + pColSRB->GetVariableByName(SHADER_TYPE_VERTEX, "tex2_dyn")->Set(pTexSRV); IShaderResourceVariable* pPosVar = nullptr; if (PosType != SHADER_RESOURCE_VARIABLE_TYPE_STATIC) { - pPosVar = pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cbInlinePositions"); + pPosVar = pPosSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cbInlinePositions"); ASSERT_TRUE(pPosVar); } IShaderResourceVariable* pColVar = nullptr; if (ColType != SHADER_RESOURCE_VARIABLE_TYPE_STATIC) { - pColVar = pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cbInlineColors"); + pColVar = pColSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cbInlineColors"); ASSERT_TRUE(pColVar); } @@ -297,8 +340,13 @@ TEST_F(InlineConstants, ResourceSignature) pColVar->SetInlineConstants(g_Colors, 0, kNumColConstants / 2); } - pContext->CommitShaderResources(pSRB, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); - pContext->CommitShaderResources(pSRB, RESOURCE_STATE_TRANSITION_MODE_VERIFY); + pContext->CommitShaderResources(pPosSRB, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); + + if (NumSignatures == 2) + { + pContext->TransitionShaderResources(pColSRB); + pContext->CommitShaderResources(pColSRB, RESOURCE_STATE_TRANSITION_MODE_VERIFY); + } if (pColVar != nullptr) { @@ -333,4 +381,14 @@ TEST_F(InlineConstants, ResourceSignature) } } +TEST_F(InlineConstants, ResourceSignature) +{ + TestSignatures(1); +} + +TEST_F(InlineConstants, TwoResourceSignatures) +{ + TestSignatures(2); +} + } // namespace From 3afba6495fecc065ae863e299aaff6be86a0ece1 Mon Sep 17 00:00:00 2001 From: assiduous Date: Thu, 23 Oct 2025 20:16:48 -0700 Subject: [PATCH 020/104] D3DShaderResourceLoader: don't use auto where unnecessary --- .../include/D3DShaderResourceLoader.hpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Graphics/GraphicsEngineD3DBase/include/D3DShaderResourceLoader.hpp b/Graphics/GraphicsEngineD3DBase/include/D3DShaderResourceLoader.hpp index cc30988c17..52dda04c71 100644 --- a/Graphics/GraphicsEngineD3DBase/include/D3DShaderResourceLoader.hpp +++ b/Graphics/GraphicsEngineD3DBase/include/D3DShaderResourceLoader.hpp @@ -97,7 +97,7 @@ void LoadShaderCodeVariableDesc(TD3DShaderReflectionType* pd3dReflecionType, Sha ShaderCodeVariableDesc MemberDesc; MemberDesc.Name = pd3dReflecionType->GetMemberTypeName(m); - auto idx = TypeDesc.AddMember(MemberDesc); + size_t idx = TypeDesc.AddMember(MemberDesc); VERIFY_EXPR(idx == m); auto* pd3dMemberType = pd3dReflecionType->GetMemberTypeByIndex(m); VERIFY_EXPR(pd3dMemberType != nullptr); @@ -122,7 +122,7 @@ void LoadD3DShaderConstantBufferReflection(TShaderReflection* pBuffReflection, S VarDesc.Name = d3dShaderVarDesc.Name; // The variable name. VarDesc.Offset = d3dShaderVarDesc.StartOffset; // Offset from the start of the parent structure to the beginning of the variable. - auto idx = BufferDesc.AddVariable(VarDesc); + size_t idx = BufferDesc.AddVariable(VarDesc); VERIFY_EXPR(idx == var); auto* pd3dReflecionType = pVaribable->GetType(); @@ -187,7 +187,7 @@ void LoadD3DShaderResources(TShaderReflection* pShaderReflection, pShaderReflection->GetResourceBindingDesc(Res, &BindingDesc); std::string Name; - const auto ArrayIndex = Parsing::GetArrayIndex(BindingDesc.Name, Name); + const int ArrayIndex = Parsing::GetArrayIndex(BindingDesc.Name, Name); if (BindingDesc.BindPoint == UINT32_MAX) { @@ -232,7 +232,7 @@ void LoadD3DShaderResources(TShaderReflection* pShaderReflection, VERIFY(BindCount == 1, "When array elements are enumerated individually, BindCount is expected to always be 1"); #ifdef DILIGENT_DEBUG - for (const auto& ExistingRes : Resources) + for (const D3DShaderResourceAttribs& ExistingRes : Resources) { VERIFY(Name.compare(ExistingRes.Name) != 0, "Resource with the same name has already been enumerated. All array elements are expected to be enumerated one after another"); } @@ -243,7 +243,7 @@ void LoadD3DShaderResources(TShaderReflection* pShaderReflection, pShaderReflection->GetResourceBindingDesc(ArrElem, &NextElemBindingDesc); std::string NextElemName; - const auto NextElemIndex = Parsing::GetArrayIndex(NextElemBindingDesc.Name, NextElemName); + const int NextElemIndex = Parsing::GetArrayIndex(NextElemBindingDesc.Name, NextElemName); // Make sure this case is handled correctly: // "g_tex2DDiffuse[.]" != "g_tex2DDiffuse2[.]" @@ -271,7 +271,7 @@ void LoadD3DShaderResources(TShaderReflection* pShaderReflection, switch (BindingDesc.Type) { - // clang-format off + // clang-format off case D3D_SIT_CBUFFER: ++RC.NumCBs; break; case D3D_SIT_TBUFFER: UNSUPPORTED( "TBuffers are not supported" ); break; case D3D_SIT_TEXTURE: ++(BindingDesc.Dimension == D3D_SRV_DIMENSION_BUFFER ? RC.NumBufSRVs : RC.NumTexSRVs); break; @@ -315,7 +315,7 @@ void LoadD3DShaderResources(TShaderReflection* pShaderReflection, for (size_t ResInd = 0; ResInd < Resources.size(); ++ResInd) { - const auto& Res = Resources[ResInd]; + const D3DShaderResourceAttribs& Res = Resources[ResInd]; switch (Res.GetInputType()) { case D3D_SIT_CBUFFER: From 4bdeda44fd4c6de2bf3b4041231bbdc90e778734 Mon Sep 17 00:00:00 2001 From: assiduous Date: Thu, 23 Oct 2025 21:06:57 -0700 Subject: [PATCH 021/104] D3DShaderResourceAttribs: store constant buffer size --- .../include/D3DShaderResourceLoader.hpp | 24 +++---- .../include/ShaderResources.hpp | 69 +++++++++++-------- 2 files changed, 53 insertions(+), 40 deletions(-) diff --git a/Graphics/GraphicsEngineD3DBase/include/D3DShaderResourceLoader.hpp b/Graphics/GraphicsEngineD3DBase/include/D3DShaderResourceLoader.hpp index 52dda04c71..a6b15609e7 100644 --- a/Graphics/GraphicsEngineD3DBase/include/D3DShaderResourceLoader.hpp +++ b/Graphics/GraphicsEngineD3DBase/include/D3DShaderResourceLoader.hpp @@ -321,23 +321,23 @@ void LoadD3DShaderResources(TShaderReflection* pShaderReflection, case D3D_SIT_CBUFFER: { ShaderCodeBufferDescX BufferDesc; - if (LoadConstantBufferReflection) + if (auto* pBuffReflection = pShaderReflection->GetConstantBufferByName(Res.Name)) { - if (auto* pBuffReflection = pShaderReflection->GetConstantBufferByName(Res.Name)) - { - D3D_SHADER_BUFFER_DESC ShaderBuffDesc = {}; - pBuffReflection->GetDesc(&ShaderBuffDesc); - VERIFY_EXPR(SafeStrEqual(Res.Name, ShaderBuffDesc.Name)); - VERIFY_EXPR(ShaderBuffDesc.Type == D3D_CT_CBUFFER); + D3D_SHADER_BUFFER_DESC ShaderBuffDesc = {}; + pBuffReflection->GetDesc(&ShaderBuffDesc); + VERIFY_EXPR(SafeStrEqual(Res.Name, ShaderBuffDesc.Name)); + VERIFY_EXPR(ShaderBuffDesc.Type == D3D_CT_CBUFFER); - BufferDesc.Size = ShaderBuffDesc.Size; - LoadD3DShaderConstantBufferReflection(pBuffReflection, BufferDesc, ShaderBuffDesc.Variables); - } - else + BufferDesc.Size = ShaderBuffDesc.Size; + if (LoadConstantBufferReflection) { - UNEXPECTED("Failed to get constant buffer reflection information."); + LoadD3DShaderConstantBufferReflection(pBuffReflection, BufferDesc, ShaderBuffDesc.Variables); } } + else + { + UNEXPECTED("Failed to get constant buffer reflection information."); + } OnNewCB(Res, std::move(BufferDesc)); break; diff --git a/Graphics/GraphicsEngineD3DBase/include/ShaderResources.hpp b/Graphics/GraphicsEngineD3DBase/include/ShaderResources.hpp index 4fcd7db194..9fb108ab58 100644 --- a/Graphics/GraphicsEngineD3DBase/include/ShaderResources.hpp +++ b/Graphics/GraphicsEngineD3DBase/include/ShaderResources.hpp @@ -91,11 +91,11 @@ struct D3DShaderResourceAttribs // 4 4 24 // bit | 0 1 2 3 | 4 5 6 7 | 8 9 10 ... 31 | // | | | | - // | InputType | SRV Dim | SamplerOrTexSRVIdBits | - static constexpr const Uint32 ShaderInputTypeBits = 4; - static constexpr const Uint32 SRVDimBits = 4; - static constexpr const Uint32 SamplerOrTexSRVIdBits = 24; - static_assert(ShaderInputTypeBits + SRVDimBits + SamplerOrTexSRVIdBits == 32, "Attributes are better be packed into 32 bits"); + // | InputType | SRV Dim | ExtraDataBits | + static constexpr const Uint32 ShaderInputTypeBits = 4; + static constexpr const Uint32 SRVDimBits = 4; + static constexpr const Uint32 ExtraDataBits = 24; + static_assert(ShaderInputTypeBits + SRVDimBits + ExtraDataBits == 32, "Attributes are better be packed into 32 bits"); static_assert(D3D_SIT_UAV_RWSTRUCTURED_WITH_COUNTER < (1 << ShaderInputTypeBits), "Not enough bits to represent D3D_SHADER_INPUT_TYPE"); static_assert(D3D_SRV_DIMENSION_BUFFEREX < (1 << SRVDimBits), "Not enough bits to represent D3D_SRV_DIMENSION"); @@ -105,17 +105,17 @@ struct D3DShaderResourceAttribs // There originally was a problem when the type of InputType was D3D_SHADER_INPUT_TYPE: // the value of D3D_SIT_UAV_RWBYTEADDRESS (8) was interpreted as -8 (as the underlying enum type // is signed) causing errors -/*20.0*/ const Uint32 InputType : ShaderInputTypeBits; // Max value: D3D_SIT_UAV_RWSTRUCTURED_WITH_COUNTER == 11 -/*20.4*/ const Uint32 SRVDimension : SRVDimBits; // Max value: D3D_SRV_DIMENSION_BUFFEREX == 11 -/*21.0*/ Uint32 SamplerOrTexSRVId : SamplerOrTexSRVIdBits; // Max value: 2^24-1 +/*20.0*/ const Uint32 InputType : ShaderInputTypeBits; // Max value: D3D_SIT_UAV_RWSTRUCTURED_WITH_COUNTER == 11 +/*20.4*/ const Uint32 SRVDimension : SRVDimBits; // Max value: D3D_SRV_DIMENSION_BUFFEREX == 11 +/*21.0*/ Uint32 ExtraData : ExtraDataBits; // Max value: 2^24-1 /*24 */ // End of structure // clang-format on public: - static constexpr const Uint32 InvalidSamplerId = (1U << SamplerOrTexSRVIdBits) - 1U; + static constexpr const Uint32 InvalidSamplerId = (1U << ExtraDataBits) - 1U; static constexpr const Uint32 MaxSamplerId = InvalidSamplerId - 1; - static constexpr const Uint32 InvalidTexSRVId = (1U << SamplerOrTexSRVIdBits) - 1U; + static constexpr const Uint32 InvalidTexSRVId = (1U << ExtraDataBits) - 1U; static constexpr const auto InvalidBindPoint = std::numeric_limits::max(); @@ -127,20 +127,20 @@ struct D3DShaderResourceAttribs D3D_SRV_DIMENSION _SRVDimension, Uint32 _SamplerId) noexcept : // clang-format off - Name {_Name}, - BindPoint {_BindPoint}, - BindCount {_BindCount}, - Space {_Space}, - InputType {static_cast (_InputType) }, - SRVDimension {static_cast(_SRVDimension)}, - SamplerOrTexSRVId {_SamplerId} + Name {_Name}, + BindPoint {_BindPoint}, + BindCount {_BindCount}, + Space {_Space}, + InputType {static_cast (_InputType) }, + SRVDimension{static_cast(_SRVDimension)}, + ExtraData {_SamplerId} // clang-format on { #ifdef DILIGENT_DEBUG // clang-format off - VERIFY(_InputType < (1 << ShaderInputTypeBits), "Shader input type is out of expected range"); - VERIFY(_SRVDimension < (1 << SRVDimBits), "SRV dimensions is out of expected range"); - VERIFY(_SamplerId < (1 << SamplerOrTexSRVIdBits), "SamplerOrTexSRVId is out of representable range"); + VERIFY(_InputType < (1 << ShaderInputTypeBits), "Shader input type is out of expected range"); + VERIFY(_SRVDimension < (1 << SRVDimBits), "SRV dimensions is out of expected range"); + VERIFY(_SamplerId < (1 << ExtraDataBits), "SamplerId is out of representable range"); // clang-format on if (_InputType == D3D_SIT_TEXTURE && _SRVDimension != D3D_SRV_DIMENSION_BUFFER) @@ -178,7 +178,7 @@ struct D3DShaderResourceAttribs rhs.Space, rhs.GetInputType(), rhs.GetSRVDimension(), - rhs.SamplerOrTexSRVId + rhs.ExtraData } // clang-format on { @@ -210,7 +210,7 @@ struct D3DShaderResourceAttribs bool IsCombinedWithSampler() const { - return GetInputType() == D3D_SIT_TEXTURE && SamplerOrTexSRVId != InvalidSamplerId; + return GetInputType() == D3D_SIT_TEXTURE && ExtraData != InvalidSamplerId; } bool IsCombinedWithTexSRV() const @@ -230,12 +230,12 @@ struct D3DShaderResourceAttribs Space == Attribs.Space && InputType == Attribs.InputType && SRVDimension == Attribs.SRVDimension && - SamplerOrTexSRVId == Attribs.SamplerOrTexSRVId; + ExtraData == Attribs.ExtraData; } size_t GetHash() const { - return ComputeHash(BindPoint, BindCount, Space, InputType, SRVDimension, SamplerOrTexSRVId); + return ComputeHash(BindPoint, BindCount, Space, InputType, SRVDimension, ExtraData); } HLSLShaderResourceDesc GetHLSLResourceDesc() const @@ -253,7 +253,13 @@ struct D3DShaderResourceAttribs Uint32 GetCombinedSamplerId() const { VERIFY(GetInputType() == D3D_SIT_TEXTURE && GetSRVDimension() != D3D_SRV_DIMENSION_BUFFER, "Invalid input type: D3D_SIT_TEXTURE is expected"); - return SamplerOrTexSRVId; + return ExtraData; + } + + Uint32 GetConstantBufferSize() const + { + VERIFY(GetInputType() == D3D_SIT_CBUFFER, "Invalid input type: D3D_SIT_CBUFFER is expected"); + return ExtraData; } SHADER_RESOURCE_TYPE GetShaderResourceType() const; @@ -265,14 +271,20 @@ struct D3DShaderResourceAttribs void SetTexSRVId(Uint32 TexSRVId) { VERIFY(GetInputType() == D3D_SIT_SAMPLER, "Invalid input type: D3D_SIT_SAMPLER is expected"); - VERIFY(TexSRVId < (1 << SamplerOrTexSRVIdBits), "TexSRVId (", TexSRVId, ") is out of representable range"); - SamplerOrTexSRVId = TexSRVId; + VERIFY(TexSRVId < (1 << ExtraDataBits), "TexSRVId (", TexSRVId, ") is out of representable range"); + ExtraData = TexSRVId; } Uint32 GetCombinedTexSRVId() const { VERIFY(GetInputType() == D3D_SIT_SAMPLER, "Invalid input type: D3D_SIT_SAMPLER is expected"); - return SamplerOrTexSRVId; + return ExtraData; + } + + void SetConstantBufferSize(Uint32 Size) + { + VERIFY(GetInputType() == D3D_SIT_CBUFFER, "Invalid input type: D3D_SIT_CBUFFER is expected"); + ExtraData = Size; } }; static_assert(sizeof(D3DShaderResourceAttribs) == sizeof(void*) + sizeof(Uint32) * 4, "Unexpected sizeof(D3DShaderResourceAttribs)"); @@ -543,6 +555,7 @@ void ShaderResources::Initialize(TShaderReflection* pShaderReflection, { VERIFY_EXPR(CBAttribs.GetInputType() == D3D_SIT_CBUFFER); D3DShaderResourceAttribs* pNewCB = new (&GetCB(CurrCB++)) D3DShaderResourceAttribs{ResourceNamesPool, CBAttribs}; + pNewCB->SetConstantBufferSize(CBReflection.Size); NewResHandler.OnNewCB(*pNewCB); if (LoadConstantBufferReflection) CBReflections.emplace_back(std::move(CBReflection)); From 5443eb7a254bf08edee2d5e0e453cc3a6fd9dcaf Mon Sep 17 00:00:00 2001 From: assiduous Date: Thu, 23 Oct 2025 21:45:03 -0700 Subject: [PATCH 022/104] Add SHADER_VARIABLE_FLAG_INLINE_CONSTANTS flag with implementation in D3D12 (#672) --- .../src/GraphicsAccessories.cpp | 5 +- .../GraphicsEngine/interface/PipelineState.h | 14 ++- .../src/PipelineStateD3D12Impl.cpp | 23 +++- .../src/InlineConstantsTest.cpp | 119 ++++++++++++++++++ 4 files changed, 155 insertions(+), 6 deletions(-) diff --git a/Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp b/Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp index 9300fccf59..98aa9e5228 100644 --- a/Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp +++ b/Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp @@ -1843,7 +1843,7 @@ PIPELINE_RESOURCE_FLAGS GetValidPipelineResourceFlags(SHADER_RESOURCE_TYPE Resou PIPELINE_RESOURCE_FLAGS ShaderVariableFlagsToPipelineResourceFlags(SHADER_VARIABLE_FLAGS Flags) { - static_assert(SHADER_VARIABLE_FLAG_LAST == (1 << 3), "Please update the switch below to handle the new shader variable flags"); + static_assert(SHADER_VARIABLE_FLAG_LAST == (1 << 4), "Please update the switch below to handle the new shader variable flags"); switch (Flags) { case SHADER_VARIABLE_FLAG_NONE: @@ -1859,6 +1859,9 @@ PIPELINE_RESOURCE_FLAGS ShaderVariableFlagsToPipelineResourceFlags(SHADER_VARIAB case SHADER_VARIABLE_FLAG_NON_FILTERING_SAMPLER_WEBGPU: return PIPELINE_RESOURCE_FLAG_NONE; + case SHADER_VARIABLE_FLAG_INLINE_CONSTANTS: + return PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS; + default: UNEXPECTED("Unexpected shader variable flag"); return PIPELINE_RESOURCE_FLAG_NONE; diff --git a/Graphics/GraphicsEngine/interface/PipelineState.h b/Graphics/GraphicsEngine/interface/PipelineState.h index b7419a98dc..ee6666d3af 100644 --- a/Graphics/GraphicsEngine/interface/PipelineState.h +++ b/Graphics/GraphicsEngine/interface/PipelineState.h @@ -103,22 +103,30 @@ DILIGENT_TYPED_ENUM(SHADER_VARIABLE_FLAGS, Uint8) /// flag in the internal pipeline resource signature. SHADER_VARIABLE_FLAG_NO_DYNAMIC_BUFFERS = 1u << 0, + /// Indicates that the resource consists of inline constants + /// (also known as push constants in Vulkan or root constants in Direct3D12). + /// Applies to Diligent::SHADER_RESOURCE_TYPE_CONSTANT_BUFFER only. + /// + /// \remarks This flag directly translates to the Diligent::PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS + /// flag in the internal pipeline resource signature. + SHADER_VARIABLE_FLAG_INLINE_CONSTANTS = 1u << 1, + /// Indicates that the resource is an input attachment in general layout, which allows simultaneously /// reading from the resource through the input attachment and writing to it via color or depth-stencil /// attachment. /// /// \note This flag is only valid in Vulkan. - SHADER_VARIABLE_FLAG_GENERAL_INPUT_ATTACHMENT_VK = 1u << 1, + SHADER_VARIABLE_FLAG_GENERAL_INPUT_ATTACHMENT_VK = 1u << 2, /// Indicates that the resource is an unfilterable-float texture. /// /// \note This flag is only valid in WebGPU and ignored in other backends. - SHADER_VARIABLE_FLAG_UNFILTERABLE_FLOAT_TEXTURE_WEBGPU = 1u << 2, + SHADER_VARIABLE_FLAG_UNFILTERABLE_FLOAT_TEXTURE_WEBGPU = 1u << 3, /// Indicates that the resource is a non-filtering sampler. /// /// \note This flag is only valid in WebGPU and ignored in other backends. - SHADER_VARIABLE_FLAG_NON_FILTERING_SAMPLER_WEBGPU = 1u << 3, + SHADER_VARIABLE_FLAG_NON_FILTERING_SAMPLER_WEBGPU = 1u << 4, /// Special value that indicates the last flag in the enumeration. SHADER_VARIABLE_FLAG_LAST = SHADER_VARIABLE_FLAG_NON_FILTERING_SAMPLER_WEBGPU diff --git a/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp index 63f0bb8459..7e87e2bba1 100644 --- a/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2024 Diligent Graphics LLC + * Copyright 2019-2025 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -380,7 +380,26 @@ PipelineResourceSignatureDescWrapper PipelineStateD3D12Impl::GetDefaultResourceS const SHADER_RESOURCE_TYPE ResType = Attribs.GetShaderResourceType(); const PIPELINE_RESOURCE_FLAGS ResFlags = Attribs.GetPipelineResourceFlags() | ShaderVariableFlagsToPipelineResourceFlags(VarDesc.Flags); - SignDesc.AddResource(VarDesc.ShaderStages, Attribs.Name, Attribs.BindCount, ResType, VarDesc.Type, ResFlags); + + Uint32 ArraySize = Attribs.BindCount; + if (ResFlags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) + { + VERIFY_EXPR(ResType == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER); + VERIFY(ResFlags == PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS, "INLINE_CONSTANTS flag cannot be combined with other flags."); + if (Attribs.BindCount != 1) + { + LOG_ERROR_AND_THROW("Inline constants resource '", Attribs.Name, "' in shader '", pShader->GetDesc().Name, + "' can not be an array."); + } + const Uint32 NumConstants = Attribs.GetConstantBufferSize() / sizeof(Uint32); + if (NumConstants > 64) + { + LOG_ERROR_AND_THROW("Inline constants resource '", Attribs.Name, "' in shader '", pShader->GetDesc().Name, + "' has ", NumConstants, " constants. The maximum supported number of inline constants is 64."); + } + ArraySize = NumConstants; + } + SignDesc.AddResource(VarDesc.ShaderStages, Attribs.Name, ArraySize, ResType, VarDesc.Type, ResFlags); } else { diff --git a/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp b/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp index 5adfa494d1..e67be77b9c 100644 --- a/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp +++ b/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp @@ -165,6 +165,125 @@ InlineConstants::Resources InlineConstants::sm_Res; FastRandFloat InlineConstants::sm_Rnd{0, 0.f, 1.f}; +TEST_F(InlineConstants, ResourceLayout) +{ + GPUTestingEnvironment* pEnv = GPUTestingEnvironment::GetInstance(); + IRenderDevice* pDevice = pEnv->GetDevice(); + IDeviceContext* pContext = pEnv->GetDeviceContext(); + ISwapChain* pSwapChain = pEnv->GetSwapChain(); + if (pDevice->GetDeviceInfo().Type != RENDER_DEVICE_TYPE_D3D12) + { + GTEST_SKIP(); + } + + for (Uint32 pos_type = 0; pos_type < SHADER_RESOURCE_VARIABLE_TYPE_NUM_TYPES; ++pos_type) + { + for (Uint32 col_type = 0; col_type < SHADER_RESOURCE_VARIABLE_TYPE_NUM_TYPES; ++col_type) + { + const float ClearColor[] = {sm_Rnd(), sm_Rnd(), sm_Rnd(), sm_Rnd()}; + RenderDrawCommandReference(pSwapChain, ClearColor); + + SHADER_RESOURCE_VARIABLE_TYPE PosType = static_cast(pos_type); + SHADER_RESOURCE_VARIABLE_TYPE ColType = static_cast(col_type); + + GraphicsPipelineStateCreateInfoX PsoCI{"Inline constants test"}; + + PipelineResourceLayoutDescX ResLayoutDesc; + ResLayoutDesc + .AddVariable(SHADER_TYPE_VERTEX, "cbInlinePositions", PosType, SHADER_VARIABLE_FLAG_INLINE_CONSTANTS) + .AddVariable(SHADER_TYPE_VERTEX, "cbInlineColors", ColType, SHADER_VARIABLE_FLAG_INLINE_CONSTANTS); + + PsoCI + .AddRenderTarget(pSwapChain->GetDesc().ColorBufferFormat) + .SetPrimitiveTopology(PRIMITIVE_TOPOLOGY_TRIANGLE_LIST) + .AddShader(sm_Res.pVS) + .AddShader(sm_Res.pPS) + .SetResourceLayout(ResLayoutDesc); + PsoCI.GraphicsPipeline.DepthStencilDesc.DepthEnable = False; + + RefCntAutoPtr pPSO; + pDevice->CreateGraphicsPipelineState(PsoCI, &pPSO); + ASSERT_TRUE(pPSO); + + if (PosType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) + { + IShaderResourceVariable* pVar = pPSO->GetStaticVariableByName(SHADER_TYPE_VERTEX, "cbInlinePositions"); + ASSERT_TRUE(pVar); + pVar->SetInlineConstants(g_Positions, 0, kNumPosConstants); + } + + if (ColType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) + { + IShaderResourceVariable* pVar = pPSO->GetStaticVariableByName(SHADER_TYPE_VERTEX, "cbInlineColors"); + ASSERT_TRUE(pVar); + pVar->SetInlineConstants(g_Colors, 0, kNumColConstants); + } + + RefCntAutoPtr pSRB; + pPSO->CreateShaderResourceBinding(&pSRB, true); + ASSERT_TRUE(pSRB); + + IShaderResourceVariable* pPosVar = nullptr; + if (PosType != SHADER_RESOURCE_VARIABLE_TYPE_STATIC) + { + pPosVar = pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cbInlinePositions"); + ASSERT_TRUE(pPosVar); + } + + IShaderResourceVariable* pColVar = nullptr; + if (ColType != SHADER_RESOURCE_VARIABLE_TYPE_STATIC) + { + pColVar = pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cbInlineColors"); + ASSERT_TRUE(pColVar); + } + + ITextureView* pRTVs[] = {pSwapChain->GetCurrentBackBufferRTV()}; + pContext->SetRenderTargets(1, pRTVs, nullptr, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); + pContext->ClearRenderTarget(pRTVs[0], ClearColor, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); + + + if (pColVar != nullptr) + { + // Set first half of color constants before committing SRB + pColVar->SetInlineConstants(g_Colors, 0, kNumColConstants / 2); + } + + pContext->CommitShaderResources(pSRB, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); + + if (pColVar != nullptr) + { + // Set second half of color constants after committing SRB + pColVar->SetInlineConstants(g_Colors[0].Data() + kNumColConstants / 2, kNumColConstants / 2, kNumColConstants / 2); + } + + pContext->SetPipelineState(pPSO); + + if (pPosVar == nullptr) + { + // Draw both triangles as positions are static + pContext->Draw({6, DRAW_FLAG_VERIFY_ALL}); + } + else + { + // Draw first triangle + pPosVar->SetInlineConstants(g_Positions, 0, kNumPosConstants / 2); + pContext->Draw({3, DRAW_FLAG_VERIFY_ALL}); + + // Draw second triangle + pPosVar->SetInlineConstants(g_Positions[0].Data() + kNumPosConstants / 2, 0, kNumPosConstants / 2); + pContext->Draw({3, DRAW_FLAG_VERIFY_ALL}); + } + + Present(); + + std::cout << TestingEnvironment::GetCurrentTestStatusString() << ' ' + << " Pos " << GetShaderVariableTypeLiteralName(PosType) << ',' + << " Col " << GetShaderVariableTypeLiteralName(ColType) << std::endl; + } + } +} + + void InlineConstants::TestSignatures(Uint32 NumSignatures) { GPUTestingEnvironment* pEnv = GPUTestingEnvironment::GetInstance(); From f99e3ddd6157898d33e7b4efad68fa981c4d19b3 Mon Sep 17 00:00:00 2001 From: assiduous Date: Fri, 24 Oct 2025 07:15:59 -0700 Subject: [PATCH 023/104] Add validation for SHADER_VARIABLE_FLAG_INLINE_CONSTANTS flag --- Graphics/GraphicsEngine/src/PipelineStateBase.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Graphics/GraphicsEngine/src/PipelineStateBase.cpp b/Graphics/GraphicsEngine/src/PipelineStateBase.cpp index 7d495c55b5..376d4a54ca 100644 --- a/Graphics/GraphicsEngine/src/PipelineStateBase.cpp +++ b/Graphics/GraphicsEngine/src/PipelineStateBase.cpp @@ -478,6 +478,9 @@ void ValidatePipelineResourceLayoutDesc(const PipelineStateDesc& PSODesc, const if (Var.ShaderStages == SHADER_TYPE_UNKNOWN) LOG_PSO_ERROR_AND_THROW("ResourceLayout.Variables[", i, "].ShaderStages must not be SHADER_TYPE_UNKNOWN."); + if ((Var.Flags & SHADER_VARIABLE_FLAG_INLINE_CONSTANTS) != 0 && (Var.Flags != SHADER_VARIABLE_FLAG_INLINE_CONSTANTS)) + LOG_PSO_ERROR_AND_THROW("ResourceLayout.Variables[", i, "].Flags: INLINE_CONSTANTS flag cannot be combined with other flags."); + auto range = UniqueVariables.equal_range(Var.Name); for (auto it = range.first; it != range.second; ++it) { From 2f706ee826a38c125a5a0a8480a770e12a925708 Mon Sep 17 00:00:00 2001 From: assiduous Date: Sat, 25 Oct 2025 08:58:54 -0700 Subject: [PATCH 024/104] Add test for inline constants in render state cache --- .../src/InlineConstantsTest.cpp | 236 ++++++++++++++++++ 1 file changed, 236 insertions(+) diff --git a/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp b/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp index e67be77b9c..7e7c2db723 100644 --- a/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp +++ b/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp @@ -28,6 +28,7 @@ #include "gtest/gtest.h" +#include "RenderStateCache.hpp" #include "GraphicsTypesX.hpp" #include "FastRand.hpp" @@ -151,6 +152,9 @@ class InlineConstants : public ::testing::Test pContext->InvalidateState(); } + static void VerifyPSOFromCache(IPipelineState* pPSO, + IShaderResourceBinding* pSRB); + struct Resources { RefCntAutoPtr pVS; @@ -510,4 +514,236 @@ TEST_F(InlineConstants, TwoResourceSignatures) TestSignatures(2); } +constexpr Uint32 kCacheContentVersion = 7; + +RefCntAutoPtr CreateCache(IRenderDevice* pDevice, + bool HotReload, + bool OptimizeGLShaders, + IDataBlob* pCacheData = nullptr, + IShaderSourceInputStreamFactory* pShaderReloadFactory = nullptr) +{ + RenderStateCacheCreateInfo CacheCI{ + pDevice, + GPUTestingEnvironment::GetInstance()->GetArchiverFactory(), + RENDER_STATE_CACHE_LOG_LEVEL_VERBOSE, + RENDER_STATE_CACHE_FILE_HASH_MODE_BY_CONTENT, + HotReload, + }; + + RefCntAutoPtr pCache; + CreateRenderStateCache(CacheCI, &pCache); + + if (pCacheData != nullptr) + pCache->Load(pCacheData, kCacheContentVersion); + + return pCache; +} + + +void CreateShadersFromCache(IRenderStateCache* pCache, bool PresentInCache, IShader** ppVS, IShader** ppPS) +{ + GPUTestingEnvironment* pEnv = GPUTestingEnvironment::GetInstance(); + IRenderDevice* pDevice = pEnv->GetDevice(); + + ShaderCreateInfo ShaderCI; + ShaderCI.SourceLanguage = SHADER_SOURCE_LANGUAGE_HLSL; + ShaderCI.ShaderCompiler = pEnv->GetDefaultCompiler(ShaderCI.SourceLanguage); + + { + ShaderCI.Desc = {"Inline constants test", SHADER_TYPE_VERTEX, true}; + ShaderCI.EntryPoint = "main"; + ShaderCI.Source = HLSL::InlineConstantsTest_VS.c_str(); + if (pCache != nullptr) + { + EXPECT_EQ(pCache->CreateShader(ShaderCI, ppVS), PresentInCache); + } + else + { + pDevice->CreateShader(ShaderCI, ppVS); + EXPECT_EQ(PresentInCache, false); + } + } + + { + ShaderCI.Desc = {"Inline constants test", SHADER_TYPE_PIXEL, true}; + ShaderCI.EntryPoint = "main"; + ShaderCI.Source = HLSL::DrawTest_PS.c_str(); + if (pCache != nullptr) + { + EXPECT_EQ(pCache->CreateShader(ShaderCI, ppPS), PresentInCache); + } + else + { + pDevice->CreateShader(ShaderCI, ppPS); + EXPECT_EQ(PresentInCache, false); + } + } +} + +void CreatePSOFromCache(IRenderStateCache* pCache, bool PresentInCache, IShader* pVS, IShader* pPS, IPipelineState** ppPSO) +{ + GPUTestingEnvironment* pEnv = GPUTestingEnvironment::GetInstance(); + ISwapChain* pSwapChain = pEnv->GetSwapChain(); + + GraphicsPipelineStateCreateInfo PsoCI; + PsoCI.PSODesc.Name = "Render State Cache Test"; + + PsoCI.pVS = pVS; + PsoCI.pPS = pPS; + + PsoCI.GraphicsPipeline.NumRenderTargets = 1; + PsoCI.GraphicsPipeline.RTVFormats[0] = pSwapChain->GetDesc().ColorBufferFormat; + + PsoCI.GraphicsPipeline.DepthStencilDesc.DepthEnable = False; + + static ShaderResourceVariableDesc Vars[] = + { + {SHADER_TYPE_VERTEX, "cbInlinePositions", SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE, SHADER_VARIABLE_FLAG_INLINE_CONSTANTS}, + {SHADER_TYPE_VERTEX, "cbInlineColors", SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE, SHADER_VARIABLE_FLAG_INLINE_CONSTANTS}, + }; + PsoCI.PSODesc.ResourceLayout.Variables = Vars; + PsoCI.PSODesc.ResourceLayout.NumVariables = _countof(Vars); + + if (pCache != nullptr) + { + bool PSOFound = pCache->CreateGraphicsPipelineState(PsoCI, ppPSO); + EXPECT_EQ(PSOFound, PresentInCache); + } + else + { + EXPECT_FALSE(PresentInCache); + pEnv->GetDevice()->CreateGraphicsPipelineState(PsoCI, ppPSO); + ASSERT_NE(*ppPSO, nullptr); + } + + if (*ppPSO != nullptr && (*ppPSO)->GetStatus() == PIPELINE_STATE_STATUS_READY) + { + const PipelineStateDesc& Desc = (*ppPSO)->GetDesc(); + EXPECT_EQ(PsoCI.PSODesc, Desc); + } +} + +void InlineConstants::VerifyPSOFromCache(IPipelineState* pPSO, + IShaderResourceBinding* pSRB) +{ + GPUTestingEnvironment* pEnv = GPUTestingEnvironment::GetInstance(); + IDeviceContext* pContext = pEnv->GetDeviceContext(); + ISwapChain* pSwapChain = pEnv->GetSwapChain(); + + const float ClearColor[] = {sm_Rnd(), sm_Rnd(), sm_Rnd(), sm_Rnd()}; + RenderDrawCommandReference(pSwapChain, ClearColor); + + ITextureView* pRTVs[] = {pSwapChain->GetCurrentBackBufferRTV()}; + pContext->SetRenderTargets(1, pRTVs, nullptr, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); + pContext->ClearRenderTarget(pRTVs[0], ClearColor, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); + + RefCntAutoPtr _pSRB; + if (pSRB == nullptr) + { + pPSO->CreateShaderResourceBinding(&_pSRB, true); + pSRB = _pSRB; + } + + pContext->SetPipelineState(pPSO); + pContext->CommitShaderResources(pSRB, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); + + IShaderResourceVariable* pColVar = pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cbInlineColors"); + ASSERT_TRUE(pColVar); + pColVar->SetInlineConstants(g_Colors, 0, kNumColConstants); + + IShaderResourceVariable* pPosVar = pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cbInlinePositions"); + ASSERT_TRUE(pPosVar); + pPosVar->SetInlineConstants(g_Positions, 0, kNumPosConstants / 2); + pContext->Draw({3, DRAW_FLAG_VERIFY_ALL}); + + pPosVar->SetInlineConstants(g_Positions[0].Data() + kNumPosConstants / 2, 0, kNumPosConstants / 2); + pContext->Draw({3, DRAW_FLAG_VERIFY_ALL}); + + Present(); +} + +TEST_F(InlineConstants, RenderStateCache) +{ + GPUTestingEnvironment* pEnv = GPUTestingEnvironment::GetInstance(); + IRenderDevice* pDevice = pEnv->GetDevice(); + + if (pDevice->GetDeviceInfo().Type != RENDER_DEVICE_TYPE_D3D12) + { + GTEST_SKIP(); + } + + GPUTestingEnvironment::ScopedReset AutoReset; + + RefCntAutoPtr pShaderSourceFactory; + pDevice->GetEngineFactory()->CreateDefaultShaderSourceStreamFactory("shaders/RenderStateCache", &pShaderSourceFactory); + ASSERT_TRUE(pShaderSourceFactory); + + RefCntAutoPtr pUncachedVS, pUncachedPS; + CreateShadersFromCache(nullptr, false, &pUncachedVS, &pUncachedPS); + ASSERT_NE(pUncachedVS, nullptr); + ASSERT_NE(pUncachedPS, nullptr); + + RefCntAutoPtr pRefPSO; + CreatePSOFromCache(nullptr, false, pUncachedVS, pUncachedPS, &pRefPSO); + ASSERT_NE(pRefPSO, nullptr); + + RefCntAutoPtr pRefSRB; + pRefPSO->CreateShaderResourceBinding(&pRefSRB); + + RefCntAutoPtr pData; + for (Uint32 pass = 0; pass < 3; ++pass) + { + // 0: empty cache + // 1: loaded cache + // 2: reloaded cache (loaded -> stored -> loaded) + + RefCntAutoPtr pCache = CreateCache(pDevice, false, false, pData); + ASSERT_TRUE(pCache); + + RefCntAutoPtr pVS1, pPS1; + CreateShadersFromCache(pCache, pData != nullptr, &pVS1, &pPS1); + ASSERT_NE(pVS1, nullptr); + ASSERT_NE(pPS1, nullptr); + + RefCntAutoPtr pPSO; + CreatePSOFromCache(pCache, pData != nullptr, pVS1, pPS1, &pPSO); + ASSERT_NE(pPSO, nullptr); + ASSERT_EQ(pPSO->GetStatus(), PIPELINE_STATE_STATUS_READY); + EXPECT_TRUE(pRefPSO->IsCompatibleWith(pPSO)); + EXPECT_TRUE(pPSO->IsCompatibleWith(pRefPSO)); + + VerifyPSOFromCache(pPSO, nullptr); + VerifyPSOFromCache(pPSO, pRefSRB); + + { + RefCntAutoPtr pPSO2; + CreatePSOFromCache(pCache, true, pVS1, pPS1, &pPSO2); + EXPECT_EQ(pPSO, pPSO2); + } + + { + RefCntAutoPtr pPSO2; + + bool PresentInCache = pData != nullptr; +#if !DILIGENT_DEBUG + if (pDevice->GetDeviceInfo().IsD3DDevice()) + { + // For some reason, hash computation consistency depends on D3DCOMPILE_DEBUG flag and differs between debug and release builds + PresentInCache = true; + } +#endif + CreatePSOFromCache(pCache, PresentInCache, pUncachedVS, pUncachedPS, &pPSO2); + ASSERT_NE(pPSO2, nullptr); + ASSERT_EQ(pPSO2->GetStatus(), PIPELINE_STATE_STATUS_READY); + EXPECT_TRUE(pRefPSO->IsCompatibleWith(pPSO2)); + EXPECT_TRUE(pPSO2->IsCompatibleWith(pRefPSO)); + VerifyPSOFromCache(pPSO2, nullptr); + VerifyPSOFromCache(pPSO2, pRefSRB); + } + + pData.Release(); + pCache->WriteToBlob(pass == 0 ? kCacheContentVersion : ~0u, &pData); + } +} + } // namespace From aeeac036126c0a28c66cd251c3e17188a62ba1a7 Mon Sep 17 00:00:00 2001 From: assiduous Date: Sat, 25 Oct 2025 15:11:10 -0700 Subject: [PATCH 025/104] PipelineStateD3D11: handle inline constants flag (#672) --- .../src/PipelineStateD3D11Impl.cpp | 9 ++++++++- .../src/PipelineStateD3D12Impl.cpp | 14 +------------ .../include/ShaderResources.hpp | 20 +++++++++++++++++++ 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/Graphics/GraphicsEngineD3D11/src/PipelineStateD3D11Impl.cpp b/Graphics/GraphicsEngineD3D11/src/PipelineStateD3D11Impl.cpp index 8155c04617..bd42290a5f 100644 --- a/Graphics/GraphicsEngineD3D11/src/PipelineStateD3D11Impl.cpp +++ b/Graphics/GraphicsEngineD3D11/src/PipelineStateD3D11Impl.cpp @@ -91,7 +91,14 @@ PipelineResourceSignatureDescWrapper PipelineStateD3D11Impl::GetDefaultResourceS const SHADER_RESOURCE_TYPE ResType = Attribs.GetShaderResourceType(); const PIPELINE_RESOURCE_FLAGS ResFlags = Attribs.GetPipelineResourceFlags() | ShaderVariableFlagsToPipelineResourceFlags(VarDesc.Flags); - SignDesc.AddResource(VarDesc.ShaderStages, Attribs.Name, Attribs.BindCount, ResType, VarDesc.Type, ResFlags); + + Uint32 ArraySize = Attribs.BindCount; + if (ResFlags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) + { + VERIFY(ResFlags == PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS, "INLINE_CONSTANTS flag cannot be combined with other flags."); + ArraySize = Attribs.GetInlineConstantCountOrThrow(pShader->GetDesc().Name); + } + SignDesc.AddResource(VarDesc.ShaderStages, Attribs.Name, ArraySize, ResType, VarDesc.Type, ResFlags); } else { diff --git a/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp index 7e87e2bba1..5bf117a939 100644 --- a/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp @@ -384,20 +384,8 @@ PipelineResourceSignatureDescWrapper PipelineStateD3D12Impl::GetDefaultResourceS Uint32 ArraySize = Attribs.BindCount; if (ResFlags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) { - VERIFY_EXPR(ResType == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER); VERIFY(ResFlags == PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS, "INLINE_CONSTANTS flag cannot be combined with other flags."); - if (Attribs.BindCount != 1) - { - LOG_ERROR_AND_THROW("Inline constants resource '", Attribs.Name, "' in shader '", pShader->GetDesc().Name, - "' can not be an array."); - } - const Uint32 NumConstants = Attribs.GetConstantBufferSize() / sizeof(Uint32); - if (NumConstants > 64) - { - LOG_ERROR_AND_THROW("Inline constants resource '", Attribs.Name, "' in shader '", pShader->GetDesc().Name, - "' has ", NumConstants, " constants. The maximum supported number of inline constants is 64."); - } - ArraySize = NumConstants; + ArraySize = Attribs.GetInlineConstantCountOrThrow(pShader->GetDesc().Name); } SignDesc.AddResource(VarDesc.ShaderStages, Attribs.Name, ArraySize, ResType, VarDesc.Type, ResFlags); } diff --git a/Graphics/GraphicsEngineD3DBase/include/ShaderResources.hpp b/Graphics/GraphicsEngineD3DBase/include/ShaderResources.hpp index 9fb108ab58..0daab12f72 100644 --- a/Graphics/GraphicsEngineD3DBase/include/ShaderResources.hpp +++ b/Graphics/GraphicsEngineD3DBase/include/ShaderResources.hpp @@ -262,6 +262,26 @@ struct D3DShaderResourceAttribs return ExtraData; } + Uint32 GetInlineConstantCountOrThrow(const char* ShaderName) const + { + VERIFY_EXPR(GetShaderResourceType() == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER); + if (BindCount != 1) + { + LOG_ERROR_AND_THROW("Inline constants resource '", Name, "' in shader '", ShaderName, "' can not be an array."); + } + const Uint32 NumConstants = GetConstantBufferSize() / sizeof(Uint32); + + constexpr Uint32 kMaxInlineConstants = 64; + if (NumConstants > kMaxInlineConstants) + { + LOG_ERROR_AND_THROW("Inline constants resource '", Name, "' in shader '", ShaderName, "' has ", + NumConstants, " constants. The maximum supported number of inline constants is ", + kMaxInlineConstants, '.'); + } + + return NumConstants; + } + SHADER_RESOURCE_TYPE GetShaderResourceType() const; PIPELINE_RESOURCE_FLAGS GetPipelineResourceFlags() const; From 73be86c26bd27a1450bcdd8b4b9839cf2253b5b2 Mon Sep 17 00:00:00 2001 From: assiduous Date: Sat, 25 Oct 2025 19:52:00 -0700 Subject: [PATCH 026/104] Inline Constants test: use constants in PS (#672) --- .../src/InlineConstantsTest.cpp | 88 +++++++++++-------- 1 file changed, 53 insertions(+), 35 deletions(-) diff --git a/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp b/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp index 7e7c2db723..5adcf7a9b8 100644 --- a/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp +++ b/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp @@ -45,9 +45,6 @@ void RenderDrawCommandReference(ISwapChain* pSwapChain, const float* pClearColor using namespace Diligent; using namespace Diligent::Testing; -#include "InlineShaders/DrawCommandTestHLSL.h" - - namespace { @@ -80,8 +77,31 @@ void main(uint VertexId : SV_VertexId, } )"}; +const std::string InlineConstantsTest_PS{ + R"( +struct PSInput +{ + float4 Pos : SV_POSITION; + float3 Color : COLOR; +}; + +cbuffer cbInlineColors +{ + float4 g_Colors[3]; } +float4 main(in PSInput PSIn) : SV_Target +{ + float3 Color = PSIn.Color.rgb; + Color.r *= g_Colors[0].a; + Color.g *= g_Colors[1].a; + Color.b *= g_Colors[2].a; + return float4(Color, 1.0); +} +)"}; + +} // namespace HLSL + float4 g_Positions[] = { float4{-1.0f, -0.5f, 0.f, 1.f}, float4{-0.5f, +0.5f, 0.f, 1.f}, @@ -109,6 +129,11 @@ class InlineConstants : public ::testing::Test GPUTestingEnvironment* pEnv = GPUTestingEnvironment::GetInstance(); IRenderDevice* pDevice = pEnv->GetDevice(); + if (pDevice->GetDeviceInfo().Type != RENDER_DEVICE_TYPE_D3D12) + { + GTEST_SKIP(); + } + ShaderCreateInfo ShaderCI; ShaderCI.SourceLanguage = SHADER_SOURCE_LANGUAGE_HLSL; ShaderCI.ShaderCompiler = pEnv->GetDefaultCompiler(ShaderCI.SourceLanguage); @@ -124,7 +149,7 @@ class InlineConstants : public ::testing::Test { ShaderCI.Desc = {"Inline constants test", SHADER_TYPE_PIXEL, true}; ShaderCI.EntryPoint = "main"; - ShaderCI.Source = HLSL::DrawTest_PS.c_str(); + ShaderCI.Source = HLSL::InlineConstantsTest_PS.c_str(); pDevice->CreateShader(ShaderCI, &sm_Res.pPS); ASSERT_NE(sm_Res.pPS, nullptr); } @@ -175,10 +200,6 @@ TEST_F(InlineConstants, ResourceLayout) IRenderDevice* pDevice = pEnv->GetDevice(); IDeviceContext* pContext = pEnv->GetDeviceContext(); ISwapChain* pSwapChain = pEnv->GetSwapChain(); - if (pDevice->GetDeviceInfo().Type != RENDER_DEVICE_TYPE_D3D12) - { - GTEST_SKIP(); - } for (Uint32 pos_type = 0; pos_type < SHADER_RESOURCE_VARIABLE_TYPE_NUM_TYPES; ++pos_type) { @@ -195,7 +216,7 @@ TEST_F(InlineConstants, ResourceLayout) PipelineResourceLayoutDescX ResLayoutDesc; ResLayoutDesc .AddVariable(SHADER_TYPE_VERTEX, "cbInlinePositions", PosType, SHADER_VARIABLE_FLAG_INLINE_CONSTANTS) - .AddVariable(SHADER_TYPE_VERTEX, "cbInlineColors", ColType, SHADER_VARIABLE_FLAG_INLINE_CONSTANTS); + .AddVariable(SHADER_TYPE_VS_PS, "cbInlineColors", ColType, SHADER_VARIABLE_FLAG_INLINE_CONSTANTS); PsoCI .AddRenderTarget(pSwapChain->GetDesc().ColorBufferFormat) @@ -234,11 +255,14 @@ TEST_F(InlineConstants, ResourceLayout) ASSERT_TRUE(pPosVar); } - IShaderResourceVariable* pColVar = nullptr; + IShaderResourceVariable* pColVarVS = nullptr; + IShaderResourceVariable* pColVarPS = nullptr; if (ColType != SHADER_RESOURCE_VARIABLE_TYPE_STATIC) { - pColVar = pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cbInlineColors"); - ASSERT_TRUE(pColVar); + pColVarVS = pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cbInlineColors"); + ASSERT_TRUE(pColVarVS); + pColVarPS = pSRB->GetVariableByName(SHADER_TYPE_PIXEL, "cbInlineColors"); + ASSERT_TRUE(pColVarPS); } ITextureView* pRTVs[] = {pSwapChain->GetCurrentBackBufferRTV()}; @@ -246,18 +270,18 @@ TEST_F(InlineConstants, ResourceLayout) pContext->ClearRenderTarget(pRTVs[0], ClearColor, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); - if (pColVar != nullptr) + if (pColVarVS != nullptr) { // Set first half of color constants before committing SRB - pColVar->SetInlineConstants(g_Colors, 0, kNumColConstants / 2); + pColVarVS->SetInlineConstants(g_Colors, 0, kNumColConstants / 2); } pContext->CommitShaderResources(pSRB, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); - if (pColVar != nullptr) + if (pColVarPS != nullptr) { // Set second half of color constants after committing SRB - pColVar->SetInlineConstants(g_Colors[0].Data() + kNumColConstants / 2, kNumColConstants / 2, kNumColConstants / 2); + pColVarPS->SetInlineConstants(g_Colors[0].Data() + kNumColConstants / 2, kNumColConstants / 2, kNumColConstants / 2); } pContext->SetPipelineState(pPSO); @@ -294,10 +318,6 @@ void InlineConstants::TestSignatures(Uint32 NumSignatures) IRenderDevice* pDevice = pEnv->GetDevice(); IDeviceContext* pContext = pEnv->GetDeviceContext(); ISwapChain* pSwapChain = pEnv->GetSwapChain(); - if (pDevice->GetDeviceInfo().Type != RENDER_DEVICE_TYPE_D3D12) - { - GTEST_SKIP(); - } RefCntAutoPtr pConstBuffer = pEnv->CreateBuffer({"InlineConstants - dummy const buffer", 256, BIND_UNIFORM_BUFFER}); ASSERT_TRUE(pConstBuffer); @@ -347,7 +367,7 @@ void InlineConstants::TestSignatures(Uint32 NumSignatures) .AddResource(SHADER_TYPE_VERTEX, "tex1_mut", SHADER_RESOURCE_TYPE_TEXTURE_SRV, SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE) .AddResource(SHADER_TYPE_VERTEX, "tex1_dyn", SHADER_RESOURCE_TYPE_TEXTURE_SRV, SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) - .AddResource(SHADER_TYPE_VERTEX, "cbInlineColors", kNumColConstants, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, ColType, PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) + .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_mut", 1u, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE) @@ -445,11 +465,14 @@ void InlineConstants::TestSignatures(Uint32 NumSignatures) ASSERT_TRUE(pPosVar); } - IShaderResourceVariable* pColVar = nullptr; + IShaderResourceVariable* pColVarVS = nullptr; + IShaderResourceVariable* pColVarPS = nullptr; if (ColType != SHADER_RESOURCE_VARIABLE_TYPE_STATIC) { - pColVar = pColSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cbInlineColors"); - ASSERT_TRUE(pColVar); + pColVarVS = pColSRB->GetVariableByName(SHADER_TYPE_VERTEX, "cbInlineColors"); + ASSERT_TRUE(pColVarVS); + pColVarPS = pColSRB->GetVariableByName(SHADER_TYPE_PIXEL, "cbInlineColors"); + ASSERT_TRUE(pColVarPS); } ITextureView* pRTVs[] = {pSwapChain->GetCurrentBackBufferRTV()}; @@ -457,10 +480,10 @@ void InlineConstants::TestSignatures(Uint32 NumSignatures) pContext->ClearRenderTarget(pRTVs[0], ClearColor, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); - if (pColVar != nullptr) + if (pColVarVS != nullptr) { // Set first half of color constants before committing SRB - pColVar->SetInlineConstants(g_Colors, 0, kNumColConstants / 2); + pColVarVS->SetInlineConstants(g_Colors, 0, kNumColConstants / 2); } pContext->CommitShaderResources(pPosSRB, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); @@ -471,10 +494,10 @@ void InlineConstants::TestSignatures(Uint32 NumSignatures) pContext->CommitShaderResources(pColSRB, RESOURCE_STATE_TRANSITION_MODE_VERIFY); } - if (pColVar != nullptr) + if (pColVarPS != nullptr) { // Set second half of color constants after committing SRB - pColVar->SetInlineConstants(g_Colors[0].Data() + kNumColConstants / 2, kNumColConstants / 2, kNumColConstants / 2); + pColVarPS->SetInlineConstants(g_Colors[0].Data() + kNumColConstants / 2, kNumColConstants / 2, kNumColConstants / 2); } pContext->SetPipelineState(pPSO); @@ -567,7 +590,7 @@ void CreateShadersFromCache(IRenderStateCache* pCache, bool PresentInCache, ISha { ShaderCI.Desc = {"Inline constants test", SHADER_TYPE_PIXEL, true}; ShaderCI.EntryPoint = "main"; - ShaderCI.Source = HLSL::DrawTest_PS.c_str(); + ShaderCI.Source = HLSL::InlineConstantsTest_PS.c_str(); if (pCache != nullptr) { EXPECT_EQ(pCache->CreateShader(ShaderCI, ppPS), PresentInCache); @@ -599,7 +622,7 @@ void CreatePSOFromCache(IRenderStateCache* pCache, bool PresentInCache, IShader* static ShaderResourceVariableDesc Vars[] = { {SHADER_TYPE_VERTEX, "cbInlinePositions", SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE, SHADER_VARIABLE_FLAG_INLINE_CONSTANTS}, - {SHADER_TYPE_VERTEX, "cbInlineColors", SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE, SHADER_VARIABLE_FLAG_INLINE_CONSTANTS}, + {SHADER_TYPE_VS_PS, "cbInlineColors", SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE, SHADER_VARIABLE_FLAG_INLINE_CONSTANTS}, }; PsoCI.PSODesc.ResourceLayout.Variables = Vars; PsoCI.PSODesc.ResourceLayout.NumVariables = _countof(Vars); @@ -667,11 +690,6 @@ TEST_F(InlineConstants, RenderStateCache) GPUTestingEnvironment* pEnv = GPUTestingEnvironment::GetInstance(); IRenderDevice* pDevice = pEnv->GetDevice(); - if (pDevice->GetDeviceInfo().Type != RENDER_DEVICE_TYPE_D3D12) - { - GTEST_SKIP(); - } - GPUTestingEnvironment::ScopedReset AutoReset; RefCntAutoPtr pShaderSourceFactory; From 43fecc0d6722756b471609b25b34914ffb85b2c8 Mon Sep 17 00:00:00 2001 From: assiduous Date: Sun, 26 Oct 2025 14:23:19 -0700 Subject: [PATCH 027/104] ShaderResourceCacheD3D11: enable inline constants (#672) --- .../PipelineResourceSignatureD3D11Impl.hpp | 12 +- .../include/ShaderResourceCacheD3D11.hpp | 110 +++++++++++++++++- .../PipelineResourceSignatureD3D11Impl.cpp | 65 +++++++++-- .../src/ShaderResourceCacheD3D11.cpp | 72 +++++++++++- .../src/ShaderVariableManagerD3D11.cpp | 12 +- 5 files changed, 253 insertions(+), 18 deletions(-) diff --git a/Graphics/GraphicsEngineD3D11/include/PipelineResourceSignatureD3D11Impl.hpp b/Graphics/GraphicsEngineD3D11/include/PipelineResourceSignatureD3D11Impl.hpp index 422c2cb0ee..0f0e1d15aa 100644 --- a/Graphics/GraphicsEngineD3D11/include/PipelineResourceSignatureD3D11Impl.hpp +++ b/Graphics/GraphicsEngineD3D11/include/PipelineResourceSignatureD3D11Impl.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2024 Diligent Graphics LLC + * Copyright 2019-2025 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -60,6 +60,13 @@ struct ImmutableSamplerAttribsD3D11 }; ASSERT_SIZEOF(ImmutableSamplerAttribsD3D11, 12, "The struct is used in serialization and must be tightly packed"); +struct InlineConstantBufferAttribsD3D11 +{ + D3D11ResourceBindPoints BindPoints; + Uint32 NumConstants = 0; + RefCntAutoPtr pBuffer; +}; + struct PipelineResourceSignatureInternalDataD3D11 : PipelineResourceSignatureInternalData { @@ -128,6 +135,9 @@ class PipelineResourceSignatureD3D11Impl final : public PipelineResourceSignatur // Indicates which constant buffer slots are allowed to contain buffers with dynamic offsets. std::array m_DynamicCBSlotsMask{}; static_assert(sizeof(m_DynamicCBSlotsMask[0]) * 8 >= D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT, "Not enough bits for all dynamic buffer slots"); + + Uint32 m_NumInlineConstantBuffers = 0; + std::unique_ptr m_InlineConstantBuffers; }; } // namespace Diligent diff --git a/Graphics/GraphicsEngineD3D11/include/ShaderResourceCacheD3D11.hpp b/Graphics/GraphicsEngineD3D11/include/ShaderResourceCacheD3D11.hpp index d3930b28eb..9c1651e23a 100644 --- a/Graphics/GraphicsEngineD3D11/include/ShaderResourceCacheD3D11.hpp +++ b/Graphics/GraphicsEngineD3D11/include/ShaderResourceCacheD3D11.hpp @@ -49,13 +49,15 @@ namespace Diligent { +struct InlineConstantBufferAttribsD3D11; + /// The class implements a cache that holds resources bound to all shader stages. // All resources are stored in the continuous memory using the following layout: // -// | CachedCB | ID3D11Buffer* || CachedResource | ID3D11ShaderResourceView* || CachedSampler | ID3D11SamplerState* || CachedResource | ID3D11UnorderedAccessView*|| -// |--------------------------|------------------------||--------------------------|---------------------------||------------------------------|-----------------------------||-------------------------|---------------------------|| -// | 0 | 1 | ... | CBCount-1 | 0 | 1 | ...| CBCount-1 || 0 | 1 | ... | SRVCount-1 | 0 | 1 | ... | SRVCount-1 || 0 | 1 | ... | SamplerCount-1 | 0 | 1 | ...| SamplerCount-1 ||0 | 1 | ... | UAVCount-1 | 0 | 1 | ... | UAVCount-1 || -// -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// | CachedCB | ID3D11Buffer* || CachedResource | ID3D11ShaderResourceView* || CachedSampler | ID3D11SamplerState* || CachedResource | ID3D11UnorderedAccessView*| Inline Constants | +// |--------------------------|------------------------||--------------------------|---------------------------||------------------------------|-----------------------------||-------------------------|---------------------------|------------------| +// | 0 | 1 | ... | CBCount-1 | 0 | 1 | ...| CBCount-1 || 0 | 1 | ... | SRVCount-1 | 0 | 1 | ... | SRVCount-1 || 0 | 1 | ... | SamplerCount-1 | 0 | 1 | ...| SamplerCount-1 ||0 | 1 | ... | UAVCount-1 | 0 | 1 | ... | UAVCount-1 | 0 | 1 | ... | +// --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // class ShaderResourceCacheD3D11 : public ShaderResourceCacheBase { @@ -89,6 +91,9 @@ class ShaderResourceCacheD3D11 : public ShaderResourceCacheBase // Dynamic offset in bytes Uint32 DynamicOffset = 0; + // Pointer to inline constant data + void* pInlineConstantData = nullptr; + explicit operator bool() const noexcept { return pBuff; @@ -138,6 +143,17 @@ class ShaderResourceCacheD3D11 : public ShaderResourceCacheBase return pBuff && RangeSize != 0 && RangeSize < pBuff->GetDesc().Size; } + void SetInlineConstants(const void* pSrcConstants, Uint32 FirstConstant, Uint32 NumConstants) + { + VERIFY(pSrcConstants != nullptr, "Source constant data pointer is null"); + VERIFY(FirstConstant + NumConstants <= RangeSize / sizeof(Uint32), + "Too many constants (", FirstConstant + NumConstants, ") for the allocated space (", RangeSize / sizeof(Uint32), ")"); + VERIFY(pInlineConstantData != nullptr, "Inline constant data pointer is null"); + memcpy(reinterpret_cast(pInlineConstantData) + FirstConstant * sizeof(Uint32), + pSrcConstants, + NumConstants * sizeof(Uint32)); + } + // Returns ID3D11Buffer template typename CachedResourceTraits::D3D11ResourceType* GetD3D11Resource(); @@ -247,7 +263,9 @@ class ShaderResourceCacheD3D11 : public ShaderResourceCacheBase // Static resource cache does not allow dynamic buffers (pDynamicCBSlotsMask == null). void Initialize(const D3D11ShaderResourceCounters& ResCount, IMemoryAllocator& MemAllocator, - const std::array* pDynamicCBSlotsMask); + const std::array* pDynamicCBSlotsMask, + const InlineConstantBufferAttribsD3D11* pInlineCBs, + Uint32 NumInlineCBs); template inline void SetResource(const D3D11ResourceBindPoints& BindPoints, @@ -256,6 +274,10 @@ class ShaderResourceCacheD3D11 : public ShaderResourceCacheBase __forceinline void SetDynamicCBOffset(const D3D11ResourceBindPoints& BindPoints, Uint32 DynamicOffset); + __forceinline void SetInlineConstants(const D3D11ResourceBindPoints& BindPoints, + const void* pConstants, + Uint32 FirstConstant, + Uint32 NumConstants); template __forceinline const typename CachedResourceTraits::CachedResourceType& GetResource(const D3D11ResourceBindPoints& BindPoints) const @@ -289,6 +311,8 @@ class ShaderResourceCacheD3D11 : public ShaderResourceCacheBase template bool CopyResource(const ShaderResourceCacheD3D11& SrcCache, const D3D11ResourceBindPoints& BindPoints); + inline void CopyInlineConstants(const ShaderResourceCacheD3D11& SrcCache, const D3D11ResourceBindPoints& BindPoints, Uint32 NumConstants); + template __forceinline bool IsResourceBound(const D3D11ResourceBindPoints& BindPoints) const { @@ -402,6 +426,11 @@ class ShaderResourceCacheD3D11 : public ShaderResourceCacheBase #endif private: + void InitInlineConstantBuffer(const D3D11ResourceBindPoints& BindPoints, + RefCntAutoPtr pBuffer, + Uint32 NumConstants, + void* pInlineConstantData); + template __forceinline Uint32 GetResourceDataOffset(Uint32 ShaderInd) const; @@ -730,6 +759,35 @@ __forceinline void ShaderResourceCacheD3D11::SetDynamicCBOffset(const D3D11Resou } } +__forceinline void ShaderResourceCacheD3D11::SetInlineConstants(const D3D11ResourceBindPoints& BindPoints, + const void* pConstants, + Uint32 FirstConstant, + Uint32 NumConstants) +{ + SHADER_TYPE ActiveStages = BindPoints.GetActiveStages(); + VERIFY_EXPR(ActiveStages != SHADER_TYPE_UNKNOWN); + const Uint32 ShaderInd0 = ExtractFirstShaderStageIndex(ActiveStages); + const Uint32 Binding0 = BindPoints[ShaderInd0]; + VERIFY(Binding0 < GetResourceCount(ShaderInd0), "Cache offset is out of range"); + const auto ResArrays0 = GetResourceArrays(ShaderInd0); + ResArrays0.first[Binding0].SetInlineConstants(pConstants, FirstConstant, NumConstants); + +#ifdef DILIGENT_DEBUG + while (ActiveStages != SHADER_TYPE_UNKNOWN) + { + const Uint32 ShaderInd = ExtractFirstShaderStageIndex(ActiveStages); + const Uint32 Binding = BindPoints[ShaderInd]; + VERIFY(Binding < GetResourceCount(ShaderInd), "Cache offset is out of range"); + + const auto ResArrays = GetResourceArrays(ShaderInd); + VERIFY(ResArrays.first[Binding].pInlineConstantData == ResArrays0.first[Binding0].pInlineConstantData, + "All active shader stages must share the same inline constant data"); + VERIFY(ResArrays.first[Binding] == ResArrays0.first[Binding0], + "All active shader stages must share the same inline constant data attributes"); + } +#endif +} + template <> inline void ShaderResourceCacheD3D11::UpdateDynamicCBOffsetFlag( const ShaderResourceCacheD3D11::CachedCB& CB, @@ -787,6 +845,48 @@ bool ShaderResourceCacheD3D11::CopyResource(const ShaderResourceCacheD3D11& SrcC return IsBound; } +inline void ShaderResourceCacheD3D11::CopyInlineConstants(const ShaderResourceCacheD3D11& SrcCache, const D3D11ResourceBindPoints& BindPoints, Uint32 NumConstants) +{ + SHADER_TYPE ActiveStages = BindPoints.GetActiveStages(); + VERIFY_EXPR(ActiveStages != SHADER_TYPE_UNKNOWN); + + const Int32 ShaderInd0 = ExtractFirstShaderStageIndex(ActiveStages); + + const auto SrcResArrays0 = SrcCache.GetConstResourceArrays(ShaderInd0); + const auto DstResArrays0 = GetResourceArrays(ShaderInd0); + + const Uint32 Binding0 = BindPoints[ShaderInd0]; + VERIFY(Binding0 < GetResourceCount(ShaderInd0), "Destination index is out of range"); + VERIFY(Binding0 < SrcCache.GetResourceCount(ShaderInd0), "Source index is out of range"); + VERIFY(SrcResArrays0.first[Binding0].pInlineConstantData != nullptr, "Source inline constant data is null"); + VERIFY(DstResArrays0.first[Binding0].pInlineConstantData != nullptr, "Destination inline constant data is null"); + VERIFY(SrcResArrays0.first[Binding0].RangeSize == NumConstants * sizeof(Uint32), "Source inline constant buffer size mismatch"); + VERIFY(DstResArrays0.first[Binding0].RangeSize == NumConstants * sizeof(Uint32), "Destination inline constant buffer size mismatch"); + memcpy(DstResArrays0.first[Binding0].pInlineConstantData, + SrcResArrays0.first[Binding0].pInlineConstantData, + NumConstants * sizeof(Uint32)); + +#ifdef DILIGENT_DEBUG + while (ActiveStages != SHADER_TYPE_UNKNOWN) + { + const Int32 ShaderInd = ExtractFirstShaderStageIndex(ActiveStages); + + const auto SrcResArrays = SrcCache.GetConstResourceArrays(ShaderInd); + const auto DstResArrays = GetResourceArrays(ShaderInd); + + const Uint32 Binding = BindPoints[ShaderInd]; + VERIFY(Binding < GetResourceCount(ShaderInd), "Index is out of range"); + VERIFY(Binding < SrcCache.GetResourceCount(ShaderInd), "Index is out of range"); + VERIFY(SrcResArrays0.first[Binding0] == SrcResArrays.first[Binding], "All shader stages must share the same inline constant data attributes"); + VERIFY(DstResArrays0.first[Binding0] == DstResArrays.first[Binding], "All shader stages must share the same inline constant data attributes"); + VERIFY(SrcResArrays0.first[Binding0].pInlineConstantData == SrcResArrays.first[Binding].pInlineConstantData, + "All active shader stages must share the same inline constant data"); + VERIFY(DstResArrays0.first[Binding0].pInlineConstantData == DstResArrays.first[Binding].pInlineConstantData, + "All active shader stages must share the same inline constant data"); + } +#endif +} + template <> __forceinline ID3D11Buffer* ShaderResourceCacheD3D11::CachedCB::GetD3D11Resource() { diff --git a/Graphics/GraphicsEngineD3D11/src/PipelineResourceSignatureD3D11Impl.cpp b/Graphics/GraphicsEngineD3D11/src/PipelineResourceSignatureD3D11Impl.cpp index 1736bc7ceb..419691e6a4 100644 --- a/Graphics/GraphicsEngineD3D11/src/PipelineResourceSignatureD3D11Impl.cpp +++ b/Graphics/GraphicsEngineD3D11/src/PipelineResourceSignatureD3D11Impl.cpp @@ -93,7 +93,7 @@ D3D11_RESOURCE_RANGE PipelineResourceSignatureD3D11Impl::ShaderResourceTypeToRan static_assert(SHADER_RESOURCE_TYPE_LAST == 8, "Please update the switch below to handle the new shader resource type"); switch (Type) { - // clang-format off + // clang-format off case SHADER_RESOURCE_TYPE_CONSTANT_BUFFER: return D3D11_RESOURCE_RANGE_CBV; case SHADER_RESOURCE_TYPE_TEXTURE_SRV: return D3D11_RESOURCE_RANGE_SRV; case SHADER_RESOURCE_TYPE_BUFFER_SRV: return D3D11_RESOURCE_RANGE_SRV; @@ -153,6 +153,17 @@ void PipelineResourceSignatureD3D11Impl::CreateLayout(const bool IsSerialized) DstImtblSampAttribs.ArraySize = std::max(DstImtblSampAttribs.ArraySize, ResDesc.ArraySize); } } + + if (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) + { + VERIFY(ResDesc.ResourceType == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, "Only constant buffers can have INLINE_CONSTANTS flag"); + ++m_NumInlineConstantBuffers; + } + } + + if (m_NumInlineConstantBuffers > 0) + { + m_InlineConstantBuffers = std::make_unique(m_NumInlineConstantBuffers); } // Allocate registers for immutable samplers first @@ -174,6 +185,7 @@ void PipelineResourceSignatureD3D11Impl::CreateLayout(const bool IsSerialized) D3D11ShaderResourceCounters StaticResCounters; + Uint32 InlineConstantBufferIdx = 0; for (Uint32 i = 0; i < m_Desc.NumResources; ++i) { const PipelineResourceDesc& ResDesc = GetResourceDesc(i); @@ -198,7 +210,12 @@ void PipelineResourceSignatureD3D11Impl::CreateLayout(const bool IsSerialized) { const D3D11_RESOURCE_RANGE Range = ShaderResourceTypeToRange(ResDesc.ResourceType); - AllocBindPoints(m_ResourceCounters, BindPoints, ResDesc.ShaderStages, ResDesc.ArraySize, Range); + VERIFY((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) == 0 || ResDesc.ResourceType == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, + "Only constant buffers can have inline constants flag"); + const Uint32 ArraySize = (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0 ? + 1 : // For inline constants, count only one resource as the ArraySize is the number of 4-byte constants. + ResDesc.ArraySize; + AllocBindPoints(m_ResourceCounters, BindPoints, ResDesc.ShaderStages, ArraySize, Range); if (ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) { // Since resources in the static cache are indexed by the same bindings, we need to @@ -219,13 +236,33 @@ void PipelineResourceSignatureD3D11Impl::CreateLayout(const bool IsSerialized) { const Int32 ShaderInd = ExtractFirstShaderStageIndex(ShaderStages); const Uint16 BindPoint = Uint16{BindPoints[ShaderInd]}; - for (Uint32 elem = 0; elem < ResDesc.ArraySize; ++elem) + for (Uint32 elem = 0; elem < ArraySize; ++elem) { VERIFY_EXPR(BindPoint + elem < Uint32{sizeof(m_DynamicCBSlotsMask[0]) * 8}); m_DynamicCBSlotsMask[ShaderInd] |= 1u << (BindPoint + elem); } } } + + if (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) + { + VERIFY(ResDesc.ResourceType == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, "Only constant buffers can have INLINE_CONSTANTS flag"); + InlineConstantBufferAttribsD3D11& InlineCBAttribs{m_InlineConstantBuffers[InlineConstantBufferIdx++]}; + InlineCBAttribs.BindPoints = BindPoints; + InlineCBAttribs.NumConstants = ResDesc.ArraySize; + if (m_pDevice) + { + std::string Name = m_Desc.Name; + Name += " - "; + Name += ResDesc.Name; + BufferDesc CBDesc{Name.c_str(), ResDesc.ArraySize * sizeof(Uint32), BIND_UNIFORM_BUFFER, USAGE_DYNAMIC, CPU_ACCESS_WRITE}; + + RefCntAutoPtr pBuffer; + m_pDevice->CreateBuffer(CBDesc, nullptr, &pBuffer); + VERIFY_EXPR(pBuffer); + InlineCBAttribs.pBuffer = pBuffer.RawPtr(); + } + } } else { @@ -253,10 +290,11 @@ void PipelineResourceSignatureD3D11Impl::CreateLayout(const bool IsSerialized) "Deserialized immutable sampler flag is invalid"); } } + VERIFY_EXPR(InlineConstantBufferIdx == m_NumInlineConstantBuffers); if (m_pStaticResCache) { - m_pStaticResCache->Initialize(StaticResCounters, GetRawAllocator(), nullptr); + m_pStaticResCache->Initialize(StaticResCounters, GetRawAllocator(), nullptr, m_InlineConstantBuffers.get(), m_NumInlineConstantBuffers); VERIFY_EXPR(m_pStaticResCache->IsInitialized()); } } @@ -289,12 +327,20 @@ void PipelineResourceSignatureD3D11Impl::CopyStaticResources(ShaderResourceCache switch (ShaderResourceTypeToRange(ResDesc.ResourceType)) { case D3D11_RESOURCE_RANGE_CBV: - for (Uint32 ArrInd = 0; ArrInd < ResDesc.ArraySize; ++ArrInd) + if (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) + { + VERIFY(ResDesc.Flags == PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS, "INLINE_CONSTANTS flag is not compatible with other flags"); + DstResourceCache.CopyInlineConstants(SrcResourceCache, ResAttr.BindPoints, ResDesc.ArraySize); + } + else { - if (!DstResourceCache.CopyResource(SrcResourceCache, ResAttr.BindPoints + ArrInd)) + for (Uint32 ArrInd = 0; ArrInd < ResDesc.ArraySize; ++ArrInd) { - if (DstCacheType == ResourceCacheContentType::SRB) - LOG_ERROR_MESSAGE("No resource is assigned to static shader variable '", GetShaderResourcePrintName(ResDesc, ArrInd), "' in pipeline resource signature '", m_Desc.Name, "'."); + if (!DstResourceCache.CopyResource(SrcResourceCache, ResAttr.BindPoints + ArrInd)) + { + if (DstCacheType == ResourceCacheContentType::SRB) + LOG_ERROR_MESSAGE("No resource is assigned to static shader variable '", GetShaderResourcePrintName(ResDesc, ArrInd), "' in pipeline resource signature '", m_Desc.Name, "'."); + } } } break; @@ -353,7 +399,8 @@ void PipelineResourceSignatureD3D11Impl::CopyStaticResources(ShaderResourceCache void PipelineResourceSignatureD3D11Impl::InitSRBResourceCache(ShaderResourceCacheD3D11& ResourceCache) { - ResourceCache.Initialize(m_ResourceCounters, m_SRBMemAllocator.GetResourceCacheDataAllocator(0), &m_DynamicCBSlotsMask); + ResourceCache.Initialize(m_ResourceCounters, m_SRBMemAllocator.GetResourceCacheDataAllocator(0), &m_DynamicCBSlotsMask, + m_InlineConstantBuffers.get(), m_NumInlineConstantBuffers); VERIFY_EXPR(ResourceCache.IsInitialized()); // Copy immutable samplers. diff --git a/Graphics/GraphicsEngineD3D11/src/ShaderResourceCacheD3D11.cpp b/Graphics/GraphicsEngineD3D11/src/ShaderResourceCacheD3D11.cpp index adc17e70ed..eee75b12aa 100755 --- a/Graphics/GraphicsEngineD3D11/src/ShaderResourceCacheD3D11.cpp +++ b/Graphics/GraphicsEngineD3D11/src/ShaderResourceCacheD3D11.cpp @@ -95,7 +95,9 @@ void ShaderResourceCacheD3D11::DestructResources(Uint32 ShaderInd) void ShaderResourceCacheD3D11::Initialize(const D3D11ShaderResourceCounters& ResCount, IMemoryAllocator& MemAllocator, - const std::array* pDynamicCBSlotsMask) + const std::array* pDynamicCBSlotsMask, + const InlineConstantBufferAttribsD3D11* pInlineCBs, + Uint32 NumInlineCBs) { // http://diligentgraphics.com/diligent-engine/architecture/d3d11/shader-resource-cache/ VERIFY(!IsInitialized(), "Resource cache has already been initialized!"); @@ -130,11 +132,42 @@ void ShaderResourceCacheD3D11::Initialize(const D3D11ShaderResourceCounters& } m_Offsets[MaxOffsets - 1] = static_cast(MemOffset); - const size_t BufferSize = MemOffset; + size_t BufferSize = MemOffset; VERIFY_EXPR(m_pResourceData == nullptr); VERIFY_EXPR(BufferSize == GetRequiredMemorySize(ResCount)); + auto ProcessInlineCBs = [pInlineCBs, NumInlineCBs, this](auto Handler) { + for (Uint32 i = 0; i < NumInlineCBs; ++i) + { + const InlineConstantBufferAttribsD3D11& InlineCBAttr = pInlineCBs[i]; + + SHADER_TYPE ActiveStages = InlineCBAttr.BindPoints.GetActiveStages(); + const Uint32 ShaderInd0 = ExtractFirstShaderStageIndex(ActiveStages); + const Uint32 Binding0 = InlineCBAttr.BindPoints[ShaderInd0]; + const bool IsInRange = Binding0 < GetResourceCount(ShaderInd0); + +#ifdef DILIGENT_DEBUG + while (ActiveStages != SHADER_TYPE_UNKNOWN) + { + const Uint32 ShaderInd = ExtractFirstShaderStageIndex(ActiveStages); + const Uint32 Binding = InlineCBAttr.BindPoints[ShaderInd]; + VERIFY(IsInRange == (Binding < GetResourceCount(ShaderInd)), + "All binding points for inline constant buffer must be in range or out of range simultaneously."); + } +#endif + + if (IsInRange) + { + Handler(InlineCBAttr); + } + } + }; + + ProcessInlineCBs([&BufferSize](const InlineConstantBufferAttribsD3D11& InlineCBAttr) { + BufferSize += InlineCBAttr.NumConstants * sizeof(Uint32); + }); + if (BufferSize > 0) { m_pResourceData = decltype(m_pResourceData){ @@ -153,6 +186,15 @@ void ShaderResourceCacheD3D11::Initialize(const D3D11ShaderResourceCounters& ConstructResources(ShaderInd); } + Uint32* pInlineCBData = reinterpret_cast(m_pResourceData.get() + MemOffset); + // Initialize inline constant buffers. + ProcessInlineCBs([&pInlineCBData, this](const InlineConstantBufferAttribsD3D11& InlineCBAttr) { + VERIFY_EXPR(InlineCBAttr.NumConstants > 0); + VERIFY_EXPR(InlineCBAttr.pBuffer != nullptr); + InitInlineConstantBuffer(InlineCBAttr.BindPoints, InlineCBAttr.pBuffer, InlineCBAttr.NumConstants, pInlineCBData); + pInlineCBData += InlineCBAttr.NumConstants; + }); + m_IsInitialized = true; } @@ -175,6 +217,32 @@ ShaderResourceCacheD3D11::~ShaderResourceCacheD3D11() } } +void ShaderResourceCacheD3D11::InitInlineConstantBuffer(const D3D11ResourceBindPoints& BindPoints, + RefCntAutoPtr pBuffer, + Uint32 NumConstants, + void* pInlineConstantData) +{ + VERIFY_EXPR(pBuffer); + VERIFY_EXPR(pInlineConstantData); + ID3D11Buffer* pd3d11Buffer = pBuffer->GetD3D11Buffer(); + VERIFY_EXPR(pd3d11Buffer); + for (SHADER_TYPE ActiveStages = BindPoints.GetActiveStages(); ActiveStages != SHADER_TYPE_UNKNOWN;) + { + const Uint32 ShaderInd = ExtractFirstShaderStageIndex(ActiveStages); + const Uint32 Binding = BindPoints[ShaderInd]; + VERIFY(Binding < GetResourceCount(ShaderInd), "Cache offset is out of range"); + + auto ResArrays = GetResourceArrays(ShaderInd); + auto& CachedRes = ResArrays.first[Binding]; + auto& pd3d11Res = ResArrays.second[Binding]; + + CachedRes.pBuff = pBuffer; + CachedRes.RangeSize = NumConstants * sizeof(Uint32); + CachedRes.pInlineConstantData = pInlineConstantData; + pd3d11Res = pd3d11Buffer; + } +} + template void ShaderResourceCacheD3D11::TransitionResourceStates(DeviceContextD3D11Impl& Ctx) { diff --git a/Graphics/GraphicsEngineD3D11/src/ShaderVariableManagerD3D11.cpp b/Graphics/GraphicsEngineD3D11/src/ShaderVariableManagerD3D11.cpp index 7f44560531..7e92c0e8f3 100644 --- a/Graphics/GraphicsEngineD3D11/src/ShaderVariableManagerD3D11.cpp +++ b/Graphics/GraphicsEngineD3D11/src/ShaderVariableManagerD3D11.cpp @@ -291,6 +291,7 @@ void ShaderVariableManagerD3D11::ConstBuffBindInfo::SetDynamicOffset(Uint32 Arra const PipelineResourceAttribsD3D11& Attr = GetAttribs(); const PipelineResourceDesc& Desc = GetDesc(); VERIFY_EXPR(Desc.ResourceType == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER); + VERIFY_EXPR((Desc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) == 0); #ifdef DILIGENT_DEVELOPMENT { const ShaderResourceCacheD3D11::CachedCB& CachedCB = m_ParentManager.m_ResourceCache.GetResource(Attr.BindPoints + ArrayIndex); @@ -302,7 +303,16 @@ void ShaderVariableManagerD3D11::ConstBuffBindInfo::SetDynamicOffset(Uint32 Arra void ShaderVariableManagerD3D11::ConstBuffBindInfo::SetConstants(const void* pConstants, Uint32 FirstConstant, Uint32 NumConstants) { - UNSUPPORTED("Not yet implemented"); + const PipelineResourceAttribsD3D11& Attr = GetAttribs(); + const PipelineResourceDesc& Desc = GetDesc(); + VERIFY_EXPR(Desc.ResourceType == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER); + VERIFY_EXPR(Desc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS); +#ifdef DILIGENT_DEVELOPMENT + { + VerifyInlineConstants(Desc, pConstants, FirstConstant, NumConstants); + } +#endif + m_ParentManager.m_ResourceCache.SetInlineConstants(Attr.BindPoints, pConstants, FirstConstant, NumConstants); } void ShaderVariableManagerD3D11::TexSRVBindInfo::BindResource(const BindResourceInfo& BindInfo) From cb316d708306141dd415943e5242b135538cbcdd Mon Sep 17 00:00:00 2001 From: assiduous Date: Sun, 26 Oct 2025 17:08:48 -0700 Subject: [PATCH 028/104] ResourceSignatureD3D11: implement inline constant buffers update (#672) --- .../PipelineResourceSignatureD3D11Impl.hpp | 2 ++ .../include/ShaderResourceCacheD3D11.hpp | 8 +++++-- .../PipelineResourceSignatureD3D11Impl.cpp | 24 +++++++++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/Graphics/GraphicsEngineD3D11/include/PipelineResourceSignatureD3D11Impl.hpp b/Graphics/GraphicsEngineD3D11/include/PipelineResourceSignatureD3D11Impl.hpp index 0f0e1d15aa..43df105b76 100644 --- a/Graphics/GraphicsEngineD3D11/include/PipelineResourceSignatureD3D11Impl.hpp +++ b/Graphics/GraphicsEngineD3D11/include/PipelineResourceSignatureD3D11Impl.hpp @@ -113,6 +113,8 @@ class PipelineResourceSignatureD3D11Impl final : public PipelineResourceSignatur // Make the base class method visible using TPipelineResourceSignatureBase::CopyStaticResources; + void UpdateInlineConstantBuffers(const ShaderResourceCacheD3D11& ResourceCache, ID3D11DeviceContext* pd3d11Ctx) const; + #ifdef DILIGENT_DEVELOPMENT /// Verifies committed resource using the D3D resource attributes from the PSO. bool DvpValidateCommittedResource(const D3DShaderResourceAttribs& D3DAttribs, diff --git a/Graphics/GraphicsEngineD3D11/include/ShaderResourceCacheD3D11.hpp b/Graphics/GraphicsEngineD3D11/include/ShaderResourceCacheD3D11.hpp index 9c1651e23a..72428aaee0 100644 --- a/Graphics/GraphicsEngineD3D11/include/ShaderResourceCacheD3D11.hpp +++ b/Graphics/GraphicsEngineD3D11/include/ShaderResourceCacheD3D11.hpp @@ -280,7 +280,9 @@ class ShaderResourceCacheD3D11 : public ShaderResourceCacheBase Uint32 NumConstants); template - __forceinline const typename CachedResourceTraits::CachedResourceType& GetResource(const D3D11ResourceBindPoints& BindPoints) const + __forceinline const typename CachedResourceTraits::CachedResourceType& GetResource( + const D3D11ResourceBindPoints& BindPoints, + typename CachedResourceTraits::D3D11ResourceType** ppd3d11Resource = nullptr) const { VERIFY(BindPoints.GetActiveStages() != SHADER_TYPE_UNKNOWN, "No active shader stage"); const Int32 FirstStageInd = GetFirstShaderStageIndex(BindPoints.GetActiveStages()); @@ -288,9 +290,9 @@ class ShaderResourceCacheD3D11 : public ShaderResourceCacheBase VERIFY(FirstStageBinding < GetResourceCount(FirstStageInd), "Resource slot is out of range"); const auto FirstStageResArrays = GetConstResourceArrays(FirstStageInd); const auto& CachedRes = FirstStageResArrays.first[FirstStageBinding]; + auto* pd3d11Res = FirstStageResArrays.second[FirstStageBinding]; #ifdef DILIGENT_DEBUG { - const auto* pd3d11Res = FirstStageResArrays.second[FirstStageBinding]; for (SHADER_TYPE ActiveStages = BindPoints.GetActiveStages(); ActiveStages != SHADER_TYPE_UNKNOWN;) { const Int32 ShaderInd = ExtractFirstShaderStageIndex(ActiveStages); @@ -300,6 +302,8 @@ class ShaderResourceCacheD3D11 : public ShaderResourceCacheBase } } #endif + if (ppd3d11Resource != nullptr) + *ppd3d11Resource = pd3d11Res; return CachedRes; } diff --git a/Graphics/GraphicsEngineD3D11/src/PipelineResourceSignatureD3D11Impl.cpp b/Graphics/GraphicsEngineD3D11/src/PipelineResourceSignatureD3D11Impl.cpp index 419691e6a4..b9086f5de4 100644 --- a/Graphics/GraphicsEngineD3D11/src/PipelineResourceSignatureD3D11Impl.cpp +++ b/Graphics/GraphicsEngineD3D11/src/PipelineResourceSignatureD3D11Impl.cpp @@ -492,6 +492,30 @@ void PipelineResourceSignatureD3D11Impl::UpdateShaderResourceBindingMap(Resource } } +void PipelineResourceSignatureD3D11Impl::UpdateInlineConstantBuffers(const ShaderResourceCacheD3D11& ResourceCache, ID3D11DeviceContext* pd3d11Ctx) const +{ + for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) + { + const InlineConstantBufferAttribsD3D11& InlineCBAttr = m_InlineConstantBuffers[i]; + + ID3D11Buffer* pd3d11CB = nullptr; + const ShaderResourceCacheD3D11::CachedCB& InlineCB = ResourceCache.GetResource(InlineCBAttr.BindPoints, &pd3d11CB); + VERIFY(InlineCBAttr.NumConstants * sizeof(Uint32) == InlineCB.RangeSize, "Inline constant buffer size mismatch"); + VERIFY(InlineCB.pInlineConstantData != nullptr, "Inline constant data pointer is null"); + + D3D11_MAPPED_SUBRESOURCE MappedData{}; + if (SUCCEEDED(pd3d11Ctx->Map(pd3d11CB, 0, D3D11_MAP_WRITE_DISCARD, 0, &MappedData))) + { + memcpy(MappedData.pData, InlineCB.pInlineConstantData, InlineCBAttr.NumConstants * sizeof(Uint32)); + pd3d11Ctx->Unmap(pd3d11CB, 0); + } + else + { + DEV_ERROR("Failed to map inline constant buffer"); + } + } +} + #ifdef DILIGENT_DEVELOPMENT bool PipelineResourceSignatureD3D11Impl::DvpValidateCommittedResource(const D3DShaderResourceAttribs& D3DAttribs, Uint32 ResIndex, From 1c3e30d68821ae70d97f505d5b1f079f1dbd9e3c Mon Sep 17 00:00:00 2001 From: assiduous Date: Sun, 26 Oct 2025 18:10:43 -0700 Subject: [PATCH 029/104] DeviceContextD3D11: update inline constant buffers (#672) --- .../include/ShaderResourceCacheD3D11.hpp | 14 +++-- .../src/DeviceContextD3D11Impl.cpp | 51 +++++++++++++++---- .../src/ShaderResourceCacheD3D11.cpp | 37 +++++++++----- 3 files changed, 77 insertions(+), 25 deletions(-) diff --git a/Graphics/GraphicsEngineD3D11/include/ShaderResourceCacheD3D11.hpp b/Graphics/GraphicsEngineD3D11/include/ShaderResourceCacheD3D11.hpp index 72428aaee0..ee0cf46dec 100644 --- a/Graphics/GraphicsEngineD3D11/include/ShaderResourceCacheD3D11.hpp +++ b/Graphics/GraphicsEngineD3D11/include/ShaderResourceCacheD3D11.hpp @@ -348,7 +348,7 @@ class ShaderResourceCacheD3D11 : public ShaderResourceCacheBase template __forceinline Uint32 GetResourceCount(Uint32 ShaderInd) const; - bool IsInitialized() const { return m_IsInitialized; } + bool IsInitialized() const { return m_Flags & FLAG_IS_INITIALIZED; } ResourceCacheContentType GetContentType() const { return m_ContentType; } @@ -422,7 +422,7 @@ class ShaderResourceCacheD3D11 : public ShaderResourceCacheBase bool HasInlineConstants() const { - return false; + return m_Flags & FLAG_HAS_INLINE_CONSTANTS; } #ifdef DILIGENT_DEBUG @@ -504,7 +504,14 @@ class ShaderResourceCacheD3D11 : public ShaderResourceCacheBase std::array m_Offsets = {}; - bool m_IsInitialized = false; + enum FLAGS : Uint8 + { + FLAG_NONE = 0, + FLAG_IS_INITIALIZED = 1u << 0u, + FLAG_HAS_INLINE_CONSTANTS = 1u << 1u, + }; + DECLARE_FRIEND_FLAG_ENUM_OPERATORS(FLAGS) + FLAGS m_Flags = FLAG_NONE; // Indicates what types of resources are stored in the cache const ResourceCacheContentType m_ContentType; @@ -518,6 +525,7 @@ class ShaderResourceCacheD3D11 : public ShaderResourceCacheBase std::unique_ptr> m_pResourceData; }; +DEFINE_FLAG_ENUM_OPERATORS(ShaderResourceCacheD3D11::FLAGS) template <> struct ShaderResourceCacheD3D11::CachedResourceTraits diff --git a/Graphics/GraphicsEngineD3D11/src/DeviceContextD3D11Impl.cpp b/Graphics/GraphicsEngineD3D11/src/DeviceContextD3D11Impl.cpp index b1f725ac77..a1bd5def27 100755 --- a/Graphics/GraphicsEngineD3D11/src/DeviceContextD3D11Impl.cpp +++ b/Graphics/GraphicsEngineD3D11/src/DeviceContextD3D11Impl.cpp @@ -460,17 +460,50 @@ void DeviceContextD3D11Impl::BindShaderResources(Uint32 BindSRBMask) else { // Bind constant buffers with dynamic offsets. In Direct3D11 only those buffers are counted as dynamic. - VERIFY((m_BindInfo.DynamicSRBMask & SignBit) != 0, - "When bit in StaleSRBMask is not set, the same bit in DynamicSRBMask must be set. Check GetCommitMask()."); - DEV_CHECK_ERR(pResourceCache->HasDynamicResources(), - "Bit in DynamicSRBMask is set, but the cache does not contain dynamic resources. This may indicate that resources " - "in the cache have changed, but the SRB has not been committed before the draw/dispatch command."); - if (pResourceCache->GetUAVCount(PSInd) > 0) + VERIFY(((m_BindInfo.DynamicSRBMask | m_BindInfo.InlineConstantsSRBMask) & SignBit) != 0, + "When bit in StaleSRBMask is not set, the same bit in either DynamicSRBMask or InlineConstantsSRBMask must be set. Check GetCommitMask()."); + + if ((m_BindInfo.DynamicSRBMask & SignBit) != 0) + { + DEV_CHECK_ERR(pResourceCache->HasDynamicResources(), + "Shader resource cache does not contain dynamic resources, but the corresponding bit in DynamicSRBMask is set. " + "This may indicate that resources in the cache have changed, but the SRB has not been committed before the draw/dispatch command."); + if (pResourceCache->GetUAVCount(PSInd) > 0) + { + if (PsUavBindMode != PixelShaderUAVBindMode::Bind) + PsUavBindMode = PixelShaderUAVBindMode::Keep; + } + BindDynamicCBs(*pResourceCache, BaseBindings); + } + else + { + DEV_CHECK_ERR(!pResourceCache->HasDynamicResources(), + "Shader resource cache contains dynamic resources, but the corresponding bit in DynamicSRBMask is not set. " + "This may indicate that resources in the cache have changed, but the SRB has not been committed before the draw/dispatch command."); + } + } + + if ((m_BindInfo.InlineConstantsSRBMask & SignBit) != 0) + { + VERIFY(pResourceCache->HasInlineConstants(), + "Shader resource cache does not contain inline constants, but the corresponding bit in InlineConstantsSRBMask is set. " + "This may be a bug because inline constants flag in the cache never changes after SRB creation, " + "while m_BindInfo.InlineConstantsSRBMask is initialized when SRB is committed."); + if (PipelineResourceSignatureD3D11Impl* pSign = m_pPipelineState->GetResourceSignature(SignIdx)) { - if (PsUavBindMode != PixelShaderUAVBindMode::Bind) - PsUavBindMode = PixelShaderUAVBindMode::Keep; + pSign->UpdateInlineConstantBuffers(*pResourceCache, m_pd3d11DeviceContext); } - BindDynamicCBs(*pResourceCache, BaseBindings); + else + { + UNEXPECTED("Pipeline resource signature is null for signature index ", SignIdx); + } + } + else + { + VERIFY(!pResourceCache->HasInlineConstants(), + "Shader resource cache contains inline constants, but the corresponding bit in InlineConstantsSRBMask is not set. " + "This may be a bug because inline constants flag in the cache never changes after SRB creation, " + "while m_BindInfo.InlineConstantsSRBMask is initialized when SRB is committed."); } } diff --git a/Graphics/GraphicsEngineD3D11/src/ShaderResourceCacheD3D11.cpp b/Graphics/GraphicsEngineD3D11/src/ShaderResourceCacheD3D11.cpp index eee75b12aa..bed80bba72 100755 --- a/Graphics/GraphicsEngineD3D11/src/ShaderResourceCacheD3D11.cpp +++ b/Graphics/GraphicsEngineD3D11/src/ShaderResourceCacheD3D11.cpp @@ -164,10 +164,17 @@ void ShaderResourceCacheD3D11::Initialize(const D3D11ShaderResourceCounters& } }; - ProcessInlineCBs([&BufferSize](const InlineConstantBufferAttribsD3D11& InlineCBAttr) { - BufferSize += InlineCBAttr.NumConstants * sizeof(Uint32); + Uint32 TotalInlineConstants = 0; + ProcessInlineCBs([&TotalInlineConstants](const InlineConstantBufferAttribsD3D11& InlineCBAttr) { + TotalInlineConstants += InlineCBAttr.NumConstants; }); + if (TotalInlineConstants > 0) + { + m_Flags |= FLAG_HAS_INLINE_CONSTANTS; + BufferSize += TotalInlineConstants * sizeof(Uint32); + } + if (BufferSize > 0) { m_pResourceData = decltype(m_pResourceData){ @@ -186,16 +193,20 @@ void ShaderResourceCacheD3D11::Initialize(const D3D11ShaderResourceCounters& ConstructResources(ShaderInd); } - Uint32* pInlineCBData = reinterpret_cast(m_pResourceData.get() + MemOffset); - // Initialize inline constant buffers. - ProcessInlineCBs([&pInlineCBData, this](const InlineConstantBufferAttribsD3D11& InlineCBAttr) { - VERIFY_EXPR(InlineCBAttr.NumConstants > 0); - VERIFY_EXPR(InlineCBAttr.pBuffer != nullptr); - InitInlineConstantBuffer(InlineCBAttr.BindPoints, InlineCBAttr.pBuffer, InlineCBAttr.NumConstants, pInlineCBData); - pInlineCBData += InlineCBAttr.NumConstants; - }); + if (TotalInlineConstants > 0) + { + Uint32* pInlineCBData = reinterpret_cast(m_pResourceData.get() + MemOffset); + // Initialize inline constant buffers. + ProcessInlineCBs([&pInlineCBData, this](const InlineConstantBufferAttribsD3D11& InlineCBAttr) { + VERIFY_EXPR(InlineCBAttr.NumConstants > 0); + VERIFY_EXPR(InlineCBAttr.pBuffer != nullptr); + InitInlineConstantBuffer(InlineCBAttr.BindPoints, InlineCBAttr.pBuffer, InlineCBAttr.NumConstants, pInlineCBData); + pInlineCBData += InlineCBAttr.NumConstants; + }); + VERIFY_EXPR(pInlineCBData == reinterpret_cast(m_pResourceData.get() + BufferSize)); + } - m_IsInitialized = true; + m_Flags |= FLAG_IS_INITIALIZED; } ShaderResourceCacheD3D11::~ShaderResourceCacheD3D11() @@ -210,8 +221,8 @@ ShaderResourceCacheD3D11::~ShaderResourceCacheD3D11() DestructResources(ShaderInd); DestructResources(ShaderInd); } - m_Offsets = {}; - m_IsInitialized = false; + m_Offsets = {}; + m_Flags = FLAG_NONE; m_pResourceData.reset(); } From 0ffcb53403e0ce0c9144779b877788dd5c1e0810 Mon Sep 17 00:00:00 2001 From: assiduous Date: Sun, 26 Oct 2025 19:07:45 -0700 Subject: [PATCH 030/104] D3D11: fix inline constant buffers remapping; enable inline constant tests --- .../interface/PipelineResourceSignature.h | 7 +++++++ .../include/ShaderResourceCacheD3D11.hpp | 4 ++-- .../src/PipelineResourceSignatureD3D11Impl.cpp | 14 +++++++------- .../src/PipelineResourceSignatureD3D12Impl.cpp | 10 +++------- .../src/InlineConstantsTest.cpp | 2 +- 5 files changed, 20 insertions(+), 17 deletions(-) diff --git a/Graphics/GraphicsEngine/interface/PipelineResourceSignature.h b/Graphics/GraphicsEngine/interface/PipelineResourceSignature.h index b73df18f75..7f62e26129 100644 --- a/Graphics/GraphicsEngine/interface/PipelineResourceSignature.h +++ b/Graphics/GraphicsEngine/interface/PipelineResourceSignature.h @@ -353,6 +353,13 @@ struct PipelineResourceDesc { return !(*this == Rhs); } + + Uint32 GetArraySize() const noexcept + { + return (Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0 ? + 1 : // For inline constants, ArraySize is the number of 4-byte constants. + ArraySize; + } #endif }; typedef struct PipelineResourceDesc PipelineResourceDesc; diff --git a/Graphics/GraphicsEngineD3D11/include/ShaderResourceCacheD3D11.hpp b/Graphics/GraphicsEngineD3D11/include/ShaderResourceCacheD3D11.hpp index ee0cf46dec..51aececd76 100644 --- a/Graphics/GraphicsEngineD3D11/include/ShaderResourceCacheD3D11.hpp +++ b/Graphics/GraphicsEngineD3D11/include/ShaderResourceCacheD3D11.hpp @@ -687,7 +687,7 @@ inline ShaderResourceCacheD3D11::MinMaxSlot ShaderResourceCacheD3D11::BindCBs( // Offsets in Direct3D11 are measure in float4 constants. const UINT FirstCBConstant = StaticCast((ResArrays.first[res].BaseOffset + ResArrays.first[res].DynamicOffset) / 16u); // The number of constants must be a multiple of 16 constants. It is OK if it is past the end of the buffer. - const UINT NumCBConstants = StaticCast(AlignUp(ResArrays.first[res].RangeSize / 16u, 16u)); + const UINT NumCBConstants = StaticCast(AlignUp((ResArrays.first[res].RangeSize + 15u) / 16u, 16u)); // clang-format off if (CommittedD3D11Resources[Slot] != pd3d11CB || FirstConstants[Slot] != FirstCBConstant || @@ -734,7 +734,7 @@ inline void ShaderResourceCacheD3D11::BindDynamicCBs(Uint32 // Offsets in Direct3D11 are measure in float4 constants. const UINT FirstCBConstant = StaticCast((CB.BaseOffset + CB.DynamicOffset) / 16u); // The number of constants must be a multiple of 16 constants. It is OK if it is past the end of the buffer. - const UINT NumCBConstants = StaticCast(AlignUp(CB.RangeSize / 16u, 16u)); + const UINT NumCBConstants = StaticCast(AlignUp((CB.RangeSize + 15u) / 16u, 16u)); // clang-format off if (CommittedD3D11Resources[Slot] != pd3d11CB || FirstConstants[Slot] != FirstCBConstant || diff --git a/Graphics/GraphicsEngineD3D11/src/PipelineResourceSignatureD3D11Impl.cpp b/Graphics/GraphicsEngineD3D11/src/PipelineResourceSignatureD3D11Impl.cpp index b9086f5de4..f175be9a4f 100644 --- a/Graphics/GraphicsEngineD3D11/src/PipelineResourceSignatureD3D11Impl.cpp +++ b/Graphics/GraphicsEngineD3D11/src/PipelineResourceSignatureD3D11Impl.cpp @@ -212,9 +212,8 @@ void PipelineResourceSignatureD3D11Impl::CreateLayout(const bool IsSerialized) VERIFY((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) == 0 || ResDesc.ResourceType == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, "Only constant buffers can have inline constants flag"); - const Uint32 ArraySize = (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0 ? - 1 : // For inline constants, count only one resource as the ArraySize is the number of 4-byte constants. - ResDesc.ArraySize; + // For inline constants, ArraySize holds the number of 4-byte constants + const Uint32 ArraySize = ResDesc.GetArraySize(); AllocBindPoints(m_ResourceCounters, BindPoints, ResDesc.ShaderStages, ArraySize, Range); if (ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) { @@ -229,7 +228,8 @@ void PipelineResourceSignatureD3D11Impl::CreateLayout(const bool IsSerialized) } } - if (Range == D3D11_RESOURCE_RANGE_CBV && (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS) == 0) + if (Range == D3D11_RESOURCE_RANGE_CBV && + (ResDesc.Flags & (PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS | PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS)) == 0) { // Set corresponding bits in m_DynamicCBSlotsMask for (SHADER_TYPE ShaderStages = ResDesc.ShaderStages; ShaderStages != SHADER_TYPE_UNKNOWN;) @@ -436,9 +436,9 @@ void PipelineResourceSignatureD3D11Impl::UpdateShaderResourceBindingMap(Resource ResourceBinding::BindInfo BindInfo // { Uint32{BaseBindings[Range][ShaderInd]} + Uint32{ResAttr.BindPoints[ShaderInd]}, - 0u, // register space is not supported - ResDesc.ArraySize, - ResDesc.ResourceType // + 0u, // register space is not supported + ResDesc.GetArraySize(), // For inline constants, ArraySize holds the number of 4-byte constants + ResDesc.ResourceType, }; bool IsUnique = ResourceMap.emplace(HashMapStringKey{ResDesc.Name}, BindInfo).second; VERIFY(IsUnique, "Shader resource '", ResDesc.Name, diff --git a/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp index a00275e153..ae307aa04b 100644 --- a/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp @@ -284,9 +284,8 @@ void PipelineResourceSignatureD3D12Impl::AllocateRootParameters(const bool IsSer // Normal resources go into space 0. Space = 0; Register = NumResources[d3d12DescriptorRangeType]; - NumResources[d3d12DescriptorRangeType] += (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0 ? - 1 : // For inline constants, count only one resource as the ArraySize is the number of 4-byte constants. - ResDesc.ArraySize; + // For inline constants, ArraySize holds the number of 4-byte constants. + NumResources[d3d12DescriptorRangeType] += ResDesc.GetArraySize(); } const PIPELINE_RESOURCE_FLAGS dbgValidResourceFlags = GetValidPipelineResourceFlags(ResDesc.ResourceType); @@ -732,15 +731,12 @@ void PipelineResourceSignatureD3D12Impl::UpdateShaderResourceBindingMap(Resource { VERIFY((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) == 0 || ResDesc.ResourceType == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, "Only constant buffers can be marked as INLINE_CONSTANTS. This error should've been caught by ValidatePipelineResourceSignatureDesc()."); - const Uint32 ArraySize = (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) ? - 1 : // For inline constants, ArraySize is the number of 4-byte constants - ResDesc.ArraySize; ResourceBinding::BindInfo BindInfo // { Attribs.Register, Attribs.Space + BaseRegisterSpace, - ArraySize, + ResDesc.GetArraySize(), // For inline constants, ArraySize holds the number of 4-byte constants ResDesc.ResourceType, }; bool IsUnique = ResourceMap.emplace(HashMapStringKey{ResDesc.Name}, BindInfo).second; diff --git a/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp b/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp index 5adcf7a9b8..729d421603 100644 --- a/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp +++ b/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp @@ -129,7 +129,7 @@ class InlineConstants : public ::testing::Test GPUTestingEnvironment* pEnv = GPUTestingEnvironment::GetInstance(); IRenderDevice* pDevice = pEnv->GetDevice(); - if (pDevice->GetDeviceInfo().Type != RENDER_DEVICE_TYPE_D3D12) + if (!pDevice->GetDeviceInfo().IsD3DDevice()) { GTEST_SKIP(); } From 93af1dae247914df2a92c7ea63e80fd6f92ce8ed Mon Sep 17 00:00:00 2001 From: assiduous Date: Sun, 26 Oct 2025 22:11:46 -0700 Subject: [PATCH 031/104] D3D11/D3D12: improved handling of DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT/INLINE_CONSTANTS_INTACT flags --- .../include/DeviceContextD3D11Impl.hpp | 4 ++- .../src/DeviceContextD3D11Impl.cpp | 28 +++++++++++++------ .../include/DeviceContextD3D12Impl.hpp | 6 +++- .../src/DeviceContextD3D12Impl.cpp | 26 +++++++++++++---- 4 files changed, 47 insertions(+), 17 deletions(-) diff --git a/Graphics/GraphicsEngineD3D11/include/DeviceContextD3D11Impl.hpp b/Graphics/GraphicsEngineD3D11/include/DeviceContextD3D11Impl.hpp index e16fca6a80..a05bbe9b40 100644 --- a/Graphics/GraphicsEngineD3D11/include/DeviceContextD3D11Impl.hpp +++ b/Graphics/GraphicsEngineD3D11/include/DeviceContextD3D11Impl.hpp @@ -359,7 +359,9 @@ class DeviceContextD3D11Impl final : public DeviceContextBaseGetResourceSignature(SignIdx)) - { - pSign->UpdateInlineConstantBuffers(*pResourceCache, m_pd3d11DeviceContext); - } - else + if ((m_BindInfo.StaleSRBMask & SignBit) != 0 || !InlineConstantsIntact) { - UNEXPECTED("Pipeline resource signature is null for signature index ", SignIdx); + if (PipelineResourceSignatureD3D11Impl* pSign = m_pPipelineState->GetResourceSignature(SignIdx)) + { + pSign->UpdateInlineConstantBuffers(*pResourceCache, m_pd3d11DeviceContext); + } + else + { + UNEXPECTED("Pipeline resource signature is null for signature index ", SignIdx); + } } } else @@ -705,9 +713,11 @@ void DeviceContextD3D11Impl::PrepareForDraw(DRAW_FLAGS Flags) CommitD3D11VertexBuffers(m_pPipelineState); } + const bool DynamicBuffersIntact = (Flags & DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT) != 0; + const bool InlineConstantsIntact = (Flags & DRAW_FLAG_INLINE_CONSTANTS_INTACT) != 0; if (Uint32 BindSRBMask = m_BindInfo.GetCommitMask(Flags & DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT, Flags & DRAW_FLAG_INLINE_CONSTANTS_INTACT)) { - BindShaderResources(BindSRBMask); + BindShaderResources(BindSRBMask, DynamicBuffersIntact, InlineConstantsIntact); } #ifdef DILIGENT_DEVELOPMENT diff --git a/Graphics/GraphicsEngineD3D12/include/DeviceContextD3D12Impl.hpp b/Graphics/GraphicsEngineD3D12/include/DeviceContextD3D12Impl.hpp index d359d0a469..ef1eb8d329 100644 --- a/Graphics/GraphicsEngineD3D12/include/DeviceContextD3D12Impl.hpp +++ b/Graphics/GraphicsEngineD3D12/include/DeviceContextD3D12Impl.hpp @@ -426,7 +426,11 @@ class DeviceContextD3D12Impl final : public DeviceContextNextGenBase - __forceinline void CommitRootTablesAndViews(RootTableInfo& RootInfo, Uint32 CommitSRBMask, CommandContext& CmdCtx) const; + __forceinline void CommitRootTablesAndViews(RootTableInfo& RootInfo, + Uint32 CommitSRBMask, + CommandContext& CmdCtx, + bool DynamicBuffersIntact = false, + bool InlineConstantsIntact = false) const; #ifdef DILIGENT_DEVELOPMENT void DvpValidateCommittedShaderResources(RootTableInfo& RootInfo) const; diff --git a/Graphics/GraphicsEngineD3D12/src/DeviceContextD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/DeviceContextD3D12Impl.cpp index 0b49ec90aa..ce56c4b42e 100644 --- a/Graphics/GraphicsEngineD3D12/src/DeviceContextD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/DeviceContextD3D12Impl.cpp @@ -370,7 +370,11 @@ void DeviceContextD3D12Impl::SetPipelineState(IPipelineState* pPipelineState) } template -void DeviceContextD3D12Impl::CommitRootTablesAndViews(RootTableInfo& RootInfo, Uint32 CommitSRBMask, CommandContext& CmdCtx) const +void DeviceContextD3D12Impl::CommitRootTablesAndViews(RootTableInfo& RootInfo, + Uint32 CommitSRBMask, + CommandContext& CmdCtx, + bool DynamicBuffersIntact, + bool InlineConstantsIntact) const { const RootSignatureD3D12& RootSig = m_pPipelineState->GetRootSignature(); @@ -397,7 +401,9 @@ void DeviceContextD3D12Impl::CommitRootTablesAndViews(RootTableInfo& RootInfo, U CommitAttribs.pResourceCache = pResourceCache; CommitAttribs.BaseRootIndex = RootSig.GetBaseRootIndex(sign); - if ((RootInfo.StaleSRBMask & SignBit) != 0) + + const bool SRBStale = (RootInfo.StaleSRBMask & SignBit) != 0; + if (SRBStale) { // Commit root tables for stale SRBs only pSignature->CommitRootTables(CommitAttribs); @@ -410,7 +416,10 @@ void DeviceContextD3D12Impl::CommitRootTablesAndViews(RootTableInfo& RootInfo, U DEV_CHECK_ERR((RootInfo.DynamicSRBMask & SignBit) != 0, "There are dynamic root buffers in the cache, but the bit in DynamicSRBMask is not set. This may indicate that resources " "in the cache have changed, but the SRB has not been committed before the draw/dispatch command."); - pSignature->CommitRootViews(CommitAttribs, DynamicRootBuffersMask); + if (SRBStale || !DynamicBuffersIntact) + { + pSignature->CommitRootViews(CommitAttribs, DynamicRootBuffersMask); + } } else { @@ -425,7 +434,10 @@ void DeviceContextD3D12Impl::CommitRootTablesAndViews(RootTableInfo& RootInfo, U "There are root constants in the cache, but the bit in InlineConstantsSRBMask is not set. " "This may be a bug because root constants mask in the cache never changes after SRB creation, " "while RootInfo.InlineConstantsSRBMask is initialized when SRB is committed."); - pSignature->CommitRootConstants(CommitAttribs, RootConstantsMask); + if (SRBStale || !InlineConstantsIntact) + { + pSignature->CommitRootConstants(CommitAttribs, RootConstantsMask); + } } else { @@ -645,9 +657,11 @@ void DeviceContextD3D12Impl::PrepareForDraw(GraphicsContext& GraphCtx, DRAW_FLAG #ifdef DILIGENT_DEVELOPMENT DvpValidateCommittedShaderResources(RootInfo); #endif - if (Uint32 CommitSRBMask = RootInfo.GetCommitMask(Flags & DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT, Flags & DRAW_FLAG_INLINE_CONSTANTS_INTACT)) + const bool DynamicBuffersIntact = (Flags & DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT) != 0; + const bool InlineConstantsIntact = (Flags & DRAW_FLAG_INLINE_CONSTANTS_INTACT) != 0; + if (Uint32 CommitSRBMask = RootInfo.GetCommitMask(DynamicBuffersIntact, InlineConstantsIntact)) { - CommitRootTablesAndViews(RootInfo, CommitSRBMask, GraphCtx); + CommitRootTablesAndViews(RootInfo, CommitSRBMask, GraphCtx, DynamicBuffersIntact, InlineConstantsIntact); } #ifdef NTDDI_WIN10_19H1 From 686f15126351f9ab1c52d0129ab7c5482600caa8 Mon Sep 17 00:00:00 2001 From: assiduous Date: Thu, 30 Oct 2025 09:18:34 -0700 Subject: [PATCH 032/104] D3D11/D3D12: clean up code and update documentation for inline constants --- .../src/GraphicsAccessories.cpp | 14 +++--- .../interface/PipelineResourceSignature.h | 45 ++++++++++--------- .../GraphicsEngine/interface/PipelineState.h | 2 + .../interface/ShaderResourceVariable.h | 3 ++ .../include/ShaderResourceCacheD3D11.hpp | 16 ++++--- .../src/DeviceContextD3D11Impl.cpp | 8 ++-- .../PipelineResourceSignatureD3D11Impl.cpp | 18 ++++++-- .../src/ShaderResourceCacheD3D11.cpp | 6 ++- .../PipelineResourceSignatureD3D12Impl.cpp | 45 ++++++++++++------- .../src/RootParamsManager.cpp | 3 +- .../src/ShaderResourceCacheD3D12.cpp | 12 ++--- .../PRSCreationFailureTest.cpp | 10 ++--- 12 files changed, 112 insertions(+), 70 deletions(-) diff --git a/Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp b/Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp index 98aa9e5228..9988f3b9f8 100644 --- a/Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp +++ b/Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp @@ -1644,6 +1644,10 @@ String GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAGS Flags, bool GetFul Str.append(GetFullName ? "PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS" : "NO_DYNAMIC_BUFFERS"); break; + case PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS: + Str.append(GetFullName ? "PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS" : "INLINE_CONSTANTS"); + break; + case PIPELINE_RESOURCE_FLAG_COMBINED_SAMPLER: Str.append(GetFullName ? "PIPELINE_RESOURCE_FLAG_COMBINED_SAMPLER" : "COMBINED_SAMPLER"); break; @@ -1660,10 +1664,6 @@ String GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAGS Flags, bool GetFul Str.append(GetFullName ? "PIPELINE_RESOURCE_FLAG_GENERAL_INPUT_ATTACHMENT" : "GENERAL_INPUT_ATTACHMENT"); break; - case PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS: - Str.append(GetFullName ? "PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS" : "INLINE_CONSTANTS"); - break; - default: UNEXPECTED("Unexpected pipeline resource flag"); } @@ -1852,6 +1852,9 @@ PIPELINE_RESOURCE_FLAGS ShaderVariableFlagsToPipelineResourceFlags(SHADER_VARIAB case SHADER_VARIABLE_FLAG_NO_DYNAMIC_BUFFERS: return PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS; + case SHADER_VARIABLE_FLAG_INLINE_CONSTANTS: + return PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS; + case SHADER_VARIABLE_FLAG_GENERAL_INPUT_ATTACHMENT_VK: return PIPELINE_RESOURCE_FLAG_GENERAL_INPUT_ATTACHMENT; @@ -1859,9 +1862,6 @@ PIPELINE_RESOURCE_FLAGS ShaderVariableFlagsToPipelineResourceFlags(SHADER_VARIAB case SHADER_VARIABLE_FLAG_NON_FILTERING_SAMPLER_WEBGPU: return PIPELINE_RESOURCE_FLAG_NONE; - case SHADER_VARIABLE_FLAG_INLINE_CONSTANTS: - return PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS; - default: UNEXPECTED("Unexpected shader variable flag"); return PIPELINE_RESOURCE_FLAG_NONE; diff --git a/Graphics/GraphicsEngine/interface/PipelineResourceSignature.h b/Graphics/GraphicsEngine/interface/PipelineResourceSignature.h index 7f62e26129..1e2fb02465 100644 --- a/Graphics/GraphicsEngine/interface/PipelineResourceSignature.h +++ b/Graphics/GraphicsEngine/interface/PipelineResourceSignature.h @@ -105,9 +105,28 @@ DILIGENT_TYPED_ENUM(PIPELINE_RESOURCE_FLAGS, Uint8) /// PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS flag. PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS = 1u << 0, + /// Indicates that the resource consists of inline constants (also + /// known as push constants in Vulkan or root constants in Direct3D12). + /// + /// Applies to SHADER_RESOURCE_TYPE_CONSTANT_BUFFER only. + /// + /// Use this flag if you have a buffer of frequently changing constants + /// - that are small in size (typically up to 128 bytes) and + /// - change often (e.g. per-draw or per-dispatch). + /// + /// Inline constants are set directly using IShaderResourceVariable::SetInlineConstants. + /// + /// This flag cannot be combined with any other flags. + /// + /// In Vulkan and Direct3D12, inline constants are not bound via descriptor sets or root + /// signatures, but are set directly in command buffers or command lists and are very cheap. + /// In legacy APIs (Direct3D11 and OpenGL), inline constants are emulated using regular + /// constant buffers and thus have higher overhead. + PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS = 1u << 1, + /// Indicates that a texture SRV will be combined with a sampler. /// Applies to SHADER_RESOURCE_TYPE_TEXTURE_SRV resources. - PIPELINE_RESOURCE_FLAG_COMBINED_SAMPLER = 1u << 1, + PIPELINE_RESOURCE_FLAG_COMBINED_SAMPLER = 1u << 2, /// Indicates that this variable will be used to bind formatted buffers. /// Applies to SHADER_RESOURCE_TYPE_BUFFER_UAV and SHADER_RESOURCE_TYPE_BUFFER_SRV @@ -117,35 +136,19 @@ DILIGENT_TYPED_ENUM(PIPELINE_RESOURCE_FLAGS, Uint8) /// as opposed to structured buffers. If an application will be using /// formatted buffers with buffer UAVs and SRVs, it must specify the /// PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER flag. - PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER = 1u << 2, + PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER = 1u << 3, /// Indicates that resource is a run-time sized shader array (e.g. an array without a specific size). - PIPELINE_RESOURCE_FLAG_RUNTIME_ARRAY = 1u << 3, + PIPELINE_RESOURCE_FLAG_RUNTIME_ARRAY = 1u << 4, /// Indicates that the resource is an input attachment in general layout, which allows simultaneously /// reading from the resource through the input attachment and writing to it via color or depth-stencil /// attachment. /// /// \note This flag is only valid in Vulkan. - PIPELINE_RESOURCE_FLAG_GENERAL_INPUT_ATTACHMENT = 1u << 4, - - /// Indicates that the resource consists of inline constants (also known as push constants in Vulkan or root constants in Direct3D12). - - /// Applies to SHADER_RESOURCE_TYPE_CONSTANT_BUFFER only. - /// - /// Inline constants are set directly using IShaderResourceVariable::SetInlineConstants. - /// - /// This flag cannot be combined with any other flags. - /// - /// In Vulkan and Direct3D12, inline constants are not bound via descriptor sets or root - /// signatures, but are set directly in command buffers or command lists. As such, they - /// are very cheap to set and are intended for small pieces of frequently changing data. - /// - /// In legacy APIs (Direct3D11 and OpenGL), inline constants are emulated using regular - /// constant buffers and thus have higher overhead. - PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS = 1u << 5, + PIPELINE_RESOURCE_FLAG_GENERAL_INPUT_ATTACHMENT = 1u << 5, - PIPELINE_RESOURCE_FLAG_LAST = PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS + PIPELINE_RESOURCE_FLAG_LAST = PIPELINE_RESOURCE_FLAG_GENERAL_INPUT_ATTACHMENT }; DEFINE_FLAG_ENUM_OPERATORS(PIPELINE_RESOURCE_FLAGS); diff --git a/Graphics/GraphicsEngine/interface/PipelineState.h b/Graphics/GraphicsEngine/interface/PipelineState.h index ee6666d3af..0246eb8897 100644 --- a/Graphics/GraphicsEngine/interface/PipelineState.h +++ b/Graphics/GraphicsEngine/interface/PipelineState.h @@ -109,6 +109,8 @@ DILIGENT_TYPED_ENUM(SHADER_VARIABLE_FLAGS, Uint8) /// /// \remarks This flag directly translates to the Diligent::PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS /// flag in the internal pipeline resource signature. + /// + /// See Diligent::PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS for more details. SHADER_VARIABLE_FLAG_INLINE_CONSTANTS = 1u << 1, /// Indicates that the resource is an input attachment in general layout, which allows simultaneously diff --git a/Graphics/GraphicsEngine/interface/ShaderResourceVariable.h b/Graphics/GraphicsEngine/interface/ShaderResourceVariable.h index 346b1e806f..eeef1683b6 100644 --- a/Graphics/GraphicsEngine/interface/ShaderResourceVariable.h +++ b/Graphics/GraphicsEngine/interface/ShaderResourceVariable.h @@ -252,6 +252,9 @@ DILIGENT_BEGIN_INTERFACE(IShaderResourceVariable, IObject) /// For inline constant variables, sets the constant values + /// Inline constant variables are defined using Diligent::SHADER_RESOURCE_VARIABLE_FLAG_INLINE_CONSTANTS + /// or Diligent::PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS flags. + /// /// \param [in] pConstants - pointer to the array of 32-bit constant values. /// \param [in] FirstConstant - index of the first 32-bit constant to set. /// \param [in] NumConstants - number of 32-bit constants to set. diff --git a/Graphics/GraphicsEngineD3D11/include/ShaderResourceCacheD3D11.hpp b/Graphics/GraphicsEngineD3D11/include/ShaderResourceCacheD3D11.hpp index 51aececd76..d716a99a55 100644 --- a/Graphics/GraphicsEngineD3D11/include/ShaderResourceCacheD3D11.hpp +++ b/Graphics/GraphicsEngineD3D11/include/ShaderResourceCacheD3D11.hpp @@ -776,6 +776,7 @@ __forceinline void ShaderResourceCacheD3D11::SetInlineConstants(const D3D11Resou Uint32 FirstConstant, Uint32 NumConstants) { + // Since all shader stages share the same inline constant data, we can just set it for one stage SHADER_TYPE ActiveStages = BindPoints.GetActiveStages(); VERIFY_EXPR(ActiveStages != SHADER_TYPE_UNKNOWN); const Uint32 ShaderInd0 = ExtractFirstShaderStageIndex(ActiveStages); @@ -793,9 +794,9 @@ __forceinline void ShaderResourceCacheD3D11::SetInlineConstants(const D3D11Resou const auto ResArrays = GetResourceArrays(ShaderInd); VERIFY(ResArrays.first[Binding].pInlineConstantData == ResArrays0.first[Binding0].pInlineConstantData, - "All active shader stages must share the same inline constant data"); + "All shader stages must share the same inline constant data (ensured by InitInlineConstantBuffer)"); VERIFY(ResArrays.first[Binding] == ResArrays0.first[Binding0], - "All active shader stages must share the same inline constant data attributes"); + "All shader stages must share the same inline constant data attributes (ensured by InitInlineConstantBuffer)"); } #endif } @@ -859,6 +860,7 @@ bool ShaderResourceCacheD3D11::CopyResource(const ShaderResourceCacheD3D11& SrcC inline void ShaderResourceCacheD3D11::CopyInlineConstants(const ShaderResourceCacheD3D11& SrcCache, const D3D11ResourceBindPoints& BindPoints, Uint32 NumConstants) { + // Since all shader stages share the same inline constant data, we can just copy from one stage SHADER_TYPE ActiveStages = BindPoints.GetActiveStages(); VERIFY_EXPR(ActiveStages != SHADER_TYPE_UNKNOWN); @@ -889,12 +891,14 @@ inline void ShaderResourceCacheD3D11::CopyInlineConstants(const ShaderResourceCa const Uint32 Binding = BindPoints[ShaderInd]; VERIFY(Binding < GetResourceCount(ShaderInd), "Index is out of range"); VERIFY(Binding < SrcCache.GetResourceCount(ShaderInd), "Index is out of range"); - VERIFY(SrcResArrays0.first[Binding0] == SrcResArrays.first[Binding], "All shader stages must share the same inline constant data attributes"); - VERIFY(DstResArrays0.first[Binding0] == DstResArrays.first[Binding], "All shader stages must share the same inline constant data attributes"); + VERIFY(SrcResArrays0.first[Binding0] == SrcResArrays.first[Binding], + "All shader stages must share the same inline constant data attributes (ensured by InitInlineConstantBuffer)"); + VERIFY(DstResArrays0.first[Binding0] == DstResArrays.first[Binding], + "All shader stages must share the same inline constant data attributes (ensured by InitInlineConstantBuffer)"); VERIFY(SrcResArrays0.first[Binding0].pInlineConstantData == SrcResArrays.first[Binding].pInlineConstantData, - "All active shader stages must share the same inline constant data"); + "All shader stages must share the same inline constant data (ensured by InitInlineConstantBuffer)"); VERIFY(DstResArrays0.first[Binding0].pInlineConstantData == DstResArrays.first[Binding].pInlineConstantData, - "All active shader stages must share the same inline constant data"); + "All shader stages must share the same inline constant data (ensured by InitInlineConstantBuffer)"); } #endif } diff --git a/Graphics/GraphicsEngineD3D11/src/DeviceContextD3D11Impl.cpp b/Graphics/GraphicsEngineD3D11/src/DeviceContextD3D11Impl.cpp index 20119211be..319cd82d31 100755 --- a/Graphics/GraphicsEngineD3D11/src/DeviceContextD3D11Impl.cpp +++ b/Graphics/GraphicsEngineD3D11/src/DeviceContextD3D11Impl.cpp @@ -454,7 +454,8 @@ void DeviceContextD3D11Impl::BindShaderResources(Uint32 BindSRBMask, continue; } - if (m_BindInfo.StaleSRBMask & SignBit) + const bool SRBStale = (m_BindInfo.StaleSRBMask & SignBit) != 0; + if (SRBStale) { // Bind all cache resources BindCacheResources(*pResourceCache, BaseBindings, PsUavBindMode); @@ -494,7 +495,8 @@ void DeviceContextD3D11Impl::BindShaderResources(Uint32 BindSRBMask, "Shader resource cache does not contain inline constants, but the corresponding bit in InlineConstantsSRBMask is set. " "This may be a bug because inline constants flag in the cache never changes after SRB creation, " "while m_BindInfo.InlineConstantsSRBMask is initialized when SRB is committed."); - if ((m_BindInfo.StaleSRBMask & SignBit) != 0 || !InlineConstantsIntact) + // Always update inline constant buffers if the SRB is stale + if (SRBStale || !InlineConstantsIntact) { if (PipelineResourceSignatureD3D11Impl* pSign = m_pPipelineState->GetResourceSignature(SignIdx)) { @@ -715,7 +717,7 @@ void DeviceContextD3D11Impl::PrepareForDraw(DRAW_FLAGS Flags) const bool DynamicBuffersIntact = (Flags & DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT) != 0; const bool InlineConstantsIntact = (Flags & DRAW_FLAG_INLINE_CONSTANTS_INTACT) != 0; - if (Uint32 BindSRBMask = m_BindInfo.GetCommitMask(Flags & DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT, Flags & DRAW_FLAG_INLINE_CONSTANTS_INTACT)) + if (Uint32 BindSRBMask = m_BindInfo.GetCommitMask(DynamicBuffersIntact, InlineConstantsIntact)) { BindShaderResources(BindSRBMask, DynamicBuffersIntact, InlineConstantsIntact); } diff --git a/Graphics/GraphicsEngineD3D11/src/PipelineResourceSignatureD3D11Impl.cpp b/Graphics/GraphicsEngineD3D11/src/PipelineResourceSignatureD3D11Impl.cpp index f175be9a4f..c02c8e3233 100644 --- a/Graphics/GraphicsEngineD3D11/src/PipelineResourceSignatureD3D11Impl.cpp +++ b/Graphics/GraphicsEngineD3D11/src/PipelineResourceSignatureD3D11Impl.cpp @@ -212,7 +212,8 @@ void PipelineResourceSignatureD3D11Impl::CreateLayout(const bool IsSerialized) VERIFY((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) == 0 || ResDesc.ResourceType == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, "Only constant buffers can have inline constants flag"); - // For inline constants, ArraySize holds the number of 4-byte constants + // For inline constants, ArraySize holds the number of 4-byte constants, while + // the resource occupies a single constant buffer slot. const Uint32 ArraySize = ResDesc.GetArraySize(); AllocBindPoints(m_ResourceCounters, BindPoints, ResDesc.ShaderStages, ArraySize, Range); if (ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) @@ -246,10 +247,20 @@ void PipelineResourceSignatureD3D11Impl::CreateLayout(const bool IsSerialized) if (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) { + // Inline constant buffers are handled mostly like regular constant buffers. The only + // difference is that the buffer is created internally here and is not expected to be bound. + // It is updated by UpdateInlineConstantBuffers() method. + VERIFY(ResDesc.ResourceType == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, "Only constant buffers can have INLINE_CONSTANTS flag"); InlineConstantBufferAttribsD3D11& InlineCBAttribs{m_InlineConstantBuffers[InlineConstantBufferIdx++]}; InlineCBAttribs.BindPoints = BindPoints; InlineCBAttribs.NumConstants = ResDesc.ArraySize; + + // All SRBs created from this signature will share the same inline constant buffer. + // An alternative design is to have a separate inline constant buffer for each SRB, + // which will allow skipping buffer update if the inline constants are not changed. + // However, this will increase memory consumption as each SRB will have its own copy of the inline CB. + // Besides, inline constants are expected to change frequently, so skipping updates is unlikely. if (m_pDevice) { std::string Name = m_Desc.Name; @@ -437,7 +448,8 @@ void PipelineResourceSignatureD3D11Impl::UpdateShaderResourceBindingMap(Resource { Uint32{BaseBindings[Range][ShaderInd]} + Uint32{ResAttr.BindPoints[ShaderInd]}, 0u, // register space is not supported - ResDesc.GetArraySize(), // For inline constants, ArraySize holds the number of 4-byte constants + ResDesc.GetArraySize(), // For inline constants, ArraySize holds the number of 4-byte constants, + // while the resource occupies a single constant buffer slot. ResDesc.ResourceType, }; bool IsUnique = ResourceMap.emplace(HashMapStringKey{ResDesc.Name}, BindInfo).second; @@ -528,7 +540,7 @@ bool PipelineResourceSignatureD3D11Impl::DvpValidateCommittedResource(const D3DS const PipelineResourceAttribsD3D11& ResAttr = m_pResourceAttribs[ResIndex]; VERIFY(strcmp(ResDesc.Name, D3DAttribs.Name) == 0, "Inconsistent resource names"); - VERIFY_EXPR(D3DAttribs.BindCount <= ResDesc.ArraySize); + VERIFY_EXPR(D3DAttribs.BindCount <= ResDesc.GetArraySize()); bool BindingsOK = true; switch (ShaderResourceTypeToRange(ResDesc.ResourceType)) diff --git a/Graphics/GraphicsEngineD3D11/src/ShaderResourceCacheD3D11.cpp b/Graphics/GraphicsEngineD3D11/src/ShaderResourceCacheD3D11.cpp index bed80bba72..a5df147fb7 100755 --- a/Graphics/GraphicsEngineD3D11/src/ShaderResourceCacheD3D11.cpp +++ b/Graphics/GraphicsEngineD3D11/src/ShaderResourceCacheD3D11.cpp @@ -145,7 +145,9 @@ void ShaderResourceCacheD3D11::Initialize(const D3D11ShaderResourceCounters& SHADER_TYPE ActiveStages = InlineCBAttr.BindPoints.GetActiveStages(); const Uint32 ShaderInd0 = ExtractFirstShaderStageIndex(ActiveStages); const Uint32 Binding0 = InlineCBAttr.BindPoints[ShaderInd0]; - const bool IsInRange = Binding0 < GetResourceCount(ShaderInd0); + // Static resource cache may not contain all inline constant buffers. + // Skip those that are out of range. + const bool IsInRange = Binding0 < GetResourceCount(ShaderInd0); #ifdef DILIGENT_DEBUG while (ActiveStages != SHADER_TYPE_UNKNOWN) @@ -200,6 +202,7 @@ void ShaderResourceCacheD3D11::Initialize(const D3D11ShaderResourceCounters& ProcessInlineCBs([&pInlineCBData, this](const InlineConstantBufferAttribsD3D11& InlineCBAttr) { VERIFY_EXPR(InlineCBAttr.NumConstants > 0); VERIFY_EXPR(InlineCBAttr.pBuffer != nullptr); + // Use the same buffer and data pointer for all active shader stages. InitInlineConstantBuffer(InlineCBAttr.BindPoints, InlineCBAttr.pBuffer, InlineCBAttr.NumConstants, pInlineCBData); pInlineCBData += InlineCBAttr.NumConstants; }); @@ -233,6 +236,7 @@ void ShaderResourceCacheD3D11::InitInlineConstantBuffer(const D3D11ResourceBindP Uint32 NumConstants, void* pInlineConstantData) { + // Use the same buffer and data pointer for all shader stages. VERIFY_EXPR(pBuffer); VERIFY_EXPR(pInlineConstantData); ID3D11Buffer* pd3d11Buffer = pBuffer->GetD3D11Buffer(); diff --git a/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp index ae307aa04b..9d06cb95aa 100644 --- a/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/PipelineResourceSignatureD3D12Impl.cpp @@ -246,6 +246,10 @@ void PipelineResourceSignatureD3D12Impl::AllocateRootParameters(const bool IsSer // Do not allocate resource slot for immutable samplers that are also defined as resource if (!(ResDesc.ResourceType == SHADER_RESOURCE_TYPE_SAMPLER && SrcImmutableSamplerInd != InvalidImmutableSamplerIndex)) { + // For inline constants, ArraySize holds the number of 4-byte constants, + // while the resource occupies only one register slot. + const Uint32 ArraySize = ResDesc.GetArraySize(); + if (ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) { // Use artificial root signature: @@ -261,16 +265,12 @@ void PipelineResourceSignatureD3D12Impl::AllocateRootParameters(const bool IsSer ShaderResourceCacheD3D12::InlineConstantParamInfo InlineConstInfo; InlineConstInfo.RootIndex = SigRootIndex; InlineConstInfo.OffsetFromTableStart = SigOffsetFromTableStart; - InlineConstInfo.NumValues = ResDesc.ArraySize; + // For inline constants, ArraySize holds the number of 4-byte constants. + InlineConstInfo.NumValues = ResDesc.ArraySize; StaticResInlineConstInfo.push_back(InlineConstInfo); - - // For inline constants, count only one resource as the ArraySize is the number of 4-byte constants - StaticResCacheTblSizes[SigRootIndex] += 1; - } - else - { - StaticResCacheTblSizes[SigRootIndex] += ResDesc.ArraySize; } + + StaticResCacheTblSizes[SigRootIndex] += ArraySize; } if (IsRTSizedArray) @@ -284,8 +284,7 @@ void PipelineResourceSignatureD3D12Impl::AllocateRootParameters(const bool IsSer // Normal resources go into space 0. Space = 0; Register = NumResources[d3d12DescriptorRangeType]; - // For inline constants, ArraySize holds the number of 4-byte constants. - NumResources[d3d12DescriptorRangeType] += ResDesc.GetArraySize(); + NumResources[d3d12DescriptorRangeType] += ArraySize; } const PIPELINE_RESOURCE_FLAGS dbgValidResourceFlags = GetValidPipelineResourceFlags(ResDesc.ResourceType); @@ -293,7 +292,7 @@ void PipelineResourceSignatureD3D12Impl::AllocateRootParameters(const bool IsSer const bool UseDynamicOffset = (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS) == 0; const bool IsFormattedBuffer = (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER) != 0; - const bool IsArray = ResDesc.ArraySize != 1; + const bool IsArray = ArraySize != 1; d3d12RootParamType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; static_assert(SHADER_RESOURCE_TYPE_LAST == SHADER_RESOURCE_TYPE_ACCEL_STRUCT, "Please update the switch below to handle the new shader resource type"); @@ -325,8 +324,10 @@ void PipelineResourceSignatureD3D12Impl::AllocateRootParameters(const bool IsSer d3d12RootParamType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; } - ParamsBuilder.AllocateResourceSlot(ResDesc.ShaderStages, ResDesc.VarType, d3d12RootParamType, - d3d12DescriptorRangeType, ResDesc.ArraySize, Register, Space, + ParamsBuilder.AllocateResourceSlot(ResDesc.ShaderStages, ResDesc.VarType, + d3d12RootParamType, d3d12DescriptorRangeType, + ResDesc.ArraySize, // Array size or the number of inline constants + Register, Space, SRBRootIndex, SRBOffsetFromTableStart); } else @@ -453,6 +454,13 @@ void PipelineResourceSignatureD3D12Impl::CopyStaticResources(ShaderResourceCache // For inline constants, array size is the number of 4-byte constant values const Uint32 NumConstantValues = ResDesc.ArraySize; + VERIFY(SrcRes.BufferRangeSize == NumConstantValues * sizeof(Uint32), + "Source inline constant buffer range size (", SrcRes.BufferRangeSize, + ") does not match the expected size (", NumConstantValues * sizeof(Uint32), ")."); + VERIFY(DstRes.BufferRangeSize == NumConstantValues * sizeof(Uint32), + "Destination inline constant buffer range size (", DstRes.BufferRangeSize, + ") does not match the expected size (", NumConstantValues * sizeof(Uint32), ")."); + // Copy the actual constant values. // For inline constants, CPUDescriptorHandle.ptr stores the pointer to the constant values buffer. memcpy(reinterpret_cast(DstRes.CPUDescriptorHandle.ptr), @@ -590,8 +598,9 @@ void PipelineResourceSignatureD3D12Impl::CommitRootConstants(const CommitCacheRe const Uint32* pConstants = reinterpret_cast(Res.CPUDescriptorHandle.ptr); VERIFY(pConstants != nullptr, "Resources used to store root constants must have valid pointer to the data."); - // The number of root constants is stored in BufferRangeSize - const Uint32 NumConstants = static_cast(Res.BufferRangeSize); + // Get the number of 4-byte constants from the buffer range size + const Uint32 NumConstants = static_cast(Res.BufferRangeSize / sizeof(Uint32)); + VERIFY_EXPR(NumConstants > 0); ID3D12GraphicsCommandList* const pd3d12CmdList = CommitAttribs.CmdCtx.GetCommandList(); if (CommitAttribs.IsCompute) @@ -736,7 +745,8 @@ void PipelineResourceSignatureD3D12Impl::UpdateShaderResourceBindingMap(Resource { Attribs.Register, Attribs.Space + BaseRegisterSpace, - ResDesc.GetArraySize(), // For inline constants, ArraySize holds the number of 4-byte constants + ResDesc.GetArraySize(), // For inline constants, ArraySize holds the number of 4-byte constants, + // while the resource occupies only one register. ResDesc.ResourceType, }; bool IsUnique = ResourceMap.emplace(HashMapStringKey{ResDesc.Name}, BindInfo).second; @@ -811,7 +821,7 @@ bool PipelineResourceSignatureD3D12Impl::DvpValidateCommittedResource(const Devi const PipelineResourceDesc& ResDesc = GetResourceDesc(ResIndex); const PipelineResourceAttribsD3D12& ResAttribs = GetResourceAttribs(ResIndex); VERIFY_EXPR(strcmp(ResDesc.Name, D3DAttribs.Name) == 0); - VERIFY_EXPR(D3DAttribs.BindCount <= ResDesc.ArraySize); + VERIFY_EXPR(D3DAttribs.BindCount <= ResDesc.GetArraySize()); if ((ResDesc.ResourceType == SHADER_RESOURCE_TYPE_SAMPLER) && ResAttribs.IsImmutableSamplerAssigned()) return true; @@ -828,6 +838,7 @@ bool PipelineResourceSignatureD3D12Impl::DvpValidateCommittedResource(const Devi const ShaderResourceCacheD3D12::Resource& CachedRes = RootTable.GetResource(OffsetFromTableStart + ArrIndex); if (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) { + VERIFY(ResDesc.ResourceType == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, "Only constant buffers can be marked as INLINE_CONSTANTS."); VERIFY(CachedRes.IsNull(), "Inline constants should not have any resource bound to them."); VERIFY(CachedRes.CPUDescriptorHandle.ptr != 0, "Inline constant resource must have valid CPU descriptor handle."); // Inline constants are not actual resources diff --git a/Graphics/GraphicsEngineD3D12/src/RootParamsManager.cpp b/Graphics/GraphicsEngineD3D12/src/RootParamsManager.cpp index b04c927d73..80d3cba945 100644 --- a/Graphics/GraphicsEngineD3D12/src/RootParamsManager.cpp +++ b/Graphics/GraphicsEngineD3D12/src/RootParamsManager.cpp @@ -390,7 +390,8 @@ void RootParamsBuilder::AllocateResourceSlot(SHADER_TYPE Shade { OffsetFromTableStart = 0; - // Add new 32-bit constants parameter to existing root parameters + // Add new 32-bit constants parameter to existing root parameters. + // ArraySize parameter specifies the number of 32-bit values in this case. AddRootConstants(RootIndex, Register, Space, ArraySize, ShaderVisibility, ParameterGroup); } else if (RootParameterType == D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE) diff --git a/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp b/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp index 48822b723e..1b7ee3785a 100644 --- a/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp +++ b/Graphics/GraphicsEngineD3D12/src/ShaderResourceCacheD3D12.cpp @@ -175,8 +175,8 @@ void ShaderResourceCacheD3D12::Initialize(IMemoryAllocator& Me // Note that normally resource type is set when a resource is bound to the cache. // For inline constants, we set it here during tinitialization. Res.Type = SHADER_RESOURCE_TYPE_CONSTANT_BUFFER; - // Store the number of constant values in BufferRangeSize - Res.BufferRangeSize = InlineConstInfo.NumValues; + // Set the buffer range size to match the number of constant values + Res.BufferRangeSize = InlineConstInfo.NumValues * sizeof(Uint32); pCurrInlineConstValueStorage += InlineConstInfo.NumValues; m_RootConstantsMask |= (Uint64{1} << Uint64{InlineConstInfo.RootIndex}); @@ -274,8 +274,8 @@ void ShaderResourceCacheD3D12::Initialize(IMemoryAllocator& MemAllocator, // Note that normally resource type is set when a resource is bound to the cache. // For inline constants, we set it here during tinitialization. Res.Type = SHADER_RESOURCE_TYPE_CONSTANT_BUFFER; - // Store the number of constant values in BufferRangeSize - Res.BufferRangeSize = RootConsts.d3d12RootParam.Constants.Num32BitValues; + // Set the buffer range size to match the number of constant values + Res.BufferRangeSize = RootConsts.d3d12RootParam.Constants.Num32BitValues * sizeof(Uint32); pCurrInlineConstValueStorage += RootConsts.d3d12RootParam.Constants.Num32BitValues; @@ -473,8 +473,8 @@ void ShaderResourceCacheD3D12::SetInlineConstants(Uint32 RootIndex, VERIFY_EXPR(Res.Type == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER); VERIFY(Res.IsNull(), "There should be no resource bound for root constants as they contain raw data."); VERIFY(Res.CPUDescriptorHandle.ptr != 0, "Resources used to store root constants must have valid pointer to the data."); - VERIFY(FirstConstant + NumConstants <= Res.BufferRangeSize, - "Too many constants (", FirstConstant + NumConstants, ") for the allocated space (", Res.BufferRangeSize, ")"); + VERIFY(FirstConstant + NumConstants <= Res.BufferRangeSize / sizeof(Uint32), + "Too many constants (", FirstConstant + NumConstants, ") for the allocated space (", Res.BufferRangeSize / sizeof(Uint32), ")"); Uint32* pDstConstants = reinterpret_cast(Res.CPUDescriptorHandle.ptr); memcpy(pDstConstants + FirstConstant, pConstants, NumConstants * sizeof(Uint32)); } diff --git a/Tests/DiligentCoreAPITest/src/ObjectCreationFailure/PRSCreationFailureTest.cpp b/Tests/DiligentCoreAPITest/src/ObjectCreationFailure/PRSCreationFailureTest.cpp index 9a652354b2..53821b3180 100644 --- a/Tests/DiligentCoreAPITest/src/ObjectCreationFailure/PRSCreationFailureTest.cpp +++ b/Tests/DiligentCoreAPITest/src/ObjectCreationFailure/PRSCreationFailureTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2024 Diligent Graphics LLC + * Copyright 2019-2025 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -40,8 +40,8 @@ namespace static void TestCreatePRSFailure(PipelineResourceSignatureDesc CI, const char* ExpectedErrorSubstring) { - auto* const pEnv = GPUTestingEnvironment::GetInstance(); - auto* const pDevice = pEnv->GetDevice(); + GPUTestingEnvironment* const pEnv = GPUTestingEnvironment::GetInstance(); + IRenderDevice* const pDevice = pEnv->GetDevice(); RefCntAutoPtr pSignature; pEnv->SetErrorAllowance(2, "Errors below are expected: testing PRS creation failure\n"); @@ -166,7 +166,7 @@ TEST(PRSCreationFailureTest, InvalidResourceFlag) {SHADER_TYPE_VERTEX, "g_Buffer", 1, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_STATIC, PIPELINE_RESOURCE_FLAG_COMBINED_SAMPLER | PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER}}; PRSDesc.Resources = Resources; PRSDesc.NumResources = _countof(Resources); - TestCreatePRSFailure(PRSDesc, "Incorrect Desc.Resources[1].Flags (COMBINED_SAMPLER|FORMATTED_BUFFER). Only the following flags are valid for a constant buffer: NO_DYNAMIC_BUFFERS, RUNTIME_ARRAY"); + TestCreatePRSFailure(PRSDesc, "Incorrect Desc.Resources[1].Flags (COMBINED_SAMPLER|FORMATTED_BUFFER). Only the following flags are valid for a constant buffer: NO_DYNAMIC_BUFFERS, INLINE_CONSTANTS, RUNTIME_ARRAY"); } TEST(PRSCreationFailureTest, InvalidTexSRVFlag) @@ -255,7 +255,7 @@ TEST(PRSCreationFailureTest, InvalidAccelStructFlag) TEST(PRSCreationFailureTest, InvalidCombinedSamplerFlag) { - const auto& DeviceInfo = GPUTestingEnvironment::GetInstance()->GetDevice()->GetDeviceInfo(); + const RenderDeviceInfo& DeviceInfo = GPUTestingEnvironment::GetInstance()->GetDevice()->GetDeviceInfo(); if (!(DeviceInfo.IsD3DDevice() || DeviceInfo.IsMetalDevice())) { GTEST_SKIP() << "Direct3D11, Direct3D12 and Metal only"; From 68a2c34c1ea0af53ea4e36c22170572e17a489f1 Mon Sep 17 00:00:00 2001 From: assiduous Date: Thu, 30 Oct 2025 18:30:33 -0700 Subject: [PATCH 033/104] Add tests for inline constants flag validation --- .../src/PipelineResourceSignatureBase.cpp | 5 ---- .../src/ShaderVariableManagerD3D11.cpp | 2 -- .../PRSCreationFailureTest.cpp | 24 ++++++++++++++++ .../PSOCreationFailureTest.cpp | 28 ++++++++++++++----- 4 files changed, 45 insertions(+), 14 deletions(-) diff --git a/Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp b/Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp index 8104d3eed3..350a5a0b9c 100644 --- a/Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp +++ b/Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp @@ -119,11 +119,6 @@ void ValidatePipelineResourceSignatureDesc(const PipelineResourceSignatureDesc& "). INLINE_CONSTANTS flag cannot be combined with other flags."); } - if (Res.ArraySize == 0) - { - LOG_PRS_ERROR_AND_THROW("Desc.Resources[", i, "].ArraySize must be greater than 0 for inline constants."); - } - if (Res.ArraySize > 64) { LOG_PRS_ERROR_AND_THROW("Desc.Resources[", i, "].ArraySize (", Res.ArraySize, diff --git a/Graphics/GraphicsEngineD3D11/src/ShaderVariableManagerD3D11.cpp b/Graphics/GraphicsEngineD3D11/src/ShaderVariableManagerD3D11.cpp index 7e92c0e8f3..6a8df98181 100644 --- a/Graphics/GraphicsEngineD3D11/src/ShaderVariableManagerD3D11.cpp +++ b/Graphics/GraphicsEngineD3D11/src/ShaderVariableManagerD3D11.cpp @@ -291,7 +291,6 @@ void ShaderVariableManagerD3D11::ConstBuffBindInfo::SetDynamicOffset(Uint32 Arra const PipelineResourceAttribsD3D11& Attr = GetAttribs(); const PipelineResourceDesc& Desc = GetDesc(); VERIFY_EXPR(Desc.ResourceType == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER); - VERIFY_EXPR((Desc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) == 0); #ifdef DILIGENT_DEVELOPMENT { const ShaderResourceCacheD3D11::CachedCB& CachedCB = m_ParentManager.m_ResourceCache.GetResource(Attr.BindPoints + ArrayIndex); @@ -306,7 +305,6 @@ void ShaderVariableManagerD3D11::ConstBuffBindInfo::SetConstants(const void* pCo const PipelineResourceAttribsD3D11& Attr = GetAttribs(); const PipelineResourceDesc& Desc = GetDesc(); VERIFY_EXPR(Desc.ResourceType == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER); - VERIFY_EXPR(Desc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS); #ifdef DILIGENT_DEVELOPMENT { VerifyInlineConstants(Desc, pConstants, FirstConstant, NumConstants); diff --git a/Tests/DiligentCoreAPITest/src/ObjectCreationFailure/PRSCreationFailureTest.cpp b/Tests/DiligentCoreAPITest/src/ObjectCreationFailure/PRSCreationFailureTest.cpp index 53821b3180..00948ffdf5 100644 --- a/Tests/DiligentCoreAPITest/src/ObjectCreationFailure/PRSCreationFailureTest.cpp +++ b/Tests/DiligentCoreAPITest/src/ObjectCreationFailure/PRSCreationFailureTest.cpp @@ -271,6 +271,30 @@ TEST(PRSCreationFailureTest, InvalidCombinedSamplerFlag) TestCreatePRSFailure(PRSDesc, "Desc.Resources[0].Flags contain COMBINED_SAMPLER flag, but Desc.UseCombinedTextureSamplers is false"); } +TEST(PRSCreationFailureTest, InvalidInlineConstantsFlag) +{ + PipelineResourceSignatureDesc PRSDesc; + PRSDesc.Name = "Invalid inline constants Flag"; + PRSDesc.UseCombinedTextureSamplers = false; + PipelineResourceDesc Resources[]{ + {SHADER_TYPE_PIXEL, "g_InlineConstants", 1, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_STATIC, PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS | PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS}}; + PRSDesc.Resources = Resources; + PRSDesc.NumResources = _countof(Resources); + TestCreatePRSFailure(PRSDesc, "INLINE_CONSTANTS flag cannot be combined with other flags"); +} + +TEST(PRSCreationFailureTest, InvalidInlineConstantCount) +{ + PipelineResourceSignatureDesc PRSDesc; + PRSDesc.Name = "Invalid inline constant count"; + PRSDesc.UseCombinedTextureSamplers = false; + PipelineResourceDesc Resources[]{ + {SHADER_TYPE_PIXEL, "g_InlineConstants", 65, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_STATIC, PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS}}; + PRSDesc.Resources = Resources; + PRSDesc.NumResources = _countof(Resources); + TestCreatePRSFailure(PRSDesc, "Desc.Resources[0].ArraySize (65) exceeds the maximum allowed value (64) for inline constants."); +} + TEST(PRSCreationFailureTest, InvalidAssignedSamplerResourceType) { PipelineResourceSignatureDesc PRSDesc; diff --git a/Tests/DiligentCoreAPITest/src/ObjectCreationFailure/PSOCreationFailureTest.cpp b/Tests/DiligentCoreAPITest/src/ObjectCreationFailure/PSOCreationFailureTest.cpp index c47105e0e5..79d614daf1 100644 --- a/Tests/DiligentCoreAPITest/src/ObjectCreationFailure/PSOCreationFailureTest.cpp +++ b/Tests/DiligentCoreAPITest/src/ObjectCreationFailure/PSOCreationFailureTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2024 Diligent Graphics LLC + * Copyright 2019-2025 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -887,6 +887,20 @@ TEST_F(PSOCreationFailureTest, OverlappingImmutableSamplerStages) TestCreatePSOFailure(PsoCI, "'g_Texture_sampler' is defined in overlapping shader stages (SHADER_TYPE_VERTEX, SHADER_TYPE_GEOMETRY and SHADER_TYPE_VERTEX, SHADER_TYPE_PIXEL)"); } +TEST_F(PSOCreationFailureTest, InvalidInlineConstantsFlag) +{ + auto PsoCI{GetGraphicsPSOCreateInfo("PSO Create Failure - Invalid Inline Constants Flag")}; + + ShaderResourceVariableDesc Variables[] // + { + ShaderResourceVariableDesc{SHADER_TYPE_VERTEX | SHADER_TYPE_PIXEL, "g_Texture", SHADER_RESOURCE_VARIABLE_TYPE_STATIC}, + ShaderResourceVariableDesc{SHADER_TYPE_VERTEX | SHADER_TYPE_PIXEL, "g_InlineConstants", SHADER_RESOURCE_VARIABLE_TYPE_STATIC, SHADER_VARIABLE_FLAG_INLINE_CONSTANTS | SHADER_VARIABLE_FLAG_NO_DYNAMIC_BUFFERS} // + }; + PsoCI.PSODesc.ResourceLayout.Variables = Variables; + PsoCI.PSODesc.ResourceLayout.NumVariables = _countof(Variables); + TestCreatePSOFailure(PsoCI, "ResourceLayout.Variables[1].Flags: INLINE_CONSTANTS flag cannot be combined with other flags."); +} + TEST_F(PSOCreationFailureTest, RenderPassWithNonZeroNumRenderTargets) { auto PsoCI{GetGraphicsPSOCreateInfo("PSO Create Failure - Render Pass With non-zero NumRenderTargets", true)}; @@ -1167,7 +1181,7 @@ TEST_F(PSOCreationFailureTest, NullProcHitShader) TestCreatePSOFailure(PsoCI, "pProceduralHitShaders[0].pIntersectionShader must not be null"); } -TEST_F(PSOCreationFailureTest, InvalidShaderinGeneralGroup) +TEST_F(PSOCreationFailureTest, InvalidShaderInGeneralGroup) { if (!HasRayTracing()) GTEST_SKIP(); @@ -1180,7 +1194,7 @@ TEST_F(PSOCreationFailureTest, InvalidShaderinGeneralGroup) TestCreatePSOFailure(PsoCI, "SHADER_TYPE_RAY_CLOSEST_HIT is not a valid type for ray tracing general shader"); } -TEST_F(PSOCreationFailureTest, InvalidShaderinTriangleHitGroup1) +TEST_F(PSOCreationFailureTest, InvalidShaderInTriangleHitGroup1) { if (!HasRayTracing()) GTEST_SKIP(); @@ -1197,7 +1211,7 @@ TEST_F(PSOCreationFailureTest, InvalidShaderinTriangleHitGroup1) TestCreatePSOFailure(PsoCI, "SHADER_TYPE_RAY_MISS is not a valid type for ray tracing triangle closest hit"); } -TEST_F(PSOCreationFailureTest, InvalidShaderinTriangleHitGroup2) +TEST_F(PSOCreationFailureTest, InvalidShaderInTriangleHitGroup2) { if (!HasRayTracing()) GTEST_SKIP(); @@ -1214,7 +1228,7 @@ TEST_F(PSOCreationFailureTest, InvalidShaderinTriangleHitGroup2) TestCreatePSOFailure(PsoCI, "SHADER_TYPE_RAY_INTERSECTION is not a valid type for ray tracing triangle any hit"); } -TEST_F(PSOCreationFailureTest, InvalidShaderinProceduralHitGroup1) +TEST_F(PSOCreationFailureTest, InvalidShaderInProceduralHitGroup1) { if (!HasRayTracing()) GTEST_SKIP(); @@ -1232,7 +1246,7 @@ TEST_F(PSOCreationFailureTest, InvalidShaderinProceduralHitGroup1) TestCreatePSOFailure(PsoCI, "SHADER_TYPE_CALLABLE is not a valid type for ray tracing procedural intersection"); } -TEST_F(PSOCreationFailureTest, InvalidShaderinProceduralHitGroup2) +TEST_F(PSOCreationFailureTest, InvalidShaderInProceduralHitGroup2) { if (!HasRayTracing()) GTEST_SKIP(); @@ -1250,7 +1264,7 @@ TEST_F(PSOCreationFailureTest, InvalidShaderinProceduralHitGroup2) TestCreatePSOFailure(PsoCI, "SHADER_TYPE_RAY_GEN is not a valid type for ray tracing procedural closest hit"); } -TEST_F(PSOCreationFailureTest, InvalidShaderinProceduralHitGroup3) +TEST_F(PSOCreationFailureTest, InvalidShaderInProceduralHitGroup3) { if (!HasRayTracing()) GTEST_SKIP(); From 0324728dcfc08a971aea1988f8cbde03e0f3e23f Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sat, 6 Dec 2025 22:20:32 +0800 Subject: [PATCH 034/104] Add vulkan inline constants support, emulated with uniform buffers. (PushConstants not impl yet) --- .../include/DeviceContextVkImpl.hpp | 3 + .../PipelineResourceSignatureVkImpl.hpp | 31 + .../include/ShaderResourceCacheVk.hpp | 42 +- .../src/DeviceContextVkImpl.cpp | 34 + .../src/PipelineResourceSignatureVkImpl.cpp | 305 ++++- .../src/PipelineStateVkImpl.cpp | 23 +- .../src/ShaderResourceCacheVk.cpp | 58 + .../src/ShaderVariableManagerVk.cpp | 17 +- doc/SetInlineConstants.md | 1037 +++++++++++++++++ 9 files changed, 1530 insertions(+), 20 deletions(-) create mode 100644 doc/SetInlineConstants.md diff --git a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp index d86ffd775d..5fb0ed0797 100644 --- a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp @@ -555,6 +555,9 @@ class DeviceContextVkImpl final : public DeviceContextNextGenBase pBuffer; // Internal dynamic uniform buffer +}; + struct PipelineResourceSignatureInternalDataVk : PipelineResourceSignatureInternalData { Uint16 DynamicUniformBufferCount = 0; @@ -134,6 +146,16 @@ class PipelineResourceSignatureVkImpl final : public PipelineResourceSignatureBa void CommitDynamicResources(const ShaderResourceCacheVk& ResourceCache, VkDescriptorSet vkDynamicDescriptorSet) const; + // Updates inline constant buffers by mapping the internal buffers and copying data from the resource cache + void UpdateInlineConstantBuffers(const ShaderResourceCacheVk& ResourceCache, + DeviceContextVkImpl& Ctx) 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(); } + #ifdef DILIGENT_DEVELOPMENT /// Verifies committed resource using the SPIRV resource attributes from the PSO. bool DvpValidateCommittedResource(const DeviceContextVkImpl* pDeviceCtx, @@ -193,6 +215,15 @@ class PipelineResourceSignatureVkImpl final : public PipelineResourceSignatureBa // The total number storage buffers with dynamic offsets in both descriptor sets, // accounting for array size. Uint16 m_DynamicStorageBufferCount = 0; + + // Number of inline constant buffers + Uint32 m_NumInlineConstantBuffers = 0; + // Inline constant buffer attributes + std::unique_ptr m_InlineConstantBuffers; + + // Pointer to CPU-side staging buffer for static inline constants + // This memory is allocated in CreateSetLayouts and must be freed in Destruct + void* m_pStaticInlineConstantData = nullptr; }; template <> Uint32 PipelineResourceSignatureVkImpl::GetDescriptorSetIndex() const; diff --git a/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp b/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp index 26bf336e4d..ae548888fb 100644 --- a/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp +++ b/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp @@ -74,7 +74,8 @@ class ShaderResourceCacheVk : public ShaderResourceCacheBase public: explicit ShaderResourceCacheVk(ResourceCacheContentType ContentType) noexcept : m_TotalResources{0}, - m_ContentType{static_cast(ContentType)} + m_ContentType{static_cast(ContentType)}, + m_HasInlineConstants{0} { VERIFY_EXPR(GetContentType() == ContentType); } @@ -93,7 +94,7 @@ class ShaderResourceCacheVk : public ShaderResourceCacheBase void InitializeSets(IMemoryAllocator& MemAllocator, Uint32 NumSets, const Uint32* SetSizes); void InitializeResources(Uint32 Set, Uint32 Offset, Uint32 ArraySize, DescriptorType Type, bool HasImmutableSampler); - // sizeof(Resource) == 32 (x64, msvc, Release) + // sizeof(Resource) == 40 (x64, msvc, Release) struct Resource { explicit Resource(DescriptorType _Type, bool _HasImmutableSampler) noexcept : @@ -120,6 +121,10 @@ class ShaderResourceCacheVk : public ShaderResourceCacheBase /*16 */ Uint64 BufferBaseOffset = 0; /*24 */ Uint64 BufferRangeSize = 0; + // For inline constants only - pointer to CPU-side staging buffer +/*32 */ void* pInlineConstantData = nullptr; +/*40 */ // End of structure + VkDescriptorBufferInfo GetUniformBufferDescriptorWriteInfo() const; VkDescriptorBufferInfo GetStorageBufferDescriptorWriteInfo() const; VkDescriptorImageInfo GetImageDescriptorWriteInfo () const; @@ -245,13 +250,36 @@ class ShaderResourceCacheVk : public ShaderResourceCacheBase Uint32 CacheOffset, Uint32 DynamicBufferOffset); + // Sets inline constant data in the resource cache + void SetInlineConstants(Uint32 DescrSetIndex, + Uint32 CacheOffset, + const void* pConstants, + Uint32 FirstConstant, + Uint32 NumConstants); + + // Gets the inline constant data pointer from the resource cache + const void* GetInlineConstantData(Uint32 DescrSetIndex, Uint32 BindingIndex) const; + + // Initialize inline constant buffer in the resource cache + void InitializeInlineConstantBuffer(Uint32 DescrSetIndex, + Uint32 CacheOffset, + Uint32 NumConstants, + void* pInlineConstantData); + + // Sets the inline constant memory block (takes ownership, will be freed in destructor) + void SetInlineConstantMemory(IMemoryAllocator& Allocator, void* pMemory) + { + m_pInlineConstantMemory = decltype(m_pInlineConstantMemory){ + pMemory, + STDDeleter(Allocator)}; + } Uint32 GetNumDescriptorSets() const { return m_NumSets; } bool HasDynamicResources() const { return m_NumDynamicBuffers > 0; } bool HasInlineConstants() const { - return false; + return m_HasInlineConstants; } ResourceCacheContentType GetContentType() const { return static_cast(m_ContentType); } @@ -287,16 +315,22 @@ class ShaderResourceCacheVk : public ShaderResourceCacheBase std::unique_ptr> m_pMemory; + // Memory for inline constant data (allocated separately, freed in destructor) + std::unique_ptr> m_pInlineConstantMemory; + Uint16 m_NumSets = 0; // Total actual number of dynamic buffers (that were created with USAGE_DYNAMIC) bound in the resource cache // regardless of the variable type. Note this variable is not equal to dynamic offsets count, which is constant. Uint16 m_NumDynamicBuffers = 0; - Uint32 m_TotalResources : 31; + Uint32 m_TotalResources : 30; // Indicates what types of resources are stored in the cache const Uint32 m_ContentType : 1; + // Indicates whether the cache contains inline constants + Uint32 m_HasInlineConstants : 1; + #ifdef DILIGENT_DEBUG // Debug array that stores flags indicating if resources in the cache have been initialized std::vector> m_DbgInitializedResources; diff --git a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp index 140acab84c..f22ee03040 100644 --- a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp @@ -415,6 +415,24 @@ DeviceContextVkImpl::ResourceBindInfo& DeviceContextVkImpl::GetBindInfo(PIPELINE return m_BindInfo[Indices[Uint32{Type}]]; } +void DeviceContextVkImpl::UpdateInlineConstantBuffers(ResourceBindInfo& BindInfo) +{ + const Uint32 SignCount = m_pPipelineState->GetResourceSignatureCount(); + for (Uint32 i = 0; i < SignCount; ++i) + { + const PipelineResourceSignatureVkImpl* pSign = m_pPipelineState->GetResourceSignature(i); + if (pSign == nullptr || pSign->GetNumInlineConstantBuffers() == 0) + continue; + + const ShaderResourceCacheVk* pResourceCache = BindInfo.ResourceCaches[i]; + if (pResourceCache == nullptr || !pResourceCache->HasInlineConstants()) + continue; + + // Update inline constant buffers + pSign->UpdateInlineConstantBuffers(*pResourceCache, *this); + } +} + void DeviceContextVkImpl::CommitDescriptorSets(ResourceBindInfo& BindInfo, Uint32 CommitSRBMask) { VERIFY(CommitSRBMask != 0, "This method should not be called when there is nothing to commit"); @@ -758,6 +776,14 @@ void DeviceContextVkImpl::PrepareForDraw(DRAW_FLAGS Flags) #endif ResourceBindInfo& BindInfo = GetBindInfo(PIPELINE_TYPE_GRAPHICS); + + // Update inline constant buffers before binding descriptor sets + const bool InlineConstantsIntact = (Flags & DRAW_FLAG_INLINE_CONSTANTS_INTACT) != 0; + if (!InlineConstantsIntact) + { + UpdateInlineConstantBuffers(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. @@ -1059,6 +1085,10 @@ void DeviceContextVkImpl::PrepareForDispatchCompute() EndRenderScope(); ResourceBindInfo& BindInfo = GetBindInfo(PIPELINE_TYPE_COMPUTE); + + // Update inline constant buffers before binding descriptor sets + UpdateInlineConstantBuffers(BindInfo); + if (Uint32 CommitMask = BindInfo.GetCommitMask()) { CommitDescriptorSets(BindInfo, CommitMask); @@ -1075,6 +1105,10 @@ void DeviceContextVkImpl::PrepareForRayTracing() EnsureVkCmdBuffer(); ResourceBindInfo& BindInfo = GetBindInfo(PIPELINE_TYPE_RAY_TRACING); + + // Update inline constant buffers before binding descriptor sets + UpdateInlineConstantBuffers(BindInfo); + if (Uint32 CommitMask = BindInfo.GetCommitMask()) { CommitDescriptorSets(BindInfo, CommitMask); diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp index d10dd562e4..1edbcb484d 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp @@ -32,11 +32,13 @@ #include "RenderDeviceVkImpl.hpp" #include "SamplerVkImpl.hpp" #include "TextureViewVkImpl.hpp" +#include "BufferVkImpl.hpp" #include "DeviceContextVkImpl.hpp" #include "VulkanTypeConversions.hpp" #include "DynamicLinearAllocator.hpp" #include "SPIRVShaderResources.hpp" +#include "GraphicsAccessories.hpp" namespace Diligent { @@ -177,7 +179,11 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) { const PipelineResourceDesc& ResDesc = m_Desc.Resources[i]; if (ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) - StaticResourceCount += ResDesc.ArraySize; + { + // For inline constants, GetArraySize() returns 1 (actual array size), + // while ArraySize contains the number of 32-bit constants + StaticResourceCount += ResDesc.GetArraySize(); + } } m_pStaticResCache->InitializeSets(GetRawAllocator(), 1, &StaticResourceCount); } @@ -191,7 +197,23 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) BindingCount[CacheGroup] += 1; // Note that we may reserve space for separate immutable samplers, which will never be used, but this is OK. - CacheGroupSizes[CacheGroup] += ResDesc.ArraySize; + // For inline constants, GetArraySize() returns 1 (actual array size for cache), + // while ArraySize contains the number of 32-bit constants + CacheGroupSizes[CacheGroup] += ResDesc.GetArraySize(); + + // Count inline constant buffers + if (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) + { + VERIFY(ResDesc.ResourceType == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, + "Only constant buffers can have INLINE_CONSTANTS flag"); + ++m_NumInlineConstantBuffers; + } + } + + // Allocate inline constant buffer attributes array + if (m_NumInlineConstantBuffers > 0) + { + m_InlineConstantBuffers = std::make_unique(m_NumInlineConstantBuffers); } // Descriptor set mapping (static/mutable (0) or dynamic (1) -> set index) @@ -245,6 +267,9 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) // Current offset in the static resource cache Uint32 StaticCacheOffset = 0; + // Current inline constant buffer index + Uint32 InlineConstantBufferIdx = 0; + std::array, DESCRIPTOR_SET_ID_NUM_SETS> vkSetLayoutBindings; DynamicLinearAllocator TempAllocator{GetRawAllocator(), 256}; @@ -323,11 +348,13 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) } BindingIndices[CacheGroup] += 1; - CacheGroupOffsets[CacheGroup] += ResDesc.ArraySize; + // For inline constants, GetArraySize() returns 1 (actual array size for cache) + CacheGroupOffsets[CacheGroup] += ResDesc.GetArraySize(); VkDescriptorSetLayoutBinding vkSetLayoutBinding{}; vkSetLayoutBinding.binding = pAttribs->BindingIndex; - vkSetLayoutBinding.descriptorCount = ResDesc.ArraySize; + // For inline constants, descriptor count is 1 (single uniform buffer) + vkSetLayoutBinding.descriptorCount = ResDesc.GetArraySize(); vkSetLayoutBinding.stageFlags = ShaderTypesToVkShaderStageFlags(ResDesc.ShaderStages); vkSetLayoutBinding.pImmutableSamplers = pVkImmutableSamplers; vkSetLayoutBinding.descriptorType = DescriptorTypeToVkDescriptorType(pAttribs->GetDescriptorType()); @@ -336,11 +363,45 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) if (ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) { VERIFY(pAttribs->DescrSet == 0, "Static resources must always be allocated in descriptor set 0"); - m_pStaticResCache->InitializeResources(pAttribs->DescrSet, StaticCacheOffset, ResDesc.ArraySize, + // For inline constants, GetArraySize() returns 1 (actual array size) + m_pStaticResCache->InitializeResources(pAttribs->DescrSet, StaticCacheOffset, ResDesc.GetArraySize(), pAttribs->GetDescriptorType(), pAttribs->IsImmutableSamplerAssigned()); - StaticCacheOffset += ResDesc.ArraySize; + StaticCacheOffset += ResDesc.GetArraySize(); + } + + // Handle inline constant buffers - create internal dynamic uniform buffers + if (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) + { + VERIFY(ResDesc.ResourceType == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, + "Only constant buffers can have INLINE_CONSTANTS flag"); + + InlineConstantBufferAttribsVk& InlineCBAttribs = m_InlineConstantBuffers[InlineConstantBufferIdx++]; + InlineCBAttribs.DescrSet = pAttribs->DescrSet; + InlineCBAttribs.BindingIndex = pAttribs->BindingIndex; + InlineCBAttribs.NumConstants = ResDesc.ArraySize; // For inline constants, ArraySize is the number of 32-bit constants + + // Create a USAGE_DYNAMIC uniform buffer for emulating inline constants + // All SRBs created from this signature will share the same inline constant buffer. + if (m_pDevice) + { + std::string Name = m_Desc.Name; + Name += " - "; + Name += ResDesc.Name; + BufferDesc CBDesc; + CBDesc.Name = Name.c_str(); + CBDesc.Size = ResDesc.ArraySize * sizeof(Uint32); + CBDesc.Usage = USAGE_DYNAMIC; + CBDesc.BindFlags = BIND_UNIFORM_BUFFER; + CBDesc.CPUAccessFlags = CPU_ACCESS_WRITE; + + RefCntAutoPtr pBuffer; + m_pDevice->CreateBuffer(CBDesc, nullptr, &pBuffer); + VERIFY_EXPR(pBuffer); + InlineCBAttribs.pBuffer = RefCntAutoPtr{pBuffer, IID_BufferVk}; + } } } + VERIFY_EXPR(InlineConstantBufferIdx == m_NumInlineConstantBuffers); #ifdef DILIGENT_DEBUG if (m_pStaticResCache != nullptr) @@ -474,6 +535,68 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) } VERIFY_EXPR(NumSets == GetNumDescriptorSets()); } + + // Initialize inline constant buffers in the static resource cache + // This must be done after the resources are initialized above + if (m_pStaticResCache != nullptr && m_NumInlineConstantBuffers > 0) + { + const Uint32 TotalResources = GetTotalResourceCount(); + constexpr ResourceCacheContentType StaticCacheType = ResourceCacheContentType::Signature; + + // First pass: calculate total memory size needed for all static inline constants + Uint32 TotalStaticInlineConstantSize = 0; + for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) + { + const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; + + for (Uint32 r = 0; r < TotalResources; ++r) + { + const PipelineResourceDesc& ResDesc = GetResourceDesc(r); + const ResourceAttribs& Attr = GetResourceAttribs(r); + + if ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) && + ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC && + Attr.DescrSet == InlineCBAttr.DescrSet && + Attr.BindingIndex == InlineCBAttr.BindingIndex) + { + TotalStaticInlineConstantSize += InlineCBAttr.NumConstants * sizeof(Uint32); + break; + } + } + } + + // Allocate single memory block for all static inline constants + if (TotalStaticInlineConstantSize > 0) + { + m_pStaticInlineConstantData = GetRawAllocator().Allocate(TotalStaticInlineConstantSize, "Static inline constant data", __FILE__, __LINE__); + memset(m_pStaticInlineConstantData, 0, TotalStaticInlineConstantSize); + + // Second pass: assign memory to each static inline constant + Uint8* pCurrentDataPtr = static_cast(m_pStaticInlineConstantData); + for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) + { + const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; + + for (Uint32 r = 0; r < TotalResources; ++r) + { + const PipelineResourceDesc& ResDesc = GetResourceDesc(r); + const ResourceAttribs& Attr = GetResourceAttribs(r); + + if ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) && + ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC && + Attr.DescrSet == InlineCBAttr.DescrSet && + Attr.BindingIndex == InlineCBAttr.BindingIndex) + { + const Uint32 DataSize = InlineCBAttr.NumConstants * sizeof(Uint32); + const Uint32 CacheOffset = Attr.StaticCacheOffset; + m_pStaticResCache->InitializeInlineConstantBuffer(0, CacheOffset, InlineCBAttr.NumConstants, pCurrentDataPtr); + pCurrentDataPtr += DataSize; + break; + } + } + } + } + } } PipelineResourceSignatureVkImpl::~PipelineResourceSignatureVkImpl() @@ -489,6 +612,13 @@ void PipelineResourceSignatureVkImpl::Destruct() GetDevice()->SafeReleaseDeviceObject(std::move(Layout), ~0ull); } + // Free static inline constant data allocated in CreateSetLayouts + if (m_pStaticInlineConstantData != nullptr) + { + GetRawAllocator().Free(m_pStaticInlineConstantData); + m_pStaticInlineConstantData = nullptr; + } + TPipelineResourceSignatureBase::Destruct(); } @@ -509,10 +639,55 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk { const PipelineResourceDesc& ResDesc = GetResourceDesc(r); const ResourceAttribs& Attr = GetResourceAttribs(r); - ResourceCache.InitializeResources(Attr.DescrSet, Attr.CacheOffset(CacheType), ResDesc.ArraySize, + // For inline constants, GetArraySize() returns 1 (actual array size), + // while ArraySize contains the number of 32-bit constants + ResourceCache.InitializeResources(Attr.DescrSet, Attr.CacheOffset(CacheType), ResDesc.GetArraySize(), Attr.GetDescriptorType(), Attr.IsImmutableSamplerAssigned()); } + // Initialize inline constant buffers - allocate staging memory and set up data pointers + if (m_NumInlineConstantBuffers > 0) + { + // Calculate total memory size needed for all inline constants + Uint32 TotalInlineConstantSize = 0; + for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) + { + TotalInlineConstantSize += m_InlineConstantBuffers[i].NumConstants * sizeof(Uint32); + } + + // Allocate single memory block for all inline constants + void* pInlineConstantMemory = CacheMemAllocator.Allocate(TotalInlineConstantSize, "Inline constant data", __FILE__, __LINE__); + memset(pInlineConstantMemory, 0, TotalInlineConstantSize); + + // Pass ownership of the memory to the resource cache for proper cleanup + ResourceCache.SetInlineConstantMemory(CacheMemAllocator, pInlineConstantMemory); + + // Assign memory to each inline constant buffer + Uint8* pCurrentDataPtr = static_cast(pInlineConstantMemory); + for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) + { + const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; + const Uint32 DataSize = InlineCBAttr.NumConstants * sizeof(Uint32); + + // Find the resource and set its inline constant data pointer + for (Uint32 r = 0; r < TotalResources; ++r) + { + const PipelineResourceDesc& ResDesc = GetResourceDesc(r); + const ResourceAttribs& Attr = GetResourceAttribs(r); + + if ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) && + Attr.DescrSet == InlineCBAttr.DescrSet && + Attr.BindingIndex == InlineCBAttr.BindingIndex) + { + ResourceCache.InitializeInlineConstantBuffer(Attr.DescrSet, Attr.CacheOffset(CacheType), + InlineCBAttr.NumConstants, pCurrentDataPtr); + break; + } + } + pCurrentDataPtr += DataSize; + } + } + #ifdef DILIGENT_DEBUG ResourceCache.DbgVerifyResourceInitialization(); #endif @@ -528,6 +703,66 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk DescriptorSetAllocation SetAllocation = GetDevice()->AllocateDescriptorSet(~Uint64{0}, vkLayout, DescrSetName); ResourceCache.AssignDescriptorSetAllocation(GetDescriptorSetIndex(), std::move(SetAllocation)); } + + // Bind internal inline constant buffers to the resource cache + // This must be done after descriptor set allocation so that descriptor writes work correctly + for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) + { + const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; + + if (!InlineCBAttr.pBuffer) + continue; + + // Find the resource and bind the internal buffer + for (Uint32 r = 0; r < TotalResources; ++r) + { + const PipelineResourceDesc& ResDesc = GetResourceDesc(r); + const ResourceAttribs& Attr = GetResourceAttribs(r); + + if ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) && + Attr.DescrSet == InlineCBAttr.DescrSet && + Attr.BindingIndex == InlineCBAttr.BindingIndex) + { + const Uint32 CacheOffset = Attr.CacheOffset(CacheType); + + // For static/mutable variables, bind to the allocated descriptor set + // For dynamic variables, the buffer will be bound during CommitDynamicResources + // Note: Dynamic descriptor sets are allocated per-draw call, so we can't write to them here + if (ResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) + { + // Bind the internal uniform buffer to the resource cache + ResourceCache.SetResource( + &GetDevice()->GetLogicalDevice(), + Attr.DescrSet, + CacheOffset, + { + Attr.BindingIndex, + 0, // ArrayIndex + RefCntAutoPtr{InlineCBAttr.pBuffer}, + 0, // BufferBaseOffset + InlineCBAttr.NumConstants * sizeof(Uint32) // BufferRangeSize + }); + } + else + { + // For dynamic variables, we still need to set the buffer in the cache + // but we pass nullptr for LogicalDevice since the descriptor set is not allocated yet + ResourceCache.SetResource( + nullptr, // Don't write to descriptor set + Attr.DescrSet, + CacheOffset, + { + Attr.BindingIndex, + 0, // ArrayIndex + RefCntAutoPtr{InlineCBAttr.pBuffer}, + 0, // BufferBaseOffset + InlineCBAttr.NumConstants * sizeof(Uint32) // BufferRangeSize + }); + } + break; + } + } + } } void PipelineResourceSignatureVkImpl::CopyStaticResources(ShaderResourceCacheVk& DstResourceCache) const @@ -555,7 +790,26 @@ void PipelineResourceSignatureVkImpl::CopyStaticResources(ShaderResourceCacheVk& if (ResDesc.ResourceType == SHADER_RESOURCE_TYPE_SAMPLER && Attr.IsImmutableSamplerAssigned()) continue; // Skip immutable separate samplers - for (Uint32 ArrInd = 0; ArrInd < ResDesc.ArraySize; ++ArrInd) + // Handle inline constants separately + if (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) + { + const Uint32 SrcCacheOffset = Attr.CacheOffset(SrcCacheType); + const ShaderResourceCacheVk::Resource& SrcCachedRes = SrcDescrSet.GetResource(SrcCacheOffset); + const Uint32 DstCacheOffset = Attr.CacheOffset(DstCacheType); + const ShaderResourceCacheVk::Resource& DstCachedRes = DstDescrSet.GetResource(DstCacheOffset); + + // Copy inline constant data from static cache to SRB cache + if (SrcCachedRes.pInlineConstantData != nullptr && DstCachedRes.pInlineConstantData != nullptr) + { + // ArraySize contains the number of 32-bit constants for inline constants + memcpy(DstCachedRes.pInlineConstantData, SrcCachedRes.pInlineConstantData, ResDesc.ArraySize * sizeof(Uint32)); + } + continue; + } + + // For regular resources (not inline constants), iterate through array elements + const Uint32 ActualArraySize = ResDesc.GetArraySize(); + for (Uint32 ArrInd = 0; ArrInd < ActualArraySize; ++ArrInd) { const Uint32 SrcCacheOffset = Attr.CacheOffset(SrcCacheType) + ArrInd; const ShaderResourceCacheVk::Resource& SrcCachedRes = SrcDescrSet.GetResource(SrcCacheOffset); @@ -652,15 +906,16 @@ void PipelineResourceSignatureVkImpl::CommitDynamicResources(const ShaderResourc for (Uint32 ResIdx = DynResIdxRange.first, ArrElem = 0; ResIdx < DynResIdxRange.second;) { const PipelineResourceAttribsType& Attr = GetResourceAttribs(ResIdx); + const PipelineResourceDesc& ResDesc = GetResourceDesc(ResIdx); const Uint32 CacheOffset = Attr.CacheOffset(CacheType); - const Uint32 ArraySize = Attr.ArraySize; + // For inline constants, GetArraySize() returns 1 (actual array size for cache), + // while ArraySize contains the number of 32-bit constants + const Uint32 ArraySize = ResDesc.GetArraySize(); const DescriptorType DescrType = Attr.GetDescriptorType(); #ifdef DILIGENT_DEBUG { - const PipelineResourceDesc& Res = GetResourceDesc(ResIdx); - VERIFY_EXPR(ArraySize == GetResourceDesc(ResIdx).ArraySize); - VERIFY_EXPR(Res.VarType == SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC); + VERIFY_EXPR(ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC); } #endif @@ -981,4 +1236,30 @@ PipelineResourceSignatureInternalDataVk PipelineResourceSignatureVkImpl::GetInte return InternalData; } +void PipelineResourceSignatureVkImpl::UpdateInlineConstantBuffers(const ShaderResourceCacheVk& ResourceCache, + DeviceContextVkImpl& Ctx) const +{ + for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) + { + const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; + + if (!InlineCBAttr.pBuffer) + continue; + + // Get the inline constant data from the resource cache + const void* pInlineConstantData = ResourceCache.GetInlineConstantData(InlineCBAttr.DescrSet, InlineCBAttr.BindingIndex); + if (pInlineConstantData == nullptr) + continue; + + // Map the buffer and copy the data + void* pMappedData = nullptr; + Ctx.MapBuffer(InlineCBAttr.pBuffer, MAP_WRITE, MAP_FLAG_DISCARD, pMappedData); + if (pMappedData != nullptr) + { + memcpy(pMappedData, pInlineConstantData, InlineCBAttr.NumConstants * sizeof(Uint32)); + Ctx.UnmapBuffer(InlineCBAttr.pBuffer, MAP_WRITE); + } + } +} + } // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp index 26e9b64225..3c74aa9fc8 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp @@ -633,7 +633,28 @@ PipelineResourceSignatureDescWrapper PipelineStateVkImpl::GetDefaultResourceSign const SHADER_RESOURCE_TYPE ResType = SPIRVShaderResourceAttribs::GetShaderResourceType(Attribs.Type); const PIPELINE_RESOURCE_FLAGS Flags = SPIRVShaderResourceAttribs::GetPipelineResourceFlags(Attribs.Type) | ShaderVariableFlagsToPipelineResourceFlags(VarDesc.Flags); - SignDesc.AddResource(VarDesc.ShaderStages, Attribs.Name, Attribs.ArraySize, ResType, VarDesc.Type, Flags); + + // For inline constants, ArraySize specifies the number of 32-bit constants, + // not the array dimension. We need to calculate it from the buffer size. + Uint32 ArraySize = Attribs.ArraySize; + if ((Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0) + { + // For inline constants specified via ShaderResourceVariableDesc, + // the SPIRV ArraySize is 1 (buffer is not an array), but we need + // the number of 32-bit constants which is BufferStaticSize / sizeof(Uint32) + if (Attribs.BufferStaticSize > 0) + { + ArraySize = Attribs.BufferStaticSize / sizeof(Uint32); + } + else + { + LOG_ERROR_AND_THROW("Resource '", Attribs.Name, "' in shader '", pShader->GetDesc().Name, + "' is marked as inline constants, but has zero buffer size. " + "Make sure the buffer type is defined in the shader."); + } + } + + SignDesc.AddResource(VarDesc.ShaderStages, Attribs.Name, ArraySize, ResType, VarDesc.Type, Flags); } else { diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp index e186c946c3..8d634624a7 100644 --- a/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp +++ b/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp @@ -950,4 +950,62 @@ Uint32 ShaderResourceCacheVk::GetDynamicBufferOffsets(DeviceContextVkImpl* pCt return OffsetInd - StartInd; } + +void ShaderResourceCacheVk::SetInlineConstants(Uint32 DescrSetIndex, + Uint32 CacheOffset, + const void* pConstants, + Uint32 FirstConstant, + Uint32 NumConstants) +{ + VERIFY(pConstants != nullptr, "Source constant data pointer is null"); + VERIFY(NumConstants > 0, "Number of constants must be greater than zero"); + + DescriptorSet& DescrSet = GetDescriptorSet(DescrSetIndex); + Resource& DstRes = DescrSet.GetResource(CacheOffset); + + VERIFY(DstRes.pInlineConstantData != nullptr, "Inline constant data pointer is null. " + "Make sure InitializeInlineConstantBuffer was called for this resource."); + + // Copy to CPU-side staging buffer + Uint32* pDstConstants = reinterpret_cast(DstRes.pInlineConstantData); + memcpy(pDstConstants + FirstConstant, pConstants, NumConstants * sizeof(Uint32)); + + UpdateRevision(); +} + +const void* ShaderResourceCacheVk::GetInlineConstantData(Uint32 DescrSetIndex, Uint32 BindingIndex) const +{ + const DescriptorSet& DescrSet = GetDescriptorSet(DescrSetIndex); + + // Find the resource with the matching binding index + for (Uint32 i = 0; i < DescrSet.GetSize(); ++i) + { + const Resource& Res = DescrSet.GetResource(i); + if (Res.pInlineConstantData != nullptr) + { + // For now, we assume binding index matches the resource index for inline constants + // This may need adjustment based on actual binding layout + return Res.pInlineConstantData; + } + } + + return nullptr; +} + +void ShaderResourceCacheVk::InitializeInlineConstantBuffer(Uint32 DescrSetIndex, + Uint32 CacheOffset, + Uint32 NumConstants, + void* pInlineConstantData) +{ + VERIFY(pInlineConstantData != nullptr, "Inline constant data pointer is null"); + + DescriptorSet& DescrSet = GetDescriptorSet(DescrSetIndex); + Resource& DstRes = DescrSet.GetResource(CacheOffset); + + DstRes.pInlineConstantData = pInlineConstantData; + + // Mark that this cache has inline constants + m_HasInlineConstants = 1; +} + } // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp index f3d870cb08..27aac2377c 100644 --- a/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp +++ b/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp @@ -286,7 +286,8 @@ BindResourceHelper::BindResourceHelper(const PipelineResourceSignatureVkImpl& Si m_DstRes {m_CachedSet.GetResource(m_DstResCacheOffset)} // clang-format on { - VERIFY(ArrayIndex < m_ResDesc.ArraySize, "Array index is out of range, but it should've been corrected by ShaderVariableBase::SetArray()"); + // For inline constants, GetArraySize() returns 1 (actual array size), while ArraySize is the number of constants + VERIFY(ArrayIndex < m_ResDesc.GetArraySize(), "Array index is out of range, but it should've been corrected by ShaderVariableBase::SetArray()"); VERIFY(m_DstRes.Type == m_Attribs.GetDescriptorType(), "Inconsistent types"); #ifdef DILIGENT_DEBUG @@ -655,7 +656,16 @@ void ShaderVariableManagerVk::SetInlineConstants(Uint32 ResIndex, Uint32 FirstConstant, Uint32 NumConstants) { - UNSUPPORTED("Not yet implemented"); + const PipelineResourceAttribsVk& Attribs = m_pSignature->GetResourceAttribs(ResIndex); + const ResourceCacheContentType CacheType = m_ResourceCache.GetContentType(); + const Uint32 CacheOffset = Attribs.CacheOffset(CacheType); + +#ifdef DILIGENT_DEVELOPMENT + const PipelineResourceDesc& ResDesc = m_pSignature->GetResourceDesc(ResIndex); + VerifyInlineConstants(ResDesc, pConstants, FirstConstant, NumConstants); +#endif + + m_ResourceCache.SetInlineConstants(Attribs.DescrSet, CacheOffset, pConstants, FirstConstant, NumConstants); } IDeviceObject* ShaderVariableManagerVk::Get(Uint32 ArrayIndex, Uint32 ResIndex) const @@ -664,7 +674,8 @@ IDeviceObject* ShaderVariableManagerVk::Get(Uint32 ArrayIndex, Uint32 ResIndex) const PipelineResourceAttribsVk& Attribs = GetResourceAttribs(ResIndex); const Uint32 CacheOffset = Attribs.CacheOffset(m_ResourceCache.GetContentType()); - VERIFY_EXPR(ArrayIndex < ResDesc.ArraySize); + // For inline constants, GetArraySize() returns 1 (actual array size) + VERIFY_EXPR(ArrayIndex < ResDesc.GetArraySize()); if (Attribs.DescrSet < m_ResourceCache.GetNumDescriptorSets()) { diff --git a/doc/SetInlineConstants.md b/doc/SetInlineConstants.md new file mode 100644 index 0000000000..249158c150 --- /dev/null +++ b/doc/SetInlineConstants.md @@ -0,0 +1,1037 @@ +# SetInlineConstants Implementation Guide + +## Overview + +Inline constants (also known as push constants in Vulkan or root constants in Direct3D12) provide an efficient way to pass small, frequently-updated data directly from the CPU to shaders without creating buffers or modifying descriptor sets. + +This document describes the implementation of `SetInlineConstants` API in DiligentCore for D3D12 and Vulkan backends. + +## API Usage + +### Defining Inline Constants in Pipeline Resource Signature + +```cpp +PipelineResourceDesc Resources[] = { + { + .Name = "Constants", + .ShaderStages = SHADER_TYPE_VERTEX, + .ArraySize = 16, // Number of 32-bit values (not bytes!) + .ResourceType = SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, + .VarType = SHADER_RESOURCE_VARIABLE_TYPE_STATIC, + .Flags = PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS + } +}; + +PipelineResourceSignatureDesc PRSDesc; +PRSDesc.Resources = Resources; +PRSDesc.NumResources = _countof(Resources); +pDevice->CreatePipelineResourceSignature(PRSDesc, &pPRS); +``` + +### Setting Inline Constants at Runtime + +```cpp +// For static variables, use the signature +pPRS->GetStaticVariableByName(SHADER_TYPE_VERTEX, "Constants") + ->SetInlineConstants(pData, FirstConstant, NumConstants); + +// For mutable/dynamic variables, use the SRB +pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "Constants") + ->SetInlineConstants(pData, FirstConstant, NumConstants); +``` + +### Parameters + +| Parameter | Description | +|-----------------|-------------------------------------------------------| +| `pData` | Pointer to the constant data (array of 32-bit values) | +| `FirstConstant` | Index of the first 32-bit constant to update | +| `NumConstants` | Number of 32-bit constants to update | + +## Limitations + +- **Maximum size**: 64 constants (256 bytes) per inline constant resource +- **Resource type**: Only `SHADER_RESOURCE_TYPE_CONSTANT_BUFFER` supports inline constants +- **Flag exclusivity**: `PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS` cannot be combined with other flags + +--- + +## D3D12 Backend Implementation + +### Architecture Overview + +``` +┌─────────────────────────────────────────────────────────────────────────────┐ +│ User Application │ +├─────────────────────────────────────────────────────────────────────────────┤ +│ pVariable->SetInlineConstants(pData, FirstConstant, NumConstants) │ +└──────────────────────────────────┬──────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────┐ +│ ShaderVariableManagerD3D12 │ +│ ┌───────────────────────────────────────────────────────────────────────┐ │ +│ │ SetInlineConstants(): │ │ +│ │ 1. Get ResourceAttribs (RootIndex, OffsetFromTableStart) │ │ +│ │ 2. Validate parameters (DEV build only) │ │ +│ │ 3. Call m_ResourceCache.SetInlineConstants(...) │ │ +│ └───────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────┬──────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────┐ +│ ShaderResourceCacheD3D12 │ +│ ┌───────────────────────────────────────────────────────────────────────┐ │ +│ │ SetInlineConstants(): │ │ +│ │ 1. Get RootTable by RootIndex │ │ +│ │ 2. Get Resource (CPUDescriptorHandle.ptr points to storage) │ │ +│ │ 3. memcpy(pDstConstants + FirstConstant, pConstants, NumConstants) │ │ +│ └───────────────────────────────────────────────────────────────────────┘ │ +│ │ +│ Memory Layout: │ +│ ┌─────────────────────────────────────────────────────────────────────┐ │ +│ │ RootTables | Resources | DescriptorAllocations | InlineConstStorage │ │ +│ └─────────────────────────────────────────────────────────────────────┘ │ +│ ▲ │ +│ Resource.CPUDescriptorHandle.ptr ─┘ │ +└──────────────────────────────────┬──────────────────────────────────────────┘ + │ + ▼ (During CommitShaderResources) +┌─────────────────────────────────────────────────────────────────────────────┐ +│ PipelineResourceSignatureD3D12Impl │ +│ ┌───────────────────────────────────────────────────────────────────────┐ │ +│ │ CommitRootConstants(): │ │ +│ │ while (ConstantsMask != 0) { │ │ +│ │ RootInd = ExtractLSB(ConstantsMask); │ │ +│ │ pConstants = (Uint32*)Res.CPUDescriptorHandle.ptr; │ │ +│ │ NumConstants = Res.BufferRangeSize; │ │ +│ │ pd3d12CmdList->SetGraphicsRoot32BitConstants( │ │ +│ │ BaseRootIndex + RootInd, NumConstants, pConstants, 0); │ │ +│ │ } │ │ +│ └───────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────┘ +``` + +### Step 1: Resource Signature Creation + +When creating a `PipelineResourceSignature` with `PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS`: + +**File**: `PipelineResourceSignatureD3D12Impl.cpp` + +```cpp +case SHADER_RESOURCE_TYPE_CONSTANT_BUFFER: + if ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0) + { + // Use D3D12 root constants instead of descriptor table or root CBV + d3d12RootParamType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; + } + // ... +``` + +The `RootParamsBuilder` allocates a dedicated root parameter: + +**File**: `RootParamsManager.cpp` + +```cpp +void RootParamsBuilder::AllocateResourceSlot(...) +{ + if (RootParameterType == D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS) + { + OffsetFromTableStart = 0; + // ArraySize = number of 32-bit values + AddRootConstants(RootIndex, Register, Space, ArraySize, ShaderVisibility, ParameterGroup); + } +} +``` + +### Step 2: Resource Cache Initialization + +The `ShaderResourceCacheD3D12` allocates memory for inline constants storage: + +**File**: `ShaderResourceCacheD3D12.cpp` + +```cpp +void ShaderResourceCacheD3D12::Initialize(...) +{ + // Calculate total inline constant values + Uint32 TotalInlineConstantValues = 0; + for (const RootParameter& RootConsts : RootParams.GetRootConstants()) + { + TotalInlineConstantValues += RootConsts.d3d12RootParam.Constants.Num32BitValues; + } + + // Allocate memory including space for inline constants + AllocateMemory(MemAllocator, TotalInlineConstantValues); + + // Initialize inline constant resources + Uint32* pCurrInlineConstValueStorage = GetInlineConstantStorage(); + for (Uint32 i = 0; i < NumRootConstants; ++i) + { + Resource& Res = GetResource(ResIdx); + // Store pointer to inline constants storage in CPUDescriptorHandle.ptr + Res.CPUDescriptorHandle.ptr = reinterpret_cast(pCurrInlineConstValueStorage); + Res.Type = SHADER_RESOURCE_TYPE_CONSTANT_BUFFER; + Res.BufferRangeSize = RootConsts.d3d12RootParam.Constants.Num32BitValues; + + pCurrInlineConstValueStorage += RootConsts.d3d12RootParam.Constants.Num32BitValues; + m_RootConstantsMask |= (Uint64{1} << Uint64{RootConsts.RootIndex}); + } +} +``` + +### Step 3: SetInlineConstants Implementation + +**File**: `ShaderVariableManagerD3D12.cpp` + +```cpp +void ShaderVariableManagerD3D12::SetInlineConstants(Uint32 ResIndex, + const void* pConstants, + Uint32 FirstConstant, + Uint32 NumConstants) +{ + const ResourceAttribs& Attribs = m_pSignature->GetResourceAttribs(ResIndex); + const ResourceCacheContentType CacheType = m_ResourceCache.GetContentType(); + const Uint32 RootIndex = Attribs.RootIndex(CacheType); + +#ifdef DILIGENT_DEVELOPMENT + const PipelineResourceDesc& ResDesc = m_pSignature->GetResourceDesc(ResIndex); + VerifyInlineConstants(ResDesc, pConstants, FirstConstant, NumConstants); +#endif + + m_ResourceCache.SetInlineConstants(RootIndex, pConstants, FirstConstant, NumConstants); +} +``` + +**File**: `ShaderResourceCacheD3D12.cpp` + +```cpp +void ShaderResourceCacheD3D12::SetInlineConstants(Uint32 RootIndex, + const void* pConstants, + Uint32 FirstConstant, + Uint32 NumConstants) +{ + RootTable& Tbl = GetRootTable(RootIndex); + Resource& Res = Tbl.GetResource(0); + + VERIFY_EXPR(Res.Type == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER); + VERIFY(Res.CPUDescriptorHandle.ptr != 0, "Invalid pointer to inline constants storage"); + + Uint32* pDstConstants = reinterpret_cast(Res.CPUDescriptorHandle.ptr); + memcpy(pDstConstants + FirstConstant, pConstants, NumConstants * sizeof(Uint32)); +} +``` + +### Step 4: Committing to GPU + +During `CommitShaderResources`, inline constants are submitted to the D3D12 command list: + +**File**: `PipelineResourceSignatureD3D12Impl.cpp` + +```cpp +void PipelineResourceSignatureD3D12Impl::CommitRootConstants( + const CommitCacheResourcesAttribs& CommitAttribs, + Uint64 ConstantsMask) const +{ + while (ConstantsMask != 0) + { + const Uint64 RootIndBit = ExtractLSB(ConstantsMask); + const Uint32 RootInd = PlatformMisc::GetLSB(RootIndBit); + + const ShaderResourceCacheD3D12::RootTable& CacheTbl = + CommitAttribs.pResourceCache->GetRootTable(RootInd); + const ShaderResourceCacheD3D12::Resource& Res = CacheTbl.GetResource(0); + + const Uint32* pConstants = reinterpret_cast(Res.CPUDescriptorHandle.ptr); + const Uint32 NumConstants = static_cast(Res.BufferRangeSize); + + ID3D12GraphicsCommandList* pd3d12CmdList = CommitAttribs.CmdCtx.GetCommandList(); + if (CommitAttribs.IsCompute) + { + pd3d12CmdList->SetComputeRoot32BitConstants( + CommitAttribs.BaseRootIndex + RootInd, NumConstants, pConstants, 0); + } + else + { + pd3d12CmdList->SetGraphicsRoot32BitConstants( + CommitAttribs.BaseRootIndex + RootInd, NumConstants, pConstants, 0); + } + } +} +``` + +### Multiple Inline Constants Support + +D3D12 backend fully supports multiple inline constant buffers. Each inline constant resource: + +- Gets its own `D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS` root parameter +- Has a unique `RootIndex` +- Has separate memory storage in the resource cache +- Is committed independently via `SetGraphicsRoot32BitConstants` + +**Example**: + +```cpp +PipelineResourceDesc Resources[] = { + {"TransformCB", SHADER_TYPE_VERTEX, 16, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, + SHADER_RESOURCE_VARIABLE_TYPE_STATIC, PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS}, + {"MaterialCB", SHADER_TYPE_PIXEL, 8, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, + SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC, PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS}, +}; +``` + +This creates two separate root constant parameters in the D3D12 root signature. + +--- + +## D3D11 Backend Implementation + +### Architecture Overview + +In D3D11, inline constants are emulated using **dynamic constant buffers** (`USAGE_DYNAMIC` with `D3D11_MAP_WRITE_DISCARD`). Unlike D3D12's direct root constants, D3D11 requires creating actual buffer objects and updating them via `Map`/`Unmap`. + +``` +┌─────────────────────────────────────────────────────────────────────────────┐ +│ User Application │ +├─────────────────────────────────────────────────────────────────────────────┤ +│ pVariable->SetInlineConstants(pData, FirstConstant, NumConstants) │ +└──────────────────────────────────┬──────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────┐ +│ ShaderVariableManagerD3D11 │ +│ ┌───────────────────────────────────────────────────────────────────────┐ │ +│ │ ConstBuffBindInfo::SetConstants(): │ │ +│ │ 1. Validate parameters (DEV build only) │ │ +│ │ 2. Call m_ResourceCache.SetInlineConstants(...) │ │ +│ └───────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────┬──────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────┐ +│ ShaderResourceCacheD3D11 │ +│ ┌───────────────────────────────────────────────────────────────────────┐ │ +│ │ SetInlineConstants(): │ │ +│ │ 1. Get CachedCB by BindPoints │ │ +│ │ 2. memcpy to pInlineConstantData (CPU-side staging buffer) │ │ +│ │ 3. Mark as dirty (implicit - data changed) │ │ +│ └───────────────────────────────────────────────────────────────────────┘ │ +│ │ +│ CachedCB Structure: │ +│ ┌─────────────────────────────────────────────────────────────────────┐ │ +│ │ pBuff (ID3D11Buffer*) │ │ +│ │ pInlineConstantData (void*) ──> CPU staging buffer │ │ +│ │ RangeSize, BaseOffset, DynamicOffset │ │ +│ └─────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────┬──────────────────────────────────────────┘ + │ + ▼ (During Draw/Dispatch) +┌─────────────────────────────────────────────────────────────────────────────┐ +│ PipelineResourceSignatureD3D11Impl │ +│ ┌───────────────────────────────────────────────────────────────────────┐ │ +│ │ UpdateInlineConstantBuffers(): │ │ +│ │ for each inline constant buffer { │ │ +│ │ pd3d11Ctx->Map(pd3d11CB, 0, D3D11_MAP_WRITE_DISCARD, 0, &Mapped);│ │ +│ │ memcpy(Mapped.pData, InlineCB.pInlineConstantData, Size); │ │ +│ │ pd3d11Ctx->Unmap(pd3d11CB, 0); │ │ +│ │ } │ │ +│ └───────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────┘ +``` + +### Key Differences from D3D12 + +| Aspect | D3D12 | D3D11 | +|-------------------------|--------------------------------|-----------------------------------| +| Implementation | Root constants (direct) | Dynamic constant buffers (emulated)| +| Buffer creation | Not needed | `USAGE_DYNAMIC` buffer required | +| Update mechanism | `SetGraphicsRoot32BitConstants`| `Map` + `memcpy` + `Unmap` | +| Performance | Very fast (no buffer) | Slower (buffer map overhead) | +| Memory | Inline in command buffer | Separate buffer object | + +### Step 1: Resource Signature Creation + +When creating a `PipelineResourceSignature` with `PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS`: + +**File**: `PipelineResourceSignatureD3D11Impl.cpp` + +```cpp +if (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) +{ + // Inline constant buffers are handled mostly like regular constant buffers. + // The only difference is that the buffer is created internally and is not expected to be bound. + // It is updated by UpdateInlineConstantBuffers() method. + + VERIFY(ResDesc.ResourceType == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, + "Only constant buffers can have INLINE_CONSTANTS flag"); + + InlineConstantBufferAttribsD3D11& InlineCBAttribs = m_InlineConstantBuffers[InlineConstantBufferIdx++]; + InlineCBAttribs.BindPoints = BindPoints; + InlineCBAttribs.NumConstants = ResDesc.ArraySize; + + // Create a USAGE_DYNAMIC constant buffer + BufferDesc CBDesc; + CBDesc.Name = m_Desc.Name + " - " + ResDesc.Name; + CBDesc.Size = ResDesc.ArraySize * sizeof(Uint32); + CBDesc.Usage = USAGE_DYNAMIC; + CBDesc.BindFlags = BIND_UNIFORM_BUFFER; + CBDesc.CPUAccessFlags = CPU_ACCESS_WRITE; + + m_pDevice->CreateBuffer(CBDesc, nullptr, &InlineCBAttribs.pBuffer); +} +``` + +**Key Points**: +- Each inline constant resource creates a `USAGE_DYNAMIC` buffer +- Buffer is shared across all SRBs created from this signature +- CPU-side staging buffer (`pInlineConstantData`) is allocated separately + +### Step 2: Resource Cache Initialization + +**File**: `ShaderResourceCacheD3D11.cpp` + +```cpp +void ShaderResourceCacheD3D11::Initialize( + const D3D11ShaderResourceCounters& ResCount, + IMemoryAllocator& MemAllocator, + const std::array* pDynamicCBSlotsMask, + const InlineConstantBufferAttribsD3D11* pInlineCBs, + Uint32 NumInlineCBs) +{ + // Calculate total memory including inline constant data storage + Uint32 TotalInlineConstantDataSize = 0; + for (Uint32 i = 0; i < NumInlineCBs; ++i) + { + TotalInlineConstantDataSize += pInlineCBs[i].NumConstants * sizeof(Uint32); + } + + // Allocate memory: Resources + d3d11 resources + inline constant data + size_t TotalSize = /* resource arrays */ + TotalInlineConstantDataSize; + m_pResourceData = ALLOCATE_RAW(MemAllocator, "Memory for cache data", TotalSize); + + // Initialize inline constant buffers + Uint8* pInlineConstDataStorage = /* end of resource arrays */; + for (Uint32 i = 0; i < NumInlineCBs; ++i) + { + const InlineConstantBufferAttribsD3D11& InlineCBAttr = pInlineCBs[i]; + + InitInlineConstantBuffer( + InlineCBAttr.BindPoints, + InlineCBAttr.pBuffer, + InlineCBAttr.NumConstants, + pInlineConstDataStorage // CPU-side staging buffer + ); + + pInlineConstDataStorage += InlineCBAttr.NumConstants * sizeof(Uint32); + } +} +``` + +**Memory Layout**: +``` +┌──────────────────────────────────────────────────────────────────────────┐ +│ CBV Resources | SRV Resources | Sampler Resources | UAV Resources | │ +│ | | | | │ +│ d3d11 CBVs | d3d11 SRVs | d3d11 Samplers | d3d11 UAVs | │ +│ │ +│ Inline Constant Data Storage (CPU-side staging buffers) │ +│ ┌──────────────┬──────────────┬──────────────┐ │ +│ │ InlineCB[0] │ InlineCB[1] │ InlineCB[2] │ ... │ +│ └──────────────┴──────────────┴──────────────┘ │ +└──────────────────────────────────────────────────────────────────────────┘ +``` + +### Step 3: SetInlineConstants Implementation + +**File**: `ShaderVariableManagerD3D11.cpp` + +```cpp +void ShaderVariableManagerD3D11::ConstBuffBindInfo::SetConstants( + const void* pConstants, + Uint32 FirstConstant, + Uint32 NumConstants) +{ + const PipelineResourceAttribsD3D11& Attr = GetAttribs(); + const PipelineResourceDesc& Desc = GetDesc(); + +#ifdef DILIGENT_DEVELOPMENT + VerifyInlineConstants(Desc, pConstants, FirstConstant, NumConstants); +#endif + + m_ParentManager.m_ResourceCache.SetInlineConstants( + Attr.BindPoints, pConstants, FirstConstant, NumConstants); +} +``` + +**File**: `ShaderResourceCacheD3D11.hpp` (inline implementation) + +```cpp +__forceinline void ShaderResourceCacheD3D11::SetInlineConstants( + const D3D11ResourceBindPoints& BindPoints, + const void* pConstants, + Uint32 FirstConstant, + Uint32 NumConstants) +{ + // Since all shader stages share the same inline constant data, + // we can just set it for one stage + SHADER_TYPE ActiveStages = BindPoints.GetActiveStages(); + VERIFY_EXPR(ActiveStages != SHADER_TYPE_UNKNOWN); + + const Uint32 ShaderInd0 = ExtractFirstShaderStageIndex(ActiveStages); + const Uint32 Binding0 = BindPoints[ShaderInd0]; + + const auto ResArrays0 = GetResourceArrays(ShaderInd0); + ResArrays0.first[Binding0].SetInlineConstants(pConstants, FirstConstant, NumConstants); +} +``` + +**File**: `ShaderResourceCacheD3D11.hpp` (CachedCB member) + +```cpp +void CachedCB::SetInlineConstants(const void* pSrcConstants, + Uint32 FirstConstant, + Uint32 NumConstants) +{ + VERIFY(pSrcConstants != nullptr, "Source constant data pointer is null"); + VERIFY(FirstConstant + NumConstants <= RangeSize / sizeof(Uint32), + "Too many constants for the allocated space"); + VERIFY(pInlineConstantData != nullptr, "Inline constant data pointer is null"); + + // Copy to CPU-side staging buffer + memcpy(reinterpret_cast(pInlineConstantData) + FirstConstant * sizeof(Uint32), + pSrcConstants, + NumConstants * sizeof(Uint32)); +} +``` + +### Step 4: Updating GPU Buffer + +During draw/dispatch commands, inline constant buffers are updated: + +**File**: `DeviceContextD3D11Impl.cpp` + +```cpp +void DeviceContextD3D11Impl::CommitShaderResources(...) +{ + // For each bound SRB + for (Uint32 SignIdx = 0; SignIdx < m_NumActiveSignatures; ++SignIdx) + { + if ((m_BindInfo.InlineConstantsSRBMask & (1u << SignIdx)) != 0) + { + // Always update inline constant buffers if the SRB is stale + // or inline constants are not intact + if (SRBStale || !InlineConstantsIntact) + { + PipelineResourceSignatureD3D11Impl* pSign = + m_pPipelineState->GetResourceSignature(SignIdx); + + pSign->UpdateInlineConstantBuffers(*pResourceCache, m_pd3d11DeviceContext); + } + } + } +} +``` + +**File**: `PipelineResourceSignatureD3D11Impl.cpp` + +```cpp +void PipelineResourceSignatureD3D11Impl::UpdateInlineConstantBuffers( + const ShaderResourceCacheD3D11& ResourceCache, + ID3D11DeviceContext* pd3d11Ctx) const +{ + for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) + { + const InlineConstantBufferAttribsD3D11& InlineCBAttr = m_InlineConstantBuffers[i]; + + // Get the cached constant buffer + ID3D11Buffer* pd3d11CB = nullptr; + const ShaderResourceCacheD3D11::CachedCB& InlineCB = + ResourceCache.GetResource(InlineCBAttr.BindPoints, &pd3d11CB); + + VERIFY(InlineCBAttr.NumConstants * sizeof(Uint32) == InlineCB.RangeSize, + "Inline constant buffer size mismatch"); + VERIFY(InlineCB.pInlineConstantData != nullptr, + "Inline constant data pointer is null"); + + // Map the D3D11 buffer + D3D11_MAPPED_SUBRESOURCE MappedData{}; + if (SUCCEEDED(pd3d11Ctx->Map(pd3d11CB, 0, D3D11_MAP_WRITE_DISCARD, 0, &MappedData))) + { + // Copy from CPU staging buffer to GPU buffer + memcpy(MappedData.pData, + InlineCB.pInlineConstantData, + InlineCBAttr.NumConstants * sizeof(Uint32)); + + pd3d11Ctx->Unmap(pd3d11CB, 0); + } + } +} +``` + +### Multiple Inline Constants Support + +D3D11 backend fully supports multiple inline constant buffers, similar to D3D12: + +- Each inline constant resource creates its own `USAGE_DYNAMIC` buffer +- Each has separate CPU-side staging buffer (`pInlineConstantData`) +- All inline constant buffers are updated together during `CommitShaderResources` + +**Example**: + +```cpp +PipelineResourceDesc Resources[] = { + {"TransformCB", SHADER_TYPE_VERTEX, 16, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, + SHADER_RESOURCE_VARIABLE_TYPE_STATIC, PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS}, + {"MaterialCB", SHADER_TYPE_PIXEL, 8, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, + SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC, PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS}, +}; +``` + +This creates two separate dynamic constant buffers. + +### Performance Considerations + +**D3D11 Inline Constants Performance**: + +1. **Overhead**: Higher than D3D12 due to `Map`/`Unmap` operations +2. **Update frequency**: Best for per-draw updates (not per-constant updates) +3. **Buffer sharing**: All SRBs share the same buffer, reducing memory but requiring updates +4. **Optimization flags**: + - Use `DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT` if constants unchanged + - This skips the `Map`/`Unmap` overhead + +--- + +## Vulkan Backend Implementation + +### Architecture Overview + +The Vulkan backend implements inline constants using **dynamic uniform buffers** for emulation, similar to the D3D11 backend. While Vulkan natively supports push constants, the current implementation uses uniform buffer emulation to support multiple inline constant resources and broader compatibility. + +**Design Decision**: +- True Vulkan push constants are reserved for SPIRV blocks explicitly marked with `layout(push_constant)` (future implementation) +- All inline constant resources currently use dynamic uniform buffer emulation +- This approach allows multiple inline constant resources (Vulkan only supports one push constant block per pipeline) + +### Implementation Architecture + +``` +┌─────────────────────────────────────────────────────────────────────────────┐ +│ User Application │ +├─────────────────────────────────────────────────────────────────────────────┤ +│ pVariable->SetInlineConstants(pData, FirstConstant, NumConstants) │ +└──────────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────┐ +│ ShaderVariableManagerVk │ +│ ┌───────────────────────────────────────────────────────────────────────┐ │ +│ │ SetInlineConstants(): │ │ +│ │ 1. Get resource offset in merged push constant block │ │ +│ │ 2. Copy data to push constant storage │ │ +│ │ 3. Mark push constants as dirty │ │ +│ └───────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────┬──────────────────────────────────────────┘ + 1 │ + ▼ (During CommitShaderResources) +┌─────────────────────────────────────────────────────────────────────────────┐ +│ DeviceContextVkImpl │ +│ ┌───────────────────────────────────────────────────────────────────────┐ │ +│ │ CommitPushConstants(): │ │ +│ │ vkCmdPushConstants(cmdBuffer, pipelineLayout, │ │ +│ │ stageFlags, offset, size, pData); │ │ +│ └───────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────┘ +``` + +### Step 1: Resource Signature Creation + +When creating a `PipelineResourceSignature` with `PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS`: + +**File**: `PipelineResourceSignatureVkImpl.cpp` - `CreateSetLayouts()` + +```cpp +if (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) +{ + VERIFY(ResDesc.ResourceType == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, + "Only constant buffers can have INLINE_CONSTANTS flag"); + + // Create internal dynamic uniform buffer for emulation + InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[InlineCBIdx++]; + InlineCBAttr.DescrSet = DescrSetId; + InlineCBAttr.BindingIndex = vkBinding; + InlineCBAttr.NumConstants = ResDesc.ArraySize; // Number of 32-bit values + + // Create a USAGE_DYNAMIC uniform buffer + BufferDesc BuffDesc; + BuffDesc.Name = std::string(m_Desc.Name) + " - " + ResDesc.Name; + BuffDesc.Size = ResDesc.ArraySize * sizeof(Uint32); + BuffDesc.Usage = USAGE_DYNAMIC; + BuffDesc.BindFlags = BIND_UNIFORM_BUFFER; + BuffDesc.CPUAccessFlags = CPU_ACCESS_WRITE; + + RefCntAutoPtr pBuffer; + GetDevice()->CreateBuffer(BuffDesc, nullptr, &pBuffer); + InlineCBAttr.pBuffer = RefCntAutoPtr(pBuffer, IID_BufferVk); +} +``` + +**Key Points**: +- Each inline constant resource creates a `USAGE_DYNAMIC` uniform buffer +- Buffer is stored in `InlineConstantBufferAttribsVk` structure +- Buffer is shared across all SRBs created from this signature + +### Step 2: Resource Cache Initialization + +#### For Static Inline Constants + +**File**: `PipelineResourceSignatureVkImpl.cpp` - `CreateSetLayouts()` + +Static inline constants require CPU-side staging buffers that are allocated once during signature creation and freed in the destructor. + +```cpp +// Calculate total memory size for all static inline constants +Uint32 TotalStaticInlineConstantSize = 0; +for (each static inline constant) +{ + TotalStaticInlineConstantSize += NumConstants * sizeof(Uint32); +} + +// Allocate single memory block for all static inline constants +m_pStaticInlineConstantData = GetRawAllocator().Allocate( + TotalStaticInlineConstantSize, "Static inline constant data", __FILE__, __LINE__); +memset(m_pStaticInlineConstantData, 0, TotalStaticInlineConstantSize); + +// Assign memory to each static inline constant +Uint8* pCurrentDataPtr = static_cast(m_pStaticInlineConstantData); +for (each static inline constant) +{ + m_pStaticResCache->InitializeInlineConstantBuffer( + 0, CacheOffset, NumConstants, pCurrentDataPtr); + pCurrentDataPtr += DataSize; +} +``` + +#### For Mutable/Dynamic Inline Constants (SRB) + +**File**: `PipelineResourceSignatureVkImpl.cpp` - `InitSRBResourceCache()` + +```cpp +void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk& ResourceCache) +{ + // Initialize inline constant buffers + if (m_NumInlineConstantBuffers > 0) + { + // Calculate total memory size + Uint32 TotalInlineConstantSize = 0; + for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) + { + TotalInlineConstantSize += m_InlineConstantBuffers[i].NumConstants * sizeof(Uint32); + } + + // Allocate single memory block + void* pInlineConstantMemory = CacheMemAllocator.Allocate( + TotalInlineConstantSize, "Inline constant data", __FILE__, __LINE__); + memset(pInlineConstantMemory, 0, TotalInlineConstantSize); + + // Pass ownership to resource cache for proper cleanup + ResourceCache.SetInlineConstantMemory(CacheMemAllocator, pInlineConstantMemory); + + // Assign memory to each inline constant buffer + Uint8* pCurrentDataPtr = static_cast(pInlineConstantMemory); + for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) + { + ResourceCache.InitializeInlineConstantBuffer( + DescrSet, CacheOffset, NumConstants, pCurrentDataPtr); + pCurrentDataPtr += InlineCBAttr.NumConstants * sizeof(Uint32); + } + } + + // Bind internal uniform buffers to the resource cache + for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) + { + ResourceCache.SetResource( + pLogicalDevice, // nullptr for dynamic variables + DescrSet, CacheOffset, + { + BindingIndex, 0, // ArrayIndex + RefCntAutoPtr{InlineCBAttr.pBuffer}, + 0, // BufferBaseOffset + InlineCBAttr.NumConstants * sizeof(Uint32) // BufferRangeSize + }); + } +} +``` + +**Memory Management**: +- Static inline constants: Memory allocated via `GetRawAllocator()`, freed in `Destruct()` +- SRB inline constants: Memory allocated via `CacheMemAllocator`, owned by `ShaderResourceCacheVk` (freed in destructor via `m_pInlineConstantMemory`) + +### Step 3: SetInlineConstants Implementation + +**File**: `ShaderVariableManagerVk.cpp` + +```cpp +void ShaderVariableManagerVk::SetInlineConstants(Uint32 ResIndex, + const void* pConstants, + Uint32 FirstConstant, + Uint32 NumConstants) +{ + const PipelineResourceAttribsVk& Attribs = m_pSignature->GetResourceAttribs(ResIndex); + const ResourceCacheContentType CacheType = m_ResourceCache.GetContentType(); + +#ifdef DILIGENT_DEVELOPMENT + const PipelineResourceDesc& ResDesc = m_pSignature->GetResourceDesc(ResIndex); + VerifyInlineConstants(ResDesc, pConstants, FirstConstant, NumConstants); +#endif + + m_ResourceCache.SetInlineConstants( + Attribs.DescrSet, + Attribs.CacheOffset(CacheType), + pConstants, + FirstConstant, + NumConstants); +} +``` + +**File**: `ShaderResourceCacheVk.cpp` + +```cpp +void ShaderResourceCacheVk::SetInlineConstants(Uint32 DescrSetIndex, + Uint32 CacheOffset, + const void* pConstants, + Uint32 FirstConstant, + Uint32 NumConstants) +{ + DescriptorSet& DescrSet = GetDescriptorSet(DescrSetIndex); + Resource& DstRes = DescrSet.GetResource(CacheOffset); + + VERIFY(DstRes.pInlineConstantData != nullptr, + "Inline constant data pointer is null."); + + // Copy to CPU-side staging buffer + Uint32* pDstConstants = reinterpret_cast(DstRes.pInlineConstantData); + memcpy(pDstConstants + FirstConstant, pConstants, NumConstants * sizeof(Uint32)); + + UpdateRevision(); // Mark cache as dirty +} +``` + +### Step 4: Updating GPU Buffer + +During draw/dispatch commands, inline constant buffers are updated: + +**File**: `DeviceContextVkImpl.cpp` + +```cpp +void DeviceContextVkImpl::PrepareForDraw(DRAW_FLAGS Flags) +{ + // Update inline constant buffers before committing descriptor sets + UpdateInlineConstantBuffers(BindInfo); + + // Commit descriptor sets + CommitDescriptorSets(BindInfo, /* ... */); +} + +void DeviceContextVkImpl::UpdateInlineConstantBuffers(ResourceBindInfo& BindInfo) +{ + for (Uint32 SignIdx = 0; SignIdx < BindInfo.ActiveSRBCount; ++SignIdx) + { + const auto& SignInfo = BindInfo.pSignatures[SignIdx]; + if (SignInfo.pSignature->GetNumInlineConstantBuffers() > 0) + { + SignInfo.pSignature->UpdateInlineConstantBuffers( + *SignInfo.pResourceCache, *this); + } + } +} +``` + +**File**: `PipelineResourceSignatureVkImpl.cpp` + +```cpp +void PipelineResourceSignatureVkImpl::UpdateInlineConstantBuffers( + const ShaderResourceCacheVk& ResourceCache, + DeviceContextVkImpl& Ctx) const +{ + for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) + { + const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; + + // Get the inline constant data from the resource cache + const void* pInlineConstantData = ResourceCache.GetInlineConstantData( + InlineCBAttr.DescrSet, InlineCBAttr.BindingIndex); + + if (pInlineConstantData == nullptr) + continue; + + // Map the buffer and copy the data + void* pMappedData = nullptr; + Ctx.MapBuffer(InlineCBAttr.pBuffer, MAP_WRITE, MAP_FLAG_DISCARD, pMappedData); + if (pMappedData != nullptr) + { + memcpy(pMappedData, pInlineConstantData, + InlineCBAttr.NumConstants * sizeof(Uint32)); + Ctx.UnmapBuffer(InlineCBAttr.pBuffer, MAP_WRITE); + } + } +} +``` + +### Step 5: Handling ArraySize for ShaderResourceVariableDesc + +When inline constants are specified via `ShaderResourceVariableDesc` (not `PipelineResourceDesc`), the `ArraySize` must be determined from SPIR-V: + +**File**: `PipelineStateVkImpl.cpp` - `GetDefaultResourceSignatureDesc()` + +```cpp +// For inline constants, ArraySize specifies the number of 32-bit constants, +// not the array dimension. Calculate it from the buffer size. +Uint32 ArraySize = Attribs.ArraySize; +if ((Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0) +{ + // For inline constants specified via ShaderResourceVariableDesc, + // the SPIRV ArraySize is 1 (buffer is not an array), but we need + // the number of 32-bit constants which is BufferStaticSize / sizeof(Uint32) + if (Attribs.BufferStaticSize > 0) + { + ArraySize = Attribs.BufferStaticSize / sizeof(Uint32); + } + else + { + LOG_ERROR_AND_THROW("Resource '", Attribs.Name, "' is marked as inline constants, " + "but has zero buffer size."); + } +} +``` + +### Multiple Inline Constants Support + +Vulkan backend fully supports multiple inline constant buffers: + +- Each inline constant resource creates its own `USAGE_DYNAMIC` uniform buffer +- Each has separate CPU-side staging buffer (`pInlineConstantData`) +- All inline constant buffers are updated together during `PrepareForDraw`/`PrepareForDispatch` + +**Example**: + +```cpp +PipelineResourceDesc Resources[] = { + {"TransformCB", SHADER_TYPE_VERTEX, 16, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, + SHADER_RESOURCE_VARIABLE_TYPE_STATIC, PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS}, + {"MaterialCB", SHADER_TYPE_PIXEL, 8, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, + SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC, PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS}, +}; +``` + +This creates two separate dynamic uniform buffers. + +### Memory Leak Prevention + +The implementation carefully manages memory to prevent leaks: + +1. **Static inline constants**: + - Single allocation in `CreateSetLayouts()` stored in `m_pStaticInlineConstantData` + - Freed in `PipelineResourceSignatureVkImpl::Destruct()` + +2. **SRB inline constants**: + - Single allocation in `InitSRBResourceCache()` + - Ownership transferred to `ShaderResourceCacheVk::m_pInlineConstantMemory` + - Automatically freed by `std::unique_ptr` in `~ShaderResourceCacheVk()` + +### Performance Considerations + +**Vulkan Inline Constants Performance**: + +1. **Overhead**: Similar to D3D11 due to `Map`/`Unmap` operations +2. **Update frequency**: Best for per-draw updates +3. **Buffer sharing**: All SRBs share the same buffer, reducing memory +4. **Future optimization**: True push constants for `layout(push_constant)` blocks (planned) + +--- + +## Static vs Mutable/Dynamic Variables + +### Important Note on Static Variables + +When using **static** inline constants, there's a critical distinction between Signature cache and SRB cache: + +```cpp +// This updates the SIGNATURE's cache (for initialization) +pPRS->GetStaticVariableByName(SHADER_TYPE_VERTEX, "Constants") + ->SetInlineConstants(pData, 0, 16); + +// SRB creation copies from Signature cache to SRB cache +pPRS->CreateShaderResourceBinding(&pSRB, true); + +// For per-frame updates, use the SRB's variable +pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "Constants") + ->SetInlineConstants(pNewData, 0, 16); +``` + +**Key Points**: +- Static variable bindings are copied from Signature to SRB during `CreateShaderResourceBinding(pSRB, true)` +- Updates to Signature's static variables after SRB creation do **not** propagate to existing SRBs +- For per-frame updates, always use the SRB's variable, not the Signature's + +### Recommended Variable Types for Inline Constants + +| Update Frequency | Recommended Variable Type | +|------------------|---------------------------| +| Once at init | `STATIC` | +| Once per SRB | `MUTABLE` | +| Per-frame/draw | `DYNAMIC` or `MUTABLE` | + +--- + +## Performance Considerations + +### D3D12 + +- Root constants are stored directly in the root signature, no indirection +- Each `SetGraphicsRoot32BitConstants` call updates constants immediately +- Optimal for small, frequently-updated data (< 64 constants / 256 bytes) +- Performanc: Fastest + +### D3D11 + +- Emulated using `USAGE_DYNAMIC` constant buffers +- Requires `Map` + `memcpy` + `Unmap` for each update +- Buffer shared across all SRBs (reduces memory, increases update frequency) +- Use `DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT` to skip updates when unchanged +- Performance: same as Constant Buffers, Moderate overhead due to Map/Unmap + +### Vulkan + +- Currently emulated using `USAGE_DYNAMIC` uniform buffers +- Requires `MapBuffer` + `memcpy` + `UnmapBuffer` for each update +- Buffer shared across all SRBs (reduces memory, increases update frequency) +- Performance: Similar to D3D11, moderate overhead due to Map/Unmap +- Future: True push constants for `layout(push_constant)` blocks (planned) + +### Performance Comparison + +| Backend | Implementation | Update Cost | Best Use Case | +|---------|---------------------|-------------|----------------------------------| +| D3D12 | Root constants | Very Low | Per-draw, high-frequency updates | +| D3D11 | Dynamic CB + Map | Moderate | Per-draw, moderate frequency | +| Vulkan | Dynamic UB + Map | Moderate | Per-draw, moderate frequency | + +### General Guidelines + +1. **Size**: Keep inline constants small (typically ≤ 128 bytes) +2. **Frequency**: Use for per-draw or per-dispatch data +3. **Fallback**: For larger data, use dynamic uniform buffers +4. **Multiple resources**: + - D3D12: Full support for multiple inline constants + - D3D11: Full support for multiple inline constants (each as separate dynamic buffer) + - Vulkan: Full support for multiple inline constants (each as separate dynamic uniform buffer) +5. **D3D11 specific**: Use `DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT` when constants unchanged between draws + +--- + +## References + +- [D3D12 Root Signatures](https://docs.microsoft.com/en-us/windows/win32/direct3d12/root-signatures) +- [Vulkan Push Constants](https://docs.vulkan.org/guide/latest/push_constants.html) +- [GLSL Vulkan Extension (GL_KHR_vulkan_glsl)](https://github.com/KhronosGroup/GLSL/blob/master/extensions/khr/GL_KHR_vulkan_glsl.txt) + From a627e363cf3ffe9c54414c6992b9d72a6b0255af Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sat, 6 Dec 2025 22:30:44 +0800 Subject: [PATCH 035/104] fix formatting. --- .../include/PipelineResourceSignatureVkImpl.hpp | 8 ++++---- .../src/PipelineResourceSignatureVkImpl.cpp | 14 +++++++------- .../src/ShaderResourceCacheVk.cpp | 2 +- .../src/ShaderVariableManagerVk.cpp | 4 ++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp index 8bae852beb..f48dc0bd4d 100644 --- a/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp @@ -64,10 +64,10 @@ ASSERT_SIZEOF(ImmutableSamplerAttribsVk, 8, "The struct is used in serialization /// are emulated using dynamic uniform buffers (similar to D3D11 backend). struct InlineConstantBufferAttribsVk { - Uint32 DescrSet = 0; // Descriptor set index - Uint32 BindingIndex = 0; // Binding index within the descriptor set - Uint32 NumConstants = 0; // Number of 32-bit constants - RefCntAutoPtr pBuffer; // Internal dynamic uniform buffer + Uint32 DescrSet = 0; // Descriptor set index + Uint32 BindingIndex = 0; // Binding index within the descriptor set + Uint32 NumConstants = 0; // Number of 32-bit constants + RefCntAutoPtr pBuffer; // Internal dynamic uniform buffer }; struct PipelineResourceSignatureInternalDataVk : PipelineResourceSignatureInternalData diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp index 1edbcb484d..79169db2e5 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp @@ -352,7 +352,7 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) CacheGroupOffsets[CacheGroup] += ResDesc.GetArraySize(); VkDescriptorSetLayoutBinding vkSetLayoutBinding{}; - vkSetLayoutBinding.binding = pAttribs->BindingIndex; + vkSetLayoutBinding.binding = pAttribs->BindingIndex; // For inline constants, descriptor count is 1 (single uniform buffer) vkSetLayoutBinding.descriptorCount = ResDesc.GetArraySize(); vkSetLayoutBinding.stageFlags = ShaderTypesToVkShaderStageFlags(ResDesc.ShaderStages); @@ -376,9 +376,9 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) "Only constant buffers can have INLINE_CONSTANTS flag"); InlineConstantBufferAttribsVk& InlineCBAttribs = m_InlineConstantBuffers[InlineConstantBufferIdx++]; - InlineCBAttribs.DescrSet = pAttribs->DescrSet; - InlineCBAttribs.BindingIndex = pAttribs->BindingIndex; - InlineCBAttribs.NumConstants = ResDesc.ArraySize; // For inline constants, ArraySize is the number of 32-bit constants + InlineCBAttribs.DescrSet = pAttribs->DescrSet; + InlineCBAttribs.BindingIndex = pAttribs->BindingIndex; + InlineCBAttribs.NumConstants = ResDesc.ArraySize; // For inline constants, ArraySize is the number of 32-bit constants // Create a USAGE_DYNAMIC uniform buffer for emulating inline constants // All SRBs created from this signature will share the same inline constant buffer. @@ -755,7 +755,7 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk Attr.BindingIndex, 0, // ArrayIndex RefCntAutoPtr{InlineCBAttr.pBuffer}, - 0, // BufferBaseOffset + 0, // BufferBaseOffset InlineCBAttr.NumConstants * sizeof(Uint32) // BufferRangeSize }); } @@ -910,8 +910,8 @@ void PipelineResourceSignatureVkImpl::CommitDynamicResources(const ShaderResourc const Uint32 CacheOffset = Attr.CacheOffset(CacheType); // For inline constants, GetArraySize() returns 1 (actual array size for cache), // while ArraySize contains the number of 32-bit constants - const Uint32 ArraySize = ResDesc.GetArraySize(); - const DescriptorType DescrType = Attr.GetDescriptorType(); + const Uint32 ArraySize = ResDesc.GetArraySize(); + const DescriptorType DescrType = Attr.GetDescriptorType(); #ifdef DILIGENT_DEBUG { diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp index 8d634624a7..c1ced3b95d 100644 --- a/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp +++ b/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp @@ -964,7 +964,7 @@ void ShaderResourceCacheVk::SetInlineConstants(Uint32 DescrSetIndex, Resource& DstRes = DescrSet.GetResource(CacheOffset); VERIFY(DstRes.pInlineConstantData != nullptr, "Inline constant data pointer is null. " - "Make sure InitializeInlineConstantBuffer was called for this resource."); + "Make sure InitializeInlineConstantBuffer was called for this resource."); // Copy to CPU-side staging buffer Uint32* pDstConstants = reinterpret_cast(DstRes.pInlineConstantData); diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp index 27aac2377c..f575af3f6d 100644 --- a/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp +++ b/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp @@ -656,8 +656,8 @@ void ShaderVariableManagerVk::SetInlineConstants(Uint32 ResIndex, Uint32 FirstConstant, Uint32 NumConstants) { - const PipelineResourceAttribsVk& Attribs = m_pSignature->GetResourceAttribs(ResIndex); - const ResourceCacheContentType CacheType = m_ResourceCache.GetContentType(); + const PipelineResourceAttribsVk& Attribs = m_pSignature->GetResourceAttribs(ResIndex); + const ResourceCacheContentType CacheType = m_ResourceCache.GetContentType(); const Uint32 CacheOffset = Attribs.CacheOffset(CacheType); #ifdef DILIGENT_DEVELOPMENT From f20c19efab6b84dea14dbde456059fb7851826cd Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sat, 6 Dec 2025 22:32:53 +0800 Subject: [PATCH 036/104] fix formatting. --- .../src/PipelineResourceSignatureVkImpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp index 79169db2e5..ac3ed9e909 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp @@ -739,7 +739,7 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk Attr.BindingIndex, 0, // ArrayIndex RefCntAutoPtr{InlineCBAttr.pBuffer}, - 0, // BufferBaseOffset + 0, // BufferBaseOffset InlineCBAttr.NumConstants * sizeof(Uint32) // BufferRangeSize }); } From 98bf2194c81d998c600c86136a2fd7e2843b8ac3 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sat, 6 Dec 2025 22:35:44 +0800 Subject: [PATCH 037/104] fix typo --- doc/SetInlineConstants.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/SetInlineConstants.md b/doc/SetInlineConstants.md index 249158c150..92c863f322 100644 --- a/doc/SetInlineConstants.md +++ b/doc/SetInlineConstants.md @@ -990,7 +990,7 @@ pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "Constants") - Root constants are stored directly in the root signature, no indirection - Each `SetGraphicsRoot32BitConstants` call updates constants immediately - Optimal for small, frequently-updated data (< 64 constants / 256 bytes) -- Performanc: Fastest +- Performance: Fastest ### D3D11 From 437a56216aa608fa78e9d3eb8a999539dc79e2fa Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sat, 6 Dec 2025 22:39:14 +0800 Subject: [PATCH 038/104] remove unused var --- .../src/PipelineResourceSignatureVkImpl.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp index ac3ed9e909..e465fb545d 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp @@ -540,8 +540,7 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) // This must be done after the resources are initialized above if (m_pStaticResCache != nullptr && m_NumInlineConstantBuffers > 0) { - const Uint32 TotalResources = GetTotalResourceCount(); - constexpr ResourceCacheContentType StaticCacheType = ResourceCacheContentType::Signature; + const Uint32 TotalResources = GetTotalResourceCount(); // First pass: calculate total memory size needed for all static inline constants Uint32 TotalStaticInlineConstantSize = 0; From feed4cd846a0b5148d03b4f7f478fe49480411fc Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sun, 7 Dec 2025 11:24:02 +0800 Subject: [PATCH 039/104] Add stuffs for vk push constants --- .../src/GraphicsAccessories.cpp | 8 +- .../interface/PipelineResourceSignature.h | 14 +- .../src/PipelineResourceSignatureBase.cpp | 4 +- .../include/DeviceContextVkImpl.hpp | 14 ++ .../include/PipelineLayoutVk.hpp | 27 +++- .../PipelineResourceSignatureVkImpl.hpp | 19 ++- .../include/PipelineStateVkImpl.hpp | 2 + .../include/VulkanUtilities/CommandBuffer.hpp | 12 ++ .../interface/DeviceContextVk.h | 20 +++ .../src/DeviceContextVkImpl.cpp | 68 ++++++++ .../src/PipelineLayoutVk.cpp | 50 ++++-- .../src/PipelineResourceSignatureVkImpl.cpp | 94 ++++++++--- .../src/PipelineStateVkImpl.cpp | 89 ++++++++++- .../include/SPIRVShaderResources.hpp | 123 ++++++++------ .../ShaderTools/src/SPIRVShaderResources.cpp | 150 +++++++++++++++--- 15 files changed, 569 insertions(+), 125 deletions(-) diff --git a/Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp b/Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp index 9988f3b9f8..ed05eb0bf7 100644 --- a/Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp +++ b/Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp @@ -1637,7 +1637,7 @@ String GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAGS Flags, bool GetFul PIPELINE_RESOURCE_FLAGS Flag = ExtractLSB(Flags); - static_assert(PIPELINE_RESOURCE_FLAG_LAST == (1u << 5), "Please update the switch below to handle the new pipeline resource flag."); + static_assert(PIPELINE_RESOURCE_FLAG_LAST == (1u << 6), "Please update the switch below to handle the new pipeline resource flag."); switch (Flag) { case PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS: @@ -1664,6 +1664,10 @@ String GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAGS Flags, bool GetFul Str.append(GetFullName ? "PIPELINE_RESOURCE_FLAG_GENERAL_INPUT_ATTACHMENT" : "GENERAL_INPUT_ATTACHMENT"); break; + case PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT: + Str.append(GetFullName ? "PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT" : "VULKAN_PUSH_CONSTANT"); + break; + default: UNEXPECTED("Unexpected pipeline resource flag"); } @@ -1812,7 +1816,7 @@ PIPELINE_RESOURCE_FLAGS GetValidPipelineResourceFlags(SHADER_RESOURCE_TYPE Resou switch (ResourceType) { case SHADER_RESOURCE_TYPE_CONSTANT_BUFFER: - return PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS | PIPELINE_RESOURCE_FLAG_RUNTIME_ARRAY | PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS; + return PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS | PIPELINE_RESOURCE_FLAG_RUNTIME_ARRAY | PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS | PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT; case SHADER_RESOURCE_TYPE_TEXTURE_SRV: return PIPELINE_RESOURCE_FLAG_COMBINED_SAMPLER | PIPELINE_RESOURCE_FLAG_RUNTIME_ARRAY; diff --git a/Graphics/GraphicsEngine/interface/PipelineResourceSignature.h b/Graphics/GraphicsEngine/interface/PipelineResourceSignature.h index 1e2fb02465..0f40867a9e 100644 --- a/Graphics/GraphicsEngine/interface/PipelineResourceSignature.h +++ b/Graphics/GraphicsEngine/interface/PipelineResourceSignature.h @@ -148,7 +148,19 @@ DILIGENT_TYPED_ENUM(PIPELINE_RESOURCE_FLAGS, Uint8) /// \note This flag is only valid in Vulkan. PIPELINE_RESOURCE_FLAG_GENERAL_INPUT_ATTACHMENT = 1u << 5, - PIPELINE_RESOURCE_FLAG_LAST = PIPELINE_RESOURCE_FLAG_GENERAL_INPUT_ATTACHMENT + /// Internal flag indicating that this inline constant is a Vulkan push constant + /// (declared with push_constant storage class in SPIR-V). + /// This flag is automatically set by the engine during shader reflection and should not + /// be set manually by the application. + /// + /// When this flag is set along with PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS, the engine + /// will use vkCmdPushConstants instead of a dynamic uniform buffer to update the constants. + /// + /// \note This flag is only valid in Vulkan and is always combined with + /// PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS. + PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT = 1u << 6, + + PIPELINE_RESOURCE_FLAG_LAST = PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT }; DEFINE_FLAG_ENUM_OPERATORS(PIPELINE_RESOURCE_FLAGS); diff --git a/Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp b/Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp index 350a5a0b9c..46230fb03d 100644 --- a/Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp +++ b/Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp @@ -113,10 +113,10 @@ void ValidatePipelineResourceSignatureDesc(const PipelineResourceSignatureDesc& if ((Res.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0) { - if (Res.Flags != PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) + if ((Res.Flags & (PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS | PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT)) != Res.Flags) { LOG_PRS_ERROR_AND_THROW("Incorrect Desc.Resources[", i, "].Flags (", GetPipelineResourceFlagsString(Res.Flags), - "). INLINE_CONSTANTS flag cannot be combined with other flags."); + "). INLINE_CONSTANTS and VULKAN_PUSH_CONSTANT flags cannot be combined with other flags."); } if (Res.ArraySize > 64) diff --git a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp index 5fb0ed0797..f011d83f47 100644 --- a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp @@ -321,6 +321,9 @@ class DeviceContextVkImpl final : public DeviceContextNextGenBase m_PushConstantsData = {}; + Uint32 m_PushConstantsDataSize = 0; // Actual size of valid push constants data + // Graphics/mesh, compute, ray tracing static constexpr Uint32 NUM_PIPELINE_BIND_POINTS = 3; @@ -558,6 +569,9 @@ class DeviceContextVkImpl final : public DeviceContextNextGenBase ppSignatures[], Uint32 SignatureCount) noexcept(false); + void Create(RenderDeviceVkImpl* pDeviceVk, + RefCntAutoPtr ppSignatures[], + Uint32 SignatureCount, + const PushConstantInfoVk& PushConstant = {}) noexcept(false); void Release(RenderDeviceVkImpl* pDeviceVkImpl, Uint64 CommandQueueMask); VkPipelineLayout GetVkPipelineLayout() const { return m_VkPipelineLayout; } @@ -59,6 +69,15 @@ class PipelineLayoutVk return m_FirstDescrSetIndex[Index]; } + // Returns true if this pipeline layout has push constants + bool HasPushConstants() const { return m_PushConstantSize > 0; } + + // Returns the size of push constants in bytes + Uint32 GetPushConstantSize() const { return m_PushConstantSize; } + + // Returns the shader stage flags for push constants + VkShaderStageFlags GetPushConstantStageFlags() const { return m_PushConstantStageFlags; } + private: VulkanUtilities::PipelineLayoutWrapper m_VkPipelineLayout; @@ -70,6 +89,12 @@ class PipelineLayoutVk // (Maximum is MAX_RESOURCE_SIGNATURES * 2) Uint8 m_DescrSetCount = 0; + // Push constant size in bytes + Uint32 m_PushConstantSize = 0; + + // Shader stages that use push constants + VkShaderStageFlags m_PushConstantStageFlags = 0; + #ifdef DILIGENT_DEBUG Uint32 m_DbgMaxBindIndex = 0; #endif diff --git a/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp index f48dc0bd4d..b617371ebc 100644 --- a/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp @@ -60,14 +60,16 @@ struct ImmutableSamplerAttribsVk ASSERT_SIZEOF(ImmutableSamplerAttribsVk, 8, "The struct is used in serialization and must be tightly packed"); /// Inline constant buffer attributes for Vulkan backend. -/// Since Vulkan only supports one push constant block per pipeline, inline constants -/// are emulated using dynamic uniform buffers (similar to D3D11 backend). +/// Inline constants can be either: +/// 1. True push constants (from SPIR-V push_constant storage class) - use vkCmdPushConstants +/// 2. Emulated inline constants - use dynamic uniform buffers (similar to D3D11 backend) struct InlineConstantBufferAttribsVk { - Uint32 DescrSet = 0; // Descriptor set index - Uint32 BindingIndex = 0; // Binding index within the descriptor set - Uint32 NumConstants = 0; // Number of 32-bit constants - RefCntAutoPtr pBuffer; // Internal dynamic uniform buffer + Uint32 DescrSet = 0; // Descriptor set index + Uint32 BindingIndex = 0; // Binding index within the descriptor set + Uint32 NumConstants = 0; // Number of 32-bit constants + bool IsPushConstant = false; // True if this is a Vulkan push constant (not emulated) + RefCntAutoPtr pBuffer; // Internal dynamic uniform buffer (null for true push constants) }; struct PipelineResourceSignatureInternalDataVk : PipelineResourceSignatureInternalData @@ -156,6 +158,11 @@ class PipelineResourceSignatureVkImpl final : public PipelineResourceSignatureBa // Returns the inline constant buffer attributes const InlineConstantBufferAttribsVk* GetInlineConstantBuffers() const { return m_InlineConstantBuffers.get(); } + // Adds PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT flag to a resource if it has + // PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS but is missing the Vulkan push constant flag. + // This is called during pipeline state creation when shader reflection detects a push constant. + void UpdatePushConstantFlags(Uint32 ResIndex); + #ifdef DILIGENT_DEVELOPMENT /// Verifies committed resource using the SPIRV resource attributes from the PSO. bool DvpValidateCommittedResource(const DeviceContextVkImpl* pDeviceCtx, diff --git a/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp index 8923ac1fe9..ed19a4cf93 100644 --- a/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp @@ -133,6 +133,8 @@ class PipelineStateVkImpl final : public PipelineStateBase void InitializePipeline(const ComputePipelineStateCreateInfo& CreateInfo); void InitializePipeline(const RayTracingPipelineStateCreateInfo& CreateInfo); + void InitPushConstantInfo(const TShaderStages& ShaderStages, PushConstantInfoVk& PushConstant) noexcept(false); + // TPipelineStateBase::Construct needs access to InitializePipeline friend TPipelineStateBase; diff --git a/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/CommandBuffer.hpp b/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/CommandBuffer.hpp index 6732457fa9..ed738a3036 100644 --- a/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/CommandBuffer.hpp +++ b/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/CommandBuffer.hpp @@ -504,6 +504,18 @@ class CommandBuffer vkCmdBindDescriptorSets(m_VkCmdBuffer, pipelineBindPoint, layout, firstSet, descriptorSetCount, pDescriptorSets, dynamicOffsetCount, pDynamicOffsets); } + __forceinline void PushConstants(VkPipelineLayout layout, + VkShaderStageFlags stageFlags, + uint32_t offset, + uint32_t size, + const void* pValues) + { + VERIFY_EXPR(m_VkCmdBuffer != VK_NULL_HANDLE); + VERIFY_EXPR(pValues != nullptr); + VERIFY_EXPR(size > 0); + vkCmdPushConstants(m_VkCmdBuffer, layout, stageFlags, offset, size, pValues); + } + __forceinline void CopyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, uint32_t regionCount, diff --git a/Graphics/GraphicsEngineVulkan/interface/DeviceContextVk.h b/Graphics/GraphicsEngineVulkan/interface/DeviceContextVk.h index 06c39dd92c..99c5eccdb0 100644 --- a/Graphics/GraphicsEngineVulkan/interface/DeviceContextVk.h +++ b/Graphics/GraphicsEngineVulkan/interface/DeviceContextVk.h @@ -84,6 +84,25 @@ DILIGENT_BEGIN_INTERFACE(IDeviceContextVk, IDeviceContext) /// calling IDeviceContext::InvalidateState() and then manually restore all required states via /// appropriate Diligent API calls. VIRTUAL VkCommandBuffer METHOD(GetVkCommandBuffer)(THIS) PURE; + + /// Sets push constant data for the currently bound pipeline. + + /// \param [in] pData - Pointer to the push constant data. + /// \param [in] Offset - Offset in bytes from the start of the push constant block. + /// \param [in] Size - Size of the data to set in bytes. + /// + /// \remarks This method sets push constant data that will be used in subsequent draw or dispatch calls. + /// The pipeline state must be set before calling this method. + /// Push constants are a Vulkan feature that allows small amounts of data to be passed to + /// shaders efficiently without using descriptor sets. In shaders, push constants are declared + /// using the layout(push_constant) qualifier in GLSL or [[vk::push_constant]] attribute in HLSL. + /// + /// \note The total size of push constants is limited by the device (typically 128-256 bytes). + /// Check VkPhysicalDeviceLimits::maxPushConstantsSize for the exact limit. + VIRTUAL void METHOD(SetPushConstants)(THIS_ + const void* pData, + Uint32 Offset, + Uint32 Size) PURE; }; DILIGENT_END_INTERFACE @@ -95,6 +114,7 @@ DILIGENT_END_INTERFACE # define IDeviceContextVk_TransitionImageLayout(This, ...) CALL_IFACE_METHOD(DeviceContextVk, TransitionImageLayout, This, __VA_ARGS__) # define IDeviceContextVk_BufferMemoryBarrier(This, ...) CALL_IFACE_METHOD(DeviceContextVk, BufferMemoryBarrier, This, __VA_ARGS__) +# define IDeviceContextVk_SetPushConstants(This, ...) CALL_IFACE_METHOD(DeviceContextVk, SetPushConstants, This, __VA_ARGS__) // clang-format on diff --git a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp index f22ee03040..9c29ad2ccd 100644 --- a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp @@ -390,6 +390,16 @@ void DeviceContextVkImpl::SetPipelineState(IPipelineState* pPipelineState) // Reserve space to store all dynamic buffer offsets m_DynamicBufferOffsets.resize(TotalDynamicOffsetCount); + + // Mark push constants as dirty when PSO changes, since the new PSO might have + // a different pipeline layout. If the new PSO has push constants, they need to + // be re-committed even if the data hasn't changed. + if (Layout.HasPushConstants()) + { + m_State.PushConstantsDirty = true; + // Ensure we have initialized push constants data for the new PSO + // (user should call SetPushConstants before draw/dispatch) + } } DeviceContextVkImpl::ResourceBindInfo& DeviceContextVkImpl::GetBindInfo(PIPELINE_TYPE Type) @@ -433,6 +443,54 @@ void DeviceContextVkImpl::UpdateInlineConstantBuffers(ResourceBindInfo& BindInfo } } +void DeviceContextVkImpl::SetPushConstants(const void* pData, Uint32 Offset, Uint32 Size) +{ + DEV_CHECK_ERR(pData != nullptr || Size == 0, "pData must not be null when Size is non-zero"); + DEV_CHECK_ERR(Offset + Size <= MAX_PUSH_CONSTANTS_SIZE, + "Push constant data range [", Offset, ", ", Offset + Size, ") exceeds the maximum supported size (", MAX_PUSH_CONSTANTS_SIZE, ")"); + + // Note: This function may be called from UpdateInlineConstantBuffers during draw preparation, + // at which point the pipeline state is guaranteed to be bound. When called from user code, + // we validate that the pipeline has push constants. + if (m_pPipelineState != nullptr) + { + const PipelineLayoutVk& Layout = m_pPipelineState->GetPipelineLayout(); + if (Layout.HasPushConstants()) + { + DEV_CHECK_ERR(Offset + Size <= Layout.GetPushConstantSize(), + "Push constant data range [", Offset, ", ", Offset + Size, ") exceeds the push constant block size (", Layout.GetPushConstantSize(), ")"); + } + } + + if (Size > 0) + { + memcpy(m_PushConstantsData.data() + Offset, pData, Size); + m_PushConstantsDataSize = std::max(m_PushConstantsDataSize, Offset + Size); + m_State.PushConstantsDirty = true; + } +} + +void DeviceContextVkImpl::CommitPushConstants() +{ + if (!m_State.PushConstantsDirty) + return; + + VERIFY_EXPR(m_pPipelineState != nullptr); + const PipelineLayoutVk& Layout = m_pPipelineState->GetPipelineLayout(); + + if (!Layout.HasPushConstants()) + return; + + const Uint32 Size = Layout.GetPushConstantSize(); + const VkShaderStageFlags StageFlags = Layout.GetPushConstantStageFlags(); + const VkPipelineLayout vkLayout = Layout.GetVkPipelineLayout(); + + VERIFY_EXPR(Size <= m_PushConstantsData.size()); + + m_CommandBuffer.PushConstants(vkLayout, StageFlags, 0, Size, m_PushConstantsData.data()); + m_State.PushConstantsDirty = false; +} + void DeviceContextVkImpl::CommitDescriptorSets(ResourceBindInfo& BindInfo, Uint32 CommitSRBMask) { 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) { CommitDescriptorSets(BindInfo, CommitMask); } + + // Commit push constants if dirty + CommitPushConstants(); + #ifdef DILIGENT_DEVELOPMENT // Must be called after CommitDescriptorSets as it needs SetInfo.BaseInd DvpValidateCommittedShaderResources(BindInfo); @@ -1094,6 +1156,9 @@ void DeviceContextVkImpl::PrepareForDispatchCompute() CommitDescriptorSets(BindInfo, CommitMask); } + // Commit push constants if dirty + CommitPushConstants(); + #ifdef DILIGENT_DEVELOPMENT // Must be called after CommitDescriptorSets as it needs SetInfo.BaseInd DvpValidateCommittedShaderResources(BindInfo); @@ -1114,6 +1179,9 @@ void DeviceContextVkImpl::PrepareForRayTracing() CommitDescriptorSets(BindInfo, CommitMask); } + // Commit push constants if dirty + CommitPushConstants(); + #ifdef DILIGENT_DEVELOPMENT // Must be called after CommitDescriptorSets as it needs SetInfo.BaseInd DvpValidateCommittedShaderResources(BindInfo); diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineLayoutVk.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineLayoutVk.cpp index 16d0b140b4..7d0a459083 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineLayoutVk.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineLayoutVk.cpp @@ -59,7 +59,10 @@ void PipelineLayoutVk::Release(RenderDeviceVkImpl* pDeviceVk, Uint64 CommandQueu } } -void PipelineLayoutVk::Create(RenderDeviceVkImpl* pDeviceVk, RefCntAutoPtr ppSignatures[], Uint32 SignatureCount) noexcept(false) +void PipelineLayoutVk::Create(RenderDeviceVkImpl* pDeviceVk, + RefCntAutoPtr ppSignatures[], + Uint32 SignatureCount, + const PushConstantInfoVk& PushConstant) noexcept(false) { VERIFY(m_DescrSetCount == 0 && !m_VkPipelineLayout, "This pipeline layout is already initialized"); @@ -114,19 +117,48 @@ void PipelineLayoutVk::Create(RenderDeviceVkImpl* pDeviceVk, RefCntAutoPtr 0) + { + if (PushConstant.Size > Limits.maxPushConstantsSize) + { + LOG_ERROR_AND_THROW("Push constant size (", PushConstant.Size, + " bytes) exceeds device limit (", Limits.maxPushConstantsSize, " bytes)"); + } + } + VERIFY(m_DescrSetCount <= std::numeric_limits::max(), "Descriptor set count (", DescSetLayoutCount, ") exceeds the maximum representable value"); VkPipelineLayoutCreateInfo PipelineLayoutCI = {}; - PipelineLayoutCI.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - PipelineLayoutCI.pNext = nullptr; - PipelineLayoutCI.flags = 0; // reserved for future use - PipelineLayoutCI.setLayoutCount = DescSetLayoutCount; - PipelineLayoutCI.pSetLayouts = DescSetLayoutCount ? DescSetLayouts.data() : nullptr; - PipelineLayoutCI.pushConstantRangeCount = 0; - PipelineLayoutCI.pPushConstantRanges = nullptr; - m_VkPipelineLayout = pDeviceVk->GetLogicalDevice().CreatePipelineLayout(PipelineLayoutCI); + PipelineLayoutCI.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + PipelineLayoutCI.pNext = nullptr; + PipelineLayoutCI.flags = 0; // reserved for future use + PipelineLayoutCI.setLayoutCount = DescSetLayoutCount; + PipelineLayoutCI.pSetLayouts = DescSetLayoutCount ? DescSetLayouts.data() : nullptr; + + // Set up push constant range if present + VkPushConstantRange PushConstantRange = {}; + if (PushConstant.Size > 0 && PushConstant.StageFlags != 0) + { + PushConstantRange.stageFlags = PushConstant.StageFlags; + PushConstantRange.offset = 0; + PushConstantRange.size = PushConstant.Size; + + PipelineLayoutCI.pushConstantRangeCount = 1; + PipelineLayoutCI.pPushConstantRanges = &PushConstantRange; + + m_PushConstantSize = PushConstant.Size; + m_PushConstantStageFlags = PushConstant.StageFlags; + } + else + { + PipelineLayoutCI.pushConstantRangeCount = 0; + PipelineLayoutCI.pPushConstantRanges = nullptr; + } + + m_VkPipelineLayout = pDeviceVk->GetLogicalDevice().CreatePipelineLayout(PipelineLayoutCI); m_DescrSetCount = static_cast(DescSetLayoutCount); } diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp index e465fb545d..7cb7aac45c 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp @@ -351,14 +351,20 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) // For inline constants, GetArraySize() returns 1 (actual array size for cache) CacheGroupOffsets[CacheGroup] += ResDesc.GetArraySize(); - VkDescriptorSetLayoutBinding vkSetLayoutBinding{}; - vkSetLayoutBinding.binding = pAttribs->BindingIndex; - // For inline constants, descriptor count is 1 (single uniform buffer) - vkSetLayoutBinding.descriptorCount = ResDesc.GetArraySize(); - vkSetLayoutBinding.stageFlags = ShaderTypesToVkShaderStageFlags(ResDesc.ShaderStages); - vkSetLayoutBinding.pImmutableSamplers = pVkImmutableSamplers; - vkSetLayoutBinding.descriptorType = DescriptorTypeToVkDescriptorType(pAttribs->GetDescriptorType()); - vkSetLayoutBindings[SetId].push_back(vkSetLayoutBinding); + // Vulkan push constants don't use descriptor sets - they use vkCmdPushConstants + // So we skip creating descriptor set layout bindings for them + const bool IsPushConstant = (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != 0; + if (!IsPushConstant) + { + VkDescriptorSetLayoutBinding vkSetLayoutBinding{}; + vkSetLayoutBinding.binding = pAttribs->BindingIndex; + // For inline constants, descriptor count is 1 (single uniform buffer) + vkSetLayoutBinding.descriptorCount = ResDesc.GetArraySize(); + vkSetLayoutBinding.stageFlags = ShaderTypesToVkShaderStageFlags(ResDesc.ShaderStages); + vkSetLayoutBinding.pImmutableSamplers = pVkImmutableSamplers; + vkSetLayoutBinding.descriptorType = DescriptorTypeToVkDescriptorType(pAttribs->GetDescriptorType()); + vkSetLayoutBindings[SetId].push_back(vkSetLayoutBinding); + } if (ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) { @@ -369,7 +375,7 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) StaticCacheOffset += ResDesc.GetArraySize(); } - // Handle inline constant buffers - create internal dynamic uniform buffers + // Handle inline constant buffers if (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) { VERIFY(ResDesc.ResourceType == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, @@ -380,10 +386,15 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) InlineCBAttribs.BindingIndex = pAttribs->BindingIndex; InlineCBAttribs.NumConstants = ResDesc.ArraySize; // For inline constants, ArraySize is the number of 32-bit constants - // Create a USAGE_DYNAMIC uniform buffer for emulating inline constants - // All SRBs created from this signature will share the same inline constant buffer. - if (m_pDevice) + // Check if this is a true Vulkan push constant (uses vkCmdPushConstants) + // or an emulated inline constant (uses dynamic uniform buffer) + const bool IsPushConstant = (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != 0; + InlineCBAttribs.IsPushConstant = IsPushConstant; + + if (!IsPushConstant && m_pDevice) { + // Create a USAGE_DYNAMIC uniform buffer for emulating inline constants + // All SRBs created from this signature will share the same inline constant buffer. std::string Name = m_Desc.Name; Name += " - "; Name += ResDesc.Name; @@ -399,6 +410,7 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) VERIFY_EXPR(pBuffer); InlineCBAttribs.pBuffer = RefCntAutoPtr{pBuffer, IID_BufferVk}; } + // For true push constants, pBuffer remains null - data is submitted via vkCmdPushConstants } } VERIFY_EXPR(InlineConstantBufferIdx == m_NumInlineConstantBuffers); @@ -1242,21 +1254,61 @@ void PipelineResourceSignatureVkImpl::UpdateInlineConstantBuffers(const ShaderRe { const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; - if (!InlineCBAttr.pBuffer) - continue; - // Get the inline constant data from the resource cache const void* pInlineConstantData = ResourceCache.GetInlineConstantData(InlineCBAttr.DescrSet, InlineCBAttr.BindingIndex); if (pInlineConstantData == nullptr) continue; - // Map the buffer and copy the data - void* pMappedData = nullptr; - Ctx.MapBuffer(InlineCBAttr.pBuffer, MAP_WRITE, MAP_FLAG_DISCARD, pMappedData); - if (pMappedData != nullptr) + const Uint32 DataSize = InlineCBAttr.NumConstants * sizeof(Uint32); + + if (InlineCBAttr.IsPushConstant) + { + // For true push constants, copy data to the device context's push constants buffer + // This will be submitted via vkCmdPushConstants before draw/dispatch + Ctx.SetPushConstants(pInlineConstantData, 0, DataSize); + } + else if (InlineCBAttr.pBuffer) + { + // For emulated inline constants, map the buffer and copy the data + void* pMappedData = nullptr; + Ctx.MapBuffer(InlineCBAttr.pBuffer, MAP_WRITE, MAP_FLAG_DISCARD, pMappedData); + if (pMappedData != nullptr) + { + memcpy(pMappedData, pInlineConstantData, DataSize); + Ctx.UnmapBuffer(InlineCBAttr.pBuffer, MAP_WRITE); + } + } + } +} + +void PipelineResourceSignatureVkImpl::UpdatePushConstantFlags(Uint32 ResIndex) +{ + VERIFY_EXPR(ResIndex < this->m_Desc.NumResources); + + // Access the mutable resource description + PipelineResourceDesc& ResDesc = const_cast(this->m_Desc.Resources[ResIndex]); + + // Check if the resource has INLINE_CONSTANTS flag but is missing VULKAN_PUSH_CONSTANT flag + if ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0 && + (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) == 0) + { + // Add the Vulkan push constant flag + ResDesc.Flags |= PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT; + + // Update the corresponding InlineConstantBufferAttribsVk to mark it as a push constant + // We need to find the inline constant buffer attribute that corresponds to this resource + const ResourceAttribs& Attr = GetResourceAttribs(ResIndex); + for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) { - memcpy(pMappedData, pInlineConstantData, InlineCBAttr.NumConstants * sizeof(Uint32)); - Ctx.UnmapBuffer(InlineCBAttr.pBuffer, MAP_WRITE); + InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; + if (InlineCBAttr.DescrSet == Attr.DescrSet && InlineCBAttr.BindingIndex == Attr.BindingIndex) + { + // Mark this as a true push constant + InlineCBAttr.IsPushConstant = true; + // Note: pBuffer may have been created during CreateSetLayouts, but that's OK - + // UpdateInlineConstantBuffers will check IsPushConstant first and use vkCmdPushConstants + break; + } } } } diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp index 3c74aa9fc8..f1609915af 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp @@ -605,6 +605,10 @@ PipelineResourceSignatureDescWrapper PipelineStateVkImpl::GetDefaultResourceSign ShaderResources.ProcessResources( [&](const SPIRVShaderResourceAttribs& Attribs, Uint32) // { + // Note: Push constants are now included in the signature with + // PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT flag set. + // They will be handled via vkCmdPushConstants at runtime. + // We can't skip immutable samplers because immutable sampler arrays have to be defined // as both resource and sampler. //if (Res.Type == SPIRVShaderResourceAttribs::SeparateSampler && @@ -711,6 +715,10 @@ void PipelineStateVkImpl::RemapOrVerifyShaderResources( pShaderResources->ProcessResources( [&](const SPIRVShaderResourceAttribs& SPIRVAttribs, Uint32) // { + // Push constants don't use descriptor sets and don't need binding/set remapping. + // They are handled via vkCmdPushConstants. We still verify they exist in the signature. + const bool IsPushConstant = (SPIRVAttribs.Type == SPIRVShaderResourceAttribs::ResourceType::PushConstant); + const ResourceAttribution ResAttribution = GetResourceAttribution(SPIRVAttribs.Name, ShaderType, pSignatures, SignatureCount); if (!ResAttribution) { @@ -723,6 +731,32 @@ void PipelineStateVkImpl::RemapOrVerifyShaderResources( const SHADER_RESOURCE_TYPE ResType = SPIRVShaderResourceAttribs::GetShaderResourceType(SPIRVAttribs.Type); const PIPELINE_RESOURCE_FLAGS Flags = SPIRVShaderResourceAttribs::GetPipelineResourceFlags(SPIRVAttribs.Type); + // For push constants, skip descriptor set operations but validate the resource exists + if (IsPushConstant) + { + if (ResAttribution.ResourceIndex != ResourceAttribution::InvalidResourceIndex) + { + const PipelineResourceDesc& ResDesc = ResAttribution.pSignature->GetResourceDesc(ResAttribution.ResourceIndex); + + // Automatically add PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT flag if the resource + // has PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS but is missing the Vulkan push constant flag. + // This ensures that true push constants use vkCmdPushConstants instead of dynamic uniform buffers. + // We got an unused uniform buffer and an unused descriptor set after this, it's okay though. + PipelineResourceSignatureVkImpl* pSignatureVk = const_cast(ResAttribution.pSignature); + pSignatureVk->UpdatePushConstantFlags(ResAttribution.ResourceIndex); + + // Get the updated resource description after adding the flag + const PipelineResourceDesc& UpdatedResDesc = pSignatureVk->GetResourceDesc(ResAttribution.ResourceIndex); + ValidatePipelineResourceCompatibility(UpdatedResDesc, ResType, Flags, SPIRVAttribs.ArraySize, + pShader->GetDesc().Name, SignDesc.Name); + } + if (pDvpResourceAttibutions) + pDvpResourceAttibutions->emplace_back(ResAttribution); + + // Skip descriptor set remapping for push constants + return; + } + Uint32 ResourceBinding = ~0u; Uint32 DescriptorSet = ~0u; if (ResAttribution.ResourceIndex != ResourceAttribution::InvalidResourceIndex) @@ -805,6 +839,38 @@ void PipelineStateVkImpl::RemapOrVerifyShaderResources( } } +void PipelineStateVkImpl::InitPushConstantInfo(const TShaderStages& ShaderStages, PushConstantInfoVk& PushConstant) noexcept(false) +{ + for (const ShaderStageInfo& Stage : ShaderStages) + { + for (const ShaderVkImpl* pShader : Stage.Shaders) + { + const auto& pShaderResources = pShader->GetShaderResources(); + if (pShaderResources && pShaderResources->GetNumPushConstants() > 0) + { + // There should be at most one push constant block per shader + const SPIRVShaderResourceAttribs& PCAttribs = pShaderResources->GetPushConstant(0); + const Uint32 PCSize = PCAttribs.BufferStaticSize; + + if (PushConstant.Size == 0) + { + // First shader with push constants - record the size + PushConstant.Size = PCSize; + } + else if (PushConstant.Size != PCSize) + { + // Multiple shaders with different push constant sizes + // This is allowed in Vulkan - take the maximum size + PushConstant.Size = std::max(PushConstant.Size, PCSize); + } + + // Add this shader stage to the stage flags + PushConstant.StageFlags |= ShaderTypeToVkShaderStageFlagBit(Stage.Type); + } + } + } +} + void PipelineStateVkImpl::InitPipelineLayout(const PipelineStateCreateInfo& CreateInfo, TShaderStages& ShaderStages) noexcept(false) { const PSO_CREATE_INTERNAL_FLAGS InternalFlags = GetInternalCreateFlags(CreateInfo); @@ -819,7 +885,12 @@ void PipelineStateVkImpl::InitPipelineLayout(const PipelineStateCreateInfo& Crea DvpValidateResourceLimits(); #endif - m_PipelineLayout.Create(GetDevice(), m_Signatures, m_SignatureCount); + // Extract push constant information from shaders + // Vulkan allows only one push constant block per pipeline, but it can be accessed from multiple shader stages + PushConstantInfoVk PushConstant; + InitPushConstantInfo(ShaderStages, PushConstant); + + m_PipelineLayout.Create(GetDevice(), m_Signatures, m_SignatureCount, PushConstant); const bool RemapResources = (CreateInfo.Flags & PSO_CREATE_FLAG_DONT_REMAP_SHADER_RESOURCES) == 0; const bool VerifyBindings = !RemapResources && ((InternalFlags & PSO_CREATE_INTERNAL_FLAG_NO_SHADER_REFLECTION) == 0); @@ -967,14 +1038,22 @@ void PipelineStateVkImpl::DvpVerifySRBResources(const DeviceContextVkImpl* pCtx, pResources->ProcessResources( [&](const SPIRVShaderResourceAttribs& ResAttribs, Uint32) // { + // Push constants are validated but don't use descriptor sets + const bool IsPushConstant = (ResAttribs.Type == SPIRVShaderResourceAttribs::ResourceType::PushConstant); + if (!res_info->IsImmutableSampler()) // There are also immutable samplers in the list { VERIFY_EXPR(res_info->pSignature != nullptr); VERIFY_EXPR(res_info->pSignature->GetDesc().BindingIndex == res_info->SignatureIndex); - const ShaderResourceCacheVk* pResourceCache = ResourceCaches[res_info->SignatureIndex]; - DEV_CHECK_ERR(pResourceCache != nullptr, "Resource cache at index ", res_info->SignatureIndex, " is null."); - res_info->pSignature->DvpValidateCommittedResource(pCtx, ResAttribs, res_info->ResourceIndex, *pResourceCache, - pResources->GetShaderName(), m_Desc.Name); + + // Skip descriptor set validation for push constants (they don't use descriptor sets) + if (!IsPushConstant) + { + const ShaderResourceCacheVk* pResourceCache = ResourceCaches[res_info->SignatureIndex]; + DEV_CHECK_ERR(pResourceCache != nullptr, "Resource cache at index ", res_info->SignatureIndex, " is null."); + res_info->pSignature->DvpValidateCommittedResource(pCtx, ResAttribs, res_info->ResourceIndex, *pResourceCache, + pResources->GetShaderName(), m_Desc.Name); + } } ++res_info; } // diff --git a/Graphics/ShaderTools/include/SPIRVShaderResources.hpp b/Graphics/ShaderTools/include/SPIRVShaderResources.hpp index 891cd8a5e2..6f057ebd08 100644 --- a/Graphics/ShaderTools/include/SPIRVShaderResources.hpp +++ b/Graphics/ShaderTools/include/SPIRVShaderResources.hpp @@ -32,9 +32,9 @@ // SPIRVShaderResources class uses continuous chunk of memory to store all resources, as follows: // -// m_MemoryBuffer m_TotalResources -// | | | -// | Uniform Buffers | Storage Buffers | Storage Images | Sampled Images | Atomic Counters | Separate Samplers | Separate Images | Stage Inputs | Resource Names | +// m_MemoryBuffer m_TotalResources +// | | | +// | Uniform Buffers | Storage Buffers | Storage Images | Sampled Images | Atomic Counters | Separate Samplers | Separate Images | Input Attachments | Accel Structs | Push Constants | Stage Inputs | Resource Names | #include #include @@ -79,6 +79,7 @@ struct SPIRVShaderResourceAttribs SeparateSampler, InputAttachment, AccelerationStructure, + PushConstant, // Vulkan push constant buffer - special inline constant that uses vkCmdPushConstants NumResourceTypes }; @@ -111,6 +112,11 @@ struct SPIRVShaderResourceAttribs Uint32 _BufferStaticSize = 0, Uint32 _BufferStride = 0) noexcept; + // Constructor for push constants (no binding or descriptor set decoration) + SPIRVShaderResourceAttribs(const char* _Name, + ResourceType _Type, + Uint32 _BufferStaticSize) noexcept; + ShaderResourceDesc GetResourceDesc() const { return ShaderResourceDesc{Name, GetShaderResourceType(Type), ArraySize}; @@ -166,28 +172,30 @@ class SPIRVShaderResources // clang-format off - Uint32 GetNumUBs ()const noexcept{ return (m_StorageBufferOffset - 0); } - Uint32 GetNumSBs ()const noexcept{ return (m_StorageImageOffset - m_StorageBufferOffset); } - Uint32 GetNumImgs ()const noexcept{ return (m_SampledImageOffset - m_StorageImageOffset); } - Uint32 GetNumSmpldImgs ()const noexcept{ return (m_AtomicCounterOffset - m_SampledImageOffset); } - Uint32 GetNumACs ()const noexcept{ return (m_SeparateSamplerOffset - m_AtomicCounterOffset); } - Uint32 GetNumSepSmplrs ()const noexcept{ return (m_SeparateImageOffset - m_SeparateSamplerOffset);} - Uint32 GetNumSepImgs ()const noexcept{ return (m_InputAttachmentOffset - m_SeparateImageOffset); } - Uint32 GetNumInptAtts ()const noexcept{ return (m_AccelStructOffset - m_InputAttachmentOffset);} - Uint32 GetNumAccelStructs()const noexcept{ return (m_TotalResources - m_AccelStructOffset); } + Uint32 GetNumUBs ()const noexcept{ return (m_StorageBufferOffset - 0); } + Uint32 GetNumSBs ()const noexcept{ return (m_StorageImageOffset - m_StorageBufferOffset); } + Uint32 GetNumImgs ()const noexcept{ return (m_SampledImageOffset - m_StorageImageOffset); } + Uint32 GetNumSmpldImgs ()const noexcept{ return (m_AtomicCounterOffset - m_SampledImageOffset); } + Uint32 GetNumACs ()const noexcept{ return (m_SeparateSamplerOffset - m_AtomicCounterOffset); } + Uint32 GetNumSepSmplrs ()const noexcept{ return (m_SeparateImageOffset - m_SeparateSamplerOffset); } + Uint32 GetNumSepImgs ()const noexcept{ return (m_InputAttachmentOffset - m_SeparateImageOffset); } + Uint32 GetNumInptAtts ()const noexcept{ return (m_AccelStructOffset - m_InputAttachmentOffset); } + Uint32 GetNumAccelStructs ()const noexcept{ return (m_PushConstantOffset - m_AccelStructOffset); } + Uint32 GetNumPushConstants()const noexcept{ return (m_TotalResources - m_PushConstantOffset); } Uint32 GetTotalResources () const noexcept { return m_TotalResources; } Uint32 GetNumShaderStageInputs()const noexcept { return m_NumShaderStageInputs; } - const SPIRVShaderResourceAttribs& GetUB (Uint32 n)const noexcept{ return GetResAttribs(n, GetNumUBs(), 0 ); } - const SPIRVShaderResourceAttribs& GetSB (Uint32 n)const noexcept{ return GetResAttribs(n, GetNumSBs(), m_StorageBufferOffset ); } - const SPIRVShaderResourceAttribs& GetImg (Uint32 n)const noexcept{ return GetResAttribs(n, GetNumImgs(), m_StorageImageOffset ); } - const SPIRVShaderResourceAttribs& GetSmpldImg (Uint32 n)const noexcept{ return GetResAttribs(n, GetNumSmpldImgs(), m_SampledImageOffset ); } - const SPIRVShaderResourceAttribs& GetAC (Uint32 n)const noexcept{ return GetResAttribs(n, GetNumACs(), m_AtomicCounterOffset ); } - const SPIRVShaderResourceAttribs& GetSepSmplr (Uint32 n)const noexcept{ return GetResAttribs(n, GetNumSepSmplrs(), m_SeparateSamplerOffset); } - const SPIRVShaderResourceAttribs& GetSepImg (Uint32 n)const noexcept{ return GetResAttribs(n, GetNumSepImgs(), m_SeparateImageOffset ); } - const SPIRVShaderResourceAttribs& GetInptAtt (Uint32 n)const noexcept{ return GetResAttribs(n, GetNumInptAtts(), m_InputAttachmentOffset); } - const SPIRVShaderResourceAttribs& GetAccelStruct(Uint32 n)const noexcept{ return GetResAttribs(n, GetNumAccelStructs(), m_AccelStructOffset ); } - const SPIRVShaderResourceAttribs& GetResource (Uint32 n)const noexcept{ return GetResAttribs(n, GetTotalResources(), 0 ); } + const SPIRVShaderResourceAttribs& GetUB (Uint32 n)const noexcept{ return GetResAttribs(n, GetNumUBs(), 0 ); } + const SPIRVShaderResourceAttribs& GetSB (Uint32 n)const noexcept{ return GetResAttribs(n, GetNumSBs(), m_StorageBufferOffset ); } + const SPIRVShaderResourceAttribs& GetImg (Uint32 n)const noexcept{ return GetResAttribs(n, GetNumImgs(), m_StorageImageOffset ); } + const SPIRVShaderResourceAttribs& GetSmpldImg (Uint32 n)const noexcept{ return GetResAttribs(n, GetNumSmpldImgs(), m_SampledImageOffset ); } + const SPIRVShaderResourceAttribs& GetAC (Uint32 n)const noexcept{ return GetResAttribs(n, GetNumACs(), m_AtomicCounterOffset ); } + const SPIRVShaderResourceAttribs& GetSepSmplr (Uint32 n)const noexcept{ return GetResAttribs(n, GetNumSepSmplrs(), m_SeparateSamplerOffset ); } + const SPIRVShaderResourceAttribs& GetSepImg (Uint32 n)const noexcept{ return GetResAttribs(n, GetNumSepImgs(), m_SeparateImageOffset ); } + const SPIRVShaderResourceAttribs& GetInptAtt (Uint32 n)const noexcept{ return GetResAttribs(n, GetNumInptAtts(), m_InputAttachmentOffset ); } + const SPIRVShaderResourceAttribs& GetAccelStruct (Uint32 n)const noexcept{ return GetResAttribs(n, GetNumAccelStructs(), m_AccelStructOffset ); } + const SPIRVShaderResourceAttribs& GetPushConstant(Uint32 n)const noexcept{ return GetResAttribs(n, GetNumPushConstants(), m_PushConstantOffset ); } + const SPIRVShaderResourceAttribs& GetResource (Uint32 n)const noexcept{ return GetResAttribs(n, GetTotalResources(), 0 ); } // clang-format on @@ -217,15 +225,16 @@ class SPIRVShaderResources struct ResourceCounters { - Uint32 NumUBs = 0; - Uint32 NumSBs = 0; - Uint32 NumImgs = 0; - Uint32 NumSmpldImgs = 0; - Uint32 NumACs = 0; - Uint32 NumSepSmplrs = 0; - Uint32 NumSepImgs = 0; - Uint32 NumInptAtts = 0; - Uint32 NumAccelStructs = 0; + Uint32 NumUBs = 0; + Uint32 NumSBs = 0; + Uint32 NumImgs = 0; + Uint32 NumSmpldImgs = 0; + Uint32 NumACs = 0; + Uint32 NumSepSmplrs = 0; + Uint32 NumSepImgs = 0; + Uint32 NumInptAtts = 0; + Uint32 NumAccelStructs = 0; + Uint32 NumPushConstants = 0; // Vulkan push constant buffers (max 1 per Vulkan spec) }; SHADER_TYPE GetShaderType() const noexcept { return m_ShaderType; } @@ -243,16 +252,18 @@ class SPIRVShaderResources typename THandleSepSmpl, typename THandleSepImg, typename THandleInptAtt, - typename THandleAccelStruct> - void ProcessResources(THandleUB HandleUB, - THandleSB HandleSB, - THandleImg HandleImg, - THandleSmplImg HandleSmplImg, - THandleAC HandleAC, - THandleSepSmpl HandleSepSmpl, - THandleSepImg HandleSepImg, - THandleInptAtt HandleInptAtt, - THandleAccelStruct HandleAccelStruct) const + typename THandleAccelStruct, + typename THandlePushConstant> + void ProcessResources(THandleUB HandleUB, + THandleSB HandleSB, + THandleImg HandleImg, + THandleSmplImg HandleSmplImg, + THandleAC HandleAC, + THandleSepSmpl HandleSepSmpl, + THandleSepImg HandleSepImg, + THandleInptAtt HandleInptAtt, + THandleAccelStruct HandleAccelStruct, + THandlePushConstant HandlePushConstant) const { for (Uint32 n = 0; n < GetNumUBs(); ++n) { @@ -308,7 +319,13 @@ class SPIRVShaderResources HandleAccelStruct(AccelStruct, n); } - static_assert(Uint32{SPIRVShaderResourceAttribs::ResourceType::NumResourceTypes} == 12, "Please handle the new resource type here, if needed"); + for (Uint32 n = 0; n < GetNumPushConstants(); ++n) + { + const SPIRVShaderResourceAttribs& PushConstant = GetPushConstant(n); + HandlePushConstant(PushConstant, n); + } + + static_assert(Uint32{SPIRVShaderResourceAttribs::ResourceType::NumResourceTypes} == 13, "Please handle the new resource type here, if needed"); } template @@ -359,16 +376,17 @@ class SPIRVShaderResources // clang-format off - SPIRVShaderResourceAttribs& GetUB (Uint32 n)noexcept{ return GetResAttribs(n, GetNumUBs(), 0 ); } - SPIRVShaderResourceAttribs& GetSB (Uint32 n)noexcept{ return GetResAttribs(n, GetNumSBs(), m_StorageBufferOffset ); } - SPIRVShaderResourceAttribs& GetImg (Uint32 n)noexcept{ return GetResAttribs(n, GetNumImgs(), m_StorageImageOffset ); } - SPIRVShaderResourceAttribs& GetSmpldImg (Uint32 n)noexcept{ return GetResAttribs(n, GetNumSmpldImgs(), m_SampledImageOffset ); } - SPIRVShaderResourceAttribs& GetAC (Uint32 n)noexcept{ return GetResAttribs(n, GetNumACs(), m_AtomicCounterOffset ); } - SPIRVShaderResourceAttribs& GetSepSmplr (Uint32 n)noexcept{ return GetResAttribs(n, GetNumSepSmplrs(), m_SeparateSamplerOffset); } - SPIRVShaderResourceAttribs& GetSepImg (Uint32 n)noexcept{ return GetResAttribs(n, GetNumSepImgs(), m_SeparateImageOffset ); } - SPIRVShaderResourceAttribs& GetInptAtt (Uint32 n)noexcept{ return GetResAttribs(n, GetNumInptAtts(), m_InputAttachmentOffset); } - SPIRVShaderResourceAttribs& GetAccelStruct(Uint32 n)noexcept{ return GetResAttribs(n, GetNumAccelStructs(), m_AccelStructOffset ); } - SPIRVShaderResourceAttribs& GetResource (Uint32 n)noexcept{ return GetResAttribs(n, GetTotalResources(), 0 ); } + SPIRVShaderResourceAttribs& GetUB (Uint32 n)noexcept{ return GetResAttribs(n, GetNumUBs(), 0 ); } + SPIRVShaderResourceAttribs& GetSB (Uint32 n)noexcept{ return GetResAttribs(n, GetNumSBs(), m_StorageBufferOffset ); } + SPIRVShaderResourceAttribs& GetImg (Uint32 n)noexcept{ return GetResAttribs(n, GetNumImgs(), m_StorageImageOffset ); } + SPIRVShaderResourceAttribs& GetSmpldImg (Uint32 n)noexcept{ return GetResAttribs(n, GetNumSmpldImgs(), m_SampledImageOffset ); } + SPIRVShaderResourceAttribs& GetAC (Uint32 n)noexcept{ return GetResAttribs(n, GetNumACs(), m_AtomicCounterOffset ); } + SPIRVShaderResourceAttribs& GetSepSmplr (Uint32 n)noexcept{ return GetResAttribs(n, GetNumSepSmplrs(), m_SeparateSamplerOffset ); } + SPIRVShaderResourceAttribs& GetSepImg (Uint32 n)noexcept{ return GetResAttribs(n, GetNumSepImgs(), m_SeparateImageOffset ); } + SPIRVShaderResourceAttribs& GetInptAtt (Uint32 n)noexcept{ return GetResAttribs(n, GetNumInptAtts(), m_InputAttachmentOffset ); } + SPIRVShaderResourceAttribs& GetAccelStruct (Uint32 n)noexcept{ return GetResAttribs(n, GetNumAccelStructs(), m_AccelStructOffset ); } + SPIRVShaderResourceAttribs& GetPushConstant(Uint32 n)noexcept{ return GetResAttribs(n, GetNumPushConstants(), m_PushConstantOffset ); } + SPIRVShaderResourceAttribs& GetResource (Uint32 n)noexcept{ return GetResAttribs(n, GetTotalResources(), 0 ); } // clang-format on @@ -394,6 +412,7 @@ class SPIRVShaderResources OffsetType m_SeparateImageOffset = 0; OffsetType m_InputAttachmentOffset = 0; OffsetType m_AccelStructOffset = 0; + OffsetType m_PushConstantOffset = 0; OffsetType m_TotalResources = 0; OffsetType m_NumShaderStageInputs = 0; diff --git a/Graphics/ShaderTools/src/SPIRVShaderResources.cpp b/Graphics/ShaderTools/src/SPIRVShaderResources.cpp index dc65e596bb..f059b589fd 100644 --- a/Graphics/ShaderTools/src/SPIRVShaderResources.cpp +++ b/Graphics/ShaderTools/src/SPIRVShaderResources.cpp @@ -126,10 +126,29 @@ SPIRVShaderResourceAttribs::SPIRVShaderResourceAttribs(const diligent_spirv_cros // clang-format on {} +// Constructor for push constants (no binding or descriptor set decoration) +SPIRVShaderResourceAttribs::SPIRVShaderResourceAttribs(const char* _Name, + ResourceType _Type, + Uint32 _BufferStaticSize) noexcept : + // clang-format off + Name {_Name}, + // For push constants, ArraySize represents the number of 32-bit constants (BufferStaticSize / 4) + // This is consistent with how inline constants work in the pipeline resource signature + ArraySize {static_cast(_BufferStaticSize / sizeof(Uint32))}, + Type {_Type}, + ResourceDim {RESOURCE_DIM_BUFFER}, + IsMS {0}, + BindingDecorationOffset {0}, // Push constants have no binding decoration + DescriptorSetDecorationOffset {0}, // Push constants have no descriptor set decoration + BufferStaticSize {_BufferStaticSize}, + BufferStride {0} +// clang-format on +{} + SHADER_RESOURCE_TYPE SPIRVShaderResourceAttribs::GetShaderResourceType(ResourceType Type) { - static_assert(Uint32{SPIRVShaderResourceAttribs::ResourceType::NumResourceTypes} == 12, "Please handle the new resource type below"); + static_assert(Uint32{SPIRVShaderResourceAttribs::ResourceType::NumResourceTypes} == 13, "Please handle the new resource type below"); switch (Type) { case SPIRVShaderResourceAttribs::ResourceType::UniformBuffer: @@ -171,6 +190,10 @@ SHADER_RESOURCE_TYPE SPIRVShaderResourceAttribs::GetShaderResourceType(ResourceT case SPIRVShaderResourceAttribs::ResourceType::AccelerationStructure: return SHADER_RESOURCE_TYPE_ACCEL_STRUCT; + case SPIRVShaderResourceAttribs::ResourceType::PushConstant: + // Push constants map to constant buffer type with special inline constants flag + return SHADER_RESOURCE_TYPE_CONSTANT_BUFFER; + default: UNEXPECTED("Unknown SPIRV resource type"); return SHADER_RESOURCE_TYPE_UNKNOWN; @@ -179,7 +202,7 @@ SHADER_RESOURCE_TYPE SPIRVShaderResourceAttribs::GetShaderResourceType(ResourceT PIPELINE_RESOURCE_FLAGS SPIRVShaderResourceAttribs::GetPipelineResourceFlags(ResourceType Type) { - static_assert(Uint32{SPIRVShaderResourceAttribs::ResourceType::NumResourceTypes} == 12, "Please handle the new resource type below"); + static_assert(Uint32{SPIRVShaderResourceAttribs::ResourceType::NumResourceTypes} == 13, "Please handle the new resource type below"); switch (Type) { case SPIRVShaderResourceAttribs::ResourceType::UniformTexelBuffer: @@ -189,6 +212,11 @@ PIPELINE_RESOURCE_FLAGS SPIRVShaderResourceAttribs::GetPipelineResourceFlags(Res case SPIRVShaderResourceAttribs::ResourceType::SampledImage: return PIPELINE_RESOURCE_FLAG_COMBINED_SAMPLER; + case SPIRVShaderResourceAttribs::ResourceType::PushConstant: + // Push constants are special inline constants that use vkCmdPushConstants + // Mark them with both INLINE_CONSTANTS and VULKAN_PUSH_CONSTANT flags + return PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS | PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT; + default: return PIPELINE_RESOURCE_FLAG_NONE; } @@ -262,6 +290,33 @@ const std::string& GetUBOrSBName(diligent_spirv_cross::Compiler& C return ((IRSource.hlsl || IRSource.lang == spv::SourceLanguageSlang) && !instance_name.empty()) ? instance_name : UB.name; } +// Get name for push constant buffer +// Push constants may have their variable name empty (e.g., when declared as [[vk::push_constant]] cbuffer in HLSL) +// In that case, we try to get the name from the base type (the struct type) +const std::string& GetPushConstantName(diligent_spirv_cross::Compiler& Compiler, + const diligent_spirv_cross::Resource& PC, + const diligent_spirv_cross::ParsedIR::Source& IRSource) +{ + // First, try the standard method used for UBs/SBs + const std::string& Name = GetUBOrSBName(Compiler, PC, IRSource); + if (!Name.empty()) + return Name; + + // If the name is empty, try to get the name from the base type + // This handles cases like: + // [[vk::push_constant]] + // cbuffer Constants { ... }; + // where the variable has no name but the struct type does + const std::string& base_type_name = Compiler.get_name(PC.base_type_id); + if (!base_type_name.empty()) + return base_type_name; + + // As a last resort, try to get the fallback name + // Note: get_fallback_name returns by value, so we can't return a reference to it + // Return the empty name and let the caller handle it + return Name; +} + static SHADER_CODE_BASIC_TYPE SpirvBaseTypeToShaderCodeBasicType(diligent_spirv_cross::SPIRType::BaseType SpvBaseType) { switch (SpvBaseType) @@ -423,7 +478,18 @@ SPIRVShaderResources::SPIRVShaderResources(IMemoryAllocator& Allocator, ResourceNamesPoolSize += GetUBOrSBName(Compiler, ub, ParsedIRSource).length() + 1; for (const diligent_spirv_cross::Resource& sb : resources.storage_buffers) ResourceNamesPoolSize += GetUBOrSBName(Compiler, sb, ParsedIRSource).length() + 1; - static_assert(Uint32{SPIRVShaderResourceAttribs::ResourceType::NumResourceTypes} == 12, "Please account for the new resource type below"); + static_assert(Uint32{SPIRVShaderResourceAttribs::ResourceType::NumResourceTypes} == 13, "Please account for the new resource type below"); + + // Process push constant buffers - Vulkan spec allows only one push_constant buffer per pipeline + if (resources.push_constant_buffers.size() > 1) + { + LOG_ERROR_AND_THROW("Shader '", shaderDesc.Name, "' contains ", resources.push_constant_buffers.size(), + " push constant buffers, but Vulkan spec allows only one push_constant buffer per pipeline."); + } + // For push constants, use GetPushConstantName which also tries to get the name from the base type + for (const diligent_spirv_cross::Resource& pc : resources.push_constant_buffers) + ResourceNamesPoolSize += GetPushConstantName(Compiler, pc, ParsedIRSource).length() + 1; + for (auto* pResType : { &resources.storage_images, @@ -493,16 +559,17 @@ SPIRVShaderResources::SPIRVShaderResources(IMemoryAllocator& Allocator, } ResourceCounters ResCounters; - ResCounters.NumUBs = static_cast(resources.uniform_buffers.size()); - ResCounters.NumSBs = static_cast(resources.storage_buffers.size()); - ResCounters.NumImgs = static_cast(resources.storage_images.size()); - ResCounters.NumSmpldImgs = static_cast(resources.sampled_images.size()); - ResCounters.NumACs = static_cast(resources.atomic_counters.size()); - ResCounters.NumSepSmplrs = static_cast(resources.separate_samplers.size()); - ResCounters.NumSepImgs = static_cast(resources.separate_images.size()); - ResCounters.NumInptAtts = static_cast(resources.subpass_inputs.size()); - ResCounters.NumAccelStructs = static_cast(resources.acceleration_structures.size()); - static_assert(Uint32{SPIRVShaderResourceAttribs::ResourceType::NumResourceTypes} == 12, "Please set the new resource type counter here"); + ResCounters.NumUBs = static_cast(resources.uniform_buffers.size()); + ResCounters.NumSBs = static_cast(resources.storage_buffers.size()); + ResCounters.NumImgs = static_cast(resources.storage_images.size()); + ResCounters.NumSmpldImgs = static_cast(resources.sampled_images.size()); + ResCounters.NumACs = static_cast(resources.atomic_counters.size()); + ResCounters.NumSepSmplrs = static_cast(resources.separate_samplers.size()); + ResCounters.NumSepImgs = static_cast(resources.separate_images.size()); + ResCounters.NumInptAtts = static_cast(resources.subpass_inputs.size()); + ResCounters.NumAccelStructs = static_cast(resources.acceleration_structures.size()); + ResCounters.NumPushConstants = static_cast(resources.push_constant_buffers.size()); + static_assert(Uint32{SPIRVShaderResourceAttribs::ResourceType::NumResourceTypes} == 13, "Please set the new resource type counter here"); // Resource names pool is only needed to facilitate string allocation. StringPool ResourceNamesPool; @@ -687,7 +754,26 @@ SPIRVShaderResources::SPIRVShaderResources(IMemoryAllocator& Allocator, VERIFY_EXPR(CurrAccelStruct == GetNumAccelStructs()); } - static_assert(Uint32{SPIRVShaderResourceAttribs::ResourceType::NumResourceTypes} == 12, "Please initialize SPIRVShaderResourceAttribs for the new resource type here"); + { + Uint32 CurrPushConstant = 0; + for (const diligent_spirv_cross::Resource& PushConst : resources.push_constant_buffers) + { + const std::string& pcName = GetPushConstantName(Compiler, PushConst, ParsedIRSource); + const diligent_spirv_cross::SPIRType& Type = Compiler.get_type(PushConst.type_id); + const size_t Size = Compiler.get_declared_struct_size(Type); + + // Push constants use a special constructor without binding/descriptor set decorations + new (&GetPushConstant(CurrPushConstant++)) SPIRVShaderResourceAttribs // + { + ResourceNamesPool.CopyString(pcName), + SPIRVShaderResourceAttribs::ResourceType::PushConstant, + static_cast(Size) // + }; + } + VERIFY_EXPR(CurrPushConstant == GetNumPushConstants()); + } + + static_assert(Uint32{SPIRVShaderResourceAttribs::ResourceType::NumResourceTypes} == 13, "Please initialize SPIRVShaderResourceAttribs for the new resource type here"); if (CombinedSamplerSuffix != nullptr) { @@ -757,8 +843,9 @@ void SPIRVShaderResources::Initialize(IMemoryAllocator& Allocator, m_SeparateImageOffset = AdvanceOffset(Counters.NumSepImgs); m_InputAttachmentOffset = AdvanceOffset(Counters.NumInptAtts); m_AccelStructOffset = AdvanceOffset(Counters.NumAccelStructs); + m_PushConstantOffset = AdvanceOffset(Counters.NumPushConstants); m_TotalResources = AdvanceOffset(0); - static_assert(Uint32{SPIRVShaderResourceAttribs::ResourceType::NumResourceTypes} == 12, "Please update the new resource type offset"); + static_assert(Uint32{SPIRVShaderResourceAttribs::ResourceType::NumResourceTypes} == 13, "Please update the new resource type offset"); VERIFY(NumShaderStageInputs <= MaxOffset, "Max offset exceeded"); m_NumShaderStageInputs = static_cast(NumShaderStageInputs); @@ -771,16 +858,17 @@ void SPIRVShaderResources::Initialize(IMemoryAllocator& Allocator, m_NumShaderStageInputs * sizeof(SPIRVShaderStageInputAttribs) + AlignedResourceNamesPoolSize * sizeof(char); - VERIFY_EXPR(GetNumUBs() == Counters.NumUBs); - VERIFY_EXPR(GetNumSBs() == Counters.NumSBs); - VERIFY_EXPR(GetNumImgs() == Counters.NumImgs); - VERIFY_EXPR(GetNumSmpldImgs() == Counters.NumSmpldImgs); - VERIFY_EXPR(GetNumACs() == Counters.NumACs); - VERIFY_EXPR(GetNumSepSmplrs() == Counters.NumSepSmplrs); - VERIFY_EXPR(GetNumSepImgs() == Counters.NumSepImgs); - VERIFY_EXPR(GetNumInptAtts() == Counters.NumInptAtts); - VERIFY_EXPR(GetNumAccelStructs() == Counters.NumAccelStructs); - static_assert(Uint32{SPIRVShaderResourceAttribs::ResourceType::NumResourceTypes} == 12, "Please update the new resource count verification"); + VERIFY_EXPR(GetNumUBs() == Counters.NumUBs); + VERIFY_EXPR(GetNumSBs() == Counters.NumSBs); + VERIFY_EXPR(GetNumImgs() == Counters.NumImgs); + VERIFY_EXPR(GetNumSmpldImgs() == Counters.NumSmpldImgs); + VERIFY_EXPR(GetNumACs() == Counters.NumACs); + VERIFY_EXPR(GetNumSepSmplrs() == Counters.NumSepSmplrs); + VERIFY_EXPR(GetNumSepImgs() == Counters.NumSepImgs); + VERIFY_EXPR(GetNumInptAtts() == Counters.NumInptAtts); + VERIFY_EXPR(GetNumAccelStructs() == Counters.NumAccelStructs); + VERIFY_EXPR(GetNumPushConstants() == Counters.NumPushConstants); + static_assert(Uint32{SPIRVShaderResourceAttribs::ResourceType::NumResourceTypes} == 13, "Please update the new resource count verification"); // clang-format on if (MemorySize) @@ -826,7 +914,10 @@ SPIRVShaderResources::~SPIRVShaderResources() for (Uint32 n = 0; n < GetNumAccelStructs(); ++n) GetAccelStruct(n).~SPIRVShaderResourceAttribs(); - static_assert(Uint32{SPIRVShaderResourceAttribs::ResourceType::NumResourceTypes} == 12, "Please add destructor for the new resource"); + for (Uint32 n = 0; n < GetNumPushConstants(); ++n) + GetPushConstant(n).~SPIRVShaderResourceAttribs(); + + static_assert(Uint32{SPIRVShaderResourceAttribs::ResourceType::NumResourceTypes} == 13, "Please add destructor for the new resource"); } void SPIRVShaderResources::MapHLSLVertexShaderInputs(std::vector& SPIRV) const @@ -966,6 +1057,13 @@ std::string SPIRVShaderResources::DumpResources() const ss << std::endl << std::setw(3) << ResNum << " Accel Struct "; DumpResource(AccelStruct); + }, + [&](const SPIRVShaderResourceAttribs& PushConst, Uint32) // + { + VERIFY(PushConst.Type == SPIRVShaderResourceAttribs::ResourceType::PushConstant, "Unexpected resource type"); + ss << std::endl + << std::setw(3) << ResNum << " Push Constant "; + DumpResource(PushConst); } // ); VERIFY_EXPR(ResNum == GetTotalResources()); From f63da22f4bfac2d0f38fef5ce905d87c5cf22d32 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sun, 7 Dec 2025 16:59:58 +0800 Subject: [PATCH 040/104] Add PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT to PRS so we don't to have to deal with SPIRVShaderResourceAttribs stuffs in PRS creation. --- .../interface/PipelineResourceSignature.h | 11 +- .../GraphicsEngine/src/PipelineStateBase.cpp | 9 + .../include/PipelineResourceAttribsVk.hpp | 22 +- .../PipelineResourceSignatureVkImpl.hpp | 7 +- .../include/ShaderResourceCacheVk.hpp | 38 +- .../src/DeviceContextVkImpl.cpp | 70 +++- .../src/PipelineResourceSignatureVkImpl.cpp | 336 +++++++++++------- .../src/PipelineStateVkImpl.cpp | 25 +- .../src/ShaderVariableManagerVk.cpp | 67 +++- 9 files changed, 402 insertions(+), 183 deletions(-) diff --git a/Graphics/GraphicsEngine/interface/PipelineResourceSignature.h b/Graphics/GraphicsEngine/interface/PipelineResourceSignature.h index 0f40867a9e..458bd6272a 100644 --- a/Graphics/GraphicsEngine/interface/PipelineResourceSignature.h +++ b/Graphics/GraphicsEngine/interface/PipelineResourceSignature.h @@ -148,13 +148,12 @@ DILIGENT_TYPED_ENUM(PIPELINE_RESOURCE_FLAGS, Uint8) /// \note This flag is only valid in Vulkan. PIPELINE_RESOURCE_FLAG_GENERAL_INPUT_ATTACHMENT = 1u << 5, - /// Internal flag indicating that this inline constant is a Vulkan push constant + /// Indicates that this inline constant is a Vulkan push constant /// (declared with push_constant storage class in SPIR-V). - /// This flag is automatically set by the engine during shader reflection and should not - /// be set manually by the application. - /// - /// When this flag is set along with PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS, the engine - /// will use vkCmdPushConstants instead of a dynamic uniform buffer to update the constants. + /// This flag is must be explicitly set when creating PipelineResourceSignature + /// And must be used together with PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS + /// When this flag is set, the engine will use vkCmdPushConstants + /// instead of a dynamic uniform buffer to update the constants. /// /// \note This flag is only valid in Vulkan and is always combined with /// PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS. diff --git a/Graphics/GraphicsEngine/src/PipelineStateBase.cpp b/Graphics/GraphicsEngine/src/PipelineStateBase.cpp index 376d4a54ca..087d53c235 100644 --- a/Graphics/GraphicsEngine/src/PipelineStateBase.cpp +++ b/Graphics/GraphicsEngine/src/PipelineStateBase.cpp @@ -849,6 +849,15 @@ void ValidatePipelineResourceCompatibility(const PipelineResourceDesc& ResDesc, GetShaderResourceTypeLiteralName(ResDesc.ResourceType), "' specified in pipeline resource signature '", SignatureName, "'."); } + if ((ResourceFlags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT)) + { + LOG_ERROR_AND_THROW("Shader '", ShaderName, "' contains resource '", ResDesc.Name, + "' that is", ((ResourceFlags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) ? "" : " not"), + " labeled as vulkan's push constant, while the same resource specified by the pipeline resource signature '", + SignatureName, "' is", ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) ? "" : " not"), + " labeled as such."); + } + if ((ResourceFlags & PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER) != (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER)) { LOG_ERROR_AND_THROW("Shader '", ShaderName, "' contains resource '", ResDesc.Name, diff --git a/Graphics/GraphicsEngineVulkan/include/PipelineResourceAttribsVk.hpp b/Graphics/GraphicsEngineVulkan/include/PipelineResourceAttribsVk.hpp index 5a76ab680a..9b030ac3c9 100644 --- a/Graphics/GraphicsEngineVulkan/include/PipelineResourceAttribsVk.hpp +++ b/Graphics/GraphicsEngineVulkan/include/PipelineResourceAttribsVk.hpp @@ -68,12 +68,13 @@ struct PipelineResourceAttribsVk private: static constexpr Uint32 _BindingIndexBits = 16; static constexpr Uint32 _SamplerIndBits = 16; - static constexpr Uint32 _ArraySizeBits = 25; + static constexpr Uint32 _ArraySizeBits = 24; static constexpr Uint32 _DescrTypeBits = 5; static constexpr Uint32 _DescrSetBits = 1; static constexpr Uint32 _SamplerAssignedBits = 1; + static constexpr Uint32 _IsPushConstantBits = 1; - static_assert((_BindingIndexBits + _ArraySizeBits + _SamplerIndBits + _DescrTypeBits + _DescrSetBits + _SamplerAssignedBits) % 32 == 0, "Bits are not optimally packed"); + static_assert((_BindingIndexBits + _ArraySizeBits + _SamplerIndBits + _DescrTypeBits + _DescrSetBits + _SamplerAssignedBits + _IsPushConstantBits) % 32 == 0, "Bits are not optimally packed"); // clang-format off static_assert((1u << _DescrTypeBits) > static_cast(DescriptorType::Count), "Not enough bits to store DescriptorType values"); @@ -88,10 +89,11 @@ struct PipelineResourceAttribsVk // clang-format off const Uint32 BindingIndex : _BindingIndexBits; // Binding in the descriptor set const Uint32 SamplerInd : _SamplerIndBits; // Index of the assigned sampler in m_Desc.Resources and m_pPipelineResourceAttribsVk - const Uint32 ArraySize : _ArraySizeBits; // Array size + const Uint32 ArraySize : _ArraySizeBits; // Array size (for push constants, this is the number of 32-bit constants) const Uint32 DescrType : _DescrTypeBits; // Descriptor type (DescriptorType) const Uint32 DescrSet : _DescrSetBits; // Descriptor set (0 or 1) const Uint32 ImtblSamplerAssigned : _SamplerAssignedBits; // Immutable sampler flag + const Uint32 IsPushConstant : _IsPushConstantBits; // True if this is a Vulkan push constant (not using descriptor sets) const Uint32 SRBCacheOffset; // Offset in the SRB resource cache const Uint32 StaticCacheOffset; // Offset in the static resource cache @@ -103,6 +105,7 @@ struct PipelineResourceAttribsVk DescriptorType _DescrType, Uint32 _DescrSet, bool _ImtblSamplerAssigned, + bool _IsPushConstant, Uint32 _SRBCacheOffset, Uint32 _StaticCacheOffset) noexcept : // clang-format off @@ -112,6 +115,7 @@ struct PipelineResourceAttribsVk DescrType {static_cast(_DescrType)}, DescrSet {_DescrSet }, ImtblSamplerAssigned {_ImtblSamplerAssigned ? 1u : 0u}, + IsPushConstant {_IsPushConstant ? 1u : 0u }, SRBCacheOffset {_SRBCacheOffset }, StaticCacheOffset {_StaticCacheOffset } // clang-format on @@ -127,7 +131,7 @@ struct PipelineResourceAttribsVk // Only for serialization PipelineResourceAttribsVk() noexcept : - PipelineResourceAttribsVk{0, 0, 0, DescriptorType::Unknown, 0, false, 0, 0} + PipelineResourceAttribsVk{0, 0, 0, DescriptorType::Unknown, 0, false, false, 0, 0} {} @@ -151,6 +155,11 @@ struct PipelineResourceAttribsVk return SamplerInd != InvalidSamplerInd; } + bool IsPushConstantBuffer() const + { + return IsPushConstant != 0; + } + bool IsCompatibleWith(const PipelineResourceAttribsVk& rhs) const { // Ignore sampler index and cache offsets. @@ -159,13 +168,14 @@ struct PipelineResourceAttribsVk ArraySize == rhs.ArraySize && DescrType == rhs.DescrType && DescrSet == rhs.DescrSet && - ImtblSamplerAssigned == rhs.ImtblSamplerAssigned; + ImtblSamplerAssigned == rhs.ImtblSamplerAssigned && + IsPushConstant == rhs.IsPushConstant; // clang-format on } size_t GetHash() const { - return ComputeHash(BindingIndex, ArraySize, DescrType, DescrSet, ImtblSamplerAssigned); + return ComputeHash(BindingIndex, ArraySize, DescrType, DescrSet, ImtblSamplerAssigned, IsPushConstant); } }; ASSERT_SIZEOF(PipelineResourceAttribsVk, 16, "The struct is used in serialization and must be tightly packed"); diff --git a/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp index b617371ebc..f18b02cce9 100644 --- a/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp @@ -65,11 +65,13 @@ ASSERT_SIZEOF(ImmutableSamplerAttribsVk, 8, "The struct is used in serialization /// 2. Emulated inline constants - use dynamic uniform buffers (similar to D3D11 backend) struct InlineConstantBufferAttribsVk { - Uint32 DescrSet = 0; // Descriptor set index + Uint32 ResIndex = 0; // Resource index in the signature (used for matching) + Uint32 DescrSet = 0; // Descriptor set index (0 for push constants as placeholder) Uint32 BindingIndex = 0; // Binding index within the descriptor set Uint32 NumConstants = 0; // Number of 32-bit constants - bool IsPushConstant = false; // True if this is a Vulkan push constant (not emulated) + bool IsPushConstant = false; // True if this is a Vulkan push constant (not emulated) RefCntAutoPtr pBuffer; // Internal dynamic uniform buffer (null for true push constants) + void* pPushConstantData = nullptr; // CPU-side staging buffer for push constants (only used when IsPushConstant is true) }; struct PipelineResourceSignatureInternalDataVk : PipelineResourceSignatureInternalData @@ -149,6 +151,7 @@ class PipelineResourceSignatureVkImpl final : public PipelineResourceSignatureBa VkDescriptorSet vkDynamicDescriptorSet) const; // Updates inline constant buffers by mapping the internal buffers and copying data from the resource cache + // ResourceCache must be valid - each SRB has its own copy of push constant data stored in the cache void UpdateInlineConstantBuffers(const ShaderResourceCacheVk& ResourceCache, DeviceContextVkImpl& Ctx) const; diff --git a/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp b/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp index ae548888fb..03a311c9a6 100644 --- a/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp +++ b/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp @@ -274,6 +274,37 @@ class ShaderResourceCacheVk : public ShaderResourceCacheBase STDDeleter(Allocator)}; } + // Sets the push constant data pointer for the given index + // This is used to store per-SRB push constant data for STATIC push constants + void SetPushConstantDataPtr(Uint32 Index, void* pData) + { + VERIFY_EXPR(Index < m_NumPushConstantBuffers); + m_pPushConstantDataPtrs[Index] = pData; + } + + // Gets the push constant data pointer for the given index + void* GetPushConstantDataPtr(Uint32 Index) const + { + VERIFY_EXPR(Index < m_NumPushConstantBuffers); + return m_pPushConstantDataPtrs[Index]; + } + + // Initializes push constant data pointers array + void InitializePushConstantDataPtrs(Uint32 NumPushConstants) + { + m_NumPushConstantBuffers = static_cast(NumPushConstants); + if (NumPushConstants > 0) + { + m_pPushConstantDataPtrs = std::make_unique(NumPushConstants); + for (Uint32 i = 0; i < NumPushConstants; ++i) + m_pPushConstantDataPtrs[i] = nullptr; + // Mark that this cache has inline constants (push constants are a type of inline constants) + m_HasInlineConstants = 1; + } + } + + Uint32 GetNumPushConstantBuffers() const { return m_NumPushConstantBuffers; } + Uint32 GetNumDescriptorSets() const { return m_NumSets; } bool HasDynamicResources() const { return m_NumDynamicBuffers > 0; } @@ -318,7 +349,12 @@ class ShaderResourceCacheVk : public ShaderResourceCacheBase // Memory for inline constant data (allocated separately, freed in destructor) std::unique_ptr> m_pInlineConstantMemory; - Uint16 m_NumSets = 0; + // Array of push constant data pointers (one per push constant buffer) + // Each pointer points to memory within m_pInlineConstantMemory + std::unique_ptr m_pPushConstantDataPtrs; + + Uint16 m_NumSets = 0; + Uint16 m_NumPushConstantBuffers = 0; // Total actual number of dynamic buffers (that were created with USAGE_DYNAMIC) bound in the resource cache // regardless of the variable type. Note this variable is not equal to dynamic offsets count, which is constant. diff --git a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp index 9c29ad2ccd..629df69651 100644 --- a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp @@ -435,7 +435,15 @@ void DeviceContextVkImpl::UpdateInlineConstantBuffers(ResourceBindInfo& BindInfo continue; const ShaderResourceCacheVk* pResourceCache = BindInfo.ResourceCaches[i]; - if (pResourceCache == nullptr || !pResourceCache->HasInlineConstants()) + if (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 (!pResourceCache->HasInlineConstants()) continue; // Update inline constant buffers @@ -461,13 +469,14 @@ void DeviceContextVkImpl::SetPushConstants(const void* pData, Uint32 Offset, Uin "Push constant data range [", Offset, ", ", Offset + Size, ") exceeds the push constant block size (", Layout.GetPushConstantSize(), ")"); } } - - if (Size > 0) + else { - memcpy(m_PushConstantsData.data() + Offset, pData, Size); - m_PushConstantsDataSize = std::max(m_PushConstantsDataSize, Offset + Size); - m_State.PushConstantsDirty = true; + DEV_ERROR("A valid PipelineState must be bound before calling SetPushConstants!"); } + + memcpy(m_PushConstantsData.data() + Offset, pData, Size); + m_PushConstantsDataSize = std::max(m_PushConstantsDataSize, Offset + Size); + m_State.PushConstantsDirty = true; } void DeviceContextVkImpl::CommitPushConstants() @@ -495,6 +504,27 @@ void DeviceContextVkImpl::CommitDescriptorSets(ResourceBindInfo& BindInfo, Uint3 { VERIFY(CommitSRBMask != 0, "This method should not be called when there is nothing to commit"); + // Filter out SRBs that only have push constants (no descriptor sets) + // These SRBs are included in CommitSRBMask due to InlineConstantsSRBMask, but they don't need descriptor set binding + Uint32 FilteredCommitMask = CommitSRBMask; + Uint32 ResourceSignaturesCount = m_pPipelineState->GetResourceSignatureCount(); + for (Uint32 sign = 0; sign < ResourceSignaturesCount; ++sign) + { + if ((CommitSRBMask & (1u << sign)) != 0) + { + const ShaderResourceCacheVk* pResourceCache = BindInfo.ResourceCaches[sign]; + if (pResourceCache != nullptr && pResourceCache->GetNumDescriptorSets() == 0) + { + // This SRB only has push constants, no descriptor sets to commit + FilteredCommitMask &= ~(1u << sign); + } + } + } + + // If all SRBs were filtered out (only push constants), nothing to commit + if (FilteredCommitMask == 0) + return; + const Uint32 FirstSign = PlatformMisc::GetLSB(CommitSRBMask); const Uint32 LastSign = PlatformMisc::GetMSB(CommitSRBMask); VERIFY_EXPR(LastSign < m_pPipelineState->GetResourceSignatureCount()); @@ -563,6 +593,8 @@ void DeviceContextVkImpl::DvpValidateCommittedShaderResources(ResourceBindInfo& if (BindInfo.ResourcesValidated) return; + // Use custom signature getter to skip signatures that have no resources at all + // Note: Signatures with only push constants still need SRB to store push constant data DvpVerifySRBCompatibility(BindInfo); const Uint32 SignCount = m_pPipelineState->GetResourceSignatureCount(); @@ -612,9 +644,24 @@ void DeviceContextVkImpl::CommitShaderResources(IShaderResourceBinding* pShaderR ShaderResourceBindingVkImpl* pResBindingVkImpl = ClassPtrCast(pShaderResourceBinding); ShaderResourceCacheVk& ResourceCache = pResBindingVkImpl->GetResourceCache(); + + const Uint32 SRBIndex = pResBindingVkImpl->GetBindingIndex(); + const PipelineResourceSignatureVkImpl* pSignature = pResBindingVkImpl->GetSignature(); + ResourceBindInfo& BindInfo = GetBindInfo(pResBindingVkImpl->GetPipelineType()); + ResourceBindInfo::DescriptorSetInfo& SetInfo = BindInfo.SetInfo[SRBIndex]; + + // Always bind the SRB, even if it has no descriptor sets (e.g., only push constants) + // The resource cache is needed to store push constant data + BindInfo.Set(SRBIndex, pResBindingVkImpl); + + // If there are no descriptor sets, we're done (SRB only contains push constants) + // Clear the stale flag since there are no descriptor sets to commit if (ResourceCache.GetNumDescriptorSets() == 0) { - // Ignore SRBs that contain no resources + // Clear the stale bit for this SRB since it has no descriptor sets to commit + // The SRB is still bound and ResourceCaches[SRBIndex] is set, so push constants can be accessed + const auto SRBBit = static_cast(1u << SRBIndex); + BindInfo.StaleSRBMask &= ~SRBBit; return; } @@ -633,12 +680,6 @@ void DeviceContextVkImpl::CommitShaderResources(IShaderResourceBinding* pShaderR } #endif - const Uint32 SRBIndex = pResBindingVkImpl->GetBindingIndex(); - const PipelineResourceSignatureVkImpl* pSignature = pResBindingVkImpl->GetSignature(); - ResourceBindInfo& BindInfo = GetBindInfo(pResBindingVkImpl->GetPipelineType()); - ResourceBindInfo::DescriptorSetInfo& SetInfo = BindInfo.SetInfo[SRBIndex]; - - BindInfo.Set(SRBIndex, pResBindingVkImpl); // We must not clear entire ResInfo as DescriptorSetBaseInd and DynamicOffsetCount // are set by SetPipelineState(). SetInfo.vkSets = {}; @@ -836,6 +877,7 @@ void DeviceContextVkImpl::PrepareForDraw(DRAW_FLAGS Flags) ResourceBindInfo& BindInfo = GetBindInfo(PIPELINE_TYPE_GRAPHICS); // Update inline constant buffers 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) { @@ -845,7 +887,7 @@ void DeviceContextVkImpl::PrepareForDraw(DRAW_FLAGS Flags) // 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); } diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp index 7cb7aac45c..8490175738 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp @@ -174,32 +174,48 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) if (GetNumStaticResStages() > 0) { Uint32 StaticResourceCount = 0; // The total number of static resources in all stages - // accounting for array sizes. + // accounting for array sizes (excluding push constants). for (Uint32 i = 0; i < m_Desc.NumResources; ++i) { const PipelineResourceDesc& ResDesc = m_Desc.Resources[i]; if (ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) { - // For inline constants, GetArraySize() returns 1 (actual array size), - // while ArraySize contains the number of 32-bit constants - StaticResourceCount += ResDesc.GetArraySize(); + // Push constants don't use descriptor sets, so they don't need cache space + const bool IsPushConstant = (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != 0; + if (!IsPushConstant) + { + // For inline constants, GetArraySize() returns 1 (actual array size), + // while ArraySize contains the number of 32-bit constants + StaticResourceCount += ResDesc.GetArraySize(); + } } } - m_pStaticResCache->InitializeSets(GetRawAllocator(), 1, &StaticResourceCount); + // Only initialize if there are non-push-constant static resources + if (StaticResourceCount > 0) + { + m_pStaticResCache->InitializeSets(GetRawAllocator(), 1, &StaticResourceCount); + } } CacheOffsetsType CacheGroupSizes = {}; // Required cache size for each cache group - BindingCountType BindingCount = {}; // Binding count in each cache group + BindingCountType BindingCount = {}; // Binding count in each cache group (excluding push constants) for (Uint32 i = 0; i < m_Desc.NumResources; ++i) { const PipelineResourceDesc& ResDesc = m_Desc.Resources[i]; const CACHE_GROUP CacheGroup = GetResourceCacheGroup(ResDesc); - BindingCount[CacheGroup] += 1; - // Note that we may reserve space for separate immutable samplers, which will never be used, but this is OK. - // For inline constants, GetArraySize() returns 1 (actual array size for cache), - // while ArraySize contains the number of 32-bit constants - CacheGroupSizes[CacheGroup] += ResDesc.GetArraySize(); + // Vulkan push constants don't use descriptor sets - they use vkCmdPushConstants directly. + // So we don't count them as bindings for descriptor set layout creation. + // They also don't need cache space since their data is stored in InlineCBAttr.pPushConstantData. + const bool IsPushConstant = (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != 0; + if (!IsPushConstant) + { + BindingCount[CacheGroup] += 1; + // Note that we may reserve space for separate immutable samplers, which will never be used, but this is OK. + // For inline constants, GetArraySize() returns 1 (actual array size for cache), + // while ArraySize contains the number of 32-bit constants. + CacheGroupSizes[CacheGroup] += ResDesc.GetArraySize(); + } // Count inline constant buffers if (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) @@ -286,9 +302,13 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) VERIFY(i == 0 || ResDesc.VarType >= m_Desc.Resources[i - 1].VarType, "Resources must be sorted by variable type"); + // Vulkan push constants don't use descriptor sets + const bool IsPushConstant = (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != 0; + // If all resources are dynamic, then the signature contains only one descriptor set layout with index 0, // so remap SetId to the actual descriptor set index. - VERIFY_EXPR(DSMapping[SetId] < MAX_DESCRIPTOR_SETS); + // Push constants don't use descriptor sets, so DSMapping[SetId] may be 0xFF for them. + VERIFY_EXPR(IsPushConstant || DSMapping[SetId] < MAX_DESCRIPTOR_SETS); // The sampler may not be yet initialized, but this is OK as all resources are initialized // in the same order as in m_Desc.Resources @@ -317,14 +337,18 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) ResourceAttribs* const pAttribs = m_pResourceAttribs + i; if (!IsSerialized) { + // For push constants, use 0 as DescrSet placeholder since they don't use descriptor sets. + // The DescrSet field only has 1 bit, so it can only be 0 or 1. + const Uint32 DescrSetValue = IsPushConstant ? 0 : DSMapping[SetId]; new (pAttribs) ResourceAttribs // { BindingIndices[CacheGroup], AssignedSamplerInd, ResDesc.ArraySize, DescrType, - DSMapping[SetId], + DescrSetValue, pVkImmutableSamplers != nullptr, + IsPushConstant, CacheGroupOffsets[CacheGroup], ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC ? StaticCacheOffset : ~0u // }; @@ -341,21 +365,20 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) DEV_CHECK_ERR(pAttribs->DescrSet == DSMapping[SetId], "Deserialized descriptor set (", pAttribs->DescrSet, ") is invalid: ", DSMapping[SetId], " is expected."); DEV_CHECK_ERR(pAttribs->IsImmutableSamplerAssigned() == (pVkImmutableSamplers != nullptr), "Immutable sampler flag is invalid"); + DEV_CHECK_ERR(pAttribs->IsPushConstantBuffer() == IsPushConstant, "Push constant flag is invalid"); DEV_CHECK_ERR(pAttribs->SRBCacheOffset == CacheGroupOffsets[CacheGroup], "SRB cache offset (", pAttribs->SRBCacheOffset, ") is invalid: ", CacheGroupOffsets[CacheGroup], " is expected."); DEV_CHECK_ERR(pAttribs->StaticCacheOffset == (ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC ? StaticCacheOffset : ~0u), "Static cache offset is invalid."); } - BindingIndices[CacheGroup] += 1; - // For inline constants, GetArraySize() returns 1 (actual array size for cache) - CacheGroupOffsets[CacheGroup] += ResDesc.GetArraySize(); - // Vulkan push constants don't use descriptor sets - they use vkCmdPushConstants - // So we skip creating descriptor set layout bindings for them - const bool IsPushConstant = (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != 0; + // So we skip creating descriptor set layout bindings for them and don't increment cache offsets if (!IsPushConstant) { + // For inline constants, GetArraySize() returns 1 (actual array size for cache) + CacheGroupOffsets[CacheGroup] += ResDesc.GetArraySize(); + VkDescriptorSetLayoutBinding vkSetLayoutBinding{}; vkSetLayoutBinding.binding = pAttribs->BindingIndex; // For inline constants, descriptor count is 1 (single uniform buffer) @@ -364,10 +387,13 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) vkSetLayoutBinding.pImmutableSamplers = pVkImmutableSamplers; vkSetLayoutBinding.descriptorType = DescriptorTypeToVkDescriptorType(pAttribs->GetDescriptorType()); vkSetLayoutBindings[SetId].push_back(vkSetLayoutBinding); + // Only increment binding index for non-push-constant resources + BindingIndices[CacheGroup] += 1; } - if (ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) + if (ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC && !IsPushConstant) { + // Push constants don't use descriptor sets, so we skip InitializeResources for them VERIFY(pAttribs->DescrSet == 0, "Static resources must always be allocated in descriptor set 0"); // For inline constants, GetArraySize() returns 1 (actual array size) m_pStaticResCache->InitializeResources(pAttribs->DescrSet, StaticCacheOffset, ResDesc.GetArraySize(), @@ -382,13 +408,11 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) "Only constant buffers can have INLINE_CONSTANTS flag"); InlineConstantBufferAttribsVk& InlineCBAttribs = m_InlineConstantBuffers[InlineConstantBufferIdx++]; + InlineCBAttribs.ResIndex = i; // Resource index for unique identification InlineCBAttribs.DescrSet = pAttribs->DescrSet; InlineCBAttribs.BindingIndex = pAttribs->BindingIndex; InlineCBAttribs.NumConstants = ResDesc.ArraySize; // For inline constants, ArraySize is the number of 32-bit constants - // Check if this is a true Vulkan push constant (uses vkCmdPushConstants) - // or an emulated inline constant (uses dynamic uniform buffer) - const bool IsPushConstant = (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != 0; InlineCBAttribs.IsPushConstant = IsPushConstant; if (!IsPushConstant && m_pDevice) @@ -427,7 +451,8 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) VERIFY_EXPR(m_DynamicUniformBufferCount == CacheGroupSizes[CACHE_GROUP_DYN_UB_STAT_VAR] + CacheGroupSizes[CACHE_GROUP_DYN_UB_DYN_VAR]); VERIFY_EXPR(m_DynamicStorageBufferCount == CacheGroupSizes[CACHE_GROUP_DYN_SB_STAT_VAR] + CacheGroupSizes[CACHE_GROUP_DYN_SB_DYN_VAR]); - VERIFY_EXPR(m_pStaticResCache == nullptr || const_cast(m_pStaticResCache)->GetDescriptorSet(0).GetSize() == StaticCacheOffset); + // When there are only push constants, m_NumSets is 0 and there are no descriptor sets to check + VERIFY_EXPR(m_pStaticResCache == nullptr || GetNumDescriptorSets() == 0 || const_cast(m_pStaticResCache)->GetDescriptorSet(0).GetSize() == StaticCacheOffset); VERIFY_EXPR(CacheGroupOffsets[CACHE_GROUP_DYN_UB_STAT_VAR] == CacheGroupSizes[CACHE_GROUP_DYN_UB_STAT_VAR]); VERIFY_EXPR(CacheGroupOffsets[CACHE_GROUP_DYN_SB_STAT_VAR] == CacheGroupSizes[CACHE_GROUP_DYN_UB_STAT_VAR] + CacheGroupSizes[CACHE_GROUP_DYN_SB_STAT_VAR]); VERIFY_EXPR(CacheGroupOffsets[CACHE_GROUP_OTHER_STAT_VAR] == CacheGroupSizes[CACHE_GROUP_DYN_UB_STAT_VAR] + CacheGroupSizes[CACHE_GROUP_DYN_SB_STAT_VAR] + CacheGroupSizes[CACHE_GROUP_OTHER_STAT_VAR]); @@ -550,29 +575,18 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) // Initialize inline constant buffers in the static resource cache // This must be done after the resources are initialized above - if (m_pStaticResCache != nullptr && m_NumInlineConstantBuffers > 0) + if (m_NumInlineConstantBuffers > 0) { - const Uint32 TotalResources = GetTotalResourceCount(); - // First pass: calculate total memory size needed for all static inline constants Uint32 TotalStaticInlineConstantSize = 0; for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) { const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; - for (Uint32 r = 0; r < TotalResources; ++r) + const PipelineResourceDesc& ResDesc = GetResourceDesc(InlineCBAttr.ResIndex); + if (ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) { - const PipelineResourceDesc& ResDesc = GetResourceDesc(r); - const ResourceAttribs& Attr = GetResourceAttribs(r); - - if ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) && - ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC && - Attr.DescrSet == InlineCBAttr.DescrSet && - Attr.BindingIndex == InlineCBAttr.BindingIndex) - { - TotalStaticInlineConstantSize += InlineCBAttr.NumConstants * sizeof(Uint32); - break; - } + TotalStaticInlineConstantSize += InlineCBAttr.NumConstants * sizeof(Uint32); } } @@ -586,24 +600,27 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) Uint8* pCurrentDataPtr = static_cast(m_pStaticInlineConstantData); for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) { - const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; + InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; + + const PipelineResourceDesc& ResDesc = GetResourceDesc(InlineCBAttr.ResIndex); + const ResourceAttribs& Attr = GetResourceAttribs(InlineCBAttr.ResIndex); - for (Uint32 r = 0; r < TotalResources; ++r) + if (ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) { - const PipelineResourceDesc& ResDesc = GetResourceDesc(r); - const ResourceAttribs& Attr = GetResourceAttribs(r); + const Uint32 DataSize = InlineCBAttr.NumConstants * sizeof(Uint32); - if ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) && - ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC && - Attr.DescrSet == InlineCBAttr.DescrSet && - Attr.BindingIndex == InlineCBAttr.BindingIndex) + if (InlineCBAttr.IsPushConstant) + { + // For push constants, store the data pointer directly + InlineCBAttr.pPushConstantData = pCurrentDataPtr; + } + else if (m_pStaticResCache != nullptr) { - const Uint32 DataSize = InlineCBAttr.NumConstants * sizeof(Uint32); + // For emulated inline constants, initialize in the static resource cache const Uint32 CacheOffset = Attr.StaticCacheOffset; m_pStaticResCache->InitializeInlineConstantBuffer(0, CacheOffset, InlineCBAttr.NumConstants, pCurrentDataPtr); - pCurrentDataPtr += DataSize; - break; } + pCurrentDataPtr += DataSize; } } } @@ -650,6 +667,12 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk { const PipelineResourceDesc& ResDesc = GetResourceDesc(r); const ResourceAttribs& Attr = GetResourceAttribs(r); + + // Push constants don't use descriptor sets, so we skip InitializeResources for them + const bool IsPushConstant = (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != 0; + if (IsPushConstant) + continue; + // For inline constants, GetArraySize() returns 1 (actual array size), // while ArraySize contains the number of 32-bit constants ResourceCache.InitializeResources(Attr.DescrSet, Attr.CacheOffset(CacheType), ResDesc.GetArraySize(), @@ -657,45 +680,61 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk } // Initialize inline constant buffers - allocate staging memory and set up data pointers + // For push constants, each SRB gets its own copy of the data (stored in ShaderResourceCacheVk) + // For emulated inline constants, data is stored in the descriptor set resources if (m_NumInlineConstantBuffers > 0) { - // Calculate total memory size needed for all inline constants + // Count push constant buffers and calculate total memory size + Uint32 NumPushConstantBuffers = 0; Uint32 TotalInlineConstantSize = 0; for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) { - TotalInlineConstantSize += m_InlineConstantBuffers[i].NumConstants * sizeof(Uint32); + const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; + TotalInlineConstantSize += InlineCBAttr.NumConstants * sizeof(Uint32); + if (InlineCBAttr.IsPushConstant) + ++NumPushConstantBuffers; + } + + // Initialize push constant data pointers array in the resource cache + if (NumPushConstantBuffers > 0) + { + ResourceCache.InitializePushConstantDataPtrs(NumPushConstantBuffers); } - // Allocate single memory block for all inline constants - void* pInlineConstantMemory = CacheMemAllocator.Allocate(TotalInlineConstantSize, "Inline constant data", __FILE__, __LINE__); - memset(pInlineConstantMemory, 0, TotalInlineConstantSize); + // Allocate memory for all inline constants + void* pInlineConstantMemory = nullptr; + if (TotalInlineConstantSize > 0) + { + pInlineConstantMemory = CacheMemAllocator.Allocate(TotalInlineConstantSize, "Inline constant data", __FILE__, __LINE__); + memset(pInlineConstantMemory, 0, TotalInlineConstantSize); - // Pass ownership of the memory to the resource cache for proper cleanup - ResourceCache.SetInlineConstantMemory(CacheMemAllocator, pInlineConstantMemory); + // Pass ownership of the memory to the resource cache for proper cleanup + ResourceCache.SetInlineConstantMemory(CacheMemAllocator, pInlineConstantMemory); + } // Assign memory to each inline constant buffer - Uint8* pCurrentDataPtr = static_cast(pInlineConstantMemory); + Uint8* pCurrentDataPtr = static_cast(pInlineConstantMemory); + Uint32 PushConstantBufferIdx = 0; for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) { const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; const Uint32 DataSize = InlineCBAttr.NumConstants * sizeof(Uint32); - // Find the resource and set its inline constant data pointer - for (Uint32 r = 0; r < TotalResources; ++r) + if (InlineCBAttr.IsPushConstant) { - const PipelineResourceDesc& ResDesc = GetResourceDesc(r); - const ResourceAttribs& Attr = GetResourceAttribs(r); - - if ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) && - Attr.DescrSet == InlineCBAttr.DescrSet && - Attr.BindingIndex == InlineCBAttr.BindingIndex) - { - ResourceCache.InitializeInlineConstantBuffer(Attr.DescrSet, Attr.CacheOffset(CacheType), - InlineCBAttr.NumConstants, pCurrentDataPtr); - break; - } + // For push constants, store the data pointer in the resource cache + // Each SRB has its own copy of push constant data + ResourceCache.SetPushConstantDataPtr(PushConstantBufferIdx++, pCurrentDataPtr); + pCurrentDataPtr += DataSize; + } + else + { + // For emulated inline constants, use the descriptor set resources + const ResourceAttribs& Attr = GetResourceAttribs(InlineCBAttr.ResIndex); + ResourceCache.InitializeInlineConstantBuffer(Attr.DescrSet, Attr.CacheOffset(CacheType), + InlineCBAttr.NumConstants, pCurrentDataPtr); + pCurrentDataPtr += DataSize; } - pCurrentDataPtr += DataSize; } } @@ -724,60 +763,77 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk if (!InlineCBAttr.pBuffer) continue; - // Find the resource and bind the internal buffer - for (Uint32 r = 0; r < TotalResources; ++r) - { - const PipelineResourceDesc& ResDesc = GetResourceDesc(r); - const ResourceAttribs& Attr = GetResourceAttribs(r); - - if ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) && - Attr.DescrSet == InlineCBAttr.DescrSet && - Attr.BindingIndex == InlineCBAttr.BindingIndex) - { - const Uint32 CacheOffset = Attr.CacheOffset(CacheType); + // Use ResIndex to directly access the resource attributes + const PipelineResourceDesc& ResDesc = GetResourceDesc(InlineCBAttr.ResIndex); + const ResourceAttribs& Attr = GetResourceAttribs(InlineCBAttr.ResIndex); + const Uint32 CacheOffset = Attr.CacheOffset(CacheType); - // For static/mutable variables, bind to the allocated descriptor set - // For dynamic variables, the buffer will be bound during CommitDynamicResources - // Note: Dynamic descriptor sets are allocated per-draw call, so we can't write to them here - if (ResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) + // For static/mutable variables, bind to the allocated descriptor set + // For dynamic variables, the buffer will be bound during CommitDynamicResources + // Note: Dynamic descriptor sets are allocated per-draw call, so we can't write to them here + if (ResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) + { + // Bind the internal uniform buffer to the resource cache + ResourceCache.SetResource( + &GetDevice()->GetLogicalDevice(), + Attr.DescrSet, + CacheOffset, { - // Bind the internal uniform buffer to the resource cache - ResourceCache.SetResource( - &GetDevice()->GetLogicalDevice(), - Attr.DescrSet, - CacheOffset, - { - Attr.BindingIndex, - 0, // ArrayIndex - RefCntAutoPtr{InlineCBAttr.pBuffer}, - 0, // BufferBaseOffset - InlineCBAttr.NumConstants * sizeof(Uint32) // BufferRangeSize - }); - } - else + Attr.BindingIndex, + 0, // ArrayIndex + RefCntAutoPtr{InlineCBAttr.pBuffer}, + 0, // BufferBaseOffset + InlineCBAttr.NumConstants * sizeof(Uint32) // BufferRangeSize + }); + } + else + { + // For dynamic variables, we still need to set the buffer in the cache + // but we pass nullptr for LogicalDevice since the descriptor set is not allocated yet + ResourceCache.SetResource( + nullptr, // Don't write to descriptor set + Attr.DescrSet, + CacheOffset, { - // For dynamic variables, we still need to set the buffer in the cache - // but we pass nullptr for LogicalDevice since the descriptor set is not allocated yet - ResourceCache.SetResource( - nullptr, // Don't write to descriptor set - Attr.DescrSet, - CacheOffset, - { - Attr.BindingIndex, - 0, // ArrayIndex - RefCntAutoPtr{InlineCBAttr.pBuffer}, - 0, // BufferBaseOffset - InlineCBAttr.NumConstants * sizeof(Uint32) // BufferRangeSize - }); - } - break; - } + Attr.BindingIndex, + 0, // ArrayIndex + RefCntAutoPtr{InlineCBAttr.pBuffer}, + 0, // BufferBaseOffset + InlineCBAttr.NumConstants * sizeof(Uint32) // BufferRangeSize + }); } } } void PipelineResourceSignatureVkImpl::CopyStaticResources(ShaderResourceCacheVk& DstResourceCache) const { + // Handle STATIC push constants first - copy data from Signature's static cache to SRB's cache + // Each SRB has its own copy of push constant data + const auto ResIdxRange = GetResourceIndexRange(SHADER_RESOURCE_VARIABLE_TYPE_STATIC); + Uint32 PushConstantBufferIdx = 0; + for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) + { + const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; + if (!InlineCBAttr.IsPushConstant) + continue; + + const PipelineResourceDesc& ResDesc = GetResourceDesc(InlineCBAttr.ResIndex); + if (ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) + { + // Get source data from Signature's static inline constant data + const void* pSrcData = InlineCBAttr.pPushConstantData; + // Get destination data pointer from SRB's resource cache + void* pDstData = DstResourceCache.GetPushConstantDataPtr(PushConstantBufferIdx); + + if (pSrcData != nullptr && pDstData != nullptr) + { + const Uint32 DataSize = InlineCBAttr.NumConstants * sizeof(Uint32); + memcpy(pDstData, pSrcData, DataSize); + } + } + ++PushConstantBufferIdx; + } + if (!HasDescriptorSet(DESCRIPTOR_SET_ID_STATIC_MUTABLE) || m_pStaticResCache == nullptr) return; @@ -788,7 +844,6 @@ void PipelineResourceSignatureVkImpl::CopyStaticResources(ShaderResourceCacheVk& const Uint32 StaticSetIdx = GetDescriptorSetIndex(); const ShaderResourceCacheVk::DescriptorSet& SrcDescrSet = SrcResourceCache.GetDescriptorSet(StaticSetIdx); const ShaderResourceCacheVk::DescriptorSet& DstDescrSet = const_cast(DstResourceCache).GetDescriptorSet(StaticSetIdx); - const auto ResIdxRange = GetResourceIndexRange(SHADER_RESOURCE_VARIABLE_TYPE_STATIC); const ResourceCacheContentType SrcCacheType = SrcResourceCache.GetContentType(); const ResourceCacheContentType DstCacheType = DstResourceCache.GetContentType(); @@ -801,7 +856,11 @@ void PipelineResourceSignatureVkImpl::CopyStaticResources(ShaderResourceCacheVk& if (ResDesc.ResourceType == SHADER_RESOURCE_TYPE_SAMPLER && Attr.IsImmutableSamplerAssigned()) continue; // Skip immutable separate samplers - // Handle inline constants separately + // Skip push constants - they were handled above + if ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != 0) + continue; + + // Handle emulated inline constants separately if (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) { const Uint32 SrcCacheOffset = Attr.CacheOffset(SrcCacheType); @@ -1250,33 +1309,39 @@ PipelineResourceSignatureInternalDataVk PipelineResourceSignatureVkImpl::GetInte void PipelineResourceSignatureVkImpl::UpdateInlineConstantBuffers(const ShaderResourceCacheVk& ResourceCache, DeviceContextVkImpl& Ctx) const { + Uint32 PushConstantBufferIdx = 0; for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) { const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; - - // Get the inline constant data from the resource cache - const void* pInlineConstantData = ResourceCache.GetInlineConstantData(InlineCBAttr.DescrSet, InlineCBAttr.BindingIndex); - if (pInlineConstantData == nullptr) - continue; - const Uint32 DataSize = InlineCBAttr.NumConstants * sizeof(Uint32); if (InlineCBAttr.IsPushConstant) { - // For true push constants, copy data to the device context's push constants buffer + // For true push constants, get data from the SRB's resource cache + // Each SRB has its own copy of push constant data + const void* pInlineConstantData = ResourceCache.GetPushConstantDataPtr(PushConstantBufferIdx); + VERIFY_EXPR(pInlineConstantData != nullptr); + + ++PushConstantBufferIdx; + + // Copy data to the device context's push constants buffer // This will be submitted via vkCmdPushConstants before draw/dispatch Ctx.SetPushConstants(pInlineConstantData, 0, DataSize); + continue; } - else if (InlineCBAttr.pBuffer) + + if (InlineCBAttr.pBuffer) { - // For emulated inline constants, map the buffer and copy the data + // For emulated inline constants, get data from the resource cache + const void* pInlineConstantData = ResourceCache.GetInlineConstantData(InlineCBAttr.DescrSet, InlineCBAttr.BindingIndex); + VERIFY_EXPR(pInlineConstantData != nullptr); + + // Map the buffer and copy the data void* pMappedData = nullptr; Ctx.MapBuffer(InlineCBAttr.pBuffer, MAP_WRITE, MAP_FLAG_DISCARD, pMappedData); - if (pMappedData != nullptr) - { - memcpy(pMappedData, pInlineConstantData, DataSize); - Ctx.UnmapBuffer(InlineCBAttr.pBuffer, MAP_WRITE); - } + memcpy(pMappedData, pInlineConstantData, DataSize); + Ctx.UnmapBuffer(InlineCBAttr.pBuffer, MAP_WRITE); + continue; } } } @@ -1296,12 +1361,11 @@ void PipelineResourceSignatureVkImpl::UpdatePushConstantFlags(Uint32 ResIndex) ResDesc.Flags |= PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT; // Update the corresponding InlineConstantBufferAttribsVk to mark it as a push constant - // We need to find the inline constant buffer attribute that corresponds to this resource - const ResourceAttribs& Attr = GetResourceAttribs(ResIndex); + // Use ResIndex for matching for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) { InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; - if (InlineCBAttr.DescrSet == Attr.DescrSet && InlineCBAttr.BindingIndex == Attr.BindingIndex) + if (InlineCBAttr.ResIndex == ResIndex) { // Mark this as a true push constant InlineCBAttr.IsPushConstant = true; diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp index f1609915af..5681d63136 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp @@ -731,23 +731,14 @@ void PipelineStateVkImpl::RemapOrVerifyShaderResources( const SHADER_RESOURCE_TYPE ResType = SPIRVShaderResourceAttribs::GetShaderResourceType(SPIRVAttribs.Type); const PIPELINE_RESOURCE_FLAGS Flags = SPIRVShaderResourceAttribs::GetPipelineResourceFlags(SPIRVAttribs.Type); - // For push constants, skip descriptor set operations but validate the resource exists + // For push constants, skip descriptor set operations but validate the resource exists. + // Note: PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT flag must be added if (IsPushConstant) { if (ResAttribution.ResourceIndex != ResourceAttribution::InvalidResourceIndex) { const PipelineResourceDesc& ResDesc = ResAttribution.pSignature->GetResourceDesc(ResAttribution.ResourceIndex); - - // Automatically add PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT flag if the resource - // has PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS but is missing the Vulkan push constant flag. - // This ensures that true push constants use vkCmdPushConstants instead of dynamic uniform buffers. - // We got an unused uniform buffer and an unused descriptor set after this, it's okay though. - PipelineResourceSignatureVkImpl* pSignatureVk = const_cast(ResAttribution.pSignature); - pSignatureVk->UpdatePushConstantFlags(ResAttribution.ResourceIndex); - - // Get the updated resource description after adding the flag - const PipelineResourceDesc& UpdatedResDesc = pSignatureVk->GetResourceDesc(ResAttribution.ResourceIndex); - ValidatePipelineResourceCompatibility(UpdatedResDesc, ResType, Flags, SPIRVAttribs.ArraySize, + ValidatePipelineResourceCompatibility(ResDesc, ResType, Flags, SPIRVAttribs.ArraySize, pShader->GetDesc().Name, SignDesc.Name); } if (pDvpResourceAttibutions) @@ -1084,7 +1075,15 @@ void PipelineStateVkImpl::DvpValidateResourceLimits() const { const PipelineResourceDesc& ResDesc = pSignature->GetResourceDesc(r); const PipelineResourceSignatureVkImpl::ResourceAttribs& ResAttr = pSignature->GetResourceAttribs(r); - const Uint32 DescIndex = static_cast(ResAttr.DescrType); + + // Skip push constants - they don't use descriptors + if ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != 0) + continue; + + if (ResAttr.IsPushConstantBuffer()) + continue; + + const Uint32 DescIndex = static_cast(ResAttr.DescrType); DescriptorCount[DescIndex] += ResAttr.ArraySize; diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp index f575af3f6d..5b5ab9e2f7 100644 --- a/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp +++ b/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp @@ -656,16 +656,73 @@ void ShaderVariableManagerVk::SetInlineConstants(Uint32 ResIndex, Uint32 FirstConstant, Uint32 NumConstants) { - const PipelineResourceAttribsVk& Attribs = m_pSignature->GetResourceAttribs(ResIndex); - const ResourceCacheContentType CacheType = m_ResourceCache.GetContentType(); - const Uint32 CacheOffset = Attribs.CacheOffset(CacheType); + const PipelineResourceAttribsVk& Attribs = m_pSignature->GetResourceAttribs(ResIndex); + const PipelineResourceDesc& ResDesc = m_pSignature->GetResourceDesc(ResIndex); #ifdef DILIGENT_DEVELOPMENT - const PipelineResourceDesc& ResDesc = m_pSignature->GetResourceDesc(ResIndex); VerifyInlineConstants(ResDesc, pConstants, FirstConstant, NumConstants); #endif - m_ResourceCache.SetInlineConstants(Attribs.DescrSet, CacheOffset, pConstants, FirstConstant, NumConstants); + // Check if this is a push constant (doesn't use descriptor sets) + const bool IsPushConstant = (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != 0; + if (IsPushConstant) + { + const InlineConstantBufferAttribsVk* pInlineCBs = m_pSignature->GetInlineConstantBuffers(); + const Uint32 NumInlineCBs = m_pSignature->GetNumInlineConstantBuffers(); + const ResourceCacheContentType CacheType = m_ResourceCache.GetContentType(); + + if (CacheType == ResourceCacheContentType::Signature) + { + // For Signature's static variables, write to InlineCBAttr.pPushConstantData + // which points to m_pStaticInlineConstantData + for (Uint32 i = 0; i < NumInlineCBs; ++i) + { + const InlineConstantBufferAttribsVk& InlineCBAttr = pInlineCBs[i]; + if (InlineCBAttr.ResIndex == ResIndex && InlineCBAttr.IsPushConstant) + { + if (InlineCBAttr.pPushConstantData != nullptr) + { + Uint32* pDstConstants = reinterpret_cast(InlineCBAttr.pPushConstantData); + memcpy(pDstConstants + FirstConstant, pConstants, NumConstants * sizeof(Uint32)); + } + return; + } + } + } + else + { + // For SRB, write to the SRB's resource cache + // Each SRB has its own copy of push constant data + Uint32 PushConstantBufferIdx = 0; + for (Uint32 i = 0; i < NumInlineCBs; ++i) + { + const InlineConstantBufferAttribsVk& InlineCBAttr = pInlineCBs[i]; + if (!InlineCBAttr.IsPushConstant) + continue; + + if (InlineCBAttr.ResIndex == ResIndex) + { + // Get the data pointer from the resource cache + void* pPushConstantData = m_ResourceCache.GetPushConstantDataPtr(PushConstantBufferIdx); + if (pPushConstantData != nullptr) + { + Uint32* pDstConstants = reinterpret_cast(pPushConstantData); + memcpy(pDstConstants + FirstConstant, pConstants, NumConstants * sizeof(Uint32)); + } + return; + } + ++PushConstantBufferIdx; + } + } + UNEXPECTED("Push constant buffer not found"); + } + else + { + // For emulated inline constants, use the resource cache + const ResourceCacheContentType CacheType = m_ResourceCache.GetContentType(); + const Uint32 CacheOffset = Attribs.CacheOffset(CacheType); + m_ResourceCache.SetInlineConstants(Attribs.DescrSet, CacheOffset, pConstants, FirstConstant, NumConstants); + } } IDeviceObject* ShaderVariableManagerVk::Get(Uint32 ArrayIndex, Uint32 ResIndex) const From b8cddc6ec7cebfa964bc2180b8715254a2bb325e Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sun, 7 Dec 2025 17:18:52 +0800 Subject: [PATCH 041/104] fix formatting --- .../include/DeviceContextVkImpl.hpp | 6 +++--- .../include/PipelineLayoutVk.hpp | 8 ++++---- .../include/PipelineResourceSignatureVkImpl.hpp | 12 ++++++------ .../include/ShaderResourceCacheVk.hpp | 2 +- .../src/DeviceContextVkImpl.cpp | 4 ++-- .../GraphicsEngineVulkan/src/PipelineLayoutVk.cpp | 8 ++++---- .../src/PipelineResourceSignatureVkImpl.cpp | 14 +++++++------- .../src/PipelineStateVkImpl.cpp | 6 +++--- .../src/ShaderVariableManagerVk.cpp | 6 +++--- 9 files changed, 33 insertions(+), 33 deletions(-) diff --git a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp index f011d83f47..9e5ee1f870 100644 --- a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp @@ -526,9 +526,9 @@ class DeviceContextVkImpl final : public DeviceContextNextGenBase m_PushConstantsData = {}; - Uint32 m_PushConstantsDataSize = 0; // Actual size of valid push constants data + static constexpr Uint32 MAX_PUSH_CONSTANTS_SIZE = 256; + std::array m_PushConstantsData = {}; + Uint32 m_PushConstantsDataSize = 0; // Actual size of valid push constants data // Graphics/mesh, compute, ray tracing static constexpr Uint32 NUM_PIPELINE_BIND_POINTS = 3; diff --git a/Graphics/GraphicsEngineVulkan/include/PipelineLayoutVk.hpp b/Graphics/GraphicsEngineVulkan/include/PipelineLayoutVk.hpp index e0ea1565db..9e4779a888 100644 --- a/Graphics/GraphicsEngineVulkan/include/PipelineLayoutVk.hpp +++ b/Graphics/GraphicsEngineVulkan/include/PipelineLayoutVk.hpp @@ -54,10 +54,10 @@ class PipelineLayoutVk PipelineLayoutVk(); ~PipelineLayoutVk(); - void Create(RenderDeviceVkImpl* pDeviceVk, - RefCntAutoPtr ppSignatures[], - Uint32 SignatureCount, - const PushConstantInfoVk& PushConstant = {}) noexcept(false); + void Create(RenderDeviceVkImpl* pDeviceVk, + RefCntAutoPtr ppSignatures[], + Uint32 SignatureCount, + const PushConstantInfoVk& PushConstant = {}) noexcept(false); void Release(RenderDeviceVkImpl* pDeviceVkImpl, Uint64 CommandQueueMask); VkPipelineLayout GetVkPipelineLayout() const { return m_VkPipelineLayout; } diff --git a/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp index f18b02cce9..8399bfcc5a 100644 --- a/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp @@ -65,12 +65,12 @@ ASSERT_SIZEOF(ImmutableSamplerAttribsVk, 8, "The struct is used in serialization /// 2. Emulated inline constants - use dynamic uniform buffers (similar to D3D11 backend) struct InlineConstantBufferAttribsVk { - Uint32 ResIndex = 0; // Resource index in the signature (used for matching) - Uint32 DescrSet = 0; // Descriptor set index (0 for push constants as placeholder) - Uint32 BindingIndex = 0; // Binding index within the descriptor set - Uint32 NumConstants = 0; // Number of 32-bit constants - bool IsPushConstant = false; // True if this is a Vulkan push constant (not emulated) - RefCntAutoPtr pBuffer; // Internal dynamic uniform buffer (null for true push constants) + Uint32 ResIndex = 0; // Resource index in the signature (used for matching) + Uint32 DescrSet = 0; // Descriptor set index (0 for push constants as placeholder) + Uint32 BindingIndex = 0; // Binding index within the descriptor set + Uint32 NumConstants = 0; // Number of 32-bit constants + bool IsPushConstant = false; // True if this is a Vulkan push constant (not emulated) + RefCntAutoPtr pBuffer; // Internal dynamic uniform buffer (null for true push constants) void* pPushConstantData = nullptr; // CPU-side staging buffer for push constants (only used when IsPushConstant is true) }; diff --git a/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp b/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp index 03a311c9a6..e1173ad002 100644 --- a/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp +++ b/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp @@ -353,7 +353,7 @@ class ShaderResourceCacheVk : public ShaderResourceCacheBase // Each pointer points to memory within m_pInlineConstantMemory std::unique_ptr m_pPushConstantDataPtrs; - Uint16 m_NumSets = 0; + Uint16 m_NumSets = 0; Uint16 m_NumPushConstantBuffers = 0; // Total actual number of dynamic buffers (that were created with USAGE_DYNAMIC) bound in the resource cache diff --git a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp index 629df69651..dd1a4e641c 100644 --- a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp @@ -439,7 +439,7 @@ void DeviceContextVkImpl::UpdateInlineConstantBuffers(ResourceBindInfo& BindInfo { // 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()?"); + "Did you call CommitShaderResources()?"); continue; } @@ -506,7 +506,7 @@ void DeviceContextVkImpl::CommitDescriptorSets(ResourceBindInfo& BindInfo, Uint3 // Filter out SRBs that only have push constants (no descriptor sets) // These SRBs are included in CommitSRBMask due to InlineConstantsSRBMask, but they don't need descriptor set binding - Uint32 FilteredCommitMask = CommitSRBMask; + Uint32 FilteredCommitMask = CommitSRBMask; Uint32 ResourceSignaturesCount = m_pPipelineState->GetResourceSignatureCount(); for (Uint32 sign = 0; sign < ResourceSignaturesCount; ++sign) { diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineLayoutVk.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineLayoutVk.cpp index 7d0a459083..c725be38cb 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineLayoutVk.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineLayoutVk.cpp @@ -59,10 +59,10 @@ void PipelineLayoutVk::Release(RenderDeviceVkImpl* pDeviceVk, Uint64 CommandQueu } } -void PipelineLayoutVk::Create(RenderDeviceVkImpl* pDeviceVk, - RefCntAutoPtr ppSignatures[], - Uint32 SignatureCount, - const PushConstantInfoVk& PushConstant) noexcept(false) +void PipelineLayoutVk::Create(RenderDeviceVkImpl* pDeviceVk, + RefCntAutoPtr ppSignatures[], + Uint32 SignatureCount, + const PushConstantInfoVk& PushConstant) noexcept(false) { VERIFY(m_DescrSetCount == 0 && !m_VkPipelineLayout, "This pipeline layout is already initialized"); diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp index 8490175738..cba585e203 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp @@ -764,8 +764,8 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk continue; // Use ResIndex to directly access the resource attributes - const PipelineResourceDesc& ResDesc = GetResourceDesc(InlineCBAttr.ResIndex); - const ResourceAttribs& Attr = GetResourceAttribs(InlineCBAttr.ResIndex); + const PipelineResourceDesc& ResDesc = GetResourceDesc(InlineCBAttr.ResIndex); + const ResourceAttribs& Attr = GetResourceAttribs(InlineCBAttr.ResIndex); const Uint32 CacheOffset = Attr.CacheOffset(CacheType); // For static/mutable variables, bind to the allocated descriptor set @@ -809,7 +809,7 @@ void PipelineResourceSignatureVkImpl::CopyStaticResources(ShaderResourceCacheVk& { // Handle STATIC push constants first - copy data from Signature's static cache to SRB's cache // Each SRB has its own copy of push constant data - const auto ResIdxRange = GetResourceIndexRange(SHADER_RESOURCE_VARIABLE_TYPE_STATIC); + const auto ResIdxRange = GetResourceIndexRange(SHADER_RESOURCE_VARIABLE_TYPE_STATIC); Uint32 PushConstantBufferIdx = 0; for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) { @@ -1313,7 +1313,7 @@ void PipelineResourceSignatureVkImpl::UpdateInlineConstantBuffers(const ShaderRe for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) { const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; - const Uint32 DataSize = InlineCBAttr.NumConstants * sizeof(Uint32); + const Uint32 DataSize = InlineCBAttr.NumConstants * sizeof(Uint32); if (InlineCBAttr.IsPushConstant) { @@ -1349,17 +1349,17 @@ void PipelineResourceSignatureVkImpl::UpdateInlineConstantBuffers(const ShaderRe void PipelineResourceSignatureVkImpl::UpdatePushConstantFlags(Uint32 ResIndex) { VERIFY_EXPR(ResIndex < this->m_Desc.NumResources); - + // Access the mutable resource description PipelineResourceDesc& ResDesc = const_cast(this->m_Desc.Resources[ResIndex]); - + // Check if the resource has INLINE_CONSTANTS flag but is missing VULKAN_PUSH_CONSTANT flag if ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0 && (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) == 0) { // Add the Vulkan push constant flag ResDesc.Flags |= PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT; - + // Update the corresponding InlineConstantBufferAttribsVk to mark it as a push constant // Use ResIndex for matching for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp index 5681d63136..89acf48505 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp @@ -1073,8 +1073,8 @@ void PipelineStateVkImpl::DvpValidateResourceLimits() const for (Uint32 r = 0; r < pSignature->GetTotalResourceCount(); ++r) { - const PipelineResourceDesc& ResDesc = pSignature->GetResourceDesc(r); - const PipelineResourceSignatureVkImpl::ResourceAttribs& ResAttr = pSignature->GetResourceAttribs(r); + const PipelineResourceDesc& ResDesc = pSignature->GetResourceDesc(r); + const PipelineResourceSignatureVkImpl::ResourceAttribs& ResAttr = pSignature->GetResourceAttribs(r); // Skip push constants - they don't use descriptors if ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != 0) @@ -1082,7 +1082,7 @@ void PipelineStateVkImpl::DvpValidateResourceLimits() const if (ResAttr.IsPushConstantBuffer()) continue; - + const Uint32 DescIndex = static_cast(ResAttr.DescrType); DescriptorCount[DescIndex] += ResAttr.ArraySize; diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp index 5b5ab9e2f7..8f5f76dc2e 100644 --- a/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp +++ b/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp @@ -667,9 +667,9 @@ void ShaderVariableManagerVk::SetInlineConstants(Uint32 ResIndex, const bool IsPushConstant = (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != 0; if (IsPushConstant) { - const InlineConstantBufferAttribsVk* pInlineCBs = m_pSignature->GetInlineConstantBuffers(); - const Uint32 NumInlineCBs = m_pSignature->GetNumInlineConstantBuffers(); - const ResourceCacheContentType CacheType = m_ResourceCache.GetContentType(); + const InlineConstantBufferAttribsVk* pInlineCBs = m_pSignature->GetInlineConstantBuffers(); + const Uint32 NumInlineCBs = m_pSignature->GetNumInlineConstantBuffers(); + const ResourceCacheContentType CacheType = m_ResourceCache.GetContentType(); if (CacheType == ResourceCacheContentType::Signature) { From 1e3f54f535137c5c5fc9604d8548b657aeb05bbd Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sun, 7 Dec 2025 17:32:58 +0800 Subject: [PATCH 042/104] Fix emulated inline constant. --- Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp index 89acf48505..f0fa11781c 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp @@ -1077,15 +1077,12 @@ void PipelineStateVkImpl::DvpValidateResourceLimits() const const PipelineResourceSignatureVkImpl::ResourceAttribs& ResAttr = pSignature->GetResourceAttribs(r); // Skip push constants - they don't use descriptors - if ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != 0) - continue; - if (ResAttr.IsPushConstantBuffer()) continue; const Uint32 DescIndex = static_cast(ResAttr.DescrType); - DescriptorCount[DescIndex] += ResAttr.ArraySize; + DescriptorCount[DescIndex] += ResDesc.GetArraySize(); for (SHADER_TYPE ShaderStages = ResDesc.ShaderStages; ShaderStages != 0;) { From 5381581fe27860dc00f96967c2766b28582e033a Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sun, 7 Dec 2025 17:36:59 +0800 Subject: [PATCH 043/104] Add missing tests --- .../src/GraphicsAccessories/GraphicsAccessoriesTest.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Tests/DiligentCoreTest/src/GraphicsAccessories/GraphicsAccessoriesTest.cpp b/Tests/DiligentCoreTest/src/GraphicsAccessories/GraphicsAccessoriesTest.cpp index 5b4c9f342f..5f74fb2e9c 100644 --- a/Tests/DiligentCoreTest/src/GraphicsAccessories/GraphicsAccessoriesTest.cpp +++ b/Tests/DiligentCoreTest/src/GraphicsAccessories/GraphicsAccessoriesTest.cpp @@ -705,7 +705,7 @@ TEST(GraphicsAccessories_GraphicsAccessories, PipelineTypeFromShaderStages) TEST(GraphicsAccessories_GraphicsAccessories, GetPipelineResourceFlagsString) { - static_assert(PIPELINE_RESOURCE_FLAG_LAST == (1u << 5), "Please add a test for the new flag here"); + static_assert(PIPELINE_RESOURCE_FLAG_LAST == (1u << 6), "Please add a test for the new flag here"); EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_NONE, true).c_str(), "PIPELINE_RESOURCE_FLAG_NONE"); EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_NONE).c_str(), "UNKNOWN"); @@ -715,12 +715,14 @@ TEST(GraphicsAccessories_GraphicsAccessories, GetPipelineResourceFlagsString) EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER, true).c_str(), "PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER"); EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_GENERAL_INPUT_ATTACHMENT, true).c_str(), "PIPELINE_RESOURCE_FLAG_GENERAL_INPUT_ATTACHMENT"); EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS, true).c_str(), "PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS"); + EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT, true).c_str(), "PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT"); EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS).c_str(), "NO_DYNAMIC_BUFFERS"); EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_COMBINED_SAMPLER).c_str(), "COMBINED_SAMPLER"); EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER).c_str(), "FORMATTED_BUFFER"); EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_GENERAL_INPUT_ATTACHMENT).c_str(), "GENERAL_INPUT_ATTACHMENT"); EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS).c_str(), "INLINE_CONSTANTS"); + EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT).c_str(), "VULKAN_PUSH_CONSTANT"); EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS | PIPELINE_RESOURCE_FLAG_COMBINED_SAMPLER, true).c_str(), "PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS|PIPELINE_RESOURCE_FLAG_COMBINED_SAMPLER"); From 4354493583f18e788d966d066943d70ba10319fa Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sun, 7 Dec 2025 17:46:35 +0800 Subject: [PATCH 044/104] constexpr has ODR issue on ARM64, better go with macro. --- .../include/DeviceContextVkImpl.hpp | 10 ++++++---- .../GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp index 9e5ee1f870..d869675835 100644 --- a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp @@ -60,6 +60,9 @@ #include "HashUtils.hpp" #include "ManagedVulkanObject.hpp" +// Push constants data storage (max size is typically 128-256 bytes, use 256 for safety) +#define DILIGENT_MAX_PUSH_CONSTANTS_SIZE 256 + namespace Diligent { @@ -525,10 +528,9 @@ class DeviceContextVkImpl final : public DeviceContextNextGenBase m_PushConstantsData = {}; - Uint32 m_PushConstantsDataSize = 0; // Actual size of valid push constants data + // Push constants data storage + std::array m_PushConstantsData = {}; + Uint32 m_PushConstantsDataSize = 0; // Actual size of valid push constants data // Graphics/mesh, compute, ray tracing static constexpr Uint32 NUM_PIPELINE_BIND_POINTS = 3; diff --git a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp index dd1a4e641c..3d3013eb8e 100644 --- a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp @@ -454,8 +454,8 @@ void DeviceContextVkImpl::UpdateInlineConstantBuffers(ResourceBindInfo& BindInfo void DeviceContextVkImpl::SetPushConstants(const void* pData, Uint32 Offset, Uint32 Size) { DEV_CHECK_ERR(pData != nullptr || Size == 0, "pData must not be null when Size is non-zero"); - DEV_CHECK_ERR(Offset + Size <= MAX_PUSH_CONSTANTS_SIZE, - "Push constant data range [", Offset, ", ", Offset + Size, ") exceeds the maximum supported size (", MAX_PUSH_CONSTANTS_SIZE, ")"); + DEV_CHECK_ERR(Offset + Size <= DILIGENT_MAX_PUSH_CONSTANTS_SIZE, + "Push constant data range [", Offset, ", ", Offset + Size, ") exceeds the maximum supported size (", DILIGENT_MAX_PUSH_CONSTANTS_SIZE, ")"); // Note: This function may be called from UpdateInlineConstantBuffers during draw preparation, // at which point the pipeline state is guaranteed to be bound. When called from user code, From 0899af00d18be0cc574e7f025b0e69ce5dd872ef Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sun, 7 Dec 2025 17:48:18 +0800 Subject: [PATCH 045/104] fix formatting --- Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp index d869675835..6c2866d1e9 100644 --- a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.hpp @@ -530,7 +530,7 @@ class DeviceContextVkImpl final : public DeviceContextNextGenBase m_PushConstantsData = {}; - Uint32 m_PushConstantsDataSize = 0; // Actual size of valid push constants data + Uint32 m_PushConstantsDataSize = 0; // Actual size of valid push constants data // Graphics/mesh, compute, ray tracing static constexpr Uint32 NUM_PIPELINE_BIND_POINTS = 3; From de1d0476e1d5ac17c146f83cb132ee58a82459e1 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sun, 7 Dec 2025 18:18:18 +0800 Subject: [PATCH 046/104] better flags check in ValidatePipelineResourceSignatureDesc --- .../src/PipelineResourceSignatureBase.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp b/Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp index 46230fb03d..82c11d54fd 100644 --- a/Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp +++ b/Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp @@ -111,12 +111,21 @@ void ValidatePipelineResourceSignatureDesc(const PipelineResourceSignatureDesc& ": ", GetPipelineResourceFlagsString(AllowedResourceFlags, false, ", "), "."); } + if ((Res.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != 0) + { + if (Res.Flags != (PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS | PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT)) + { + LOG_PRS_ERROR_AND_THROW("Incorrect Desc.Resources[", i, "].Flags (", GetPipelineResourceFlagsString(Res.Flags), + "). VULKAN_PUSH_CONSTANT flags must be combined with INLINE_CONSTANTS."); + } + } + if ((Res.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0) { - if ((Res.Flags & (PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS | PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT)) != Res.Flags) + if (Res.Flags != PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS && Res.Flags != (PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS | PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT)) { LOG_PRS_ERROR_AND_THROW("Incorrect Desc.Resources[", i, "].Flags (", GetPipelineResourceFlagsString(Res.Flags), - "). INLINE_CONSTANTS and VULKAN_PUSH_CONSTANT flags cannot be combined with other flags."); + "). INLINE_CONSTANTS flag cannot be combined with flags other than VULKAN_PUSH_CONSTANT."); } if (Res.ArraySize > 64) From c42471c0f944ad8de662743b087e9a5261da7bba Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sun, 7 Dec 2025 18:26:09 +0800 Subject: [PATCH 047/104] update SetInlineConstants.md vulkan impl. --- doc/SetInlineConstants.md | 408 ++++++++++---------------------------- 1 file changed, 108 insertions(+), 300 deletions(-) diff --git a/doc/SetInlineConstants.md b/doc/SetInlineConstants.md index 92c863f322..ac7162428b 100644 --- a/doc/SetInlineConstants.md +++ b/doc/SetInlineConstants.md @@ -605,347 +605,156 @@ This creates two separate dynamic constant buffers. ### Architecture Overview -The Vulkan backend implements inline constants using **dynamic uniform buffers** for emulation, similar to the D3D11 backend. While Vulkan natively supports push constants, the current implementation uses uniform buffer emulation to support multiple inline constant resources and broader compatibility. +The Vulkan backend now supports **two** inline constant paths: -**Design Decision**: -- True Vulkan push constants are reserved for SPIRV blocks explicitly marked with `layout(push_constant)` (future implementation) -- All inline constant resources currently use dynamic uniform buffer emulation -- This approach allows multiple inline constant resources (Vulkan only supports one push constant block per pipeline) +- `PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS` + `PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT` → true push constants backed by `vkCmdPushConstants`. +- `PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS` without the push-constant flag → emulated via internal `USAGE_DYNAMIC` uniform buffers (same behavior as D3D11). + +This split lets us mix SPIR-V `layout(push_constant)` blocks with regular inline constant buffers in a single signature while still supporting multiple inline constant resources per pipeline. ### Implementation Architecture ``` -┌─────────────────────────────────────────────────────────────────────────────┐ -│ User Application │ -├─────────────────────────────────────────────────────────────────────────────┤ -│ pVariable->SetInlineConstants(pData, FirstConstant, NumConstants) │ -└──────────────────────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────────────────┐ -│ ShaderVariableManagerVk │ -│ ┌───────────────────────────────────────────────────────────────────────┐ │ -│ │ SetInlineConstants(): │ │ -│ │ 1. Get resource offset in merged push constant block │ │ -│ │ 2. Copy data to push constant storage │ │ -│ │ 3. Mark push constants as dirty │ │ -│ └───────────────────────────────────────────────────────────────────────┘ │ -└──────────────────────────────────┬──────────────────────────────────────────┘ - 1 │ - ▼ (During CommitShaderResources) -┌─────────────────────────────────────────────────────────────────────────────┐ -│ DeviceContextVkImpl │ -│ ┌───────────────────────────────────────────────────────────────────────┐ │ -│ │ CommitPushConstants(): │ │ -│ │ vkCmdPushConstants(cmdBuffer, pipelineLayout, │ │ -│ │ stageFlags, offset, size, pData); │ │ -│ └───────────────────────────────────────────────────────────────────────┘ │ -└─────────────────────────────────────────────────────────────────────────────┘ +┌────────────────────────────────────────────────────────────────────────────┐ +│ ShaderVariableManagerVk │ +│ SetInlineConstants(): │ +│ if resource has PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT │ +│ ├─ STATIC var -> copy into InlineCBAttr.pPushConstantData │ +│ └─ SRB cache -> write to ShaderResourceCacheVk push constant slot │ +│ else │ +│ └─ ShaderResourceCacheVk::SetInlineConstants(...) (CPU staging) │ +└────────────────────────────────────────────────────────────────────────────┘ + │ │ + │ ┌────────────────────┘ + ▼ (Bind SRB) ▼ (Bind SRB with emulation) +┌────────────────────────────────────────────────────────────────────────────┐ +│ DeviceContextVkImpl::UpdateInlineConstantBuffers │ +│ for each InlineConstantBufferAttribsVk │ +│ if IsPushConstant │ +│ -> ctx.SetPushConstants(pData, 0, Size) │ +│ else │ +│ -> Map internal uniform buffer, memcpy, Unmap │ +└────────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌────────────────────────────────────────────────────────────────────────────┐ +│ DeviceContextVkImpl::CommitPushConstants │ +│ vkCmdPushConstants(vkPipelineLayout, StageFlags, 0, Size, pCpuData) │ +└────────────────────────────────────────────────────────────────────────────┘ ``` ### Step 1: Resource Signature Creation -When creating a `PipelineResourceSignature` with `PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS`: +**SPIR-V reflection → default signature** +`PipelineStateVkImpl::GetDefaultResourceSignatureDesc()` converts SPIR-V resources into `PipelineResourceDesc`. When an attribute reports inline constants, the code derives the **number of 32-bit values** from `BufferStaticSize` instead of the logical array size, and SPIR-V `push_constant` resources propagate `PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT` (see `Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp:592-666`). -**File**: `PipelineResourceSignatureVkImpl.cpp` - `CreateSetLayouts()` +**Push constant metadata for the pipeline layout** +`PipelineStateVkImpl::InitPushConstantInfo()` walks every shader and records the max push constant size and the union of stage flags. `PipelineLayoutVk::Create()` stores that information so that `vkCmdPushConstants` can be issued later (`Graphics/GraphicsEngineVulkan/src/PipelineLayoutVk.cpp:62-164`). -```cpp -if (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) -{ - VERIFY(ResDesc.ResourceType == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, - "Only constant buffers can have INLINE_CONSTANTS flag"); - - // Create internal dynamic uniform buffer for emulation - InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[InlineCBIdx++]; - InlineCBAttr.DescrSet = DescrSetId; - InlineCBAttr.BindingIndex = vkBinding; - InlineCBAttr.NumConstants = ResDesc.ArraySize; // Number of 32-bit values - - // Create a USAGE_DYNAMIC uniform buffer - BufferDesc BuffDesc; - BuffDesc.Name = std::string(m_Desc.Name) + " - " + ResDesc.Name; - BuffDesc.Size = ResDesc.ArraySize * sizeof(Uint32); - BuffDesc.Usage = USAGE_DYNAMIC; - BuffDesc.BindFlags = BIND_UNIFORM_BUFFER; - BuffDesc.CPUAccessFlags = CPU_ACCESS_WRITE; - - RefCntAutoPtr pBuffer; - GetDevice()->CreateBuffer(BuffDesc, nullptr, &pBuffer); - InlineCBAttr.pBuffer = RefCntAutoPtr(pBuffer, IID_BufferVk); -} -``` +**InlineConstantBufferAttribsVk initialization** +Inside `PipelineResourceSignatureVkImpl::CreateSetLayouts()` every inline constant resource gets an `InlineConstantBufferAttribsVk` entry (`Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp:200-441`): -**Key Points**: -- Each inline constant resource creates a `USAGE_DYNAMIC` uniform buffer -- Buffer is stored in `InlineConstantBufferAttribsVk` structure -- Buffer is shared across all SRBs created from this signature +- `IsPushConstant` mirrors `PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT`. +- Push constants skip descriptor-set bookkeeping altogether; emulated inline constants allocate a `USAGE_DYNAMIC` uniform buffer that is shared by all SRBs originating from the signature. +- Static push constants allocate CPU-side storage (`pPushConstantData`) inside `m_pStaticInlineConstantData`. ### Step 2: Resource Cache Initialization -#### For Static Inline Constants +`PipelineResourceSignatureVkImpl::InitSRBResourceCache()` now sets up both flavors (`Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp:653-739`): -**File**: `PipelineResourceSignatureVkImpl.cpp` - `CreateSetLayouts()` +- A single block of CPU memory is allocated for all inline constants and the pointer is owned by `ShaderResourceCacheVk::m_pInlineConstantMemory`. +- Push-constant resources register their per-SRB staging pointers via `ShaderResourceCacheVk::InitializePushConstantDataPtrs()` / `SetPushConstantDataPtr()` so that each SRB maintains its own copy. +- Emulated inline constants call `InitializeInlineConstantBuffer()` to hook the CPU staging memory into the descriptor-set resource slots and bind the internal dynamic buffer through `SetResource()`. -Static inline constants require CPU-side staging buffers that are allocated once during signature creation and freed in the destructor. +Static inline constants follow the same pattern during `CreateSetLayouts()` (lines `576-626`), so signatures keep zeroed data for static variables and copy it into SRBs later. -```cpp -// Calculate total memory size for all static inline constants -Uint32 TotalStaticInlineConstantSize = 0; -for (each static inline constant) -{ - TotalStaticInlineConstantSize += NumConstants * sizeof(Uint32); -} +### Step 3: ShaderVariableManagerVk::SetInlineConstants -// Allocate single memory block for all static inline constants -m_pStaticInlineConstantData = GetRawAllocator().Allocate( - TotalStaticInlineConstantSize, "Static inline constant data", __FILE__, __LINE__); -memset(m_pStaticInlineConstantData, 0, TotalStaticInlineConstantSize); +`ShaderVariableManagerVk::SetInlineConstants()` branches on the new flag (`Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp:654-725`): -// Assign memory to each static inline constant -Uint8* pCurrentDataPtr = static_cast(m_pStaticInlineConstantData); -for (each static inline constant) -{ - m_pStaticResCache->InitializeInlineConstantBuffer( - 0, CacheOffset, NumConstants, pCurrentDataPtr); - pCurrentDataPtr += DataSize; -} ``` - -#### For Mutable/Dynamic Inline Constants (SRB) - -**File**: `PipelineResourceSignatureVkImpl.cpp` - `InitSRBResourceCache()` - -```cpp -void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk& ResourceCache) +if (IsPushConstant) { - // Initialize inline constant buffers - if (m_NumInlineConstantBuffers > 0) + if (CacheType == ResourceCacheContentType::Signature) { - // Calculate total memory size - Uint32 TotalInlineConstantSize = 0; - for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) - { - TotalInlineConstantSize += m_InlineConstantBuffers[i].NumConstants * sizeof(Uint32); - } - - // Allocate single memory block - void* pInlineConstantMemory = CacheMemAllocator.Allocate( - TotalInlineConstantSize, "Inline constant data", __FILE__, __LINE__); - memset(pInlineConstantMemory, 0, TotalInlineConstantSize); - - // Pass ownership to resource cache for proper cleanup - ResourceCache.SetInlineConstantMemory(CacheMemAllocator, pInlineConstantMemory); - - // Assign memory to each inline constant buffer - Uint8* pCurrentDataPtr = static_cast(pInlineConstantMemory); - for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) - { - ResourceCache.InitializeInlineConstantBuffer( - DescrSet, CacheOffset, NumConstants, pCurrentDataPtr); - pCurrentDataPtr += InlineCBAttr.NumConstants * sizeof(Uint32); - } + // Static push constants live inside InlineCBAttr.pPushConstantData. } - - // Bind internal uniform buffers to the resource cache - for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) + else { - ResourceCache.SetResource( - pLogicalDevice, // nullptr for dynamic variables - DescrSet, CacheOffset, - { - BindingIndex, 0, // ArrayIndex - RefCntAutoPtr{InlineCBAttr.pBuffer}, - 0, // BufferBaseOffset - InlineCBAttr.NumConstants * sizeof(Uint32) // BufferRangeSize - }); + // SRB cache owns per-instance push constant storage. + void* pDst = m_ResourceCache.GetPushConstantDataPtr(PushConstantBufferIdx); } + memcpy(...); // copy 32-bit values + return; } -``` - -**Memory Management**: -- Static inline constants: Memory allocated via `GetRawAllocator()`, freed in `Destruct()` -- SRB inline constants: Memory allocated via `CacheMemAllocator`, owned by `ShaderResourceCacheVk` (freed in destructor via `m_pInlineConstantMemory`) - -### Step 3: SetInlineConstants Implementation - -**File**: `ShaderVariableManagerVk.cpp` - -```cpp -void ShaderVariableManagerVk::SetInlineConstants(Uint32 ResIndex, - const void* pConstants, - Uint32 FirstConstant, - Uint32 NumConstants) -{ - const PipelineResourceAttribsVk& Attribs = m_pSignature->GetResourceAttribs(ResIndex); - const ResourceCacheContentType CacheType = m_ResourceCache.GetContentType(); - -#ifdef DILIGENT_DEVELOPMENT - const PipelineResourceDesc& ResDesc = m_pSignature->GetResourceDesc(ResIndex); - VerifyInlineConstants(ResDesc, pConstants, FirstConstant, NumConstants); -#endif - - m_ResourceCache.SetInlineConstants( - Attribs.DescrSet, - Attribs.CacheOffset(CacheType), - pConstants, - FirstConstant, - NumConstants); -} -``` - -**File**: `ShaderResourceCacheVk.cpp` -```cpp -void ShaderResourceCacheVk::SetInlineConstants(Uint32 DescrSetIndex, - Uint32 CacheOffset, - const void* pConstants, - Uint32 FirstConstant, - Uint32 NumConstants) -{ - DescriptorSet& DescrSet = GetDescriptorSet(DescrSetIndex); - Resource& DstRes = DescrSet.GetResource(CacheOffset); - - VERIFY(DstRes.pInlineConstantData != nullptr, - "Inline constant data pointer is null."); - - // Copy to CPU-side staging buffer - Uint32* pDstConstants = reinterpret_cast(DstRes.pInlineConstantData); - memcpy(pDstConstants + FirstConstant, pConstants, NumConstants * sizeof(Uint32)); - - UpdateRevision(); // Mark cache as dirty -} +const Uint32 CacheOffset = Attribs.CacheOffset(CacheType); +m_ResourceCache.SetInlineConstants(Attribs.DescrSet, CacheOffset, pConstants, + FirstConstant, NumConstants); ``` -### Step 4: Updating GPU Buffer - -During draw/dispatch commands, inline constant buffers are updated: +Emulated inline constants therefore keep using `ShaderResourceCacheVk::SetInlineConstants`, while push constants never touch descriptor sets. -**File**: `DeviceContextVkImpl.cpp` +### Step 4: Updating GPU State -```cpp -void DeviceContextVkImpl::PrepareForDraw(DRAW_FLAGS Flags) -{ - // Update inline constant buffers before committing descriptor sets - UpdateInlineConstantBuffers(BindInfo); - - // Commit descriptor sets - CommitDescriptorSets(BindInfo, /* ... */); -} +`PipelineResourceSignatureVkImpl::UpdateInlineConstantBuffers()` now understands both data paths (`Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp:1309-1345`): -void DeviceContextVkImpl::UpdateInlineConstantBuffers(ResourceBindInfo& BindInfo) -{ - for (Uint32 SignIdx = 0; SignIdx < BindInfo.ActiveSRBCount; ++SignIdx) - { - const auto& SignInfo = BindInfo.pSignatures[SignIdx]; - if (SignInfo.pSignature->GetNumInlineConstantBuffers() > 0) - { - SignInfo.pSignature->UpdateInlineConstantBuffers( - *SignInfo.pResourceCache, *this); - } - } -} ``` - -**File**: `PipelineResourceSignatureVkImpl.cpp` - -```cpp -void PipelineResourceSignatureVkImpl::UpdateInlineConstantBuffers( - const ShaderResourceCacheVk& ResourceCache, - DeviceContextVkImpl& Ctx) const +if (InlineCBAttr.IsPushConstant) { - for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) - { - const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; - - // Get the inline constant data from the resource cache - const void* pInlineConstantData = ResourceCache.GetInlineConstantData( - InlineCBAttr.DescrSet, InlineCBAttr.BindingIndex); - - if (pInlineConstantData == nullptr) - continue; - - // Map the buffer and copy the data - void* pMappedData = nullptr; - Ctx.MapBuffer(InlineCBAttr.pBuffer, MAP_WRITE, MAP_FLAG_DISCARD, pMappedData); - if (pMappedData != nullptr) - { - memcpy(pMappedData, pInlineConstantData, - InlineCBAttr.NumConstants * sizeof(Uint32)); - Ctx.UnmapBuffer(InlineCBAttr.pBuffer, MAP_WRITE); - } - } + const void* pData = ResourceCache.GetPushConstantDataPtr(PushConstantBufferIdx++); + Ctx.SetPushConstants(pData, 0, DataSize); } -``` - -### Step 5: Handling ArraySize for ShaderResourceVariableDesc - -When inline constants are specified via `ShaderResourceVariableDesc` (not `PipelineResourceDesc`), the `ArraySize` must be determined from SPIR-V: - -**File**: `PipelineStateVkImpl.cpp` - `GetDefaultResourceSignatureDesc()` - -```cpp -// For inline constants, ArraySize specifies the number of 32-bit constants, -// not the array dimension. Calculate it from the buffer size. -Uint32 ArraySize = Attribs.ArraySize; -if ((Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0) +else if (InlineCBAttr.pBuffer) { - // For inline constants specified via ShaderResourceVariableDesc, - // the SPIRV ArraySize is 1 (buffer is not an array), but we need - // the number of 32-bit constants which is BufferStaticSize / sizeof(Uint32) - if (Attribs.BufferStaticSize > 0) - { - ArraySize = Attribs.BufferStaticSize / sizeof(Uint32); - } - else - { - LOG_ERROR_AND_THROW("Resource '", Attribs.Name, "' is marked as inline constants, " - "but has zero buffer size."); - } + void* pMappedData = nullptr; + Ctx.MapBuffer(InlineCBAttr.pBuffer, MAP_WRITE, MAP_FLAG_DISCARD, pMappedData); + memcpy(pMappedData, pInlineConstantData, DataSize); + Ctx.UnmapBuffer(InlineCBAttr.pBuffer, MAP_WRITE); } ``` -### Multiple Inline Constants Support - -Vulkan backend fully supports multiple inline constant buffers: - -- Each inline constant resource creates its own `USAGE_DYNAMIC` uniform buffer -- Each has separate CPU-side staging buffer (`pInlineConstantData`) -- All inline constant buffers are updated together during `PrepareForDraw`/`PrepareForDispatch` +- `DeviceContextVkImpl::SetPushConstants()` copies the data into an internal `std::array` buffer and marks it dirty (`Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp:454-480`). +- After descriptor sets are committed, `CommitPushConstants()` checks whether the current pipeline layout exposes push constants and issues `vkCmdPushConstants` with the stored stage flags (`Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp:482-501`). Draw, dispatch, and trace entry points call `CommitPushConstants()` right after `CommitDescriptorSets`. +- SRBs that only contain push constants no longer trigger descriptor-set binding (`CommitDescriptorSets()` filters them out at lines `503-521`). -**Example**: +### Step 5: Example Usage -```cpp +``` PipelineResourceDesc Resources[] = { - {"TransformCB", SHADER_TYPE_VERTEX, 16, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, - SHADER_RESOURCE_VARIABLE_TYPE_STATIC, PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS}, - {"MaterialCB", SHADER_TYPE_PIXEL, 8, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, - SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC, PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS}, + // True push constant block declared with layout(push_constant) + {"PerFramePC", SHADER_TYPE_VERTEX | SHADER_TYPE_PIXEL, 32, + SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_STATIC, + PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS | PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT}, + + // Emulated inline constant buffer bound through a uniform buffer + {"PerDrawInlineCB", SHADER_TYPE_VERTEX, 16, + SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC, + PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS}, }; ``` -This creates two separate dynamic uniform buffers. - -### Memory Leak Prevention +`PerFramePC` is copied directly with `vkCmdPushConstants`, while `PerDrawInlineCB` maps the internal dynamic uniform buffer each time `UpdateInlineConstantBuffers()` runs. -The implementation carefully manages memory to prevent leaks: +### Memory Management -1. **Static inline constants**: - - Single allocation in `CreateSetLayouts()` stored in `m_pStaticInlineConstantData` - - Freed in `PipelineResourceSignatureVkImpl::Destruct()` +1. **Static inline constants / push constants** + - Single allocation in `CreateSetLayouts()` stored inside `m_pStaticInlineConstantData`. + - Freed by `PipelineResourceSignatureVkImpl::Destruct()`. + - Push constants also stash the pointer inside `InlineConstantBufferAttribsVk::pPushConstantData`. -2. **SRB inline constants**: - - Single allocation in `InitSRBResourceCache()` - - Ownership transferred to `ShaderResourceCacheVk::m_pInlineConstantMemory` - - Automatically freed by `std::unique_ptr` in `~ShaderResourceCacheVk()` +2. **SRB inline constants** + - Single block per SRB, allocated in `InitSRBResourceCache()` and owned by `ShaderResourceCacheVk::m_pInlineConstantMemory`. + - Push constants additionally keep an array of pointers (`m_pPushConstantDataPtrs`) managed by `InitializePushConstantDataPtrs()` / `SetPushConstantDataPtr()`. ### Performance Considerations -**Vulkan Inline Constants Performance**: +- Push constants avoid the `Map`/`Unmap` cost entirely—`DeviceContextVkImpl` simply memcpy's into a small CPU buffer before issuing `vkCmdPushConstants`. +- Emulated inline constants still incur a map/discard write, so they are best suited for larger constant blocks or when more than one inline constant buffer is needed. +- SRBs sharing the internal uniform buffers keeps memory usage small even when every SRB uses emulation. + +--- -1. **Overhead**: Similar to D3D11 due to `Map`/`Unmap` operations -2. **Update frequency**: Best for per-draw updates -3. **Buffer sharing**: All SRBs share the same buffer, reducing memory -4. **Future optimization**: True push constants for `layout(push_constant)` blocks (planned) --- @@ -1002,29 +811,28 @@ pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "Constants") ### Vulkan -- Currently emulated using `USAGE_DYNAMIC` uniform buffers -- Requires `MapBuffer` + `memcpy` + `UnmapBuffer` for each update -- Buffer shared across all SRBs (reduces memory, increases update frequency) -- Performance: Similar to D3D11, moderate overhead due to Map/Unmap -- Future: True push constants for `layout(push_constant)` blocks (planned) +- Resources flagged with `PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT` use `vkCmdPushConstants` (no descriptor binding, no mapping). +- All other inline constants are emulated through internal `USAGE_DYNAMIC` uniform buffers that are shared by every SRB. +- Map/Unmap is only required for the emulated path; push constants merely copy into a small CPU buffer right before `vkCmdPushConstants`. +- Performance: push-constant path is comparable to D3D12 root constants, emulated path matches the D3D11 behavior. ### Performance Comparison -| Backend | Implementation | Update Cost | Best Use Case | -|---------|---------------------|-------------|----------------------------------| -| D3D12 | Root constants | Very Low | Per-draw, high-frequency updates | -| D3D11 | Dynamic CB + Map | Moderate | Per-draw, moderate frequency | -| Vulkan | Dynamic UB + Map | Moderate | Per-draw, moderate frequency | +| Backend | Implementation | Update Cost | Best Use Case | +|---------|-------------------------------------|----------------|--------------------------------------------| +| D3D12 | Root constants | Very Low | Per-draw, high-frequency updates | +| D3D11 | Dynamic CB + Map | Moderate | Per-draw, moderate frequency | +| Vulkan | Push constants / Dynamic UB + Map | Low / Moderate | Push constants <=256 B, UB for larger data | ### General Guidelines -1. **Size**: Keep inline constants small (typically ≤ 128 bytes) +1. **Size**: Keep inline constants small (typically <= 128 bytes) 2. **Frequency**: Use for per-draw or per-dispatch data 3. **Fallback**: For larger data, use dynamic uniform buffers 4. **Multiple resources**: - D3D12: Full support for multiple inline constants - D3D11: Full support for multiple inline constants (each as separate dynamic buffer) - - Vulkan: Full support for multiple inline constants (each as separate dynamic uniform buffer) + - Vulkan: One push constant block per pipeline + unlimited emulated inline buffers via dynamic uniform buffers 5. **D3D11 specific**: Use `DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT` when constants unchanged between draws --- From 75ad2c3c889c1d1e585094e134dc9ebfe6ecbe64 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sun, 7 Dec 2025 18:39:30 +0800 Subject: [PATCH 048/104] update doc --- doc/SetInlineConstants.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/doc/SetInlineConstants.md b/doc/SetInlineConstants.md index ac7162428b..d0149344de 100644 --- a/doc/SetInlineConstants.md +++ b/doc/SetInlineConstants.md @@ -818,11 +818,11 @@ pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "Constants") ### Performance Comparison -| Backend | Implementation | Update Cost | Best Use Case | -|---------|-------------------------------------|----------------|--------------------------------------------| -| D3D12 | Root constants | Very Low | Per-draw, high-frequency updates | -| D3D11 | Dynamic CB + Map | Moderate | Per-draw, moderate frequency | -| Vulkan | Push constants / Dynamic UB + Map | Low / Moderate | Push constants <=256 B, UB for larger data | +| Backend | Implementation | Update Cost | +|---------|-------------------------------------|----------------| +| D3D12 | Root constants | Very Low | +| D3D11 | Dynamic CB + Map | Moderate | +| Vulkan | Push constants / Dynamic UB + Map | Low / Moderate | ### General Guidelines @@ -831,9 +831,8 @@ pSRB->GetVariableByName(SHADER_TYPE_VERTEX, "Constants") 3. **Fallback**: For larger data, use dynamic uniform buffers 4. **Multiple resources**: - D3D12: Full support for multiple inline constants - - D3D11: Full support for multiple inline constants (each as separate dynamic buffer) + - D3D11: Full support for multiple inline constants (emulated with separate dynamic buffer) - Vulkan: One push constant block per pipeline + unlimited emulated inline buffers via dynamic uniform buffers -5. **D3D11 specific**: Use `DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT` when constants unchanged between draws --- From d5cbc6dcb18c851dd3d2ebc78d3792fbfd67b0e3 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sun, 7 Dec 2025 18:57:41 +0800 Subject: [PATCH 049/104] Fix testing --- .../src/ObjectCreationFailure/PRSCreationFailureTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/DiligentCoreAPITest/src/ObjectCreationFailure/PRSCreationFailureTest.cpp b/Tests/DiligentCoreAPITest/src/ObjectCreationFailure/PRSCreationFailureTest.cpp index 00948ffdf5..57df94a36f 100644 --- a/Tests/DiligentCoreAPITest/src/ObjectCreationFailure/PRSCreationFailureTest.cpp +++ b/Tests/DiligentCoreAPITest/src/ObjectCreationFailure/PRSCreationFailureTest.cpp @@ -280,7 +280,7 @@ TEST(PRSCreationFailureTest, InvalidInlineConstantsFlag) {SHADER_TYPE_PIXEL, "g_InlineConstants", 1, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_STATIC, PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS | PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS}}; PRSDesc.Resources = Resources; PRSDesc.NumResources = _countof(Resources); - TestCreatePRSFailure(PRSDesc, "INLINE_CONSTANTS flag cannot be combined with other flags"); + TestCreatePRSFailure(PRSDesc, "INLINE_CONSTANTS flag cannot be combined with flags other than VULKAN_PUSH_CONSTANT"); } TEST(PRSCreationFailureTest, InvalidInlineConstantCount) From 1844c676281ca966e204a923424ecc6a80806a3d Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sun, 7 Dec 2025 21:39:11 +0800 Subject: [PATCH 050/104] enable testing for vulkan --- Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp b/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp index 729d421603..22df16baa5 100644 --- a/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp +++ b/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp @@ -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(); } @@ -744,7 +744,7 @@ TEST_F(InlineConstants, RenderStateCache) bool PresentInCache = pData != nullptr; #if !DILIGENT_DEBUG - if (pDevice->GetDeviceInfo().IsD3DDevice()) + if (pDevice->GetDeviceInfo().IsD3DDevice() || pDevice->GetDeviceInfo().IsVulkanDevice()) { // For some reason, hash computation consistency depends on D3DCOMPILE_DEBUG flag and differs between debug and release builds PresentInCache = true; From 869683a92b8fed8b38338e92c82142929e9fc3e8 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sun, 7 Dec 2025 22:45:02 +0800 Subject: [PATCH 051/104] Don't update revision, aligning to D3D11 inline constant behavior. --- Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp index c1ced3b95d..bbee15b9ac 100644 --- a/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp +++ b/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp @@ -969,8 +969,6 @@ void ShaderResourceCacheVk::SetInlineConstants(Uint32 DescrSetIndex, // Copy to CPU-side staging buffer Uint32* pDstConstants = reinterpret_cast(DstRes.pInlineConstantData); memcpy(pDstConstants + FirstConstant, pConstants, NumConstants * sizeof(Uint32)); - - UpdateRevision(); } const void* ShaderResourceCacheVk::GetInlineConstantData(Uint32 DescrSetIndex, Uint32 BindingIndex) const From 1307ba8c31c85afdd855e74bac52dc9210014372 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sun, 7 Dec 2025 23:22:32 +0800 Subject: [PATCH 052/104] Fix m_HasInlineConstant not set when PRS restored from Render State Cache. --- .../include/ShaderResourceCacheVk.hpp | 8 ++++++++ .../src/PipelineResourceSignatureVkImpl.cpp | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp b/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp index e1173ad002..9e642e6aee 100644 --- a/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp +++ b/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp @@ -303,6 +303,14 @@ class ShaderResourceCacheVk : public ShaderResourceCacheBase } } + // Explicitly marks that this cache contains inline constants. + // This is useful when inline constant memory is initialized externally + // (e.g., during SRB cache setup) before any data is written. + void MarkHasInlineConstants() + { + m_HasInlineConstants = 1; + } + Uint32 GetNumPushConstantBuffers() const { return m_NumPushConstantBuffers; } Uint32 GetNumDescriptorSets() const { return m_NumSets; } diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp index cba585e203..faf84c9677 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp @@ -684,6 +684,10 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk // For emulated inline constants, data is stored in the descriptor set resources if (m_NumInlineConstantBuffers > 0) { + // Ensure the cache reports inline constants so DeviceContextVkImpl + // updates emulated buffers even if no data has been written yet. + ResourceCache.MarkHasInlineConstants(); + // Count push constant buffers and calculate total memory size Uint32 NumPushConstantBuffers = 0; Uint32 TotalInlineConstantSize = 0; From c4856ba1078aefdbaa47bc91394864b93cfe5718 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sun, 7 Dec 2025 23:35:37 +0800 Subject: [PATCH 053/104] use vk staging buffer instead of dynamic buffer to avoid dynamic buffer id reuse issue. --- .../src/PipelineResourceSignatureVkImpl.cpp | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp index faf84c9677..5969f9e1d1 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp @@ -417,7 +417,10 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) if (!IsPushConstant && m_pDevice) { - // Create a USAGE_DYNAMIC uniform buffer for emulating inline constants + // Create a USAGE_STAGING uniform buffer for emulating inline constants. + // Using USAGE_STAGING ensures the buffer has a backing VkBuffer resource with + // persistently mapped host-visible memory, avoiding Dynamic Buffer ID reuse issues + // that can occur with USAGE_DYNAMIC buffers (which suballocate from dynamic heap). // All SRBs created from this signature will share the same inline constant buffer. std::string Name = m_Desc.Name; Name += " - "; @@ -425,7 +428,7 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) BufferDesc CBDesc; CBDesc.Name = Name.c_str(); CBDesc.Size = ResDesc.ArraySize * sizeof(Uint32); - CBDesc.Usage = USAGE_DYNAMIC; + CBDesc.Usage = USAGE_STAGING; CBDesc.BindFlags = BIND_UNIFORM_BUFFER; CBDesc.CPUAccessFlags = CPU_ACCESS_WRITE; @@ -686,7 +689,7 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk { // Ensure the cache reports inline constants so DeviceContextVkImpl // updates emulated buffers even if no data has been written yet. - ResourceCache.MarkHasInlineConstants(); + //ResourceCache.MarkHasInlineConstants(); // Count push constant buffers and calculate total memory size Uint32 NumPushConstantBuffers = 0; @@ -1340,11 +1343,12 @@ void PipelineResourceSignatureVkImpl::UpdateInlineConstantBuffers(const ShaderRe const void* pInlineConstantData = ResourceCache.GetInlineConstantData(InlineCBAttr.DescrSet, InlineCBAttr.BindingIndex); VERIFY_EXPR(pInlineConstantData != nullptr); - // Map the buffer and copy the data - void* pMappedData = nullptr; - Ctx.MapBuffer(InlineCBAttr.pBuffer, MAP_WRITE, MAP_FLAG_DISCARD, pMappedData); - memcpy(pMappedData, pInlineConstantData, DataSize); - Ctx.UnmapBuffer(InlineCBAttr.pBuffer, MAP_WRITE); + // Copy data directly to the buffer's CPU address. + // USAGE_STAGING buffer has a backing VkBuffer resource with persistently mapped + // host-visible memory, so we can write directly without Map/Unmap. + void* pDstData = InlineCBAttr.pBuffer->GetCPUAddress(); + VERIFY_EXPR(pDstData != nullptr); + memcpy(pDstData, pInlineConstantData, DataSize); continue; } } From 9f796b627af1780ce4a8c936c3324d3e88bbb690 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Sun, 7 Dec 2025 23:43:08 +0800 Subject: [PATCH 054/104] Restore dynamic buffer, and skip buffer id verification --- .../src/PipelineResourceSignatureVkImpl.cpp | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp index 5969f9e1d1..3572976739 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp @@ -417,18 +417,17 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) if (!IsPushConstant && m_pDevice) { - // Create a USAGE_STAGING uniform buffer for emulating inline constants. - // Using USAGE_STAGING ensures the buffer has a backing VkBuffer resource with - // persistently mapped host-visible memory, avoiding Dynamic Buffer ID reuse issues - // that can occur with USAGE_DYNAMIC buffers (which suballocate from dynamic heap). + // Create a USAGE_DYNAMIC uniform buffer for emulating inline constants. // All SRBs created from this signature will share the same inline constant buffer. + // Note: DvpValidateCommittedResource skips dynamic allocation verification for + // inline constant buffers to avoid Dynamic Buffer ID reuse validation issues. std::string Name = m_Desc.Name; Name += " - "; Name += ResDesc.Name; BufferDesc CBDesc; CBDesc.Name = Name.c_str(); CBDesc.Size = ResDesc.ArraySize * sizeof(Uint32); - CBDesc.Usage = USAGE_STAGING; + CBDesc.Usage = USAGE_DYNAMIC; CBDesc.BindFlags = BIND_UNIFORM_BUFFER; CBDesc.CPUAccessFlags = CPU_ACCESS_WRITE; @@ -689,7 +688,7 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk { // Ensure the cache reports inline constants so DeviceContextVkImpl // updates emulated buffers even if no data has been written yet. - //ResourceCache.MarkHasInlineConstants(); + ResourceCache.MarkHasInlineConstants(); // Count push constant buffers and calculate total memory size Uint32 NumPushConstantBuffers = 0; @@ -1198,7 +1197,16 @@ bool PipelineResourceSignatureVkImpl::DvpValidateCommittedResource(const DeviceC // is bound. It will be null if the type is incorrect. if (const BufferVkImpl* pBufferVk = Res.pObject.RawPtr()) { - 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. + // The Dynamic Buffer ID may be reused after PSO recreation (e.g., from cache), + // which would cause false validation failures. + const bool IsInlineConstantBuffer = (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0; + if (!IsInlineConstantBuffer) + { + pDeviceCtx->DvpVerifyDynamicAllocation(pBufferVk); + } if ((pBufferVk->GetDesc().Size < SPIRVAttribs.BufferStaticSize) && (GetDevice()->GetValidationFlags() & VALIDATION_FLAG_CHECK_SHADER_BUFFER_SIZE) != 0) @@ -1343,12 +1351,11 @@ void PipelineResourceSignatureVkImpl::UpdateInlineConstantBuffers(const ShaderRe const void* pInlineConstantData = ResourceCache.GetInlineConstantData(InlineCBAttr.DescrSet, InlineCBAttr.BindingIndex); VERIFY_EXPR(pInlineConstantData != nullptr); - // Copy data directly to the buffer's CPU address. - // USAGE_STAGING buffer has a backing VkBuffer resource with persistently mapped - // host-visible memory, so we can write directly without Map/Unmap. - void* pDstData = InlineCBAttr.pBuffer->GetCPUAddress(); - VERIFY_EXPR(pDstData != nullptr); - memcpy(pDstData, pInlineConstantData, DataSize); + // Map the buffer and copy the data + void* pMappedData = nullptr; + Ctx.MapBuffer(InlineCBAttr.pBuffer, MAP_WRITE, MAP_FLAG_DISCARD, pMappedData); + memcpy(pMappedData, pInlineConstantData, DataSize); + Ctx.UnmapBuffer(InlineCBAttr.pBuffer, MAP_WRITE); continue; } } From e030acca06c78edf8d2e39944051352294d0c5f7 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Mon, 8 Dec 2025 11:22:28 +0800 Subject: [PATCH 055/104] Fix issue when multiple inline constants using same pInlineConstantData. --- .../src/ShaderResourceCacheVk.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp index bbee15b9ac..53ecb3cee6 100644 --- a/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp +++ b/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp @@ -975,16 +975,14 @@ const void* ShaderResourceCacheVk::GetInlineConstantData(Uint32 DescrSetIndex, U { const DescriptorSet& DescrSet = GetDescriptorSet(DescrSetIndex); - // Find the resource with the matching binding index - for (Uint32 i = 0; i < DescrSet.GetSize(); ++i) + VERIFY(BindingIndex < DescrSet.GetSize(), "BindingIndex out of bounds"); + + const Resource& Res = DescrSet.GetResource(BindingIndex); + if (Res.pInlineConstantData != nullptr) { - const Resource& Res = DescrSet.GetResource(i); - if (Res.pInlineConstantData != nullptr) - { - // For now, we assume binding index matches the resource index for inline constants - // This may need adjustment based on actual binding layout - return Res.pInlineConstantData; - } + // For now, we assume binding index matches the resource index for inline constants + // This may need adjustment based on actual binding layout + return Res.pInlineConstantData; } return nullptr; From 03090870a69bd4529228d5d8d92a312e0ec30694 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Mon, 8 Dec 2025 11:24:18 +0800 Subject: [PATCH 056/104] fix formatting --- Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp index 53ecb3cee6..ff643657b2 100644 --- a/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp +++ b/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp @@ -977,7 +977,7 @@ const void* ShaderResourceCacheVk::GetInlineConstantData(Uint32 DescrSetIndex, U VERIFY(BindingIndex < DescrSet.GetSize(), "BindingIndex out of bounds"); - const Resource& Res = DescrSet.GetResource(BindingIndex); + const Resource& Res = DescrSet.GetResource(BindingIndex); if (Res.pInlineConstantData != nullptr) { // For now, we assume binding index matches the resource index for inline constants From cbe779bfa58161b4037cf0a9c5829c3657b63ecc Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Mon, 8 Dec 2025 12:01:10 +0800 Subject: [PATCH 057/104] Fix PipelineResourceSignatureVkImpl::UpdateInlineConstantBuffers not updating dynamic buffer correctly. --- .../include/ShaderResourceCacheVk.hpp | 2 +- .../src/PipelineResourceSignatureVkImpl.cpp | 11 +++++++++-- .../src/ShaderResourceCacheVk.cpp | 8 +++----- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp b/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp index 9e642e6aee..945206a4c8 100644 --- a/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp +++ b/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp @@ -258,7 +258,7 @@ class ShaderResourceCacheVk : public ShaderResourceCacheBase Uint32 NumConstants); // Gets the inline constant data pointer from the resource cache - const void* GetInlineConstantData(Uint32 DescrSetIndex, Uint32 BindingIndex) const; + const void* GetInlineConstantData(Uint32 DescrSetIndex, Uint32 CacheOffset) const; // Initialize inline constant buffer in the resource cache void InitializeInlineConstantBuffer(Uint32 DescrSetIndex, diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp index 3572976739..42a1f00619 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp @@ -1324,6 +1324,10 @@ PipelineResourceSignatureInternalDataVk PipelineResourceSignatureVkImpl::GetInte void PipelineResourceSignatureVkImpl::UpdateInlineConstantBuffers(const ShaderResourceCacheVk& ResourceCache, DeviceContextVkImpl& Ctx) const { + // Determine the cache type based on the resource cache content + // SRB caches use SRBCacheOffset, static caches use StaticCacheOffset + const ResourceCacheContentType CacheType = ResourceCache.GetContentType(); + Uint32 PushConstantBufferIdx = 0; for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) { @@ -1347,8 +1351,11 @@ void PipelineResourceSignatureVkImpl::UpdateInlineConstantBuffers(const ShaderRe if (InlineCBAttr.pBuffer) { - // For emulated inline constants, get data from the resource cache - const void* pInlineConstantData = ResourceCache.GetInlineConstantData(InlineCBAttr.DescrSet, InlineCBAttr.BindingIndex); + // For emulated inline constants, get data from the resource cache using the correct CacheOffset + // Similar to D3D11 backend which uses BindPoints to locate the resource + const ResourceAttribs& Attr = GetResourceAttribs(InlineCBAttr.ResIndex); + const Uint32 CacheOffset = Attr.CacheOffset(CacheType); + const void* pInlineConstantData = ResourceCache.GetInlineConstantData(Attr.DescrSet, CacheOffset); VERIFY_EXPR(pInlineConstantData != nullptr); // Map the buffer and copy the data diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp index ff643657b2..94a4bd529c 100644 --- a/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp +++ b/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp @@ -971,17 +971,15 @@ void ShaderResourceCacheVk::SetInlineConstants(Uint32 DescrSetIndex, memcpy(pDstConstants + FirstConstant, pConstants, NumConstants * sizeof(Uint32)); } -const void* ShaderResourceCacheVk::GetInlineConstantData(Uint32 DescrSetIndex, Uint32 BindingIndex) const +const void* ShaderResourceCacheVk::GetInlineConstantData(Uint32 DescrSetIndex, Uint32 CacheOffset) const { const DescriptorSet& DescrSet = GetDescriptorSet(DescrSetIndex); - VERIFY(BindingIndex < DescrSet.GetSize(), "BindingIndex out of bounds"); + VERIFY(CacheOffset < DescrSet.GetSize(), "CacheOffset out of bounds"); - const Resource& Res = DescrSet.GetResource(BindingIndex); + const Resource& Res = DescrSet.GetResource(CacheOffset); if (Res.pInlineConstantData != nullptr) { - // For now, we assume binding index matches the resource index for inline constants - // This may need adjustment based on actual binding layout return Res.pInlineConstantData; } From f523086e92b87f6ed241500de70dabde16dac31e Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Mon, 8 Dec 2025 12:03:40 +0800 Subject: [PATCH 058/104] fix formatting --- .../src/PipelineResourceSignatureVkImpl.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp index 42a1f00619..5342f78216 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp @@ -1353,9 +1353,9 @@ void PipelineResourceSignatureVkImpl::UpdateInlineConstantBuffers(const ShaderRe { // For emulated inline constants, get data from the resource cache using the correct CacheOffset // Similar to D3D11 backend which uses BindPoints to locate the resource - const ResourceAttribs& Attr = GetResourceAttribs(InlineCBAttr.ResIndex); - const Uint32 CacheOffset = Attr.CacheOffset(CacheType); - const void* pInlineConstantData = ResourceCache.GetInlineConstantData(Attr.DescrSet, CacheOffset); + const ResourceAttribs& Attr = GetResourceAttribs(InlineCBAttr.ResIndex); + const Uint32 CacheOffset = Attr.CacheOffset(CacheType); + const void* pInlineConstantData = ResourceCache.GetInlineConstantData(Attr.DescrSet, CacheOffset); VERIFY_EXPR(pInlineConstantData != nullptr); // Map the buffer and copy the data From e2bbc2a17aa310e4de82099d525e37f4a3208820 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Mon, 8 Dec 2025 12:04:32 +0800 Subject: [PATCH 059/104] Fix https://github.com/DiligentGraphics/DiligentCore/issues/730 --- .../DiligentCoreAPITest/src/InlineConstantsTest.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp b/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp index 22df16baa5..2631c49b66 100644 --- a/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp +++ b/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp @@ -341,8 +341,8 @@ 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_mut", 1u, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE) + .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, PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS) .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) .AddResource(SHADER_TYPE_VERTEX, "tex0_mut", SHADER_RESOURCE_TYPE_TEXTURE_SRV, SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE) @@ -360,8 +360,8 @@ 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) - .AddResource(SHADER_TYPE_VERTEX, "cb1_mut", 1u, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE) + 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, PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS) .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) .AddResource(SHADER_TYPE_VERTEX, "tex1_mut", SHADER_RESOURCE_TYPE_TEXTURE_SRV, SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE) @@ -369,8 +369,8 @@ 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_mut", 1u, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE) + .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, PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS) .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) .AddResource(SHADER_TYPE_VERTEX, "tex2_mut", SHADER_RESOURCE_TYPE_TEXTURE_SRV, SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE) From 1a25e22326e2001ef03285118309702492c7c79b Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Mon, 8 Dec 2025 13:24:31 +0800 Subject: [PATCH 060/104] Move pBuffer and pPushConstantData from PipelineResourceSignatureVkImpl to ShaderResourceCacheVk as per-SRB --- .../PipelineResourceSignatureVkImpl.hpp | 25 ++- .../include/ShaderResourceCacheVk.hpp | 32 ++++ .../src/PipelineResourceSignatureVkImpl.cpp | 175 +++++++++++------- .../src/ShaderVariableManagerVk.cpp | 54 ++---- 4 files changed, 172 insertions(+), 114 deletions(-) diff --git a/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp index 8399bfcc5a..92c4b11186 100644 --- a/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp @@ -63,15 +63,19 @@ ASSERT_SIZEOF(ImmutableSamplerAttribsVk, 8, "The struct is used in serialization /// Inline constants can be either: /// 1. True push constants (from SPIR-V push_constant storage class) - use vkCmdPushConstants /// 2. Emulated inline constants - use dynamic uniform buffers (similar to D3D11 backend) +/// +/// Note: This structure only stores attributes/metadata. The actual storage (pBuffer for emulated +/// inline constants, push constant data for true push constants) is managed per-SRB in +/// ShaderResourceCacheVk to avoid conflicts between multiple PipelineStates sharing the same +/// PipelineResourceSignature. struct InlineConstantBufferAttribsVk { - Uint32 ResIndex = 0; // Resource index in the signature (used for matching) - Uint32 DescrSet = 0; // Descriptor set index (0 for push constants as placeholder) - Uint32 BindingIndex = 0; // Binding index within the descriptor set - Uint32 NumConstants = 0; // Number of 32-bit constants - bool IsPushConstant = false; // True if this is a Vulkan push constant (not emulated) - RefCntAutoPtr pBuffer; // Internal dynamic uniform buffer (null for true push constants) - void* pPushConstantData = nullptr; // CPU-side staging buffer for push constants (only used when IsPushConstant is true) + Uint32 ResIndex = 0; // Resource index in the signature (used for matching) + Uint32 DescrSet = 0; // Descriptor set index (0 for push constants as placeholder) + Uint32 BindingIndex = 0; // Binding index within the descriptor set + Uint32 NumConstants = 0; // Number of 32-bit constants + bool IsPushConstant = false; // True if this is a Vulkan push constant (not emulated) + // Note: pBuffer and pPushConstantData are now stored per-SRB in ShaderResourceCacheVk }; struct PipelineResourceSignatureInternalDataVk : PipelineResourceSignatureInternalData @@ -231,9 +235,10 @@ class PipelineResourceSignatureVkImpl final : public PipelineResourceSignatureBa // Inline constant buffer attributes std::unique_ptr m_InlineConstantBuffers; - // Pointer to CPU-side staging buffer for static inline constants - // This memory is allocated in CreateSetLayouts and must be freed in Destruct - void* m_pStaticInlineConstantData = nullptr; + // Note: Static inline constant data (including push constants) is stored in m_pStaticResCache. + // For push constants, we use InitializePushConstantDataPtrs() and SetPushConstantDataPtr() + // in the static resource cache, similar to how SRB caches store push constant data. + // This ensures each SRB gets its own copy when CopyStaticResources() is called. }; template <> Uint32 PipelineResourceSignatureVkImpl::GetDescriptorSetIndex() const; diff --git a/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp b/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp index 945206a4c8..7a24da3362 100644 --- a/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp +++ b/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp @@ -313,6 +313,32 @@ class ShaderResourceCacheVk : public ShaderResourceCacheBase Uint32 GetNumPushConstantBuffers() const { return m_NumPushConstantBuffers; } + // Initializes inline constant buffers array + void InitializeInlineConstantBuffers(Uint32 NumBuffers) + { + m_NumInlineConstantBuffers = static_cast(NumBuffers); + if (NumBuffers > 0) + { + m_pInlineConstantBuffers = std::make_unique[]>(NumBuffers); + } + } + + // Sets the inline constant buffer at the given index + void SetInlineConstantBuffer(Uint32 Index, RefCntAutoPtr&& pBuffer) + { + VERIFY_EXPR(Index < m_NumInlineConstantBuffers); + m_pInlineConstantBuffers[Index] = std::move(pBuffer); + } + + // Gets the inline constant buffer at the given index + BufferVkImpl* GetInlineConstantBuffer(Uint32 Index) const + { + VERIFY_EXPR(Index < m_NumInlineConstantBuffers); + return m_pInlineConstantBuffers[Index].RawPtr(); + } + + Uint32 GetNumInlineConstantBuffers() const { return m_NumInlineConstantBuffers; } + Uint32 GetNumDescriptorSets() const { return m_NumSets; } bool HasDynamicResources() const { return m_NumDynamicBuffers > 0; } @@ -361,6 +387,12 @@ class ShaderResourceCacheVk : public ShaderResourceCacheBase // Each pointer points to memory within m_pInlineConstantMemory std::unique_ptr m_pPushConstantDataPtrs; + // Array of inline constant buffers (one per emulated inline constant buffer) + // Each SRB has its own copy of these buffers to avoid conflicts between + // multiple PipelineStates sharing the same PipelineResourceSignature + std::unique_ptr[]> m_pInlineConstantBuffers; + Uint16 m_NumInlineConstantBuffers = 0; + Uint16 m_NumSets = 0; Uint16 m_NumPushConstantBuffers = 0; diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp index 5342f78216..14329e6b63 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp @@ -206,7 +206,8 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) // Vulkan push constants don't use descriptor sets - they use vkCmdPushConstants directly. // So we don't count them as bindings for descriptor set layout creation. - // They also don't need cache space since their data is stored in InlineCBAttr.pPushConstantData. + // They also don't need descriptor set cache space since their data is stored in + // the resource cache via SetPushConstantDataPtr(). const bool IsPushConstant = (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != 0; if (!IsPushConstant) { @@ -412,31 +413,9 @@ 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.IsPushConstant = IsPushConstant; - - if (!IsPushConstant && m_pDevice) - { - // Create a USAGE_DYNAMIC uniform buffer for emulating inline constants. - // All SRBs created from this signature will share the same inline constant buffer. - // Note: DvpValidateCommittedResource skips dynamic allocation verification for - // inline constant buffers to avoid Dynamic Buffer ID reuse validation issues. - std::string Name = m_Desc.Name; - Name += " - "; - Name += ResDesc.Name; - BufferDesc CBDesc; - CBDesc.Name = Name.c_str(); - CBDesc.Size = ResDesc.ArraySize * sizeof(Uint32); - CBDesc.Usage = USAGE_DYNAMIC; - CBDesc.BindFlags = BIND_UNIFORM_BUFFER; - CBDesc.CPUAccessFlags = CPU_ACCESS_WRITE; - - RefCntAutoPtr pBuffer; - m_pDevice->CreateBuffer(CBDesc, nullptr, &pBuffer); - VERIFY_EXPR(pBuffer); - InlineCBAttribs.pBuffer = RefCntAutoPtr{pBuffer, IID_BufferVk}; - } - // For true push constants, pBuffer remains null - data is submitted via vkCmdPushConstants + InlineCBAttribs.IsPushConstant = IsPushConstant; + // Note: Per-SRB buffers are created in InitSRBResourceCache() to avoid conflicts + // between multiple PipelineStates sharing the same PipelineResourceSignature } } VERIFY_EXPR(InlineConstantBufferIdx == m_NumInlineConstantBuffers); @@ -577,10 +556,12 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) // Initialize inline constant buffers in the static resource cache // This must be done after the resources are initialized above - if (m_NumInlineConstantBuffers > 0) + if (m_NumInlineConstantBuffers > 0 && m_pStaticResCache != nullptr) { // First pass: calculate total memory size needed for all static inline constants + // and count static push constant buffers Uint32 TotalStaticInlineConstantSize = 0; + Uint32 NumStaticPushConstantBuffers = 0; for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) { const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; @@ -589,20 +570,33 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) if (ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) { TotalStaticInlineConstantSize += InlineCBAttr.NumConstants * sizeof(Uint32); + if (InlineCBAttr.IsPushConstant) + ++NumStaticPushConstantBuffers; } } - // Allocate single memory block for all static inline constants + // Initialize push constant data pointers array in the static resource cache + if (NumStaticPushConstantBuffers > 0) + { + m_pStaticResCache->InitializePushConstantDataPtrs(NumStaticPushConstantBuffers); + } + + // Allocate memory for all static inline constants in the static resource cache if (TotalStaticInlineConstantSize > 0) { - m_pStaticInlineConstantData = GetRawAllocator().Allocate(TotalStaticInlineConstantSize, "Static inline constant data", __FILE__, __LINE__); - memset(m_pStaticInlineConstantData, 0, TotalStaticInlineConstantSize); + IMemoryAllocator& Allocator = GetRawAllocator(); + void* pInlineConstMemory = Allocator.Allocate(TotalStaticInlineConstantSize, "Static inline constant data", __FILE__, __LINE__); + memset(pInlineConstMemory, 0, TotalStaticInlineConstantSize); + + // Pass ownership of the memory to the static resource cache for proper cleanup + m_pStaticResCache->SetInlineConstantMemory(Allocator, pInlineConstMemory); // Second pass: assign memory to each static inline constant - Uint8* pCurrentDataPtr = static_cast(m_pStaticInlineConstantData); + Uint8* pCurrentDataPtr = static_cast(pInlineConstMemory); + Uint32 StaticPushConstantIdx = 0; for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) { - InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; + const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; const PipelineResourceDesc& ResDesc = GetResourceDesc(InlineCBAttr.ResIndex); const ResourceAttribs& Attr = GetResourceAttribs(InlineCBAttr.ResIndex); @@ -613,10 +607,10 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) if (InlineCBAttr.IsPushConstant) { - // For push constants, store the data pointer directly - InlineCBAttr.pPushConstantData = pCurrentDataPtr; + // For push constants, store the data pointer in the static resource cache + m_pStaticResCache->SetPushConstantDataPtr(StaticPushConstantIdx++, pCurrentDataPtr); } - else if (m_pStaticResCache != nullptr) + else { // For emulated inline constants, initialize in the static resource cache const Uint32 CacheOffset = Attr.StaticCacheOffset; @@ -642,12 +636,8 @@ void PipelineResourceSignatureVkImpl::Destruct() GetDevice()->SafeReleaseDeviceObject(std::move(Layout), ~0ull); } - // Free static inline constant data allocated in CreateSetLayouts - if (m_pStaticInlineConstantData != nullptr) - { - GetRawAllocator().Free(m_pStaticInlineConstantData); - m_pStaticInlineConstantData = nullptr; - } + // Note: Static inline constant data is now managed by m_pStaticResCache + // and will be freed when the cache is destroyed TPipelineResourceSignatureBase::Destruct(); } @@ -683,22 +673,26 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk // Initialize inline constant buffers - allocate staging memory and set up data pointers // For push constants, each SRB gets its own copy of the data (stored in ShaderResourceCacheVk) - // For emulated inline constants, data is stored in the descriptor set resources + // For emulated inline constants, each SRB gets its own buffer to avoid conflicts between + // multiple PipelineStates sharing the same PipelineResourceSignature if (m_NumInlineConstantBuffers > 0) { // Ensure the cache reports inline constants so DeviceContextVkImpl // updates emulated buffers even if no data has been written yet. ResourceCache.MarkHasInlineConstants(); - // Count push constant buffers and calculate total memory size - Uint32 NumPushConstantBuffers = 0; - Uint32 TotalInlineConstantSize = 0; + // Count push constant buffers, emulated inline constant buffers, and calculate total memory size + Uint32 NumPushConstantBuffers = 0; + Uint32 NumEmulatedInlineConstantCBs = 0; + Uint32 TotalInlineConstantSize = 0; for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) { const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; TotalInlineConstantSize += InlineCBAttr.NumConstants * sizeof(Uint32); if (InlineCBAttr.IsPushConstant) ++NumPushConstantBuffers; + else + ++NumEmulatedInlineConstantCBs; } // Initialize push constant data pointers array in the resource cache @@ -707,6 +701,12 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk ResourceCache.InitializePushConstantDataPtrs(NumPushConstantBuffers); } + // Initialize inline constant buffers array in the resource cache + if (NumEmulatedInlineConstantCBs > 0) + { + ResourceCache.InitializeInlineConstantBuffers(NumEmulatedInlineConstantCBs); + } + // Allocate memory for all inline constants void* pInlineConstantMemory = nullptr; if (TotalInlineConstantSize > 0) @@ -718,9 +718,10 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk ResourceCache.SetInlineConstantMemory(CacheMemAllocator, pInlineConstantMemory); } - // Assign memory to each inline constant buffer - Uint8* pCurrentDataPtr = static_cast(pInlineConstantMemory); - Uint32 PushConstantBufferIdx = 0; + // Assign memory to each inline constant buffer and create per-SRB buffers + Uint8* pCurrentDataPtr = static_cast(pInlineConstantMemory); + Uint32 PushConstantBufferIdx = 0; + Uint32 EmulatedInlineConstantIdx = 0; for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) { const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; @@ -735,10 +736,35 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk } else { - // For emulated inline constants, use the descriptor set resources - const ResourceAttribs& Attr = GetResourceAttribs(InlineCBAttr.ResIndex); + // For emulated inline constants, create a per-SRB buffer + const PipelineResourceDesc& ResDesc = GetResourceDesc(InlineCBAttr.ResIndex); + const ResourceAttribs& Attr = GetResourceAttribs(InlineCBAttr.ResIndex); + + // Initialize the inline constant data in the resource cache ResourceCache.InitializeInlineConstantBuffer(Attr.DescrSet, Attr.CacheOffset(CacheType), InlineCBAttr.NumConstants, pCurrentDataPtr); + + // Create a per-SRB USAGE_DYNAMIC uniform buffer + if (m_pDevice) + { + std::string Name = m_Desc.Name; + Name += " - "; + Name += ResDesc.Name; + Name += " (SRB)"; + BufferDesc CBDesc; + CBDesc.Name = Name.c_str(); + CBDesc.Size = DataSize; + CBDesc.Usage = USAGE_DYNAMIC; + CBDesc.BindFlags = BIND_UNIFORM_BUFFER; + CBDesc.CPUAccessFlags = CPU_ACCESS_WRITE; + + RefCntAutoPtr pBuffer; + m_pDevice->CreateBuffer(CBDesc, nullptr, &pBuffer); + VERIFY_EXPR(pBuffer); + ResourceCache.SetInlineConstantBuffer(EmulatedInlineConstantIdx, RefCntAutoPtr{pBuffer, IID_BufferVk}); + } + + ++EmulatedInlineConstantIdx; pCurrentDataPtr += DataSize; } } @@ -762,11 +788,20 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk // Bind internal inline constant buffers to the resource cache // This must be done after descriptor set allocation so that descriptor writes work correctly + Uint32 EmulatedInlineConstantIdx = 0; for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) { const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; - if (!InlineCBAttr.pBuffer) + // Skip push constants - they don't use descriptor sets + if (InlineCBAttr.IsPushConstant) + continue; + + // Get the per-SRB buffer from the resource cache + BufferVkImpl* pBuffer = ResourceCache.GetInlineConstantBuffer(EmulatedInlineConstantIdx); + ++EmulatedInlineConstantIdx; + + if (!pBuffer) continue; // Use ResIndex to directly access the resource attributes @@ -787,7 +822,7 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk { Attr.BindingIndex, 0, // ArrayIndex - RefCntAutoPtr{InlineCBAttr.pBuffer}, + RefCntAutoPtr{pBuffer}, 0, // BufferBaseOffset InlineCBAttr.NumConstants * sizeof(Uint32) // BufferRangeSize }); @@ -803,7 +838,7 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk { Attr.BindingIndex, 0, // ArrayIndex - RefCntAutoPtr{InlineCBAttr.pBuffer}, + RefCntAutoPtr{pBuffer}, 0, // BufferBaseOffset InlineCBAttr.NumConstants * sizeof(Uint32) // BufferRangeSize }); @@ -815,8 +850,9 @@ void PipelineResourceSignatureVkImpl::CopyStaticResources(ShaderResourceCacheVk& { // Handle STATIC push constants first - copy data from Signature's static cache to SRB's cache // Each SRB has its own copy of push constant data - const auto ResIdxRange = GetResourceIndexRange(SHADER_RESOURCE_VARIABLE_TYPE_STATIC); - Uint32 PushConstantBufferIdx = 0; + const auto ResIdxRange = GetResourceIndexRange(SHADER_RESOURCE_VARIABLE_TYPE_STATIC); + Uint32 DstPushConstantBufferIdx = 0; + Uint32 SrcPushConstantBufferIdx = 0; for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) { const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; @@ -826,10 +862,12 @@ void PipelineResourceSignatureVkImpl::CopyStaticResources(ShaderResourceCacheVk& const PipelineResourceDesc& ResDesc = GetResourceDesc(InlineCBAttr.ResIndex); if (ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) { - // Get source data from Signature's static inline constant data - const void* pSrcData = InlineCBAttr.pPushConstantData; + // Get source data from Signature's static resource cache + const void* pSrcData = m_pStaticResCache ? m_pStaticResCache->GetPushConstantDataPtr(SrcPushConstantBufferIdx) : nullptr; + ++SrcPushConstantBufferIdx; + // Get destination data pointer from SRB's resource cache - void* pDstData = DstResourceCache.GetPushConstantDataPtr(PushConstantBufferIdx); + void* pDstData = DstResourceCache.GetPushConstantDataPtr(DstPushConstantBufferIdx); if (pSrcData != nullptr && pDstData != nullptr) { @@ -837,7 +875,7 @@ void PipelineResourceSignatureVkImpl::CopyStaticResources(ShaderResourceCacheVk& memcpy(pDstData, pSrcData, DataSize); } } - ++PushConstantBufferIdx; + ++DstPushConstantBufferIdx; } if (!HasDescriptorSet(DESCRIPTOR_SET_ID_STATIC_MUTABLE) || m_pStaticResCache == nullptr) @@ -1328,7 +1366,8 @@ void PipelineResourceSignatureVkImpl::UpdateInlineConstantBuffers(const ShaderRe // SRB caches use SRBCacheOffset, static caches use StaticCacheOffset const ResourceCacheContentType CacheType = ResourceCache.GetContentType(); - Uint32 PushConstantBufferIdx = 0; + Uint32 PushConstantBufferIdx = 0; + Uint32 EmulatedInlineConstantIdx = 0; for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) { const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; @@ -1349,9 +1388,13 @@ void PipelineResourceSignatureVkImpl::UpdateInlineConstantBuffers(const ShaderRe continue; } - if (InlineCBAttr.pBuffer) + // For emulated inline constants, get the per-SRB buffer from the resource cache + BufferVkImpl* pBuffer = ResourceCache.GetInlineConstantBuffer(EmulatedInlineConstantIdx); + ++EmulatedInlineConstantIdx; + + if (pBuffer) { - // For emulated inline constants, get data from the resource cache using the correct CacheOffset + // Get data from the resource cache using the correct CacheOffset // Similar to D3D11 backend which uses BindPoints to locate the resource const ResourceAttribs& Attr = GetResourceAttribs(InlineCBAttr.ResIndex); const Uint32 CacheOffset = Attr.CacheOffset(CacheType); @@ -1360,9 +1403,9 @@ void PipelineResourceSignatureVkImpl::UpdateInlineConstantBuffers(const ShaderRe // Map the buffer and copy the data void* pMappedData = nullptr; - Ctx.MapBuffer(InlineCBAttr.pBuffer, MAP_WRITE, MAP_FLAG_DISCARD, pMappedData); + Ctx.MapBuffer(pBuffer, MAP_WRITE, MAP_FLAG_DISCARD, pMappedData); memcpy(pMappedData, pInlineConstantData, DataSize); - Ctx.UnmapBuffer(InlineCBAttr.pBuffer, MAP_WRITE); + Ctx.UnmapBuffer(pBuffer, MAP_WRITE); continue; } } @@ -1390,9 +1433,9 @@ void PipelineResourceSignatureVkImpl::UpdatePushConstantFlags(Uint32 ResIndex) if (InlineCBAttr.ResIndex == ResIndex) { // Mark this as a true push constant + // Note: Per-SRB buffers are created in InitSRBResourceCache(), which will check + // IsPushConstant and skip buffer creation for true push constants InlineCBAttr.IsPushConstant = true; - // Note: pBuffer may have been created during CreateSetLayouts, but that's OK - - // UpdateInlineConstantBuffers will check IsPushConstant first and use vkCmdPushConstants break; } } diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp index 8f5f76dc2e..91e7b96fba 100644 --- a/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp +++ b/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp @@ -669,50 +669,28 @@ void ShaderVariableManagerVk::SetInlineConstants(Uint32 ResIndex, { const InlineConstantBufferAttribsVk* pInlineCBs = m_pSignature->GetInlineConstantBuffers(); const Uint32 NumInlineCBs = m_pSignature->GetNumInlineConstantBuffers(); - const ResourceCacheContentType CacheType = m_ResourceCache.GetContentType(); - if (CacheType == ResourceCacheContentType::Signature) + // For both Signature's static cache and SRB cache, push constant data is stored + // in the resource cache via GetPushConstantDataPtr() + Uint32 PushConstantBufferIdx = 0; + for (Uint32 i = 0; i < NumInlineCBs; ++i) { - // For Signature's static variables, write to InlineCBAttr.pPushConstantData - // which points to m_pStaticInlineConstantData - for (Uint32 i = 0; i < NumInlineCBs; ++i) - { - const InlineConstantBufferAttribsVk& InlineCBAttr = pInlineCBs[i]; - if (InlineCBAttr.ResIndex == ResIndex && InlineCBAttr.IsPushConstant) - { - if (InlineCBAttr.pPushConstantData != nullptr) - { - Uint32* pDstConstants = reinterpret_cast(InlineCBAttr.pPushConstantData); - memcpy(pDstConstants + FirstConstant, pConstants, NumConstants * sizeof(Uint32)); - } - return; - } - } - } - else - { - // For SRB, write to the SRB's resource cache - // Each SRB has its own copy of push constant data - Uint32 PushConstantBufferIdx = 0; - for (Uint32 i = 0; i < NumInlineCBs; ++i) - { - const InlineConstantBufferAttribsVk& InlineCBAttr = pInlineCBs[i]; - if (!InlineCBAttr.IsPushConstant) - continue; + const InlineConstantBufferAttribsVk& InlineCBAttr = pInlineCBs[i]; + if (!InlineCBAttr.IsPushConstant) + continue; - if (InlineCBAttr.ResIndex == ResIndex) + if (InlineCBAttr.ResIndex == ResIndex) + { + // Get the data pointer from the resource cache + void* pPushConstantData = m_ResourceCache.GetPushConstantDataPtr(PushConstantBufferIdx); + if (pPushConstantData != nullptr) { - // Get the data pointer from the resource cache - void* pPushConstantData = m_ResourceCache.GetPushConstantDataPtr(PushConstantBufferIdx); - if (pPushConstantData != nullptr) - { - Uint32* pDstConstants = reinterpret_cast(pPushConstantData); - memcpy(pDstConstants + FirstConstant, pConstants, NumConstants * sizeof(Uint32)); - } - return; + Uint32* pDstConstants = reinterpret_cast(pPushConstantData); + memcpy(pDstConstants + FirstConstant, pConstants, NumConstants * sizeof(Uint32)); } - ++PushConstantBufferIdx; + return; } + ++PushConstantBufferIdx; } UNEXPECTED("Push constant buffer not found"); } From 1f18ab5b7b5c8e30329b79e20acbef0c93ca966a Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Mon, 8 Dec 2025 13:27:06 +0800 Subject: [PATCH 061/104] Fix formatting --- .../include/PipelineResourceSignatureVkImpl.hpp | 2 +- .../src/PipelineResourceSignatureVkImpl.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp index 92c4b11186..54e606e2ef 100644 --- a/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp @@ -236,7 +236,7 @@ class PipelineResourceSignatureVkImpl final : public PipelineResourceSignatureBa std::unique_ptr m_InlineConstantBuffers; // Note: Static inline constant data (including push constants) is stored in m_pStaticResCache. - // For push constants, we use InitializePushConstantDataPtrs() and SetPushConstantDataPtr() + // For push constants, we use InitializePushConstantDataPtrs() and SetPushConstantDataPtr() // in the static resource cache, similar to how SRB caches store push constant data. // This ensures each SRB gets its own copy when CopyStaticResources() is called. }; diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp index 14329e6b63..64bf3c0ef3 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp @@ -850,9 +850,9 @@ void PipelineResourceSignatureVkImpl::CopyStaticResources(ShaderResourceCacheVk& { // Handle STATIC push constants first - copy data from Signature's static cache to SRB's cache // Each SRB has its own copy of push constant data - const auto ResIdxRange = GetResourceIndexRange(SHADER_RESOURCE_VARIABLE_TYPE_STATIC); - Uint32 DstPushConstantBufferIdx = 0; - Uint32 SrcPushConstantBufferIdx = 0; + const auto ResIdxRange = GetResourceIndexRange(SHADER_RESOURCE_VARIABLE_TYPE_STATIC); + Uint32 DstPushConstantBufferIdx = 0; + Uint32 SrcPushConstantBufferIdx = 0; for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) { const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; From 6a02357485188e588f090320735cb19fcbf39855 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Mon, 8 Dec 2025 14:47:05 +0800 Subject: [PATCH 062/104] Don't apply PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS to SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE. --- Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp b/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp index 2631c49b66..c50987f31d 100644 --- a/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp +++ b/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp @@ -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, PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS) - .AddResource(SHADER_TYPE_VERTEX, "cb0_mut", 1u, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE, 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) .AddResource(SHADER_TYPE_VERTEX, "tex0_mut", SHADER_RESOURCE_TYPE_TEXTURE_SRV, SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE) @@ -361,7 +361,7 @@ void InlineConstants::TestSignatures(Uint32 NumSignatures) } 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, 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) .AddResource(SHADER_TYPE_VERTEX, "tex1_mut", SHADER_RESOURCE_TYPE_TEXTURE_SRV, SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE) @@ -370,7 +370,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, PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS) - .AddResource(SHADER_TYPE_VERTEX, "cb2_mut", 1u, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE, 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) .AddResource(SHADER_TYPE_VERTEX, "tex2_mut", SHADER_RESOURCE_TYPE_TEXTURE_SRV, SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE) @@ -744,7 +744,7 @@ TEST_F(InlineConstants, RenderStateCache) bool PresentInCache = pData != nullptr; #if !DILIGENT_DEBUG - if (pDevice->GetDeviceInfo().IsD3DDevice() || pDevice->GetDeviceInfo().IsVulkanDevice()) + if (pDevice->GetDeviceInfo().IsD3DDevice()) { // For some reason, hash computation consistency depends on D3DCOMPILE_DEBUG flag and differs between debug and release builds PresentInCache = true; @@ -764,4 +764,4 @@ TEST_F(InlineConstants, RenderStateCache) } } -} // namespace +} // namespace \ No newline at end of file From ad65b58c48c0048a925b6f7548ea25eb9c958f2c Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Mon, 8 Dec 2025 14:54:51 +0800 Subject: [PATCH 063/104] For some reason, pUncachedVS and pVS1 got same hash computation result on Vulkan. --- Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp b/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp index c50987f31d..86ab7b6276 100644 --- a/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp +++ b/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp @@ -750,6 +750,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); From 00a01d940b833c34f6a70e70db87035566c55cbf Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Mon, 8 Dec 2025 20:04:58 +0800 Subject: [PATCH 064/104] Fix GetBindInfo() from DeviceContextVkImpl.cpp returning unexpected PIPELINE_TYPE_INVALID. --- .../src/PipelineResourceSignatureTest.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Tests/DiligentCoreAPITest/src/PipelineResourceSignatureTest.cpp b/Tests/DiligentCoreAPITest/src/PipelineResourceSignatureTest.cpp index c1aa236f3d..9e8ae48c4d 100644 --- a/Tests/DiligentCoreAPITest/src/PipelineResourceSignatureTest.cpp +++ b/Tests/DiligentCoreAPITest/src/PipelineResourceSignatureTest.cpp @@ -771,6 +771,15 @@ TEST_F(PipelineResourceSignatureTest, ImmutableSamplers2) PipelineResourceSignatureDesc Desc; Desc.Name = "ImmutableSamplers2 - PRS3"; Desc.BindingIndex = 3; + + //Note that PipelineTypeFromShaderStages() will return PIPELINE_TYPE_INVALID if we leave Desc.Resources empty, + //which will lead to PIPELINE_TYPE_INVALID from CmmoitShaderResources() -> pResBindingVkImpl->GetPipelineType() + //and causing assert or crash essentially. + const PipelineResourceDesc PlaceholderResource[] = { + {SHADER_TYPE_VERTEX, "Placeholder", 1, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_STATIC} + }; + Desc.Resources = PlaceholderResource; + Desc.NumResources = _countof(PlaceholderResource); pDevice->CreatePipelineResourceSignature(Desc, &pSignature3); ASSERT_NE(pSignature3, nullptr); } From 617b449cf9bace1bc63f988dab4285361106f29f Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Mon, 8 Dec 2025 20:10:52 +0800 Subject: [PATCH 065/104] Fix formatting. --- .../DiligentCoreAPITest/src/PipelineResourceSignatureTest.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Tests/DiligentCoreAPITest/src/PipelineResourceSignatureTest.cpp b/Tests/DiligentCoreAPITest/src/PipelineResourceSignatureTest.cpp index 9e8ae48c4d..fa9c04ab3e 100644 --- a/Tests/DiligentCoreAPITest/src/PipelineResourceSignatureTest.cpp +++ b/Tests/DiligentCoreAPITest/src/PipelineResourceSignatureTest.cpp @@ -776,8 +776,7 @@ TEST_F(PipelineResourceSignatureTest, ImmutableSamplers2) //which will lead to PIPELINE_TYPE_INVALID from CmmoitShaderResources() -> pResBindingVkImpl->GetPipelineType() //and causing assert or crash essentially. const PipelineResourceDesc PlaceholderResource[] = { - {SHADER_TYPE_VERTEX, "Placeholder", 1, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_STATIC} - }; + {SHADER_TYPE_VERTEX, "Placeholder", 1, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_STATIC}}; Desc.Resources = PlaceholderResource; Desc.NumResources = _countof(PlaceholderResource); pDevice->CreatePipelineResourceSignature(Desc, &pSignature3); From 17b7af5935fcde810284b9eddcd12bc12c5538de Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Mon, 8 Dec 2025 20:28:43 +0800 Subject: [PATCH 066/104] Fix ImmutableSamplers2 test when no resources provided in PRS Desc. --- .../GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp | 11 ++++++++++- .../src/PipelineResourceSignatureTest.cpp | 8 -------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp index 3d3013eb8e..c0ea7ddaa3 100644 --- a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp @@ -647,7 +647,16 @@ void DeviceContextVkImpl::CommitShaderResources(IShaderResourceBinding* pShaderR const Uint32 SRBIndex = pResBindingVkImpl->GetBindingIndex(); const PipelineResourceSignatureVkImpl* pSignature = pResBindingVkImpl->GetSignature(); - ResourceBindInfo& BindInfo = GetBindInfo(pResBindingVkImpl->GetPipelineType()); + + // Use PSO's pipeline type as fallback when PRS has no resources and thus PIPELINE_TYPE_INVALID + PIPELINE_TYPE SrbPipelineType = pResBindingVkImpl->GetPipelineType(); + if (SrbPipelineType == PIPELINE_TYPE_INVALID) + { + VERIFY_EXPR(m_pPipelineState != nullptr); + SrbPipelineType = m_pPipelineState->GetDesc().PipelineType; + } + + ResourceBindInfo& BindInfo = GetBindInfo(SrbPipelineType); ResourceBindInfo::DescriptorSetInfo& SetInfo = BindInfo.SetInfo[SRBIndex]; // Always bind the SRB, even if it has no descriptor sets (e.g., only push constants) diff --git a/Tests/DiligentCoreAPITest/src/PipelineResourceSignatureTest.cpp b/Tests/DiligentCoreAPITest/src/PipelineResourceSignatureTest.cpp index fa9c04ab3e..c1aa236f3d 100644 --- a/Tests/DiligentCoreAPITest/src/PipelineResourceSignatureTest.cpp +++ b/Tests/DiligentCoreAPITest/src/PipelineResourceSignatureTest.cpp @@ -771,14 +771,6 @@ TEST_F(PipelineResourceSignatureTest, ImmutableSamplers2) PipelineResourceSignatureDesc Desc; Desc.Name = "ImmutableSamplers2 - PRS3"; Desc.BindingIndex = 3; - - //Note that PipelineTypeFromShaderStages() will return PIPELINE_TYPE_INVALID if we leave Desc.Resources empty, - //which will lead to PIPELINE_TYPE_INVALID from CmmoitShaderResources() -> pResBindingVkImpl->GetPipelineType() - //and causing assert or crash essentially. - const PipelineResourceDesc PlaceholderResource[] = { - {SHADER_TYPE_VERTEX, "Placeholder", 1, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_STATIC}}; - Desc.Resources = PlaceholderResource; - Desc.NumResources = _countof(PlaceholderResource); pDevice->CreatePipelineResourceSignature(Desc, &pSignature3); ASSERT_NE(pSignature3, nullptr); } From fb8722229d0231539c78d3c3ec2e878dcf955cbc Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Mon, 8 Dec 2025 20:32:00 +0800 Subject: [PATCH 067/104] Fix formatting --- Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp index c0ea7ddaa3..8fd3f93a9b 100644 --- a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp @@ -656,8 +656,8 @@ void DeviceContextVkImpl::CommitShaderResources(IShaderResourceBinding* pShaderR SrbPipelineType = m_pPipelineState->GetDesc().PipelineType; } - ResourceBindInfo& BindInfo = GetBindInfo(SrbPipelineType); - ResourceBindInfo::DescriptorSetInfo& SetInfo = BindInfo.SetInfo[SRBIndex]; + ResourceBindInfo& BindInfo = GetBindInfo(SrbPipelineType); + ResourceBindInfo::DescriptorSetInfo& SetInfo = BindInfo.SetInfo[SRBIndex]; // Always bind the SRB, even if it has no descriptor sets (e.g., only push constants) // The resource cache is needed to store push constant data From af59a53177f0d7ddeb5ee853fdd4d5adf598b43a Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Mon, 8 Dec 2025 21:07:31 +0800 Subject: [PATCH 068/104] Skip CommitShaderResources for PRS that has no resources bound on it. --- .../src/DeviceContextVkImpl.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp index 8fd3f93a9b..6f248e475f 100644 --- a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp @@ -648,15 +648,12 @@ void DeviceContextVkImpl::CommitShaderResources(IShaderResourceBinding* pShaderR const Uint32 SRBIndex = pResBindingVkImpl->GetBindingIndex(); const PipelineResourceSignatureVkImpl* pSignature = pResBindingVkImpl->GetSignature(); - // Use PSO's pipeline type as fallback when PRS has no resources and thus PIPELINE_TYPE_INVALID - PIPELINE_TYPE SrbPipelineType = pResBindingVkImpl->GetPipelineType(); - if (SrbPipelineType == PIPELINE_TYPE_INVALID) - { - VERIFY_EXPR(m_pPipelineState != nullptr); - SrbPipelineType = m_pPipelineState->GetDesc().PipelineType; - } + // PRS has no resources and thus PIPELINE_TYPE_INVALID ? + PIPELINE_TYPE SRBPipelineType = pResBindingVkImpl->GetPipelineType(); + if (SRBPipelineType == PIPELINE_TYPE_INVALID) + return; - ResourceBindInfo& BindInfo = GetBindInfo(SrbPipelineType); + ResourceBindInfo& BindInfo = GetBindInfo(SRBPipelineType); ResourceBindInfo::DescriptorSetInfo& SetInfo = BindInfo.SetInfo[SRBIndex]; // Always bind the SRB, even if it has no descriptor sets (e.g., only push constants) From 2574f0446ac4f5029b397620ca6a4529f92f87c3 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Mon, 8 Dec 2025 23:27:31 +0800 Subject: [PATCH 069/104] Use ResIndex to locate actual InlineCB and PushConstantData. --- .../include/ShaderResourceCacheVk.hpp | 32 ++++++------ .../src/PipelineResourceSignatureVkImpl.cpp | 50 +++++-------------- .../src/ShaderVariableManagerVk.cpp | 4 +- 3 files changed, 29 insertions(+), 57 deletions(-) diff --git a/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp b/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp index 7a24da3362..f85e0e7b35 100644 --- a/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp +++ b/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp @@ -274,19 +274,19 @@ class ShaderResourceCacheVk : public ShaderResourceCacheBase STDDeleter(Allocator)}; } - // Sets the push constant data pointer for the given index + // Sets the push constant data pointer for the given ResIndex // This is used to store per-SRB push constant data for STATIC push constants - void SetPushConstantDataPtr(Uint32 Index, void* pData) + void SetPushConstantDataPtr(Uint32 ResIndex, void* pData) { - VERIFY_EXPR(Index < m_NumPushConstantBuffers); - m_pPushConstantDataPtrs[Index] = pData; + VERIFY_EXPR(ResIndex < m_NumPushConstantBuffers); + m_pPushConstantDataPtrs[ResIndex] = pData; } - // Gets the push constant data pointer for the given index - void* GetPushConstantDataPtr(Uint32 Index) const + // Gets the push constant data pointer for the given ResIndex + void* GetPushConstantDataPtr(Uint32 ResIndex) const { - VERIFY_EXPR(Index < m_NumPushConstantBuffers); - return m_pPushConstantDataPtrs[Index]; + VERIFY_EXPR(ResIndex < m_NumPushConstantBuffers); + return m_pPushConstantDataPtrs[ResIndex]; } // Initializes push constant data pointers array @@ -323,18 +323,18 @@ class ShaderResourceCacheVk : public ShaderResourceCacheBase } } - // Sets the inline constant buffer at the given index - void SetInlineConstantBuffer(Uint32 Index, RefCntAutoPtr&& pBuffer) + // Sets the inline constant buffer at the given ResIndex + void SetInlineConstantBuffer(Uint32 ResIndex, RefCntAutoPtr&& pBuffer) { - VERIFY_EXPR(Index < m_NumInlineConstantBuffers); - m_pInlineConstantBuffers[Index] = std::move(pBuffer); + VERIFY_EXPR(ResIndex < m_NumInlineConstantBuffers); + m_pInlineConstantBuffers[ResIndex] = std::move(pBuffer); } - // Gets the inline constant buffer at the given index - BufferVkImpl* GetInlineConstantBuffer(Uint32 Index) const + // Gets the inline constant buffer at the given ResIndex + BufferVkImpl* GetInlineConstantBuffer(Uint32 ResIndex) const { - VERIFY_EXPR(Index < m_NumInlineConstantBuffers); - return m_pInlineConstantBuffers[Index].RawPtr(); + VERIFY_EXPR(ResIndex < m_NumInlineConstantBuffers); + return m_pInlineConstantBuffers[ResIndex].RawPtr(); } Uint32 GetNumInlineConstantBuffers() const { return m_NumInlineConstantBuffers; } diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp index 64bf3c0ef3..1e0ed62be9 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp @@ -682,30 +682,18 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk ResourceCache.MarkHasInlineConstants(); // Count push constant buffers, emulated inline constant buffers, and calculate total memory size - Uint32 NumPushConstantBuffers = 0; - Uint32 NumEmulatedInlineConstantCBs = 0; Uint32 TotalInlineConstantSize = 0; for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) { const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; TotalInlineConstantSize += InlineCBAttr.NumConstants * sizeof(Uint32); - if (InlineCBAttr.IsPushConstant) - ++NumPushConstantBuffers; - else - ++NumEmulatedInlineConstantCBs; } - // Initialize push constant data pointers array in the resource cache - if (NumPushConstantBuffers > 0) - { - ResourceCache.InitializePushConstantDataPtrs(NumPushConstantBuffers); - } + // Initialize push constant data pointers array in the resource cache, use ResIndex to access actual PushConstantDataPtr later. + ResourceCache.InitializePushConstantDataPtrs(TotalResources); - // Initialize inline constant buffers array in the resource cache - if (NumEmulatedInlineConstantCBs > 0) - { - ResourceCache.InitializeInlineConstantBuffers(NumEmulatedInlineConstantCBs); - } + // Initialize inline constant buffers array in the resource cache, use ResIndex to access actual InlineConstantBuffer later. + ResourceCache.InitializeInlineConstantBuffers(TotalResources); // Allocate memory for all inline constants void* pInlineConstantMemory = nullptr; @@ -720,8 +708,6 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk // Assign memory to each inline constant buffer and create per-SRB buffers Uint8* pCurrentDataPtr = static_cast(pInlineConstantMemory); - Uint32 PushConstantBufferIdx = 0; - Uint32 EmulatedInlineConstantIdx = 0; for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) { const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; @@ -731,7 +717,7 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk { // For push constants, store the data pointer in the resource cache // Each SRB has its own copy of push constant data - ResourceCache.SetPushConstantDataPtr(PushConstantBufferIdx++, pCurrentDataPtr); + ResourceCache.SetPushConstantDataPtr(InlineCBAttr.ResIndex, pCurrentDataPtr); pCurrentDataPtr += DataSize; } else @@ -761,10 +747,9 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk RefCntAutoPtr pBuffer; m_pDevice->CreateBuffer(CBDesc, nullptr, &pBuffer); VERIFY_EXPR(pBuffer); - ResourceCache.SetInlineConstantBuffer(EmulatedInlineConstantIdx, RefCntAutoPtr{pBuffer, IID_BufferVk}); + ResourceCache.SetInlineConstantBuffer(InlineCBAttr.ResIndex, RefCntAutoPtr{pBuffer, IID_BufferVk}); } - ++EmulatedInlineConstantIdx; pCurrentDataPtr += DataSize; } } @@ -788,7 +773,6 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk // Bind internal inline constant buffers to the resource cache // This must be done after descriptor set allocation so that descriptor writes work correctly - Uint32 EmulatedInlineConstantIdx = 0; for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) { const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; @@ -798,9 +782,7 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk continue; // Get the per-SRB buffer from the resource cache - BufferVkImpl* pBuffer = ResourceCache.GetInlineConstantBuffer(EmulatedInlineConstantIdx); - ++EmulatedInlineConstantIdx; - + BufferVkImpl* pBuffer = ResourceCache.GetInlineConstantBuffer(InlineCBAttr.ResIndex); if (!pBuffer) continue; @@ -851,8 +833,7 @@ void PipelineResourceSignatureVkImpl::CopyStaticResources(ShaderResourceCacheVk& // Handle STATIC push constants first - copy data from Signature's static cache to SRB's cache // Each SRB has its own copy of push constant data const auto ResIdxRange = GetResourceIndexRange(SHADER_RESOURCE_VARIABLE_TYPE_STATIC); - Uint32 DstPushConstantBufferIdx = 0; - Uint32 SrcPushConstantBufferIdx = 0; + for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) { const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; @@ -863,11 +844,10 @@ void PipelineResourceSignatureVkImpl::CopyStaticResources(ShaderResourceCacheVk& if (ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) { // Get source data from Signature's static resource cache - const void* pSrcData = m_pStaticResCache ? m_pStaticResCache->GetPushConstantDataPtr(SrcPushConstantBufferIdx) : nullptr; - ++SrcPushConstantBufferIdx; + const void* pSrcData = m_pStaticResCache ? m_pStaticResCache->GetPushConstantDataPtr(InlineCBAttr.ResIndex) : nullptr; // Get destination data pointer from SRB's resource cache - void* pDstData = DstResourceCache.GetPushConstantDataPtr(DstPushConstantBufferIdx); + void* pDstData = DstResourceCache.GetPushConstantDataPtr(InlineCBAttr.ResIndex); if (pSrcData != nullptr && pDstData != nullptr) { @@ -875,7 +855,6 @@ void PipelineResourceSignatureVkImpl::CopyStaticResources(ShaderResourceCacheVk& memcpy(pDstData, pSrcData, DataSize); } } - ++DstPushConstantBufferIdx; } if (!HasDescriptorSet(DESCRIPTOR_SET_ID_STATIC_MUTABLE) || m_pStaticResCache == nullptr) @@ -1366,8 +1345,6 @@ void PipelineResourceSignatureVkImpl::UpdateInlineConstantBuffers(const ShaderRe // SRB caches use SRBCacheOffset, static caches use StaticCacheOffset const ResourceCacheContentType CacheType = ResourceCache.GetContentType(); - Uint32 PushConstantBufferIdx = 0; - Uint32 EmulatedInlineConstantIdx = 0; for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) { const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; @@ -1377,11 +1354,9 @@ void PipelineResourceSignatureVkImpl::UpdateInlineConstantBuffers(const ShaderRe { // For true push constants, get data from the SRB's resource cache // Each SRB has its own copy of push constant data - const void* pInlineConstantData = ResourceCache.GetPushConstantDataPtr(PushConstantBufferIdx); + const void* pInlineConstantData = ResourceCache.GetPushConstantDataPtr(InlineCBAttr.ResIndex); VERIFY_EXPR(pInlineConstantData != nullptr); - ++PushConstantBufferIdx; - // Copy data to the device context's push constants buffer // This will be submitted via vkCmdPushConstants before draw/dispatch Ctx.SetPushConstants(pInlineConstantData, 0, DataSize); @@ -1389,8 +1364,7 @@ void PipelineResourceSignatureVkImpl::UpdateInlineConstantBuffers(const ShaderRe } // For emulated inline constants, get the per-SRB buffer from the resource cache - BufferVkImpl* pBuffer = ResourceCache.GetInlineConstantBuffer(EmulatedInlineConstantIdx); - ++EmulatedInlineConstantIdx; + BufferVkImpl* pBuffer = ResourceCache.GetInlineConstantBuffer(InlineCBAttr.ResIndex); if (pBuffer) { diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp index 91e7b96fba..eb1ab948ea 100644 --- a/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp +++ b/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp @@ -672,7 +672,6 @@ void ShaderVariableManagerVk::SetInlineConstants(Uint32 ResIndex, // For both Signature's static cache and SRB cache, push constant data is stored // in the resource cache via GetPushConstantDataPtr() - Uint32 PushConstantBufferIdx = 0; for (Uint32 i = 0; i < NumInlineCBs; ++i) { const InlineConstantBufferAttribsVk& InlineCBAttr = pInlineCBs[i]; @@ -682,7 +681,7 @@ void ShaderVariableManagerVk::SetInlineConstants(Uint32 ResIndex, if (InlineCBAttr.ResIndex == ResIndex) { // Get the data pointer from the resource cache - void* pPushConstantData = m_ResourceCache.GetPushConstantDataPtr(PushConstantBufferIdx); + void* pPushConstantData = m_ResourceCache.GetPushConstantDataPtr(InlineCBAttr.ResIndex); if (pPushConstantData != nullptr) { Uint32* pDstConstants = reinterpret_cast(pPushConstantData); @@ -690,7 +689,6 @@ void ShaderVariableManagerVk::SetInlineConstants(Uint32 ResIndex, } return; } - ++PushConstantBufferIdx; } UNEXPECTED("Push constant buffer not found"); } From 693ca4e96c14510c2ab84735e5f2bfcf08c2bd3a Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Mon, 8 Dec 2025 23:30:09 +0800 Subject: [PATCH 070/104] Fix formatting. --- .../src/PipelineResourceSignatureVkImpl.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp index 1e0ed62be9..e7564aa08a 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp @@ -682,7 +682,7 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk ResourceCache.MarkHasInlineConstants(); // Count push constant buffers, emulated inline constant buffers, and calculate total memory size - Uint32 TotalInlineConstantSize = 0; + Uint32 TotalInlineConstantSize = 0; for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) { const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; @@ -707,7 +707,7 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk } // Assign memory to each inline constant buffer and create per-SRB buffers - Uint8* pCurrentDataPtr = static_cast(pInlineConstantMemory); + Uint8* pCurrentDataPtr = static_cast(pInlineConstantMemory); for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) { const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; @@ -832,7 +832,7 @@ void PipelineResourceSignatureVkImpl::CopyStaticResources(ShaderResourceCacheVk& { // Handle STATIC push constants first - copy data from Signature's static cache to SRB's cache // Each SRB has its own copy of push constant data - const auto ResIdxRange = GetResourceIndexRange(SHADER_RESOURCE_VARIABLE_TYPE_STATIC); + const auto ResIdxRange = GetResourceIndexRange(SHADER_RESOURCE_VARIABLE_TYPE_STATIC); for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) { From 8d57cdf6cdbea6e8090e62f8d0de71d40e6bdedc Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Tue, 9 Dec 2025 11:26:38 +0800 Subject: [PATCH 071/104] Rename GetInlineConstantBuffers to GetInlineConstantBufferAttribs to avoid confusion with actual buffer storage. Add implicit vulkan push constant in GetDefaultResourceSignatureDesc. --- .../PipelineResourceSignatureVkImpl.hpp | 13 +--- .../src/DeviceContextVkImpl.cpp | 2 +- .../src/PipelineResourceSignatureVkImpl.cpp | 73 ++++++------------- .../src/PipelineStateVkImpl.cpp | 32 +++++++- .../src/ShaderVariableManagerVk.cpp | 8 +- 5 files changed, 58 insertions(+), 70 deletions(-) diff --git a/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp index 54e606e2ef..1bc5f08b0d 100644 --- a/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp @@ -160,15 +160,10 @@ class PipelineResourceSignatureVkImpl final : public PipelineResourceSignatureBa DeviceContextVkImpl& Ctx) const; // Returns the number of inline constant buffers - Uint32 GetNumInlineConstantBuffers() const { return m_NumInlineConstantBuffers; } + Uint32 GetNumInlineConstantBufferAttribs() const { return m_NumInlineConstantBufferAttribs; } // Returns the inline constant buffer attributes - const InlineConstantBufferAttribsVk* GetInlineConstantBuffers() const { return m_InlineConstantBuffers.get(); } - - // Adds PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT flag to a resource if it has - // PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS but is missing the Vulkan push constant flag. - // This is called during pipeline state creation when shader reflection detects a push constant. - void UpdatePushConstantFlags(Uint32 ResIndex); + const InlineConstantBufferAttribsVk* GetInlineConstantBufferAttribs() const { return m_InlineConstantBufferAttribs.get(); } #ifdef DILIGENT_DEVELOPMENT /// Verifies committed resource using the SPIRV resource attributes from the PSO. @@ -231,9 +226,9 @@ class PipelineResourceSignatureVkImpl final : public PipelineResourceSignatureBa Uint16 m_DynamicStorageBufferCount = 0; // Number of inline constant buffers - Uint32 m_NumInlineConstantBuffers = 0; + Uint32 m_NumInlineConstantBufferAttribs = 0; // Inline constant buffer attributes - std::unique_ptr m_InlineConstantBuffers; + std::unique_ptr m_InlineConstantBufferAttribs; // Note: Static inline constant data (including push constants) is stored in m_pStaticResCache. // For push constants, we use InitializePushConstantDataPtrs() and SetPushConstantDataPtr() diff --git a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp index 6f248e475f..fbfff10376 100644 --- a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp @@ -431,7 +431,7 @@ void DeviceContextVkImpl::UpdateInlineConstantBuffers(ResourceBindInfo& BindInfo for (Uint32 i = 0; i < SignCount; ++i) { const PipelineResourceSignatureVkImpl* pSign = m_pPipelineState->GetResourceSignature(i); - if (pSign == nullptr || pSign->GetNumInlineConstantBuffers() == 0) + if (pSign == nullptr || pSign->GetNumInlineConstantBufferAttribs() == 0) continue; const ShaderResourceCacheVk* pResourceCache = BindInfo.ResourceCaches[i]; diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp index e7564aa08a..a5af5ac4e1 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp @@ -223,14 +223,14 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) { VERIFY(ResDesc.ResourceType == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, "Only constant buffers can have INLINE_CONSTANTS flag"); - ++m_NumInlineConstantBuffers; + ++m_NumInlineConstantBufferAttribs; } } // Allocate inline constant buffer attributes array - if (m_NumInlineConstantBuffers > 0) + if (m_NumInlineConstantBufferAttribs > 0) { - m_InlineConstantBuffers = std::make_unique(m_NumInlineConstantBuffers); + m_InlineConstantBufferAttribs = std::make_unique(m_NumInlineConstantBufferAttribs); } // Descriptor set mapping (static/mutable (0) or dynamic (1) -> set index) @@ -408,7 +408,7 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) VERIFY(ResDesc.ResourceType == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, "Only constant buffers can have INLINE_CONSTANTS flag"); - InlineConstantBufferAttribsVk& InlineCBAttribs = m_InlineConstantBuffers[InlineConstantBufferIdx++]; + InlineConstantBufferAttribsVk& InlineCBAttribs = m_InlineConstantBufferAttribs[InlineConstantBufferIdx++]; InlineCBAttribs.ResIndex = i; // Resource index for unique identification InlineCBAttribs.DescrSet = pAttribs->DescrSet; InlineCBAttribs.BindingIndex = pAttribs->BindingIndex; @@ -418,7 +418,7 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) // between multiple PipelineStates sharing the same PipelineResourceSignature } } - VERIFY_EXPR(InlineConstantBufferIdx == m_NumInlineConstantBuffers); + VERIFY_EXPR(InlineConstantBufferIdx == m_NumInlineConstantBufferAttribs); #ifdef DILIGENT_DEBUG if (m_pStaticResCache != nullptr) @@ -556,15 +556,15 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) // Initialize inline constant buffers in the static resource cache // This must be done after the resources are initialized above - if (m_NumInlineConstantBuffers > 0 && m_pStaticResCache != nullptr) + if (m_NumInlineConstantBufferAttribs > 0 && m_pStaticResCache != nullptr) { // First pass: calculate total memory size needed for all static inline constants // and count static push constant buffers Uint32 TotalStaticInlineConstantSize = 0; Uint32 NumStaticPushConstantBuffers = 0; - for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) + for (Uint32 i = 0; i < m_NumInlineConstantBufferAttribs; ++i) { - const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; + const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBufferAttribs[i]; const PipelineResourceDesc& ResDesc = GetResourceDesc(InlineCBAttr.ResIndex); if (ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) @@ -594,9 +594,9 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) // Second pass: assign memory to each static inline constant Uint8* pCurrentDataPtr = static_cast(pInlineConstMemory); Uint32 StaticPushConstantIdx = 0; - for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) + for (Uint32 i = 0; i < m_NumInlineConstantBufferAttribs; ++i) { - const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; + const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBufferAttribs[i]; const PipelineResourceDesc& ResDesc = GetResourceDesc(InlineCBAttr.ResIndex); const ResourceAttribs& Attr = GetResourceAttribs(InlineCBAttr.ResIndex); @@ -675,7 +675,7 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk // For push constants, each SRB gets its own copy of the data (stored in ShaderResourceCacheVk) // For emulated inline constants, each SRB gets its own buffer to avoid conflicts between // multiple PipelineStates sharing the same PipelineResourceSignature - if (m_NumInlineConstantBuffers > 0) + if (m_NumInlineConstantBufferAttribs > 0) { // Ensure the cache reports inline constants so DeviceContextVkImpl // updates emulated buffers even if no data has been written yet. @@ -683,9 +683,9 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk // Count push constant buffers, emulated inline constant buffers, and calculate total memory size Uint32 TotalInlineConstantSize = 0; - for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) + for (Uint32 i = 0; i < m_NumInlineConstantBufferAttribs; ++i) { - const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; + const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBufferAttribs[i]; TotalInlineConstantSize += InlineCBAttr.NumConstants * sizeof(Uint32); } @@ -708,9 +708,9 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk // Assign memory to each inline constant buffer and create per-SRB buffers Uint8* pCurrentDataPtr = static_cast(pInlineConstantMemory); - for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) + for (Uint32 i = 0; i < m_NumInlineConstantBufferAttribs; ++i) { - const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; + const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBufferAttribs[i]; const Uint32 DataSize = InlineCBAttr.NumConstants * sizeof(Uint32); if (InlineCBAttr.IsPushConstant) @@ -773,9 +773,9 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk // Bind internal inline constant buffers to the resource cache // This must be done after descriptor set allocation so that descriptor writes work correctly - for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) + for (Uint32 i = 0; i < m_NumInlineConstantBufferAttribs; ++i) { - const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; + const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBufferAttribs[i]; // Skip push constants - they don't use descriptor sets if (InlineCBAttr.IsPushConstant) @@ -834,9 +834,9 @@ void PipelineResourceSignatureVkImpl::CopyStaticResources(ShaderResourceCacheVk& // Each SRB has its own copy of push constant data const auto ResIdxRange = GetResourceIndexRange(SHADER_RESOURCE_VARIABLE_TYPE_STATIC); - for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) + for (Uint32 i = 0; i < m_NumInlineConstantBufferAttribs; ++i) { - const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; + const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBufferAttribs[i]; if (!InlineCBAttr.IsPushConstant) continue; @@ -1345,9 +1345,9 @@ void PipelineResourceSignatureVkImpl::UpdateInlineConstantBuffers(const ShaderRe // SRB caches use SRBCacheOffset, static caches use StaticCacheOffset const ResourceCacheContentType CacheType = ResourceCache.GetContentType(); - for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) + for (Uint32 i = 0; i < m_NumInlineConstantBufferAttribs; ++i) { - const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; + const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBufferAttribs[i]; const Uint32 DataSize = InlineCBAttr.NumConstants * sizeof(Uint32); if (InlineCBAttr.IsPushConstant) @@ -1385,35 +1385,4 @@ void PipelineResourceSignatureVkImpl::UpdateInlineConstantBuffers(const ShaderRe } } -void PipelineResourceSignatureVkImpl::UpdatePushConstantFlags(Uint32 ResIndex) -{ - VERIFY_EXPR(ResIndex < this->m_Desc.NumResources); - - // Access the mutable resource description - PipelineResourceDesc& ResDesc = const_cast(this->m_Desc.Resources[ResIndex]); - - // Check if the resource has INLINE_CONSTANTS flag but is missing VULKAN_PUSH_CONSTANT flag - if ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0 && - (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) == 0) - { - // Add the Vulkan push constant flag - ResDesc.Flags |= PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT; - - // Update the corresponding InlineConstantBufferAttribsVk to mark it as a push constant - // Use ResIndex for matching - for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) - { - InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBuffers[i]; - if (InlineCBAttr.ResIndex == ResIndex) - { - // Mark this as a true push constant - // Note: Per-SRB buffers are created in InitSRBResourceCache(), which will check - // IsPushConstant and skip buffer creation for true push constants - InlineCBAttr.IsPushConstant = true; - break; - } - } - } -} - } // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp index f0fa11781c..a8100c1aa6 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp @@ -605,10 +605,6 @@ PipelineResourceSignatureDescWrapper PipelineStateVkImpl::GetDefaultResourceSign ShaderResources.ProcessResources( [&](const SPIRVShaderResourceAttribs& Attribs, Uint32) // { - // Note: Push constants are now included in the signature with - // PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT flag set. - // They will be handled via vkCmdPushConstants at runtime. - // We can't skip immutable samplers because immutable sampler arrays have to be defined // as both resource and sampler. //if (Res.Type == SPIRVShaderResourceAttribs::SeparateSampler && @@ -674,6 +670,34 @@ PipelineResourceSignatureDescWrapper PipelineStateVkImpl::GetDefaultResourceSign } } + // If no resource has PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT explicitly set, + // find the first inline constant and mark it as a Vulkan push constant + bool HasExplicitPushConstant = false; + + for (Uint32 i = 0; i < SignDesc.Get().NumResources; ++i) + { + const PipelineResourceDesc& Res = SignDesc.Get().Resources[i]; + if ((Res.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != 0) + { + HasExplicitPushConstant = true; + break; + } + } + + if (!HasExplicitPushConstant) + { + // Find the first inline constant and add PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT flag + for (Uint32 i = 0; i < SignDesc.Get().NumResources; ++i) + { + PipelineResourceDesc& Res = const_cast(SignDesc.Get().Resources[i]); + if ((Res.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0) + { + Res.Flags |= PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT; + break; + } + } + } + return SignDesc; } diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp index eb1ab948ea..3ae409a484 100644 --- a/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp +++ b/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp @@ -667,14 +667,14 @@ void ShaderVariableManagerVk::SetInlineConstants(Uint32 ResIndex, const bool IsPushConstant = (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != 0; if (IsPushConstant) { - const InlineConstantBufferAttribsVk* pInlineCBs = m_pSignature->GetInlineConstantBuffers(); - const Uint32 NumInlineCBs = m_pSignature->GetNumInlineConstantBuffers(); + const InlineConstantBufferAttribsVk* pInlineCBAttribs = m_pSignature->GetInlineConstantBufferAttribs(); + const Uint32 NumInlineCBAttribs = m_pSignature->GetNumInlineConstantBufferAttribs(); // For both Signature's static cache and SRB cache, push constant data is stored // in the resource cache via GetPushConstantDataPtr() - for (Uint32 i = 0; i < NumInlineCBs; ++i) + for (Uint32 i = 0; i < NumInlineCBAttribs; ++i) { - const InlineConstantBufferAttribsVk& InlineCBAttr = pInlineCBs[i]; + const InlineConstantBufferAttribsVk& InlineCBAttr = pInlineCBAttribs[i]; if (!InlineCBAttr.IsPushConstant) continue; From b882f3eb3d0dede7e696f5e3044c0341946b34fd Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Tue, 9 Dec 2025 20:05:16 +0800 Subject: [PATCH 072/104] Add PatchShaderConvertUniformBufferToPushConstant --- .../include/PipelineStateVkImpl.hpp | 4 +- .../include/ShaderVkImpl.hpp | 6 + .../src/PipelineStateVkImpl.cpp | 133 +++++++++++++++--- .../src/ShaderVariableManagerVk.cpp | 31 ++-- .../GraphicsEngineVulkan/src/ShaderVkImpl.cpp | 58 +++++--- 5 files changed, 167 insertions(+), 65 deletions(-) diff --git a/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp index ed19a4cf93..cd473c57d5 100644 --- a/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp @@ -133,7 +133,9 @@ class PipelineStateVkImpl final : public PipelineStateBase void InitializePipeline(const ComputePipelineStateCreateInfo& CreateInfo); void InitializePipeline(const RayTracingPipelineStateCreateInfo& CreateInfo); - void InitPushConstantInfo(const TShaderStages& ShaderStages, PushConstantInfoVk& PushConstant) noexcept(false); + void InitPushConstantInfoFromSignatures(PushConstantInfoVk& PushConstant) const noexcept(false); + + void PatchShaderConvertUniformBufferToPushConstant(TShaderStages& ShaderStages) const noexcept(false); // TPipelineStateBase::Construct needs access to InitializePipeline friend TPipelineStateBase; diff --git a/Graphics/GraphicsEngineVulkan/include/ShaderVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/ShaderVkImpl.hpp index 1da2b141f1..343b5d8bc2 100644 --- a/Graphics/GraphicsEngineVulkan/include/ShaderVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/ShaderVkImpl.hpp @@ -110,6 +110,10 @@ class ShaderVkImpl final : public ShaderBase Size = m_SPIRV.size() * sizeof(m_SPIRV[0]); } + void SetSPIRV(const std::vector& SPIRV) noexcept(false); + + void CreateSPIRVShaderResources() noexcept(false); + private: void Initialize(const ShaderCreateInfo& ShaderCI, const CreateInfo& VkShaderCI) noexcept(false); @@ -119,6 +123,8 @@ class ShaderVkImpl final : public ShaderBase std::string m_EntryPoint; std::vector m_SPIRV; + + bool m_LoadConstantBufferReflection = false; }; } // namespace Diligent diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp index a8100c1aa6..740a534366 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp @@ -43,9 +43,7 @@ #include "EngineMemory.h" #include "StringTools.hpp" -#if !DILIGENT_NO_HLSL -# include "SPIRVTools.hpp" -#endif +#include "SPIRVTools.hpp" namespace Diligent { @@ -854,33 +852,43 @@ void PipelineStateVkImpl::RemapOrVerifyShaderResources( } } -void PipelineStateVkImpl::InitPushConstantInfo(const TShaderStages& ShaderStages, PushConstantInfoVk& PushConstant) noexcept(false) +void PipelineStateVkImpl::InitPushConstantInfoFromSignatures(PushConstantInfoVk& PushConstant) const noexcept(false) { - for (const ShaderStageInfo& Stage : ShaderStages) + // Iterate through all signatures to find resources marked as VULKAN_PUSH_CONSTANT + for (Uint32 s = 0; s < m_SignatureCount; ++s) { - for (const ShaderVkImpl* pShader : Stage.Shaders) + const PipelineResourceSignatureVkImpl* pSignature = m_Signatures[s]; + if (pSignature == nullptr) + continue; + + for (Uint32 r = 0; r < pSignature->GetTotalResourceCount(); ++r) { - const auto& pShaderResources = pShader->GetShaderResources(); - if (pShaderResources && pShaderResources->GetNumPushConstants() > 0) + const PipelineResourceDesc& ResDesc = pSignature->GetResourceDesc(r); + + // Check if this resource is marked as a Vulkan push constant + if ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != 0) { - // There should be at most one push constant block per shader - const SPIRVShaderResourceAttribs& PCAttribs = pShaderResources->GetPushConstant(0); - const Uint32 PCSize = PCAttribs.BufferStaticSize; + // For push constants, ArraySize contains the number of 32-bit constants + const Uint32 PCSize = ResDesc.ArraySize * sizeof(Uint32); if (PushConstant.Size == 0) { - // First shader with push constants - record the size + // First push constant resource - record the size PushConstant.Size = PCSize; } else if (PushConstant.Size != PCSize) { - // Multiple shaders with different push constant sizes - // This is allowed in Vulkan - take the maximum size + // Multiple push constant resources with different sizes + // Take the maximum size (Vulkan allows only one push constant block per pipeline) PushConstant.Size = std::max(PushConstant.Size, PCSize); } - // Add this shader stage to the stage flags - PushConstant.StageFlags |= ShaderTypeToVkShaderStageFlagBit(Stage.Type); + // Add shader stages to the stage flags + for (SHADER_TYPE ShaderStages = ResDesc.ShaderStages; ShaderStages != SHADER_TYPE_UNKNOWN;) + { + const SHADER_TYPE ShaderType = ExtractLSB(ShaderStages); + PushConstant.StageFlags |= ShaderTypeToVkShaderStageFlagBit(ShaderType); + } } } } @@ -900,13 +908,19 @@ void PipelineStateVkImpl::InitPipelineLayout(const PipelineStateCreateInfo& Crea DvpValidateResourceLimits(); #endif - // Extract push constant information from shaders - // Vulkan allows only one push constant block per pipeline, but it can be accessed from multiple shader stages + // Extract push constant information from signatures + // Vulkan allows only one push constant block per pipeline, but it can be accessed from multiple shader stages. + // We use resource attributes from m_Signatures instead of from shader stages because we may patch SPIRV + // later according to m_Signatures definitions (e.g., converting uniform buffers to push constants). PushConstantInfoVk PushConstant; - InitPushConstantInfo(ShaderStages, PushConstant); + InitPushConstantInfoFromSignatures(PushConstant); m_PipelineLayout.Create(GetDevice(), m_Signatures, m_SignatureCount, PushConstant); + // Check if any resource in shader is a uniform buffer but marked as VULKAN_PUSH_CONSTANT in signatures. + // If so, convert the uniform buffer to push constant in SPIRV bytecode. + PatchShaderConvertUniformBufferToPushConstant(ShaderStages); + const bool RemapResources = (CreateInfo.Flags & PSO_CREATE_FLAG_DONT_REMAP_SHADER_RESOURCES) == 0; const bool VerifyBindings = !RemapResources && ((InternalFlags & PSO_CREATE_INTERNAL_FLAG_NO_SHADER_REFLECTION) == 0); if (RemapResources || VerifyBindings) @@ -933,6 +947,87 @@ void PipelineStateVkImpl::InitPipelineLayout(const PipelineStateCreateInfo& Crea } } +void PipelineStateVkImpl::PatchShaderConvertUniformBufferToPushConstant(TShaderStages& ShaderStages) const noexcept(false) +{ + // Build a set of resource names that need to be converted to push constants + std::unordered_set PushConstantNames; + for (Uint32 s = 0; s < m_SignatureCount; ++s) + { + const PipelineResourceSignatureVkImpl* pSignature = m_Signatures[s]; + if (pSignature == nullptr) + continue; + + for (Uint32 r = 0; r < pSignature->GetTotalResourceCount(); ++r) + { + const PipelineResourceDesc& ResDesc = pSignature->GetResourceDesc(r); + if ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != 0) + { + PushConstantNames.insert(ResDesc.Name); + } + } + } + + if (PushConstantNames.empty()) + return; + + // For each shader stage, check if any uniform buffer needs to be patched + for (ShaderStageInfo& Stage : ShaderStages) + { + for (size_t i = 0; i < Stage.Shaders.size(); ++i) + { + ShaderVkImpl* pShader = const_cast(Stage.Shaders[i]); + + bool ShouldPatchUniformBuffer = false; + std::string PatchingUniformBufferName; + { + const SPIRVShaderResources* pRes = pShader->GetShaderResources().get(); + + if (pRes == nullptr) + continue; + + // Check each uniform buffer in the shader + for (Uint32 ub = 0; ub < pRes->GetNumUBs(); ++ub) + { + const SPIRVShaderResourceAttribs& UBAttribs = pRes->GetUB(ub); + + // If this uniform buffer is marked as push constant in the signature, + // convert it in the SPIRV bytecode + if (PushConstantNames.count(UBAttribs.Name) > 0) + { + PatchingUniformBufferName = UBAttribs.Name; + ShouldPatchUniformBuffer = true; + break; + } + } + } + + if(ShouldPatchUniformBuffer) + { + const std::vector& SPIRV = Stage.SPIRVs[i]; + std::vector ConvertedSPIRV = PatchSPIRVConvertUniformBufferToPushConstant( + SPIRV, + SPV_ENV_MAX, // Auto-detect target environment + PatchingUniformBufferName); + + if (!ConvertedSPIRV.empty()) + { + Stage.SPIRVs[i] = ConvertedSPIRV; + + pShader->SetSPIRV(ConvertedSPIRV); + + //Reconstruct shader resources from SPIRV + pShader->CreateSPIRVShaderResources(); + } + else + { + LOG_ERROR_MESSAGE("Failed to convert uniform buffer '", PatchingUniformBufferName, + "' to push constant in shader '", pShader->GetDesc().Name, "'"); + } + } + } + } +} + template PipelineStateVkImpl::TShaderStages PipelineStateVkImpl::InitInternalObjects( const PSOCreateInfoType& CreateInfo, diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp index 3ae409a484..122d5a443a 100644 --- a/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp +++ b/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp @@ -667,30 +667,17 @@ void ShaderVariableManagerVk::SetInlineConstants(Uint32 ResIndex, const bool IsPushConstant = (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != 0; if (IsPushConstant) { - const InlineConstantBufferAttribsVk* pInlineCBAttribs = m_pSignature->GetInlineConstantBufferAttribs(); - const Uint32 NumInlineCBAttribs = m_pSignature->GetNumInlineConstantBufferAttribs(); - - // For both Signature's static cache and SRB cache, push constant data is stored - // in the resource cache via GetPushConstantDataPtr() - for (Uint32 i = 0; i < NumInlineCBAttribs; ++i) + // Get the data pointer from the resource cache + void* pPushConstantData = m_ResourceCache.GetPushConstantDataPtr(ResIndex); + if (pPushConstantData != nullptr) { - const InlineConstantBufferAttribsVk& InlineCBAttr = pInlineCBAttribs[i]; - if (!InlineCBAttr.IsPushConstant) - continue; - - if (InlineCBAttr.ResIndex == ResIndex) - { - // Get the data pointer from the resource cache - void* pPushConstantData = m_ResourceCache.GetPushConstantDataPtr(InlineCBAttr.ResIndex); - if (pPushConstantData != nullptr) - { - Uint32* pDstConstants = reinterpret_cast(pPushConstantData); - memcpy(pDstConstants + FirstConstant, pConstants, NumConstants * sizeof(Uint32)); - } - return; - } + Uint32* pDstConstants = reinterpret_cast(pPushConstantData); + memcpy(pDstConstants + FirstConstant, pConstants, NumConstants * sizeof(Uint32)); + } + else + { + UNEXPECTED("Push constant buffer not found"); } - UNEXPECTED("Push constant buffer not found"); } else { diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderVkImpl.cpp index 0de01359d1..93b8634823 100644 --- a/Graphics/GraphicsEngineVulkan/src/ShaderVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/ShaderVkImpl.cpp @@ -229,37 +229,17 @@ void ShaderVkImpl::Initialize(const ShaderCreateInfo& ShaderCI, // We cannot create shader module here because resource bindings are assigned when // pipeline state is created - + m_LoadConstantBufferReflection = ShaderCI.LoadConstantBufferReflection; // Load shader resources if (!m_SPIRV.empty()) { if ((ShaderCI.CompileFlags & SHADER_COMPILE_FLAG_SKIP_REFLECTION) == 0) { - IMemoryAllocator& Allocator = GetRawAllocator(); - - std::unique_ptr> pRawMem{ - ALLOCATE(Allocator, "Memory for SPIRVShaderResources", SPIRVShaderResources, 1), - STDDeleterRawMem(Allocator), - }; - const bool LoadShaderInputs = m_Desc.ShaderType == SHADER_TYPE_VERTEX; - new (pRawMem.get()) SPIRVShaderResources // May throw - { - Allocator, - m_SPIRV, - m_Desc, - m_Desc.UseCombinedTextureSamplers ? m_Desc.CombinedSamplerSuffix : nullptr, - LoadShaderInputs, - ShaderCI.LoadConstantBufferReflection, - m_EntryPoint // - }; + CreateSPIRVShaderResources(); + VERIFY_EXPR(ShaderCI.ByteCode != nullptr || m_EntryPoint == ShaderCI.EntryPoint || (m_EntryPoint == "main" && (ShaderCI.CompileFlags & SHADER_COMPILE_FLAG_HLSL_TO_SPIRV_VIA_GLSL) != 0)); - m_pShaderResources.reset(static_cast(pRawMem.release()), STDDeleterRawMem(Allocator)); - if (LoadShaderInputs && m_pShaderResources->IsHLSLSource()) - { - m_pShaderResources->MapHLSLVertexShaderInputs(m_SPIRV); - } } else { @@ -270,6 +250,38 @@ void ShaderVkImpl::Initialize(const ShaderCreateInfo& ShaderCI, } } +void ShaderVkImpl::SetSPIRV(const std::vector& SPIRV) noexcept(false) +{ + m_SPIRV = SPIRV; +} + +void ShaderVkImpl::CreateSPIRVShaderResources() noexcept(false) +{ + IMemoryAllocator& Allocator = GetRawAllocator(); + + std::unique_ptr> pRawMem{ + ALLOCATE(Allocator, "Memory for SPIRVShaderResources", SPIRVShaderResources, 1), + STDDeleterRawMem(Allocator), + }; + const bool LoadShaderInputs = m_Desc.ShaderType == SHADER_TYPE_VERTEX; + new (pRawMem.get()) SPIRVShaderResources // May throw + { + Allocator, + m_SPIRV, + m_Desc, + m_Desc.UseCombinedTextureSamplers ? m_Desc.CombinedSamplerSuffix : nullptr, + LoadShaderInputs, + m_LoadConstantBufferReflection, + m_EntryPoint // + }; + + m_pShaderResources.reset(static_cast(pRawMem.release()), STDDeleterRawMem(Allocator)); + + if (LoadShaderInputs && m_pShaderResources->IsHLSLSource()) + { + m_pShaderResources->MapHLSLVertexShaderInputs(m_SPIRV); + } +} ShaderVkImpl::ShaderVkImpl(IReferenceCounters* pRefCounters, RenderDeviceVkImpl* pRenderDeviceVk, From 333c3d1ce053120facc91e3c739e7bb2f1b4cf75 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Tue, 9 Dec 2025 20:47:22 +0800 Subject: [PATCH 073/104] Move buffervk to InlineConstantBufferAttribsVk, aligning to D3D11 impl. --- .../PipelineResourceSignatureVkImpl.hpp | 16 ++- .../include/ShaderResourceCacheVk.hpp | 34 +----- .../src/PipelineResourceSignatureVkImpl.cpp | 109 ++++++++++-------- .../src/PipelineStateVkImpl.cpp | 3 +- 4 files changed, 75 insertions(+), 87 deletions(-) diff --git a/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp index 1bc5f08b0d..3b2655a05d 100644 --- a/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp @@ -64,10 +64,9 @@ ASSERT_SIZEOF(ImmutableSamplerAttribsVk, 8, "The struct is used in serialization /// 1. True push constants (from SPIR-V push_constant storage class) - use vkCmdPushConstants /// 2. Emulated inline constants - use dynamic uniform buffers (similar to D3D11 backend) /// -/// Note: This structure only stores attributes/metadata. The actual storage (pBuffer for emulated -/// inline constants, push constant data for true push constants) is managed per-SRB in -/// ShaderResourceCacheVk to avoid conflicts between multiple PipelineStates sharing the same -/// PipelineResourceSignature. +/// For emulated inline constants (not push constants), the Buffer is created in the Signature +/// and shared by all SRBs (similar to D3D11). Each SRB has its own CPU staging memory. +/// For push constants, pPushConstantData points to the static cache's staging memory. struct InlineConstantBufferAttribsVk { Uint32 ResIndex = 0; // Resource index in the signature (used for matching) @@ -75,7 +74,14 @@ struct InlineConstantBufferAttribsVk Uint32 BindingIndex = 0; // Binding index within the descriptor set Uint32 NumConstants = 0; // Number of 32-bit constants bool IsPushConstant = false; // True if this is a Vulkan push constant (not emulated) - // Note: pBuffer and pPushConstantData are now stored per-SRB in ShaderResourceCacheVk + + // For emulated inline constants: shared buffer created in the Signature (similar to D3D11) + // All SRBs reference this same buffer to reduce memory usage. + RefCntAutoPtr pBuffer; + + // For static push constants: pointer to data in the static resource cache + // This allows CopyStaticResources to copy the data to SRB caches. + void* pPushConstantData = nullptr; }; struct PipelineResourceSignatureInternalDataVk : PipelineResourceSignatureInternalData diff --git a/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp b/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp index f85e0e7b35..fd9908df33 100644 --- a/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp +++ b/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp @@ -313,32 +313,6 @@ class ShaderResourceCacheVk : public ShaderResourceCacheBase Uint32 GetNumPushConstantBuffers() const { return m_NumPushConstantBuffers; } - // Initializes inline constant buffers array - void InitializeInlineConstantBuffers(Uint32 NumBuffers) - { - m_NumInlineConstantBuffers = static_cast(NumBuffers); - if (NumBuffers > 0) - { - m_pInlineConstantBuffers = std::make_unique[]>(NumBuffers); - } - } - - // Sets the inline constant buffer at the given ResIndex - void SetInlineConstantBuffer(Uint32 ResIndex, RefCntAutoPtr&& pBuffer) - { - VERIFY_EXPR(ResIndex < m_NumInlineConstantBuffers); - m_pInlineConstantBuffers[ResIndex] = std::move(pBuffer); - } - - // Gets the inline constant buffer at the given ResIndex - BufferVkImpl* GetInlineConstantBuffer(Uint32 ResIndex) const - { - VERIFY_EXPR(ResIndex < m_NumInlineConstantBuffers); - return m_pInlineConstantBuffers[ResIndex].RawPtr(); - } - - Uint32 GetNumInlineConstantBuffers() const { return m_NumInlineConstantBuffers; } - Uint32 GetNumDescriptorSets() const { return m_NumSets; } bool HasDynamicResources() const { return m_NumDynamicBuffers > 0; } @@ -387,11 +361,9 @@ class ShaderResourceCacheVk : public ShaderResourceCacheBase // Each pointer points to memory within m_pInlineConstantMemory std::unique_ptr m_pPushConstantDataPtrs; - // Array of inline constant buffers (one per emulated inline constant buffer) - // Each SRB has its own copy of these buffers to avoid conflicts between - // multiple PipelineStates sharing the same PipelineResourceSignature - std::unique_ptr[]> m_pInlineConstantBuffers; - Uint16 m_NumInlineConstantBuffers = 0; + // Note: Inline constant buffers (for emulated inline constants) are now stored in + // InlineConstantBufferAttribsVk::pBuffer in the PipelineResourceSignature, similar to D3D11. + // All SRBs share the same buffer to reduce memory usage. Uint16 m_NumSets = 0; Uint16 m_NumPushConstantBuffers = 0; diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp index a5af5ac4e1..c19f4db4f9 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp @@ -414,8 +414,26 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) InlineCBAttribs.BindingIndex = pAttribs->BindingIndex; InlineCBAttribs.NumConstants = ResDesc.ArraySize; // For inline constants, ArraySize is the number of 32-bit constants InlineCBAttribs.IsPushConstant = IsPushConstant; - // Note: Per-SRB buffers are created in InitSRBResourceCache() to avoid conflicts - // between multiple PipelineStates sharing the same PipelineResourceSignature + + // For emulated inline constants (not push constants), create a shared buffer in the Signature + // All SRBs will reference this same buffer (similar to D3D11 backend) + if (!IsPushConstant && m_pDevice) + { + std::string Name = m_Desc.Name; + Name += " - "; + Name += ResDesc.Name; + BufferDesc CBDesc; + CBDesc.Name = Name.c_str(); + CBDesc.Size = ResDesc.ArraySize * sizeof(Uint32); + CBDesc.Usage = USAGE_DYNAMIC; + CBDesc.BindFlags = BIND_UNIFORM_BUFFER; + CBDesc.CPUAccessFlags = CPU_ACCESS_WRITE; + + RefCntAutoPtr pBuffer; + m_pDevice->CreateBuffer(CBDesc, nullptr, &pBuffer); + VERIFY_EXPR(pBuffer); + InlineCBAttribs.pBuffer = RefCntAutoPtr{pBuffer, IID_BufferVk}; + } } } VERIFY_EXPR(InlineConstantBufferIdx == m_NumInlineConstantBufferAttribs); @@ -596,7 +614,7 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) Uint32 StaticPushConstantIdx = 0; for (Uint32 i = 0; i < m_NumInlineConstantBufferAttribs; ++i) { - const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBufferAttribs[i]; + InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBufferAttribs[i]; const PipelineResourceDesc& ResDesc = GetResourceDesc(InlineCBAttr.ResIndex); const ResourceAttribs& Attr = GetResourceAttribs(InlineCBAttr.ResIndex); @@ -607,8 +625,10 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) if (InlineCBAttr.IsPushConstant) { - // For push constants, store the data pointer in the static resource cache + // For push constants, store the data pointer both in the static resource cache + // and in InlineCBAttr for easy access during CopyStaticResources m_pStaticResCache->SetPushConstantDataPtr(StaticPushConstantIdx++, pCurrentDataPtr); + InlineCBAttr.pPushConstantData = pCurrentDataPtr; } else { @@ -636,6 +656,18 @@ void PipelineResourceSignatureVkImpl::Destruct() GetDevice()->SafeReleaseDeviceObject(std::move(Layout), ~0ull); } + // Release shared inline constant buffers before base class Destruct + // Each InlineConstantBufferAttribsVk::pBuffer holds a RefCntAutoPtr to the shared buffer + if (m_InlineConstantBufferAttribs) + { + for (Uint32 i = 0; i < m_NumInlineConstantBufferAttribs; ++i) + { + m_InlineConstantBufferAttribs[i].pBuffer.Release(); + } + m_InlineConstantBufferAttribs.reset(); + } + m_NumInlineConstantBufferAttribs = 0; + // Note: Static inline constant data is now managed by m_pStaticResCache // and will be freed when the cache is destroyed @@ -673,15 +705,15 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk // Initialize inline constant buffers - allocate staging memory and set up data pointers // For push constants, each SRB gets its own copy of the data (stored in ShaderResourceCacheVk) - // For emulated inline constants, each SRB gets its own buffer to avoid conflicts between - // multiple PipelineStates sharing the same PipelineResourceSignature + // For emulated inline constants, all SRBs share the same buffer (stored in InlineConstantBufferAttribsVk::pBuffer) + // This is similar to D3D11 backend design where buffers are created in the Signature and shared by all SRBs if (m_NumInlineConstantBufferAttribs > 0) { // Ensure the cache reports inline constants so DeviceContextVkImpl // updates emulated buffers even if no data has been written yet. ResourceCache.MarkHasInlineConstants(); - // Count push constant buffers, emulated inline constant buffers, and calculate total memory size + // Calculate total memory size for CPU staging data Uint32 TotalInlineConstantSize = 0; for (Uint32 i = 0; i < m_NumInlineConstantBufferAttribs; ++i) { @@ -689,13 +721,12 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk TotalInlineConstantSize += InlineCBAttr.NumConstants * sizeof(Uint32); } - // Initialize push constant data pointers array in the resource cache, use ResIndex to access actual PushConstantDataPtr later. + // Initialize push constant data pointers array in the resource cache + // Use TotalResources as the array size to allow direct indexing by ResIndex ResourceCache.InitializePushConstantDataPtrs(TotalResources); - // Initialize inline constant buffers array in the resource cache, use ResIndex to access actual InlineConstantBuffer later. - ResourceCache.InitializeInlineConstantBuffers(TotalResources); - - // Allocate memory for all inline constants + // Allocate memory for all inline constants (CPU staging data) + // Each SRB has its own copy of the staging data, but shares the GPU buffer void* pInlineConstantMemory = nullptr; if (TotalInlineConstantSize > 0) { @@ -706,7 +737,7 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk ResourceCache.SetInlineConstantMemory(CacheMemAllocator, pInlineConstantMemory); } - // Assign memory to each inline constant buffer and create per-SRB buffers + // Assign staging memory to each inline constant buffer Uint8* pCurrentDataPtr = static_cast(pInlineConstantMemory); for (Uint32 i = 0; i < m_NumInlineConstantBufferAttribs; ++i) { @@ -718,40 +749,16 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk // For push constants, store the data pointer in the resource cache // Each SRB has its own copy of push constant data ResourceCache.SetPushConstantDataPtr(InlineCBAttr.ResIndex, pCurrentDataPtr); - pCurrentDataPtr += DataSize; } else { - // For emulated inline constants, create a per-SRB buffer - const PipelineResourceDesc& ResDesc = GetResourceDesc(InlineCBAttr.ResIndex); - const ResourceAttribs& Attr = GetResourceAttribs(InlineCBAttr.ResIndex); - - // Initialize the inline constant data in the resource cache + // For emulated inline constants, initialize staging memory in the resource cache + // The GPU buffer is shared from InlineCBAttr.pBuffer (created in CreateSetLayouts) + const ResourceAttribs& Attr = GetResourceAttribs(InlineCBAttr.ResIndex); ResourceCache.InitializeInlineConstantBuffer(Attr.DescrSet, Attr.CacheOffset(CacheType), InlineCBAttr.NumConstants, pCurrentDataPtr); - - // Create a per-SRB USAGE_DYNAMIC uniform buffer - if (m_pDevice) - { - std::string Name = m_Desc.Name; - Name += " - "; - Name += ResDesc.Name; - Name += " (SRB)"; - BufferDesc CBDesc; - CBDesc.Name = Name.c_str(); - CBDesc.Size = DataSize; - CBDesc.Usage = USAGE_DYNAMIC; - CBDesc.BindFlags = BIND_UNIFORM_BUFFER; - CBDesc.CPUAccessFlags = CPU_ACCESS_WRITE; - - RefCntAutoPtr pBuffer; - m_pDevice->CreateBuffer(CBDesc, nullptr, &pBuffer); - VERIFY_EXPR(pBuffer); - ResourceCache.SetInlineConstantBuffer(InlineCBAttr.ResIndex, RefCntAutoPtr{pBuffer, IID_BufferVk}); - } - - pCurrentDataPtr += DataSize; } + pCurrentDataPtr += DataSize; } } @@ -771,8 +778,9 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk ResourceCache.AssignDescriptorSetAllocation(GetDescriptorSetIndex(), std::move(SetAllocation)); } - // Bind internal inline constant buffers to the resource cache + // Bind shared inline constant buffers to the resource cache // This must be done after descriptor set allocation so that descriptor writes work correctly + // The buffers are created in CreateSetLayouts() and shared by all SRBs (similar to D3D11) for (Uint32 i = 0; i < m_NumInlineConstantBufferAttribs; ++i) { const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBufferAttribs[i]; @@ -781,8 +789,8 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk if (InlineCBAttr.IsPushConstant) continue; - // Get the per-SRB buffer from the resource cache - BufferVkImpl* pBuffer = ResourceCache.GetInlineConstantBuffer(InlineCBAttr.ResIndex); + // Get the shared buffer from the Signature (created in CreateSetLayouts) + BufferVkImpl* pBuffer = InlineCBAttr.pBuffer.RawPtr(); if (!pBuffer) continue; @@ -796,7 +804,7 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk // Note: Dynamic descriptor sets are allocated per-draw call, so we can't write to them here if (ResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) { - // Bind the internal uniform buffer to the resource cache + // Bind the shared uniform buffer to the resource cache ResourceCache.SetResource( &GetDevice()->GetLogicalDevice(), Attr.DescrSet, @@ -1363,19 +1371,20 @@ void PipelineResourceSignatureVkImpl::UpdateInlineConstantBuffers(const ShaderRe continue; } - // For emulated inline constants, get the per-SRB buffer from the resource cache - BufferVkImpl* pBuffer = ResourceCache.GetInlineConstantBuffer(InlineCBAttr.ResIndex); + // For emulated inline constants, get the shared buffer from the Signature + // All SRBs share this buffer (similar to D3D11 backend) + BufferVkImpl* pBuffer = InlineCBAttr.pBuffer.RawPtr(); if (pBuffer) { - // Get data from the resource cache using the correct CacheOffset - // Similar to D3D11 backend which uses BindPoints to locate the resource + // Get data from the SRB's resource cache using the correct CacheOffset + // Each SRB has its own CPU staging data, but shares the GPU buffer const ResourceAttribs& Attr = GetResourceAttribs(InlineCBAttr.ResIndex); const Uint32 CacheOffset = Attr.CacheOffset(CacheType); const void* pInlineConstantData = ResourceCache.GetInlineConstantData(Attr.DescrSet, CacheOffset); VERIFY_EXPR(pInlineConstantData != nullptr); - // Map the buffer and copy the data + // Map the shared buffer and copy the data void* pMappedData = nullptr; Ctx.MapBuffer(pBuffer, MAP_WRITE, MAP_FLAG_DISCARD, pMappedData); memcpy(pMappedData, pInlineConstantData, DataSize); diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp index 740a534366..0044b62c37 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp @@ -690,7 +690,8 @@ PipelineResourceSignatureDescWrapper PipelineStateVkImpl::GetDefaultResourceSign PipelineResourceDesc& Res = const_cast(SignDesc.Get().Resources[i]); if ((Res.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0) { - Res.Flags |= PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT; + //TODO: making first inline constant a push constant will lead to assert on CreateSetLayouts + //Res.Flags |= PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT; break; } } From 11b59182b5a5a86a70d41ebfcd7989dac442991a Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Tue, 9 Dec 2025 21:26:48 +0800 Subject: [PATCH 074/104] Fix bad SRBCacheOffset. --- .../src/PipelineResourceSignatureVkImpl.cpp | 19 ++++++++++++++++--- .../src/PipelineStateVkImpl.cpp | 3 +-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp index c19f4db4f9..9500b6fb06 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp @@ -350,7 +350,7 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) DescrSetValue, pVkImmutableSamplers != nullptr, IsPushConstant, - CacheGroupOffsets[CacheGroup], + IsPushConstant ? ~0u : CacheGroupOffsets[CacheGroup], // SRBCacheOffset - push constants don't use it ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC ? StaticCacheOffset : ~0u // }; } @@ -367,8 +367,8 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) "Deserialized descriptor set (", pAttribs->DescrSet, ") is invalid: ", DSMapping[SetId], " is expected."); DEV_CHECK_ERR(pAttribs->IsImmutableSamplerAssigned() == (pVkImmutableSamplers != nullptr), "Immutable sampler flag is invalid"); DEV_CHECK_ERR(pAttribs->IsPushConstantBuffer() == IsPushConstant, "Push constant flag is invalid"); - DEV_CHECK_ERR(pAttribs->SRBCacheOffset == CacheGroupOffsets[CacheGroup], - "SRB cache offset (", pAttribs->SRBCacheOffset, ") is invalid: ", CacheGroupOffsets[CacheGroup], " is expected."); + DEV_CHECK_ERR(pAttribs->SRBCacheOffset == (IsPushConstant ? ~0u : CacheGroupOffsets[CacheGroup]), + "SRB cache offset (", pAttribs->SRBCacheOffset, ") is invalid: ", (IsPushConstant ? ~0u : CacheGroupOffsets[CacheGroup]), " is expected."); DEV_CHECK_ERR(pAttribs->StaticCacheOffset == (ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC ? StaticCacheOffset : ~0u), "Static cache offset is invalid."); } @@ -868,6 +868,11 @@ void PipelineResourceSignatureVkImpl::CopyStaticResources(ShaderResourceCacheVk& if (!HasDescriptorSet(DESCRIPTOR_SET_ID_STATIC_MUTABLE) || m_pStaticResCache == nullptr) return; + // If static cache has no descriptor sets (e.g., only push constants), + // there's nothing to copy from the descriptor set. + if (m_pStaticResCache->GetNumDescriptorSets() == 0) + return; + // SrcResourceCache contains only static resources. // In case of SRB, DstResourceCache contains static, mutable and dynamic resources. // In case of Signature, DstResourceCache contains only static resources. @@ -1008,6 +1013,14 @@ void PipelineResourceSignatureVkImpl::CommitDynamicResources(const ShaderResourc { const PipelineResourceAttribsType& Attr = GetResourceAttribs(ResIdx); const PipelineResourceDesc& ResDesc = GetResourceDesc(ResIdx); + + // Push constants don't use descriptor sets, skip them + if (Attr.IsPushConstantBuffer()) + { + ++ResIdx; + continue; + } + const Uint32 CacheOffset = Attr.CacheOffset(CacheType); // For inline constants, GetArraySize() returns 1 (actual array size for cache), // while ArraySize contains the number of 32-bit constants diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp index 0044b62c37..740a534366 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp @@ -690,8 +690,7 @@ PipelineResourceSignatureDescWrapper PipelineStateVkImpl::GetDefaultResourceSign PipelineResourceDesc& Res = const_cast(SignDesc.Get().Resources[i]); if ((Res.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0) { - //TODO: making first inline constant a push constant will lead to assert on CreateSetLayouts - //Res.Flags |= PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT; + Res.Flags |= PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT; break; } } From fb2ecee657364fca10d9d617e09672ae199230d8 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Tue, 9 Dec 2025 22:36:00 +0800 Subject: [PATCH 075/104] Remove PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT, implement deferred push constant selection. --- .../src/GraphicsAccessories.cpp | 8 +- .../interface/PipelineResourceSignature.h | 13 +- .../src/PipelineResourceSignatureBase.cpp | 15 -- .../GraphicsEngine/src/PipelineStateBase.cpp | 9 - .../include/PipelineLayoutVk.hpp | 24 +- .../include/PipelineResourceAttribsVk.hpp | 25 +- .../PipelineResourceSignatureVkImpl.hpp | 42 ++- .../include/PipelineStateVkImpl.hpp | 6 +- .../include/ShaderResourceCacheVk.hpp | 43 +-- .../src/DeviceContextVkImpl.cpp | 13 +- .../src/PipelineLayoutVk.cpp | 6 +- .../src/PipelineResourceSignatureVkImpl.cpp | 249 +++++------------- .../src/PipelineStateVkImpl.cpp | 228 ++++++++-------- .../src/ShaderVariableManagerVk.cpp | 30 +-- Graphics/ShaderTools/include/SPIRVTools.hpp | 13 + .../ShaderTools/src/SPIRVShaderResources.cpp | 2 +- Graphics/ShaderTools/src/SPIRVTools.cpp | 36 ++- 17 files changed, 326 insertions(+), 436 deletions(-) diff --git a/Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp b/Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp index ed05eb0bf7..9988f3b9f8 100644 --- a/Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp +++ b/Graphics/GraphicsAccessories/src/GraphicsAccessories.cpp @@ -1637,7 +1637,7 @@ String GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAGS Flags, bool GetFul PIPELINE_RESOURCE_FLAGS Flag = ExtractLSB(Flags); - static_assert(PIPELINE_RESOURCE_FLAG_LAST == (1u << 6), "Please update the switch below to handle the new pipeline resource flag."); + static_assert(PIPELINE_RESOURCE_FLAG_LAST == (1u << 5), "Please update the switch below to handle the new pipeline resource flag."); switch (Flag) { case PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS: @@ -1664,10 +1664,6 @@ String GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAGS Flags, bool GetFul Str.append(GetFullName ? "PIPELINE_RESOURCE_FLAG_GENERAL_INPUT_ATTACHMENT" : "GENERAL_INPUT_ATTACHMENT"); break; - case PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT: - Str.append(GetFullName ? "PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT" : "VULKAN_PUSH_CONSTANT"); - break; - default: UNEXPECTED("Unexpected pipeline resource flag"); } @@ -1816,7 +1812,7 @@ PIPELINE_RESOURCE_FLAGS GetValidPipelineResourceFlags(SHADER_RESOURCE_TYPE Resou switch (ResourceType) { case SHADER_RESOURCE_TYPE_CONSTANT_BUFFER: - return PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS | PIPELINE_RESOURCE_FLAG_RUNTIME_ARRAY | PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS | PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT; + return PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS | PIPELINE_RESOURCE_FLAG_RUNTIME_ARRAY | PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS; case SHADER_RESOURCE_TYPE_TEXTURE_SRV: return PIPELINE_RESOURCE_FLAG_COMBINED_SAMPLER | PIPELINE_RESOURCE_FLAG_RUNTIME_ARRAY; diff --git a/Graphics/GraphicsEngine/interface/PipelineResourceSignature.h b/Graphics/GraphicsEngine/interface/PipelineResourceSignature.h index 458bd6272a..1e2fb02465 100644 --- a/Graphics/GraphicsEngine/interface/PipelineResourceSignature.h +++ b/Graphics/GraphicsEngine/interface/PipelineResourceSignature.h @@ -148,18 +148,7 @@ DILIGENT_TYPED_ENUM(PIPELINE_RESOURCE_FLAGS, Uint8) /// \note This flag is only valid in Vulkan. PIPELINE_RESOURCE_FLAG_GENERAL_INPUT_ATTACHMENT = 1u << 5, - /// Indicates that this inline constant is a Vulkan push constant - /// (declared with push_constant storage class in SPIR-V). - /// This flag is must be explicitly set when creating PipelineResourceSignature - /// And must be used together with PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS - /// When this flag is set, the engine will use vkCmdPushConstants - /// instead of a dynamic uniform buffer to update the constants. - /// - /// \note This flag is only valid in Vulkan and is always combined with - /// PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS. - PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT = 1u << 6, - - PIPELINE_RESOURCE_FLAG_LAST = PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT + PIPELINE_RESOURCE_FLAG_LAST = PIPELINE_RESOURCE_FLAG_GENERAL_INPUT_ATTACHMENT }; DEFINE_FLAG_ENUM_OPERATORS(PIPELINE_RESOURCE_FLAGS); diff --git a/Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp b/Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp index 82c11d54fd..65f5da4829 100644 --- a/Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp +++ b/Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp @@ -111,23 +111,8 @@ void ValidatePipelineResourceSignatureDesc(const PipelineResourceSignatureDesc& ": ", GetPipelineResourceFlagsString(AllowedResourceFlags, false, ", "), "."); } - if ((Res.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != 0) - { - if (Res.Flags != (PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS | PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT)) - { - LOG_PRS_ERROR_AND_THROW("Incorrect Desc.Resources[", i, "].Flags (", GetPipelineResourceFlagsString(Res.Flags), - "). VULKAN_PUSH_CONSTANT flags must be combined with INLINE_CONSTANTS."); - } - } - if ((Res.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0) { - if (Res.Flags != PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS && Res.Flags != (PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS | PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT)) - { - LOG_PRS_ERROR_AND_THROW("Incorrect Desc.Resources[", i, "].Flags (", GetPipelineResourceFlagsString(Res.Flags), - "). INLINE_CONSTANTS flag cannot be combined with flags other than VULKAN_PUSH_CONSTANT."); - } - if (Res.ArraySize > 64) { LOG_PRS_ERROR_AND_THROW("Desc.Resources[", i, "].ArraySize (", Res.ArraySize, diff --git a/Graphics/GraphicsEngine/src/PipelineStateBase.cpp b/Graphics/GraphicsEngine/src/PipelineStateBase.cpp index 087d53c235..376d4a54ca 100644 --- a/Graphics/GraphicsEngine/src/PipelineStateBase.cpp +++ b/Graphics/GraphicsEngine/src/PipelineStateBase.cpp @@ -849,15 +849,6 @@ void ValidatePipelineResourceCompatibility(const PipelineResourceDesc& ResDesc, GetShaderResourceTypeLiteralName(ResDesc.ResourceType), "' specified in pipeline resource signature '", SignatureName, "'."); } - if ((ResourceFlags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT)) - { - LOG_ERROR_AND_THROW("Shader '", ShaderName, "' contains resource '", ResDesc.Name, - "' that is", ((ResourceFlags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) ? "" : " not"), - " labeled as vulkan's push constant, while the same resource specified by the pipeline resource signature '", - SignatureName, "' is", ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) ? "" : " not"), - " labeled as such."); - } - if ((ResourceFlags & PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER) != (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER)) { LOG_ERROR_AND_THROW("Shader '", ShaderName, "' contains resource '", ResDesc.Name, diff --git a/Graphics/GraphicsEngineVulkan/include/PipelineLayoutVk.hpp b/Graphics/GraphicsEngineVulkan/include/PipelineLayoutVk.hpp index 9e4779a888..f7b84a729b 100644 --- a/Graphics/GraphicsEngineVulkan/include/PipelineLayoutVk.hpp +++ b/Graphics/GraphicsEngineVulkan/include/PipelineLayoutVk.hpp @@ -40,13 +40,17 @@ namespace Diligent class RenderDeviceVkImpl; class PipelineResourceSignatureVkImpl; -/// Push constant information extracted from shaders +/// Push constant information extracted from shaders or selected from inline constants struct PushConstantInfoVk { - VkShaderStageFlags StageFlags = 0; - Uint32 Size = 0; + VkShaderStageFlags StageFlags = 0; + Uint32 Size = 0; + Uint32 SignatureIndex = ~0u; // Index of the signature containing the push constant (~0u if none) + Uint32 ResourceIndex = ~0u; // Resource index within the signature (~0u if none) }; +static constexpr Uint32 INVALID_PUSH_CONSTANT_INDEX = ~0u; + /// Implementation of the Diligent::PipelineLayoutVk class class PipelineLayoutVk { @@ -78,6 +82,14 @@ class PipelineLayoutVk // Returns the shader stage flags for push constants VkShaderStageFlags GetPushConstantStageFlags() const { return m_PushConstantStageFlags; } + // Returns the signature index containing the push constant resource + // Returns INVALID_PUSH_CONSTANT_INDEX if no push constant is selected + Uint32 GetPushConstantSignatureIndex() const { return m_PushConstantSignatureIndex; } + + // Returns the resource index within the signature for push constant + // Returns INVALID_PUSH_CONSTANT_INDEX if no push constant is selected + Uint32 GetPushConstantResourceIndex() const { return m_PushConstantResourceIndex; } + private: VulkanUtilities::PipelineLayoutWrapper m_VkPipelineLayout; @@ -95,6 +107,12 @@ class PipelineLayoutVk // Shader stages that use push constants VkShaderStageFlags m_PushConstantStageFlags = 0; + // Index of the signature containing the push constant resource + Uint32 m_PushConstantSignatureIndex = INVALID_PUSH_CONSTANT_INDEX; + + // Resource index within the signature for push constant + Uint32 m_PushConstantResourceIndex = INVALID_PUSH_CONSTANT_INDEX; + #ifdef DILIGENT_DEBUG Uint32 m_DbgMaxBindIndex = 0; #endif diff --git a/Graphics/GraphicsEngineVulkan/include/PipelineResourceAttribsVk.hpp b/Graphics/GraphicsEngineVulkan/include/PipelineResourceAttribsVk.hpp index 9b030ac3c9..8b5fa67db9 100644 --- a/Graphics/GraphicsEngineVulkan/include/PipelineResourceAttribsVk.hpp +++ b/Graphics/GraphicsEngineVulkan/include/PipelineResourceAttribsVk.hpp @@ -63,6 +63,8 @@ enum class DescriptorType : Uint8 // sizeof(PipelineResourceAttribsVk) == 16, x64 +// Note: Push constant selection is deferred to PSO creation time. +// At PRS level, all inline constants are treated uniformly with descriptor sets. struct PipelineResourceAttribsVk { private: @@ -72,9 +74,9 @@ struct PipelineResourceAttribsVk static constexpr Uint32 _DescrTypeBits = 5; static constexpr Uint32 _DescrSetBits = 1; static constexpr Uint32 _SamplerAssignedBits = 1; - static constexpr Uint32 _IsPushConstantBits = 1; + static constexpr Uint32 _ReservedBits = 1; // Reserved for future use - static_assert((_BindingIndexBits + _ArraySizeBits + _SamplerIndBits + _DescrTypeBits + _DescrSetBits + _SamplerAssignedBits + _IsPushConstantBits) % 32 == 0, "Bits are not optimally packed"); + static_assert((_BindingIndexBits + _ArraySizeBits + _SamplerIndBits + _DescrTypeBits + _DescrSetBits + _SamplerAssignedBits + _ReservedBits) % 32 == 0, "Bits are not optimally packed"); // clang-format off static_assert((1u << _DescrTypeBits) > static_cast(DescriptorType::Count), "Not enough bits to store DescriptorType values"); @@ -89,11 +91,11 @@ struct PipelineResourceAttribsVk // clang-format off const Uint32 BindingIndex : _BindingIndexBits; // Binding in the descriptor set const Uint32 SamplerInd : _SamplerIndBits; // Index of the assigned sampler in m_Desc.Resources and m_pPipelineResourceAttribsVk - const Uint32 ArraySize : _ArraySizeBits; // Array size (for push constants, this is the number of 32-bit constants) + const Uint32 ArraySize : _ArraySizeBits; // Array size (for inline constants, this is the number of 32-bit constants) const Uint32 DescrType : _DescrTypeBits; // Descriptor type (DescriptorType) const Uint32 DescrSet : _DescrSetBits; // Descriptor set (0 or 1) const Uint32 ImtblSamplerAssigned : _SamplerAssignedBits; // Immutable sampler flag - const Uint32 IsPushConstant : _IsPushConstantBits; // True if this is a Vulkan push constant (not using descriptor sets) + const Uint32 Reserved : _ReservedBits; // Reserved for future use const Uint32 SRBCacheOffset; // Offset in the SRB resource cache const Uint32 StaticCacheOffset; // Offset in the static resource cache @@ -105,7 +107,6 @@ struct PipelineResourceAttribsVk DescriptorType _DescrType, Uint32 _DescrSet, bool _ImtblSamplerAssigned, - bool _IsPushConstant, Uint32 _SRBCacheOffset, Uint32 _StaticCacheOffset) noexcept : // clang-format off @@ -115,7 +116,7 @@ struct PipelineResourceAttribsVk DescrType {static_cast(_DescrType)}, DescrSet {_DescrSet }, ImtblSamplerAssigned {_ImtblSamplerAssigned ? 1u : 0u}, - IsPushConstant {_IsPushConstant ? 1u : 0u }, + Reserved {0 }, SRBCacheOffset {_SRBCacheOffset }, StaticCacheOffset {_StaticCacheOffset } // clang-format on @@ -131,7 +132,7 @@ struct PipelineResourceAttribsVk // Only for serialization PipelineResourceAttribsVk() noexcept : - PipelineResourceAttribsVk{0, 0, 0, DescriptorType::Unknown, 0, false, false, 0, 0} + PipelineResourceAttribsVk{0, 0, 0, DescriptorType::Unknown, 0, false, 0, 0} {} @@ -155,11 +156,6 @@ struct PipelineResourceAttribsVk return SamplerInd != InvalidSamplerInd; } - bool IsPushConstantBuffer() const - { - return IsPushConstant != 0; - } - bool IsCompatibleWith(const PipelineResourceAttribsVk& rhs) const { // Ignore sampler index and cache offsets. @@ -168,14 +164,13 @@ struct PipelineResourceAttribsVk ArraySize == rhs.ArraySize && DescrType == rhs.DescrType && DescrSet == rhs.DescrSet && - ImtblSamplerAssigned == rhs.ImtblSamplerAssigned && - IsPushConstant == rhs.IsPushConstant; + ImtblSamplerAssigned == rhs.ImtblSamplerAssigned; // clang-format on } size_t GetHash() const { - return ComputeHash(BindingIndex, ArraySize, DescrType, DescrSet, ImtblSamplerAssigned, IsPushConstant); + return ComputeHash(BindingIndex, ArraySize, DescrType, DescrSet, ImtblSamplerAssigned); } }; ASSERT_SIZEOF(PipelineResourceAttribsVk, 16, "The struct is used in serialization and must be tightly packed"); diff --git a/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp index 3b2655a05d..63c8eba987 100644 --- a/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/PipelineResourceSignatureVkImpl.hpp @@ -60,28 +60,24 @@ struct ImmutableSamplerAttribsVk ASSERT_SIZEOF(ImmutableSamplerAttribsVk, 8, "The struct is used in serialization and must be tightly packed"); /// Inline constant buffer attributes for Vulkan backend. -/// Inline constants can be either: -/// 1. True push constants (from SPIR-V push_constant storage class) - use vkCmdPushConstants -/// 2. Emulated inline constants - use dynamic uniform buffers (similar to D3D11 backend) +/// All inline constants are treated uniformly at PRS level - they all get: +/// - DescriptorSet binding and cache allocation +/// - Shared emulated buffer (created in the Signature, shared by all SRBs) /// -/// For emulated inline constants (not push constants), the Buffer is created in the Signature -/// and shared by all SRBs (similar to D3D11). Each SRB has its own CPU staging memory. -/// For push constants, pPushConstantData points to the static cache's staging memory. +/// Push constant selection is deferred to PSO creation time. At PSO creation, +/// one inline constant may be selected to use vkCmdPushConstants based on: +/// 1. SPIR-V reflection (ResourceType::PushConstant in shader) +/// 2. First inline constant as fallback (converted via PatchShader) struct InlineConstantBufferAttribsVk { - Uint32 ResIndex = 0; // Resource index in the signature (used for matching) - Uint32 DescrSet = 0; // Descriptor set index (0 for push constants as placeholder) - Uint32 BindingIndex = 0; // Binding index within the descriptor set - Uint32 NumConstants = 0; // Number of 32-bit constants - bool IsPushConstant = false; // True if this is a Vulkan push constant (not emulated) + Uint32 ResIndex = 0; // Resource index in the signature (used for matching) + Uint32 DescrSet = 0; // Descriptor set index + Uint32 BindingIndex = 0; // Binding index within the descriptor set + Uint32 NumConstants = 0; // Number of 32-bit constants - // For emulated inline constants: shared buffer created in the Signature (similar to D3D11) + // Shared buffer created in the Signature (similar to D3D11) // All SRBs reference this same buffer to reduce memory usage. RefCntAutoPtr pBuffer; - - // For static push constants: pointer to data in the static resource cache - // This allows CopyStaticResources to copy the data to SRB caches. - void* pPushConstantData = nullptr; }; struct PipelineResourceSignatureInternalDataVk : PipelineResourceSignatureInternalData @@ -161,9 +157,12 @@ class PipelineResourceSignatureVkImpl final : public PipelineResourceSignatureBa VkDescriptorSet vkDynamicDescriptorSet) const; // Updates inline constant buffers by mapping the internal buffers and copying data from the resource cache - // ResourceCache must be valid - each SRB has its own copy of push constant data stored in the cache + // ResourceCache must be valid - each SRB has its own copy of inline constant data stored in the cache + // PushConstantResIndex: Resource index of the inline constant selected as push constant by PSO + // Pass ~0u if no push constant is selected from this signature void UpdateInlineConstantBuffers(const ShaderResourceCacheVk& ResourceCache, - DeviceContextVkImpl& Ctx) const; + DeviceContextVkImpl& Ctx, + Uint32 PushConstantResIndex) const; // Returns the number of inline constant buffers Uint32 GetNumInlineConstantBufferAttribs() const { return m_NumInlineConstantBufferAttribs; } @@ -236,10 +235,9 @@ class PipelineResourceSignatureVkImpl final : public PipelineResourceSignatureBa // Inline constant buffer attributes std::unique_ptr m_InlineConstantBufferAttribs; - // Note: Static inline constant data (including push constants) is stored in m_pStaticResCache. - // For push constants, we use InitializePushConstantDataPtrs() and SetPushConstantDataPtr() - // in the static resource cache, similar to how SRB caches store push constant data. - // This ensures each SRB gets its own copy when CopyStaticResources() is called. + // Note: Static inline constant data is stored in m_pStaticResCache. + // Push constant selection is deferred to PSO creation time - the PRS treats + // all inline constants uniformly with emulated buffers. }; template <> Uint32 PipelineResourceSignatureVkImpl::GetDescriptorSetIndex() const; diff --git a/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp index cd473c57d5..3f0c47d132 100644 --- a/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp @@ -133,9 +133,11 @@ class PipelineStateVkImpl final : public PipelineStateBase void InitializePipeline(const ComputePipelineStateCreateInfo& CreateInfo); void InitializePipeline(const RayTracingPipelineStateCreateInfo& CreateInfo); - void InitPushConstantInfoFromSignatures(PushConstantInfoVk& PushConstant) const noexcept(false); + bool InitPushConstantInfoFromSignatures(PushConstantInfoVk& PushConstant, + TShaderStages& ShaderStages) const noexcept(false); - void PatchShaderConvertUniformBufferToPushConstant(TShaderStages& ShaderStages) const noexcept(false); + void PatchShaderConvertUniformBufferToPushConstant(TShaderStages& ShaderStages, + const PushConstantInfoVk& PushConstant) const noexcept(false); // TPipelineStateBase::Construct needs access to InitializePipeline friend TPipelineStateBase; diff --git a/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp b/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp index fd9908df33..941108805d 100644 --- a/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp +++ b/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.hpp @@ -274,35 +274,6 @@ class ShaderResourceCacheVk : public ShaderResourceCacheBase STDDeleter(Allocator)}; } - // Sets the push constant data pointer for the given ResIndex - // This is used to store per-SRB push constant data for STATIC push constants - void SetPushConstantDataPtr(Uint32 ResIndex, void* pData) - { - VERIFY_EXPR(ResIndex < m_NumPushConstantBuffers); - m_pPushConstantDataPtrs[ResIndex] = pData; - } - - // Gets the push constant data pointer for the given ResIndex - void* GetPushConstantDataPtr(Uint32 ResIndex) const - { - VERIFY_EXPR(ResIndex < m_NumPushConstantBuffers); - return m_pPushConstantDataPtrs[ResIndex]; - } - - // Initializes push constant data pointers array - void InitializePushConstantDataPtrs(Uint32 NumPushConstants) - { - m_NumPushConstantBuffers = static_cast(NumPushConstants); - if (NumPushConstants > 0) - { - m_pPushConstantDataPtrs = std::make_unique(NumPushConstants); - for (Uint32 i = 0; i < NumPushConstants; ++i) - m_pPushConstantDataPtrs[i] = nullptr; - // Mark that this cache has inline constants (push constants are a type of inline constants) - m_HasInlineConstants = 1; - } - } - // Explicitly marks that this cache contains inline constants. // This is useful when inline constant memory is initialized externally // (e.g., during SRB cache setup) before any data is written. @@ -311,8 +282,6 @@ class ShaderResourceCacheVk : public ShaderResourceCacheBase m_HasInlineConstants = 1; } - Uint32 GetNumPushConstantBuffers() const { return m_NumPushConstantBuffers; } - Uint32 GetNumDescriptorSets() const { return m_NumSets; } bool HasDynamicResources() const { return m_NumDynamicBuffers > 0; } @@ -355,18 +324,16 @@ class ShaderResourceCacheVk : public ShaderResourceCacheBase std::unique_ptr> m_pMemory; // Memory for inline constant data (allocated separately, freed in destructor) + // All inline constants use this memory for CPU-side staging. + // Push constant selection is done at PSO level, not cache level. std::unique_ptr> m_pInlineConstantMemory; - // Array of push constant data pointers (one per push constant buffer) - // Each pointer points to memory within m_pInlineConstantMemory - std::unique_ptr m_pPushConstantDataPtrs; - - // Note: Inline constant buffers (for emulated inline constants) are now stored in + // Note: Inline constant buffers (for emulated inline constants) are stored in // InlineConstantBufferAttribsVk::pBuffer in the PipelineResourceSignature, similar to D3D11. // All SRBs share the same buffer to reduce memory usage. + // Push constant selection is deferred to PSO creation time. - Uint16 m_NumSets = 0; - Uint16 m_NumPushConstantBuffers = 0; + Uint16 m_NumSets = 0; // Total actual number of dynamic buffers (that were created with USAGE_DYNAMIC) bound in the resource cache // regardless of the variable type. Note this variable is not equal to dynamic offsets count, which is constant. diff --git a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp index fbfff10376..c19f0b9308 100644 --- a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp @@ -427,7 +427,11 @@ DeviceContextVkImpl::ResourceBindInfo& DeviceContextVkImpl::GetBindInfo(PIPELINE void DeviceContextVkImpl::UpdateInlineConstantBuffers(ResourceBindInfo& BindInfo) { - const Uint32 SignCount = m_pPipelineState->GetResourceSignatureCount(); + const PipelineLayoutVk& Layout = m_pPipelineState->GetPipelineLayout(); + const Uint32 PushConstSignIdx = Layout.GetPushConstantSignatureIndex(); + const Uint32 PushConstResIdx = Layout.GetPushConstantResourceIndex(); + const Uint32 SignCount = m_pPipelineState->GetResourceSignatureCount(); + for (Uint32 i = 0; i < SignCount; ++i) { const PipelineResourceSignatureVkImpl* pSign = m_pPipelineState->GetResourceSignature(i); @@ -446,8 +450,13 @@ void DeviceContextVkImpl::UpdateInlineConstantBuffers(ResourceBindInfo& BindInfo if (!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 INVALID_PUSH_CONSTANT_INDEX to use emulated buffer path for all + const Uint32 PushConstantResIndex = (i == PushConstSignIdx) ? PushConstResIdx : INVALID_PUSH_CONSTANT_INDEX; + // Update inline constant buffers - pSign->UpdateInlineConstantBuffers(*pResourceCache, *this); + pSign->UpdateInlineConstantBuffers(*pResourceCache, *this, PushConstantResIndex); } } diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineLayoutVk.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineLayoutVk.cpp index c725be38cb..d8b3601bc1 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineLayoutVk.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineLayoutVk.cpp @@ -149,8 +149,10 @@ void PipelineLayoutVk::Create(RenderDeviceVkImpl* pDe PipelineLayoutCI.pushConstantRangeCount = 1; PipelineLayoutCI.pPushConstantRanges = &PushConstantRange; - m_PushConstantSize = PushConstant.Size; - m_PushConstantStageFlags = PushConstant.StageFlags; + m_PushConstantSize = PushConstant.Size; + m_PushConstantStageFlags = PushConstant.StageFlags; + m_PushConstantSignatureIndex = PushConstant.SignatureIndex; + m_PushConstantResourceIndex = PushConstant.ResourceIndex; } else { diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp index 9500b6fb06..0b5c08020d 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp @@ -174,23 +174,17 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) if (GetNumStaticResStages() > 0) { Uint32 StaticResourceCount = 0; // The total number of static resources in all stages - // accounting for array sizes (excluding push constants). + // accounting for array sizes. for (Uint32 i = 0; i < m_Desc.NumResources; ++i) { const PipelineResourceDesc& ResDesc = m_Desc.Resources[i]; if (ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) { - // Push constants don't use descriptor sets, so they don't need cache space - const bool IsPushConstant = (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != 0; - if (!IsPushConstant) - { - // For inline constants, GetArraySize() returns 1 (actual array size), - // while ArraySize contains the number of 32-bit constants - StaticResourceCount += ResDesc.GetArraySize(); - } + // For inline constants, GetArraySize() returns 1 (actual array size), + // while ArraySize contains the number of 32-bit constants + StaticResourceCount += ResDesc.GetArraySize(); } } - // Only initialize if there are non-push-constant static resources if (StaticResourceCount > 0) { m_pStaticResCache->InitializeSets(GetRawAllocator(), 1, &StaticResourceCount); @@ -198,25 +192,19 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) } CacheOffsetsType CacheGroupSizes = {}; // Required cache size for each cache group - BindingCountType BindingCount = {}; // Binding count in each cache group (excluding push constants) + BindingCountType BindingCount = {}; // Binding count in each cache group for (Uint32 i = 0; i < m_Desc.NumResources; ++i) { const PipelineResourceDesc& ResDesc = m_Desc.Resources[i]; const CACHE_GROUP CacheGroup = GetResourceCacheGroup(ResDesc); - // Vulkan push constants don't use descriptor sets - they use vkCmdPushConstants directly. - // So we don't count them as bindings for descriptor set layout creation. - // They also don't need descriptor set cache space since their data is stored in - // the resource cache via SetPushConstantDataPtr(). - const bool IsPushConstant = (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != 0; - if (!IsPushConstant) - { - BindingCount[CacheGroup] += 1; - // Note that we may reserve space for separate immutable samplers, which will never be used, but this is OK. - // For inline constants, GetArraySize() returns 1 (actual array size for cache), - // while ArraySize contains the number of 32-bit constants. - CacheGroupSizes[CacheGroup] += ResDesc.GetArraySize(); - } + // All resources (including inline constants) use descriptor sets. + // Push constant selection is deferred to PSO creation time. + BindingCount[CacheGroup] += 1; + // Note that we may reserve space for separate immutable samplers, which will never be used, but this is OK. + // For inline constants, GetArraySize() returns 1 (actual array size for cache), + // while ArraySize contains the number of 32-bit constants. + CacheGroupSizes[CacheGroup] += ResDesc.GetArraySize(); // Count inline constant buffers if (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) @@ -303,13 +291,10 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) VERIFY(i == 0 || ResDesc.VarType >= m_Desc.Resources[i - 1].VarType, "Resources must be sorted by variable type"); - // Vulkan push constants don't use descriptor sets - const bool IsPushConstant = (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != 0; - // If all resources are dynamic, then the signature contains only one descriptor set layout with index 0, // so remap SetId to the actual descriptor set index. - // Push constants don't use descriptor sets, so DSMapping[SetId] may be 0xFF for them. - VERIFY_EXPR(IsPushConstant || DSMapping[SetId] < MAX_DESCRIPTOR_SETS); + // All resources (including inline constants) use descriptor sets - push constant selection is deferred to PSO creation. + VERIFY_EXPR(DSMapping[SetId] < MAX_DESCRIPTOR_SETS); // The sampler may not be yet initialized, but this is OK as all resources are initialized // in the same order as in m_Desc.Resources @@ -338,20 +323,17 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) ResourceAttribs* const pAttribs = m_pResourceAttribs + i; if (!IsSerialized) { - // For push constants, use 0 as DescrSet placeholder since they don't use descriptor sets. - // The DescrSet field only has 1 bit, so it can only be 0 or 1. - const Uint32 DescrSetValue = IsPushConstant ? 0 : DSMapping[SetId]; + // All resources use descriptor sets - push constant selection is deferred to PSO creation new (pAttribs) ResourceAttribs // { BindingIndices[CacheGroup], AssignedSamplerInd, ResDesc.ArraySize, DescrType, - DescrSetValue, + DSMapping[SetId], pVkImmutableSamplers != nullptr, - IsPushConstant, - IsPushConstant ? ~0u : CacheGroupOffsets[CacheGroup], // SRBCacheOffset - push constants don't use it - ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC ? StaticCacheOffset : ~0u // + CacheGroupOffsets[CacheGroup], // SRBCacheOffset + ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC ? StaticCacheOffset : ~0u // StaticCacheOffset }; } else @@ -366,35 +348,28 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) DEV_CHECK_ERR(pAttribs->DescrSet == DSMapping[SetId], "Deserialized descriptor set (", pAttribs->DescrSet, ") is invalid: ", DSMapping[SetId], " is expected."); DEV_CHECK_ERR(pAttribs->IsImmutableSamplerAssigned() == (pVkImmutableSamplers != nullptr), "Immutable sampler flag is invalid"); - DEV_CHECK_ERR(pAttribs->IsPushConstantBuffer() == IsPushConstant, "Push constant flag is invalid"); - DEV_CHECK_ERR(pAttribs->SRBCacheOffset == (IsPushConstant ? ~0u : CacheGroupOffsets[CacheGroup]), - "SRB cache offset (", pAttribs->SRBCacheOffset, ") is invalid: ", (IsPushConstant ? ~0u : CacheGroupOffsets[CacheGroup]), " is expected."); + DEV_CHECK_ERR(pAttribs->SRBCacheOffset == CacheGroupOffsets[CacheGroup], + "SRB cache offset (", pAttribs->SRBCacheOffset, ") is invalid: ", CacheGroupOffsets[CacheGroup], " is expected."); DEV_CHECK_ERR(pAttribs->StaticCacheOffset == (ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC ? StaticCacheOffset : ~0u), "Static cache offset is invalid."); } - // Vulkan push constants don't use descriptor sets - they use vkCmdPushConstants - // So we skip creating descriptor set layout bindings for them and don't increment cache offsets - if (!IsPushConstant) - { - // For inline constants, GetArraySize() returns 1 (actual array size for cache) - CacheGroupOffsets[CacheGroup] += ResDesc.GetArraySize(); - - VkDescriptorSetLayoutBinding vkSetLayoutBinding{}; - vkSetLayoutBinding.binding = pAttribs->BindingIndex; - // For inline constants, descriptor count is 1 (single uniform buffer) - vkSetLayoutBinding.descriptorCount = ResDesc.GetArraySize(); - vkSetLayoutBinding.stageFlags = ShaderTypesToVkShaderStageFlags(ResDesc.ShaderStages); - vkSetLayoutBinding.pImmutableSamplers = pVkImmutableSamplers; - vkSetLayoutBinding.descriptorType = DescriptorTypeToVkDescriptorType(pAttribs->GetDescriptorType()); - vkSetLayoutBindings[SetId].push_back(vkSetLayoutBinding); - // Only increment binding index for non-push-constant resources - BindingIndices[CacheGroup] += 1; - } + // All resources use descriptor sets - push constant selection is deferred to PSO creation + // For inline constants, GetArraySize() returns 1 (actual array size for cache) + CacheGroupOffsets[CacheGroup] += ResDesc.GetArraySize(); - if (ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC && !IsPushConstant) + VkDescriptorSetLayoutBinding vkSetLayoutBinding{}; + vkSetLayoutBinding.binding = pAttribs->BindingIndex; + // For inline constants, descriptor count is 1 (single uniform buffer) + vkSetLayoutBinding.descriptorCount = ResDesc.GetArraySize(); + vkSetLayoutBinding.stageFlags = ShaderTypesToVkShaderStageFlags(ResDesc.ShaderStages); + vkSetLayoutBinding.pImmutableSamplers = pVkImmutableSamplers; + vkSetLayoutBinding.descriptorType = DescriptorTypeToVkDescriptorType(pAttribs->GetDescriptorType()); + vkSetLayoutBindings[SetId].push_back(vkSetLayoutBinding); + BindingIndices[CacheGroup] += 1; + + if (ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) { - // Push constants don't use descriptor sets, so we skip InitializeResources for them VERIFY(pAttribs->DescrSet == 0, "Static resources must always be allocated in descriptor set 0"); // For inline constants, GetArraySize() returns 1 (actual array size) m_pStaticResCache->InitializeResources(pAttribs->DescrSet, StaticCacheOffset, ResDesc.GetArraySize(), @@ -403,6 +378,8 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) } // Handle inline constant buffers + // All inline constants get descriptor set bindings and emulated buffers. + // Push constant selection is deferred to PSO creation time. if (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) { VERIFY(ResDesc.ResourceType == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, @@ -413,11 +390,11 @@ 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.IsPushConstant = IsPushConstant; - // For emulated inline constants (not push constants), create a shared buffer in the Signature + // Create a shared buffer in the Signature for all inline constants // All SRBs will reference this same buffer (similar to D3D11 backend) - if (!IsPushConstant && m_pDevice) + // Push constant selection is handled at PSO creation time + if (m_pDevice) { std::string Name = m_Desc.Name; Name += " - "; @@ -450,7 +427,6 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) VERIFY_EXPR(m_DynamicUniformBufferCount == CacheGroupSizes[CACHE_GROUP_DYN_UB_STAT_VAR] + CacheGroupSizes[CACHE_GROUP_DYN_UB_DYN_VAR]); VERIFY_EXPR(m_DynamicStorageBufferCount == CacheGroupSizes[CACHE_GROUP_DYN_SB_STAT_VAR] + CacheGroupSizes[CACHE_GROUP_DYN_SB_DYN_VAR]); - // When there are only push constants, m_NumSets is 0 and there are no descriptor sets to check VERIFY_EXPR(m_pStaticResCache == nullptr || GetNumDescriptorSets() == 0 || const_cast(m_pStaticResCache)->GetDescriptorSet(0).GetSize() == StaticCacheOffset); VERIFY_EXPR(CacheGroupOffsets[CACHE_GROUP_DYN_UB_STAT_VAR] == CacheGroupSizes[CACHE_GROUP_DYN_UB_STAT_VAR]); VERIFY_EXPR(CacheGroupOffsets[CACHE_GROUP_DYN_SB_STAT_VAR] == CacheGroupSizes[CACHE_GROUP_DYN_UB_STAT_VAR] + CacheGroupSizes[CACHE_GROUP_DYN_SB_STAT_VAR]); @@ -576,10 +552,8 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) // This must be done after the resources are initialized above if (m_NumInlineConstantBufferAttribs > 0 && m_pStaticResCache != nullptr) { - // First pass: calculate total memory size needed for all static inline constants - // and count static push constant buffers + // Calculate total memory size needed for all static inline constants Uint32 TotalStaticInlineConstantSize = 0; - Uint32 NumStaticPushConstantBuffers = 0; for (Uint32 i = 0; i < m_NumInlineConstantBufferAttribs; ++i) { const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBufferAttribs[i]; @@ -588,18 +562,11 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) if (ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) { TotalStaticInlineConstantSize += InlineCBAttr.NumConstants * sizeof(Uint32); - if (InlineCBAttr.IsPushConstant) - ++NumStaticPushConstantBuffers; } } - // Initialize push constant data pointers array in the static resource cache - if (NumStaticPushConstantBuffers > 0) - { - m_pStaticResCache->InitializePushConstantDataPtrs(NumStaticPushConstantBuffers); - } - // Allocate memory for all static inline constants in the static resource cache + // All inline constants use the same path - push constant selection is deferred to PSO creation if (TotalStaticInlineConstantSize > 0) { IMemoryAllocator& Allocator = GetRawAllocator(); @@ -609,33 +576,20 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) // Pass ownership of the memory to the static resource cache for proper cleanup m_pStaticResCache->SetInlineConstantMemory(Allocator, pInlineConstMemory); - // Second pass: assign memory to each static inline constant - Uint8* pCurrentDataPtr = static_cast(pInlineConstMemory); - Uint32 StaticPushConstantIdx = 0; + // Assign memory to each static inline constant + Uint8* pCurrentDataPtr = static_cast(pInlineConstMemory); for (Uint32 i = 0; i < m_NumInlineConstantBufferAttribs; ++i) { - InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBufferAttribs[i]; + const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBufferAttribs[i]; const PipelineResourceDesc& ResDesc = GetResourceDesc(InlineCBAttr.ResIndex); const ResourceAttribs& Attr = GetResourceAttribs(InlineCBAttr.ResIndex); if (ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) { - const Uint32 DataSize = InlineCBAttr.NumConstants * sizeof(Uint32); - - if (InlineCBAttr.IsPushConstant) - { - // For push constants, store the data pointer both in the static resource cache - // and in InlineCBAttr for easy access during CopyStaticResources - m_pStaticResCache->SetPushConstantDataPtr(StaticPushConstantIdx++, pCurrentDataPtr); - InlineCBAttr.pPushConstantData = pCurrentDataPtr; - } - else - { - // For emulated inline constants, initialize in the static resource cache - const Uint32 CacheOffset = Attr.StaticCacheOffset; - m_pStaticResCache->InitializeInlineConstantBuffer(0, CacheOffset, InlineCBAttr.NumConstants, pCurrentDataPtr); - } + const Uint32 DataSize = InlineCBAttr.NumConstants * sizeof(Uint32); + const Uint32 CacheOffset = Attr.StaticCacheOffset; + m_pStaticResCache->InitializeInlineConstantBuffer(0, CacheOffset, InlineCBAttr.NumConstants, pCurrentDataPtr); pCurrentDataPtr += DataSize; } } @@ -692,11 +646,6 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk const PipelineResourceDesc& ResDesc = GetResourceDesc(r); const ResourceAttribs& Attr = GetResourceAttribs(r); - // Push constants don't use descriptor sets, so we skip InitializeResources for them - const bool IsPushConstant = (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != 0; - if (IsPushConstant) - continue; - // For inline constants, GetArraySize() returns 1 (actual array size), // while ArraySize contains the number of 32-bit constants ResourceCache.InitializeResources(Attr.DescrSet, Attr.CacheOffset(CacheType), ResDesc.GetArraySize(), @@ -704,9 +653,10 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk } // Initialize inline constant buffers - allocate staging memory and set up data pointers - // For push constants, each SRB gets its own copy of the data (stored in ShaderResourceCacheVk) - // For emulated inline constants, all SRBs share the same buffer (stored in InlineConstantBufferAttribsVk::pBuffer) - // This is similar to D3D11 backend design where buffers are created in the Signature and shared by all SRBs + // All inline constants use the emulated buffer path at PRS level. + // Push constant selection is deferred to PSO creation time. + // All SRBs share the same GPU buffer (stored in InlineConstantBufferAttribsVk::pBuffer), + // but each has its own CPU staging data. if (m_NumInlineConstantBufferAttribs > 0) { // Ensure the cache reports inline constants so DeviceContextVkImpl @@ -721,10 +671,6 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk TotalInlineConstantSize += InlineCBAttr.NumConstants * sizeof(Uint32); } - // Initialize push constant data pointers array in the resource cache - // Use TotalResources as the array size to allow direct indexing by ResIndex - ResourceCache.InitializePushConstantDataPtrs(TotalResources); - // Allocate memory for all inline constants (CPU staging data) // Each SRB has its own copy of the staging data, but shares the GPU buffer void* pInlineConstantMemory = nullptr; @@ -743,21 +689,12 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk { const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBufferAttribs[i]; const Uint32 DataSize = InlineCBAttr.NumConstants * sizeof(Uint32); + const ResourceAttribs& Attr = GetResourceAttribs(InlineCBAttr.ResIndex); - if (InlineCBAttr.IsPushConstant) - { - // For push constants, store the data pointer in the resource cache - // Each SRB has its own copy of push constant data - ResourceCache.SetPushConstantDataPtr(InlineCBAttr.ResIndex, pCurrentDataPtr); - } - else - { - // For emulated inline constants, initialize staging memory in the resource cache - // The GPU buffer is shared from InlineCBAttr.pBuffer (created in CreateSetLayouts) - const ResourceAttribs& Attr = GetResourceAttribs(InlineCBAttr.ResIndex); - ResourceCache.InitializeInlineConstantBuffer(Attr.DescrSet, Attr.CacheOffset(CacheType), - InlineCBAttr.NumConstants, pCurrentDataPtr); - } + // Initialize staging memory in the resource cache + // The GPU buffer is shared from InlineCBAttr.pBuffer (created in CreateSetLayouts) + ResourceCache.InitializeInlineConstantBuffer(Attr.DescrSet, Attr.CacheOffset(CacheType), + InlineCBAttr.NumConstants, pCurrentDataPtr); pCurrentDataPtr += DataSize; } } @@ -781,14 +718,11 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk // Bind shared inline constant buffers to the resource cache // This must be done after descriptor set allocation so that descriptor writes work correctly // The buffers are created in CreateSetLayouts() and shared by all SRBs (similar to D3D11) + // Push constant selection is deferred to PSO creation - all inline constants get buffers bound here for (Uint32 i = 0; i < m_NumInlineConstantBufferAttribs; ++i) { const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBufferAttribs[i]; - // Skip push constants - they don't use descriptor sets - if (InlineCBAttr.IsPushConstant) - continue; - // Get the shared buffer from the Signature (created in CreateSetLayouts) BufferVkImpl* pBuffer = InlineCBAttr.pBuffer.RawPtr(); if (!pBuffer) @@ -838,41 +772,14 @@ void PipelineResourceSignatureVkImpl::InitSRBResourceCache(ShaderResourceCacheVk void PipelineResourceSignatureVkImpl::CopyStaticResources(ShaderResourceCacheVk& DstResourceCache) const { - // Handle STATIC push constants first - copy data from Signature's static cache to SRB's cache - // Each SRB has its own copy of push constant data - const auto ResIdxRange = GetResourceIndexRange(SHADER_RESOURCE_VARIABLE_TYPE_STATIC); - - for (Uint32 i = 0; i < m_NumInlineConstantBufferAttribs; ++i) - { - const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBufferAttribs[i]; - if (!InlineCBAttr.IsPushConstant) - continue; - - const PipelineResourceDesc& ResDesc = GetResourceDesc(InlineCBAttr.ResIndex); - if (ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) - { - // Get source data from Signature's static resource cache - const void* pSrcData = m_pStaticResCache ? m_pStaticResCache->GetPushConstantDataPtr(InlineCBAttr.ResIndex) : nullptr; - - // Get destination data pointer from SRB's resource cache - void* pDstData = DstResourceCache.GetPushConstantDataPtr(InlineCBAttr.ResIndex); - - if (pSrcData != nullptr && pDstData != nullptr) - { - const Uint32 DataSize = InlineCBAttr.NumConstants * sizeof(Uint32); - memcpy(pDstData, pSrcData, DataSize); - } - } - } - if (!HasDescriptorSet(DESCRIPTOR_SET_ID_STATIC_MUTABLE) || m_pStaticResCache == nullptr) return; - // If static cache has no descriptor sets (e.g., only push constants), - // there's nothing to copy from the descriptor set. if (m_pStaticResCache->GetNumDescriptorSets() == 0) return; + const auto ResIdxRange = GetResourceIndexRange(SHADER_RESOURCE_VARIABLE_TYPE_STATIC); + // SrcResourceCache contains only static resources. // In case of SRB, DstResourceCache contains static, mutable and dynamic resources. // In case of Signature, DstResourceCache contains only static resources. @@ -892,11 +799,7 @@ void PipelineResourceSignatureVkImpl::CopyStaticResources(ShaderResourceCacheVk& if (ResDesc.ResourceType == SHADER_RESOURCE_TYPE_SAMPLER && Attr.IsImmutableSamplerAssigned()) continue; // Skip immutable separate samplers - // Skip push constants - they were handled above - if ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != 0) - continue; - - // Handle emulated inline constants separately + // Handle inline constants separately - copy staging data if (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) { const Uint32 SrcCacheOffset = Attr.CacheOffset(SrcCacheType); @@ -1013,14 +916,6 @@ void PipelineResourceSignatureVkImpl::CommitDynamicResources(const ShaderResourc { const PipelineResourceAttribsType& Attr = GetResourceAttribs(ResIdx); const PipelineResourceDesc& ResDesc = GetResourceDesc(ResIdx); - - // Push constants don't use descriptor sets, skip them - if (Attr.IsPushConstantBuffer()) - { - ++ResIdx; - continue; - } - const Uint32 CacheOffset = Attr.CacheOffset(CacheType); // For inline constants, GetArraySize() returns 1 (actual array size for cache), // while ArraySize contains the number of 32-bit constants @@ -1360,7 +1255,8 @@ PipelineResourceSignatureInternalDataVk PipelineResourceSignatureVkImpl::GetInte } void PipelineResourceSignatureVkImpl::UpdateInlineConstantBuffers(const ShaderResourceCacheVk& ResourceCache, - DeviceContextVkImpl& Ctx) const + DeviceContextVkImpl& Ctx, + Uint32 PushConstantResIndex) const { // Determine the cache type based on the resource cache content // SRB caches use SRBCacheOffset, static caches use StaticCacheOffset @@ -1370,14 +1266,15 @@ void PipelineResourceSignatureVkImpl::UpdateInlineConstantBuffers(const ShaderRe { const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBufferAttribs[i]; const Uint32 DataSize = InlineCBAttr.NumConstants * sizeof(Uint32); + const ResourceAttribs& Attr = GetResourceAttribs(InlineCBAttr.ResIndex); + const Uint32 CacheOffset = Attr.CacheOffset(CacheType); + const void* pInlineConstantData = ResourceCache.GetInlineConstantData(Attr.DescrSet, CacheOffset); + VERIFY_EXPR(pInlineConstantData != nullptr); - if (InlineCBAttr.IsPushConstant) + // Check if this inline constant is selected as push constant by the PSO + if (InlineCBAttr.ResIndex == PushConstantResIndex) { - // For true push constants, get data from the SRB's resource cache - // Each SRB has its own copy of push constant data - const void* pInlineConstantData = ResourceCache.GetPushConstantDataPtr(InlineCBAttr.ResIndex); - VERIFY_EXPR(pInlineConstantData != nullptr); - + // This inline constant is selected as push constant at PSO level // Copy data to the device context's push constants buffer // This will be submitted via vkCmdPushConstants before draw/dispatch Ctx.SetPushConstants(pInlineConstantData, 0, DataSize); @@ -1390,19 +1287,11 @@ void PipelineResourceSignatureVkImpl::UpdateInlineConstantBuffers(const ShaderRe if (pBuffer) { - // Get data from the SRB's resource cache using the correct CacheOffset - // Each SRB has its own CPU staging data, but shares the GPU buffer - const ResourceAttribs& Attr = GetResourceAttribs(InlineCBAttr.ResIndex); - const Uint32 CacheOffset = Attr.CacheOffset(CacheType); - const void* pInlineConstantData = ResourceCache.GetInlineConstantData(Attr.DescrSet, CacheOffset); - VERIFY_EXPR(pInlineConstantData != nullptr); - // Map the shared buffer and copy the data void* pMappedData = nullptr; Ctx.MapBuffer(pBuffer, MAP_WRITE, MAP_FLAG_DISCARD, pMappedData); memcpy(pMappedData, pInlineConstantData, DataSize); Ctx.UnmapBuffer(pBuffer, MAP_WRITE); - continue; } } } diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp index 740a534366..85894e4cf3 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp @@ -668,34 +668,6 @@ PipelineResourceSignatureDescWrapper PipelineStateVkImpl::GetDefaultResourceSign } } - // If no resource has PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT explicitly set, - // find the first inline constant and mark it as a Vulkan push constant - bool HasExplicitPushConstant = false; - - for (Uint32 i = 0; i < SignDesc.Get().NumResources; ++i) - { - const PipelineResourceDesc& Res = SignDesc.Get().Resources[i]; - if ((Res.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != 0) - { - HasExplicitPushConstant = true; - break; - } - } - - if (!HasExplicitPushConstant) - { - // Find the first inline constant and add PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT flag - for (Uint32 i = 0; i < SignDesc.Get().NumResources; ++i) - { - PipelineResourceDesc& Res = const_cast(SignDesc.Get().Resources[i]); - if ((Res.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0) - { - Res.Flags |= PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT; - break; - } - } - } - return SignDesc; } @@ -754,7 +726,6 @@ void PipelineStateVkImpl::RemapOrVerifyShaderResources( const PIPELINE_RESOURCE_FLAGS Flags = SPIRVShaderResourceAttribs::GetPipelineResourceFlags(SPIRVAttribs.Type); // For push constants, skip descriptor set operations but validate the resource exists. - // Note: PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT flag must be added if (IsPushConstant) { if (ResAttribution.ResourceIndex != ResourceAttribution::InvalidResourceIndex) @@ -852,9 +823,60 @@ void PipelineStateVkImpl::RemapOrVerifyShaderResources( } } -void PipelineStateVkImpl::InitPushConstantInfoFromSignatures(PushConstantInfoVk& PushConstant) const noexcept(false) +bool PipelineStateVkImpl::InitPushConstantInfoFromSignatures(PushConstantInfoVk& PushConstant, + TShaderStages& ShaderStages) const noexcept(false) { - // Iterate through all signatures to find resources marked as VULKAN_PUSH_CONSTANT + // Step 1: Check if any shader already has a push constant declared in SPIR-V + // This happens when the shader source uses layout(push_constant) explicitly + for (const ShaderStageInfo& Stage : ShaderStages) + { + for (const ShaderVkImpl* pShader : Stage.Shaders) + { + const auto& pShaderResources = pShader->GetShaderResources(); + if (!pShaderResources) + continue; + + // Check for push constants in shader resources + for (Uint32 pc = 0; pc < pShaderResources->GetNumPushConstants(); ++pc) + { + const SPIRVShaderResourceAttribs& PCAttribs = pShaderResources->GetPushConstant(pc); + + // Find matching resource in signatures + for (Uint32 s = 0; s < m_SignatureCount; ++s) + { + const PipelineResourceSignatureVkImpl* pSignature = m_Signatures[s]; + if (pSignature == nullptr) + continue; + + for (Uint32 r = 0; r < pSignature->GetTotalResourceCount(); ++r) + { + const PipelineResourceDesc& ResDesc = pSignature->GetResourceDesc(r); + if (strcmp(ResDesc.Name, PCAttribs.Name) == 0) + { + // Found matching resource - this is the push constant + const Uint32 PCSize = PCAttribs.BufferStaticSize; + + PushConstant.Size = PCSize; + PushConstant.SignatureIndex = s; + PushConstant.ResourceIndex = r; + + // Add shader stages + for (SHADER_TYPE ShaderTypes = ResDesc.ShaderStages; ShaderTypes != SHADER_TYPE_UNKNOWN;) + { + const SHADER_TYPE ShaderType = ExtractLSB(ShaderTypes); + PushConstant.StageFlags |= ShaderTypeToVkShaderStageFlagBit(ShaderType); + } + + // Found push constant from SPIR-V, we're done + return true; + } + } + } + } + } + } + + // Step 2: No explicit push constant in SPIR-V, select first inline constant from signatures for (Uint32 s = 0; s < m_SignatureCount; ++s) { const PipelineResourceSignatureVkImpl* pSignature = m_Signatures[s]; @@ -865,33 +887,31 @@ void PipelineStateVkImpl::InitPushConstantInfoFromSignatures(PushConstantInfoVk& { const PipelineResourceDesc& ResDesc = pSignature->GetResourceDesc(r); - // Check if this resource is marked as a Vulkan push constant - if ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != 0) + // Check if this resource is marked as inline constant + if ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0) { - // For push constants, ArraySize contains the number of 32-bit constants + // For inline constants, ArraySize contains the number of 32-bit constants const Uint32 PCSize = ResDesc.ArraySize * sizeof(Uint32); - if (PushConstant.Size == 0) - { - // First push constant resource - record the size - PushConstant.Size = PCSize; - } - else if (PushConstant.Size != PCSize) - { - // Multiple push constant resources with different sizes - // Take the maximum size (Vulkan allows only one push constant block per pipeline) - PushConstant.Size = std::max(PushConstant.Size, PCSize); - } + PushConstant.Size = PCSize; + PushConstant.SignatureIndex = s; + PushConstant.ResourceIndex = r; - // Add shader stages to the stage flags - for (SHADER_TYPE ShaderStages = ResDesc.ShaderStages; ShaderStages != SHADER_TYPE_UNKNOWN;) + // Add shader stages + for (SHADER_TYPE ShaderTypes = ResDesc.ShaderStages; ShaderTypes != SHADER_TYPE_UNKNOWN;) { - const SHADER_TYPE ShaderType = ExtractLSB(ShaderStages); + const SHADER_TYPE ShaderType = ExtractLSB(ShaderTypes); PushConstant.StageFlags |= ShaderTypeToVkShaderStageFlagBit(ShaderType); } + + // Found first inline constant, we're done + return true; } } } + + // No inline constants found - PushConstant remains empty + return false; } void PipelineStateVkImpl::InitPipelineLayout(const PipelineStateCreateInfo& CreateInfo, TShaderStages& ShaderStages) noexcept(false) @@ -908,18 +928,16 @@ void PipelineStateVkImpl::InitPipelineLayout(const PipelineStateCreateInfo& Crea DvpValidateResourceLimits(); #endif - // Extract push constant information from signatures // Vulkan allows only one push constant block per pipeline, but it can be accessed from multiple shader stages. - // We use resource attributes from m_Signatures instead of from shader stages because we may patch SPIRV - // later according to m_Signatures definitions (e.g., converting uniform buffers to push constants). + // First check if SPIR-V already has a push constant, otherwise select the first inline constant and promote it to push constant. PushConstantInfoVk PushConstant; - InitPushConstantInfoFromSignatures(PushConstant); + InitPushConstantInfoFromSignatures(PushConstant, ShaderStages); m_PipelineLayout.Create(GetDevice(), m_Signatures, m_SignatureCount, PushConstant); - // Check if any resource in shader is a uniform buffer but marked as VULKAN_PUSH_CONSTANT in signatures. - // If so, convert the uniform buffer to push constant in SPIRV bytecode. - PatchShaderConvertUniformBufferToPushConstant(ShaderStages); + // If we promoted an inline constant as push constant (not an existing SPIR-V push constant), + // convert the uniform buffer to push constant in SPIRV bytecode. + PatchShaderConvertUniformBufferToPushConstant(ShaderStages, PushConstant); const bool RemapResources = (CreateInfo.Flags & PSO_CREATE_FLAG_DONT_REMAP_SHADER_RESOURCES) == 0; const bool VerifyBindings = !RemapResources && ((InternalFlags & PSO_CREATE_INTERNAL_FLAG_NO_SHADER_REFLECTION) == 0); @@ -947,81 +965,83 @@ void PipelineStateVkImpl::InitPipelineLayout(const PipelineStateCreateInfo& Crea } } -void PipelineStateVkImpl::PatchShaderConvertUniformBufferToPushConstant(TShaderStages& ShaderStages) const noexcept(false) +void PipelineStateVkImpl::PatchShaderConvertUniformBufferToPushConstant(TShaderStages& ShaderStages, + const PushConstantInfoVk& PushConstantInfo) const noexcept(false) { - // Build a set of resource names that need to be converted to push constants - std::unordered_set PushConstantNames; - for (Uint32 s = 0; s < m_SignatureCount; ++s) - { - const PipelineResourceSignatureVkImpl* pSignature = m_Signatures[s]; - if (pSignature == nullptr) - continue; - - for (Uint32 r = 0; r < pSignature->GetTotalResourceCount(); ++r) - { - const PipelineResourceDesc& ResDesc = pSignature->GetResourceDesc(r); - if ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != 0) - { - PushConstantNames.insert(ResDesc.Name); - } - } - } + // If no push constant was selected, no patching needed + if (PushConstantInfo.SignatureIndex == INVALID_PUSH_CONSTANT_INDEX || + PushConstantInfo.ResourceIndex == INVALID_PUSH_CONSTANT_INDEX) + return; - if (PushConstantNames.empty()) + // Get the name of the selected push constant resource + const PipelineResourceSignatureVkImpl* pSignature = m_Signatures[PushConstantInfo.SignatureIndex]; + if (pSignature == nullptr) return; - // For each shader stage, check if any uniform buffer needs to be patched + const PipelineResourceDesc& ResDesc = pSignature->GetResourceDesc(PushConstantInfo.ResourceIndex); + const std::string PushConstantName = ResDesc.Name; + + // For each shader stage, check if the uniform buffer needs to be patched for (ShaderStageInfo& Stage : ShaderStages) { for (size_t i = 0; i < Stage.Shaders.size(); ++i) { ShaderVkImpl* pShader = const_cast(Stage.Shaders[i]); + const SPIRVShaderResources* pRes = pShader->GetShaderResources().get(); - bool ShouldPatchUniformBuffer = false; - std::string PatchingUniformBufferName; + if (pRes == nullptr) + continue; + + // First check if the shader already has this as push constant + bool AlreadyPushConstant = false; + for (Uint32 pc = 0; pc < pRes->GetNumPushConstants(); ++pc) { - const SPIRVShaderResources* pRes = pShader->GetShaderResources().get(); + const SPIRVShaderResourceAttribs& PCAttribs = pRes->GetPushConstant(pc); + if (PCAttribs.Name == PushConstantName) + { + AlreadyPushConstant = true; + break; + } + } + + // If already push constant, no conversion needed + if (AlreadyPushConstant) + continue; - if (pRes == nullptr) - continue; + // Check if this shader has a uniform buffer with the push constant name + bool ShouldPatchUniformBuffer = false; - // Check each uniform buffer in the shader - for (Uint32 ub = 0; ub < pRes->GetNumUBs(); ++ub) + for (Uint32 ub = 0; ub < pRes->GetNumUBs(); ++ub) + { + const SPIRVShaderResourceAttribs& UBAttribs = pRes->GetUB(ub); + if (UBAttribs.Name == PushConstantName) { - const SPIRVShaderResourceAttribs& UBAttribs = pRes->GetUB(ub); - - // If this uniform buffer is marked as push constant in the signature, - // convert it in the SPIRV bytecode - if (PushConstantNames.count(UBAttribs.Name) > 0) - { - PatchingUniformBufferName = UBAttribs.Name; - ShouldPatchUniformBuffer = true; - break; - } + ShouldPatchUniformBuffer = true; + break; } } - if(ShouldPatchUniformBuffer) + if (ShouldPatchUniformBuffer) { const std::vector& SPIRV = Stage.SPIRVs[i]; - std::vector ConvertedSPIRV = PatchSPIRVConvertUniformBufferToPushConstant( + std::vector PatchedSPIRV = PatchSPIRVConvertUniformBufferToPushConstant( SPIRV, SPV_ENV_MAX, // Auto-detect target environment - PatchingUniformBufferName); + PushConstantName); - if (!ConvertedSPIRV.empty()) + if (!PatchedSPIRV.empty()) { - Stage.SPIRVs[i] = ConvertedSPIRV; + Stage.SPIRVs[i] = PatchedSPIRV; - pShader->SetSPIRV(ConvertedSPIRV); + pShader->SetSPIRV(PatchedSPIRV); - //Reconstruct shader resources from SPIRV + // Reconstruct shader resources from SPIRV pShader->CreateSPIRVShaderResources(); } else { - LOG_ERROR_MESSAGE("Failed to convert uniform buffer '", PatchingUniformBufferName, - "' to push constant in shader '", pShader->GetDesc().Name, "'"); + LOG_ERROR_MESSAGE("Failed to convert uniform buffer '", PushConstantName, + "' to push constant in shader '", pShader->GetDesc().Name, "'"); } } } @@ -1195,10 +1215,8 @@ void PipelineStateVkImpl::DvpValidateResourceLimits() const const PipelineResourceDesc& ResDesc = pSignature->GetResourceDesc(r); const PipelineResourceSignatureVkImpl::ResourceAttribs& ResAttr = pSignature->GetResourceAttribs(r); - // Skip push constants - they don't use descriptors - if (ResAttr.IsPushConstantBuffer()) - continue; - + // All resources (including inline constants) use descriptor sets at PRS level. + // Push constant selection is deferred to PSO creation time. const Uint32 DescIndex = static_cast(ResAttr.DescrType); DescriptorCount[DescIndex] += ResDesc.GetArraySize(); diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp index 122d5a443a..4506f2b610 100644 --- a/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp +++ b/Graphics/GraphicsEngineVulkan/src/ShaderVariableManagerVk.cpp @@ -663,29 +663,13 @@ void ShaderVariableManagerVk::SetInlineConstants(Uint32 ResIndex, VerifyInlineConstants(ResDesc, pConstants, FirstConstant, NumConstants); #endif - // Check if this is a push constant (doesn't use descriptor sets) - const bool IsPushConstant = (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT) != 0; - if (IsPushConstant) - { - // Get the data pointer from the resource cache - void* pPushConstantData = m_ResourceCache.GetPushConstantDataPtr(ResIndex); - if (pPushConstantData != nullptr) - { - Uint32* pDstConstants = reinterpret_cast(pPushConstantData); - memcpy(pDstConstants + FirstConstant, pConstants, NumConstants * sizeof(Uint32)); - } - else - { - UNEXPECTED("Push constant buffer not found"); - } - } - else - { - // For emulated inline constants, use the resource cache - const ResourceCacheContentType CacheType = m_ResourceCache.GetContentType(); - const Uint32 CacheOffset = Attribs.CacheOffset(CacheType); - m_ResourceCache.SetInlineConstants(Attribs.DescrSet, CacheOffset, pConstants, FirstConstant, NumConstants); - } + // All inline constants use the same path at PRS level - store data in the resource cache. + // Push constant selection is done at PSO creation time, not here. + // The data will be used either for push constants (vkCmdPushConstants) or emulated buffers + // depending on the PSO's selection during UpdateInlineConstantBuffers(). + const ResourceCacheContentType CacheType = m_ResourceCache.GetContentType(); + const Uint32 CacheOffset = Attribs.CacheOffset(CacheType); + m_ResourceCache.SetInlineConstants(Attribs.DescrSet, CacheOffset, pConstants, FirstConstant, NumConstants); } IDeviceObject* ShaderVariableManagerVk::Get(Uint32 ArrayIndex, Uint32 ResIndex) const diff --git a/Graphics/ShaderTools/include/SPIRVTools.hpp b/Graphics/ShaderTools/include/SPIRVTools.hpp index 98bd2565b1..d37bc1974d 100644 --- a/Graphics/ShaderTools/include/SPIRVTools.hpp +++ b/Graphics/ShaderTools/include/SPIRVTools.hpp @@ -27,6 +27,7 @@ #pragma once #include +#include #include "FlagEnum.h" @@ -49,4 +50,16 @@ std::vector OptimizeSPIRV(const std::vector& SrcSPIRV, spv_target_env TargetEnv, SPIRV_OPTIMIZATION_FLAGS Passes); +/// Converts a uniform buffer variable to a push constant in SPIR-V bytecode. +/// This function modifies the storage class of the specified variable from Uniform to PushConstant, +/// and removes Binding and DescriptorSet decorations. +/// +/// \param [in] SPIRV - Source SPIR-V bytecode +/// \param [in] TargetEnv - Target SPIR-V environment (use SPV_ENV_MAX to auto-detect) +/// \param [in] BlockName - Name of the uniform buffer block to convert +/// \return Modified SPIR-V bytecode, or empty vector on failure +std::vector PatchSPIRVConvertUniformBufferToPushConstant(const std::vector& SPIRV, + spv_target_env TargetEnv, + const std::string& BlockName); + } // namespace Diligent diff --git a/Graphics/ShaderTools/src/SPIRVShaderResources.cpp b/Graphics/ShaderTools/src/SPIRVShaderResources.cpp index f059b589fd..92c15dd470 100644 --- a/Graphics/ShaderTools/src/SPIRVShaderResources.cpp +++ b/Graphics/ShaderTools/src/SPIRVShaderResources.cpp @@ -215,7 +215,7 @@ PIPELINE_RESOURCE_FLAGS SPIRVShaderResourceAttribs::GetPipelineResourceFlags(Res case SPIRVShaderResourceAttribs::ResourceType::PushConstant: // Push constants are special inline constants that use vkCmdPushConstants // Mark them with both INLINE_CONSTANTS and VULKAN_PUSH_CONSTANT flags - return PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS | PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT; + return PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS; default: return PIPELINE_RESOURCE_FLAG_NONE; diff --git a/Graphics/ShaderTools/src/SPIRVTools.cpp b/Graphics/ShaderTools/src/SPIRVTools.cpp index 75793da993..46d6bacb1a 100644 --- a/Graphics/ShaderTools/src/SPIRVTools.cpp +++ b/Graphics/ShaderTools/src/SPIRVTools.cpp @@ -83,6 +83,8 @@ void SpvOptimizerMessageConsumer( LOG_DEBUG_MESSAGE(MsgSeverity, "Spirv optimizer ", LevelText, ": ", message); } +#define SPV_SPIRV_VERSION_WORD(MAJOR, MINOR) ((uint32_t(uint8_t(MAJOR)) << 16) | (uint32_t(uint8_t(MINOR)) << 8)) + spv_target_env SpvTargetEnvFromSPIRV(const std::vector& SPIRV) { if (SPIRV.size() < 2) @@ -91,7 +93,6 @@ spv_target_env SpvTargetEnvFromSPIRV(const std::vector& SPIRV) return SPV_ENV_VULKAN_1_0; } -#define SPV_SPIRV_VERSION_WORD(MAJOR, MINOR) ((uint32_t(uint8_t(MAJOR)) << 16) | (uint32_t(uint8_t(MINOR)) << 8)) switch (SPIRV[1]) { case SPV_SPIRV_VERSION_WORD(1, 0): return SPV_ENV_VULKAN_1_0; @@ -105,6 +106,8 @@ spv_target_env SpvTargetEnvFromSPIRV(const std::vector& SPIRV) } } +#undef SPV_SPIRV_VERSION_WORD + } // namespace std::vector OptimizeSPIRV(const std::vector& SrcSPIRV, spv_target_env TargetEnv, SPIRV_OPTIMIZATION_FLAGS Passes) @@ -153,4 +156,35 @@ std::vector OptimizeSPIRV(const std::vector& SrcSPIRV, spv_t return OptimizedSPIRV; } +std::vector PatchSPIRVConvertUniformBufferToPushConstant( + const std::vector& SPIRV, + spv_target_env TargetEnv, + const std::string& BlockName) +{ + if (TargetEnv == SPV_ENV_MAX) + TargetEnv = SpvTargetEnvFromSPIRV(SPIRV); + + spvtools::Optimizer optimizer(TargetEnv); + + optimizer.SetMessageConsumer(SpvOptimizerMessageConsumer); + + // Register the pass to convert UBO to push constant + optimizer.RegisterPass(spvtools::CreateConvertUBOToPushConstantPass(BlockName)); + + spvtools::OptimizerOptions options; +#ifndef DILIGENT_DEVELOPMENT + options.set_run_validator(false); +#endif + + std::vector result; + if (!optimizer.Run(SPIRV.data(), SPIRV.size(), &result, options)) + { + return {}; + } + auto fp = fopen("d:\\testubo.spv", "wb"); + fwrite(result.data(), result.size() * 4, 1, fp); + fclose(fp); + return result; +} + } // namespace Diligent From 0708918970ae1bed96c278c707d5ec0981653059 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Tue, 9 Dec 2025 22:50:21 +0800 Subject: [PATCH 076/104] bump SPIRV-Tools --- .gitmodules | 2 +- ThirdParty/SPIRV-Tools | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 92b6928627..5041dbea9e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,7 +6,7 @@ url = https://github.com/DiligentGraphics/SPIRV-Cross [submodule "ThirdParty/SPIRV-Tools"] path = ThirdParty/SPIRV-Tools - url = https://github.com/DiligentGraphics/SPIRV-Tools + url = https://github.com/hzqst/SPIRV-Tools-DG [submodule "ThirdParty/SPIRV-Headers"] path = ThirdParty/SPIRV-Headers url = https://github.com/DiligentGraphics/SPIRV-Headers diff --git a/ThirdParty/SPIRV-Tools b/ThirdParty/SPIRV-Tools index 7f2d9ee926..dddd7c564b 160000 --- a/ThirdParty/SPIRV-Tools +++ b/ThirdParty/SPIRV-Tools @@ -1 +1 @@ -Subproject commit 7f2d9ee926f98fc77a3ed1e1e0f113b8c9c49458 +Subproject commit dddd7c564b048223023390e9452ada718679f5b3 From 7f42a31540c141673c3b69d6ee2c5d1ede0ac4d3 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Tue, 9 Dec 2025 22:56:32 +0800 Subject: [PATCH 077/104] fix formatting --- .../include/PipelineLayoutVk.hpp | 8 ++++---- .../GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp | 8 ++++---- .../GraphicsEngineVulkan/src/PipelineLayoutVk.cpp | 8 ++++---- .../src/PipelineResourceSignatureVkImpl.cpp | 12 ++++++------ .../GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp | 4 ++-- Graphics/GraphicsEngineVulkan/src/ShaderVkImpl.cpp | 1 - 6 files changed, 20 insertions(+), 21 deletions(-) diff --git a/Graphics/GraphicsEngineVulkan/include/PipelineLayoutVk.hpp b/Graphics/GraphicsEngineVulkan/include/PipelineLayoutVk.hpp index f7b84a729b..ed0eeecf89 100644 --- a/Graphics/GraphicsEngineVulkan/include/PipelineLayoutVk.hpp +++ b/Graphics/GraphicsEngineVulkan/include/PipelineLayoutVk.hpp @@ -43,10 +43,10 @@ class PipelineResourceSignatureVkImpl; /// Push constant information extracted from shaders or selected from inline constants struct PushConstantInfoVk { - VkShaderStageFlags StageFlags = 0; - Uint32 Size = 0; - Uint32 SignatureIndex = ~0u; // Index of the signature containing the push constant (~0u if none) - Uint32 ResourceIndex = ~0u; // Resource index within the signature (~0u if none) + VkShaderStageFlags StageFlags = 0; + Uint32 Size = 0; + Uint32 SignatureIndex = ~0u; // Index of the signature containing the push constant (~0u if none) + Uint32 ResourceIndex = ~0u; // Resource index within the signature (~0u if none) }; static constexpr Uint32 INVALID_PUSH_CONSTANT_INDEX = ~0u; diff --git a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp index c19f0b9308..081b15de7c 100644 --- a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp @@ -427,10 +427,10 @@ DeviceContextVkImpl::ResourceBindInfo& DeviceContextVkImpl::GetBindInfo(PIPELINE void DeviceContextVkImpl::UpdateInlineConstantBuffers(ResourceBindInfo& BindInfo) { - const PipelineLayoutVk& Layout = m_pPipelineState->GetPipelineLayout(); - const Uint32 PushConstSignIdx = Layout.GetPushConstantSignatureIndex(); - const Uint32 PushConstResIdx = Layout.GetPushConstantResourceIndex(); - const Uint32 SignCount = m_pPipelineState->GetResourceSignatureCount(); + const PipelineLayoutVk& Layout = m_pPipelineState->GetPipelineLayout(); + const Uint32 PushConstSignIdx = Layout.GetPushConstantSignatureIndex(); + const Uint32 PushConstResIdx = Layout.GetPushConstantResourceIndex(); + const Uint32 SignCount = m_pPipelineState->GetResourceSignatureCount(); for (Uint32 i = 0; i < SignCount; ++i) { diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineLayoutVk.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineLayoutVk.cpp index d8b3601bc1..4d057357d9 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineLayoutVk.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineLayoutVk.cpp @@ -149,10 +149,10 @@ void PipelineLayoutVk::Create(RenderDeviceVkImpl* pDe PipelineLayoutCI.pushConstantRangeCount = 1; PipelineLayoutCI.pPushConstantRanges = &PushConstantRange; - m_PushConstantSize = PushConstant.Size; - m_PushConstantStageFlags = PushConstant.StageFlags; - m_PushConstantSignatureIndex = PushConstant.SignatureIndex; - m_PushConstantResourceIndex = PushConstant.ResourceIndex; + m_PushConstantSize = PushConstant.Size; + m_PushConstantStageFlags = PushConstant.StageFlags; + m_PushConstantSignatureIndex = PushConstant.SignatureIndex; + m_PushConstantResourceIndex = PushConstant.ResourceIndex; } else { diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp index 0b5c08020d..f5f0fbd843 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineResourceSignatureVkImpl.cpp @@ -332,8 +332,8 @@ void PipelineResourceSignatureVkImpl::CreateSetLayouts(const bool IsSerialized) DescrType, DSMapping[SetId], pVkImmutableSamplers != nullptr, - CacheGroupOffsets[CacheGroup], // SRBCacheOffset - ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC ? StaticCacheOffset : ~0u // StaticCacheOffset + CacheGroupOffsets[CacheGroup], // SRBCacheOffset + ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC ? StaticCacheOffset : ~0u // StaticCacheOffset }; } else @@ -1264,10 +1264,10 @@ void PipelineResourceSignatureVkImpl::UpdateInlineConstantBuffers(const ShaderRe for (Uint32 i = 0; i < m_NumInlineConstantBufferAttribs; ++i) { - const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBufferAttribs[i]; - const Uint32 DataSize = InlineCBAttr.NumConstants * sizeof(Uint32); - const ResourceAttribs& Attr = GetResourceAttribs(InlineCBAttr.ResIndex); - const Uint32 CacheOffset = Attr.CacheOffset(CacheType); + const InlineConstantBufferAttribsVk& InlineCBAttr = m_InlineConstantBufferAttribs[i]; + const Uint32 DataSize = InlineCBAttr.NumConstants * sizeof(Uint32); + const ResourceAttribs& Attr = GetResourceAttribs(InlineCBAttr.ResIndex); + const Uint32 CacheOffset = Attr.CacheOffset(CacheType); const void* pInlineConstantData = ResourceCache.GetInlineConstantData(Attr.DescrSet, CacheOffset); VERIFY_EXPR(pInlineConstantData != nullptr); diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp index 85894e4cf3..8d8af7d3ec 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp @@ -979,7 +979,7 @@ void PipelineStateVkImpl::PatchShaderConvertUniformBufferToPushConstant(TShaderS return; const PipelineResourceDesc& ResDesc = pSignature->GetResourceDesc(PushConstantInfo.ResourceIndex); - const std::string PushConstantName = ResDesc.Name; + const std::string PushConstantName = ResDesc.Name; // For each shader stage, check if the uniform buffer needs to be patched for (ShaderStageInfo& Stage : ShaderStages) @@ -1023,7 +1023,7 @@ void PipelineStateVkImpl::PatchShaderConvertUniformBufferToPushConstant(TShaderS if (ShouldPatchUniformBuffer) { - const std::vector& SPIRV = Stage.SPIRVs[i]; + const std::vector& SPIRV = Stage.SPIRVs[i]; std::vector PatchedSPIRV = PatchSPIRVConvertUniformBufferToPushConstant( SPIRV, SPV_ENV_MAX, // Auto-detect target environment diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderVkImpl.cpp index 93b8634823..ebc3df6d24 100644 --- a/Graphics/GraphicsEngineVulkan/src/ShaderVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/ShaderVkImpl.cpp @@ -239,7 +239,6 @@ void ShaderVkImpl::Initialize(const ShaderCreateInfo& ShaderCI, VERIFY_EXPR(ShaderCI.ByteCode != nullptr || m_EntryPoint == ShaderCI.EntryPoint || (m_EntryPoint == "main" && (ShaderCI.CompileFlags & SHADER_COMPILE_FLAG_HLSL_TO_SPIRV_VIA_GLSL) != 0)); - } else { From ac772fab9bdae7659e0bf17771f1976352d48400 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Tue, 9 Dec 2025 23:06:51 +0800 Subject: [PATCH 078/104] restore test --- .../src/ObjectCreationFailure/PRSCreationFailureTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/DiligentCoreAPITest/src/ObjectCreationFailure/PRSCreationFailureTest.cpp b/Tests/DiligentCoreAPITest/src/ObjectCreationFailure/PRSCreationFailureTest.cpp index 57df94a36f..00948ffdf5 100644 --- a/Tests/DiligentCoreAPITest/src/ObjectCreationFailure/PRSCreationFailureTest.cpp +++ b/Tests/DiligentCoreAPITest/src/ObjectCreationFailure/PRSCreationFailureTest.cpp @@ -280,7 +280,7 @@ TEST(PRSCreationFailureTest, InvalidInlineConstantsFlag) {SHADER_TYPE_PIXEL, "g_InlineConstants", 1, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, SHADER_RESOURCE_VARIABLE_TYPE_STATIC, PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS | PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS}}; PRSDesc.Resources = Resources; PRSDesc.NumResources = _countof(Resources); - TestCreatePRSFailure(PRSDesc, "INLINE_CONSTANTS flag cannot be combined with flags other than VULKAN_PUSH_CONSTANT"); + TestCreatePRSFailure(PRSDesc, "INLINE_CONSTANTS flag cannot be combined with other flags"); } TEST(PRSCreationFailureTest, InvalidInlineConstantCount) From e2accc3775a1d5cd1d38f868d36b28fbaa1671a1 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Tue, 9 Dec 2025 23:07:36 +0800 Subject: [PATCH 079/104] restore tests --- .../src/GraphicsAccessories/GraphicsAccessoriesTest.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Tests/DiligentCoreTest/src/GraphicsAccessories/GraphicsAccessoriesTest.cpp b/Tests/DiligentCoreTest/src/GraphicsAccessories/GraphicsAccessoriesTest.cpp index 5f74fb2e9c..5b4c9f342f 100644 --- a/Tests/DiligentCoreTest/src/GraphicsAccessories/GraphicsAccessoriesTest.cpp +++ b/Tests/DiligentCoreTest/src/GraphicsAccessories/GraphicsAccessoriesTest.cpp @@ -705,7 +705,7 @@ TEST(GraphicsAccessories_GraphicsAccessories, PipelineTypeFromShaderStages) TEST(GraphicsAccessories_GraphicsAccessories, GetPipelineResourceFlagsString) { - static_assert(PIPELINE_RESOURCE_FLAG_LAST == (1u << 6), "Please add a test for the new flag here"); + static_assert(PIPELINE_RESOURCE_FLAG_LAST == (1u << 5), "Please add a test for the new flag here"); EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_NONE, true).c_str(), "PIPELINE_RESOURCE_FLAG_NONE"); EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_NONE).c_str(), "UNKNOWN"); @@ -715,14 +715,12 @@ TEST(GraphicsAccessories_GraphicsAccessories, GetPipelineResourceFlagsString) EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER, true).c_str(), "PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER"); EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_GENERAL_INPUT_ATTACHMENT, true).c_str(), "PIPELINE_RESOURCE_FLAG_GENERAL_INPUT_ATTACHMENT"); EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS, true).c_str(), "PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS"); - EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT, true).c_str(), "PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT"); EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS).c_str(), "NO_DYNAMIC_BUFFERS"); EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_COMBINED_SAMPLER).c_str(), "COMBINED_SAMPLER"); EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER).c_str(), "FORMATTED_BUFFER"); EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_GENERAL_INPUT_ATTACHMENT).c_str(), "GENERAL_INPUT_ATTACHMENT"); EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS).c_str(), "INLINE_CONSTANTS"); - EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_VULKAN_PUSH_CONSTANT).c_str(), "VULKAN_PUSH_CONSTANT"); EXPECT_STREQ(GetPipelineResourceFlagsString(PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS | PIPELINE_RESOURCE_FLAG_COMBINED_SAMPLER, true).c_str(), "PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS|PIPELINE_RESOURCE_FLAG_COMBINED_SAMPLER"); From a2bb192e990b3f7d8054e7f4477fd1a7d2cdd934 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Tue, 9 Dec 2025 23:12:01 +0800 Subject: [PATCH 080/104] remove test code. --- Graphics/ShaderTools/src/SPIRVTools.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/Graphics/ShaderTools/src/SPIRVTools.cpp b/Graphics/ShaderTools/src/SPIRVTools.cpp index 46d6bacb1a..bf05b90ee0 100644 --- a/Graphics/ShaderTools/src/SPIRVTools.cpp +++ b/Graphics/ShaderTools/src/SPIRVTools.cpp @@ -181,9 +181,6 @@ std::vector PatchSPIRVConvertUniformBufferToPushConstant( { return {}; } - auto fp = fopen("d:\\testubo.spv", "wb"); - fwrite(result.data(), result.size() * 4, 1, fp); - fclose(fp); return result; } From 2db3e635362a311fd897acb529e9f415a3173178 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Tue, 9 Dec 2025 23:24:17 +0800 Subject: [PATCH 081/104] bump submodule --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 5041dbea9e..71463a3355 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,7 +6,7 @@ url = https://github.com/DiligentGraphics/SPIRV-Cross [submodule "ThirdParty/SPIRV-Tools"] path = ThirdParty/SPIRV-Tools - url = https://github.com/hzqst/SPIRV-Tools-DG + url = https://github.com/hzqst/SPIRV-Tools [submodule "ThirdParty/SPIRV-Headers"] path = ThirdParty/SPIRV-Headers url = https://github.com/DiligentGraphics/SPIRV-Headers From c67819de0db2073ecac9b617619f6d68a67dee89 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Wed, 10 Dec 2025 10:50:24 +0800 Subject: [PATCH 082/104] Now using pShaderResources from ShaderStageInfo instead of from ShaderVkImpl, allow updating SPIRVShaderResource with ShaderStageInfo on patching SPIRV simultaneously. --- Graphics/Archiver/src/Archiver_Vk.cpp | 1 + .../include/PipelineStateVkImpl.hpp | 5 +- .../include/ShaderVkImpl.hpp | 4 +- .../src/PipelineStateVkImpl.cpp | 78 ++++++++++--------- .../GraphicsEngineVulkan/src/ShaderVkImpl.cpp | 28 ++++--- Graphics/ShaderTools/src/SPIRVTools.cpp | 6 ++ 6 files changed, 67 insertions(+), 55 deletions(-) diff --git a/Graphics/Archiver/src/Archiver_Vk.cpp b/Graphics/Archiver/src/Archiver_Vk.cpp index 0f53a74b9b..ccbf9f61eb 100644 --- a/Graphics/Archiver/src/Archiver_Vk.cpp +++ b/Graphics/Archiver/src/Archiver_Vk.cpp @@ -131,6 +131,7 @@ void SerializedPipelineStateImpl::PatchShadersVk(const CreateInfoType& CreateInf Dst.Type = Src.Type; Dst.Shaders = std::move(Src.Shaders); Dst.SPIRVs = std::move(Src.SPIRVs); + Dst.ShaderResources = std::move(Src.ShaderResources); } IPipelineResourceSignature** ppSignatures = CreateInfo.ppResourceSignatures; diff --git a/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp index 3f0c47d132..814466d17a 100644 --- a/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp @@ -86,6 +86,7 @@ class PipelineStateVkImpl final : public PipelineStateBase std::vector Shaders; std::vector> SPIRVs; + std::vector> ShaderResources;//This can be also updated due to push constant friend SHADER_TYPE GetShaderStageType(const ShaderStageInfo& Stage) { return Stage.Type; } }; @@ -136,8 +137,8 @@ class PipelineStateVkImpl final : public PipelineStateBase bool InitPushConstantInfoFromSignatures(PushConstantInfoVk& PushConstant, TShaderStages& ShaderStages) const noexcept(false); - void PatchShaderConvertUniformBufferToPushConstant(TShaderStages& ShaderStages, - const PushConstantInfoVk& PushConstant) const noexcept(false); + void PatchShaderConvertUniformBufferToPushConstant(const PushConstantInfoVk& PushConstant, + TShaderStages& ShaderStages) const noexcept(false); // TPipelineStateBase::Construct needs access to InitializePipeline friend TPipelineStateBase; diff --git a/Graphics/GraphicsEngineVulkan/include/ShaderVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/ShaderVkImpl.hpp index 343b5d8bc2..37458cd0fd 100644 --- a/Graphics/GraphicsEngineVulkan/include/ShaderVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/ShaderVkImpl.hpp @@ -110,9 +110,7 @@ class ShaderVkImpl final : public ShaderBase Size = m_SPIRV.size() * sizeof(m_SPIRV[0]); } - void SetSPIRV(const std::vector& SPIRV) noexcept(false); - - void CreateSPIRVShaderResources() noexcept(false); + std::shared_ptr CreateSPIRVShaderResources(const std::vector& SPIRV) noexcept(false); private: void Initialize(const ShaderCreateInfo& ShaderCI, diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp index 8d8af7d3ec..505334d45f 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp @@ -555,7 +555,8 @@ void VerifyResourceMerge(const char* PSOName, PipelineStateVkImpl::ShaderStageInfo::ShaderStageInfo(const ShaderVkImpl* pShader) : Type{pShader->GetDesc().ShaderType}, Shaders{pShader}, - SPIRVs{pShader->GetSPIRV()} + SPIRVs{pShader->GetSPIRV()}, + ShaderResources{pShader->GetShaderResources()} {} void PipelineStateVkImpl::ShaderStageInfo::Append(const ShaderVkImpl* pShader) @@ -578,6 +579,7 @@ void PipelineStateVkImpl::ShaderStageInfo::Append(const ShaderVkImpl* pShader) } Shaders.push_back(pShader); SPIRVs.push_back(pShader->GetSPIRV()); + ShaderResources.push_back(pShader->GetShaderResources()); } size_t PipelineStateVkImpl::ShaderStageInfo::Count() const @@ -691,16 +693,19 @@ void PipelineStateVkImpl::RemapOrVerifyShaderResources( { const std::vector& Shaders = ShaderStages[s].Shaders; std::vector>& SPIRVs = ShaderStages[s].SPIRVs; - const SHADER_TYPE ShaderType = ShaderStages[s].Type; + const SHADER_TYPE ShaderType = ShaderStages[s].Type; + std::vector>& ShaderResources = ShaderStages[s].ShaderResources; VERIFY_EXPR(Shaders.size() == SPIRVs.size()); + VERIFY_EXPR(Shaders.size() == ShaderResources.size()); for (size_t i = 0; i < Shaders.size(); ++i) { const ShaderVkImpl* pShader = Shaders[i]; std::vector& SPIRV = SPIRVs[i]; + const std::shared_ptr& pShaderResources = ShaderResources[i]; - const auto& pShaderResources = pShader->GetShaderResources(); + //const auto& pShaderResources = pShader->GetShaderResources(); VERIFY_EXPR(pShaderResources); if (pDvpShaderResources) @@ -867,7 +872,7 @@ bool PipelineStateVkImpl::InitPushConstantInfoFromSignatures(PushConstantInfoVk& PushConstant.StageFlags |= ShaderTypeToVkShaderStageFlagBit(ShaderType); } - // Found push constant from SPIR-V, we're done + // Found push constant from SPIR-V return true; } } @@ -904,7 +909,7 @@ bool PipelineStateVkImpl::InitPushConstantInfoFromSignatures(PushConstantInfoVk& PushConstant.StageFlags |= ShaderTypeToVkShaderStageFlagBit(ShaderType); } - // Found first inline constant, we're done + // Found first inline constant return true; } } @@ -937,7 +942,7 @@ void PipelineStateVkImpl::InitPipelineLayout(const PipelineStateCreateInfo& Crea // If we promoted an inline constant as push constant (not an existing SPIR-V push constant), // convert the uniform buffer to push constant in SPIRV bytecode. - PatchShaderConvertUniformBufferToPushConstant(ShaderStages, PushConstant); + PatchShaderConvertUniformBufferToPushConstant(PushConstant, ShaderStages); const bool RemapResources = (CreateInfo.Flags & PSO_CREATE_FLAG_DONT_REMAP_SHADER_RESOURCES) == 0; const bool VerifyBindings = !RemapResources && ((InternalFlags & PSO_CREATE_INTERNAL_FLAG_NO_SHADER_REFLECTION) == 0); @@ -965,8 +970,8 @@ void PipelineStateVkImpl::InitPipelineLayout(const PipelineStateCreateInfo& Crea } } -void PipelineStateVkImpl::PatchShaderConvertUniformBufferToPushConstant(TShaderStages& ShaderStages, - const PushConstantInfoVk& PushConstantInfo) const noexcept(false) +void PipelineStateVkImpl::PatchShaderConvertUniformBufferToPushConstant(const PushConstantInfoVk& PushConstantInfo, + TShaderStages& ShaderStages) const noexcept(false) { // If no push constant was selected, no patching needed if (PushConstantInfo.SignatureIndex == INVALID_PUSH_CONSTANT_INDEX || @@ -987,20 +992,39 @@ void PipelineStateVkImpl::PatchShaderConvertUniformBufferToPushConstant(TShaderS for (size_t i = 0; i < Stage.Shaders.size(); ++i) { ShaderVkImpl* pShader = const_cast(Stage.Shaders[i]); - const SPIRVShaderResources* pRes = pShader->GetShaderResources().get(); - - if (pRes == nullptr) - continue; // First check if the shader already has this as push constant bool AlreadyPushConstant = false; - for (Uint32 pc = 0; pc < pRes->GetNumPushConstants(); ++pc) + + // Check if this shader has a uniform buffer with the push constant name + bool ShouldPatchUniformBuffer = false; { - const SPIRVShaderResourceAttribs& PCAttribs = pRes->GetPushConstant(pc); - if (PCAttribs.Name == PushConstantName) + const SPIRVShaderResources* pShaderRes = pShader->GetShaderResources().get(); + + if (pShaderRes == nullptr) + continue; + + for (Uint32 pc = 0; pc < pShaderRes->GetNumPushConstants(); ++pc) + { + const SPIRVShaderResourceAttribs& PCAttribs = pShaderRes->GetPushConstant(pc); + if (PCAttribs.Name == PushConstantName) + { + AlreadyPushConstant = true; + break; + } + } + + if (!AlreadyPushConstant) { - AlreadyPushConstant = true; - break; + for (Uint32 ub = 0; ub < pShaderRes->GetNumUBs(); ++ub) + { + const SPIRVShaderResourceAttribs& UBAttribs = pShaderRes->GetUB(ub); + if (UBAttribs.Name == PushConstantName) + { + ShouldPatchUniformBuffer = true; + break; + } + } } } @@ -1008,22 +1032,10 @@ void PipelineStateVkImpl::PatchShaderConvertUniformBufferToPushConstant(TShaderS if (AlreadyPushConstant) continue; - // Check if this shader has a uniform buffer with the push constant name - bool ShouldPatchUniformBuffer = false; - - for (Uint32 ub = 0; ub < pRes->GetNumUBs(); ++ub) - { - const SPIRVShaderResourceAttribs& UBAttribs = pRes->GetUB(ub); - if (UBAttribs.Name == PushConstantName) - { - ShouldPatchUniformBuffer = true; - break; - } - } - if (ShouldPatchUniformBuffer) { const std::vector& SPIRV = Stage.SPIRVs[i]; + std::vector PatchedSPIRV = PatchSPIRVConvertUniformBufferToPushConstant( SPIRV, SPV_ENV_MAX, // Auto-detect target environment @@ -1032,11 +1044,7 @@ void PipelineStateVkImpl::PatchShaderConvertUniformBufferToPushConstant(TShaderS if (!PatchedSPIRV.empty()) { Stage.SPIRVs[i] = PatchedSPIRV; - - pShader->SetSPIRV(PatchedSPIRV); - - // Reconstruct shader resources from SPIRV - pShader->CreateSPIRVShaderResources(); + Stage.ShaderResources[i] = pShader->CreateSPIRVShaderResources(PatchedSPIRV); } else { diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderVkImpl.cpp index ebc3df6d24..475990616a 100644 --- a/Graphics/GraphicsEngineVulkan/src/ShaderVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/ShaderVkImpl.cpp @@ -235,7 +235,12 @@ void ShaderVkImpl::Initialize(const ShaderCreateInfo& ShaderCI, { if ((ShaderCI.CompileFlags & SHADER_COMPILE_FLAG_SKIP_REFLECTION) == 0) { - CreateSPIRVShaderResources(); + m_pShaderResources = CreateSPIRVShaderResources(m_SPIRV); + + if (m_Desc.ShaderType == SHADER_TYPE_VERTEX && m_pShaderResources->IsHLSLSource()) + { + m_pShaderResources->MapHLSLVertexShaderInputs(m_SPIRV); + } VERIFY_EXPR(ShaderCI.ByteCode != nullptr || m_EntryPoint == ShaderCI.EntryPoint || (m_EntryPoint == "main" && (ShaderCI.CompileFlags & SHADER_COMPILE_FLAG_HLSL_TO_SPIRV_VIA_GLSL) != 0)); @@ -249,12 +254,7 @@ void ShaderVkImpl::Initialize(const ShaderCreateInfo& ShaderCI, } } -void ShaderVkImpl::SetSPIRV(const std::vector& SPIRV) noexcept(false) -{ - m_SPIRV = SPIRV; -} - -void ShaderVkImpl::CreateSPIRVShaderResources() noexcept(false) +std::shared_ptr ShaderVkImpl::CreateSPIRVShaderResources(const std::vector& SPIRV) noexcept(false) { IMemoryAllocator& Allocator = GetRawAllocator(); @@ -262,24 +262,22 @@ void ShaderVkImpl::CreateSPIRVShaderResources() noexcept(false) ALLOCATE(Allocator, "Memory for SPIRVShaderResources", SPIRVShaderResources, 1), STDDeleterRawMem(Allocator), }; - const bool LoadShaderInputs = m_Desc.ShaderType == SHADER_TYPE_VERTEX; new (pRawMem.get()) SPIRVShaderResources // May throw { Allocator, - m_SPIRV, + SPIRV, m_Desc, m_Desc.UseCombinedTextureSamplers ? m_Desc.CombinedSamplerSuffix : nullptr, - LoadShaderInputs, + m_Desc.ShaderType == SHADER_TYPE_VERTEX ? true : false, m_LoadConstantBufferReflection, m_EntryPoint // }; - m_pShaderResources.reset(static_cast(pRawMem.release()), STDDeleterRawMem(Allocator)); + std::shared_ptr pShaderResources; - if (LoadShaderInputs && m_pShaderResources->IsHLSLSource()) - { - m_pShaderResources->MapHLSLVertexShaderInputs(m_SPIRV); - } + pShaderResources.reset(static_cast(pRawMem.release()), STDDeleterRawMem(Allocator)); + + return pShaderResources; } ShaderVkImpl::ShaderVkImpl(IReferenceCounters* pRefCounters, diff --git a/Graphics/ShaderTools/src/SPIRVTools.cpp b/Graphics/ShaderTools/src/SPIRVTools.cpp index bf05b90ee0..2cfc53a5ca 100644 --- a/Graphics/ShaderTools/src/SPIRVTools.cpp +++ b/Graphics/ShaderTools/src/SPIRVTools.cpp @@ -170,10 +170,16 @@ std::vector PatchSPIRVConvertUniformBufferToPushConstant( // Register the pass to convert UBO to push constant optimizer.RegisterPass(spvtools::CreateConvertUBOToPushConstantPass(BlockName)); + //optimizer.RegisterPass(spvtools::CreateDeadVariableEliminationPass()); + //optimizer.RegisterPerformancePasses(); spvtools::OptimizerOptions options; #ifndef DILIGENT_DEVELOPMENT + // Do not run validator in release build options.set_run_validator(false); +#else + // Run validator in debug build + options.set_run_validator(true); #endif std::vector result; From 475de54d0f574078f9611016255e01196db7538a Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Wed, 10 Dec 2025 10:56:44 +0800 Subject: [PATCH 083/104] fix formatting. --- Graphics/Archiver/src/Archiver_Vk.cpp | 6 +++--- .../include/PipelineStateVkImpl.hpp | 10 +++++----- .../src/PipelineStateVkImpl.cpp | 18 +++++++++--------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Graphics/Archiver/src/Archiver_Vk.cpp b/Graphics/Archiver/src/Archiver_Vk.cpp index ccbf9f61eb..312ed32d79 100644 --- a/Graphics/Archiver/src/Archiver_Vk.cpp +++ b/Graphics/Archiver/src/Archiver_Vk.cpp @@ -128,9 +128,9 @@ void SerializedPipelineStateImpl::PatchShadersVk(const CreateInfoType& CreateInf { ShaderStageInfoVk& Src{ShaderStages[i]}; PipelineStateVkImpl::ShaderStageInfo& Dst{ShaderStagesVk[i]}; - Dst.Type = Src.Type; - Dst.Shaders = std::move(Src.Shaders); - Dst.SPIRVs = std::move(Src.SPIRVs); + Dst.Type = Src.Type; + Dst.Shaders = std::move(Src.Shaders); + Dst.SPIRVs = std::move(Src.SPIRVs); Dst.ShaderResources = std::move(Src.ShaderResources); } diff --git a/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp index 814466d17a..8691afc051 100644 --- a/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.hpp @@ -84,9 +84,9 @@ class PipelineStateVkImpl final : public PipelineStateBase // Shader stage type. All shaders in the stage must have the same type. SHADER_TYPE Type = SHADER_TYPE_UNKNOWN; - std::vector Shaders; - std::vector> SPIRVs; - std::vector> ShaderResources;//This can be also updated due to push constant + std::vector Shaders; + std::vector> SPIRVs; + std::vector> ShaderResources; //This can be also updated due to push constant friend SHADER_TYPE GetShaderStageType(const ShaderStageInfo& Stage) { return Stage.Type; } }; @@ -137,8 +137,8 @@ class PipelineStateVkImpl final : public PipelineStateBase bool InitPushConstantInfoFromSignatures(PushConstantInfoVk& PushConstant, TShaderStages& ShaderStages) const noexcept(false); - void PatchShaderConvertUniformBufferToPushConstant(const PushConstantInfoVk& PushConstant, - TShaderStages& ShaderStages) const noexcept(false); + void PatchShaderConvertUniformBufferToPushConstant(const PushConstantInfoVk& PushConstant, + TShaderStages& ShaderStages) const noexcept(false); // TPipelineStateBase::Construct needs access to InitializePipeline friend TPipelineStateBase; diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp index 505334d45f..fba530e53f 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp @@ -691,8 +691,8 @@ void PipelineStateVkImpl::RemapOrVerifyShaderResources( // remap resource bindings. for (size_t s = 0; s < ShaderStages.size(); ++s) { - const std::vector& Shaders = ShaderStages[s].Shaders; - std::vector>& SPIRVs = ShaderStages[s].SPIRVs; + const std::vector& Shaders = ShaderStages[s].Shaders; + std::vector>& SPIRVs = ShaderStages[s].SPIRVs; const SHADER_TYPE ShaderType = ShaderStages[s].Type; std::vector>& ShaderResources = ShaderStages[s].ShaderResources; @@ -701,8 +701,8 @@ void PipelineStateVkImpl::RemapOrVerifyShaderResources( for (size_t i = 0; i < Shaders.size(); ++i) { - const ShaderVkImpl* pShader = Shaders[i]; - std::vector& SPIRV = SPIRVs[i]; + const ShaderVkImpl* pShader = Shaders[i]; + std::vector& SPIRV = SPIRVs[i]; const std::shared_ptr& pShaderResources = ShaderResources[i]; //const auto& pShaderResources = pShader->GetShaderResources(); @@ -971,7 +971,7 @@ void PipelineStateVkImpl::InitPipelineLayout(const PipelineStateCreateInfo& Crea } void PipelineStateVkImpl::PatchShaderConvertUniformBufferToPushConstant(const PushConstantInfoVk& PushConstantInfo, - TShaderStages& ShaderStages) const noexcept(false) + TShaderStages& ShaderStages) const noexcept(false) { // If no push constant was selected, no patching needed if (PushConstantInfo.SignatureIndex == INVALID_PUSH_CONSTANT_INDEX || @@ -991,7 +991,7 @@ void PipelineStateVkImpl::PatchShaderConvertUniformBufferToPushConstant(const Pu { for (size_t i = 0; i < Stage.Shaders.size(); ++i) { - ShaderVkImpl* pShader = const_cast(Stage.Shaders[i]); + ShaderVkImpl* pShader = const_cast(Stage.Shaders[i]); // First check if the shader already has this as push constant bool AlreadyPushConstant = false; @@ -1034,16 +1034,16 @@ void PipelineStateVkImpl::PatchShaderConvertUniformBufferToPushConstant(const Pu if (ShouldPatchUniformBuffer) { - const std::vector& SPIRV = Stage.SPIRVs[i]; + const std::vector& SPIRV = Stage.SPIRVs[i]; - std::vector PatchedSPIRV = PatchSPIRVConvertUniformBufferToPushConstant( + std::vector PatchedSPIRV = PatchSPIRVConvertUniformBufferToPushConstant( SPIRV, SPV_ENV_MAX, // Auto-detect target environment PushConstantName); if (!PatchedSPIRV.empty()) { - Stage.SPIRVs[i] = PatchedSPIRV; + Stage.SPIRVs[i] = PatchedSPIRV; Stage.ShaderResources[i] = pShader->CreateSPIRVShaderResources(PatchedSPIRV); } else From 01daa97a85af899aef46bebd4949d5e2b19e907d Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Wed, 10 Dec 2025 11:00:04 +0800 Subject: [PATCH 084/104] fix formatting. --- Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp index fba530e53f..e75d42711d 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp @@ -970,8 +970,8 @@ void PipelineStateVkImpl::InitPipelineLayout(const PipelineStateCreateInfo& Crea } } -void PipelineStateVkImpl::PatchShaderConvertUniformBufferToPushConstant(const PushConstantInfoVk& PushConstantInfo, - TShaderStages& ShaderStages) const noexcept(false) +void PipelineStateVkImpl::PatchShaderConvertUniformBufferToPushConstant(const PushConstantInfoVk& PushConstantInfo, + TShaderStages& ShaderStages) const noexcept(false) { // If no push constant was selected, no patching needed if (PushConstantInfo.SignatureIndex == INVALID_PUSH_CONSTANT_INDEX || From 9e813e40ff5a0fd9af82ab6ef7002740780c18de Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Wed, 10 Dec 2025 11:02:20 +0800 Subject: [PATCH 085/104] fix formatting --- Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp index e75d42711d..463438b58c 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp @@ -971,7 +971,7 @@ void PipelineStateVkImpl::InitPipelineLayout(const PipelineStateCreateInfo& Crea } void PipelineStateVkImpl::PatchShaderConvertUniformBufferToPushConstant(const PushConstantInfoVk& PushConstantInfo, - TShaderStages& ShaderStages) const noexcept(false) + TShaderStages& ShaderStages) const noexcept(false) { // If no push constant was selected, no patching needed if (PushConstantInfo.SignatureIndex == INVALID_PUSH_CONSTANT_INDEX || From e1493568ca11480ff1ac1331961560ecf8d2ce29 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Wed, 10 Dec 2025 11:43:33 +0800 Subject: [PATCH 086/104] restore check in PRSBase --- .../GraphicsEngine/src/PipelineResourceSignatureBase.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp b/Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp index 65f5da4829..350a5a0b9c 100644 --- a/Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp +++ b/Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp @@ -113,6 +113,12 @@ void ValidatePipelineResourceSignatureDesc(const PipelineResourceSignatureDesc& if ((Res.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0) { + if (Res.Flags != PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) + { + LOG_PRS_ERROR_AND_THROW("Incorrect Desc.Resources[", i, "].Flags (", GetPipelineResourceFlagsString(Res.Flags), + "). INLINE_CONSTANTS flag cannot be combined with other flags."); + } + if (Res.ArraySize > 64) { LOG_PRS_ERROR_AND_THROW("Desc.Resources[", i, "].ArraySize (", Res.ArraySize, From 3fa09774d7ab26396b60111e6f4939f713e8d77e Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Wed, 10 Dec 2025 12:22:26 +0800 Subject: [PATCH 087/104] Allow empty ShaderResource. --- .../include/ShaderVkImpl.hpp | 8 ++++- .../src/PipelineStateVkImpl.cpp | 20 ++++------- .../include/SPIRVShaderResources.hpp | 33 +++++++++++++++++++ 3 files changed, 46 insertions(+), 15 deletions(-) diff --git a/Graphics/GraphicsEngineVulkan/include/ShaderVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/ShaderVkImpl.hpp index 37458cd0fd..16010d84f9 100644 --- a/Graphics/GraphicsEngineVulkan/include/ShaderVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/ShaderVkImpl.hpp @@ -92,7 +92,13 @@ class ShaderVkImpl final : public ShaderBase const std::shared_ptr& GetShaderResources() const { - DEV_CHECK_ERR(!IsCompiling(), "Shader resources are not available until the shader is compiled. Use GetStatus() to check the shader status."); + //DEV_CHECK_ERR(!IsCompiling(), "Shader resources are not available until the shader is compiled. Use GetStatus() to check the shader status."); + + static const std::shared_ptr NullShaderResource; + // NOTE: while shader is compiled asynchronously, is not available + if (IsCompiling()) + return NullShaderResource; + return m_pShaderResources; } diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp index 463438b58c..76ba370d8b 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp @@ -636,22 +636,14 @@ PipelineResourceSignatureDescWrapper PipelineStateVkImpl::GetDefaultResourceSign // For inline constants, ArraySize specifies the number of 32-bit constants, // not the array dimension. We need to calculate it from the buffer size. + // the SPIRV ArraySize is 1 (buffer is not an array), but we need + // the number of 32-bit constants which is BufferStaticSize / sizeof(Uint32) Uint32 ArraySize = Attribs.ArraySize; - if ((Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) != 0) + if (Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) { - // For inline constants specified via ShaderResourceVariableDesc, - // the SPIRV ArraySize is 1 (buffer is not an array), but we need - // the number of 32-bit constants which is BufferStaticSize / sizeof(Uint32) - if (Attribs.BufferStaticSize > 0) - { - ArraySize = Attribs.BufferStaticSize / sizeof(Uint32); - } - else - { - LOG_ERROR_AND_THROW("Resource '", Attribs.Name, "' in shader '", pShader->GetDesc().Name, - "' is marked as inline constants, but has zero buffer size. " - "Make sure the buffer type is defined in the shader."); - } + VERIFY(Flags == PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS, "INLINE_CONSTANTS flag cannot be combined with other flags."); + + ArraySize = Attribs.GetInlineConstantCountOrThrow(pShader->GetDesc().Name); } SignDesc.AddResource(VarDesc.ShaderStages, Attribs.Name, ArraySize, ResType, VarDesc.Type, Flags); diff --git a/Graphics/ShaderTools/include/SPIRVShaderResources.hpp b/Graphics/ShaderTools/include/SPIRVShaderResources.hpp index 6f057ebd08..576971cb7e 100644 --- a/Graphics/ShaderTools/include/SPIRVShaderResources.hpp +++ b/Graphics/ShaderTools/include/SPIRVShaderResources.hpp @@ -131,6 +131,39 @@ struct SPIRVShaderResourceAttribs { return IsMS != 0; } + + Uint32 GetConstantBufferSize() const + { + VERIFY((Type == PushConstant || Type == UniformBuffer), "Invalid resource type: PushConstant or UniformBuffer expected"); + return BufferStaticSize; + } + + Uint32 GetInlineConstantCountOrThrow(const char* ShaderName) const + { + VERIFY_EXPR(Type == PushConstant || Type == UniformBuffer); + + if (ArraySize != 1) + { + LOG_ERROR_AND_THROW("Inline constants resource '", Name, "' in shader '", ShaderName, "' can not be an array."); + } + const Uint32 NumConstants = GetConstantBufferSize() / sizeof(Uint32); + + constexpr Uint32 kMaxInlineConstants = 64; + if (NumConstants > kMaxInlineConstants) + { + LOG_ERROR_AND_THROW("Inline constants resource '", Name, "' in shader '", ShaderName, "' has ", + NumConstants, " constants. The maximum supported number of inline constants is ", + kMaxInlineConstants, '.'); + } + + if (NumConstants == 0) + { + LOG_ERROR_AND_THROW("Resource '", Name, "' in shader '", ShaderName, + "' is marked as inline constants, but has zero buffer size. "); + } + + return NumConstants; + } }; static_assert(sizeof(SPIRVShaderResourceAttribs) % sizeof(void*) == 0, "Size of SPIRVShaderResourceAttribs struct must be multiple of sizeof(void*)"); From 63411b2752533f0bbf200aeb46683f7cae0f7042 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Wed, 10 Dec 2025 14:36:26 +0800 Subject: [PATCH 088/104] restore SPIRV-Tools --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 71463a3355..92b6928627 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,7 +6,7 @@ url = https://github.com/DiligentGraphics/SPIRV-Cross [submodule "ThirdParty/SPIRV-Tools"] path = ThirdParty/SPIRV-Tools - url = https://github.com/hzqst/SPIRV-Tools + url = https://github.com/DiligentGraphics/SPIRV-Tools [submodule "ThirdParty/SPIRV-Headers"] path = ThirdParty/SPIRV-Headers url = https://github.com/DiligentGraphics/SPIRV-Headers From d1e2ce3053939528e2d8065d16339ec4a7db0d38 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Wed, 10 Dec 2025 14:41:06 +0800 Subject: [PATCH 089/104] Port ConvertUBOToPushConstantPass to SPIRVTools.cpp --- Graphics/ShaderTools/CMakeLists.txt | 11 + Graphics/ShaderTools/src/SPIRVTools.cpp | 346 +++++++++++++++++++++++- 2 files changed, 353 insertions(+), 4 deletions(-) diff --git a/Graphics/ShaderTools/CMakeLists.txt b/Graphics/ShaderTools/CMakeLists.txt index 2c988c0916..f95a3dadfe 100644 --- a/Graphics/ShaderTools/CMakeLists.txt +++ b/Graphics/ShaderTools/CMakeLists.txt @@ -180,6 +180,17 @@ if(ENABLE_SPIRV) PRIVATE SPIRV-Tools-opt ) + # Add SPIRV-Tools internal headers path for custom pass implementation + # We need both the source directory (for internal headers like pass.h) + # and the binary directory (for generated headers like NonSemanticShaderDebugInfo100.h) + get_target_property(SPIRV_TOOLS_SOURCE_DIR SPIRV-Tools-opt SOURCE_DIR) + get_target_property(SPIRV_TOOLS_BINARY_DIR SPIRV-Tools-opt BINARY_DIR) + get_filename_component(SPIRV_TOOLS_ROOT_DIR "${SPIRV_TOOLS_SOURCE_DIR}/../.." ABSOLUTE) + get_filename_component(SPIRV_TOOLS_ROOT_BINARY_DIR "${SPIRV_TOOLS_BINARY_DIR}/../.." ABSOLUTE) + target_include_directories(Diligent-ShaderTools PRIVATE + ${SPIRV_TOOLS_ROOT_DIR} + ${SPIRV_TOOLS_ROOT_BINARY_DIR} + ) target_compile_definitions(Diligent-ShaderTools PRIVATE USE_SPIRV_TOOLS=1) endif() diff --git a/Graphics/ShaderTools/src/SPIRVTools.cpp b/Graphics/ShaderTools/src/SPIRVTools.cpp index 2cfc53a5ca..e580e78a69 100644 --- a/Graphics/ShaderTools/src/SPIRVTools.cpp +++ b/Graphics/ShaderTools/src/SPIRVTools.cpp @@ -29,6 +29,12 @@ #include "spirv-tools/optimizer.hpp" +// SPIRV-Tools internal headers for custom pass implementation +#include "source/opt/pass.h" +#include "source/opt/ir_context.h" +#include "source/opt/type_manager.h" +#include "source/opt/decoration_manager.h" + namespace Diligent { @@ -108,6 +114,339 @@ spv_target_env SpvTargetEnvFromSPIRV(const std::vector& SPIRV) #undef SPV_SPIRV_VERSION_WORD +// A pass that converts a uniform buffer variable to a push constant. +// This pass: +// 1. Finds the variable with the specified block name +// 2. Changes its storage class from Uniform to PushConstant +// 3. Updates all pointer types that reference this variable +// 4. Removes Binding and DescriptorSet decorations +class ConvertUBOToPushConstantPass : public spvtools::opt::Pass +{ +public: + explicit ConvertUBOToPushConstantPass(const std::string& block_name) : + m_BlockName{block_name} + {} + + const char* name() const override { return "convert-ubo-to-push-constant"; } + + Status Process() override + { + bool modified = false; + + // Find the ID that matches the block name by searching OpName instructions + // This could be either a variable ID or a type ID (struct type) + uint32_t named_id = 0; + for (auto& debug_inst : context()->module()->debugs2()) + { + if (debug_inst.opcode() == spv::Op::OpName && + debug_inst.GetOperand(1).AsString() == m_BlockName) + { + named_id = debug_inst.GetOperand(0).AsId(); + break; + } + } + + if (named_id == 0) + { + // Block name not found + return Status::SuccessWithoutChange; + } + + // Check if the named_id is a variable or a type + spvtools::opt::Instruction* target_var = nullptr; + spvtools::opt::Instruction* named_inst = get_def_use_mgr()->GetDef(named_id); + + if (named_inst == nullptr) + { + return Status::SuccessWithoutChange; + } + + if (named_inst->opcode() == spv::Op::OpVariable) + { + // The name refers directly to a variable + target_var = named_inst; + } + else if (named_inst->opcode() == spv::Op::OpTypeStruct) + { + // The name refers to a struct type, we need to find the variable + // that uses a pointer to this struct type with Uniform storage class + uint32_t struct_type_id = named_id; + + // Search for a variable that points to this struct type with Uniform storage class + for (auto& inst : context()->types_values()) + { + if (inst.opcode() != spv::Op::OpVariable) + { + continue; + } + + // Get the pointer type of this variable + spvtools::opt::Instruction* ptr_type = get_def_use_mgr()->GetDef(inst.type_id()); + if (ptr_type == nullptr || ptr_type->opcode() != spv::Op::OpTypePointer) + { + continue; + } + + // Check storage class is Uniform + spv::StorageClass sc = static_cast( + ptr_type->GetSingleWordInOperand(0)); + if (sc != spv::StorageClass::Uniform) + { + continue; + } + + // Check if the pointee type is our struct type + uint32_t pointee_type_id = ptr_type->GetSingleWordInOperand(1); + if (pointee_type_id == struct_type_id) + { + target_var = &inst; + break; + } + } + } + + if (target_var == nullptr) + { + // Variable not found + return Status::SuccessWithoutChange; + } + + uint32_t target_var_id = target_var->result_id(); + + // Get the pointer type of the variable + spvtools::opt::Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(target_var->type_id()); + if (ptr_type_inst == nullptr || ptr_type_inst->opcode() != spv::Op::OpTypePointer) + { + return Status::SuccessWithoutChange; + } + + // Check if the storage class is Uniform + spv::StorageClass storage_class = + static_cast(ptr_type_inst->GetSingleWordInOperand(0)); + if (storage_class != spv::StorageClass::Uniform) + { + // Not a uniform buffer, nothing to do + return Status::SuccessWithoutChange; + } + + // Get the pointee type ID + uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1); + + // Create or find a pointer type with PushConstant storage class + spvtools::opt::analysis::TypeManager* type_mgr = context()->get_type_mgr(); + uint32_t new_ptr_type_id = + type_mgr->FindPointerToType(pointee_type_id, spv::StorageClass::PushConstant); + + if (new_ptr_type_id == 0) + { + // Failed to create new pointer type + return Status::Failure; + } + + // Ensure the new pointer type is defined before the variable + // FindPointerToType may have created it at the end, we need to move it + spvtools::opt::Instruction* new_ptr_type_inst = get_def_use_mgr()->GetDef(new_ptr_type_id); + if (new_ptr_type_inst != nullptr) + { + // Find the pointee type instruction to insert after it + spvtools::opt::Instruction* pointee_type_inst = get_def_use_mgr()->GetDef(pointee_type_id); + + // Check if new_ptr_type_inst is after target_var in the types_values list + bool needs_move = false; + for (auto& inst : context()->types_values()) + { + if (&inst == target_var) + { + // Found target_var first, so new_ptr_type_inst is after it + needs_move = true; + break; + } + if (&inst == new_ptr_type_inst) + { + // Found new_ptr_type_inst first, it's in the right position + needs_move = false; + break; + } + } + + if (needs_move && pointee_type_inst != nullptr) + { + // Move the new pointer type to right after the pointee type + // InsertAfter will automatically remove it from its current position + new_ptr_type_inst->InsertAfter(pointee_type_inst); + } + } + + // Update the variable's type to the new pointer type + target_var->SetResultType(new_ptr_type_id); + + // Also update the storage class operand of OpVariable itself + // OpVariable has the storage class as the first operand (index 0) + target_var->SetInOperand(0, {static_cast(spv::StorageClass::PushConstant)}); + + context()->UpdateDefUse(target_var); + modified = true; + + // Propagate storage class change to all users of this variable + std::set seen; + std::vector users; + get_def_use_mgr()->ForEachUser(target_var, [&users](spvtools::opt::Instruction* user) { + users.push_back(user); + }); + + for (spvtools::opt::Instruction* user : users) + { + modified |= PropagateStorageClass(user, &seen); + } + + // Remove Binding and DescriptorSet decorations from the variable + auto* deco_mgr = context()->get_decoration_mgr(); + deco_mgr->RemoveDecorationsFrom(target_var_id, [](const spvtools::opt::Instruction& inst) { + if (inst.opcode() != spv::Op::OpDecorate) + { + return false; + } + spv::Decoration decoration = + static_cast(inst.GetSingleWordInOperand(1)); + return decoration == spv::Decoration::Binding || + decoration == spv::Decoration::DescriptorSet; + }); + + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; + } + + spvtools::opt::IRContext::Analysis GetPreservedAnalyses() override + { + // This pass modifies types and decorations + return spvtools::opt::IRContext::kAnalysisNone; + } + +private: + // Recursively updates the storage class of pointer types used by instructions + // that reference the target variable. + bool PropagateStorageClass(spvtools::opt::Instruction* inst, std::set* seen) + { + if (!IsPointerResultType(inst)) + { + return false; + } + + // Already has the correct storage class + if (IsPointerToStorageClass(inst, spv::StorageClass::PushConstant)) + { + if (inst->opcode() == spv::Op::OpPhi) + { + if (!seen->insert(inst->result_id()).second) + { + return false; + } + } + + bool modified = false; + std::vector users; + get_def_use_mgr()->ForEachUser(inst, [&users](spvtools::opt::Instruction* user) { + users.push_back(user); + }); + for (spvtools::opt::Instruction* user : users) + { + modified |= PropagateStorageClass(user, seen); + } + + if (inst->opcode() == spv::Op::OpPhi) + { + seen->erase(inst->result_id()); + } + return modified; + } + + // Handle instructions that produce pointer results + switch (inst->opcode()) + { + case spv::Op::OpAccessChain: + case spv::Op::OpPtrAccessChain: + case spv::Op::OpInBoundsAccessChain: + case spv::Op::OpInBoundsPtrAccessChain: + case spv::Op::OpCopyObject: + case spv::Op::OpPhi: + case spv::Op::OpSelect: + ChangeResultStorageClass(inst); + { + std::vector users; + get_def_use_mgr()->ForEachUser(inst, [&users](spvtools::opt::Instruction* user) { + users.push_back(user); + }); + for (spvtools::opt::Instruction* user : users) + { + PropagateStorageClass(user, seen); + } + } + return true; + + case spv::Op::OpLoad: + case spv::Op::OpStore: + case spv::Op::OpCopyMemory: + case spv::Op::OpCopyMemorySized: + // These don't produce pointer results that need updating + return false; + + default: + return false; + } + } + + // Changes the result type of an instruction to use the new storage class. + void ChangeResultStorageClass(spvtools::opt::Instruction* inst) + { + spvtools::opt::analysis::TypeManager* type_mgr = context()->get_type_mgr(); + spvtools::opt::Instruction* result_type_inst = get_def_use_mgr()->GetDef(inst->type_id()); + + if (result_type_inst->opcode() != spv::Op::OpTypePointer) + { + return; + } + + uint32_t pointee_type_id = result_type_inst->GetSingleWordInOperand(1); + uint32_t new_result_type_id = + type_mgr->FindPointerToType(pointee_type_id, spv::StorageClass::PushConstant); + + inst->SetResultType(new_result_type_id); + context()->UpdateDefUse(inst); + } + + // Checks if the instruction result type is a pointer. + bool IsPointerResultType(spvtools::opt::Instruction* inst) + { + if (inst->type_id() == 0) + { + return false; + } + + spvtools::opt::Instruction* type_def = get_def_use_mgr()->GetDef(inst->type_id()); + return type_def != nullptr && type_def->opcode() == spv::Op::OpTypePointer; + } + + // Checks if the instruction result type is a pointer to the specified storage class. + bool IsPointerToStorageClass(spvtools::opt::Instruction* inst, spv::StorageClass storage_class) + { + if (inst->type_id() == 0) + { + return false; + } + + spvtools::opt::Instruction* type_def = get_def_use_mgr()->GetDef(inst->type_id()); + if (type_def == nullptr || type_def->opcode() != spv::Op::OpTypePointer) + { + return false; + } + + spv::StorageClass pointer_storage_class = + static_cast(type_def->GetSingleWordInOperand(0)); + return pointer_storage_class == storage_class; + } + + std::string m_BlockName; +}; + } // namespace std::vector OptimizeSPIRV(const std::vector& SrcSPIRV, spv_target_env TargetEnv, SPIRV_OPTIMIZATION_FLAGS Passes) @@ -168,10 +507,9 @@ std::vector PatchSPIRVConvertUniformBufferToPushConstant( optimizer.SetMessageConsumer(SpvOptimizerMessageConsumer); - // Register the pass to convert UBO to push constant - optimizer.RegisterPass(spvtools::CreateConvertUBOToPushConstantPass(BlockName)); - //optimizer.RegisterPass(spvtools::CreateDeadVariableEliminationPass()); - //optimizer.RegisterPerformancePasses(); + // Register the pass to convert UBO to push constant using custom out-of-tree pass + optimizer.RegisterPass(spvtools::Optimizer::PassToken( + std::make_unique(BlockName))); spvtools::OptimizerOptions options; #ifndef DILIGENT_DEVELOPMENT From 9ebbadbece3415e4a5bfec3cacefb926e017db98 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Wed, 10 Dec 2025 14:43:15 +0800 Subject: [PATCH 090/104] fix formatting --- Graphics/ShaderTools/src/SPIRVTools.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Graphics/ShaderTools/src/SPIRVTools.cpp b/Graphics/ShaderTools/src/SPIRVTools.cpp index e580e78a69..bc07d3cf0c 100644 --- a/Graphics/ShaderTools/src/SPIRVTools.cpp +++ b/Graphics/ShaderTools/src/SPIRVTools.cpp @@ -153,7 +153,7 @@ class ConvertUBOToPushConstantPass : public spvtools::opt::Pass } // Check if the named_id is a variable or a type - spvtools::opt::Instruction* target_var = nullptr; + spvtools::opt::Instruction* target_var = nullptr; spvtools::opt::Instruction* named_inst = get_def_use_mgr()->GetDef(named_id); if (named_inst == nullptr) @@ -234,7 +234,7 @@ class ConvertUBOToPushConstantPass : public spvtools::opt::Pass // Create or find a pointer type with PushConstant storage class spvtools::opt::analysis::TypeManager* type_mgr = context()->get_type_mgr(); - uint32_t new_ptr_type_id = + uint32_t new_ptr_type_id = type_mgr->FindPointerToType(pointee_type_id, spv::StorageClass::PushConstant); if (new_ptr_type_id == 0) @@ -397,7 +397,7 @@ class ConvertUBOToPushConstantPass : public spvtools::opt::Pass // Changes the result type of an instruction to use the new storage class. void ChangeResultStorageClass(spvtools::opt::Instruction* inst) { - spvtools::opt::analysis::TypeManager* type_mgr = context()->get_type_mgr(); + spvtools::opt::analysis::TypeManager* type_mgr = context()->get_type_mgr(); spvtools::opt::Instruction* result_type_inst = get_def_use_mgr()->GetDef(inst->type_id()); if (result_type_inst->opcode() != spv::Op::OpTypePointer) From 9271f70589b4cf9f873ea8f8ca7d82ab5a7e99e4 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Wed, 10 Dec 2025 14:54:25 +0800 Subject: [PATCH 091/104] Fix SPIRV-Tools headers warning --- Graphics/ShaderTools/src/SPIRVTools.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Graphics/ShaderTools/src/SPIRVTools.cpp b/Graphics/ShaderTools/src/SPIRVTools.cpp index bc07d3cf0c..40da9da26e 100644 --- a/Graphics/ShaderTools/src/SPIRVTools.cpp +++ b/Graphics/ShaderTools/src/SPIRVTools.cpp @@ -27,6 +27,13 @@ #include "SPIRVTools.hpp" #include "DebugUtilities.hpp" +// Temporarily disable warning C4127: conditional expression is constant +// This warning is triggered by SPIRV-Tools headers in ThirdParty +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4127) +#endif + #include "spirv-tools/optimizer.hpp" // SPIRV-Tools internal headers for custom pass implementation @@ -35,6 +42,11 @@ #include "source/opt/type_manager.h" #include "source/opt/decoration_manager.h" +// Restore warning settings +#ifdef _MSC_VER +# pragma warning(pop) +#endif + namespace Diligent { From 59a3c8f3730bcc4981b3c5bbb1fbbf5551ffdbf5 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Wed, 10 Dec 2025 14:58:00 +0800 Subject: [PATCH 092/104] restore SPIRV-Tools --- ThirdParty/SPIRV-Tools | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ThirdParty/SPIRV-Tools b/ThirdParty/SPIRV-Tools index dddd7c564b..7f2d9ee926 160000 --- a/ThirdParty/SPIRV-Tools +++ b/ThirdParty/SPIRV-Tools @@ -1 +1 @@ -Subproject commit dddd7c564b048223023390e9452ada718679f5b3 +Subproject commit 7f2d9ee926f98fc77a3ed1e1e0f113b8c9c49458 From 733ac6b17b73e14baefae782b5f57bf8fd47986c Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Wed, 10 Dec 2025 14:59:53 +0800 Subject: [PATCH 093/104] Fix formatting --- Graphics/ShaderTools/src/SPIRVTools.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Graphics/ShaderTools/src/SPIRVTools.cpp b/Graphics/ShaderTools/src/SPIRVTools.cpp index 40da9da26e..632fa37d8d 100644 --- a/Graphics/ShaderTools/src/SPIRVTools.cpp +++ b/Graphics/ShaderTools/src/SPIRVTools.cpp @@ -30,8 +30,8 @@ // Temporarily disable warning C4127: conditional expression is constant // This warning is triggered by SPIRV-Tools headers in ThirdParty #ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4127) +# pragma warning(push) +# pragma warning(disable : 4127) #endif #include "spirv-tools/optimizer.hpp" From 65f99b663f4975a81aadb2bf5e91972112462d79 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Wed, 10 Dec 2025 15:06:03 +0800 Subject: [PATCH 094/104] restore #include "SPIRVTools.hpp" --- Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp index 76ba370d8b..9a1c79825b 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp @@ -43,7 +43,9 @@ #include "EngineMemory.h" #include "StringTools.hpp" -#include "SPIRVTools.hpp" +#if !DILIGENT_NO_HLSL +# include "SPIRVTools.hpp" +#endif namespace Diligent { From 56eb92d059dedcc0ca0b24f031c003f4f254dcfa Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Wed, 10 Dec 2025 15:12:04 +0800 Subject: [PATCH 095/104] We don't need TargetEnv in PatchSPIRVConvertUniformBufferToPushConstant --- Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp | 1 - Graphics/ShaderTools/include/SPIRVTools.hpp | 2 -- Graphics/ShaderTools/src/SPIRVTools.cpp | 3 +-- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp index 9a1c79825b..e7037bf563 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp @@ -1032,7 +1032,6 @@ void PipelineStateVkImpl::PatchShaderConvertUniformBufferToPushConstant(const Pu std::vector PatchedSPIRV = PatchSPIRVConvertUniformBufferToPushConstant( SPIRV, - SPV_ENV_MAX, // Auto-detect target environment PushConstantName); if (!PatchedSPIRV.empty()) diff --git a/Graphics/ShaderTools/include/SPIRVTools.hpp b/Graphics/ShaderTools/include/SPIRVTools.hpp index d37bc1974d..323e408119 100644 --- a/Graphics/ShaderTools/include/SPIRVTools.hpp +++ b/Graphics/ShaderTools/include/SPIRVTools.hpp @@ -55,11 +55,9 @@ std::vector OptimizeSPIRV(const std::vector& SrcSPIRV, /// and removes Binding and DescriptorSet decorations. /// /// \param [in] SPIRV - Source SPIR-V bytecode -/// \param [in] TargetEnv - Target SPIR-V environment (use SPV_ENV_MAX to auto-detect) /// \param [in] BlockName - Name of the uniform buffer block to convert /// \return Modified SPIR-V bytecode, or empty vector on failure std::vector PatchSPIRVConvertUniformBufferToPushConstant(const std::vector& SPIRV, - spv_target_env TargetEnv, const std::string& BlockName); } // namespace Diligent diff --git a/Graphics/ShaderTools/src/SPIRVTools.cpp b/Graphics/ShaderTools/src/SPIRVTools.cpp index 632fa37d8d..e19cc7dcbc 100644 --- a/Graphics/ShaderTools/src/SPIRVTools.cpp +++ b/Graphics/ShaderTools/src/SPIRVTools.cpp @@ -512,8 +512,7 @@ std::vector PatchSPIRVConvertUniformBufferToPushConstant( spv_target_env TargetEnv, const std::string& BlockName) { - if (TargetEnv == SPV_ENV_MAX) - TargetEnv = SpvTargetEnvFromSPIRV(SPIRV); + spv_target_env TargetEnv = SpvTargetEnvFromSPIRV(SPIRV); spvtools::Optimizer optimizer(TargetEnv); From d0b19f13ce6fefa6ed6c121c5ca6bad4cd0b5cf8 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Wed, 10 Dec 2025 15:19:01 +0800 Subject: [PATCH 096/104] fiX not commit --- Graphics/ShaderTools/src/SPIRVTools.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Graphics/ShaderTools/src/SPIRVTools.cpp b/Graphics/ShaderTools/src/SPIRVTools.cpp index e19cc7dcbc..a269b4281c 100644 --- a/Graphics/ShaderTools/src/SPIRVTools.cpp +++ b/Graphics/ShaderTools/src/SPIRVTools.cpp @@ -509,7 +509,6 @@ std::vector OptimizeSPIRV(const std::vector& SrcSPIRV, spv_t std::vector PatchSPIRVConvertUniformBufferToPushConstant( const std::vector& SPIRV, - spv_target_env TargetEnv, const std::string& BlockName) { spv_target_env TargetEnv = SpvTargetEnvFromSPIRV(SPIRV); From 7cfe7e4446ae1e3be639abf16c5f2099b7a59ef2 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Wed, 10 Dec 2025 15:26:09 +0800 Subject: [PATCH 097/104] Fix error C3861: 'PatchSPIRVConvertUniformBufferToPushConstant': identifier not found --- Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp index e7037bf563..b50f11df84 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp @@ -50,6 +50,10 @@ namespace Diligent { +//when DILIGENT_NO_HLSL not defined +std::vector PatchSPIRVConvertUniformBufferToPushConstant(const std::vector& SPIRV, + const std::string& BlockName); + constexpr INTERFACE_ID PipelineStateVkImpl::IID_InternalImpl; namespace From de856be5860db6d75c27a03e2344158708bcbcbf Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Wed, 10 Dec 2025 15:31:10 +0800 Subject: [PATCH 098/104] fix formatting. --- Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp index b50f11df84..ecd4680391 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp @@ -51,8 +51,8 @@ namespace Diligent { //when DILIGENT_NO_HLSL not defined -std::vector PatchSPIRVConvertUniformBufferToPushConstant(const std::vector& SPIRV, - const std::string& BlockName); +std::vector PatchSPIRVConvertUniformBufferToPushConstant(const std::vector& SPIRV, + const std::string& BlockName); constexpr INTERFACE_ID PipelineStateVkImpl::IID_InternalImpl; From 57b9bbf4646f77591588a99c5409f46e0a14ecf7 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Wed, 10 Dec 2025 15:46:11 +0800 Subject: [PATCH 099/104] We always need SPIRV-Tools to patch uniform buffer block, no matter NO_HLSL defined or not --- .../GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp | 9 ++------- Graphics/ShaderTools/CMakeLists.txt | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp index ecd4680391..1541dfd57f 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp @@ -43,17 +43,12 @@ #include "EngineMemory.h" #include "StringTools.hpp" -#if !DILIGENT_NO_HLSL -# include "SPIRVTools.hpp" -#endif +//We always need this to patch uniform buffer block, no matter DILIGENT_NO_HLSL defined or not. +#include "SPIRVTools.hpp" namespace Diligent { -//when DILIGENT_NO_HLSL not defined -std::vector PatchSPIRVConvertUniformBufferToPushConstant(const std::vector& SPIRV, - const std::string& BlockName); - constexpr INTERFACE_ID PipelineStateVkImpl::IID_InternalImpl; namespace diff --git a/Graphics/ShaderTools/CMakeLists.txt b/Graphics/ShaderTools/CMakeLists.txt index f95a3dadfe..6f0f55faf6 100644 --- a/Graphics/ShaderTools/CMakeLists.txt +++ b/Graphics/ShaderTools/CMakeLists.txt @@ -106,7 +106,7 @@ if(DILIGENT_USE_SPIRV_TOOLCHAIN) set(USE_SPIRV_TOOLS TRUE) endif() endif() - if (NOT ${DILIGENT_NO_HLSL} AND TARGET SPIRV-Tools-opt) + if (TARGET SPIRV-Tools-opt) # NOT ${DILIGENT_NO_HLSL} AND -- we always need SPIRV-Tools. set(USE_SPIRV_TOOLS TRUE) endif() endif() From 1164bdef0fec6f92049e76cce89727492ac47603 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Wed, 10 Dec 2025 15:57:48 +0800 Subject: [PATCH 100/104] Enable SPIRV-Tools by default. --- Graphics/ShaderTools/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Graphics/ShaderTools/CMakeLists.txt b/Graphics/ShaderTools/CMakeLists.txt index 6f0f55faf6..ceb8ba1788 100644 --- a/Graphics/ShaderTools/CMakeLists.txt +++ b/Graphics/ShaderTools/CMakeLists.txt @@ -95,7 +95,7 @@ endif() set(ENABLE_SPIRV FALSE) set(USE_GLSLANG FALSE) -set(USE_SPIRV_TOOLS FALSE) +set(USE_SPIRV_TOOLS TRUE) # -- we always need SPIRV-Tools for SPIRV bytecode patching on vulkan backend. if(DILIGENT_USE_SPIRV_TOOLCHAIN) set(ENABLE_SPIRV TRUE) # NB: do not use if(TARGET glsang) check here as glsang may be present @@ -106,7 +106,7 @@ if(DILIGENT_USE_SPIRV_TOOLCHAIN) set(USE_SPIRV_TOOLS TRUE) endif() endif() - if (TARGET SPIRV-Tools-opt) # NOT ${DILIGENT_NO_HLSL} AND -- we always need SPIRV-Tools. + if (NOT ${DILIGENT_NO_HLSL} AND TARGET SPIRV-Tools-opt) set(USE_SPIRV_TOOLS TRUE) endif() endif() From 35205192c990070f0db5a9d160de41601f92ba4b Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Wed, 10 Dec 2025 16:04:44 +0800 Subject: [PATCH 101/104] restore CMakeLists.txt --- .../GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp | 9 +++++++-- Graphics/ShaderTools/CMakeLists.txt | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp index 1541dfd57f..a8aad2f4f5 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp @@ -43,8 +43,9 @@ #include "EngineMemory.h" #include "StringTools.hpp" -//We always need this to patch uniform buffer block, no matter DILIGENT_NO_HLSL defined or not. -#include "SPIRVTools.hpp" +#if !DILIGENT_NO_HLSL +# include "SPIRVTools.hpp" +#endif namespace Diligent { @@ -966,6 +967,7 @@ void PipelineStateVkImpl::InitPipelineLayout(const PipelineStateCreateInfo& Crea void PipelineStateVkImpl::PatchShaderConvertUniformBufferToPushConstant(const PushConstantInfoVk& PushConstantInfo, TShaderStages& ShaderStages) const noexcept(false) { +#if !DILIGENT_NO_HLSL // If no push constant was selected, no patching needed if (PushConstantInfo.SignatureIndex == INVALID_PUSH_CONSTANT_INDEX || PushConstantInfo.ResourceIndex == INVALID_PUSH_CONSTANT_INDEX) @@ -1046,6 +1048,9 @@ void PipelineStateVkImpl::PatchShaderConvertUniformBufferToPushConstant(const Pu } } } +#else + //Warning: SPIRV-Tools is not available due to DILIGENT_NO_HLSL defined. +#endif } template diff --git a/Graphics/ShaderTools/CMakeLists.txt b/Graphics/ShaderTools/CMakeLists.txt index ceb8ba1788..f95a3dadfe 100644 --- a/Graphics/ShaderTools/CMakeLists.txt +++ b/Graphics/ShaderTools/CMakeLists.txt @@ -95,7 +95,7 @@ endif() set(ENABLE_SPIRV FALSE) set(USE_GLSLANG FALSE) -set(USE_SPIRV_TOOLS TRUE) # -- we always need SPIRV-Tools for SPIRV bytecode patching on vulkan backend. +set(USE_SPIRV_TOOLS FALSE) if(DILIGENT_USE_SPIRV_TOOLCHAIN) set(ENABLE_SPIRV TRUE) # NB: do not use if(TARGET glsang) check here as glsang may be present From c84f619a5147172b8c0018aba3f26788f79554cc Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Wed, 10 Dec 2025 16:21:20 +0800 Subject: [PATCH 102/104] Don't filter out SRBs that only have push constants (no descriptor sets), cuz we no longer filter out push constants when creating layout. --- .../src/DeviceContextVkImpl.cpp | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp index 081b15de7c..df317b9328 100644 --- a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp @@ -513,27 +513,6 @@ void DeviceContextVkImpl::CommitDescriptorSets(ResourceBindInfo& BindInfo, Uint3 { VERIFY(CommitSRBMask != 0, "This method should not be called when there is nothing to commit"); - // Filter out SRBs that only have push constants (no descriptor sets) - // These SRBs are included in CommitSRBMask due to InlineConstantsSRBMask, but they don't need descriptor set binding - Uint32 FilteredCommitMask = CommitSRBMask; - Uint32 ResourceSignaturesCount = m_pPipelineState->GetResourceSignatureCount(); - for (Uint32 sign = 0; sign < ResourceSignaturesCount; ++sign) - { - if ((CommitSRBMask & (1u << sign)) != 0) - { - const ShaderResourceCacheVk* pResourceCache = BindInfo.ResourceCaches[sign]; - if (pResourceCache != nullptr && pResourceCache->GetNumDescriptorSets() == 0) - { - // This SRB only has push constants, no descriptor sets to commit - FilteredCommitMask &= ~(1u << sign); - } - } - } - - // If all SRBs were filtered out (only push constants), nothing to commit - if (FilteredCommitMask == 0) - return; - const Uint32 FirstSign = PlatformMisc::GetLSB(CommitSRBMask); const Uint32 LastSign = PlatformMisc::GetMSB(CommitSRBMask); VERIFY_EXPR(LastSign < m_pPipelineState->GetResourceSignatureCount()); From 7a6a6c1a3bf1b081801e6fb003d2669761580353 Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Wed, 10 Dec 2025 16:52:05 +0800 Subject: [PATCH 103/104] throw err when SPIRV-Tools is not available. remove unused code. --- Graphics/GraphicsEngineVulkan/include/ShaderVkImpl.hpp | 2 -- .../GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp | 9 +++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Graphics/GraphicsEngineVulkan/include/ShaderVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/ShaderVkImpl.hpp index 16010d84f9..2fc599ab05 100644 --- a/Graphics/GraphicsEngineVulkan/include/ShaderVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/ShaderVkImpl.hpp @@ -92,8 +92,6 @@ class ShaderVkImpl final : public ShaderBase const std::shared_ptr& GetShaderResources() const { - //DEV_CHECK_ERR(!IsCompiling(), "Shader resources are not available until the shader is compiled. Use GetStatus() to check the shader status."); - static const std::shared_ptr NullShaderResource; // NOTE: while shader is compiled asynchronously, is not available if (IsCompiling()) diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp index a8aad2f4f5..296be85206 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp @@ -967,7 +967,6 @@ void PipelineStateVkImpl::InitPipelineLayout(const PipelineStateCreateInfo& Crea void PipelineStateVkImpl::PatchShaderConvertUniformBufferToPushConstant(const PushConstantInfoVk& PushConstantInfo, TShaderStages& ShaderStages) const noexcept(false) { -#if !DILIGENT_NO_HLSL // If no push constant was selected, no patching needed if (PushConstantInfo.SignatureIndex == INVALID_PUSH_CONSTANT_INDEX || PushConstantInfo.ResourceIndex == INVALID_PUSH_CONSTANT_INDEX) @@ -1029,6 +1028,7 @@ void PipelineStateVkImpl::PatchShaderConvertUniformBufferToPushConstant(const Pu if (ShouldPatchUniformBuffer) { +#if !DILIGENT_NO_HLSL const std::vector& SPIRV = Stage.SPIRVs[i]; std::vector PatchedSPIRV = PatchSPIRVConvertUniformBufferToPushConstant( @@ -1045,12 +1045,13 @@ void PipelineStateVkImpl::PatchShaderConvertUniformBufferToPushConstant(const Pu LOG_ERROR_MESSAGE("Failed to convert uniform buffer '", PushConstantName, "' to push constant in shader '", pShader->GetDesc().Name, "'"); } +#else + LOG_ERROR_AND_THROW("Cannot patch shader, SPIRV-Tools is not available when DILIGENT_NO_HLSL defined."); +#endif } } } -#else - //Warning: SPIRV-Tools is not available due to DILIGENT_NO_HLSL defined. -#endif + } template From 616e607af5162d6aa3555d620f52c8ba7173c1cd Mon Sep 17 00:00:00 2001 From: hzqst <113660872@qq.com> Date: Wed, 10 Dec 2025 16:54:04 +0800 Subject: [PATCH 104/104] fix formatting --- Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp index 296be85206..298f57c789 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp @@ -1051,7 +1051,6 @@ void PipelineStateVkImpl::PatchShaderConvertUniformBufferToPushConstant(const Pu } } } - } template