diff --git a/Graphics/GraphicsEngineOpenGL/include/DeviceContextGLImpl.hpp b/Graphics/GraphicsEngineOpenGL/include/DeviceContextGLImpl.hpp index a5aae0e4d..7848b1cd5 100644 --- a/Graphics/GraphicsEngineOpenGL/include/DeviceContextGLImpl.hpp +++ b/Graphics/GraphicsEngineOpenGL/include/DeviceContextGLImpl.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2025 Diligent Graphics LLC + * Copyright 2019-2026 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -325,7 +325,7 @@ class DeviceContextGLImpl final : public DeviceContextBase __forceinline void PostDraw(); using TBindings = PipelineResourceSignatureGLImpl::TBindings; - void BindProgramResources(Uint32 BindSRBMask); + void BindProgramResources(Uint32 BindSRBMask, bool DynamicBuffersIntact = false, bool InlineConstantsIntact = false); #ifdef DILIGENT_DEVELOPMENT void DvpValidateCommittedShaderResources(); diff --git a/Graphics/GraphicsEngineOpenGL/include/PipelineResourceSignatureGLImpl.hpp b/Graphics/GraphicsEngineOpenGL/include/PipelineResourceSignatureGLImpl.hpp index 3845f5b66..5183ee892 100644 --- a/Graphics/GraphicsEngineOpenGL/include/PipelineResourceSignatureGLImpl.hpp +++ b/Graphics/GraphicsEngineOpenGL/include/PipelineResourceSignatureGLImpl.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2025 Diligent Graphics LLC + * Copyright 2019-2026 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -31,6 +31,7 @@ /// Declaration of Diligent::PipelineResourceSignatureGLImpl class #include +#include #include "EngineGLImplTraits.hpp" #include "PipelineResourceAttribsGL.hpp" @@ -63,6 +64,19 @@ struct ImmutableSamplerAttribsGL Uint32 Dummy = 0; }; +/// Attributes of an inline constant buffer in OpenGL. +/// OpenGL does not support push constants, so inline constants are emulated +/// using a shared dynamic UBO that is updated before each draw/dispatch. +struct InlineConstantBufferAttribsGL +{ + Uint32 CacheOffset = 0; // UBO cache slot offset for this resource + Uint32 NumConstants = 0; // Number of 32-bit constants (from ResDesc.ArraySize) + + // Shared dynamic UBO created in the Signature. + // All SRBs reference this same buffer to reduce memory usage. + RefCntAutoPtr pBuffer; +}; + struct PipelineResourceSignatureInternalDataGL : PipelineResourceSignatureInternalData { PipelineResourceSignatureInternalDataGL() noexcept @@ -127,6 +141,11 @@ class PipelineResourceSignatureGLImpl final : public PipelineResourceSignatureBa // Make the base class method visible using TPipelineResourceSignatureBase::CopyStaticResources; + // Updates inline constant buffers by mapping the shared dynamic UBOs and copying + // data from the CPU-side staging buffer in the resource cache. + void UpdateInlineConstantBuffers(const ShaderResourceCacheGL& ResourceCache, + class GLContextState& CtxState) const; + Uint32 GetImmutableSamplerIdx(const ResourceAttribs& Res) const { Uint32 ImtblSamIdx = InvalidImmutableSamplerIndex; @@ -146,6 +165,12 @@ class PipelineResourceSignatureGLImpl final : public PipelineResourceSignatureBa private: void CreateLayout(bool IsSerialized); + const InlineConstantBufferAttribsGL& GetInlineConstantBuffer(Uint32 Index) const + { + VERIFY_EXPR(Index < m_NumInlineConstantBuffers); + return m_InlineConstantBuffers[Index]; + } + private: TBindings m_BindingCount = {}; @@ -153,6 +178,15 @@ class PipelineResourceSignatureGLImpl final : public PipelineResourceSignatureBa Uint64 m_DynamicUBOMask = 0; // Indicates which SSBO slots allow binding buffers with dynamic offsets Uint64 m_DynamicSSBOMask = 0; + + // Number of inline constant buffers + Uint16 m_NumInlineConstantBuffers = 0; + + // The total number of inline constants (32-bit values) in all inline constant buffers + Uint16 m_TotalInlineConstants = 0; + + // Inline constant buffer attributes + std::unique_ptr m_InlineConstantBuffers; }; } // namespace Diligent diff --git a/Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp b/Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp index 6e6b91b2c..68b144e87 100644 --- a/Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2025 Diligent Graphics LLC + * Copyright 2019-2026 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -698,7 +698,7 @@ void DeviceContextGLImpl::DvpValidateCommittedShaderResources() } #endif -void DeviceContextGLImpl::BindProgramResources(Uint32 BindSRBMask) +void DeviceContextGLImpl::BindProgramResources(Uint32 BindSRBMask, bool DynamicBuffersIntact, bool InlineConstantsIntact) { VERIFY_EXPR(BindSRBMask != 0); //if (m_CommittedResourcesTentativeBarriers != 0) @@ -721,16 +721,55 @@ void DeviceContextGLImpl::BindProgramResources(Uint32 BindSRBMask) const ShaderResourceCacheImplType* pResourceCache = m_BindInfo.ResourceCaches[sign]; DEV_CHECK_ERR(pResourceCache != nullptr, "Resource cache at index ", sign, " is null"); - if (m_BindInfo.StaleSRBMask & SignBit) + + const bool SRBStale = (m_BindInfo.StaleSRBMask & SignBit) != 0; + if (SRBStale) + { pResourceCache->BindResources(GetContextState(), BaseBindings, m_BoundWritableTextures, m_BoundWritableBuffers); + } else { - 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."); - pResourceCache->BindDynamicBuffers(GetContextState(), BaseBindings); + 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(), + "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 (!DynamicBuffersIntact) + { + pResourceCache->BindDynamicBuffers(GetContextState(), BaseBindings); + } + } + } + + // Update inline constant buffers if needed + 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."); + // Always update inline constant buffers if the SRB is stale + if (SRBStale || !InlineConstantsIntact) + { + if (PipelineResourceSignatureGLImpl* pSign = m_pPipelineState->GetResourceSignature(sign)) + { + pSign->UpdateInlineConstantBuffers(*pResourceCache, GetContextState()); + } + else + { + UNEXPECTED("Pipeline resource signature is null for signature index ", sign); + } + } + } + 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."); } } m_BindInfo.StaleSRBMask &= ~m_BindInfo.ActiveSRBMask; @@ -797,9 +836,11 @@ 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, 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 BindSRBMask = m_BindInfo.GetCommitMask(DynamicBuffersIntact, InlineConstantsIntact)) { - BindProgramResources(BindSRBMask); + BindProgramResources(BindSRBMask, DynamicBuffersIntact, InlineConstantsIntact); } #ifdef DILIGENT_DEVELOPMENT diff --git a/Graphics/GraphicsEngineOpenGL/src/PipelineResourceSignatureGLImpl.cpp b/Graphics/GraphicsEngineOpenGL/src/PipelineResourceSignatureGLImpl.cpp index b100a2b8b..097bca256 100644 --- a/Graphics/GraphicsEngineOpenGL/src/PipelineResourceSignatureGLImpl.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/PipelineResourceSignatureGLImpl.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2025 Diligent Graphics LLC + * Copyright 2019-2026 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -33,6 +33,8 @@ #include #include "RenderDeviceGLImpl.hpp" +#include "BufferGLImpl.hpp" +#include "GLContextState.hpp" namespace Diligent { @@ -92,7 +94,7 @@ PipelineResourceSignatureGLImpl::PipelineResourceSignatureGLImpl(IReferenceCount }, [this]() // { - return ShaderResourceCacheGL::GetRequiredMemorySize(m_BindingCount); + return ShaderResourceCacheGL::GetRequiredMemorySize(m_BindingCount, m_TotalInlineConstants); }); } catch (...) @@ -104,8 +106,25 @@ PipelineResourceSignatureGLImpl::PipelineResourceSignatureGLImpl(IReferenceCount void PipelineResourceSignatureGLImpl::CreateLayout(const bool IsSerialized) { - TBindings StaticResCounter = {}; + // Count inline constant buffers + for (Uint32 i = 0; i < m_Desc.NumResources; ++i) + { + const PipelineResourceDesc& ResDesc = m_Desc.Resources[i]; + 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); + } + TBindings StaticResCounter = {}; + Uint16 NumStaticInlineConstants = 0; + Uint32 InlineConstantBufferIdx = 0; for (Uint32 i = 0; i < m_Desc.NumResources; ++i) { const PipelineResourceDesc& ResDesc = m_Desc.Resources[i]; @@ -170,21 +189,52 @@ void PipelineResourceSignatureGLImpl::CreateLayout(const bool IsSerialized) "Deserialized immutable sampler flag is invalid."); } - if (Range == BINDING_RANGE_UNIFORM_BUFFER && (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS) == 0) + // 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(); + + if (Range == BINDING_RANGE_UNIFORM_BUFFER && + (ResDesc.Flags & (PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS | PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS)) == 0) { - DEV_CHECK_ERR(size_t{CacheOffset} + ResDesc.ArraySize < sizeof(m_DynamicUBOMask) * 8, "Dynamic UBO index exceeds maximum representable bit position in the mask"); - for (Uint64 elem = 0; elem < ResDesc.ArraySize; ++elem) + DEV_CHECK_ERR(size_t{CacheOffset} + ArraySize < sizeof(m_DynamicUBOMask) * 8, "Dynamic UBO index exceeds maximum representable bit position in the mask"); + for (Uint64 elem = 0; elem < ArraySize; ++elem) m_DynamicUBOMask |= Uint64{1} << (Uint64{CacheOffset} + elem); } else if (Range == BINDING_RANGE_STORAGE_BUFFER && (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_NO_DYNAMIC_BUFFERS) == 0) { - DEV_CHECK_ERR(size_t{CacheOffset} + ResDesc.ArraySize < sizeof(m_DynamicSSBOMask) * 8, "Dynamic SSBO index exceeds maximum representable bit position in the mask"); - for (Uint64 elem = 0; elem < ResDesc.ArraySize; ++elem) + DEV_CHECK_ERR(size_t{CacheOffset} + ArraySize < sizeof(m_DynamicSSBOMask) * 8, "Dynamic SSBO index exceeds maximum representable bit position in the mask"); + for (Uint64 elem = 0; elem < ArraySize; ++elem) m_DynamicSSBOMask |= Uint64{1} << (Uint64{CacheOffset} + elem); } - VERIFY(CacheOffset + ResDesc.ArraySize <= std::numeric_limits::max(), "Cache offset exceeds representable range"); - CacheOffset += static_cast(ResDesc.ArraySize); + 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"); + InlineConstantBufferAttribsGL& InlineCBAttribs{m_InlineConstantBuffers[InlineConstantBufferIdx++]}; + InlineCBAttribs.CacheOffset = CacheOffset; + 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. + InlineCBAttribs.pBuffer = CreateInlineConstantBuffer(ResDesc.Name, ResDesc.ArraySize); + + m_TotalInlineConstants += static_cast(ResDesc.ArraySize); + + if (ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) + { + NumStaticInlineConstants += static_cast(ResDesc.ArraySize); + } + } + + VERIFY(CacheOffset + ArraySize <= std::numeric_limits::max(), "Cache offset exceeds representable range"); + CacheOffset += static_cast(ArraySize); if (ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) { @@ -194,10 +244,41 @@ void PipelineResourceSignatureGLImpl::CreateLayout(const bool IsSerialized) } } } + VERIFY_EXPR(InlineConstantBufferIdx == m_NumInlineConstantBuffers); if (m_pStaticResCache) { - m_pStaticResCache->Initialize(StaticResCounter, GetRawAllocator(), 0x0, 0x0); + m_pStaticResCache->Initialize(StaticResCounter, GetRawAllocator(), 0x0, 0x0, NumStaticInlineConstants); + + // Initialize inline constant buffers in the static cache. + // This allows static inline constants to be set on the signature and later copied to SRBs. + if (m_NumInlineConstantBuffers > 0) + { + Uint32 InlineConstantOffset = 0; + for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) + { + const InlineConstantBufferAttribsGL& InlineCBAttr = GetInlineConstantBuffer(i); + VERIFY_EXPR(InlineCBAttr.pBuffer); + VERIFY_EXPR(InlineCBAttr.NumConstants > 0); + + // Only initialize inline constant buffers that are within the static cache range. + // Mutable and dynamic inline constant buffers are not stored in the static cache. + if (InlineCBAttr.CacheOffset < StaticResCounter[BINDING_RANGE_UNIFORM_BUFFER]) + { + m_pStaticResCache->InitInlineConstantBuffer( + InlineCBAttr.CacheOffset, + InlineCBAttr.pBuffer, + InlineCBAttr.NumConstants, + InlineConstantOffset); + + InlineConstantOffset += InlineCBAttr.NumConstants; + } + } + VERIFY_EXPR(InlineConstantOffset == NumStaticInlineConstants); + } +#ifdef DILIGENT_DEBUG + m_pStaticResCache->DbgVerifyResourceInitialization(); +#endif } } @@ -403,19 +484,29 @@ void PipelineResourceSignatureGLImpl::CopyStaticResources(ShaderResourceCacheGL& switch (PipelineResourceToBindingRange(ResDesc)) { case BINDING_RANGE_UNIFORM_BUFFER: - for (Uint32 ArrInd = 0; ArrInd < ResDesc.ArraySize; ++ArrInd) + if (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) { - const ShaderResourceCacheGL::CachedUB& SrcCachedRes = SrcResourceCache.GetConstUB(ResAttr.CacheOffset + ArrInd); - if (!SrcCachedRes.pBuffer) + VERIFY(ResDesc.Flags == PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS, "INLINE_CONSTANTS flag is not compatible with other flags"); + // For inline constants, ResDesc.ArraySize is the number of 32-bit constants, not the array size. + // Copy the staging data from signature cache to SRB cache. + DstResourceCache.CopyInlineConstants(SrcResourceCache, ResAttr.CacheOffset, ResDesc.ArraySize); + } + else + { + 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, "'."); - continue; - } + const ShaderResourceCacheGL::CachedUB& SrcCachedRes = SrcResourceCache.GetConstUB(ResAttr.CacheOffset + ArrInd); + if (!SrcCachedRes.pBuffer) + { + 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; + } - DstResourceCache.SetUniformBuffer(ResAttr.CacheOffset + ArrInd, - RefCntAutoPtr{SrcCachedRes.pBuffer}, - SrcCachedRes.BaseOffset, SrcCachedRes.RangeSize); + DstResourceCache.SetUniformBuffer(ResAttr.CacheOffset + ArrInd, + RefCntAutoPtr{SrcCachedRes.pBuffer}, + SrcCachedRes.BaseOffset, SrcCachedRes.RangeSize); + } } break; case BINDING_RANGE_STORAGE_BUFFER: @@ -508,9 +599,65 @@ void PipelineResourceSignatureGLImpl::CopyStaticResources(ShaderResourceCacheGL& #endif } +void PipelineResourceSignatureGLImpl::UpdateInlineConstantBuffers(const ShaderResourceCacheGL& ResourceCache, + GLContextState& CtxState) const +{ + for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) + { + const InlineConstantBufferAttribsGL& InlineCBAttr = GetInlineConstantBuffer(i); + + const ShaderResourceCacheGL::CachedUB& InlineCB = ResourceCache.GetConstUB(InlineCBAttr.CacheOffset); + VERIFY(InlineCBAttr.NumConstants * sizeof(Uint32) == InlineCB.RangeSize, "Inline constant buffer size mismatch"); + VERIFY(InlineCB.pInlineConstantData != nullptr, "Inline constant data pointer is null"); + VERIFY(InlineCB.pBuffer, "Inline constant buffer is null in cache"); + + const Uint32 BufferSize = InlineCBAttr.NumConstants * sizeof(Uint32); + + // Get the buffer from the SRB cache (not from the signature's InlineCBAttr.pBuffer). + // This ensures we update the same buffer that was bound by BindResources(). + // When an SRB created from a compatible but different signature is used, + // the SRB's cache contains buffer pointers to the original signature's shared buffers. + // By updating the buffer from cache, we maintain consistency with D3D11's design. + BufferGLImpl* pBuffer = InlineCB.pBuffer; + + // Map the buffer and copy the staging data + PVoid pMappedData = nullptr; + pBuffer->Map(CtxState, MAP_WRITE, MAP_FLAG_DISCARD, pMappedData); + memcpy(pMappedData, InlineCB.pInlineConstantData, BufferSize); + pBuffer->Unmap(CtxState); + } +} + void PipelineResourceSignatureGLImpl::InitSRBResourceCache(ShaderResourceCacheGL& ResourceCache) { - ResourceCache.Initialize(m_BindingCount, m_SRBMemAllocator.GetResourceCacheDataAllocator(0), m_DynamicUBOMask, m_DynamicSSBOMask); + ResourceCache.Initialize(m_BindingCount, m_SRBMemAllocator.GetResourceCacheDataAllocator(0), m_DynamicUBOMask, m_DynamicSSBOMask, m_TotalInlineConstants); + + // Initialize inline constant buffers. + // Each inline constant buffer shares a single dynamic UBO created in CreateLayout(). + // The staging data is stored contiguously at the tail of the resource cache memory. + if (m_NumInlineConstantBuffers > 0) + { + // Inline constant data starts at m_MemoryEndOffset in the cache + Uint32 InlineConstantOffset = 0; + for (Uint32 i = 0; i < m_NumInlineConstantBuffers; ++i) + { + const InlineConstantBufferAttribsGL& InlineCBAttr = GetInlineConstantBuffer(i); + VERIFY_EXPR(InlineCBAttr.pBuffer); + VERIFY_EXPR(InlineCBAttr.NumConstants > 0); + + ResourceCache.InitInlineConstantBuffer( + InlineCBAttr.CacheOffset, + InlineCBAttr.pBuffer, + InlineCBAttr.NumConstants, + InlineConstantOffset); + + InlineConstantOffset += InlineCBAttr.NumConstants; + } + VERIFY_EXPR(InlineConstantOffset == m_TotalInlineConstants); + } +#ifdef DILIGENT_DEBUG + ResourceCache.DbgVerifyResourceInitialization(); +#endif // Initialize immutable samplers for (Uint32 r = 0; r < m_Desc.NumResources; ++r) @@ -653,7 +800,7 @@ PipelineResourceSignatureGLImpl::PipelineResourceSignatureGLImpl(IReferenceCount }, [this]() // { - return ShaderResourceCacheGL::GetRequiredMemorySize(m_BindingCount); + return ShaderResourceCacheGL::GetRequiredMemorySize(m_BindingCount, m_TotalInlineConstants); }); } catch (...) diff --git a/Graphics/GraphicsEngineOpenGL/src/PipelineStateGLImpl.cpp b/Graphics/GraphicsEngineOpenGL/src/PipelineStateGLImpl.cpp index eebbba198..c15e3ca5e 100644 --- a/Graphics/GraphicsEngineOpenGL/src/PipelineStateGLImpl.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/PipelineStateGLImpl.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2025 Diligent Graphics LLC + * Copyright 2019-2026 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -130,12 +130,44 @@ PipelineResourceSignatureDescWrapper PipelineStateGLImpl::GetDefaultSignatureDes } }; + // Specialized handler for uniform buffers to handle inline constants correctly + const auto HandleUniformBuffer = [&](const ShaderResourcesGL::UniformBufferInfo& UB) // + { + const ShaderResourceVariableDesc VarDesc = FindPipelineResourceLayoutVariable(ResourceLayout, UB.Name, UB.ShaderStages, nullptr); + const auto it_assigned = UniqueResources.emplace(ShaderResourceHashKey{VarDesc.ShaderStages, UB.Name}, UB); + if (it_assigned.second) + { + const PIPELINE_RESOURCE_FLAGS Flags = UB.ResourceFlags | ShaderVariableFlagsToPipelineResourceFlags(VarDesc.Flags); + + Uint32 ArraySize = UB.ArraySize; + if (Flags & PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS) + { + VERIFY(Flags == PIPELINE_RESOURCE_FLAG_INLINE_CONSTANTS, "INLINE_CONSTANTS flag cannot be combined with other flags."); + // For inline constants, use the constant count instead of the UBO array size + ArraySize = UB.BufferSize / sizeof(Uint32); + + if (ArraySize > MAX_INLINE_CONSTANTS) + { + LOG_ERROR_AND_THROW("Inline constants resource '", UB.Name, "' has ", + ArraySize, " constants. The maximum supported number of inline constants is ", + MAX_INLINE_CONSTANTS, '.'); + } + } + + SignDesc.AddResource(VarDesc.ShaderStages, UB.Name, ArraySize, UB.ResourceType, VarDesc.Type, Flags); + } + else + { + VerifyResourceMerge(m_Desc, it_assigned.first->second, UB); + } + }; + if (m_IsProgramPipelineSupported) { for (size_t i = 0; i < ShaderStages.size(); ++i) { ShaderGLImpl* pShaderGL = ShaderStages[i]; - pShaderGL->GetShaderResources()->ProcessConstResources(HandleResource, HandleResource, HandleResource, HandleResource); + pShaderGL->GetShaderResources()->ProcessConstResources(HandleUniformBuffer, HandleResource, HandleResource, HandleResource); } } else @@ -153,7 +185,7 @@ PipelineResourceSignatureDescWrapper PipelineStateGLImpl::GetDefaultSignatureDes SamplerResFlag, pImmediateCtx->GetContextState()); } - m_GLPrograms[0]->GetResources()->ProcessConstResources(HandleResource, HandleResource, HandleResource, HandleResource); + m_GLPrograms[0]->GetResources()->ProcessConstResources(HandleUniformBuffer, HandleResource, HandleResource, HandleResource); if (ResourceLayout.NumImmutableSamplers > 0) { diff --git a/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp b/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp index c147fe9c6..ea976cf0f 100644 --- a/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp +++ b/Tests/DiligentCoreAPITest/src/InlineConstantsTest.cpp @@ -213,7 +213,7 @@ class InlineConstants : public ::testing::Test GPUTestingEnvironment* pEnv = GPUTestingEnvironment::GetInstance(); IRenderDevice* pDevice = pEnv->GetDevice(); - if (!pDevice->GetDeviceInfo().IsD3DDevice() && !pDevice->GetDeviceInfo().IsVulkanDevice()) + if (!pDevice->GetDeviceInfo().IsD3DDevice() && !pDevice->GetDeviceInfo().IsVulkanDevice() && !pDevice->GetDeviceInfo().IsGLDevice()) { GTEST_SKIP(); }