From 42b12901274c2aabee3ddbb9d3a012d28e5f5f7d Mon Sep 17 00:00:00 2001 From: Egor Yusov Date: Mon, 7 Oct 2019 09:19:35 -0700 Subject: Completely reworked shader resource binding implementation in OpenGL backend to make it function similar to other backends --- Graphics/GraphicsEngineOpenGL/CMakeLists.txt | 6 +- .../include/BufferViewGLImpl.h | 10 +- .../include/GLPipelineResourceLayout.h | 352 +++++ Graphics/GraphicsEngineOpenGL/include/GLProgram.h | 52 - .../include/GLProgramResourceCache.h | 266 ++++ .../include/GLProgramResources.h | 382 +++--- .../include/PipelineStateGLImpl.h | 35 +- .../GraphicsEngineOpenGL/include/ShaderGLImpl.h | 17 +- .../include/ShaderResourceBindingGLImpl.h | 16 +- Graphics/GraphicsEngineOpenGL/include/pch.h | 6 + .../src/DeviceContextGLImpl.cpp | 476 +++---- .../GraphicsEngineOpenGL/src/GLContextState.cpp | 92 +- .../src/GLPipelineResourceLayout.cpp | 880 +++++++++++++ Graphics/GraphicsEngineOpenGL/src/GLProgram.cpp | 47 - .../src/GLProgramResourceCache.cpp | 112 ++ .../src/GLProgramResources.cpp | 1369 ++++++++------------ .../src/PipelineStateGLImpl.cpp | 229 ++-- Graphics/GraphicsEngineOpenGL/src/ShaderGLImpl.cpp | 156 ++- .../src/ShaderResourceBindingGLImpl.cpp | 104 +- .../GraphicsEngineOpenGL/src/TexRegionRender.cpp | 6 +- 20 files changed, 2897 insertions(+), 1716 deletions(-) create mode 100644 Graphics/GraphicsEngineOpenGL/include/GLPipelineResourceLayout.h delete mode 100644 Graphics/GraphicsEngineOpenGL/include/GLProgram.h create mode 100644 Graphics/GraphicsEngineOpenGL/include/GLProgramResourceCache.h create mode 100644 Graphics/GraphicsEngineOpenGL/src/GLPipelineResourceLayout.cpp delete mode 100644 Graphics/GraphicsEngineOpenGL/src/GLProgram.cpp create mode 100644 Graphics/GraphicsEngineOpenGL/src/GLProgramResourceCache.cpp (limited to 'Graphics/GraphicsEngineOpenGL') diff --git a/Graphics/GraphicsEngineOpenGL/CMakeLists.txt b/Graphics/GraphicsEngineOpenGL/CMakeLists.txt index 1a17fa14..41760feb 100644 --- a/Graphics/GraphicsEngineOpenGL/CMakeLists.txt +++ b/Graphics/GraphicsEngineOpenGL/CMakeLists.txt @@ -12,7 +12,8 @@ set(INCLUDE include/GLContext.h include/GLContextState.h include/GLObjectWrapper.h - include/GLProgram.h + include/GLProgramResourceCache.h + include/GLPipelineResourceLayout.h include/GLProgramResources.h include/GLTypeConversions.h include/pch.h @@ -60,7 +61,8 @@ set(SOURCE src/FenceGLImpl.cpp src/GLContextState.cpp src/GLObjectWrapper.cpp - src/GLProgram.cpp + src/GLProgramResourceCache.cpp + src/GLPipelineResourceLayout.cpp src/GLProgramResources.cpp src/GLTypeConversions.cpp src/PipelineStateGLImpl.cpp diff --git a/Graphics/GraphicsEngineOpenGL/include/BufferViewGLImpl.h b/Graphics/GraphicsEngineOpenGL/include/BufferViewGLImpl.h index b43a0c6e..bb834536 100644 --- a/Graphics/GraphicsEngineOpenGL/include/BufferViewGLImpl.h +++ b/Graphics/GraphicsEngineOpenGL/include/BufferViewGLImpl.h @@ -45,12 +45,12 @@ class BufferViewGLImpl final : public BufferViewBase; - BufferViewGLImpl( IReferenceCounters *pRefCounters, - RenderDeviceGLImpl *pDevice, - IDeviceContext *pContext, + BufferViewGLImpl( IReferenceCounters* pRefCounters, + RenderDeviceGLImpl* pDevice, + IDeviceContext* pContext, const BufferViewDesc& ViewDesc, - BufferGLImpl *pBuffer, - bool bIsDefaultView); + BufferGLImpl* pBuffer, + bool bIsDefaultView); /// Queries the specific interface, see IObject::QueryInterface() for details virtual void QueryInterface(const INTERFACE_ID& IID, IObject** ppInterface )override final; diff --git a/Graphics/GraphicsEngineOpenGL/include/GLPipelineResourceLayout.h b/Graphics/GraphicsEngineOpenGL/include/GLPipelineResourceLayout.h new file mode 100644 index 00000000..d2cf2951 --- /dev/null +++ b/Graphics/GraphicsEngineOpenGL/include/GLPipelineResourceLayout.h @@ -0,0 +1,352 @@ +/* Copyright 2019 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 + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS. + * + * 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. + */ + +#pragma once + +#include + +#include "Object.h" +#include "ShaderResourceVariableBase.h" +#include "GLProgramResources.h" +#include "GLProgramResourceCache.h" + +namespace Diligent +{ + +class IMemoryAllocator; + +class GLPipelineResourceLayout +{ +public: + GLPipelineResourceLayout(IObject& Owner) : + m_Owner(Owner) + {} + ~GLPipelineResourceLayout(); + + // No copies, only moves are allowed + GLPipelineResourceLayout (const GLPipelineResourceLayout&) = delete; + GLPipelineResourceLayout& operator = (const GLPipelineResourceLayout&) = delete; + GLPipelineResourceLayout ( GLPipelineResourceLayout&&) = default; + GLPipelineResourceLayout& operator = ( GLPipelineResourceLayout&&) = delete; + + void Initialize(GLProgramResources* ProgramResources, + Uint32 NumPrograms, + const PipelineResourceLayoutDesc& ResourceLayout, + const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, + Uint32 NumAllowedTypes, + GLProgramResourceCache* pResourceCache); + + static size_t GetRequiredMemorySize(GLProgramResources* ProgramResources, + Uint32 NumPrograms, + const PipelineResourceLayoutDesc& ResourceLayout, + const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, + Uint32 NumAllowedTypes); + + void CopyResources(GLProgramResourceCache& DstCache)const; + + struct GLVariableBase : public ShaderVariableBase + { + using TBase = ShaderVariableBase; + GLVariableBase(const GLProgramResources::GLResourceAttribs& ResourceAttribs, + GLPipelineResourceLayout& ParentLayout, + SHADER_RESOURCE_VARIABLE_TYPE VariableType, + Int32 StaticSamplerIdx) : + TBase {ParentLayout}, + m_Attribs {ResourceAttribs }, + m_VariableType {VariableType }, + m_StaticSamplerIdx{StaticSamplerIdx} + { + VERIFY_EXPR(StaticSamplerIdx < 0 || ResourceAttribs.ResourceType == SHADER_RESOURCE_TYPE_TEXTURE_SRV); + } + + virtual SHADER_RESOURCE_VARIABLE_TYPE GetType()const override final + { + return m_VariableType; + } + + virtual ShaderResourceDesc GetResourceDesc()const override final + { + return m_Attribs.GetResourceDesc(); + } + + virtual Uint32 GetIndex()const override final + { + return m_ParentResLayout.GetVariableIndex(*this); + } + + const GLProgramResources::GLResourceAttribs& m_Attribs; + const SHADER_RESOURCE_VARIABLE_TYPE m_VariableType; + const Int32 m_StaticSamplerIdx; + }; + + + struct UniformBuffBindInfo final : GLVariableBase + { + UniformBuffBindInfo(const GLProgramResources::GLResourceAttribs& ResourceAttribs, + GLPipelineResourceLayout& ParentResLayout, + SHADER_RESOURCE_VARIABLE_TYPE VariableType) : + GLVariableBase{ResourceAttribs, ParentResLayout, VariableType, -1} + {} + + // Non-virtual function + void BindResource(IDeviceObject* pObject, Uint32 ArrayIndex); + virtual void Set(IDeviceObject* pObject)override final + { + BindResource(pObject, 0); + } + + virtual void SetArray(IDeviceObject* const* ppObjects, Uint32 FirstElement, Uint32 NumElements)override final + { + for(Uint32 elem=0; elem < NumElements; ++elem) + BindResource(ppObjects[elem], FirstElement+elem); + } + + virtual bool IsBound(Uint32 ArrayIndex)const override final + { + VERIFY_EXPR(ArrayIndex < m_Attribs.ArraySize); + return m_ParentResLayout.m_pResourceCache->IsUBBound(m_Attribs.Binding + ArrayIndex); + } + }; + + + struct SamplerBindInfo final : GLVariableBase + { + SamplerBindInfo(const GLProgramResources::GLResourceAttribs& ResourceAttribs, + GLPipelineResourceLayout& ParentResLayout, + SHADER_RESOURCE_VARIABLE_TYPE VariableType, + Int32 StaticSamplerIdx) : + GLVariableBase {ResourceAttribs, ParentResLayout, VariableType, StaticSamplerIdx} + {} + + // Non-virtual function + void BindResource(IDeviceObject* pObject, Uint32 ArrayIndex); + virtual void Set(IDeviceObject* pObject)override final + { + BindResource(pObject, 0); + } + + virtual void SetArray(IDeviceObject* const* ppObjects, Uint32 FirstElement, Uint32 NumElements)override final + { + for(Uint32 elem=0; elem < NumElements; ++elem) + BindResource(ppObjects[elem], FirstElement+elem); + } + + virtual bool IsBound(Uint32 ArrayIndex)const override final + { + VERIFY_EXPR(ArrayIndex < m_Attribs.ArraySize); + return m_ParentResLayout.m_pResourceCache->IsSamplerBound(m_Attribs.Binding + ArrayIndex, m_Attribs.ResourceType == SHADER_RESOURCE_TYPE_TEXTURE_SRV); + } + }; + + + struct ImageBindInfo final : GLVariableBase + { + ImageBindInfo(const GLProgramResources::GLResourceAttribs& ResourceAttribs, + GLPipelineResourceLayout& ParentResLayout, + SHADER_RESOURCE_VARIABLE_TYPE VariableType) : + GLVariableBase{ResourceAttribs, ParentResLayout, VariableType, -1} + {} + + // Provide non-virtual function + void BindResource(IDeviceObject* pObject, Uint32 ArrayIndex); + virtual void Set(IDeviceObject* pObject)override final + { + BindResource(pObject, 0); + } + + virtual void SetArray(IDeviceObject* const* ppObjects, Uint32 FirstElement, Uint32 NumElements)override final + { + for(Uint32 elem=0; elem < NumElements; ++elem) + BindResource(ppObjects[elem], FirstElement+elem); + } + + virtual bool IsBound(Uint32 ArrayIndex)const override final + { + VERIFY_EXPR(ArrayIndex < m_Attribs.ArraySize); + return m_ParentResLayout.m_pResourceCache->IsImageBound(m_Attribs.Binding + ArrayIndex, m_Attribs.ResourceType == SHADER_RESOURCE_TYPE_TEXTURE_UAV); + } + }; + + + struct StorageBufferBindInfo final : GLVariableBase + { + StorageBufferBindInfo(const GLProgramResources::GLResourceAttribs& ResourceAttribs, + GLPipelineResourceLayout& ParentResLayout, + SHADER_RESOURCE_VARIABLE_TYPE VariableType) : + GLVariableBase{ResourceAttribs, ParentResLayout, VariableType, -1} + {} + + // Non-virtual function + void BindResource(IDeviceObject* pObject, Uint32 ArrayIndex); + virtual void Set(IDeviceObject* pObject)override final + { + BindResource(pObject, 0); + } + + virtual void SetArray(IDeviceObject* const* ppObjects, Uint32 FirstElement, Uint32 NumElements)override final + { + for(Uint32 elem=0; elem < NumElements; ++elem) + BindResource(ppObjects[elem], FirstElement+elem); + } + + virtual bool IsBound(Uint32 ArrayIndex)const override final + { + VERIFY_EXPR(ArrayIndex < m_Attribs.ArraySize); + return m_ParentResLayout.m_pResourceCache->IsSSBOBound(m_Attribs.Binding + ArrayIndex); + } + }; + + + // dbgResourceCache is only used for sanity check and as a remainder that the resource cache must be alive + // while Layout is alive + void BindResources(SHADER_TYPE ShaderStage, IResourceMapping* pResourceMapping, Uint32 Flags, const GLProgramResourceCache& dbgResourceCache); + +#ifdef DEVELOPMENT + bool dvpVerifyBindings()const; +#endif + + IShaderResourceVariable* GetShaderVariable( SHADER_TYPE ShaderStage, const Char* Name ); + IShaderResourceVariable* GetShaderVariable( SHADER_TYPE ShaderStage, Uint32 Index ); + + IObject& GetOwner(){return m_Owner;} + + Uint32 GetNumVariables(SHADER_TYPE ShaderStage)const; + + Uint32 GetNumUBs() const { return (m_SamplerOffset - m_UBOffset) / sizeof(UniformBuffBindInfo); } + Uint32 GetNumSamplers() const { return (m_ImageOffset - m_SamplerOffset) / sizeof(SamplerBindInfo); } + Uint32 GetNumImages() const { return (m_StorageBufferOffset - m_ImageOffset) / sizeof(ImageBindInfo) ; } + Uint32 GetNumStorageBuffers() const { return (m_VariableEndOffset - m_StorageBufferOffset) / sizeof(StorageBufferBindInfo); } + + template Uint32 GetNumResources()const; + template<> Uint32 GetNumResources() const { return GetNumUBs(); } + template<> Uint32 GetNumResources() const { return GetNumSamplers(); } + template<> Uint32 GetNumResources() const { return GetNumImages(); } + template<> Uint32 GetNumResources() const { return GetNumStorageBuffers(); } + + template + const ResourceType& GetConstResource(Uint32 ResIndex)const + { + VERIFY(ResIndex < GetNumResources(), "Resource index (", ResIndex, ") exceeds max allowed value (", GetNumResources(), ")"); + auto Offset = GetResourceOffset(); + return reinterpret_cast( reinterpret_cast(m_ResourceBuffer.get()) + Offset)[ResIndex]; + } + + Uint32 GetVariableIndex(const GLVariableBase& Var)const; + +private: + +/* 0*/ IObject& m_Owner; + // No need to use shared pointer, as the resource cache is either part of the same + // ShaderGLImpl object, or ShaderResourceBindingGLImpl object +/* 8*/ GLProgramResourceCache* m_pResourceCache = nullptr; +/*16*/ std::unique_ptr > m_ResourceBuffer; + + // Offsets in bytes + using OffsetType = Uint16; + static constexpr OffsetType m_UBOffset = 0; +/*32*/ OffsetType m_SamplerOffset = 0; +/*34*/ OffsetType m_ImageOffset = 0; +/*36*/ OffsetType m_StorageBufferOffset = 0; +/*38*/ OffsetType m_VariableEndOffset = 0; +/*40*/ std::array m_ProgramIndex = {-1, -1, -1, -1, -1, -1}; +/*46*/ Uint8 m_NumPrograms = 0; +/*48*/ + + template OffsetType GetResourceOffset()const; + template<> OffsetType GetResourceOffset () const { return m_UBOffset; } + template<> OffsetType GetResourceOffset () const { return m_SamplerOffset; } + template<> OffsetType GetResourceOffset () const { return m_ImageOffset; } + template<> OffsetType GetResourceOffset() const { return m_StorageBufferOffset; } + + template + ResourceType& GetResource(Uint32 ResIndex) + { + VERIFY(ResIndex < GetNumResources(), "Resource index (", ResIndex, ") exceeds max allowed value (", GetNumResources()-1, ")"); + auto Offset = GetResourceOffset(); + return reinterpret_cast( reinterpret_cast(m_ResourceBuffer.get()) + Offset)[ResIndex]; + } + + GLProgramResources::ResourceCounters& GetProgramVarEndOffsets(Uint32 prog) + { + VERIFY_EXPR(prog < m_NumPrograms); + return reinterpret_cast( reinterpret_cast(m_ResourceBuffer.get()) + m_VariableEndOffset)[prog]; + } + + const GLProgramResources::ResourceCounters& GetProgramVarEndOffsets(Uint32 prog)const + { + VERIFY_EXPR(prog < m_NumPrograms); + return reinterpret_cast( reinterpret_cast(m_ResourceBuffer.get()) + m_VariableEndOffset)[prog]; + } + + template + IShaderResourceVariable* GetResourceByName(SHADER_TYPE ShaderStage, const Char* Name); + + template + void HandleResources(THandleUB HandleUB, + THandleSampler HandleSampler, + THandleImage HandleImage, + THandleStorageBuffer HandleStorageBuffer) + { + for (Uint32 ub = 0; ub < GetNumResources(); ++ub) + HandleUB(GetResource(ub)); + + for (Uint32 s = 0; s < GetNumResources(); ++s) + HandleSampler(GetResource(s)); + + for (Uint32 i = 0; i < GetNumResources(); ++i) + HandleImage(GetResource(i)); + + for (Uint32 s = 0; s < GetNumResources(); ++s) + HandleStorageBuffer(GetResource(s)); + } + + template + void HandleConstResources(THandleUB HandleUB, + THandleSampler HandleSampler, + THandleImage HandleImage, + THandleStorageBuffer HandleStorageBuffer)const + { + for (Uint32 ub = 0; ub < GetNumResources(); ++ub) + HandleUB(GetConstResource(ub)); + + for (Uint32 s = 0; s < GetNumResources(); ++s) + HandleSampler(GetConstResource(s)); + + for (Uint32 i = 0; i < GetNumResources(); ++i) + HandleImage(GetConstResource(i)); + + for (Uint32 s = 0; s < GetNumResources(); ++s) + HandleStorageBuffer(GetConstResource(s)); + } + + friend class ShaderVariableIndexLocator; + friend class ShaderVariableLocator; +}; + +} diff --git a/Graphics/GraphicsEngineOpenGL/include/GLProgram.h b/Graphics/GraphicsEngineOpenGL/include/GLProgram.h deleted file mode 100644 index 03399bd1..00000000 --- a/Graphics/GraphicsEngineOpenGL/include/GLProgram.h +++ /dev/null @@ -1,52 +0,0 @@ -/* Copyright 2019 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 - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS. - * - * 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. - */ - -#pragma once -#include "GLObjectWrapper.h" -#include "GLProgramResources.h" - -namespace Diligent -{ - class GLProgram : public GLObjectWrappers::GLProgramObj - { - public: - GLProgram(bool CreateObject); - GLProgram(GLProgram&& Program); - - GLProgram (const GLProgram&) = delete; - GLProgram& operator = (const GLProgram&) = delete; - GLProgram& operator = ( GLProgram&&) = delete; - - void InitResources(RenderDeviceGLImpl* pDeviceGLImpl, - SHADER_TYPE ShaderStage, - IObject& Owner); - - void BindConstantResources(IResourceMapping* pResourceMapping, Uint32 Flags); - - const GLProgramResources& GetResources() const { return m_AllResources; } - - private: - GLProgramResources m_AllResources; - // When adding new member DO NOT FORGET TO UPDATE GLProgram( GLProgram&& Program )!!! - }; -} diff --git a/Graphics/GraphicsEngineOpenGL/include/GLProgramResourceCache.h b/Graphics/GraphicsEngineOpenGL/include/GLProgramResourceCache.h new file mode 100644 index 00000000..a98d88cb --- /dev/null +++ b/Graphics/GraphicsEngineOpenGL/include/GLProgramResourceCache.h @@ -0,0 +1,266 @@ +/* Copyright 2019 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 + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS. + * + * 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. + */ + +#pragma once + +#include "BufferGLImpl.h" +#include "TextureBaseGL.h" +#include "SamplerGLImpl.h" + +namespace Diligent +{ + +/// The class implements a cache that holds resources bound to a specific GL program +// All resources are stored in the continuous memory using the following layout: +// +// | Cached UBs | Cached Samplers | Cached Images | Cached Strg Blocks | +// |----------------------------------------------------|--------------------------|---------------------------| +// | 0 | 1 | ... | UBCount-1 | 0 | 1 | ...| SmpCount-1 | 0 | 1 | ... | ImgCount-1 | 0 | 1 | ... | SBOCount-1 | +// ----------------------------------------------------------------------------------------------------------- +// +class GLProgramResourceCache +{ +public: + GLProgramResourceCache() + {} + + ~GLProgramResourceCache(); + + GLProgramResourceCache (const GLProgramResourceCache&) = delete; + GLProgramResourceCache& operator = (const GLProgramResourceCache&) = delete; + GLProgramResourceCache (GLProgramResourceCache&&) = delete; + GLProgramResourceCache& operator = (GLProgramResourceCache&&) = delete; + + /// Describes a resource bound to a uniform buffer or a shader storage block slot + struct CachedUB + { + /// Strong reference to the buffer + RefCntAutoPtr pBuffer; + }; + + /// Describes a resource bound to a sampler or an image slot + struct CachedResourceView + { + /// Wee keep strong reference to the view instead of the reference + /// to the texture or buffer because this is more efficient from + /// performance point of view: this avoids one pair of + /// AddStrongRef()/ReleaseStrongRef(). The view holds a strong reference + /// to the texture or the buffer, so it makes no difference. + RefCntAutoPtr pView; + TextureBaseGL* pTexture = nullptr; + union + { + BufferGLImpl* pBuffer = nullptr; // When pTexture == nullptr + SamplerGLImpl* pSampler; // When pTexture != nullptr + }; + CachedResourceView() noexcept {} + + void Set(RefCntAutoPtr&& pTexView, bool SetSampler) + { + // Do not null out pSampler as it could've been initialized by PipelineStateGLImpl::InitializeSRBResourceCache! + // pSampler = nullptr; + + // Avoid unnecessary virtual function calls + pTexture = pTexView ? ValidatedCast(pTexView->TextureViewGLImpl::GetTexture()) : nullptr; + if (pTexView && SetSampler) + { + pSampler = ValidatedCast(pTexView->GetSampler()); + } + + pView.Attach(pTexView.Detach()); + } + + void Set(RefCntAutoPtr&& pBufView) + { + pTexture = nullptr; + // Avoid unnecessary virtual function calls + pBuffer = pBufView ? ValidatedCast(pBufView->BufferViewGLImpl::GetBuffer()) : nullptr; + pView.Attach(pBufView.Detach()); + } + }; + + struct CachedSSBO + { + /// Strong reference to the buffer + RefCntAutoPtr pBufferView; + }; + + + static size_t GetRequriedMemorySize(Uint32 UBCount, Uint32 SamplerCount, Uint32 ImageCount, Uint32 SSBOCount); + void Initialize(Uint32 UBCount, Uint32 SamplerCount, Uint32 ImageCount, Uint32 SSBOCount, class IMemoryAllocator& MemAllocator); + void Destroy(class IMemoryAllocator& MemAllocator); + + void SetUniformBuffer(Uint32 Binding, RefCntAutoPtr&& pBuff) + { + GetUB(Binding).pBuffer = std::move(pBuff); + } + + void SetTexSampler(Uint32 Binding, RefCntAutoPtr&& pTexView, bool SetSampler) + { + GetSampler(Binding).Set(std::move(pTexView), SetSampler); + } + + void SetStaticSampler(Uint32 Binding, ISampler* pStaticSampler) + { + GetSampler(Binding).pSampler = ValidatedCast(pStaticSampler); + } + + void CopySampler(Uint32 Binding, const CachedResourceView& SrcSam) + { + GetSampler(Binding) = SrcSam; + } + + void SetBufSampler(Uint32 Binding, RefCntAutoPtr&& pBuffView) + { + GetSampler(Binding).Set(std::move(pBuffView)); + } + + void SetTexImage(Uint32 Binding, RefCntAutoPtr&& pTexView) + { + GetImage(Binding).Set(std::move(pTexView), false); + } + + void SetBufImage(Uint32 Binding, RefCntAutoPtr&& pBuffView) + { + GetImage(Binding).Set(std::move(pBuffView)); + } + + void CopyImage(Uint32 Binding, const CachedResourceView& SrcImg) + { + GetImage(Binding) = SrcImg; + } + + void SetSSBO(Uint32 Binding, RefCntAutoPtr&& pBuffView) + { + GetSSBO(Binding).pBufferView = std::move(pBuffView); + } + + bool IsUBBound(Uint32 Binding)const + { + if (Binding >= GetUBCount()) + return false; + + const auto& UB = GetUB(Binding); + return UB.pBuffer; + } + + bool IsSamplerBound(Uint32 Binding, bool dbgIsTextureView)const + { + if (Binding >= GetSamplerCount()) + return false; + + const auto& Sampler = GetSampler(Binding); + VERIFY_EXPR(dbgIsTextureView || Sampler.pTexture == nullptr); + return Sampler.pView; + } + + bool IsImageBound(Uint32 Binding, bool dbgIsTextureView)const + { + if (Binding >= GetImageCount()) + return false; + + const auto& Image = GetImage(Binding); + VERIFY_EXPR(dbgIsTextureView || Image.pTexture == nullptr); + return Image.pView; + } + + bool IsSSBOBound(Uint32 Binding)const + { + if (Binding >= GetSSBOCount()) + return false; + + const auto& SSBO = GetSSBO(Binding); + return SSBO.pBufferView; + } + + Uint32 GetUBCount() const { return (m_SmplrsOffset - m_UBsOffset) / sizeof(CachedUB); } + Uint32 GetSamplerCount() const { return (m_ImgsOffset - m_SmplrsOffset) / sizeof(CachedResourceView); } + Uint32 GetImageCount() const { return (m_SSBOsOffset - m_ImgsOffset) / sizeof(CachedResourceView); } + Uint32 GetSSBOCount() const { return (m_MemoryEndOffset - m_SSBOsOffset) / sizeof(CachedSSBO); } + + const CachedUB& GetUB(Uint32 Binding)const + { + VERIFY(Binding < GetUBCount(), "Uniform buffer binding (", Binding, ") is out of range"); + return reinterpret_cast(m_pResourceData + m_UBsOffset)[Binding]; + } + + const CachedResourceView& GetSampler(Uint32 Binding)const + { + VERIFY(Binding < GetSamplerCount(), "Sampler binding (", Binding, ") is out of range"); + return reinterpret_cast(m_pResourceData + m_SmplrsOffset)[Binding]; + } + + const CachedResourceView& GetImage(Uint32 Binding)const + { + VERIFY(Binding < GetImageCount(), "Image buffer binding (", Binding, ") is out of range"); + return reinterpret_cast(m_pResourceData + m_ImgsOffset)[Binding]; + } + + const CachedSSBO& GetSSBO(Uint32 Binding)const + { + VERIFY(Binding < GetSSBOCount(), "Shader storage block binding (", Binding, ") is out of range"); + return reinterpret_cast(m_pResourceData + m_SSBOsOffset)[Binding]; + } + + bool IsInitialized()const + { + return m_MemoryEndOffset != InvalidResourceOffset; + } + +private: + CachedUB& GetUB(Uint32 Binding) + { + return const_cast(const_cast(this)->GetUB(Binding)); + } + + CachedResourceView& GetSampler(Uint32 Binding) + { + return const_cast(const_cast(this)->GetSampler(Binding)); + } + + CachedResourceView& GetImage(Uint32 Binding) + { + return const_cast(const_cast(this)->GetImage(Binding)); + } + + CachedSSBO& GetSSBO(Uint32 Binding) + { + return const_cast(const_cast(this)->GetSSBO(Binding)); + } + + static constexpr const Uint16 InvalidResourceOffset = 0xFFFF; + static constexpr const Uint16 m_UBsOffset = 0; + Uint16 m_SmplrsOffset = InvalidResourceOffset; + Uint16 m_ImgsOffset = InvalidResourceOffset; + Uint16 m_SSBOsOffset = InvalidResourceOffset; + Uint16 m_MemoryEndOffset = InvalidResourceOffset; + + Uint8* m_pResourceData = nullptr; + +#ifdef _DEBUG + IMemoryAllocator* m_pdbgMemoryAllocator = nullptr; +#endif + +}; + +} diff --git a/Graphics/GraphicsEngineOpenGL/include/GLProgramResources.h b/Graphics/GraphicsEngineOpenGL/include/GLProgramResources.h index 703841cc..74faad45 100644 --- a/Graphics/GraphicsEngineOpenGL/include/GLProgramResources.h +++ b/Graphics/GraphicsEngineOpenGL/include/GLProgramResources.h @@ -25,17 +25,9 @@ #include -#include "GLObjectWrapper.h" -#include "HashUtils.h" -#include "ShaderBase.h" -#include "SamplerGLImpl.h" -#include "HashUtils.h" -#include "ShaderResourceVariableBase.h" +#include "Object.h" #include "StringPool.h" - -#ifdef _DEBUG -# define VERIFY_RESOURCE_BINDINGS -#endif +#include "HashUtils.h" namespace Diligent { @@ -50,91 +42,75 @@ namespace Diligent GLProgramResources& operator = (const GLProgramResources&) = delete; GLProgramResources& operator = ( GLProgramResources&&) = delete; - void LoadUniforms(IObject& Owner, - class RenderDeviceGLImpl* pDeviceGLImpl, - SHADER_TYPE ShaderStages, - GLuint GLProgram); + void LoadUniforms(SHADER_TYPE ShaderStages, + const GLObjectWrappers::GLProgramObj& GLProgram, + class GLContextState& State, + Uint32& UniformBufferBinding, + Uint32& SamplerBinding, + Uint32& ImageBinding, + Uint32& StorageBufferBinding); - - void Clone(class RenderDeviceGLImpl* pDeviceGLImpl, - IObject& Owner, - const GLProgramResources& SrcResources, - const PipelineResourceLayoutDesc& ResourceLayout, - const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, - Uint32 NumAllowedTypes); - - struct GLProgramVariableBase : ShaderVariableBase + struct GLResourceAttribs { -/* 0 */ // ShaderVariableBase -/* 16 */ const Char* Name; -/* 24 */ const SHADER_RESOURCE_VARIABLE_TYPE VariableType; -/* 25 */ const SHADER_RESOURCE_TYPE ResourceType; -/* 26 */ const Uint16 VariableIndex; -/* 28 */ Uint32 ArraySize; -/* 32 */ RefCntAutoPtr* const pResources; -/* 40 */ //End of data - - GLProgramVariableBase(IObject& _Owner, - const Char* _Name, - SHADER_RESOURCE_VARIABLE_TYPE _VariableType, - SHADER_RESOURCE_TYPE _ResourceType, - Uint16 _VariableIndex, - Uint32 _ArraySize, - RefCntAutoPtr* _pResources) : - ShaderVariableBase(_Owner), - Name (_Name), - VariableType (_VariableType), - ResourceType (_ResourceType), - VariableIndex (_VariableIndex), - ArraySize (_ArraySize), - pResources (_pResources) - { - VERIFY_EXPR(_ArraySize >= 1); - } - - bool IsCompatibleWith(const GLProgramVariableBase& Var)const - { - return VariableType == Var.VariableType && - ResourceType == Var.ResourceType && - ArraySize == Var.ArraySize; - } - - size_t GetHash()const +/* 0 */ const Char* Name; +/* 8 */ const SHADER_TYPE ShaderStages; +/* 12 */ const SHADER_RESOURCE_TYPE ResourceType; +/* 16 */ const Uint32 Binding; +/* 20 */ Uint32 ArraySize; +/* 24 */ // End of data + + GLResourceAttribs(const Char* _Name, + SHADER_TYPE _ShaderStages, + SHADER_RESOURCE_TYPE _ResourceType, + Uint32 _Binding, + Uint32 _ArraySize)noexcept : + Name {_Name }, + ShaderStages {_ShaderStages}, + ResourceType {_ResourceType}, + Binding {_Binding }, + ArraySize {_ArraySize } { - return ComputeHash(static_cast(VariableType), static_cast(ResourceType), ArraySize); + VERIFY(_ShaderStages != SHADER_TYPE_UNKNOWN, "At least one shader stage must be specified"); + VERIFY(_ResourceType != SHADER_RESOURCE_TYPE_UNKNOWN, "Unknown shader resource type"); + VERIFY(_ArraySize >= 1, "Array size must be greater than 1"); } - virtual void Set(IDeviceObject* pObject)override final + GLResourceAttribs(const GLResourceAttribs& Attribs, + StringPool& NamesPool)noexcept : + GLResourceAttribs + { + NamesPool.CopyString(Attribs.Name), + Attribs.ShaderStages, + Attribs.ResourceType, + Attribs.Binding, + Attribs.ArraySize + } { - VERIFY(pResources != nullptr, "This variable has no resource cache attached"); - pResources[0] = pObject; } - virtual void SetArray(IDeviceObject* const* ppObjects, Uint32 FirstElement, Uint32 NumElements)override final + bool IsCompatibleWith(const GLResourceAttribs& Var)const { - VERIFY(pResources, "This variable has no resource cache attached"); - VERIFY(FirstElement + NumElements <= ArraySize, "Array indices are out of range"); - for (Uint32 i=0; i < NumElements; ++i) - pResources[FirstElement + i] = ppObjects[i]; - } - - virtual SHADER_RESOURCE_VARIABLE_TYPE GetType()const override final - { - return VariableType; + return ShaderStages == Var.ShaderStages && + ResourceType == Var.ResourceType && + Binding == Var.Binding && + ArraySize == Var.ArraySize; } - virtual Uint32 GetIndex()const override final + size_t GetHash()const { - return VariableIndex; + return ComputeHash(static_cast(ShaderStages), static_cast(ResourceType), Binding, ArraySize); } - virtual bool IsBound(Uint32 ArrayIndex) const override final + String GetPrintName(Uint32 ArrayInd)const { - VERIFY(ArrayIndex < ArraySize, "Array index (", ArrayIndex, ") is out of range"); - return pResources[ArrayIndex] != nullptr; + VERIFY_EXPR(ArrayInd < ArraySize); + if (ArraySize > 1) + return String(Name) + '[' + std::to_string(ArrayInd) + ']'; + else + return Name; } - virtual ShaderResourceDesc GetResourceDesc()const override final + ShaderResourceDesc GetResourceDesc()const { ShaderResourceDesc ResourceDesc; ResourceDesc.Name = Name; @@ -144,34 +120,38 @@ namespace Diligent } }; - struct UniformBufferInfo final : GLProgramVariableBase + struct UniformBufferInfo final : GLResourceAttribs { UniformBufferInfo (const UniformBufferInfo&) = delete; UniformBufferInfo& operator= (const UniformBufferInfo&) = delete; UniformBufferInfo ( UniformBufferInfo&&) = default; UniformBufferInfo& operator= ( UniformBufferInfo&&) = delete; - UniformBufferInfo(IObject& _Owner, - const Char* _Name, - SHADER_RESOURCE_VARIABLE_TYPE _VariableType, - SHADER_RESOURCE_TYPE _ResourceType, - Uint16 _VariableIndex, - Uint32 _ArraySize, - RefCntAutoPtr* _pResources, - GLuint _UBIndex) : - GLProgramVariableBase(_Owner, _Name, _VariableType, _ResourceType, _VariableIndex, _ArraySize, _pResources), - UBIndex(_UBIndex) + UniformBufferInfo(const Char* _Name, + SHADER_TYPE _ShaderStages, + SHADER_RESOURCE_TYPE _ResourceType, + Uint32 _Binding, + Uint32 _ArraySize, + GLuint _UBIndex)noexcept : + GLResourceAttribs{_Name, _ShaderStages, _ResourceType, _Binding, _ArraySize}, + UBIndex {_UBIndex} + {} + + UniformBufferInfo(const UniformBufferInfo& UB, + StringPool& NamesPool)noexcept : + GLResourceAttribs{UB, NamesPool}, + UBIndex {UB.UBIndex } {} - bool IsCompatibleWith(const UniformBufferInfo& UBI)const + bool IsCompatibleWith(const UniformBufferInfo& UB)const { - return UBIndex == UBI.UBIndex && - GLProgramVariableBase::IsCompatibleWith(UBI); + return UBIndex == UB.UBIndex && + GLResourceAttribs::IsCompatibleWith(UB); } size_t GetHash()const { - return ComputeHash(UBIndex, GLProgramVariableBase::GetHash()); + return ComputeHash(UBIndex, GLResourceAttribs::GetHash()); } const GLuint UBIndex; @@ -179,79 +159,86 @@ namespace Diligent static_assert((sizeof(UniformBufferInfo) % sizeof(void*)) == 0, "sizeof(UniformBufferInfo) must be multiple of sizeof(void*)"); - struct SamplerInfo final : GLProgramVariableBase + struct SamplerInfo final : GLResourceAttribs { SamplerInfo (const SamplerInfo&) = delete; SamplerInfo& operator= (const SamplerInfo&) = delete; SamplerInfo ( SamplerInfo&&) = default; SamplerInfo& operator= ( SamplerInfo&&) = delete; - SamplerInfo(IObject& _Owner, - const Char* _Name, - SHADER_RESOURCE_VARIABLE_TYPE _VariableType, - SHADER_RESOURCE_TYPE _ResourceType, - Uint16 _VariableIndex, - Uint32 _ArraySize, - RefCntAutoPtr* _pResources, - GLint _Location, - GLenum _SamplerType, - class SamplerGLImpl* _pStaticSampler) : - GLProgramVariableBase(_Owner, _Name, _VariableType, _ResourceType, _VariableIndex, _ArraySize, _pResources), - Location (_Location), - SamplerType (_SamplerType), - pStaticSampler(_pStaticSampler) + SamplerInfo(const Char* _Name, + SHADER_TYPE _ShaderStages, + SHADER_RESOURCE_TYPE _ResourceType, + Uint32 _Binding, + Uint32 _ArraySize, + GLint _Location, + GLenum _SamplerType)noexcept : + GLResourceAttribs{_Name, _ShaderStages, _ResourceType, _Binding, _ArraySize}, + Location {_Location }, + SamplerType {_SamplerType} {} - bool IsCompatibleWith(const SamplerInfo& SI)const + SamplerInfo(const SamplerInfo& Sam, + StringPool& NamesPool)noexcept : + GLResourceAttribs{Sam, NamesPool}, + Location {Sam.Location }, + SamplerType {Sam.SamplerType} + {} + + bool IsCompatibleWith(const SamplerInfo& Sam)const { - return Location == SI.Location && - pStaticSampler == SI.pStaticSampler && - GLProgramVariableBase::IsCompatibleWith(SI); + return Location == Sam.Location && + SamplerType == Sam.SamplerType && + GLResourceAttribs::IsCompatibleWith(Sam); } size_t GetHash()const { - return ComputeHash(Location, SamplerType, GLProgramVariableBase::GetHash()); + return ComputeHash(Location, SamplerType, GLResourceAttribs::GetHash()); } - const GLint Location; - const GLenum SamplerType; - RefCntAutoPtr pStaticSampler; + const GLint Location; + const GLenum SamplerType; }; static_assert((sizeof(SamplerInfo) % sizeof(void*)) == 0, "sizeof(SamplerInfo) must be multiple of sizeof(void*)"); - struct ImageInfo final : GLProgramVariableBase + struct ImageInfo final : GLResourceAttribs { ImageInfo (const ImageInfo&) = delete; ImageInfo& operator= (const ImageInfo&) = delete; ImageInfo ( ImageInfo&&) = default; ImageInfo& operator= ( ImageInfo&&) = delete; - ImageInfo(IObject& _Owner, - const Char* _Name, - SHADER_RESOURCE_VARIABLE_TYPE _VariableType, - SHADER_RESOURCE_TYPE _ResourceType, - Uint16 _VariableIndex, - Uint32 _ArraySize, - RefCntAutoPtr* _pResources, - GLint _Location, - GLenum _ImageType) : - GLProgramVariableBase(_Owner, _Name, _VariableType, _ResourceType, _VariableIndex, _ArraySize, _pResources), - Location (_Location), - ImageType (_ImageType) + ImageInfo(const Char* _Name, + SHADER_TYPE _ShaderStages, + SHADER_RESOURCE_TYPE _ResourceType, + Uint32 _Binding, + Uint32 _ArraySize, + GLint _Location, + GLenum _ImageType)noexcept : + GLResourceAttribs{_Name, _ShaderStages, _ResourceType, _Binding, _ArraySize}, + Location {_Location }, + ImageType {_ImageType} + {} + + ImageInfo(const ImageInfo& Img, + StringPool& NamesPool)noexcept : + GLResourceAttribs{Img, NamesPool}, + Location {Img.Location }, + ImageType {Img.ImageType} {} - bool IsCompatibleWith(const ImageInfo& II)const + bool IsCompatibleWith(const ImageInfo& Img)const { - return Location == II.Location && - ImageType == II.ImageType && - GLProgramVariableBase::IsCompatibleWith(II); + return Location == Img.Location && + ImageType == Img.ImageType && + GLResourceAttribs::IsCompatibleWith(Img); } size_t GetHash()const { - return ComputeHash(Location, ImageType, GLProgramVariableBase::GetHash()); + return ComputeHash(Location, ImageType, GLResourceAttribs::GetHash()); } const GLint Location; @@ -260,34 +247,38 @@ namespace Diligent static_assert((sizeof(ImageInfo) % sizeof(void*)) == 0, "sizeof(ImageInfo) must be multiple of sizeof(void*)"); - struct StorageBlockInfo final : GLProgramVariableBase + struct StorageBlockInfo final : GLResourceAttribs { StorageBlockInfo (const StorageBlockInfo&) = delete; StorageBlockInfo& operator= (const StorageBlockInfo&) = delete; StorageBlockInfo ( StorageBlockInfo&&) = default; StorageBlockInfo& operator= ( StorageBlockInfo&&) = delete; - StorageBlockInfo(IObject& _Owner, - const Char* _Name, - SHADER_RESOURCE_VARIABLE_TYPE _VariableType, - SHADER_RESOURCE_TYPE _ResourceType, - Uint16 _VariableIndex, - Uint32 _ArraySize, - RefCntAutoPtr* _pResources, - GLint _SBIndex) : - GLProgramVariableBase(_Owner, _Name, _VariableType, _ResourceType, _VariableIndex, _ArraySize, _pResources), - SBIndex(_SBIndex) + StorageBlockInfo(const Char* _Name, + SHADER_TYPE _ShaderStages, + SHADER_RESOURCE_TYPE _ResourceType, + Uint32 _Binding, + Uint32 _ArraySize, + GLint _SBIndex)noexcept : + GLResourceAttribs{_Name, _ShaderStages, _ResourceType, _Binding, _ArraySize}, + SBIndex {_SBIndex} {} - bool IsCompatibleWith(const StorageBlockInfo& SBI)const + StorageBlockInfo(const StorageBlockInfo& SB, + StringPool& NamesPool)noexcept : + GLResourceAttribs{SB, NamesPool}, + SBIndex {SB.SBIndex} + {} + + bool IsCompatibleWith(const StorageBlockInfo& SB)const { - return SBIndex == SBI.SBIndex && - GLProgramVariableBase::IsCompatibleWith(SBI); + return SBIndex == SB.SBIndex && + GLResourceAttribs::IsCompatibleWith(SB); } size_t GetHash()const { - return ComputeHash(SBIndex, GLProgramVariableBase::GetHash()); + return ComputeHash(SBIndex, GLResourceAttribs::GetHash()); } const GLint SBIndex; @@ -349,24 +340,12 @@ namespace Diligent return m_StorageBlocks[Index]; } - Uint32 GetVariableCount()const { return m_NumUniformBuffers + m_NumSamplers + m_NumImages + m_NumStorageBlocks; } - void BindResources(IResourceMapping* pResourceMapping, Uint32 Flags); - -#ifdef VERIFY_RESOURCE_BINDINGS - void dbgVerifyResourceBindings()const; -#endif - - GLProgramVariableBase* GetVariable(const Char* Name); - GLProgramVariableBase* GetVariable(Uint32 Index) - { - return const_cast(const_cast(this)->GetVariable(Index)); - } - const GLProgramVariableBase* GetVariable(Uint32 Index)const; + ShaderResourceDesc GetResourceDesc(Uint32 Index)const; bool IsCompatibleWith(const GLProgramResources& Res)const; size_t GetHash()const; @@ -380,27 +359,51 @@ namespace Diligent void ProcessConstResources(THandleUB HandleUB, THandleSampler HandleSampler, THandleImg HandleImg, - THandleSB HandleSB)const + THandleSB HandleSB, + const PipelineResourceLayoutDesc* pResourceLayout = nullptr, + const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes = nullptr, + Uint32 NumAllowedTypes = 0)const { + const Uint32 AllowedTypeBits = GetAllowedTypeBits(AllowedVarTypes, NumAllowedTypes); + auto CheckResourceType = [&](const char* Name) + { + if (pResourceLayout == nullptr) + return true; + else + { + auto VarType = GetShaderVariableType(m_ShaderStages, Name, *pResourceLayout); + return IsAllowedType(VarType, AllowedTypeBits); + } + }; + for (Uint32 ub=0; ub < m_NumUniformBuffers; ++ub) - HandleUB(GetUniformBuffer(ub)); + { + const auto& UB = GetUniformBuffer(ub); + if (CheckResourceType(UB.Name)) + HandleUB(UB); + } for (Uint32 s=0; s < m_NumSamplers; ++s) - HandleSampler(GetSampler(s)); + { + const auto& Sam = GetSampler(s); + if (CheckResourceType(Sam.Name)) + HandleSampler(Sam); + } for (Uint32 img=0; img < m_NumImages; ++img) - HandleImg(GetImage(img)); + { + const auto& Img = GetImage(img); + if (CheckResourceType(Img.Name)) + HandleImg(Img); + } for (Uint32 sb=0; sb < m_NumStorageBlocks; ++sb) - HandleSB(GetStorageBlock(sb)); + { + const auto& SB = GetStorageBlock(sb); + if (CheckResourceType(SB.Name)) + HandleSB(SB); + } } - private: - void AllocateResources(IObject& Owner, - std::vector& UniformBlocks, - std::vector& Samplers, - std::vector& Images, - std::vector& StorageBlocks, - bool InitializeResourceCache); template& UniformBlocks, + std::vector& Samplers, + std::vector& Images, + std::vector& StorageBlocks); + + // There could be more than one stage if using non-separable programs SHADER_TYPE m_ShaderStages = SHADER_TYPE_UNKNOWN; // Memory layout: // - // | Uniform buffers | Samplers | Images | Storage Blocks | Resource Cache | String Pool Data | + // | Uniform buffers | Samplers | Images | Storage Blocks | String Pool Data | // - UniformBufferInfo* m_UniformBuffers = nullptr; - SamplerInfo* m_Samplers = nullptr; - ImageInfo* m_Images = nullptr; - StorageBlockInfo* m_StorageBlocks = nullptr; - RefCntAutoPtr* m_ResourceCache = nullptr; + UniformBufferInfo* m_UniformBuffers = nullptr; + SamplerInfo* m_Samplers = nullptr; + ImageInfo* m_Images = nullptr; + StorageBlockInfo* m_StorageBlocks = nullptr; StringPool m_StringPool; diff --git a/Graphics/GraphicsEngineOpenGL/include/PipelineStateGLImpl.h b/Graphics/GraphicsEngineOpenGL/include/PipelineStateGLImpl.h index 67980f15..baa96a90 100644 --- a/Graphics/GraphicsEngineOpenGL/include/PipelineStateGLImpl.h +++ b/Graphics/GraphicsEngineOpenGL/include/PipelineStateGLImpl.h @@ -27,10 +27,12 @@ #include "PipelineStateGL.h" #include "PipelineStateBase.h" #include "RenderDevice.h" -#include "GLProgram.h" #include "GLObjectWrapper.h" #include "GLContext.h" #include "RenderDeviceGLImpl.h" +#include "GLProgramResources.h" +#include "GLPipelineResourceLayout.h" +#include "GLProgramResourceCache.h" namespace Diligent { @@ -64,22 +66,33 @@ public: virtual bool IsCompatibleWith(const IPipelineState* pPSO)const override final; - GLProgram& GetGLProgram(){return m_GLProgram;} - GLObjectWrappers::GLPipelineObj& GetGLProgramPipeline(GLContext::NativeGLContextType Context); + void CommitProgram(GLContextState& State); + + void InitializeSRBResourceCache(GLProgramResourceCache& ResourceCache)const; - GLProgramResources& GetStaticResources(Uint32 s) - { - return m_StaticResources[s]; - } + const GLPipelineResourceLayout& GetStaticResourceLayout()const {return m_StaticResourceLayout;} private: - void LinkGLProgram(bool bIsProgramPipelineSupported); + GLObjectWrappers::GLPipelineObj& GetGLProgramPipeline(GLContext::NativeGLContextType Context); + void InitStaticSamplersInResourceCache(const GLPipelineResourceLayout& ResourceLayout, GLProgramResourceCache& Cache)const; + + std::vector m_GLPrograms; - GLProgram m_GLProgram; ThreadingTools::LockFlag m_ProgPipelineLockFlag; std::vector< std::pair > m_GLProgPipelines; - std::vector m_StaticResources; - Int8 m_ResourceLayoutIndex[6] = {-1, -1, -1, -1, -1, -1}; + + GLPipelineResourceLayout m_ResourceLayout; + GLPipelineResourceLayout m_StaticResourceLayout; + GLProgramResourceCache m_StaticResourceCache; + + std::vector m_ProgramResources; + + Uint32 m_TotalUniformBufferBindings = 0; + Uint32 m_TotalSamplerBindings = 0; + Uint32 m_TotalImageBindings = 0; + Uint32 m_TotalStorageBufferBindings = 0; + + std::vector> m_StaticSamplers; }; } diff --git a/Graphics/GraphicsEngineOpenGL/include/ShaderGLImpl.h b/Graphics/GraphicsEngineOpenGL/include/ShaderGLImpl.h index 32e2cd47..53a01a85 100644 --- a/Graphics/GraphicsEngineOpenGL/include/ShaderGLImpl.h +++ b/Graphics/GraphicsEngineOpenGL/include/ShaderGLImpl.h @@ -28,8 +28,8 @@ #include "ShaderBase.h" #include "RenderDevice.h" #include "GLObjectWrapper.h" -#include "GLProgram.h" #include "RenderDeviceGLImpl.h" +#include "GLProgramResources.h" namespace Diligent { @@ -70,7 +70,10 @@ class ShaderGLImpl final : public ShaderBase public: using TShaderBase = ShaderBase; - ShaderGLImpl( IReferenceCounters *pRefCounters, RenderDeviceGLImpl *pDeviceGL, const ShaderCreateInfo &ShaderCreateInfo, bool bIsDeviceInternal = false ); + ShaderGLImpl(IReferenceCounters* pRefCounters, + RenderDeviceGLImpl* pDeviceGL, + const ShaderCreateInfo& ShaderCreateInfo, + bool bIsDeviceInternal = false); ~ShaderGLImpl(); virtual void QueryInterface(const INTERFACE_ID& IID, IObject** ppInterface)override final; @@ -78,15 +81,11 @@ public: virtual Uint32 GetResourceCount()const override final; virtual ShaderResourceDesc GetResource(Uint32 Index)const override final; - GLProgram& GetGlProgram(){return m_GlProgObj;} + static GLObjectWrappers::GLProgramObj LinkProgram(IShader** ppShaders, Uint32 NumShaders, bool IsSeparableProgram); private: - - friend class PipelineStateGLImpl; - friend class DeviceContextGLImpl; - - GLProgram m_GlProgObj; // Used if program pipeline supported - GLObjectWrappers::GLShaderObj m_GLShaderObj; // Used if program pipelines are not supported + GLObjectWrappers::GLShaderObj m_GLShaderObj; + GLProgramResources m_Resources; }; } diff --git a/Graphics/GraphicsEngineOpenGL/include/ShaderResourceBindingGLImpl.h b/Graphics/GraphicsEngineOpenGL/include/ShaderResourceBindingGLImpl.h index 56503cfb..962e3bc6 100644 --- a/Graphics/GraphicsEngineOpenGL/include/ShaderResourceBindingGLImpl.h +++ b/Graphics/GraphicsEngineOpenGL/include/ShaderResourceBindingGLImpl.h @@ -31,6 +31,8 @@ #include "ShaderResourceBindingBase.h" #include "GLProgramResources.h" #include "ShaderBase.h" +#include "GLProgramResourceCache.h" +#include "GLPipelineResourceLayout.h" namespace Diligent { @@ -44,7 +46,10 @@ class ShaderResourceBindingGLImpl final : public ShaderResourceBindingBase; - ShaderResourceBindingGLImpl(IReferenceCounters* pRefCounters, PipelineStateGLImpl* pPSO); + ShaderResourceBindingGLImpl(IReferenceCounters* pRefCounters, + PipelineStateGLImpl* pPSO, + GLProgramResources* ProgramResources, + Uint32 NumPrograms); ~ShaderResourceBindingGLImpl(); virtual void QueryInterface( const INTERFACE_ID& IID, IObject** ppInterface )override final; @@ -59,13 +64,12 @@ public: virtual void InitializeStaticResources(const IPipelineState* pPipelineState)override final; - GLProgramResources& GetResources(Uint32 Ind, PipelineStateGLImpl* pdbgPSO); + const GLProgramResourceCache& GetResourceCache(PipelineStateGLImpl* pdbgPSO); private: - bool IsUsingSeparatePrograms()const; - - Int8 m_ResourceIndex[6] = {-1, -1, -1, -1, -1, -1}; - std::vector m_Resources; + GLPipelineResourceLayout m_ResourceLayout; + GLProgramResourceCache m_ResourceCache; + bool m_bIsStaticResourcesBound = false; }; } diff --git a/Graphics/GraphicsEngineOpenGL/include/pch.h b/Graphics/GraphicsEngineOpenGL/include/pch.h index 0ef48b8d..260ae5ae 100644 --- a/Graphics/GraphicsEngineOpenGL/include/pch.h +++ b/Graphics/GraphicsEngineOpenGL/include/pch.h @@ -131,3 +131,9 @@ if( err != GL_NO_ERROR ) \ LogError(__FUNCTION__, __FILE__, __LINE__, __VA_ARGS__, "\nGL Error Code: ", err); \ } + +#ifdef DEVELOPMENT +# define DEV_CHECK_GL_ERROR CHECK_GL_ERROR +#else +# define DEV_CHECK_GL_ERROR(...) do{}while(false) +#endif diff --git a/Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp b/Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp index c04d54ea..dfb2acb3 100644 --- a/Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp @@ -353,336 +353,190 @@ namespace Diligent void DeviceContextGLImpl::BindProgramResources(Uint32& NewMemoryBarriers, IShaderResourceBinding* pResBinding) { - auto* pRenderDeviceGL = m_pDevice.RawPtr(); if (!m_pPipelineState) { - LOG_ERROR("No pipeline state is bound"); + LOG_ERROR_MESSAGE("No pipeline state is bound"); return; } + + if (pResBinding == nullptr) + return; + auto* pShaderResBindingGL = ValidatedCast(pResBinding); + const auto& ResourceCache = pShaderResBindingGL->GetResourceCache(m_pPipelineState); - const auto& DeviceCaps = pRenderDeviceGL->GetDeviceCaps(); - auto& Prog = m_pPipelineState->GetGLProgram(); - auto ProgramPipelineSupported = DeviceCaps.bSeparableProgramSupported; + VERIFY_EXPR(m_BoundWritableTextures.empty()); + VERIFY_EXPR(m_BoundWritableBuffers.empty()); - // WARNING: glUseProgram() overrides glBindProgramPipeline(). That is, if you have a program in use and - // a program pipeline bound, all rendering will use the program that is in use, not the pipeline programs!!! - // So make sure that glUseProgram(0) has been called if pipeline is in use - m_ContextState.SetProgram(Prog); - if (ProgramPipelineSupported) + for (Uint32 ub = 0; ub < ResourceCache.GetUBCount(); ++ub) { - VERIFY(Prog == 0, "Program must be null when program pipeline is used"); - auto& Pipeline = m_pPipelineState->GetGLProgramPipeline( m_ContextState.GetCurrentGLContext() ); - VERIFY(Pipeline != 0, "Program pipeline must not be null"); - m_ContextState.SetPipeline( Pipeline ); - } - else - { - VERIFY(Prog != 0, "Program must not be null"); + const auto& UB = ResourceCache.GetUB(ub); + if (auto* pBufferGL = UB.pBuffer.RawPtr()) + { + pBufferGL->BufferMemoryBarrier( + GL_UNIFORM_BARRIER_BIT,// Shader uniforms sourced from buffer objects after the barrier + // will reflect data written by shaders prior to the barrier + m_ContextState); + + glBindBufferBase(GL_UNIFORM_BUFFER, ub, pBufferGL->m_GlBuffer); + DEV_CHECK_GL_ERROR("Failed to bind uniform buffer to slot ", ub); + //glBindBufferRange(GL_UNIFORM_BUFFER, it->Index, pBufferGL->m_GlBuffer, 0, pBufferGL->GetDesc().uiSizeInBytes); + } } - Uint32 NumPrograms = ProgramPipelineSupported ? m_pPipelineState->GetNumShaders() : 1; - GLuint UnifromBufferBindSlot = 0; - GLuint StorageBufferBindSlot = 0; - GLuint TextureBindSlot = 0; - GLuint ImageBindSlot = 0; - m_BoundWritableTextures.clear(); - m_BoundWritableBuffers.clear(); - for (Uint32 ProgNum = 0; ProgNum < NumPrograms; ++ProgNum) + for (Uint32 s = 0; s < ResourceCache.GetSamplerCount(); ++s) { - auto* pShaderGL = m_pPipelineState->GetShader(ProgNum); - auto& GLProgramObj = ProgramPipelineSupported ? pShaderGL->m_GlProgObj : Prog; - - GLProgramResources* pSRBResources = pShaderResBindingGL ? &pShaderResBindingGL->GetResources(ProgNum, m_pPipelineState) : nullptr; -#ifdef VERIFY_RESOURCE_BINDINGS - //GLProgramObj.dbgVerifyBindingCompleteness(pDynamicResources, m_pPipelineState); -#endif - - for (int BindSRBResources = 0; BindSRBResources < (pShaderResBindingGL ? 2 : 1); ++BindSRBResources) + const auto& Sam = ResourceCache.GetSampler(s); + // We must check 'pTexture' first as 'pBuffer' is in union with 'pSampler' + if (Sam.pTexture != nullptr) { - GLProgramResources& ProgResources = BindSRBResources ? *pSRBResources : m_pPipelineState->GetStaticResources(ProgNum); - -#ifdef VERIFY_RESOURCE_BINDINGS - ProgResources.dbgVerifyResourceBindings(); -#endif - - GLuint GLProgID = GLProgramObj; - for (Uint32 ub = 0; ub < ProgResources.GetNumUniformBuffers(); ++ub) + auto* pTexViewGL = Sam.pView.RawPtr(); + auto* pTextureGL = ValidatedCast(Sam.pTexture); + VERIFY_EXPR(pTextureGL == pTexViewGL->GetTexture()); + m_ContextState.BindTexture(s, pTexViewGL->GetBindTarget(), pTexViewGL->GetHandle()); + + pTextureGL->TextureMemoryBarrier( + GL_TEXTURE_FETCH_BARRIER_BIT, // Texture fetches from shaders, including fetches from buffer object + // memory via buffer textures, after the barrier will reflect data + // written by shaders prior to the barrier + m_ContextState); + + if (Sam.pSampler) { - auto& UB = ProgResources.GetUniformBuffer(ub); - for(Uint32 ArrInd = 0; ArrInd < UB.ArraySize; ++ArrInd) - { - auto& Resource = UB.pResources[ArrInd]; - if (Resource) - { - auto* pBufferGL = Resource.RawPtr(); - pBufferGL->BufferMemoryBarrier( - GL_UNIFORM_BARRIER_BIT,// Shader uniforms sourced from buffer objects after the barrier - // will reflect data written by shaders prior to the barrier - m_ContextState); - - glBindBufferBase(GL_UNIFORM_BUFFER, UnifromBufferBindSlot, pBufferGL->m_GlBuffer); - CHECK_GL_ERROR("Failed to bind uniform buffer"); - //glBindBufferRange(GL_UNIFORM_BUFFER, it->Index, pBufferGL->m_GlBuffer, 0, pBufferGL->GetDesc().uiSizeInBytes); - - glUniformBlockBinding(GLProgID, UB.UBIndex + ArrInd, UnifromBufferBindSlot); - CHECK_GL_ERROR("glUniformBlockBinding() failed"); - - ++UnifromBufferBindSlot; - } - else - { -#define LOG_MISSING_BINDING(VarType, Res, ArrInd)\ - do{ \ - if(Res.ArraySize > 1) \ - LOG_ERROR_MESSAGE( "No ", VarType, " is bound to '", Res.Name, '[', ArrInd, "]' variable in shader '", pShaderGL->GetDesc().Name, "'" );\ - else \ - LOG_ERROR_MESSAGE( "No ", VarType, " is bound to '", Res.Name, "' variable in shader '", pShaderGL->GetDesc().Name, "'" );\ - }while(false) - - LOG_MISSING_BINDING("uniform buffer", UB, ArrInd); - } - } + m_ContextState.BindSampler(s, Sam.pSampler->GetHandle()); } - - for (Uint32 sam = 0; sam < ProgResources.GetNumSamplers(); ++sam) + else { - auto& Sam = ProgResources.GetSampler(sam); - for (Uint32 ArrInd = 0; ArrInd < Sam.ArraySize; ++ArrInd) - { - auto& Resource = Sam.pResources[ArrInd]; - if (Resource) - { - if (Sam.SamplerType == GL_SAMPLER_BUFFER || - Sam.SamplerType == GL_INT_SAMPLER_BUFFER || - Sam.SamplerType == GL_UNSIGNED_INT_SAMPLER_BUFFER) - { - auto* pBufViewGL = Resource.RawPtr(); - auto* pBuffer = pBufViewGL->GetBuffer(); - - m_ContextState.BindTexture(TextureBindSlot, GL_TEXTURE_BUFFER, pBufViewGL->GetTexBufferHandle()); - m_ContextState.BindSampler(TextureBindSlot, GLObjectWrappers::GLSamplerObj(false)); // Use default texture sampling parameters - - ValidatedCast(pBuffer)->BufferMemoryBarrier( - GL_TEXTURE_FETCH_BARRIER_BIT, // Texture fetches from shaders, including fetches from buffer object - // memory via buffer textures, after the barrier will reflect data - // written by shaders prior to the barrier - m_ContextState); - } - else - { - auto* pTexViewGL = Resource.RawPtr(); - m_ContextState.BindTexture(TextureBindSlot, pTexViewGL->GetBindTarget(), pTexViewGL->GetHandle()); - - auto* pTexture = pTexViewGL->GetTexture(); - ValidatedCast(pTexture)->TextureMemoryBarrier( - GL_TEXTURE_FETCH_BARRIER_BIT, // Texture fetches from shaders, including fetches from buffer object - // memory via buffer textures, after the barrier will reflect data - // written by shaders prior to the barrier - m_ContextState); - - SamplerGLImpl* pSamplerGL = nullptr; - if (Sam.pStaticSampler) - { - pSamplerGL = Sam.pStaticSampler; - } - else - { - auto pSampler = pTexViewGL->GetSampler(); - pSamplerGL = ValidatedCast(pSampler); - } - - if (pSamplerGL) - { - m_ContextState.BindSampler(TextureBindSlot, pSamplerGL->GetHandle()); - } - else - { - m_ContextState.BindSampler(TextureBindSlot, GLObjectWrappers::GLSamplerObj(false)); - } - } - - // Texture is now bound to texture slot TextureIndex. - // We now need to set the program uniform to use that slot - if (ProgramPipelineSupported) - { - // glProgramUniform1i does not require program to be bound to the pipeline - glProgramUniform1i(GLProgramObj, Sam.Location + ArrInd, TextureBindSlot); - } - else - { - // glUniform1i requires program to be bound to the pipeline - glUniform1i(Sam.Location + ArrInd, TextureBindSlot); - } - CHECK_GL_ERROR("Failed to set binding point for sampler uniform '", Sam.Name, '\''); - - ++TextureBindSlot; - } - else - { - LOG_MISSING_BINDING("texture sampler", Sam, ArrInd); - } - } + m_ContextState.BindSampler(s, GLObjectWrappers::GLSamplerObj(false)); } + } + else if (Sam.pBuffer != nullptr) + { + auto* pBufViewGL = Sam.pView.RawPtr(); + auto* pBufferGL = ValidatedCast(Sam.pBuffer); + VERIFY_EXPR(pBufferGL == pBufViewGL->GetBuffer()); + + m_ContextState.BindTexture(s, GL_TEXTURE_BUFFER, pBufViewGL->GetTexBufferHandle()); + m_ContextState.BindSampler(s, GLObjectWrappers::GLSamplerObj(false)); // Use default texture sampling parameters + + pBufferGL->BufferMemoryBarrier( + GL_TEXTURE_FETCH_BARRIER_BIT, // Texture fetches from shaders, including fetches from buffer object + // memory via buffer textures, after the barrier will reflect data + // written by shaders prior to the barrier + m_ContextState); + } + } #if GL_ARB_shader_image_load_store - for (Uint32 img = 0; img < ProgResources.GetNumImages(); ++img) + for (Uint32 img = 0; img < ResourceCache.GetImageCount(); ++img) + { + const auto& Img = ResourceCache.GetImage(img); + // We must check 'pTexture' first as 'pBuffer' is in union with 'pSampler' + if (Img.pTexture != nullptr) + { + auto* pTexViewGL = Img.pView.RawPtr(); + auto* pTextureGL = ValidatedCast(Img.pTexture); + VERIFY_EXPR(pTextureGL == pTexViewGL->GetTexture()); + + const auto& ViewDesc = pTexViewGL->GetDesc(); + VERIFY( ViewDesc.ViewType == TEXTURE_VIEW_UNORDERED_ACCESS, "Unexpected buffer view type" ); + + if (ViewDesc.AccessFlags & UAV_ACCESS_FLAG_WRITE) { - auto& Img = ProgResources.GetImage(img); - for (Uint32 ArrInd = 0; ArrInd < Img.ArraySize; ++ArrInd) - { - auto& Resource = Img.pResources[ArrInd]; - if (Resource) - { - if (Img.ImageType == GL_IMAGE_BUFFER || - Img.ImageType == GL_INT_IMAGE_BUFFER || - Img.ImageType == GL_UNSIGNED_INT_IMAGE_BUFFER) - { - auto* pBuffViewGL = Resource.RawPtr(); - const auto& ViewDesc = pBuffViewGL->GetDesc(); - VERIFY( ViewDesc.ViewType == BUFFER_VIEW_UNORDERED_ACCESS, "Unexpected buffer view type" ); - - auto* pBufferGL = pBuffViewGL->GetBuffer(); - pBufferGL->BufferMemoryBarrier( - GL_SHADER_IMAGE_ACCESS_BARRIER_BIT,// Memory accesses using shader image load, store, and atomic built-in - // functions issued after the barrier will reflect data written by shaders - // prior to the barrier. Additionally, image stores and atomics issued after - // the barrier will not execute until all memory accesses (e.g., loads, - // stores, texture fetches, vertex fetches) initiated prior to the barrier - // complete. - m_ContextState); - - m_BoundWritableBuffers.push_back(pBufferGL); - - auto GlFormat = TypeToGLTexFormat(ViewDesc.Format.ValueType, ViewDesc.Format.NumComponents, ViewDesc.Format.IsNormalized); - m_ContextState.BindImage(ImageBindSlot, pBuffViewGL, GL_READ_WRITE, GlFormat); - } - else - { - auto* pTexViewGL = Resource.RawPtr(); - const auto& ViewDesc = pTexViewGL->GetDesc(); - VERIFY( ViewDesc.ViewType == TEXTURE_VIEW_UNORDERED_ACCESS, "Unexpected buffer view type" ); - - if (ViewDesc.AccessFlags & UAV_ACCESS_FLAG_WRITE) - { - auto* pTexGL = pTexViewGL->GetTexture(); - pTexGL->TextureMemoryBarrier( - GL_SHADER_IMAGE_ACCESS_BARRIER_BIT,// Memory accesses using shader image load, store, and atomic built-in - // functions issued after the barrier will reflect data written by shaders - // prior to the barrier. Additionally, image stores and atomics issued after - // the barrier will not execute until all memory accesses (e.g., loads, - // stores, texture fetches, vertex fetches) initiated prior to the barrier - // complete. - m_ContextState); - // We cannot set pending memory barriers here, because - // if some texture is bound twice, the logic will fail - m_BoundWritableTextures.push_back( pTexGL ); - } - - #ifdef _DEBUG - // Check that the texure being bound has immutable storage - { - m_ContextState.BindTexture(-1, pTexViewGL->GetBindTarget(), pTexViewGL->GetHandle()); - GLint IsImmutable = 0; - glGetTexParameteriv( pTexViewGL->GetBindTarget(), GL_TEXTURE_IMMUTABLE_FORMAT, &IsImmutable ); - CHECK_GL_ERROR( "glGetTexParameteriv() failed" ); - VERIFY( IsImmutable, "Only immutable textures can be bound to pipeline using glBindImageTexture()" ); - m_ContextState.BindTexture( -1, pTexViewGL->GetBindTarget(), GLObjectWrappers::GLTextureObj(false) ); - } - #endif - auto GlTexFormat = TexFormatToGLInternalTexFormat( ViewDesc.Format ); - // Note that if a format qulifier is specified in the shader, the format - // must match it - - GLboolean Layered = ViewDesc.NumArraySlices > 1 && ViewDesc.FirstArraySlice == 0; - // If "layered" is TRUE, the entire Mip level is bound. Layer parameter is ignored in this - // case. If "layered" is FALSE, only the single layer identified by "layer" will - // be bound. When "layered" is FALSE, the single bound layer is treated as a 2D texture. - GLint Layer = ViewDesc.FirstArraySlice; - - auto GLAccess = AccessFlags2GLAccess(ViewDesc.AccessFlags); - // WARNING: Texture being bound to the image unit must be complete - // That means that if an integer texture is being bound, its - // GL_TEXTURE_MIN_FILTER and GL_TEXTURE_MAG_FILTER must be NEAREST, - // otherwise it will be incomplete - m_ContextState.BindImage(ImageBindSlot, pTexViewGL, ViewDesc.MostDetailedMip, Layered, Layer, GLAccess, GlTexFormat); - // Do not use binding points from reflection as they may not be initialized - } - - // Image is now bound to binding slot ImageIndex. - // We now need to set the program uniform to use that slot - if (ProgramPipelineSupported) - { - // glProgramUniform1i does not require program to be bound to the pipeline - glProgramUniform1i(GLProgramObj, Img.Location + ArrInd, ImageBindSlot); - } - else - { - // glUniform1i requires program to be bound to the pipeline - glUniform1i(Img.Location + ArrInd, ImageBindSlot); - } - CHECK_GL_ERROR("Failed to set binding point for image uniform '", Img.Name, '\''); - - ++ImageBindSlot; - } - else - { - LOG_MISSING_BINDING("image", Img, ArrInd); - } - } + pTextureGL->TextureMemoryBarrier( + GL_SHADER_IMAGE_ACCESS_BARRIER_BIT,// Memory accesses using shader image load, store, and atomic built-in + // functions issued after the barrier will reflect data written by shaders + // prior to the barrier. Additionally, image stores and atomics issued after + // the barrier will not execute until all memory accesses (e.g., loads, + // stores, texture fetches, vertex fetches) initiated prior to the barrier + // complete. + m_ContextState); + // We cannot set pending memory barriers here, because + // if some texture is bound twice, the logic will fail + m_BoundWritableTextures.push_back( pTextureGL ); } -#endif -#if GL_ARB_shader_storage_buffer_object - for (Uint32 sb=0; sb < ProgResources.GetNumStorageBlocks(); ++sb) +#ifdef _DEBUG + // Check that the texure being bound has immutable storage { - auto& SB = ProgResources.GetStorageBlock(sb); - for (Uint32 ArrInd = 0; ArrInd < SB.ArraySize; ++ArrInd) - { - auto& Resource = SB.pResources[ArrInd]; - if (Resource) - { - auto* pBufferViewGL = Resource.RawPtr(); - const auto& ViewDesc = pBufferViewGL->GetDesc(); - VERIFY( ViewDesc.ViewType == BUFFER_VIEW_UNORDERED_ACCESS || ViewDesc.ViewType == BUFFER_VIEW_SHADER_RESOURCE, "Unexpected buffer view type" ); - - auto* pBufferGL = pBufferViewGL->GetBuffer(); - pBufferGL->BufferMemoryBarrier( - GL_SHADER_STORAGE_BARRIER_BIT,// Accesses to shader storage blocks after the barrier - // will reflect writes prior to the barrier - m_ContextState); - - glBindBufferRange(GL_SHADER_STORAGE_BUFFER, StorageBufferBindSlot, pBufferGL->m_GlBuffer, ViewDesc.ByteOffset, ViewDesc.ByteWidth); - CHECK_GL_ERROR("Failed to bind shader storage buffer"); - - if (glShaderStorageBlockBinding) - { - glShaderStorageBlockBinding(GLProgID, SB.SBIndex + ArrInd, StorageBufferBindSlot); - CHECK_GL_ERROR("glShaderStorageBlockBinding() failed"); - } - else - { - LOG_WARNING_MESSAGE_ONCE("glShaderStorageBlockBinding is not available on this device and " - "the engine is unable to automatically assign shader storage block bindindg points. " - "To make shader storage blocks work properly, all binding points must be explicitly assigned " - "in the shader starting from 0 and proceeding in the storage block declaration order. " - "Note that the binding points are properly assigned by HLSL->GLSL converter."); - } - - ++StorageBufferBindSlot; - - if (ViewDesc.ViewType == BUFFER_VIEW_UNORDERED_ACCESS) - m_BoundWritableBuffers.push_back(pBufferGL); - } - else - { - LOG_MISSING_BINDING("shader storage block", SB, ArrInd); - } - } + m_ContextState.BindTexture(-1, pTexViewGL->GetBindTarget(), pTexViewGL->GetHandle()); + GLint IsImmutable = 0; + glGetTexParameteriv( pTexViewGL->GetBindTarget(), GL_TEXTURE_IMMUTABLE_FORMAT, &IsImmutable ); + CHECK_GL_ERROR( "glGetTexParameteriv() failed" ); + VERIFY( IsImmutable, "Only immutable textures can be bound to pipeline using glBindImageTexture()" ); + m_ContextState.BindTexture( -1, pTexViewGL->GetBindTarget(), GLObjectWrappers::GLTextureObj(false) ); } #endif + auto GlTexFormat = TexFormatToGLInternalTexFormat( ViewDesc.Format ); + // Note that if a format qulifier is specified in the shader, the format + // must match it + + GLboolean Layered = ViewDesc.NumArraySlices > 1 && ViewDesc.FirstArraySlice == 0; + // If "layered" is TRUE, the entire Mip level is bound. Layer parameter is ignored in this + // case. If "layered" is FALSE, only the single layer identified by "layer" will + // be bound. When "layered" is FALSE, the single bound layer is treated as a 2D texture. + GLint Layer = ViewDesc.FirstArraySlice; + + auto GLAccess = AccessFlags2GLAccess(ViewDesc.AccessFlags); + // WARNING: Texture being bound to the image unit must be complete + // That means that if an integer texture is being bound, its + // GL_TEXTURE_MIN_FILTER and GL_TEXTURE_MAG_FILTER must be NEAREST, + // otherwise it will be incomplete + m_ContextState.BindImage(img, pTexViewGL, ViewDesc.MostDetailedMip, Layered, Layer, GLAccess, GlTexFormat); + // Do not use binding points from reflection as they may not be initialized + } + else if (Img.pBuffer != nullptr) + { + auto* pBuffViewGL = Img.pView.RawPtr(); + auto* pBufferGL = ValidatedCast(Img.pBuffer); + VERIFY_EXPR(pBufferGL == pBuffViewGL->GetBuffer()); + + const auto& ViewDesc = pBuffViewGL->GetDesc(); + VERIFY( ViewDesc.ViewType == BUFFER_VIEW_UNORDERED_ACCESS, "Unexpected buffer view type" ); + + pBufferGL->BufferMemoryBarrier( + GL_SHADER_IMAGE_ACCESS_BARRIER_BIT,// Memory accesses using shader image load, store, and atomic built-in + // functions issued after the barrier will reflect data written by shaders + // prior to the barrier. Additionally, image stores and atomics issued after + // the barrier will not execute until all memory accesses (e.g., loads, + // stores, texture fetches, vertex fetches) initiated prior to the barrier + // complete. + m_ContextState); + + m_BoundWritableBuffers.push_back(pBufferGL); + + auto GlFormat = TypeToGLTexFormat(ViewDesc.Format.ValueType, ViewDesc.Format.NumComponents, ViewDesc.Format.IsNormalized); + m_ContextState.BindImage(img, pBuffViewGL, GL_READ_WRITE, GlFormat); } } +#endif + + +#if GL_ARB_shader_storage_buffer_object + for (Uint32 ssbo = 0; ssbo < ResourceCache.GetSSBOCount(); ++ssbo) + { + const auto& SSBO = ResourceCache.GetSSBO(ssbo); + if (SSBO.pBufferView) + { + auto* pBufferViewGL = SSBO.pBufferView.RawPtr(); + const auto& ViewDesc = pBufferViewGL->GetDesc(); + VERIFY( ViewDesc.ViewType == BUFFER_VIEW_UNORDERED_ACCESS || ViewDesc.ViewType == BUFFER_VIEW_SHADER_RESOURCE, "Unexpected buffer view type" ); + + auto* pBufferGL = pBufferViewGL->GetBuffer(); + pBufferGL->BufferMemoryBarrier( + GL_SHADER_STORAGE_BARRIER_BIT,// Accesses to shader storage blocks after the barrier + // will reflect writes prior to the barrier + m_ContextState); + + glBindBufferRange(GL_SHADER_STORAGE_BUFFER, ssbo, pBufferGL->m_GlBuffer, ViewDesc.ByteOffset, ViewDesc.ByteWidth); + DEV_CHECK_GL_ERROR("Failed to bind shader storage buffer"); + + if (ViewDesc.ViewType == BUFFER_VIEW_UNORDERED_ACCESS) + m_BoundWritableBuffers.push_back(pBufferGL); + } + } +#endif + #if GL_ARB_shader_image_load_store // Go through the list of textures bound as AUVs and set the required memory barriers @@ -714,6 +568,7 @@ namespace Diligent // Set new required barriers for the time when texture is used next time pWritableTex->SetPendingMemoryBarriers(TextureMemBarriers); } + m_BoundWritableTextures.clear(); for (auto* pWritableBuff : m_BoundWritableBuffers) { @@ -732,6 +587,7 @@ namespace Diligent // Set new required barriers for the time when buffer is used next time pWritableBuff->SetPendingMemoryBarriers( BufferMemoryBarriers ); } + m_BoundWritableBuffers.clear(); #endif } @@ -745,6 +601,8 @@ namespace Diligent DvpVerifyRenderTargets(); #endif + m_pPipelineState->CommitProgram(m_ContextState); + auto* pRenderDeviceGL = m_pDevice.RawPtr(); auto CurrNativeGLContext = pRenderDeviceGL->m_GLContext.GetCurrentNativeGLContext(); const auto& PipelineDesc = m_pPipelineState->GetDesc().GraphicsPipeline; @@ -829,7 +687,7 @@ namespace Diligent //} DrawElementsIndirectCommand; glDrawElementsIndirect( GlTopology, IndexType, reinterpret_cast( static_cast(drawAttribs.IndirectDrawArgsOffset) ) ); // Note that on GLES 3.1, baseInstance is present but reserved and must be zero - CHECK_GL_ERROR( "glDrawElementsIndirect() failed" ); + DEV_CHECK_GL_ERROR( "glDrawElementsIndirect() failed" ); } else { @@ -841,12 +699,12 @@ namespace Diligent //} DrawArraysIndirectCommand; glDrawArraysIndirect( GlTopology, reinterpret_cast( static_cast(drawAttribs.IndirectDrawArgsOffset) ) ); // Note that on GLES 3.1, baseInstance is present but reserved and must be zero - CHECK_GL_ERROR( "glDrawArraysIndirect() failed" ); + DEV_CHECK_GL_ERROR( "glDrawArraysIndirect() failed" ); } glBindBuffer( GL_DRAW_INDIRECT_BUFFER, 0 ); #else - UNSUPPORTED("Indirect rendering is not supported"); + LOG_ERROR_MESSAGE("Indirect rendering is not supported"); #endif } else @@ -890,7 +748,7 @@ namespace Diligent else glDrawArrays(GlTopology, drawAttribs.StartVertexLocation, drawAttribs.NumVertices); } - CHECK_GL_ERROR( "OpenGL draw command failed" ); + DEV_CHECK_GL_ERROR( "OpenGL draw command failed" ); } // IMPORTANT: new pending memory barriers in the context must be set @@ -909,6 +767,8 @@ namespace Diligent #endif #if GL_ARB_compute_shader + m_pPipelineState->CommitProgram(m_ContextState); + if (DispatchAttrs.pIndirectDispatchAttribs) { CHECK_DYNAMIC_TYPE( BufferGLImpl, DispatchAttrs.pIndirectDispatchAttribs ); diff --git a/Graphics/GraphicsEngineOpenGL/src/GLContextState.cpp b/Graphics/GraphicsEngineOpenGL/src/GLContextState.cpp index 4d0b2bad..e7886e8d 100644 --- a/Graphics/GraphicsEngineOpenGL/src/GLContextState.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/GLContextState.cpp @@ -80,7 +80,7 @@ namespace Diligent glBindVertexArray( 0 ); glBindFramebuffer( GL_DRAW_FRAMEBUFFER, 0 ); glBindFramebuffer( GL_READ_FRAMEBUFFER, 0 ); - CHECK_GL_ERROR( "Failed to reset GL context state" ); + DEV_CHECK_GL_ERROR( "Failed to reset GL context state" ); m_GLProgId = -1; m_GLPipelineId = -1; @@ -132,7 +132,7 @@ namespace Diligent if( UpdateBoundObject( m_GLProgId, GLProgram, GLProgHandle ) ) { glUseProgram( GLProgHandle ); - CHECK_GL_ERROR( "Failed to set GL program" ); + DEV_CHECK_GL_ERROR( "Failed to set GL program" ); } } @@ -142,7 +142,7 @@ namespace Diligent if( UpdateBoundObject( m_GLPipelineId, GLPipeline, GLPipelineHandle ) ) { glBindProgramPipeline( GLPipelineHandle ); - CHECK_GL_ERROR( "Failed to bind program pipeline" ); + DEV_CHECK_GL_ERROR( "Failed to bind program pipeline" ); } } @@ -152,7 +152,7 @@ namespace Diligent if( UpdateBoundObject( m_VAOId, VAO, VAOHandle ) ) { glBindVertexArray( VAOHandle ); - CHECK_GL_ERROR( "Failed to set VAO" ); + DEV_CHECK_GL_ERROR( "Failed to set VAO" ); } } @@ -169,9 +169,9 @@ namespace Diligent // level is NOT level_base, the texture MUST BE MIPMAP COMPLETE // If image is part of a cubemap texture, the texture must also be mipmap cube complete. glBindFramebuffer( GL_DRAW_FRAMEBUFFER, FBOHandle ); - CHECK_GL_ERROR( "Failed to bind FBO as draw framebuffer" ); + DEV_CHECK_GL_ERROR( "Failed to bind FBO as draw framebuffer" ); glBindFramebuffer( GL_READ_FRAMEBUFFER, FBOHandle ); - CHECK_GL_ERROR( "Failed to bind FBO as read framebuffer" ); + DEV_CHECK_GL_ERROR( "Failed to bind FBO as read framebuffer" ); } } @@ -195,7 +195,7 @@ namespace Diligent if( m_iActiveTexture != Index ) { glActiveTexture( GL_TEXTURE0 + Index ); - CHECK_GL_ERROR( "Failed to activate texture slot ", Index ); + DEV_CHECK_GL_ERROR( "Failed to activate texture slot ", Index ); m_iActiveTexture = Index; } } @@ -215,7 +215,7 @@ namespace Diligent if( UpdateBoundObjectsArr( m_BoundTextures, Index, Tex, GLTexHandle ) ) { glBindTexture( BindTarget, GLTexHandle ); - CHECK_GL_ERROR( "Failed to bind texture to slot ", Index ); + DEV_CHECK_GL_ERROR( "Failed to bind texture to slot ", Index ); } } @@ -225,7 +225,7 @@ namespace Diligent if( UpdateBoundObjectsArr( m_BoundSamplers, Index, GLSampler, GLSamplerHandle ) ) { glBindSampler( Index, GLSamplerHandle ); - CHECK_GL_ERROR( "Failed to bind sampler to slot ", Index ); + DEV_CHECK_GL_ERROR( "Failed to bind sampler to slot ", Index ); } } @@ -253,7 +253,7 @@ namespace Diligent m_BoundImages[Index] = NewImageInfo; GLint GLTexHandle = pTexView->GetHandle(); glBindImageTexture( Index, GLTexHandle, MipLevel, IsLayered, Layer, Access, Format ); - CHECK_GL_ERROR( "glBindImageTexture() failed" ); + DEV_CHECK_GL_ERROR( "glBindImageTexture() failed" ); } #else UNSUPPORTED("GL_ARB_shader_image_load_store is not supported"); @@ -278,7 +278,7 @@ namespace Diligent m_BoundImages[Index] = NewImageInfo; GLint GLBuffHandle = pBuffView->GetTexBufferHandle(); glBindImageTexture( Index, GLBuffHandle, 0, GL_FALSE, 0, Access, Format ); - CHECK_GL_ERROR( "glBindImageTexture() failed" ); + DEV_CHECK_GL_ERROR( "glBindImageTexture() failed" ); } #else UNSUPPORTED("GL_ARB_shader_image_load_store is not supported"); @@ -322,7 +322,7 @@ namespace Diligent if( RequiredBarriers ) { glMemoryBarrier( RequiredBarriers ); - CHECK_GL_ERROR( "glMemoryBarrier() failed" ); + DEV_CHECK_GL_ERROR( "glMemoryBarrier() failed" ); m_PendingMemoryBarriers &= ~RequiredBarriers; } @@ -346,12 +346,12 @@ namespace Diligent if( bEnable ) { glEnable( GL_DEPTH_TEST ); - CHECK_GL_ERROR( "Failed to enable detph test" ); + DEV_CHECK_GL_ERROR( "Failed to enable detph test" ); } else { glDisable( GL_DEPTH_TEST ); - CHECK_GL_ERROR( "Failed to disable detph test" ); + DEV_CHECK_GL_ERROR( "Failed to disable detph test" ); } m_DSState.m_DepthEnableState = bEnable; } @@ -363,7 +363,7 @@ namespace Diligent { // If mask is non-zero, the depth buffer is enabled for writing; otherwise, it is disabled. glDepthMask( bEnable ? 1 : 0 ); - CHECK_GL_ERROR( "Failed to enale/disable depth writes" ); + DEV_CHECK_GL_ERROR( "Failed to enale/disable depth writes" ); m_DSState.m_DepthWritesEnableState = bEnable; } } @@ -374,7 +374,7 @@ namespace Diligent { auto GlCmpFunc = CompareFuncToGLCompareFunc( CmpFunc ); glDepthFunc( GlCmpFunc ); - CHECK_GL_ERROR( "Failed to set GL comparison function" ); + DEV_CHECK_GL_ERROR( "Failed to set GL comparison function" ); m_DSState.m_DepthCmpFunc = CmpFunc; } } @@ -386,12 +386,12 @@ namespace Diligent if( bEnable ) { glEnable( GL_STENCIL_TEST ); - CHECK_GL_ERROR( "Failed to enable stencil test" ); + DEV_CHECK_GL_ERROR( "Failed to enable stencil test" ); } else { glDisable( GL_STENCIL_TEST ); - CHECK_GL_ERROR( "Failed to disable stencil test" ); + DEV_CHECK_GL_ERROR( "Failed to disable stencil test" ); } m_DSState.m_StencilTestEnableState = bEnable; } @@ -411,7 +411,7 @@ namespace Diligent auto& FaceStencilOp = m_DSState.m_StencilOpState[Face == GL_FRONT ? 0 : 1]; auto GlStencilFunc = CompareFuncToGLCompareFunc( FaceStencilOp.Func ); glStencilFuncSeparate( Face, GlStencilFunc, Ref, FaceStencilOp.Mask ); - CHECK_GL_ERROR( "Failed to set stencil function" ); + DEV_CHECK_GL_ERROR( "Failed to set stencil function" ); } void GLContextState::SetStencilFunc( GLenum Face, COMPARISON_FUNCTION Func, Int32 Ref, Uint32 Mask ) @@ -441,7 +441,7 @@ namespace Diligent auto dppass = StencilOp2GlStencilOp( StencilPassOp ); glStencilOpSeparate( Face, glsfail, dpfail, dppass ); - CHECK_GL_ERROR( "Failed to set stencil operation" ); + DEV_CHECK_GL_ERROR( "Failed to set stencil operation" ); FaceStencilOp.StencilFailOp = StencilFailOp; FaceStencilOp.StencilDepthFailOp = StencilDepthFailOp; @@ -459,7 +459,7 @@ namespace Diligent { auto PolygonMode = FillMode == FILL_MODE_WIREFRAME ? GL_LINE : GL_FILL; glPolygonMode( GL_FRONT_AND_BACK, PolygonMode ); - CHECK_GL_ERROR( "Failed to set polygon mode" ); + DEV_CHECK_GL_ERROR( "Failed to set polygon mode" ); } else { @@ -484,16 +484,16 @@ namespace Diligent if( CullMode == CULL_MODE_NONE ) { glDisable( GL_CULL_FACE ); - CHECK_GL_ERROR( "Failed to disable face culling" ); + DEV_CHECK_GL_ERROR( "Failed to disable face culling" ); } else { VERIFY( CullMode == CULL_MODE_FRONT || CullMode == CULL_MODE_BACK, "Unexpected cull mode" ); glEnable( GL_CULL_FACE ); - CHECK_GL_ERROR( "Failed to enable face culling" ); + DEV_CHECK_GL_ERROR( "Failed to enable face culling" ); auto CullFace = CullMode == CULL_MODE_BACK ? GL_BACK : GL_FRONT; glCullFace( CullFace ); - CHECK_GL_ERROR( "Failed to set cull face" ); + DEV_CHECK_GL_ERROR( "Failed to set cull face" ); } m_RSState.CullMode = CullMode; @@ -506,7 +506,7 @@ namespace Diligent { auto FrontFace = FrontCounterClockwise ? GL_CCW : GL_CW; glFrontFace( FrontFace ); - CHECK_GL_ERROR( "Failed to set front face" ); + DEV_CHECK_GL_ERROR( "Failed to set front face" ); m_RSState.FrontCounterClockwise = FrontCounterClockwise; } } @@ -519,16 +519,16 @@ namespace Diligent if( fDepthBias != 0 || fSlopeScaledDepthBias != 0 ) { glEnable( GL_POLYGON_OFFSET_FILL ); - CHECK_GL_ERROR( "Failed to enable polygon offset fill" ); + DEV_CHECK_GL_ERROR( "Failed to enable polygon offset fill" ); } else { glDisable( GL_POLYGON_OFFSET_FILL ); - CHECK_GL_ERROR( "Failed to disable polygon offset fill" ); + DEV_CHECK_GL_ERROR( "Failed to disable polygon offset fill" ); } glPolygonOffset( fSlopeScaledDepthBias, fDepthBias ); - CHECK_GL_ERROR( "Failed to set polygon offset" ); + DEV_CHECK_GL_ERROR( "Failed to set polygon offset" ); m_RSState.fDepthBias = fDepthBias; m_RSState.fSlopeScaledDepthBias = fSlopeScaledDepthBias; @@ -550,7 +550,7 @@ namespace Diligent if( GL_DEPTH_CLAMP ) { glEnable( GL_DEPTH_CLAMP ); - CHECK_GL_ERROR( "Failed to enable depth clamp" ); + DEV_CHECK_GL_ERROR( "Failed to enable depth clamp" ); } } else @@ -558,7 +558,7 @@ namespace Diligent if( GL_DEPTH_CLAMP ) { glDisable( GL_DEPTH_CLAMP ); - CHECK_GL_ERROR( "Failed to disable depth clamp" ); + DEV_CHECK_GL_ERROR( "Failed to disable depth clamp" ); } else { @@ -577,12 +577,12 @@ namespace Diligent if( bEnableScissorTest ) { glEnable( GL_SCISSOR_TEST ); - CHECK_GL_ERROR( "Failed to enable scissor test" ); + DEV_CHECK_GL_ERROR( "Failed to enable scissor test" ); } else { glDisable( GL_SCISSOR_TEST ); - CHECK_GL_ERROR( "Failed to disable scissor clamp" ); + DEV_CHECK_GL_ERROR( "Failed to disable scissor clamp" ); } m_RSState.ScissorTestEnable = bEnableScissorTest; @@ -592,7 +592,7 @@ namespace Diligent void GLContextState::SetBlendFactors(const float *BlendFactors) { glBlendColor( BlendFactors[0], BlendFactors[1], BlendFactors[2], BlendFactors[3] ); - CHECK_GL_ERROR( "Failed to set blend color" ); + DEV_CHECK_GL_ERROR( "Failed to set blend color" ); } void GLContextState::SetBlendState( const BlendStateDesc &BSDsc, Uint32 SampleMask ) @@ -629,17 +629,17 @@ namespace Diligent { // Sets the blend enable flag for ALL color buffers. glEnable( GL_BLEND ); - CHECK_GL_ERROR( "Failed to enable alpha blending" ); + DEV_CHECK_GL_ERROR( "Failed to enable alpha blending" ); if( BSDsc.AlphaToCoverageEnable ) { glEnable( GL_SAMPLE_ALPHA_TO_COVERAGE ); - CHECK_GL_ERROR( "Failed to enable alpha to coverage" ); + DEV_CHECK_GL_ERROR( "Failed to enable alpha to coverage" ); } else { glDisable( GL_SAMPLE_ALPHA_TO_COVERAGE ); - CHECK_GL_ERROR( "Failed to disable alpha to coverage" ); + DEV_CHECK_GL_ERROR( "Failed to disable alpha to coverage" ); } if( BSDsc.IndependentBlendEnable ) @@ -658,23 +658,23 @@ namespace Diligent if( RT.BlendEnable ) { glEnablei( GL_BLEND, i ); - CHECK_GL_ERROR( "Failed to enable alpha blending" ); + DEV_CHECK_GL_ERROR( "Failed to enable alpha blending" ); auto srcFactorRGB = BlendFactor2GLBlend( RT.SrcBlend ); auto dstFactorRGB = BlendFactor2GLBlend( RT.DestBlend ); auto srcFactorAlpha = BlendFactor2GLBlend( RT.SrcBlendAlpha ); auto dstFactorAlpha = BlendFactor2GLBlend( RT.DestBlendAlpha ); glBlendFuncSeparatei( i, srcFactorRGB, dstFactorRGB, srcFactorAlpha, dstFactorAlpha ); - CHECK_GL_ERROR( "Failed to set separate blending factors" ); + DEV_CHECK_GL_ERROR( "Failed to set separate blending factors" ); auto modeRGB = BlendOperation2GLBlendOp( RT.BlendOp ); auto modeAlpha = BlendOperation2GLBlendOp( RT.BlendOpAlpha ); glBlendEquationSeparatei( i, modeRGB, modeAlpha ); - CHECK_GL_ERROR( "Failed to set separate blending equations" ); + DEV_CHECK_GL_ERROR( "Failed to set separate blending equations" ); } else { glDisablei( GL_BLEND, i ); - CHECK_GL_ERROR( "Failed to disable alpha blending" ); + DEV_CHECK_GL_ERROR( "Failed to disable alpha blending" ); } } } @@ -686,19 +686,19 @@ namespace Diligent auto srcFactorAlpha = BlendFactor2GLBlend( RT0.SrcBlendAlpha ); auto dstFactorAlpha = BlendFactor2GLBlend( RT0.DestBlendAlpha ); glBlendFuncSeparate( srcFactorRGB, dstFactorRGB, srcFactorAlpha, dstFactorAlpha ); - CHECK_GL_ERROR( "Failed to set blending factors" ); + DEV_CHECK_GL_ERROR( "Failed to set blending factors" ); auto modeRGB = BlendOperation2GLBlendOp( RT0.BlendOp ); auto modeAlpha = BlendOperation2GLBlendOp( RT0.BlendOpAlpha ); glBlendEquationSeparate( modeRGB, modeAlpha ); - CHECK_GL_ERROR( "Failed to set blending equations" ); + DEV_CHECK_GL_ERROR( "Failed to set blending equations" ); } } else { // Sets the blend disable flag for ALL color buffers. glDisable( GL_BLEND ); - CHECK_GL_ERROR( "Failed to disable alpha blending" ); + DEV_CHECK_GL_ERROR( "Failed to disable alpha blending" ); } } @@ -724,7 +724,7 @@ namespace Diligent (WriteMask & COLOR_MASK_GREEN) ? GL_TRUE : GL_FALSE, (WriteMask & COLOR_MASK_BLUE) ? GL_TRUE : GL_FALSE, (WriteMask & COLOR_MASK_ALPHA) ? GL_TRUE : GL_FALSE ); - CHECK_GL_ERROR( "Failed to set GL color mask" ); + DEV_CHECK_GL_ERROR( "Failed to set GL color mask" ); m_ColorWriteMasks[RTIndex] = WriteMask; } @@ -735,7 +735,7 @@ namespace Diligent (WriteMask & COLOR_MASK_GREEN) ? GL_TRUE : GL_FALSE, (WriteMask & COLOR_MASK_BLUE) ? GL_TRUE : GL_FALSE, (WriteMask & COLOR_MASK_ALPHA) ? GL_TRUE : GL_FALSE ); - CHECK_GL_ERROR( "Failed to set GL color mask" ); + DEV_CHECK_GL_ERROR( "Failed to set GL color mask" ); for( int rt = 0; rt < _countof( m_ColorWriteMasks ); ++rt ) m_ColorWriteMasks[rt] = WriteMask; @@ -759,7 +759,7 @@ namespace Diligent { m_NumPatchVertices = NumVertices; glPatchParameteri(GL_PATCH_VERTICES, static_cast(NumVertices)); - CHECK_GL_ERROR( "Failed to set the number of patch vertices" ); + DEV_CHECK_GL_ERROR( "Failed to set the number of patch vertices" ); } #endif } diff --git a/Graphics/GraphicsEngineOpenGL/src/GLPipelineResourceLayout.cpp b/Graphics/GraphicsEngineOpenGL/src/GLPipelineResourceLayout.cpp new file mode 100644 index 00000000..ba1574b8 --- /dev/null +++ b/Graphics/GraphicsEngineOpenGL/src/GLPipelineResourceLayout.cpp @@ -0,0 +1,880 @@ +/* Copyright 2019 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 + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS. + * + * 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 "pch.h" +#include +#include "GLPipelineResourceLayout.h" +#include "Align.h" +#include "PlatformMisc.h" +#include "ShaderBase.h" + +namespace Diligent +{ + +size_t GLPipelineResourceLayout::GetRequiredMemorySize(GLProgramResources* ProgramResources, + Uint32 NumPrograms, + const PipelineResourceLayoutDesc& ResourceLayout, + const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, + Uint32 NumAllowedTypes) +{ + GLProgramResources::ResourceCounters Counters; + for(Uint32 prog = 0; prog < NumPrograms; ++prog) + { + ProgramResources[prog].CountResources(ResourceLayout, AllowedVarTypes, NumAllowedTypes, Counters); + } + + size_t RequiredSize = Counters.NumUBs * sizeof(UniformBuffBindInfo) + + Counters.NumSamplers * sizeof(SamplerBindInfo) + + Counters.NumImages * sizeof(ImageBindInfo) + + Counters.NumStorageBlocks * sizeof(StorageBufferBindInfo) + + NumPrograms * sizeof(GLProgramResources::ResourceCounters); + return RequiredSize; +} + +void GLPipelineResourceLayout::Initialize(GLProgramResources* ProgramResources, + Uint32 NumPrograms, + const PipelineResourceLayoutDesc& ResourceLayout, + const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, + Uint32 NumAllowedTypes, + GLProgramResourceCache* pResourceCache) +{ + GLProgramResources::ResourceCounters Counters; + + for(Uint32 prog = 0; prog < NumPrograms; ++prog) + { + ProgramResources[prog].CountResources(ResourceLayout, AllowedVarTypes, NumAllowedTypes, Counters); + } + + // Initialize offsets + size_t CurrentOffset = 0; + auto AdvanceOffset = [&CurrentOffset](size_t NumBytes) + { + constexpr size_t MaxOffset = std::numeric_limits::max(); + VERIFY(CurrentOffset <= MaxOffset, "Current offser (", CurrentOffset, ") exceeds max allowed value (", MaxOffset, ")"); + auto Offset = static_cast(CurrentOffset); + CurrentOffset += NumBytes; + return Offset; + }; + + auto UBOffset = AdvanceOffset(Counters.NumUBs * sizeof(UniformBuffBindInfo) ); (void)UBOffset; // To suppress warning + m_SamplerOffset = AdvanceOffset(Counters.NumSamplers * sizeof(SamplerBindInfo) ); + m_ImageOffset = AdvanceOffset(Counters.NumImages * sizeof(ImageBindInfo) ); + m_StorageBufferOffset = AdvanceOffset(Counters.NumStorageBlocks * sizeof(StorageBufferBindInfo)); + m_VariableEndOffset = AdvanceOffset(0); + m_NumPrograms = static_cast(NumPrograms); + VERIFY_EXPR(m_NumPrograms == NumPrograms); + auto TotalMemorySize = m_VariableEndOffset + m_NumPrograms * sizeof(GLProgramResources::ResourceCounters); + VERIFY_EXPR(TotalMemorySize == GetRequiredMemorySize(ProgramResources, NumPrograms, ResourceLayout, AllowedVarTypes, NumAllowedTypes)); + + auto& ResLayoutDataAllocator = GetRawAllocator(); + if (TotalMemorySize) + { + auto* pRawMem = ALLOCATE_RAW(ResLayoutDataAllocator, "Raw memory buffer for shader resource layout resources", TotalMemorySize); + m_ResourceBuffer = std::unique_ptr >(pRawMem, ResLayoutDataAllocator); + } + + VERIFY_EXPR(Counters.NumUBs == GetNumUBs() ); + VERIFY_EXPR(Counters.NumSamplers == GetNumSamplers() ); + VERIFY_EXPR(Counters.NumImages == GetNumImages() ); + VERIFY_EXPR(Counters.NumStorageBlocks == GetNumStorageBuffers()); + + // Current resource index for every resource type + GLProgramResources::ResourceCounters VarCounters = {}; + + Uint32 UniformBindingSlots = 0; + Uint32 SamplerBindingSlots = 0; + Uint32 ImageBindingSlots = 0; + Uint32 SSBOBindingSlots = 0; + +#ifdef _DEBUG + const Uint32 DbgAllowedTypeBits = GetAllowedTypeBits(AllowedVarTypes, NumAllowedTypes); +#endif + for(Uint32 prog = 0; prog < NumPrograms; ++prog) + { + const auto& Resources = ProgramResources[prog]; + auto ShaderStages = Resources.GetShaderStages(); + Resources.ProcessConstResources( + [&](const GLProgramResources::UniformBufferInfo& UB) + { + auto VarType = GetShaderVariableType(ShaderStages, UB.Name, ResourceLayout); + VERIFY_EXPR(IsAllowedType(VarType, DbgAllowedTypeBits)); + auto* pUBVar = new (&GetResource(VarCounters.NumUBs++)) UniformBuffBindInfo + { + UB, + *this, + VarType + }; + UniformBindingSlots = std::max(UniformBindingSlots, pUBVar->m_Attribs.Binding + pUBVar->m_Attribs.ArraySize); + }, + [&](const GLProgramResources::SamplerInfo& Sam) + { + auto VarType = GetShaderVariableType(ShaderStages, Sam.Name, ResourceLayout); + VERIFY_EXPR(IsAllowedType(VarType, DbgAllowedTypeBits)); + Int32 StaticSamplerIdx = -1; + if (Sam.ResourceType == SHADER_RESOURCE_TYPE_TEXTURE_SRV) + { + StaticSamplerIdx = FindStaticSampler(ResourceLayout.StaticSamplers, ResourceLayout.NumStaticSamplers, ShaderStages, + Sam.Name, nullptr); + } + auto* pSamVar = new (&GetResource(VarCounters.NumSamplers++)) SamplerBindInfo + { + Sam, + *this, + VarType, + StaticSamplerIdx + }; + SamplerBindingSlots = std::max(SamplerBindingSlots, pSamVar->m_Attribs.Binding + pSamVar->m_Attribs.ArraySize); + }, + [&](const GLProgramResources::ImageInfo& Img) + { + auto VarType = GetShaderVariableType(ShaderStages, Img.Name, ResourceLayout); + VERIFY_EXPR(IsAllowedType(VarType, DbgAllowedTypeBits)); + auto* pImgVar = new (&GetResource(VarCounters.NumImages++)) ImageBindInfo + { + Img, + *this, + VarType + }; + ImageBindingSlots = std::max(ImageBindingSlots, pImgVar->m_Attribs.Binding + pImgVar->m_Attribs.ArraySize); + }, + [&](const GLProgramResources::StorageBlockInfo& SB) + { + auto VarType = GetShaderVariableType(ShaderStages, SB.Name, ResourceLayout); + VERIFY_EXPR(IsAllowedType(VarType, DbgAllowedTypeBits)); + auto* pSSBOVar = new (&GetResource(VarCounters.NumStorageBlocks++)) StorageBufferBindInfo + { + SB, + *this, + VarType + }; + SSBOBindingSlots = std::max(SSBOBindingSlots, pSSBOVar->m_Attribs.Binding + pSSBOVar->m_Attribs.ArraySize); + }, + &ResourceLayout, + AllowedVarTypes, + NumAllowedTypes + ); + + new (&GetProgramVarEndOffsets(prog)) GLProgramResources::ResourceCounters{VarCounters}; + while (ShaderStages != SHADER_TYPE_UNKNOWN) + { + auto Stage = static_cast(Uint32{ShaderStages} & ~(Uint32{ShaderStages}-1)); + auto ShaderInd = GetShaderTypeIndex(Stage); + m_ProgramIndex[ShaderInd] = static_cast(prog); + ShaderStages = static_cast(Uint32{ShaderStages} & ~Stage); + } + } + + VERIFY(VarCounters.NumUBs == GetNumUBs(), "Not all UBs are initialized which will cause a crash when dtor is called"); + VERIFY(VarCounters.NumSamplers == GetNumSamplers(), "Not all Samplers are initialized which will cause a crash when dtor is called"); + VERIFY(VarCounters.NumImages == GetNumImages(), "Not all Images are initialized which will cause a crash when dtor is called"); + VERIFY(VarCounters.NumStorageBlocks == GetNumStorageBuffers(), "Not all SSBOs are initialized which will cause a crash when dtor is called"); + + m_pResourceCache = pResourceCache; + if (m_pResourceCache != nullptr && !m_pResourceCache->IsInitialized()) + { + // NOTE that here we are using max bind points required to cache only the shader variables of allowed types! + m_pResourceCache->Initialize(UniformBindingSlots, SamplerBindingSlots, ImageBindingSlots, SSBOBindingSlots, GetRawAllocator()); + } +} + +GLPipelineResourceLayout::~GLPipelineResourceLayout() +{ + HandleResources( + [&](UniformBuffBindInfo& ub) + { + ub.~UniformBuffBindInfo(); + }, + + [&](SamplerBindInfo& sam) + { + sam.~SamplerBindInfo(); + }, + + [&](ImageBindInfo& img) + { + img.~ImageBindInfo(); + }, + + [&](StorageBufferBindInfo& ssbo) + { + ssbo.~StorageBufferBindInfo(); + } + ); +} + +#define LOG_RESOURCE_BINDING_ERROR(ResType, pResource, Attribs, ArrayInd, ...)\ +do{ \ + const auto* ResName = pResource->GetDesc().Name; \ + if (Attribs.ArraySize > 1) \ + LOG_ERROR_MESSAGE( "Failed to bind ", ResType, " '", ResName, "' to variable '", Attribs.Name,\ + "[", ArrayInd, "]'. ", __VA_ARGS__ ); \ + else \ + LOG_ERROR_MESSAGE( "Failed to bind ", ResType, " '", ResName, "' to variable '", Attribs.Name,\ + "'. ", __VA_ARGS__ ); \ +}while(false) + + +void GLPipelineResourceLayout::UniformBuffBindInfo::BindResource(IDeviceObject* pBuffer, + Uint32 ArrayIndex) +{ + DEV_CHECK_ERR(ArrayIndex < m_Attribs.ArraySize, "Array index (", ArrayIndex, ") is out of range for variable '", m_Attribs.Name, "'. Max allowed index: ", m_Attribs.ArraySize-1); + VERIFY(m_ParentResLayout.m_pResourceCache != nullptr, "Resource cache is not initialized"); + auto& ResourceCache = *m_ParentResLayout.m_pResourceCache; + + VERIFY_EXPR(m_Attribs.ResourceType == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER); + + // We cannot use ValidatedCast<> here as the resource retrieved from the + // resource mapping can be of wrong type + RefCntAutoPtr pBuffGLImpl(pBuffer, IID_BufferGL); +#ifdef DEVELOPMENT + if (pBuffer && !pBuffGLImpl) + LOG_RESOURCE_BINDING_ERROR("buffer", pBuffer, m_Attribs, ArrayIndex, "Incorrect resource type: buffer is expected."); + + if (pBuffGLImpl && (pBuffGLImpl->GetDesc().BindFlags & BIND_UNIFORM_BUFFER) == 0) + { + LOG_RESOURCE_BINDING_ERROR("buffer", pBuffer, m_Attribs, ArrayIndex, "Buffer was not created with BIND_UNIFORM_BUFFER flag."); + pBuffGLImpl.Release(); + } + + if (GetType() != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) + { + const auto& CachedUB = const_cast(ResourceCache).GetUB(m_Attribs.Binding + ArrayIndex); + if (CachedUB.pBuffer != nullptr && CachedUB.pBuffer != pBuffGLImpl) + { + auto VarTypeStr = GetShaderVariableTypeLiteralName(GetType()); + LOG_ERROR_MESSAGE( "Non-null uniform buffer is already bound to ", VarTypeStr, " shader variable '", m_Attribs.GetPrintName(ArrayIndex), "'. Attempting to bind another resource or null is an error and may cause unpredicted behavior. Use another shader resource binding instance or label the variable as dynamic." ); + } + } +#endif + + ResourceCache.SetUniformBuffer(m_Attribs.Binding + ArrayIndex, std::move(pBuffGLImpl) ); +} + + + +#ifdef DEVELOPMENT +template ///< Type of the expected view enum +bool dbgVerifyViewType( const char* ViewTypeName, + TResourceViewType pViewGL, + const GLProgramResources::GLResourceAttribs& Attribs, + Uint32 ArrayIndex, + TViewTypeEnum dbgExpectedViewType) +{ + const auto& ViewDesc = pViewGL->GetDesc(); + auto ViewType = ViewDesc.ViewType; + if (ViewType == dbgExpectedViewType) + { + return true; + } + else + { + const auto* ExpectedViewTypeName = GetViewTypeLiteralName( dbgExpectedViewType ); + const auto* ActualViewTypeName = GetViewTypeLiteralName( ViewType ); + LOG_RESOURCE_BINDING_ERROR(ViewTypeName, pViewGL, Attribs, ArrayIndex, "Incorrect view type: ", + ExpectedViewTypeName, " is expected, ", ActualViewTypeName, " is provided."); + return false; + } +} +#endif + +void GLPipelineResourceLayout::SamplerBindInfo::BindResource(IDeviceObject* pView, + Uint32 ArrayIndex) +{ + DEV_CHECK_ERR(ArrayIndex < m_Attribs.ArraySize, "Array index (", ArrayIndex, ") is out of range for variable '", m_Attribs.Name, "'. Max allowed index: ", m_Attribs.ArraySize-1); + VERIFY(m_ParentResLayout.m_pResourceCache != nullptr, "Resource cache is not initialized"); + auto& ResourceCache = *m_ParentResLayout.m_pResourceCache; + + VERIFY_EXPR(m_Attribs.ResourceType == SHADER_RESOURCE_TYPE_BUFFER_SRV || + m_Attribs.ResourceType == SHADER_RESOURCE_TYPE_TEXTURE_SRV); + + if (m_Attribs.ResourceType == SHADER_RESOURCE_TYPE_TEXTURE_SRV) + { + // We cannot use ValidatedCast<> here as the resource retrieved from the + // resource mapping can be of wrong type + RefCntAutoPtr pViewGL(pView, IID_TextureViewGL); +#ifdef DEVELOPMENT + if (pView && !pViewGL) + LOG_RESOURCE_BINDING_ERROR("resource", pView, m_Attribs, ArrayIndex, "Incorect resource type: texture view is expected."); + if (pViewGL && !dbgVerifyViewType("texture view", pViewGL.RawPtr(), m_Attribs, ArrayIndex, TEXTURE_VIEW_SHADER_RESOURCE)) + pViewGL.Release(); + + auto& CachedTexSampler = const_cast(ResourceCache).GetSampler(m_Attribs.Binding + ArrayIndex); + if (GetType() != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) + { + if (CachedTexSampler.pView != nullptr && CachedTexSampler.pView != pViewGL) + { + auto VarTypeStr = GetShaderVariableTypeLiteralName(GetType()); + LOG_ERROR_MESSAGE( "Non-null texture sampler is already bound to ", VarTypeStr, " shader variable '", m_Attribs.GetPrintName(ArrayIndex), "'. Attempting to bind another resource or null is an error and may cause unpredicted behavior. Use another shader resource binding instance or label the variable as dynamic." ); + } + } + if (m_StaticSamplerIdx >= 0) + { + VERIFY(CachedTexSampler.pSampler != nullptr, "Static samplers must be initialized by PipelineStateGLImpl::InitializeSRBResourceCache!"); + } +#endif + ResourceCache.SetTexSampler(m_Attribs.Binding + ArrayIndex, std::move(pViewGL), m_StaticSamplerIdx < 0); + } + else if (m_Attribs.ResourceType == SHADER_RESOURCE_TYPE_BUFFER_SRV) + { + // We cannot use ValidatedCast<> here as the resource retrieved from the + // resource mapping can be of wrong type + RefCntAutoPtr pViewGL(pView, IID_BufferViewGL); +#ifdef DEVELOPMENT + if (pView && !pViewGL) + LOG_RESOURCE_BINDING_ERROR("resource", pView, m_Attribs, ArrayIndex, "Incorect resource type: buffer view is expected."); + if (pViewGL && !dbgVerifyViewType("buffer view", pViewGL.RawPtr(), m_Attribs, ArrayIndex, BUFFER_VIEW_SHADER_RESOURCE)) + pViewGL.Release(); + + if (GetType() != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) + { + auto& CachedBuffSampler = const_cast(ResourceCache).GetSampler(m_Attribs.Binding + ArrayIndex); + if (CachedBuffSampler.pView != nullptr && CachedBuffSampler.pView != pViewGL) + { + auto VarTypeStr = GetShaderVariableTypeLiteralName(GetType()); + LOG_ERROR_MESSAGE( "Non-null buffer sampler is already bound to ", VarTypeStr, " shader variable '", m_Attribs.GetPrintName(ArrayIndex), "'. Attempting to bind another resource or null is an error and may cause unpredicted behavior. Use another shader resource binding instance or label the variable as dynamic." ); + } + } +#endif + ResourceCache.SetBufSampler(m_Attribs.Binding + ArrayIndex, std::move(pViewGL)); + } + else + { + UNEXPECTED("Unexpected resource type ", GetShaderResourceTypeLiteralName(m_Attribs.ResourceType), ". Texture SRV or buffer SRV is expected."); + } +} + + +void GLPipelineResourceLayout::ImageBindInfo::BindResource(IDeviceObject* pView, + Uint32 ArrayIndex) +{ + DEV_CHECK_ERR(ArrayIndex < m_Attribs.ArraySize, "Array index (", ArrayIndex, ") is out of range for variable '", m_Attribs.Name, "'. Max allowed index: ", m_Attribs.ArraySize-1); + VERIFY(m_ParentResLayout.m_pResourceCache != nullptr, "Resource cache is not initialized"); + auto& ResourceCache = *m_ParentResLayout.m_pResourceCache; + + if (m_Attribs.ResourceType == SHADER_RESOURCE_TYPE_TEXTURE_UAV) + { + // We cannot use ValidatedCast<> here as the resource retrieved from the + // resource mapping can be of wrong type + RefCntAutoPtr pViewGL(pView, IID_TextureViewGL); +#ifdef DEVELOPMENT + if (pView && !pViewGL) + LOG_RESOURCE_BINDING_ERROR("resource", pView, m_Attribs, ArrayIndex, "Incorect resource type: texture view is expected."); + if (pViewGL && !dbgVerifyViewType("texture view", pViewGL.RawPtr(), m_Attribs, ArrayIndex, TEXTURE_VIEW_UNORDERED_ACCESS)) + pViewGL.Release(); + + if (GetType() != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) + { + auto& CachedUAV = const_cast(ResourceCache).GetImage(m_Attribs.Binding + ArrayIndex); + if (CachedUAV.pView != nullptr && CachedUAV.pView != pViewGL) + { + auto VarTypeStr = GetShaderVariableTypeLiteralName(GetType()); + LOG_ERROR_MESSAGE("Non-null texture image is already bound to ", VarTypeStr, " shader variable '", m_Attribs.GetPrintName(ArrayIndex), "'. Attempting to bind another resource or null is an error and may cause unpredicted behavior. Use another shader resource binding instance or label the variable as dynamic."); + } + } +#endif + ResourceCache.SetTexImage(m_Attribs.Binding + ArrayIndex, std::move(pViewGL)); + } + else if (m_Attribs.ResourceType == SHADER_RESOURCE_TYPE_BUFFER_UAV) + { + // We cannot use ValidatedCast<> here as the resource retrieved from the + // resource mapping can be of wrong type + RefCntAutoPtr pViewGL(pView, IID_BufferViewGL); +#ifdef DEVELOPMENT + if (pView && !pViewGL) + LOG_RESOURCE_BINDING_ERROR("resource", pView, m_Attribs, ArrayIndex, "Incorect resource type: buffer view is expected."); + if (pViewGL && !dbgVerifyViewType("buffer view", pViewGL.RawPtr(), m_Attribs, ArrayIndex, BUFFER_VIEW_UNORDERED_ACCESS) ) + pViewGL.Release(); + + if (GetType() != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) + { + auto& CachedUAV = const_cast(ResourceCache).GetImage(m_Attribs.Binding + ArrayIndex); + if (CachedUAV.pView != nullptr && CachedUAV.pView != pViewGL) + { + auto VarTypeStr = GetShaderVariableTypeLiteralName(GetType()); + LOG_ERROR_MESSAGE( "Non-null buffer image is already bound to ", VarTypeStr, " shader variable '", m_Attribs.GetPrintName(ArrayIndex), "'. Attempting to bind another resource or null is an error and may cause unpredicted behavior. Use another shader resource binding instance or label the variable as dynamic." ); + } + } +#endif + ResourceCache.SetBufImage(m_Attribs.Binding + ArrayIndex, std::move(pViewGL)); + } + else + { + UNEXPECTED("Unexpected resource type ", GetShaderResourceTypeLiteralName(m_Attribs.ResourceType), ". Texture UAV or buffer UAV is expected."); + } +} + + + +void GLPipelineResourceLayout::StorageBufferBindInfo::BindResource(IDeviceObject* pView, + Uint32 ArrayIndex) +{ + DEV_CHECK_ERR(ArrayIndex < m_Attribs.ArraySize, "Array index (", ArrayIndex, ") is out of range for variable '", m_Attribs.Name, "'. Max allowed index: ", m_Attribs.ArraySize-1); + VERIFY(m_ParentResLayout.m_pResourceCache != nullptr, "Resource cache is not initialized"); + auto& ResourceCache = *m_ParentResLayout.m_pResourceCache; + VERIFY_EXPR(m_Attribs.ResourceType == SHADER_RESOURCE_TYPE_BUFFER_UAV); + + // We cannot use ValidatedCast<> here as the resource retrieved from the + // resource mapping can be of wrong type + RefCntAutoPtr pViewGL(pView, IID_BufferViewGL); +#ifdef DEVELOPMENT + if (pView && !pViewGL) + LOG_RESOURCE_BINDING_ERROR("resource", pView, m_Attribs, ArrayIndex, "Incorect resource type: buffer view is expected."); + if (pViewGL) + { + auto ViewType = pViewGL->GetDesc().ViewType; + // Structured buffers are mapped to SSBOs + if (ViewType != BUFFER_VIEW_UNORDERED_ACCESS && ViewType != BUFFER_VIEW_SHADER_RESOURCE) + { + LOG_RESOURCE_BINDING_ERROR("resource", pView, m_Attribs, ArrayIndex, "Incorect view type: buffer SRV or UAV is expected."); + pViewGL.Release(); + } + } + + if (GetType() != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) + { + auto& CachedSSBO = const_cast(ResourceCache).GetSSBO(m_Attribs.Binding + ArrayIndex); + if (CachedSSBO.pBufferView != nullptr && CachedSSBO.pBufferView != pViewGL) + { + auto VarTypeStr = GetShaderVariableTypeLiteralName(GetType()); + LOG_ERROR_MESSAGE("Non-null storage block is already bound to ", VarTypeStr, " shader variable '", m_Attribs.GetPrintName(ArrayIndex), "'. Attempting to bind another resource or null is an error and may cause unpredicted behavior. Use another shader resource binding instance or label the variable as dynamic."); + } + } +#endif + ResourceCache.SetSSBO(m_Attribs.Binding + ArrayIndex, std::move(pViewGL)); +} + + + +// Helper template class that facilitates binding CBs, SRVs, and UAVs +class BindResourceHelper +{ +public: + BindResourceHelper(IResourceMapping& RM, SHADER_TYPE SS, Uint32 Fl) : + ResourceMapping (RM), + ShaderStage (SS), + Flags (Fl) + { + } + + template + void Bind( ResourceType& Res) + { + if ( (Flags & (1 << Res.GetType())) == 0 ) + return; + + for (Uint16 elem=0; elem < Res.m_Attribs.ArraySize; ++elem) + { + if ((Res.m_Attribs.ShaderStages & ShaderStage) == 0) + continue; + + if ( (Flags & BIND_SHADER_RESOURCES_KEEP_EXISTING) && Res.IsBound(elem) ) + continue; + + const auto* VarName = Res.m_Attribs.Name; + RefCntAutoPtr pRes; + ResourceMapping.GetResource( VarName, &pRes, elem ); + if (pRes) + { + // Call non-virtual function + Res.BindResource(pRes, elem); + } + else + { + if ( (Flags & BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED) && !Res.IsBound(elem) ) + LOG_ERROR_MESSAGE( "Unable to bind resource to shader variable '", VarName, "': resource is not found in the resource mapping" ); + } + } + } + +private: + IResourceMapping& ResourceMapping; + const SHADER_TYPE ShaderStage; + const Uint32 Flags; +}; + + +void GLPipelineResourceLayout::BindResources(SHADER_TYPE ShaderStage, IResourceMapping* pResourceMapping, Uint32 Flags, const GLProgramResourceCache& dbgResourceCache) +{ + VERIFY(&dbgResourceCache == m_pResourceCache, "Resource cache does not match the cache provided at initialization"); + + if (pResourceMapping == nullptr) + { + LOG_ERROR_MESSAGE( "Failed to bind resources: resource mapping is null" ); + return; + } + + if ( (Flags & BIND_SHADER_RESOURCES_UPDATE_ALL) == 0 ) + Flags |= BIND_SHADER_RESOURCES_UPDATE_ALL; + + BindResourceHelper BindResHelper(*pResourceMapping, ShaderStage, Flags); + + HandleResources( + [&](UniformBuffBindInfo& ub) + { + BindResHelper.Bind(ub); + }, + + [&](SamplerBindInfo& sam) + { + BindResHelper.Bind(sam); + }, + + [&](ImageBindInfo& img) + { + BindResHelper.Bind(img); + }, + + [&](StorageBufferBindInfo& ssbo) + { + BindResHelper.Bind(ssbo); + } + ); +} + + +template +IShaderResourceVariable* GLPipelineResourceLayout::GetResourceByName(SHADER_TYPE ShaderStage, const Char* Name) +{ + auto NumResources = GetNumResources(); + for (Uint32 res = 0; res < NumResources; ++res) + { + auto& Resource = GetResource(res); + if ( (Resource.m_Attribs.ShaderStages & ShaderStage) != 0 && strcmp(Resource.m_Attribs.Name, Name) == 0) + return &Resource; + } + + return nullptr; +} + + +IShaderResourceVariable* GLPipelineResourceLayout::GetShaderVariable(SHADER_TYPE ShaderStage, const Char* Name) +{ + if (auto* pUB = GetResourceByName(ShaderStage, Name)) + return pUB; + + if (auto* pSampler = GetResourceByName(ShaderStage, Name)) + return pSampler; + + if (auto* pImage = GetResourceByName(ShaderStage, Name)) + return pImage; + + if (auto* pSSBO = GetResourceByName(ShaderStage, Name)) + return pSSBO; + + return nullptr; +} + +Uint32 GLPipelineResourceLayout::GetNumVariables(SHADER_TYPE ShaderStage)const +{ + VERIFY(IsPowerOfTwo(Uint32{ShaderStage}), "Only one shader stage must be specified"); + auto ShaderInd = GetShaderTypeIndex(ShaderStage); + auto ProgIdx = m_ProgramIndex[ShaderInd]; + + if (ProgIdx < 0) + return 0; + + const auto& VariableEndOffset = GetProgramVarEndOffsets(ProgIdx); + const auto& VariableStartOffset = ProgIdx > 0 ? GetProgramVarEndOffsets(ProgIdx-1) : GLProgramResources::ResourceCounters{}; + + Uint32 NumVars = VariableEndOffset.NumUBs - VariableStartOffset.NumUBs + + VariableEndOffset.NumSamplers - VariableStartOffset.NumSamplers + + VariableEndOffset.NumImages - VariableStartOffset.NumImages + + VariableEndOffset.NumStorageBlocks - VariableStartOffset.NumStorageBlocks; +#ifdef _DEBUG + { + Uint32 DbgNumVars = 0; + auto CountVar = [&](const GLVariableBase& Var) + { + DbgNumVars += ((Var.m_Attribs.ShaderStages & ShaderStage) != 0) ? 1 : 0; + }; + HandleConstResources(CountVar, CountVar, CountVar, CountVar); + VERIFY_EXPR(DbgNumVars == NumVars); + } +#endif + + return NumVars; +} + +class ShaderVariableLocator +{ +public: + ShaderVariableLocator(GLPipelineResourceLayout& _Layout, + Uint32 _Index) : + Layout {_Layout}, + Index {_Index} + { + } + + template + IShaderResourceVariable* TryResource(Uint32 StartVarOffset, Uint32 EndVarOffset) + { + auto NumResources = EndVarOffset - StartVarOffset; + if (Index < NumResources) + return &Layout.GetResource(StartVarOffset + Index); + else + { + Index -= NumResources; + return nullptr; + } + } + +private: + GLPipelineResourceLayout& Layout; + Uint32 Index; +}; + + +IShaderResourceVariable* GLPipelineResourceLayout::GetShaderVariable(SHADER_TYPE ShaderStage, Uint32 Index) +{ + VERIFY(IsPowerOfTwo(Uint32{ShaderStage}), "Only one shader stage must be specified"); + auto ShaderInd = GetShaderTypeIndex(ShaderStage); + auto ProgIdx = m_ProgramIndex[ShaderInd]; + + if (ProgIdx < 0) + return nullptr; + + const auto& VariableEndOffset = GetProgramVarEndOffsets(ProgIdx); + const auto& VariableStartOffset = ProgIdx > 0 ? GetProgramVarEndOffsets(ProgIdx-1) : GLProgramResources::ResourceCounters{}; + + ShaderVariableLocator VarLocator(*this, Index); + + if(auto* pUB = VarLocator.TryResource(VariableStartOffset.NumUBs, VariableEndOffset.NumUBs)) + return pUB; + + if(auto* pSampler = VarLocator.TryResource(VariableStartOffset.NumSamplers, VariableEndOffset.NumSamplers)) + return pSampler; + + if(auto* pImage = VarLocator.TryResource(VariableStartOffset.NumImages, VariableEndOffset.NumImages)) + return pImage; + + if(auto* pSSBO = VarLocator.TryResource(VariableStartOffset.NumStorageBlocks, VariableEndOffset.NumStorageBlocks)) + return pSSBO; + + LOG_ERROR(Index, " is not a valid variable index."); + return nullptr; +} + + + +class ShaderVariableIndexLocator +{ +public: + ShaderVariableIndexLocator(const GLPipelineResourceLayout& _Layout, const GLPipelineResourceLayout::GLVariableBase& Variable) : + Layout (_Layout), + VarOffset(reinterpret_cast(&Variable) - reinterpret_cast(_Layout.m_ResourceBuffer.get())) + {} + + template + bool TryResource(Uint32 NextResourceTypeOffset, Uint32 FirstVarOffset, Uint32 LastVarOffset) + { + if (VarOffset < NextResourceTypeOffset) + { + auto RelativeOffset = VarOffset - Layout.GetResourceOffset(); + DEV_CHECK_ERR( RelativeOffset % sizeof(ResourceType) == 0, "Offset is not multiple of resource type (", sizeof(ResourceType), ")"); + RelativeOffset /= sizeof(ResourceType); + VERIFY(RelativeOffset >= FirstVarOffset && RelativeOffset < LastVarOffset, + "Relative offset is out of bounds which either means the variable does not belong to this SRB or " + "there is a bug in varaible offsets"); + Index += static_cast(RelativeOffset) - FirstVarOffset; + return true; + } + else + { + Index += LastVarOffset - FirstVarOffset; + return false; + } + } + + Uint32 GetIndex() const {return Index;} + +private: + const GLPipelineResourceLayout& Layout; + const size_t VarOffset; + Uint32 Index = 0; +}; + + +Uint32 GLPipelineResourceLayout::GetVariableIndex(const GLVariableBase& Var)const +{ + if (!m_ResourceBuffer) + { + LOG_ERROR("This shader resource layout does not have resources"); + return static_cast(-1); + } + + auto FirstStage = static_cast(Uint32{Var.m_Attribs.ShaderStages} & ~(Uint32{Var.m_Attribs.ShaderStages}-1)); + auto ProgIdx = m_ProgramIndex[GetShaderTypeIndex(FirstStage)]; + VERIFY(ProgIdx >= 0, "This shader stage is not initialized in the resource layout"); + + const auto& VariableEndOffset = GetProgramVarEndOffsets(ProgIdx); + const auto& VariableStartOffset = ProgIdx > 0 ? GetProgramVarEndOffsets(ProgIdx-1) : GLProgramResources::ResourceCounters{}; + + ShaderVariableIndexLocator IdxLocator(*this, Var); + + if (IdxLocator.TryResource(m_SamplerOffset, VariableStartOffset.NumUBs, VariableEndOffset.NumUBs)) + return IdxLocator.GetIndex(); + + if (IdxLocator.TryResource(m_ImageOffset, VariableStartOffset.NumSamplers, VariableEndOffset.NumSamplers)) + return IdxLocator.GetIndex(); + + if (IdxLocator.TryResource(m_StorageBufferOffset, VariableStartOffset.NumImages, VariableEndOffset.NumImages)) + return IdxLocator.GetIndex(); + + if (IdxLocator.TryResource(m_VariableEndOffset, VariableStartOffset.NumStorageBlocks, VariableEndOffset.NumStorageBlocks)) + return IdxLocator.GetIndex(); + + LOG_ERROR("Failed to get variable index. The variable ", &Var, " does not belong to this shader resource layout"); + return static_cast(-1); +} + +void GLPipelineResourceLayout::CopyResources(GLProgramResourceCache& DstCache)const +{ + VERIFY_EXPR(m_pResourceCache != nullptr); + const auto& SrcCache = *m_pResourceCache; + VERIFY( DstCache.GetUBCount() >= SrcCache.GetUBCount(), "Dst cache is not large enough to contain all CBs" ); + VERIFY( DstCache.GetSamplerCount() >= SrcCache.GetSamplerCount(), "Dst cache is not large enough to contain all SRVs" ); + VERIFY( DstCache.GetImageCount() >= SrcCache.GetImageCount(), "Dst cache is not large enough to contain all samplers" ); + VERIFY( DstCache.GetSSBOCount() >= SrcCache.GetSSBOCount(), "Dst cache is not large enough to contain all UAVs" ); + + HandleConstResources( + [&](const UniformBuffBindInfo& UB) + { + for (auto binding = UB.m_Attribs.Binding; binding < UB.m_Attribs.Binding + UB.m_Attribs.ArraySize; ++binding) + { + auto pBufferGL = SrcCache.GetUB(binding).pBuffer; + DstCache.SetUniformBuffer(binding, std::move(pBufferGL)); + } + }, + + [&](const SamplerBindInfo& Sam) + { + for (auto binding = Sam.m_Attribs.Binding; binding < Sam.m_Attribs.Binding + Sam.m_Attribs.ArraySize; ++binding) + { + const auto& SrcSam = SrcCache.GetSampler(binding); + DstCache.CopySampler(binding, SrcSam); + } + }, + + [&](const ImageBindInfo& Img) + { + for (auto binding = Img.m_Attribs.Binding; binding < Img.m_Attribs.Binding + Img.m_Attribs.ArraySize; ++binding) + { + const auto& SrcImg = SrcCache.GetImage(binding); + DstCache.CopyImage(binding, SrcImg); + } + }, + + [&](const StorageBufferBindInfo& SSBO) + { + for (auto binding = SSBO.m_Attribs.Binding; binding < SSBO.m_Attribs.Binding + SSBO.m_Attribs.ArraySize; ++binding) + { + auto pBufferView = SrcCache.GetSSBO(binding).pBufferView; + DstCache.SetSSBO(binding, std::move(pBufferView)); + } + } + ); +} + +#ifdef DEVELOPMENT +bool GLPipelineResourceLayout::dvpVerifyBindings()const +{ +#define LOG_MISSING_BINDING(VarType, BindInfo, BindPt)\ +do{ \ + if (BindInfo.m_Attribs.ArraySize == 1) \ + LOG_ERROR_MESSAGE( "No resource is bound to ", VarType, " variable '", BindInfo.m_Attribs.Name, "'" ); \ + else \ + LOG_ERROR_MESSAGE( "No resource is bound to ", VarType, " variable '", BindInfo.m_Attribs.Name, "[", BindPt - BindInfo.m_Attribs.Binding, "]'");\ +}while(false) + + bool BindingsOK = true; + HandleConstResources( + [&](const UniformBuffBindInfo& ub) + { + for (Uint32 BindPoint = ub.m_Attribs.Binding; BindPoint < Uint32{ub.m_Attribs.Binding} + ub.m_Attribs.ArraySize; ++BindPoint) + { + if (!m_pResourceCache->IsUBBound(BindPoint)) + { + LOG_MISSING_BINDING("constant buffer", ub, BindPoint); + BindingsOK = false; + } + } + }, + + [&](const SamplerBindInfo& sam) + { + for (Uint32 BindPoint = sam.m_Attribs.Binding; BindPoint < Uint32{sam.m_Attribs.Binding} + sam.m_Attribs.ArraySize; ++BindPoint) + { + VERIFY_EXPR(sam.m_Attribs.ResourceType == SHADER_RESOURCE_TYPE_TEXTURE_SRV || + sam.m_Attribs.ResourceType == SHADER_RESOURCE_TYPE_BUFFER_SRV); + if (!m_pResourceCache->IsSamplerBound(BindPoint, sam.m_Attribs.ResourceType == SHADER_RESOURCE_TYPE_TEXTURE_SRV)) + { + LOG_MISSING_BINDING("texture", sam, BindPoint); + BindingsOK = false; + } + else + { + const auto& CachedSampler = const_cast(m_pResourceCache)->GetSampler(BindPoint); + if (sam.m_StaticSamplerIdx >= 0 && CachedSampler.pSampler == nullptr) + { + LOG_ERROR_MESSAGE("Static sampler is not initialized for texture '", sam.m_Attribs.Name, "'"); + BindingsOK = false; + } + } + } + }, + + [&](const ImageBindInfo& img) + { + for (Uint32 BindPoint = img.m_Attribs.Binding; BindPoint < Uint32{img.m_Attribs.Binding} + img.m_Attribs.ArraySize; ++BindPoint) + { + VERIFY_EXPR(img.m_Attribs.ResourceType == SHADER_RESOURCE_TYPE_TEXTURE_UAV || + img.m_Attribs.ResourceType == SHADER_RESOURCE_TYPE_BUFFER_UAV); + if (!m_pResourceCache->IsImageBound(BindPoint, img.m_Attribs.ResourceType == SHADER_RESOURCE_TYPE_TEXTURE_UAV)) + { + LOG_MISSING_BINDING("texture UAV", img, BindPoint); + BindingsOK = false; + } + } + }, + + [&](const StorageBufferBindInfo& ssbo) + { + for (Uint32 BindPoint = ssbo.m_Attribs.Binding; BindPoint < Uint32{ssbo.m_Attribs.Binding} + ssbo.m_Attribs.ArraySize; ++BindPoint) + { + if (!m_pResourceCache->IsSSBOBound(BindPoint)) + { + LOG_MISSING_BINDING("buffer", ssbo, BindPoint); + BindingsOK = false; + } + } + } + ); +#undef LOG_MISSING_BINDING + + return BindingsOK; +} + +#endif + +} diff --git a/Graphics/GraphicsEngineOpenGL/src/GLProgram.cpp b/Graphics/GraphicsEngineOpenGL/src/GLProgram.cpp deleted file mode 100644 index 226f991b..00000000 --- a/Graphics/GraphicsEngineOpenGL/src/GLProgram.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* Copyright 2019 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 - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS. - * - * 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 "pch.h" -#include "GLProgram.h" -#include "ShaderBase.h" -#include "ShaderResourceBindingGLImpl.h" - -namespace Diligent -{ - GLProgram::GLProgram( bool CreateObject ) : - GLObjectWrappers::GLProgramObj( CreateObject ) - {} - - GLProgram::GLProgram( GLProgram&& Program ): - GLObjectWrappers::GLProgramObj(std::move(Program ) ), - m_AllResources (std::move(Program.m_AllResources)) - {} - - void GLProgram::InitResources(RenderDeviceGLImpl* pDeviceGLImpl, - SHADER_TYPE ShaderStage, - IObject& Owner) - { - GLuint GLProgram = static_cast(*this); - m_AllResources.LoadUniforms(Owner, pDeviceGLImpl, ShaderStage, GLProgram); - } -} diff --git a/Graphics/GraphicsEngineOpenGL/src/GLProgramResourceCache.cpp b/Graphics/GraphicsEngineOpenGL/src/GLProgramResourceCache.cpp new file mode 100644 index 00000000..f8b73d8d --- /dev/null +++ b/Graphics/GraphicsEngineOpenGL/src/GLProgramResourceCache.cpp @@ -0,0 +1,112 @@ +/* Copyright 2019 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 + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS. + * + * 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 "pch.h" +#include "GLProgramResourceCache.h" + +namespace Diligent +{ + +size_t GLProgramResourceCache::GetRequriedMemorySize(Uint32 UBCount, Uint32 SamplerCount, Uint32 ImageCount, Uint32 SSBOCount) +{ + auto MemSize = + sizeof(CachedUB) * UBCount + + sizeof(CachedResourceView) * SamplerCount + + sizeof(CachedResourceView) * ImageCount + + sizeof(CachedSSBO) * SSBOCount; + return MemSize; +} + +void GLProgramResourceCache::Initialize(Uint32 UBCount, Uint32 SamplerCount, Uint32 ImageCount, Uint32 SSBOCount, class IMemoryAllocator& MemAllocator) +{ + m_SmplrsOffset = static_cast(m_UBsOffset + sizeof(CachedUB) * UBCount); + m_ImgsOffset = static_cast(m_SmplrsOffset + sizeof(CachedResourceView) * SamplerCount); + m_SSBOsOffset = static_cast(m_ImgsOffset + sizeof(CachedResourceView) * ImageCount); + m_MemoryEndOffset = static_cast(m_SSBOsOffset + sizeof(CachedSSBO) * SSBOCount); + + VERIFY_EXPR(GetUBCount() == static_cast(UBCount)); + VERIFY_EXPR(GetSamplerCount() == static_cast(SamplerCount)); + VERIFY_EXPR(GetImageCount() == static_cast(ImageCount)); + VERIFY_EXPR(GetSSBOCount() == static_cast(SSBOCount)); + + VERIFY_EXPR(m_pResourceData == nullptr); + size_t BufferSize = m_MemoryEndOffset; + + VERIFY_EXPR(BufferSize == GetRequriedMemorySize(UBCount, SamplerCount, ImageCount, SSBOCount)); + +#ifdef _DEBUG + m_pdbgMemoryAllocator = &MemAllocator; +#endif + if( BufferSize > 0 ) + { + m_pResourceData = ALLOCATE(MemAllocator, "Shader resource cache data buffer", Uint8, BufferSize); + memset(m_pResourceData, 0, BufferSize); + } + + // Explicitly construct all objects + for (Uint32 cb = 0; cb < UBCount; ++cb) + new(&GetUB(cb)) CachedUB; + + for (Uint32 s = 0; s < SamplerCount; ++s) + new(&GetSampler(s)) CachedResourceView; + + for (Uint32 i = 0; i < ImageCount; ++i) + new(&GetImage(i)) CachedResourceView; + + for (Uint32 s = 0; s < SSBOCount; ++s) + new(&GetSSBO(s)) CachedSSBO; +} + +GLProgramResourceCache::~GLProgramResourceCache() +{ + VERIFY( !IsInitialized(), "Shader resource cache memory must be released with GLProgramResourceCache::Destroy()" ); +} + +void GLProgramResourceCache::Destroy(class IMemoryAllocator& MemAllocator) +{ + VERIFY( IsInitialized(), "Resource cache is not initialized"); + VERIFY( m_pdbgMemoryAllocator == &MemAllocator, "The allocator does not match the one used to create resources"); + + for (Uint32 cb = 0; cb < GetUBCount(); ++cb) + GetUB(cb).~CachedUB(); + + for (Uint32 s = 0; s < GetSamplerCount(); ++s) + GetSampler(s).~CachedResourceView(); + + for (Uint32 i = 0; i < GetImageCount(); ++i) + GetImage(i).~CachedResourceView(); + + for (Uint32 s = 0; s < GetSSBOCount(); ++s) + GetSSBO(s).~CachedSSBO(); + + if (m_pResourceData != nullptr) + MemAllocator.Free(m_pResourceData); + + m_pResourceData = nullptr; + m_SmplrsOffset = InvalidResourceOffset; + m_ImgsOffset = InvalidResourceOffset; + m_SSBOsOffset = InvalidResourceOffset; + m_MemoryEndOffset = InvalidResourceOffset; +} + +} diff --git a/Graphics/GraphicsEngineOpenGL/src/GLProgramResources.cpp b/Graphics/GraphicsEngineOpenGL/src/GLProgramResources.cpp index 91bdf371..2ad23df1 100644 --- a/Graphics/GraphicsEngineOpenGL/src/GLProgramResources.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/GLProgramResources.cpp @@ -23,931 +23,686 @@ #include "pch.h" #include +#include "GLContextState.h" #include "GLProgramResources.h" #include "RenderDeviceGLImpl.h" #include "ShaderResourceBindingBase.h" +#include "ShaderResourceVariableBase.h" #include "Align.h" namespace Diligent { - GLProgramResources::GLProgramResources(GLProgramResources&& Program)noexcept : - m_ShaderStages (Program.m_ShaderStages), - m_UniformBuffers (Program.m_UniformBuffers), - m_Samplers (Program.m_Samplers), - m_Images (Program.m_Images), - m_StorageBlocks (Program.m_StorageBlocks), - m_ResourceCache (Program.m_ResourceCache), - m_StringPool (std::move(Program.m_StringPool)), - m_NumUniformBuffers(Program.m_NumUniformBuffers), - m_NumSamplers (Program.m_NumSamplers), - m_NumImages (Program.m_NumImages), - m_NumStorageBlocks (Program.m_NumStorageBlocks) - { - Program.m_UniformBuffers = nullptr; - Program.m_Samplers = nullptr; - Program.m_Images = nullptr; - Program.m_StorageBlocks = nullptr; - Program.m_ResourceCache = nullptr; - - Program.m_NumUniformBuffers = 0; - Program.m_NumSamplers = 0; - Program.m_NumImages = 0; - Program.m_NumStorageBlocks = 0; - } - inline void RemoveArrayBrackets(char *Str) - { - auto* OpenBacketPtr = strchr(Str, '['); - if (OpenBacketPtr != nullptr) - *OpenBacketPtr = 0; - } +GLProgramResources::GLProgramResources(GLProgramResources&& Program)noexcept : + m_ShaderStages {Program.m_ShaderStages }, + m_UniformBuffers {Program.m_UniformBuffers }, + m_Samplers {Program.m_Samplers }, + m_Images {Program.m_Images }, + m_StorageBlocks {Program.m_StorageBlocks }, + m_StringPool {std::move(Program.m_StringPool)}, + m_NumUniformBuffers{Program.m_NumUniformBuffers }, + m_NumSamplers {Program.m_NumSamplers }, + m_NumImages {Program.m_NumImages }, + m_NumStorageBlocks {Program.m_NumStorageBlocks } +{ + Program.m_UniformBuffers = nullptr; + Program.m_Samplers = nullptr; + Program.m_Images = nullptr; + Program.m_StorageBlocks = nullptr; + + Program.m_NumUniformBuffers = 0; + Program.m_NumSamplers = 0; + Program.m_NumImages = 0; + Program.m_NumStorageBlocks = 0; +} - void GLProgramResources::AllocateResources(IObject& Owner, - std::vector& UniformBlocks, - std::vector& Samplers, - std::vector& Images, - std::vector& StorageBlocks, - bool InitializeResourceCache) - { - VERIFY(m_UniformBuffers == nullptr, "Resources have already been allocated!"); +inline void RemoveArrayBrackets(char *Str) +{ + auto* OpenBacketPtr = strchr(Str, '['); + if (OpenBacketPtr != nullptr) + *OpenBacketPtr = 0; +} - m_NumUniformBuffers = static_cast(UniformBlocks.size()); - m_NumSamplers = static_cast(Samplers.size()); - m_NumImages = static_cast(Images.size()); - m_NumStorageBlocks = static_cast(StorageBlocks.size()); +void GLProgramResources::AllocateResources(std::vector& UniformBlocks, + std::vector& Samplers, + std::vector& Images, + std::vector& StorageBlocks) +{ + VERIFY(m_UniformBuffers == nullptr, "Resources have already been allocated!"); - size_t StringPoolDataSize = 0; - size_t ResourceCacheSize = 0; - for (const auto& ub : UniformBlocks) - { - StringPoolDataSize += strlen(ub.Name) + 1; - ResourceCacheSize += ub.ArraySize; - } + m_NumUniformBuffers = static_cast(UniformBlocks.size()); + m_NumSamplers = static_cast(Samplers.size()); + m_NumImages = static_cast(Images.size()); + m_NumStorageBlocks = static_cast(StorageBlocks.size()); - for (const auto& sam : Samplers) - { - StringPoolDataSize += strlen(sam.Name) + 1; - ResourceCacheSize += sam.ArraySize; - } + size_t StringPoolDataSize = 0; + for (const auto& ub : UniformBlocks) + { + StringPoolDataSize += strlen(ub.Name) + 1; + } - for (const auto& img : Images) - { - StringPoolDataSize += strlen(img.Name) + 1; - ResourceCacheSize += img.ArraySize; - } + for (const auto& sam : Samplers) + { + StringPoolDataSize += strlen(sam.Name) + 1; + } - for (const auto& sb : StorageBlocks) - { - StringPoolDataSize += strlen(sb.Name) + 1; - ResourceCacheSize += sb.ArraySize; - } + for (const auto& img : Images) + { + StringPoolDataSize += strlen(img.Name) + 1; + } - auto AlignedStringPoolDataSize = Align(StringPoolDataSize, sizeof(void*)); + for (const auto& sb : StorageBlocks) + { + StringPoolDataSize += strlen(sb.Name) + 1; + } - size_t TotalMemorySize = - m_NumUniformBuffers * sizeof(UniformBufferInfo) + - m_NumSamplers * sizeof(SamplerInfo) + - m_NumImages * sizeof(ImageInfo) + - m_NumStorageBlocks * sizeof(StorageBlockInfo); - - if (TotalMemorySize == 0) - { - m_UniformBuffers = nullptr; - m_Samplers = nullptr; - m_Images = nullptr; - m_StorageBlocks = nullptr; - m_ResourceCache = nullptr; - - m_NumUniformBuffers = 0; - m_NumSamplers = 0; - m_NumImages = 0; - m_NumStorageBlocks = 0; - - return; - } + auto AlignedStringPoolDataSize = Align(StringPoolDataSize, sizeof(void*)); - if (InitializeResourceCache) - TotalMemorySize += ResourceCacheSize * sizeof(RefCntAutoPtr); + size_t TotalMemorySize = + m_NumUniformBuffers * sizeof(UniformBufferInfo) + + m_NumSamplers * sizeof(SamplerInfo) + + m_NumImages * sizeof(ImageInfo) + + m_NumStorageBlocks * sizeof(StorageBlockInfo); - TotalMemorySize += AlignedStringPoolDataSize * sizeof(Char); + if (TotalMemorySize == 0) + { + m_UniformBuffers = nullptr; + m_Samplers = nullptr; + m_Images = nullptr; + m_StorageBlocks = nullptr; - auto& MemAllocator = GetRawAllocator(); - void* RawMemory = ALLOCATE_RAW(MemAllocator, "Memory buffer for GLProgramResources", TotalMemorySize); - - m_UniformBuffers = reinterpret_cast(RawMemory); - m_Samplers = reinterpret_cast (m_UniformBuffers + m_NumUniformBuffers); - m_Images = reinterpret_cast (m_Samplers + m_NumSamplers); - m_StorageBlocks = reinterpret_cast(m_Images + m_NumImages); - void* EndOfResourceData = m_StorageBlocks + m_NumStorageBlocks; - Char* StringPoolData = nullptr; - if (InitializeResourceCache) - { - m_ResourceCache = reinterpret_cast*>(EndOfResourceData); - StringPoolData = reinterpret_cast(m_ResourceCache + ResourceCacheSize); - for (Uint32 res=0; res < ResourceCacheSize; ++res) - new (m_ResourceCache+res) RefCntAutoPtr{}; - } - else - { - m_ResourceCache = nullptr; - StringPoolData = reinterpret_cast(EndOfResourceData); - } + m_NumUniformBuffers = 0; + m_NumSamplers = 0; + m_NumImages = 0; + m_NumStorageBlocks = 0; - m_StringPool.AssignMemory(StringPoolData, StringPoolDataSize); + return; + } - Uint16 VariableIndex = 0; - auto* pCurrResource = m_ResourceCache; - for (Uint32 ub=0; ub < m_NumUniformBuffers; ++ub) - { - auto& SrcUB = UniformBlocks[ub]; - new (m_UniformBuffers + ub) UniformBufferInfo - { - Owner, - m_StringPool.CopyString(SrcUB.Name), - SrcUB.VariableType, - SrcUB.ResourceType, - VariableIndex++, - SrcUB.ArraySize, - pCurrResource, - SrcUB.UBIndex - }; - if (pCurrResource != nullptr) - pCurrResource += SrcUB.ArraySize; - } + TotalMemorySize += AlignedStringPoolDataSize * sizeof(Char); - for (Uint32 s=0; s < m_NumSamplers; ++s) - { - auto& SrcSam = Samplers[s]; - new (m_Samplers + s) SamplerInfo - { - Owner, - m_StringPool.CopyString(SrcSam.Name), - SrcSam.VariableType, - SrcSam.ResourceType, - VariableIndex++, - SrcSam.ArraySize, - pCurrResource, - SrcSam.Location, - SrcSam.SamplerType, - SrcSam.pStaticSampler - }; - if (pCurrResource != nullptr) - pCurrResource += SrcSam.ArraySize; - } + auto& MemAllocator = GetRawAllocator(); + void* RawMemory = ALLOCATE_RAW(MemAllocator, "Memory buffer for GLProgramResources", TotalMemorySize); - for (Uint32 img=0; img < m_NumImages; ++img) - { - auto& SrcImg = Images[img]; - new (m_Images + img) ImageInfo - { - Owner, - m_StringPool.CopyString(SrcImg.Name), - SrcImg.VariableType, - SrcImg.ResourceType, - VariableIndex++, - SrcImg.ArraySize, - pCurrResource, - SrcImg.Location, - SrcImg.ImageType - }; - if (pCurrResource != nullptr) - pCurrResource += SrcImg.ArraySize; - } + m_UniformBuffers = reinterpret_cast(RawMemory); + m_Samplers = reinterpret_cast (m_UniformBuffers + m_NumUniformBuffers); + m_Images = reinterpret_cast (m_Samplers + m_NumSamplers); + m_StorageBlocks = reinterpret_cast(m_Images + m_NumImages); + void* EndOfResourceData = m_StorageBlocks + m_NumStorageBlocks; + Char* StringPoolData = reinterpret_cast(EndOfResourceData); - for (Uint32 sb=0; sb < m_NumStorageBlocks; ++sb) - { - auto& SrcSB = StorageBlocks[sb]; - new (m_StorageBlocks + sb) StorageBlockInfo - { - Owner, - m_StringPool.CopyString(SrcSB.Name), - SrcSB.VariableType, - SrcSB.ResourceType, - VariableIndex++, - SrcSB.ArraySize, - pCurrResource, - SrcSB.SBIndex - }; - - if (pCurrResource != nullptr) - pCurrResource += SrcSB.ArraySize; - } + m_StringPool.AssignMemory(StringPoolData, StringPoolDataSize); - VERIFY_EXPR(VariableIndex == GetVariableCount()); - VERIFY_EXPR(m_StringPool.GetRemainingSize() == 0); - VERIFY_EXPR(pCurrResource == nullptr || static_cast(pCurrResource - m_ResourceCache) == ResourceCacheSize); + for (Uint32 ub=0; ub < m_NumUniformBuffers; ++ub) + { + auto& SrcUB = UniformBlocks[ub]; + new (m_UniformBuffers + ub) UniformBufferInfo{SrcUB, m_StringPool}; } - GLProgramResources::~GLProgramResources() + for (Uint32 s=0; s < m_NumSamplers; ++s) { - Uint32 ResourceCacheSize = 0; - ProcessResources( - [&](UniformBufferInfo& UB) - { - ResourceCacheSize += UB.ArraySize; - UB.~UniformBufferInfo(); - }, - [&](SamplerInfo& Sam) - { - ResourceCacheSize += Sam.ArraySize; - Sam.~SamplerInfo(); - }, - [&](ImageInfo& Img) - { - ResourceCacheSize += Img.ArraySize; - Img.~ImageInfo(); - }, - [&](StorageBlockInfo& SB) - { - ResourceCacheSize += SB.ArraySize; - SB.~StorageBlockInfo(); - } - ); - - if (m_ResourceCache != nullptr) - { - for (Uint32 res=0; res < ResourceCacheSize; ++res) - m_ResourceCache[res].~RefCntAutoPtr(); - } - - void* RawMemory = m_UniformBuffers; - if (RawMemory != nullptr) - { - auto& MemAllocator = GetRawAllocator(); - MemAllocator.Free(RawMemory); - } + auto& SrcSam = Samplers[s]; + new (m_Samplers + s) SamplerInfo{SrcSam, m_StringPool}; } - - void GLProgramResources::LoadUniforms(IObject& Owner, - RenderDeviceGLImpl* pDeviceGLImpl, - SHADER_TYPE ShaderStages, - GLuint GLProgram) + for (Uint32 img=0; img < m_NumImages; ++img) { - std::vector UniformBlocks; - std::vector Samplers; - std::vector Images; - std::vector StorageBlocks; - std::unordered_set NamesPool; + auto& SrcImg = Images[img]; + new (m_Images + img) ImageInfo{SrcImg, m_StringPool}; + } - VERIFY(GLProgram != 0, "Null GL program"); + for (Uint32 sb=0; sb < m_NumStorageBlocks; ++sb) + { + auto& SrcSB = StorageBlocks[sb]; + new (m_StorageBlocks + sb) StorageBlockInfo{SrcSB, m_StringPool}; + } - m_ShaderStages = ShaderStages; + VERIFY_EXPR(m_StringPool.GetRemainingSize() == 0); +} - GLint numActiveUniforms = 0; - glGetProgramiv(GLProgram, GL_ACTIVE_UNIFORMS, &numActiveUniforms); - CHECK_GL_ERROR_AND_THROW("Unable to get the number of active uniforms\n"); +GLProgramResources::~GLProgramResources() +{ + ProcessResources( + [&](UniformBufferInfo& UB) + { + UB.~UniformBufferInfo(); + }, + [&](SamplerInfo& Sam) + { + Sam.~SamplerInfo(); + }, + [&](ImageInfo& Img) + { + Img.~ImageInfo(); + }, + [&](StorageBlockInfo& SB) + { + SB.~StorageBlockInfo(); + } + ); - // Query the maximum name length of the active uniform (including null terminator) - GLint activeUniformMaxLength = 0; - glGetProgramiv(GLProgram, GL_ACTIVE_UNIFORM_MAX_LENGTH, &activeUniformMaxLength); - CHECK_GL_ERROR_AND_THROW("Unable to get the maximum uniform name length\n"); + void* RawMemory = m_UniformBuffers; + if (RawMemory != nullptr) + { + auto& MemAllocator = GetRawAllocator(); + MemAllocator.Free(RawMemory); + } +} - GLint numActiveUniformBlocks = 0; - glGetProgramiv(GLProgram, GL_ACTIVE_UNIFORM_BLOCKS, &numActiveUniformBlocks); - CHECK_GL_ERROR_AND_THROW("Unable to get the number of active uniform blocks\n"); - // - // #### This parameter is currently unsupported by Intel OGL drivers. - // - // Query the maximum name length of the active uniform block (including null terminator) - GLint activeUniformBlockMaxLength = 0; - // On Intel driver, this call might fail: - glGetProgramiv(GLProgram, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &activeUniformBlockMaxLength); - //CHECK_GL_ERROR_AND_THROW("Unable to get the maximum uniform block name length\n"); - if (glGetError() != GL_NO_ERROR) - { - LOG_WARNING_MESSAGE( "Unable to get the maximum uniform block name length. Using 1024 as a workaround\n" ); - activeUniformBlockMaxLength = 1024; - } +void GLProgramResources::LoadUniforms(SHADER_TYPE ShaderStages, + const GLObjectWrappers::GLProgramObj& GLProgram, + GLContextState& State, + Uint32& UniformBufferBinding, + Uint32& SamplerBinding, + Uint32& ImageBinding, + Uint32& StorageBufferBinding) +{ + std::vector UniformBlocks; + std::vector Samplers; + std::vector Images; + std::vector StorageBlocks; + std::unordered_set NamesPool; + + VERIFY(GLProgram != 0, "Null GL program"); + State.SetProgram(GLProgram); + + m_ShaderStages = ShaderStages; + + GLint numActiveUniforms = 0; + glGetProgramiv(GLProgram, GL_ACTIVE_UNIFORMS, &numActiveUniforms); + CHECK_GL_ERROR_AND_THROW("Unable to get the number of active uniforms\n"); + + // Query the maximum name length of the active uniform (including null terminator) + GLint activeUniformMaxLength = 0; + glGetProgramiv(GLProgram, GL_ACTIVE_UNIFORM_MAX_LENGTH, &activeUniformMaxLength); + CHECK_GL_ERROR_AND_THROW("Unable to get the maximum uniform name length\n"); + + GLint numActiveUniformBlocks = 0; + glGetProgramiv(GLProgram, GL_ACTIVE_UNIFORM_BLOCKS, &numActiveUniformBlocks); + CHECK_GL_ERROR_AND_THROW("Unable to get the number of active uniform blocks\n"); + + // + // #### This parameter is currently unsupported by Intel OGL drivers. + // + // Query the maximum name length of the active uniform block (including null terminator) + GLint activeUniformBlockMaxLength = 0; + // On Intel driver, this call might fail: + glGetProgramiv(GLProgram, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &activeUniformBlockMaxLength); + //CHECK_GL_ERROR_AND_THROW("Unable to get the maximum uniform block name length\n"); + if (glGetError() != GL_NO_ERROR) + { + LOG_WARNING_MESSAGE( "Unable to get the maximum uniform block name length. Using 1024 as a workaround\n" ); + activeUniformBlockMaxLength = 1024; + } - auto MaxNameLength = std::max(activeUniformMaxLength, activeUniformBlockMaxLength); + auto MaxNameLength = std::max(activeUniformMaxLength, activeUniformBlockMaxLength); #if GL_ARB_program_interface_query - GLint numActiveShaderStorageBlocks = 0; - if (glGetProgramInterfaceiv) - { - glGetProgramInterfaceiv(GLProgram, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, &numActiveShaderStorageBlocks); - CHECK_GL_ERROR_AND_THROW( "Unable to get the number of shader storage blocks blocks\n" ); - - // Query the maximum name length of the active shader storage block (including null terminator) - GLint MaxShaderStorageBlockNameLen = 0; - glGetProgramInterfaceiv(GLProgram, GL_SHADER_STORAGE_BLOCK, GL_MAX_NAME_LENGTH, &MaxShaderStorageBlockNameLen); - CHECK_GL_ERROR_AND_THROW( "Unable to get the maximum shader storage block name length\n" ); - MaxNameLength = std::max( MaxNameLength, MaxShaderStorageBlockNameLen ); - } + GLint numActiveShaderStorageBlocks = 0; + if (glGetProgramInterfaceiv) + { + glGetProgramInterfaceiv(GLProgram, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, &numActiveShaderStorageBlocks); + CHECK_GL_ERROR_AND_THROW( "Unable to get the number of shader storage blocks blocks\n" ); + + // Query the maximum name length of the active shader storage block (including null terminator) + GLint MaxShaderStorageBlockNameLen = 0; + glGetProgramInterfaceiv(GLProgram, GL_SHADER_STORAGE_BLOCK, GL_MAX_NAME_LENGTH, &MaxShaderStorageBlockNameLen); + CHECK_GL_ERROR_AND_THROW( "Unable to get the maximum shader storage block name length\n" ); + MaxNameLength = std::max( MaxNameLength, MaxShaderStorageBlockNameLen ); + } #endif - MaxNameLength = std::max(MaxNameLength, 512); - std::vector Name(MaxNameLength + 1); - for (int i = 0; i < numActiveUniforms; i++) - { - GLenum dataType = 0; - GLint size = 0; - GLint NameLen = 0; - // If one or more elements of an array are active, the name of the array is returned in 'name', - // the type is returned in 'type', and the 'size' parameter returns the highest array element index used, - // plus one, as determined by the compiler and/or linker. - // Only one active uniform variable will be reported for a uniform array. - // Uniform variables other than arrays will have a size of 1 - glGetActiveUniform(GLProgram, i, MaxNameLength, &NameLen, &size, &dataType, Name.data()); - CHECK_GL_ERROR_AND_THROW("Unable to get active uniform\n"); - VERIFY(NameLen < MaxNameLength && static_cast(NameLen) == strlen( Name.data() ), "Incorrect uniform name"); - VERIFY(size >= 1, "Size is expected to be at least 1"); - // Note that - // glGetActiveUniform( program, index, bufSize, length, size, type, name ); - // - // is equivalent to - // - // const enum props[] = { ARRAY_SIZE, TYPE }; - // glGetProgramResourceName( program, UNIFORM, index, bufSize, length, name ); - // glGetProgramResourceiv( program, GL_UNIFORM, index, 1, &props[0], 1, NULL, size ); - // glGetProgramResourceiv( program, GL_UNIFORM, index, 1, &props[1], 1, NULL, (int *)type ); - // - // The latter is only available in GL 4.4 and GLES 3.1 - - switch (dataType) + MaxNameLength = std::max(MaxNameLength, 512); + std::vector Name(MaxNameLength + 1); + for (int i = 0; i < numActiveUniforms; i++) + { + GLenum dataType = 0; + GLint size = 0; + GLint NameLen = 0; + // If one or more elements of an array are active, the name of the array is returned in 'name', + // the type is returned in 'type', and the 'size' parameter returns the highest array element index used, + // plus one, as determined by the compiler and/or linker. + // Only one active uniform variable will be reported for a uniform array. + // Uniform variables other than arrays will have a size of 1 + glGetActiveUniform(GLProgram, i, MaxNameLength, &NameLen, &size, &dataType, Name.data()); + CHECK_GL_ERROR_AND_THROW("Unable to get active uniform\n"); + VERIFY(NameLen < MaxNameLength && static_cast(NameLen) == strlen( Name.data() ), "Incorrect uniform name"); + VERIFY(size >= 1, "Size is expected to be at least 1"); + // Note that + // glGetActiveUniform( program, index, bufSize, length, size, type, name ); + // + // is equivalent to + // + // const enum props[] = { ARRAY_SIZE, TYPE }; + // glGetProgramResourceName( program, UNIFORM, index, bufSize, length, name ); + // glGetProgramResourceiv( program, GL_UNIFORM, index, 1, &props[0], 1, NULL, size ); + // glGetProgramResourceiv( program, GL_UNIFORM, index, 1, &props[1], 1, NULL, (int *)type ); + // + // The latter is only available in GL 4.4 and GLES 3.1 + + switch (dataType) + { + case GL_SAMPLER_1D: + case GL_SAMPLER_2D: + case GL_SAMPLER_3D: + case GL_SAMPLER_CUBE: + case GL_SAMPLER_1D_SHADOW: + case GL_SAMPLER_2D_SHADOW: + + case GL_SAMPLER_1D_ARRAY: + case GL_SAMPLER_2D_ARRAY: + case GL_SAMPLER_1D_ARRAY_SHADOW: + case GL_SAMPLER_2D_ARRAY_SHADOW: + case GL_SAMPLER_CUBE_SHADOW: + + case GL_INT_SAMPLER_1D: + case GL_INT_SAMPLER_2D: + case GL_INT_SAMPLER_3D: + case GL_INT_SAMPLER_CUBE: + case GL_INT_SAMPLER_1D_ARRAY: + case GL_INT_SAMPLER_2D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_1D: + case GL_UNSIGNED_INT_SAMPLER_2D: + case GL_UNSIGNED_INT_SAMPLER_3D: + case GL_UNSIGNED_INT_SAMPLER_CUBE: + case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: + + case GL_SAMPLER_CUBE_MAP_ARRAY: + case GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW: + case GL_INT_SAMPLER_CUBE_MAP_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY: + + case GL_SAMPLER_2D_MULTISAMPLE: + case GL_INT_SAMPLER_2D_MULTISAMPLE: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: + case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + + case GL_SAMPLER_BUFFER: + case GL_INT_SAMPLER_BUFFER: + case GL_UNSIGNED_INT_SAMPLER_BUFFER: { - case GL_SAMPLER_1D: - case GL_SAMPLER_2D: - case GL_SAMPLER_3D: - case GL_SAMPLER_CUBE: - case GL_SAMPLER_1D_SHADOW: - case GL_SAMPLER_2D_SHADOW: - - case GL_SAMPLER_1D_ARRAY: - case GL_SAMPLER_2D_ARRAY: - case GL_SAMPLER_1D_ARRAY_SHADOW: - case GL_SAMPLER_2D_ARRAY_SHADOW: - case GL_SAMPLER_CUBE_SHADOW: - - case GL_INT_SAMPLER_1D: - case GL_INT_SAMPLER_2D: - case GL_INT_SAMPLER_3D: - case GL_INT_SAMPLER_CUBE: - case GL_INT_SAMPLER_1D_ARRAY: - case GL_INT_SAMPLER_2D_ARRAY: - case GL_UNSIGNED_INT_SAMPLER_1D: - case GL_UNSIGNED_INT_SAMPLER_2D: - case GL_UNSIGNED_INT_SAMPLER_3D: - case GL_UNSIGNED_INT_SAMPLER_CUBE: - case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: - case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: - - case GL_SAMPLER_CUBE_MAP_ARRAY: - case GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW: - case GL_INT_SAMPLER_CUBE_MAP_ARRAY: - case GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY: - - case GL_SAMPLER_2D_MULTISAMPLE: - case GL_INT_SAMPLER_2D_MULTISAMPLE: - case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: - case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: - case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: - case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: - - case GL_SAMPLER_BUFFER: - case GL_INT_SAMPLER_BUFFER: - case GL_UNSIGNED_INT_SAMPLER_BUFFER: - { - auto UniformLocation = glGetUniformLocation(GLProgram, Name.data()); - // Note that glGetUniformLocation(program, name) is equivalent to - // glGetProgramResourceLocation(program, GL_UNIFORM, name); - // The latter is only available in GL 4.4 and GLES 3.1 + auto UniformLocation = glGetUniformLocation(GLProgram, Name.data()); + // Note that glGetUniformLocation(program, name) is equivalent to + // glGetProgramResourceLocation(program, GL_UNIFORM, name); + // The latter is only available in GL 4.4 and GLES 3.1 - const auto ResourceType = dataType == GL_SAMPLER_BUFFER || - dataType == GL_INT_SAMPLER_BUFFER || - dataType == GL_UNSIGNED_INT_SAMPLER_BUFFER ? - SHADER_RESOURCE_TYPE_BUFFER_SRV : SHADER_RESOURCE_TYPE_TEXTURE_SRV; + const auto ResourceType = dataType == GL_SAMPLER_BUFFER || + dataType == GL_INT_SAMPLER_BUFFER || + dataType == GL_UNSIGNED_INT_SAMPLER_BUFFER ? + SHADER_RESOURCE_TYPE_BUFFER_SRV : SHADER_RESOURCE_TYPE_TEXTURE_SRV; - RemoveArrayBrackets(Name.data()); + RemoveArrayBrackets(Name.data()); - Samplers.emplace_back( - Owner, - NamesPool.emplace(Name.data()).first->c_str(), - SHADER_RESOURCE_VARIABLE_TYPE_STATIC, - ResourceType, - Uint16{0xFFFF}, // Variable index is assigned by AllocateResources - static_cast(size), - nullptr, // pResources - UniformLocation, - dataType, - nullptr - ); - break; + Samplers.emplace_back( + NamesPool.emplace(Name.data()).first->c_str(), + ShaderStages, + ResourceType, + SamplerBinding, + static_cast(size), + UniformLocation, + dataType + ); + + for (GLint arr_ind = 0; arr_ind < size; ++arr_ind) + { + // glProgramUniform1i is not available in GLES3.0 + glUniform1i(UniformLocation + arr_ind, SamplerBinding++); + CHECK_GL_ERROR("Failed to set binding point for sampler uniform '", Name.data(), '\''); } + break; + } + #if GL_ARB_shader_image_load_store - case GL_IMAGE_1D: - case GL_IMAGE_2D: - case GL_IMAGE_3D: - case GL_IMAGE_2D_RECT: - case GL_IMAGE_CUBE: - case GL_IMAGE_BUFFER: - case GL_IMAGE_1D_ARRAY: - case GL_IMAGE_2D_ARRAY: - case GL_IMAGE_CUBE_MAP_ARRAY: - case GL_IMAGE_2D_MULTISAMPLE: - case GL_IMAGE_2D_MULTISAMPLE_ARRAY: - case GL_INT_IMAGE_1D: - case GL_INT_IMAGE_2D: - case GL_INT_IMAGE_3D: - case GL_INT_IMAGE_2D_RECT: - case GL_INT_IMAGE_CUBE: - case GL_INT_IMAGE_BUFFER: - case GL_INT_IMAGE_1D_ARRAY: - case GL_INT_IMAGE_2D_ARRAY: - case GL_INT_IMAGE_CUBE_MAP_ARRAY: - case GL_INT_IMAGE_2D_MULTISAMPLE: - case GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY: - case GL_UNSIGNED_INT_IMAGE_1D: - case GL_UNSIGNED_INT_IMAGE_2D: - case GL_UNSIGNED_INT_IMAGE_3D: - case GL_UNSIGNED_INT_IMAGE_2D_RECT: - case GL_UNSIGNED_INT_IMAGE_CUBE: - case GL_UNSIGNED_INT_IMAGE_BUFFER: - case GL_UNSIGNED_INT_IMAGE_1D_ARRAY: - case GL_UNSIGNED_INT_IMAGE_2D_ARRAY: - case GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY: - case GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE: - case GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY: + case GL_IMAGE_1D: + case GL_IMAGE_2D: + case GL_IMAGE_3D: + case GL_IMAGE_2D_RECT: + case GL_IMAGE_CUBE: + case GL_IMAGE_BUFFER: + case GL_IMAGE_1D_ARRAY: + case GL_IMAGE_2D_ARRAY: + case GL_IMAGE_CUBE_MAP_ARRAY: + case GL_IMAGE_2D_MULTISAMPLE: + case GL_IMAGE_2D_MULTISAMPLE_ARRAY: + case GL_INT_IMAGE_1D: + case GL_INT_IMAGE_2D: + case GL_INT_IMAGE_3D: + case GL_INT_IMAGE_2D_RECT: + case GL_INT_IMAGE_CUBE: + case GL_INT_IMAGE_BUFFER: + case GL_INT_IMAGE_1D_ARRAY: + case GL_INT_IMAGE_2D_ARRAY: + case GL_INT_IMAGE_CUBE_MAP_ARRAY: + case GL_INT_IMAGE_2D_MULTISAMPLE: + case GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY: + case GL_UNSIGNED_INT_IMAGE_1D: + case GL_UNSIGNED_INT_IMAGE_2D: + case GL_UNSIGNED_INT_IMAGE_3D: + case GL_UNSIGNED_INT_IMAGE_2D_RECT: + case GL_UNSIGNED_INT_IMAGE_CUBE: + case GL_UNSIGNED_INT_IMAGE_BUFFER: + case GL_UNSIGNED_INT_IMAGE_1D_ARRAY: + case GL_UNSIGNED_INT_IMAGE_2D_ARRAY: + case GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY: + case GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE: + case GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY: + { + auto UniformLocation = glGetUniformLocation( GLProgram, Name.data() ); + + const auto ResourceType = dataType == GL_IMAGE_BUFFER || + dataType == GL_INT_IMAGE_BUFFER || + dataType == GL_UNSIGNED_INT_IMAGE_BUFFER ? + SHADER_RESOURCE_TYPE_BUFFER_UAV : SHADER_RESOURCE_TYPE_TEXTURE_UAV; + + RemoveArrayBrackets(Name.data()); + + Images.emplace_back( + NamesPool.emplace(Name.data()).first->c_str(), + ShaderStages, + ResourceType, + ImageBinding, + static_cast(size), + UniformLocation, + dataType ); + + for (GLint arr_ind = 0; arr_ind < size; ++arr_ind) { - auto UniformLocation = glGetUniformLocation( GLProgram, Name.data() ); - - const auto ResourceType = dataType == GL_IMAGE_BUFFER || - dataType == GL_INT_IMAGE_BUFFER || - dataType == GL_UNSIGNED_INT_IMAGE_BUFFER ? - SHADER_RESOURCE_TYPE_BUFFER_UAV : SHADER_RESOURCE_TYPE_TEXTURE_UAV; - - RemoveArrayBrackets(Name.data()); - - Images.emplace_back( - Owner, - NamesPool.emplace(Name.data()).first->c_str(), - SHADER_RESOURCE_VARIABLE_TYPE_STATIC, - ResourceType, - Uint16{0xFFFF}, // Variable index is assigned by AllocateResources - static_cast(size), - nullptr, // pResources - UniformLocation, - dataType ); - break; + // glUniform1i for image uniforms is not supported in at least GLES3.2. + // glProgramUniform1i is not available in GLES3.0 + glUniform1i(UniformLocation + arr_ind, ImageBinding++); + if (glGetError() != GL_NO_ERROR) + { + if (size > 1) + { + LOG_WARNING_MESSAGE("Failed to set binding for image uniform '", Name.data(), "'[", arr_ind, + "]. Expected binding: ", ImageBinding, + " Make sure that this binding is explicitly assigned in the shader source." + " Note that bindings are properly assigned by HLSL->GLSL converter."); + } + else + { + LOG_WARNING_MESSAGE("Failed to set binding for image uniform '", Name.data(), "'." + " Expected binding: ", ImageBinding, + " Make sure that this binding is explicitly assigned in the shader source." + " Note that bindings are properly assigned by HLSL->GLSL converter."); + } + } } -#endif - default: - // Some other uniform type like scalar, matrix etc. - break; + + break; } +#endif + default: + // Some other uniform type like scalar, matrix etc. + break; } + } - for (int i = 0; i < numActiveUniformBlocks; i++) + for (int i = 0; i < numActiveUniformBlocks; i++) + { + // In contrast to shader uniforms, every element in uniform block array is enumerated individually + GLsizei NameLen = 0; + glGetActiveUniformBlockName(GLProgram, i, MaxNameLength, &NameLen, Name.data()); + CHECK_GL_ERROR_AND_THROW("Unable to get active uniform block name\n"); + VERIFY(NameLen < MaxNameLength && static_cast(NameLen) == strlen( Name.data() ), "Incorrect uniform block name"); + + // glGetActiveUniformBlockName( program, uniformBlockIndex, bufSize, length, uniformBlockName ); + // is equivalent to + // glGetProgramResourceName(program, GL_UNIFORM_BLOCK, uniformBlockIndex, bufSize, length, uniformBlockName); + + auto UniformBlockIndex = glGetUniformBlockIndex(GLProgram, Name.data()); + CHECK_GL_ERROR_AND_THROW("Unable to get active uniform block index\n"); + // glGetUniformBlockIndex( program, uniformBlockName ); + // is equivalent to + // glGetProgramResourceIndex( program, GL_UNIFORM_BLOCK, uniformBlockName ); + + bool IsNewBlock = true; + + GLint ArraySize = 1; + auto* OpenBacketPtr = strchr(Name.data(), '['); + if (OpenBacketPtr != nullptr) { - // In contrast to shader uniforms, every element in uniform block array is enumerated individually - GLsizei NameLen = 0; - glGetActiveUniformBlockName(GLProgram, i, MaxNameLength, &NameLen, Name.data()); - CHECK_GL_ERROR_AND_THROW("Unable to get active uniform block name\n"); - VERIFY(NameLen < MaxNameLength && static_cast(NameLen) == strlen( Name.data() ), "Incorrect uniform block name"); - - // glGetActiveUniformBlockName( program, uniformBlockIndex, bufSize, length, uniformBlockName ); - // is equivalent to - // glGetProgramResourceName(program, GL_UNIFORM_BLOCK, uniformBlockIndex, bufSize, length, uniformBlockName); - - auto UniformBlockIndex = glGetUniformBlockIndex(GLProgram, Name.data()); - CHECK_GL_ERROR_AND_THROW("Unable to get active uniform block index\n"); - // glGetUniformBlockIndex( program, uniformBlockName ); - // is equivalent to - // glGetProgramResourceIndex( program, GL_UNIFORM_BLOCK, uniformBlockName ); - - GLint ArraySize = 1; - auto* OpenBacketPtr = strchr(Name.data(), '['); - if (OpenBacketPtr != nullptr) + auto Ind = atoi(OpenBacketPtr+1); + ArraySize = std::max(ArraySize, Ind+1); + *OpenBacketPtr = 0; + if (!UniformBlocks.empty()) { - auto Ind = atoi(OpenBacketPtr+1); - ArraySize = std::max(ArraySize, Ind+1); - *OpenBacketPtr = 0; - if (UniformBlocks.size() > 0) + // Look at previous uniform block to check if it is the same array + auto& LastBlock = UniformBlocks.back(); + if (strcmp(LastBlock.Name, Name.data()) == 0) + { + ArraySize = std::max(ArraySize, static_cast(LastBlock.ArraySize)); + VERIFY(UniformBlockIndex == LastBlock.UBIndex + Ind, "Uniform block indices are expected to be continuous"); + LastBlock.ArraySize = ArraySize; + IsNewBlock = false; + } + else { - // Look at previous uniform block to check if it is the same array - auto& LastBlock = UniformBlocks.back(); - if ( strcmp(LastBlock.Name, Name.data()) == 0) - { - ArraySize = std::max(ArraySize, static_cast(LastBlock.ArraySize)); - VERIFY(UniformBlockIndex == LastBlock.UBIndex + Ind, "Uniform block indices are expected to be continuous"); - LastBlock.ArraySize = ArraySize; - continue; - } - else - { #ifdef _DEBUG - for (const auto& ub : UniformBlocks) - VERIFY(strcmp(ub.Name, Name.data()) != 0, "Uniform block with the name '", ub.Name, "' has already been enumerated"); + for (const auto& ub : UniformBlocks) + VERIFY(strcmp(ub.Name, Name.data()) != 0, "Uniform block with the name '", ub.Name, "' has already been enumerated"); #endif - } } } + } + if (IsNewBlock) + { UniformBlocks.emplace_back( - Owner, NamesPool.emplace(Name.data()).first->c_str(), - SHADER_RESOURCE_VARIABLE_TYPE_STATIC, + ShaderStages, SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, - Uint16{0xFFFF}, // Variable index is assigned by AllocateResources + UniformBufferBinding, static_cast(ArraySize), - nullptr, // pResources UniformBlockIndex ); } + glUniformBlockBinding(GLProgram, UniformBlockIndex, UniformBufferBinding++); + CHECK_GL_ERROR("glUniformBlockBinding() failed"); + } + #if GL_ARB_shader_storage_buffer_object - for (int i = 0; i < numActiveShaderStorageBlocks; ++i) - { - GLsizei Length = 0; - glGetProgramResourceName(GLProgram, GL_SHADER_STORAGE_BLOCK, i, MaxNameLength, &Length, Name.data()); - CHECK_GL_ERROR_AND_THROW("Unable to get shader storage block name\n"); - VERIFY( Length < MaxNameLength && static_cast(Length) == strlen( Name.data() ), "Incorrect shader storage block name" ); + for (int i = 0; i < numActiveShaderStorageBlocks; ++i) + { + GLsizei Length = 0; + glGetProgramResourceName(GLProgram, GL_SHADER_STORAGE_BLOCK, i, MaxNameLength, &Length, Name.data()); + CHECK_GL_ERROR_AND_THROW("Unable to get shader storage block name\n"); + VERIFY( Length < MaxNameLength && static_cast(Length) == strlen( Name.data() ), "Incorrect shader storage block name" ); - auto SBIndex = glGetProgramResourceIndex(GLProgram, GL_SHADER_STORAGE_BLOCK, Name.data()); - CHECK_GL_ERROR_AND_THROW("Unable to get shader storage block index\n"); + auto SBIndex = glGetProgramResourceIndex(GLProgram, GL_SHADER_STORAGE_BLOCK, Name.data()); + CHECK_GL_ERROR_AND_THROW("Unable to get shader storage block index\n"); - Int32 ArraySize = 1; - auto* OpenBacketPtr = strchr(Name.data(), '['); - if (OpenBacketPtr != nullptr) + bool IsNewBlock = true; + Int32 ArraySize = 1; + auto* OpenBacketPtr = strchr(Name.data(), '['); + if (OpenBacketPtr != nullptr) + { + auto Ind = atoi(OpenBacketPtr+1); + ArraySize = std::max(ArraySize, Ind+1); + *OpenBacketPtr = 0; + if (!StorageBlocks.empty()) { - auto Ind = atoi(OpenBacketPtr+1); - ArraySize = std::max(ArraySize, Ind+1); - *OpenBacketPtr = 0; - if (StorageBlocks.size() > 0) + // Look at previous storage block to check if it is the same array + auto& LastBlock = StorageBlocks.back(); + if (strcmp(LastBlock.Name, Name.data()) == 0) + { + ArraySize = std::max(ArraySize, static_cast(LastBlock.ArraySize)); + VERIFY(static_cast(SBIndex) == LastBlock.SBIndex + Ind, "Storage block indices are expected to be continuous"); + LastBlock.ArraySize = ArraySize; + IsNewBlock = false; + } + else { - // Look at previous storage block to check if it is the same array - auto& LastBlock = StorageBlocks.back(); - if ( strcmp(LastBlock.Name, Name.data()) == 0) - { - ArraySize = std::max(ArraySize, static_cast(LastBlock.ArraySize)); - VERIFY(static_cast(SBIndex) == LastBlock.SBIndex + Ind, "Storage block indices are expected to be continuous"); - LastBlock.ArraySize = ArraySize; - continue; - } - else - { #ifdef _DEBUG - for (const auto& sb : StorageBlocks) - VERIFY(strcmp(sb.Name, Name.data()) != 0, "Storage block with the name \"", sb.Name, "\" has already been enumerated"); + for (const auto& sb : StorageBlocks) + VERIFY(strcmp(sb.Name, Name.data()) != 0, "Storage block with the name \"", sb.Name, "\" has already been enumerated"); #endif - } } } + } + if (IsNewBlock) + { StorageBlocks.emplace_back( - Owner, NamesPool.emplace(Name.data()).first->c_str(), - SHADER_RESOURCE_VARIABLE_TYPE_STATIC, + ShaderStages, SHADER_RESOURCE_TYPE_BUFFER_UAV, - Uint16{0xFFFF}, // Variable index is assigned by AllocateResources + StorageBufferBinding, static_cast(ArraySize), - nullptr, // pResources SBIndex ); } -#endif - AllocateResources(Owner, UniformBlocks, Samplers, Images, StorageBlocks, false); - } - - - void GLProgramResources::Clone(RenderDeviceGLImpl* pDeviceGLImpl, - IObject& Owner, - const GLProgramResources& SrcResources, - const PipelineResourceLayoutDesc& ResourceLayout, - const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, - Uint32 NumAllowedTypes) - { - std::vector UniformBlocks; - std::vector Samplers; - std::vector Images; - std::vector StorageBlocks; - - m_ShaderStages = SrcResources.m_ShaderStages; - const Uint32 AllowedTypeBits = GetAllowedTypeBits(AllowedVarTypes, NumAllowedTypes); - for (Uint32 ub=0; ub < SrcResources.GetNumUniformBuffers(); ++ub) + if (glShaderStorageBlockBinding) { - const auto& SrcUB = SrcResources.GetUniformBuffer(ub); - auto VarType = GetShaderVariableType(m_ShaderStages, SrcUB.Name, ResourceLayout); - if (IsAllowedType(VarType, AllowedTypeBits)) - { - UniformBlocks.emplace_back( - Owner, - SrcUB.Name, - VarType, - SrcUB.ResourceType, - Uint16{0xFFFF}, // Variable index is assigned by AllocateResources - SrcUB.ArraySize, - nullptr, // pResources - SrcUB.UBIndex - ); - } - } - - for (Uint32 sam = 0; sam < SrcResources.GetNumSamplers(); ++sam) - { - const auto& SrcSam = SrcResources.GetSampler(sam); - auto VarType = GetShaderVariableType(m_ShaderStages, SrcSam.Name, ResourceLayout); - if (IsAllowedType(VarType, AllowedTypeBits)) - { - RefCntAutoPtr pStaticSampler; - for (Uint32 s = 0; s < ResourceLayout.NumStaticSamplers; ++s) - { - const auto& StSam = ResourceLayout.StaticSamplers[s]; - if (strcmp(SrcSam.Name, StSam.SamplerOrTextureName) == 0) - { - pDeviceGLImpl->CreateSampler(StSam.Desc, &pStaticSampler); - break; - } - } - Samplers.emplace_back( - Owner, - SrcSam.Name, - VarType, - SrcSam.ResourceType, - Uint16{0xFFFF}, // Variable index is assigned by AllocateResources - SrcSam.ArraySize, - nullptr, // pResources - SrcSam.Location, - SrcSam.SamplerType, - pStaticSampler.RawPtr() - ); - } + glShaderStorageBlockBinding(GLProgram, SBIndex, StorageBufferBinding); + CHECK_GL_ERROR("glShaderStorageBlockBinding() failed"); } - - for (Uint32 img = 0; img < SrcResources.GetNumImages(); ++img) - { - const auto& SrcImg = SrcResources.GetImage(img); - auto VarType = GetShaderVariableType(m_ShaderStages, SrcImg.Name, ResourceLayout); - if (IsAllowedType(VarType, AllowedTypeBits)) - { - Images.emplace_back( - Owner, - SrcImg.Name, - VarType, - SrcImg.ResourceType, - Uint16{0xFFFF}, // Variable index is assigned by AllocateResources - SrcImg.ArraySize, - nullptr, // pResources - SrcImg.Location, - SrcImg.ImageType - ); - } - } - - for (Uint32 sb = 0; sb < SrcResources.GetNumStorageBlocks(); ++sb) + else { - const auto& SrcSB = SrcResources.GetStorageBlock(sb); - auto VarType = GetShaderVariableType(m_ShaderStages, SrcSB.Name, ResourceLayout); - if (IsAllowedType(VarType, AllowedTypeBits)) - { - StorageBlocks.emplace_back( - Owner, - SrcSB.Name, - VarType, - SrcSB.ResourceType, - Uint16{0xFFFF}, // Variable index is assigned by AllocateResources - SrcSB.ArraySize, - nullptr, // pResources - SrcSB.SBIndex - ); - } + LOG_WARNING_MESSAGE("glShaderStorageBlockBinding is not available on this device and " + "the engine is unable to automatically assign shader storage block bindindg for '", + Name.data(), "' variable. Expected binding: ", StorageBufferBinding, + " Make sure that this binding is explicitly assigned in the shader source." + " Note that bindings are properly assigned by HLSL->GLSL converter."); } - - AllocateResources(Owner, UniformBlocks, Samplers, Images, StorageBlocks, true); + ++StorageBufferBinding; } +#endif + State.SetProgram(GLObjectWrappers::GLProgramObj{false}); + AllocateResources(UniformBlocks, Samplers, Images, StorageBlocks); +} - GLProgramResources::GLProgramVariableBase* GLProgramResources::GetVariable(const Char* Name) - { - // Name will be implicitly converted to HashMapStringKey without making a copy - for (Uint32 ub=0; ub < m_NumUniformBuffers; ++ub) - { - auto& UB = GetUniformBuffer(ub); - if (strcmp(UB.Name, Name) == 0) - return &UB; - } - - for (Uint32 s=0; s < m_NumSamplers; ++s) - { - auto& Sam = GetSampler(s); - if (strcmp(Sam.Name, Name) == 0) - return &Sam; - } +ShaderResourceDesc GLProgramResources::GetResourceDesc(Uint32 Index)const +{ + if (Index < m_NumUniformBuffers) + return GetUniformBuffer(Index).GetResourceDesc(); + else + Index -= m_NumUniformBuffers; + + if (Index < m_NumSamplers) + return GetSampler(Index).GetResourceDesc(); + else + Index -= m_NumSamplers; + + if (Index < m_NumImages) + return GetImage(Index).GetResourceDesc(); + else + Index -= m_NumImages; + + if (Index < m_NumStorageBlocks) + return GetStorageBlock(Index).GetResourceDesc(); + else + Index -= m_NumStorageBlocks; + + LOG_ERROR_MESSAGE("Resource index ", Index + GetVariableCount(), " is invalid"); + return ShaderResourceDesc{}; +} - for (Uint32 img=0; img < m_NumImages; ++img) - { - auto& Img = GetImage(img); - if (strcmp(Img.Name, Name) == 0) - return &Img; - } +void GLProgramResources::CountResources(const PipelineResourceLayoutDesc& ResourceLayout, + const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, + Uint32 NumAllowedTypes, + ResourceCounters& Counters)const +{ + ProcessConstResources( + [&](const GLProgramResources::UniformBufferInfo& UB) + { + ++Counters.NumUBs; + }, + [&](const GLProgramResources::SamplerInfo& Sam) + { + ++Counters.NumSamplers; + }, + [&](const GLProgramResources::ImageInfo& Img) + { + ++Counters.NumImages; + }, + [&](const GLProgramResources::StorageBlockInfo& SB) + { + ++Counters.NumStorageBlocks; + }, + &ResourceLayout, + AllowedVarTypes, + NumAllowedTypes + ); +} - for (Uint32 sb=0; sb < m_NumStorageBlocks; ++sb) - { - auto& SB = GetStorageBlock(sb); - if (strcmp(SB.Name, Name) == 0) - return &SB; - } +bool GLProgramResources::IsCompatibleWith(const GLProgramResources& Res)const +{ + if (GetNumUniformBuffers() != Res.GetNumUniformBuffers() || + GetNumSamplers() != Res.GetNumSamplers() || + GetNumImages() != Res.GetNumImages() || + GetNumStorageBlocks() != Res.GetNumStorageBlocks()) + return false; - return nullptr; + for (Uint32 ub = 0; ub < GetNumUniformBuffers(); ++ub) + { + const auto& UB0 = GetUniformBuffer(ub); + const auto& UB1 = Res.GetUniformBuffer(ub); + if (!UB0.IsCompatibleWith(UB1)) + return false; } - const GLProgramResources::GLProgramVariableBase* GLProgramResources::GetVariable(Uint32 Index)const + for (Uint32 sam = 0; sam < GetNumSamplers(); ++sam) { - if (Index < GetNumUniformBuffers()) - return &GetUniformBuffer(Index); - else - Index -= GetNumUniformBuffers(); - - if (Index < GetNumSamplers()) - return &GetSampler(Index); - else - Index -= GetNumSamplers(); - - if (Index < GetNumImages()) - return &GetImage(Index); - else - Index -= GetNumImages(); - - if (Index < GetNumStorageBlocks()) - return &GetStorageBlock(Index); - else - Index -= GetNumStorageBlocks(); - - return nullptr; + const auto& Sam0 = GetSampler(sam); + const auto& Sam1 = Res.GetSampler(sam); + if (!Sam0.IsCompatibleWith(Sam1)) + return false; } - - static void BindResourcesHelper(GLProgramResources::GLProgramVariableBase& res, IResourceMapping* pResourceMapping, Uint32 Flags) + for (Uint32 img = 0; img < GetNumImages(); ++img) { - if ( (Flags & (1 << res.VariableType)) == 0 ) - return; - - auto& Name = res.Name; - for (Uint32 ArrInd = 0; ArrInd < res.ArraySize; ++ArrInd) - { - auto& CurrResource = res.pResources[ArrInd]; - - if ( (Flags & BIND_SHADER_RESOURCES_KEEP_EXISTING) != 0 && CurrResource ) - continue; // Skip already resolved resources - - RefCntAutoPtr pNewRes; - pResourceMapping->GetResource( Name, static_cast(&pNewRes), ArrInd ); - - if (pNewRes != nullptr) - { - if(res.VariableType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC && CurrResource != nullptr && CurrResource != pNewRes ) - LOG_ERROR_MESSAGE( "Updating binding for static variable \"", Name, "\" is invalid and may result in an undefined behavior" ); - CurrResource = pNewRes; - } - else - { - if ( CurrResource == nullptr && (Flags & BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED) ) - LOG_ERROR_MESSAGE("Resource \"", Name, "\" is not found in the resource mapping"); - } - } + const auto& Img0 = GetImage(img); + const auto& Img1 = Res.GetImage(img); + if (!Img0.IsCompatibleWith(Img1)) + return false; } - - void GLProgramResources::BindResources(IResourceMapping* pResourceMapping, Uint32 Flags ) + for (Uint32 sb = 0; sb < GetNumStorageBlocks(); ++sb) { - if( !pResourceMapping ) - return; - - if ( (Flags & BIND_SHADER_RESOURCES_UPDATE_ALL) == 0 ) - Flags |= BIND_SHADER_RESOURCES_UPDATE_ALL; - - ProcessResources( - [&](UniformBufferInfo& UB) - { - BindResourcesHelper(UB, pResourceMapping, Flags); - }, - [&](SamplerInfo& Sam) - { - BindResourcesHelper(Sam, pResourceMapping, Flags); - }, - [&](ImageInfo& Img) - { - BindResourcesHelper(Img, pResourceMapping, Flags); - }, - [&](StorageBlockInfo& SB) - { - BindResourcesHelper(SB, pResourceMapping, Flags); - } - ); + const auto& SB0 = GetStorageBlock(sb); + const auto& SB1 = Res.GetStorageBlock(sb); + if (!SB0.IsCompatibleWith(SB1)) + return false; } + return true; +} - bool GLProgramResources::IsCompatibleWith(const GLProgramResources& Res)const - { - if (GetNumUniformBuffers() != Res.GetNumUniformBuffers() || - GetNumSamplers() != Res.GetNumSamplers() || - GetNumImages() != Res.GetNumImages() || - GetNumStorageBlocks() != Res.GetNumStorageBlocks()) - return false; - for (Uint32 ub = 0; ub < GetNumUniformBuffers(); ++ub) - { - const auto& UB0 = GetUniformBuffer(ub); - const auto& UB1 = Res.GetUniformBuffer(ub); - if (!UB0.IsCompatibleWith(UB1)) - return false; - } +size_t GLProgramResources::GetHash()const +{ + size_t hash = ComputeHash(GetNumUniformBuffers(), GetNumSamplers(), GetNumImages(), GetNumStorageBlocks()); - for (Uint32 sam = 0; sam < GetNumSamplers(); ++sam) + ProcessConstResources( + [&](const UniformBufferInfo& UB) { - const auto& Sam0 = GetSampler(sam); - const auto& Sam1 = Res.GetSampler(sam); - if (!Sam0.IsCompatibleWith(Sam1)) - return false; - } - - for (Uint32 img = 0; img < GetNumImages(); ++img) + HashCombine(hash, UB.GetHash()); + }, + [&](const SamplerInfo& Sam) { - const auto& Img0 = GetImage(img); - const auto& Img1 = Res.GetImage(img); - if (!Img0.IsCompatibleWith(Img1)) - return false; - } - - for (Uint32 sb = 0; sb < GetNumStorageBlocks(); ++sb) + HashCombine(hash, Sam.GetHash()); + }, + [&](const ImageInfo& Img) { - const auto& SB0 = GetStorageBlock(sb); - const auto& SB1 = Res.GetStorageBlock(sb); - if (!SB0.IsCompatibleWith(SB1)) - return false; - } - - return true; - } - - - size_t GLProgramResources::GetHash()const - { - size_t hash = ComputeHash(GetNumUniformBuffers(), GetNumSamplers(), GetNumImages(), GetNumStorageBlocks()); - - ProcessConstResources( - [&](const UniformBufferInfo& UB) - { - HashCombine(hash, UB.GetHash()); - }, - [&](const SamplerInfo& Sam) - { - HashCombine(hash, Sam.GetHash()); - }, - [&](const ImageInfo& Img) - { - HashCombine(hash, Img.GetHash()); - }, - [&](const StorageBlockInfo& SB) - { - HashCombine(hash, SB.GetHash()); - } - ); - - return hash; - } - -#ifdef VERIFY_RESOURCE_BINDINGS - static void dbgVerifyResourceBindingsHelper(const GLProgramResources::GLProgramVariableBase& res, const Char* VarTypeName) - { - for (Uint32 ArrInd = 0; ArrInd < res.ArraySize; ++ArrInd) + HashCombine(hash, Img.GetHash()); + }, + [&](const StorageBlockInfo& SB) { - if (!res.pResources[ArrInd]) - { - if( res.ArraySize > 1) - LOG_ERROR_MESSAGE( "No resource is bound to ", VarTypeName, " variable \"", res.Name, "[", ArrInd, "]\"" ); - else - LOG_ERROR_MESSAGE( "No resource is bound to ", VarTypeName, " variable \"", res.Name, "\"" ); - } + HashCombine(hash, SB.GetHash()); } - } + ); - void GLProgramResources::dbgVerifyResourceBindings()const - { - ProcessConstResources( - [&](const UniformBufferInfo& UB) - { - dbgVerifyResourceBindingsHelper(UB, "uniform block"); - }, - [&](const SamplerInfo& Sam) - { - dbgVerifyResourceBindingsHelper(Sam, "sampler"); - }, - [&](const ImageInfo& Img) - { - dbgVerifyResourceBindingsHelper(Img, "image"); - }, - [&](const StorageBlockInfo& SB) - { - dbgVerifyResourceBindingsHelper(SB, "shader storage block"); - } - ); - } -#endif + return hash; +} } diff --git a/Graphics/GraphicsEngineOpenGL/src/PipelineStateGLImpl.cpp b/Graphics/GraphicsEngineOpenGL/src/PipelineStateGLImpl.cpp index 3d0e597c..aa67f70c 100644 --- a/Graphics/GraphicsEngineOpenGL/src/PipelineStateGLImpl.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/PipelineStateGLImpl.cpp @@ -27,6 +27,7 @@ #include "ShaderGLImpl.h" #include "ShaderResourceBindingGLImpl.h" #include "EngineMemory.h" +#include "DeviceContextGLImpl.h" namespace Diligent { @@ -36,7 +37,8 @@ PipelineStateGLImpl::PipelineStateGLImpl(IReferenceCounters* pRefCounters, const PipelineStateDesc& PipelineDesc, bool bIsDeviceInternal) : TPipelineStateBase(pRefCounters, pDeviceGL, PipelineDesc, bIsDeviceInternal), - m_GLProgram(false) + m_ResourceLayout (*this), + m_StaticResourceLayout(*this) { if (!m_Desc.IsComputePipeline && m_pPS == nullptr) { @@ -52,96 +54,78 @@ PipelineStateGLImpl::PipelineStateGLImpl(IReferenceCounters* pRefCounters, m_ppShaders[m_NumShaders++] = m_pPS; } - auto &DeviceCaps = pDeviceGL->GetDeviceCaps(); + auto& DeviceCaps = pDeviceGL->GetDeviceCaps(); VERIFY( DeviceCaps.DevType != DeviceType::Undefined, "Device caps are not initialized" ); - bool bIsProgramPipelineSupported = DeviceCaps.bSeparableProgramSupported; - LinkGLProgram(bIsProgramPipelineSupported); -} + auto pImmediateCtx = m_pDevice->GetImmediateContext(); + VERIFY_EXPR(pImmediateCtx); + auto& GLState = pImmediateCtx.RawPtr()->GetContextState(); -void PipelineStateGLImpl::LinkGLProgram(bool bIsProgramPipelineSupported) -{ - if (bIsProgramPipelineSupported) { - // Program pipelines are not shared between GL contexts, so we cannot create - // it now - m_ShaderResourceLayoutHash = 0; - m_StaticResources.resize(m_NumShaders); - for (Uint32 Shader = 0; Shader < m_NumShaders; ++Shader) + m_TotalUniformBufferBindings = 0; + m_TotalSamplerBindings = 0; + m_TotalImageBindings = 0; + m_TotalStorageBufferBindings = 0; + if (DeviceCaps.bSeparableProgramSupported) { - auto* pShaderGL = GetShader(Shader); - const SHADER_RESOURCE_VARIABLE_TYPE StaticVars[] = {SHADER_RESOURCE_VARIABLE_TYPE_STATIC}; - m_StaticResources[Shader].Clone(GetDevice(), *this, pShaderGL->GetGlProgram().GetResources(), m_Desc.ResourceLayout, StaticVars, _countof(StaticVars)); - - const auto ShaderType = pShaderGL->GetDesc().ShaderType; - const auto ShaderTypeInd = GetShaderTypeIndex(ShaderType); - m_ResourceLayoutIndex[ShaderTypeInd] = static_cast(Shader); - - HashCombine(m_ShaderResourceLayoutHash, pShaderGL->m_GlProgObj.GetResources().GetHash()); + // Program pipelines are not shared between GL contexts, so we cannot create + // it now + m_ShaderResourceLayoutHash = 0; + m_ProgramResources.resize(m_NumShaders); + m_GLPrograms.reserve(m_NumShaders); + for (Uint32 i = 0; i < m_NumShaders; ++i) + { + auto* pShaderGL = GetShader(i); + const auto& ShaderDesc = pShaderGL->GetDesc(); + m_GLPrograms.emplace_back(ShaderGLImpl::LinkProgram(&m_ppShaders[i], 1, true)); + m_ProgramResources[i].LoadUniforms(ShaderDesc.ShaderType, m_GLPrograms[i], GLState, + m_TotalUniformBufferBindings, + m_TotalSamplerBindings, + m_TotalImageBindings, + m_TotalStorageBufferBindings); + + HashCombine(m_ShaderResourceLayoutHash, m_ProgramResources[i].GetHash()); + } } - } - else - { - // Create new progam - m_GLProgram.Create(); - for (Uint32 Shader = 0; Shader < m_NumShaders; ++Shader) + else { - auto* pCurrShader = GetShader(Shader); - glAttachShader(m_GLProgram, pCurrShader->m_GLShaderObj); - CHECK_GL_ERROR("glAttachShader() failed"); - } - glLinkProgram(m_GLProgram); - CHECK_GL_ERROR("glLinkProgram() failed"); - int IsLinked = GL_FALSE; - glGetProgramiv(m_GLProgram, GL_LINK_STATUS, &IsLinked); - CHECK_GL_ERROR("glGetProgramiv() failed"); - if (!IsLinked) - { - int LengthWithNull = 0, Length = 0; - // Notice that glGetProgramiv is used to get the length for a shader program, not glGetShaderiv. - // The length of the info log includes a null terminator. - glGetProgramiv(m_GLProgram, GL_INFO_LOG_LENGTH, &LengthWithNull); - - // The maxLength includes the NULL character - std::vector shaderProgramInfoLog(LengthWithNull); - - // Notice that glGetProgramInfoLog is used, not glGetShaderInfoLog. - glGetProgramInfoLog(m_GLProgram, LengthWithNull, &Length, shaderProgramInfoLog.data()); - VERIFY(Length == LengthWithNull-1, "Incorrect program info log len"); - LOG_ERROR_MESSAGE("Failed to link shader program:\n", shaderProgramInfoLog.data(), '\n'); - UNEXPECTED("glLinkProgram failed"); - } - - // Detach shaders from the program object - for (Uint32 Shader = 0; Shader < m_NumShaders; ++Shader) - { - auto* pCurrShader = GetShader(Shader); - glDetachShader(m_GLProgram, pCurrShader->m_GLShaderObj); - CHECK_GL_ERROR("glDetachShader() failed"); + m_GLPrograms.emplace_back(ShaderGLImpl::LinkProgram(m_ppShaders, m_NumShaders, false)); + m_ProgramResources.resize(1); + SHADER_TYPE ShaderStages = SHADER_TYPE_UNKNOWN; + for (Uint32 i = 0; i < m_NumShaders; ++i) + { + const auto& ShaderDesc = m_ppShaders[i]->GetDesc(); + ShaderStages |= ShaderDesc.ShaderType; + } + m_ProgramResources[0].LoadUniforms(ShaderStages, m_GLPrograms[0], GLState, + m_TotalUniformBufferBindings, + m_TotalSamplerBindings, + m_TotalImageBindings, + m_TotalStorageBufferBindings); + + m_ShaderResourceLayoutHash = m_ProgramResources[0].GetHash(); } - SHADER_TYPE ShaderStages = SHADER_TYPE_UNKNOWN; - for (Uint32 Shader = 0; Shader < m_NumShaders; ++Shader) - { - auto* pCurrShader = GetShader(Shader); - const auto& Desc = pCurrShader->GetDesc(); - ShaderStages |= Desc.ShaderType; - } + m_ResourceLayout.Initialize(m_ProgramResources.data(), static_cast(m_GLPrograms.size()), m_Desc.ResourceLayout, nullptr, 0, nullptr); + } + + m_StaticSamplers.resize(m_Desc.ResourceLayout.NumStaticSamplers); + for (Uint32 s=0; s < m_Desc.ResourceLayout.NumStaticSamplers; ++s) + { + pDeviceGL->CreateSampler(m_Desc.ResourceLayout.StaticSamplers[s].Desc, &m_StaticSamplers[s]); + } - auto pDeviceGL = GetDevice(); - m_GLProgram.InitResources(pDeviceGL, ShaderStages, *this); - - m_StaticResources.resize(1); + { const SHADER_RESOURCE_VARIABLE_TYPE StaticVars[] = {SHADER_RESOURCE_VARIABLE_TYPE_STATIC}; - m_StaticResources[0].Clone(GetDevice(), *this, m_GLProgram.GetResources(), m_Desc.ResourceLayout, StaticVars, _countof(StaticVars)); - - m_ShaderResourceLayoutHash = m_GLProgram.GetResources().GetHash(); - } + m_StaticResourceLayout.Initialize(m_ProgramResources.data(), static_cast(m_GLPrograms.size()), m_Desc.ResourceLayout, StaticVars, _countof(StaticVars), &m_StaticResourceCache); + InitStaticSamplersInResourceCache(m_StaticResourceLayout, m_StaticResourceCache); + } } PipelineStateGLImpl::~PipelineStateGLImpl() { + m_StaticResourceCache.Destroy(GetRawAllocator()); GetDevice()->OnDestroyPSO(this); } @@ -152,7 +136,7 @@ void PipelineStateGLImpl::CreateShaderResourceBinding(IShaderResourceBinding** p { auto* pRenderDeviceGL = GetDevice(); auto& SRBAllocator = pRenderDeviceGL->GetSRBAllocator(); - auto pResBinding = NEW_RC_OBJ(SRBAllocator, "ShaderResourceBindingGLImpl instance", ShaderResourceBindingGLImpl)(this); + auto pResBinding = NEW_RC_OBJ(SRBAllocator, "ShaderResourceBindingGLImpl instance", ShaderResourceBindingGLImpl)(this, m_ProgramResources.data(), static_cast(m_ProgramResources.size())); if (InitStaticResources) pResBinding->InitializeStaticResources(this); pResBinding->QueryInterface(IID_ShaderResourceBinding, reinterpret_cast(ppShaderResourceBinding)); @@ -169,13 +153,43 @@ bool PipelineStateGLImpl::IsCompatibleWith(const IPipelineState* pPSO)const if (m_ShaderResourceLayoutHash != pPSOGL->m_ShaderResourceLayoutHash) return false; - return m_GLProgram.GetResources().IsCompatibleWith(pPSOGL->m_GLProgram.GetResources()); + if (m_ProgramResources.size() != pPSOGL->m_ProgramResources.size()) + return false; + + for (size_t i=0; i < m_ProgramResources.size(); ++i) + { + if (!m_ProgramResources[i].IsCompatibleWith(pPSOGL->m_ProgramResources[i])) + return false; + } + + return true; +} + +void PipelineStateGLImpl::CommitProgram(GLContextState& State) +{ + auto ProgramPipelineSupported = m_pDevice->GetDeviceCaps().bSeparableProgramSupported; + + if (ProgramPipelineSupported) + { + // WARNING: glUseProgram() overrides glBindProgramPipeline(). That is, if you have a program in use and + // a program pipeline bound, all rendering will use the program that is in use, not the pipeline programs! + // So make sure that glUseProgram(0) has been called if pipeline is in use + State.SetProgram(GLObjectWrappers::GLProgramObj{false}); + auto& Pipeline = GetGLProgramPipeline(State.GetCurrentGLContext()); + VERIFY(Pipeline != 0, "Program pipeline must not be null"); + State.SetPipeline( Pipeline ); + } + else + { + VERIFY_EXPR(m_GLPrograms.size() == 1); + State.SetProgram(m_GLPrograms[0]); + } } GLObjectWrappers::GLPipelineObj& PipelineStateGLImpl::GetGLProgramPipeline(GLContext::NativeGLContextType Context) { ThreadingTools::LockHelper Lock(m_ProgPipelineLockFlag); - for(auto& ctx_pipeline : m_GLProgPipelines) + for (auto& ctx_pipeline : m_GLProgPipelines) { if (ctx_pipeline.first == Context) return ctx_pipeline.second; @@ -185,66 +199,57 @@ GLObjectWrappers::GLPipelineObj& PipelineStateGLImpl::GetGLProgramPipeline(GLCon m_GLProgPipelines.emplace_back(Context, true); auto& ctx_pipeline = m_GLProgPipelines.back(); GLuint Pipeline = ctx_pipeline.second; - for (Uint32 Shader = 0; Shader < m_NumShaders; ++Shader) + for (Uint32 i = 0; i < m_NumShaders; ++i) { - auto* pCurrShader = GetShader(Shader); + auto* pCurrShader = GetShader(i); auto GLShaderBit = ShaderTypeToGLShaderBit(pCurrShader->GetDesc().ShaderType); // If the program has an active code for each stage mentioned in set flags, // then that code will be used by the pipeline. If program is 0, then the given // stages are cleared from the pipeline. - glUseProgramStages(Pipeline, GLShaderBit, pCurrShader->m_GlProgObj); + glUseProgramStages(Pipeline, GLShaderBit, m_GLPrograms[i]); CHECK_GL_ERROR("glUseProgramStages() failed"); } return ctx_pipeline.second; } +void PipelineStateGLImpl::InitializeSRBResourceCache(GLProgramResourceCache& ResourceCache)const +{ + ResourceCache.Initialize(m_TotalUniformBufferBindings, m_TotalSamplerBindings, m_TotalImageBindings, m_TotalStorageBufferBindings, GetRawAllocator()); + InitStaticSamplersInResourceCache(m_ResourceLayout, ResourceCache); +} -void PipelineStateGLImpl::BindStaticResources(Uint32 ShaderFlags, IResourceMapping* pResourceMapping, Uint32 Flags) +void PipelineStateGLImpl::InitStaticSamplersInResourceCache(const GLPipelineResourceLayout& ResourceLayout, GLProgramResourceCache& Cache)const { - for(auto& StaticRes : m_StaticResources) + for (Uint32 s=0; s < ResourceLayout.GetNumResources(); ++s) { - if ((StaticRes.GetShaderStages() & ShaderFlags)!=0) - StaticRes.BindResources(pResourceMapping, Flags); + const auto& Sam = ResourceLayout.GetConstResource(s); + if (Sam.m_StaticSamplerIdx >= 0) + { + ISampler* pSampler = m_StaticSamplers[Sam.m_StaticSamplerIdx].RawPtr(); + for (Uint32 binding = Sam.m_Attribs.Binding; binding < Sam.m_Attribs.Binding + Sam.m_Attribs.ArraySize; ++binding) + Cache.SetStaticSampler(binding, pSampler); + } } } + +void PipelineStateGLImpl::BindStaticResources(Uint32 ShaderFlags, IResourceMapping* pResourceMapping, Uint32 Flags) +{ + m_StaticResourceLayout.BindResources(static_cast(ShaderFlags), pResourceMapping, Flags, m_StaticResourceCache); +} Uint32 PipelineStateGLImpl::GetStaticVariableCount(SHADER_TYPE ShaderType) const { - if (m_GLProgram) - { - return (m_StaticResources[0].GetShaderStages() & ShaderType) != 0 ? m_StaticResources[0].GetVariableCount() : 0; - } - else - { - const auto LayoutInd = m_ResourceLayoutIndex[GetShaderTypeIndex(ShaderType)]; - return LayoutInd >= 0 ? m_StaticResources[LayoutInd].GetVariableCount() : 0; - } + return m_StaticResourceLayout.GetNumVariables(ShaderType); } IShaderResourceVariable* PipelineStateGLImpl::GetStaticVariableByName(SHADER_TYPE ShaderType, const Char* Name) { - if (m_GLProgram) - { - return (m_StaticResources[0].GetShaderStages() & ShaderType) != 0 ? m_StaticResources[0].GetVariable(Name) : nullptr; - } - else - { - const auto LayoutInd = m_ResourceLayoutIndex[GetShaderTypeIndex(ShaderType)]; - return LayoutInd >= 0 ? m_StaticResources[LayoutInd].GetVariable(Name) : nullptr; - } + return m_StaticResourceLayout.GetShaderVariable(ShaderType, Name); } IShaderResourceVariable* PipelineStateGLImpl::GetStaticVariableByIndex(SHADER_TYPE ShaderType, Uint32 Index) { - if (m_GLProgram) - { - return (m_StaticResources[0].GetShaderStages() & ShaderType) != 0 ? m_StaticResources[0].GetVariable(Index) : nullptr; - } - else - { - const auto LayoutInd = m_ResourceLayoutIndex[GetShaderTypeIndex(ShaderType)]; - return LayoutInd >= 0 ? m_StaticResources[LayoutInd].GetVariable(Index) : nullptr; - } + return m_StaticResourceLayout.GetShaderVariable(ShaderType, Index); } } diff --git a/Graphics/GraphicsEngineOpenGL/src/ShaderGLImpl.cpp b/Graphics/GraphicsEngineOpenGL/src/ShaderGLImpl.cpp index 61ad6dcb..26413796 100644 --- a/Graphics/GraphicsEngineOpenGL/src/ShaderGLImpl.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/ShaderGLImpl.cpp @@ -25,6 +25,7 @@ #include "ShaderGLImpl.h" #include "RenderDeviceGLImpl.h" +#include "DeviceContextGLImpl.h" #include "DataBlobImpl.h" #include "GLSLSourceBuilder.h" @@ -38,8 +39,7 @@ ShaderGLImpl::ShaderGLImpl(IReferenceCounters* pRefCounters, const ShaderCreateInfo& CreationAttribs, bool bIsDeviceInternal) : TShaderBase( pRefCounters, pDeviceGL, CreationAttribs.Desc, bIsDeviceInternal ), - m_GlProgObj(false), - m_GLShaderObj( false, GLObjectWrappers::GLShaderObjCreateReleaseHelper( GetGLShaderType( m_Desc.ShaderType ) ) ) + m_GLShaderObj(true, GLObjectWrappers::GLShaderObjCreateReleaseHelper( GetGLShaderType( m_Desc.ShaderType ) )) { auto GLSLSource = BuildGLSLSourceString(CreationAttribs, pDeviceGL->GetDeviceCaps(), TargetGLSLCompiler::driver); @@ -53,34 +53,31 @@ ShaderGLImpl::ShaderGLImpl(IReferenceCounters* pRefCounters, // glGetProgramiv(program, GL_LINK_STATUS, &isLinked); // The log can then be queried in the same way - // Create empty shader object - auto GLShaderType = GetGLShaderType(m_Desc.ShaderType); - GLObjectWrappers::GLShaderObj ShaderObj(true, GLObjectWrappers::GLShaderObjCreateReleaseHelper(GLShaderType)); // Each element in the length array may contain the length of the corresponding string // (the null character is not counted as part of the string length). // Not specifying lengths causes shader compilation errors on Android - const char * ShaderStrings[] = { GLSLSource.c_str() }; + const char* ShaderStrings[] = { GLSLSource.c_str() }; GLint Lenghts[] = { static_cast(GLSLSource.length()) }; // Provide source strings (the strings will be saved in internal OpenGL memory) - glShaderSource(ShaderObj, _countof(ShaderStrings), ShaderStrings, Lenghts ); + glShaderSource(m_GLShaderObj, _countof(ShaderStrings), ShaderStrings, Lenghts ); // When the shader is compiled, it will be compiled as if all of the given strings were concatenated end-to-end. - glCompileShader(ShaderObj); + glCompileShader(m_GLShaderObj); GLint compiled = GL_FALSE; // Get compilation status - glGetShaderiv(ShaderObj, GL_COMPILE_STATUS, &compiled); + glGetShaderiv(m_GLShaderObj, GL_COMPILE_STATUS, &compiled); if(!compiled) { std::string FullSource; - for(const auto *str : ShaderStrings) + for(const auto* str : ShaderStrings) FullSource.append(str); std::stringstream ErrorMsgSS; - ErrorMsgSS << "Failed to compile shader file \""<< (CreationAttribs.Desc.Name != nullptr ? CreationAttribs.Desc.Name : "") << '\"' << std::endl; + ErrorMsgSS << "Failed to compile shader file '"<< (CreationAttribs.Desc.Name != nullptr ? CreationAttribs.Desc.Name : "") << '\'' << std::endl; int infoLogLen = 0; // The function glGetShaderiv() tells how many bytes to allocate; the length includes the NULL terminator. - glGetShaderiv(ShaderObj, GL_INFO_LOG_LENGTH, &infoLogLen); + glGetShaderiv(m_GLShaderObj, GL_INFO_LOG_LENGTH, &infoLogLen); std::vector infoLog(infoLogLen); if (infoLogLen > 0) @@ -89,7 +86,7 @@ ShaderGLImpl::ShaderGLImpl(IReferenceCounters* pRefCounters, // Get the log. infoLogLen is the size of infoLog. This tells OpenGL how many bytes at maximum it will write // charsWritten is a return value, specifying how many bytes it actually wrote. One may pass NULL if he // doesn't care - glGetShaderInfoLog(ShaderObj, infoLogLen, &charsWritten, infoLog.data()); + glGetShaderInfoLog(m_GLShaderObj, infoLogLen, &charsWritten, infoLog.data()); VERIFY(charsWritten == infoLogLen-1, "Unexpected info log length"); ErrorMsgSS << "InfoLog:" << std::endl << infoLog.data() << std::endl; } @@ -113,58 +110,18 @@ ShaderGLImpl::ShaderGLImpl(IReferenceCounters* pRefCounters, LOG_ERROR_AND_THROW(ErrorMsgSS.str().c_str()); } - auto DeviceCaps = pDeviceGL->GetDeviceCaps(); - if( DeviceCaps.bSeparableProgramSupported ) + if (pDeviceGL->GetDeviceCaps().bSeparableProgramSupported) { - m_GlProgObj.Create(); - - // GL_PROGRAM_SEPARABLE parameter must be set before linking! - glProgramParameteri( m_GlProgObj, GL_PROGRAM_SEPARABLE, GL_TRUE ); - glAttachShader( m_GlProgObj, ShaderObj ); - //With separable program objects, interfaces between shader stages may - //involve the outputs from one program object and the inputs from a - //second program object. For such interfaces, it is not possible to - //detect mismatches at link time, because the programs are linked - //separately. When each such program is linked, all inputs or outputs - //interfacing with another program stage are treated as active. The - //linker will generate an executable that assumes the presence of a - //compatible program on the other side of the interface. If a mismatch - //between programs occurs, no GL error will be generated, but some or all - //of the inputs on the interface will be undefined. - glLinkProgram( m_GlProgObj ); - CHECK_GL_ERROR( "glLinkProgram() failed" ); - int IsLinked = GL_FALSE; - glGetProgramiv( m_GlProgObj, GL_LINK_STATUS, (int *)&IsLinked ); - CHECK_GL_ERROR( "glGetProgramiv() failed" ); - if( !IsLinked ) - { - int LengthWithNull = 0, Length = 0; - // Notice that glGetProgramiv is used to get the length for a shader program, not glGetShaderiv. - // The length of the info log includes a null terminator. - glGetProgramiv( m_GlProgObj, GL_INFO_LOG_LENGTH, &LengthWithNull ); - - // The maxLength includes the NULL character - std::vector shaderProgramInfoLog( LengthWithNull ); - - // Notice that glGetProgramInfoLog is used, not glGetShaderInfoLog. - glGetProgramInfoLog( m_GlProgObj, LengthWithNull, &Length, &shaderProgramInfoLog[0] ); - VERIFY( Length == LengthWithNull-1, "Incorrect program info log len" ); - LOG_ERROR_AND_THROW( "Failed to link shader program:\n", &shaderProgramInfoLog[0], '\n'); - } - - glDetachShader( m_GlProgObj, ShaderObj ); - - // glDeleteShader() deletes the shader immediately if it is not attached to any program - // object. Otherwise, the shader is flagged for deletion and will be deleted when it is - // no longer attached to any program object. If an object is flagged for deletion, its - // boolean status bit DELETE_STATUS is set to true - ShaderObj.Release(); - - m_GlProgObj.InitResources(pDeviceGL, m_Desc.ShaderType, *this); - } - else - { - m_GLShaderObj = std::move( ShaderObj ); + IShader* ThisShader[] = {this}; + GLObjectWrappers::GLProgramObj Program = LinkProgram(ThisShader, 1, true); + Uint32 UniformBufferBinding = 0; + Uint32 SamplerBinding = 0; + Uint32 ImageBinding = 0; + Uint32 StorageBufferBinding = 0; + auto pImmediateCtx = m_pDevice->GetImmediateContext(); + VERIFY_EXPR(pImmediateCtx); + auto& GLState = pImmediateCtx.RawPtr()->GetContextState(); + m_Resources.LoadUniforms(m_Desc.ShaderType, Program, GLState, UniformBufferBinding, SamplerBinding, ImageBinding, StorageBufferBinding); } } @@ -174,27 +131,86 @@ ShaderGLImpl::~ShaderGLImpl() IMPLEMENT_QUERY_INTERFACE( ShaderGLImpl, IID_ShaderGL, TShaderBase ) + +GLObjectWrappers::GLProgramObj ShaderGLImpl::LinkProgram(IShader** ppShaders, Uint32 NumShaders, bool IsSeparableProgram) +{ + VERIFY(!IsSeparableProgram || NumShaders == 1, "Number of shaders must be 1 when separable program is created"); + + GLObjectWrappers::GLProgramObj GLProg(true); + + // GL_PROGRAM_SEPARABLE parameter must be set before linking! + if (IsSeparableProgram) + glProgramParameteri(GLProg, GL_PROGRAM_SEPARABLE, GL_TRUE); + + for (Uint32 i = 0; i < NumShaders; ++i) + { + auto* pCurrShader = ValidatedCast(ppShaders[i]); + glAttachShader(GLProg, pCurrShader->m_GLShaderObj); + CHECK_GL_ERROR("glAttachShader() failed"); + } + + //With separable program objects, interfaces between shader stages may + //involve the outputs from one program object and the inputs from a + //second program object. For such interfaces, it is not possible to + //detect mismatches at link time, because the programs are linked + //separately. When each such program is linked, all inputs or outputs + //interfacing with another program stage are treated as active. The + //linker will generate an executable that assumes the presence of a + //compatible program on the other side of the interface. If a mismatch + //between programs occurs, no GL error will be generated, but some or all + //of the inputs on the interface will be undefined. + glLinkProgram(GLProg); + CHECK_GL_ERROR("glLinkProgram() failed"); + int IsLinked = GL_FALSE; + glGetProgramiv(GLProg, GL_LINK_STATUS, &IsLinked); + CHECK_GL_ERROR("glGetProgramiv() failed"); + if (!IsLinked) + { + int LengthWithNull = 0, Length = 0; + // Notice that glGetProgramiv is used to get the length for a shader program, not glGetShaderiv. + // The length of the info log includes a null terminator. + glGetProgramiv(GLProg, GL_INFO_LOG_LENGTH, &LengthWithNull); + + // The maxLength includes the NULL character + std::vector shaderProgramInfoLog(LengthWithNull); + + // Notice that glGetProgramInfoLog is used, not glGetShaderInfoLog. + glGetProgramInfoLog(GLProg, LengthWithNull, &Length, shaderProgramInfoLog.data()); + VERIFY(Length == LengthWithNull-1, "Incorrect program info log len"); + LOG_ERROR_MESSAGE("Failed to link shader program:\n", shaderProgramInfoLog.data(), '\n'); + UNEXPECTED("glLinkProgram failed"); + } + + for (Uint32 i = 0; i < NumShaders; ++i) + { + auto* pCurrShader = ValidatedCast(ppShaders[i]); + glDetachShader(GLProg, pCurrShader->m_GLShaderObj); + CHECK_GL_ERROR("glDetachShader() failed"); + } + + return GLProg; +} + Uint32 ShaderGLImpl::GetResourceCount()const { - Uint32 ResCount = 0; - if (m_GlProgObj) + if (m_pDevice->GetDeviceCaps().bSeparableProgramSupported) { - return m_GlProgObj.GetResources().GetVariableCount(); + return m_Resources.GetVariableCount(); } else { LOG_WARNING_MESSAGE("Shader resource queries are not available when separate shader objects are unsupported"); + return 0; } - return ResCount; } ShaderResourceDesc ShaderGLImpl::GetResource(Uint32 Index)const { ShaderResourceDesc ResourceDesc; - if (m_GlProgObj) + if (m_pDevice->GetDeviceCaps().bSeparableProgramSupported) { DEV_CHECK_ERR(Index < GetResourceCount(), "Index is out of range"); - ResourceDesc = m_GlProgObj.GetResources().GetVariable(Index)->GetResourceDesc(); + ResourceDesc = m_Resources.GetResourceDesc(Index); } else { diff --git a/Graphics/GraphicsEngineOpenGL/src/ShaderResourceBindingGLImpl.cpp b/Graphics/GraphicsEngineOpenGL/src/ShaderResourceBindingGLImpl.cpp index 63b7e40f..bcc2482a 100644 --- a/Graphics/GraphicsEngineOpenGL/src/ShaderResourceBindingGLImpl.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/ShaderResourceBindingGLImpl.cpp @@ -30,89 +30,48 @@ namespace Diligent { -ShaderResourceBindingGLImpl::ShaderResourceBindingGLImpl(IReferenceCounters* pRefCounters, PipelineStateGLImpl* pPSO) : +ShaderResourceBindingGLImpl::ShaderResourceBindingGLImpl(IReferenceCounters* pRefCounters, + PipelineStateGLImpl* pPSO, + GLProgramResources* ProgramResources, + Uint32 NumPrograms) : TBase (pRefCounters, pPSO), - m_Resources(pPSO->GetGLProgram() == 0 ? pPSO->GetNumShaders() : 1) + m_ResourceLayout(*this) { + pPSO->InitializeSRBResourceCache(m_ResourceCache); + const SHADER_RESOURCE_VARIABLE_TYPE SRBVarTypes[] = {SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE, SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC}; - if (IsUsingSeparatePrograms()) - { - for (Uint32 s = 0; s < pPSO->GetNumShaders(); ++s) - { - auto* pShaderGL = pPSO->GetShader(s); - m_Resources[s].Clone(pPSO->GetDevice(), *this, pShaderGL->GetGlProgram().GetResources(), pPSO->GetDesc().ResourceLayout, SRBVarTypes, _countof(SRBVarTypes)); - const auto ShaderType = pShaderGL->GetDesc().ShaderType; - const auto ShaderTypeInd = GetShaderTypeIndex(ShaderType); - m_ResourceIndex[ShaderTypeInd] = static_cast(s); - } - } - else - { - m_Resources[0].Clone(pPSO->GetDevice(), *this, pPSO->GetGLProgram().GetResources(), pPSO->GetDesc().ResourceLayout, SRBVarTypes, _countof(SRBVarTypes)); - } + const auto& ResourceLayout = pPSO->GetDesc().ResourceLayout; + m_ResourceLayout.Initialize(ProgramResources, NumPrograms, ResourceLayout, SRBVarTypes, _countof(SRBVarTypes), &m_ResourceCache); } ShaderResourceBindingGLImpl::~ShaderResourceBindingGLImpl() { + m_ResourceCache.Destroy(GetRawAllocator()); } IMPLEMENT_QUERY_INTERFACE(ShaderResourceBindingGLImpl, IID_ShaderResourceBindingGL, TBase) -bool ShaderResourceBindingGLImpl::IsUsingSeparatePrograms()const -{ - return GetPipelineState()->GetGLProgram() == 0; -} - void ShaderResourceBindingGLImpl::BindResources(Uint32 ShaderFlags, IResourceMapping* pResMapping, Uint32 Flags) { - for(auto& Resource : m_Resources) - { - if ((Resource.GetShaderStages() & ShaderFlags)!=0) - Resource.BindResources(pResMapping, Flags); - } + m_ResourceLayout.BindResources(static_cast(ShaderFlags), pResMapping, Flags, m_ResourceCache); } IShaderResourceVariable* ShaderResourceBindingGLImpl::GetVariableByName(SHADER_TYPE ShaderType, const char* Name) { - if (IsUsingSeparatePrograms()) - { - auto ShaderInd = m_ResourceIndex[GetShaderTypeIndex(ShaderType)]; - return ShaderInd >= 0 ? m_Resources[ShaderInd].GetVariable(Name) : nullptr; - } - else - { - return (m_Resources[0].GetShaderStages() & ShaderType) != 0 ? m_Resources[0].GetVariable(Name) : nullptr; - } + return m_ResourceLayout.GetShaderVariable(ShaderType, Name); } Uint32 ShaderResourceBindingGLImpl::GetVariableCount(SHADER_TYPE ShaderType) const { - if (IsUsingSeparatePrograms()) - { - auto ShaderInd = m_ResourceIndex[GetShaderTypeIndex(ShaderType)]; - return ShaderInd >= 0 ? m_Resources[ShaderInd].GetVariableCount() : 0; - } - else - { - return (m_Resources[0].GetShaderStages() & ShaderType) != 0 ? m_Resources[0].GetVariableCount() : 0; - } + return m_ResourceLayout.GetNumVariables(ShaderType); } IShaderResourceVariable* ShaderResourceBindingGLImpl::GetVariableByIndex(SHADER_TYPE ShaderType, Uint32 Index) { - if (IsUsingSeparatePrograms()) - { - auto ShaderInd = m_ResourceIndex[GetShaderTypeIndex(ShaderType)]; - return ShaderInd >= 0 ? m_Resources[ShaderInd].GetVariable(Index) : 0; - } - else - { - return (m_Resources[0].GetShaderStages() & ShaderType) != 0 ? m_Resources[0].GetVariable(Index) : nullptr; - } + return m_ResourceLayout.GetShaderVariable(ShaderType, Index); } -static GLProgramResources NullProgramResources; -GLProgramResources& ShaderResourceBindingGLImpl::GetResources(Uint32 Ind, PipelineStateGLImpl* pdbgPSO) +const GLProgramResourceCache& ShaderResourceBindingGLImpl::GetResourceCache(PipelineStateGLImpl* pdbgPSO) { #ifdef _DEBUG if (pdbgPSO->IsIncompatibleWith(GetPipelineState())) @@ -120,12 +79,41 @@ GLProgramResources& ShaderResourceBindingGLImpl::GetResources(Uint32 Ind, Pipeli LOG_ERROR("Shader resource binding is incompatible with the currently bound pipeline state."); } #endif - return m_Resources[Ind]; + return m_ResourceCache; } void ShaderResourceBindingGLImpl::InitializeStaticResources(const IPipelineState* pPipelineState) { - // Do nothing + if (m_bIsStaticResourcesBound) + { + LOG_WARNING_MESSAGE("Static resources have already been initialized in this shader resource binding object. The operation will be ignored."); + return; + } + + if (pPipelineState == nullptr) + { + pPipelineState = GetPipelineState(); + } + else + { + DEV_CHECK_ERR(pPipelineState->IsCompatibleWith(GetPipelineState()), "The pipeline state is not compatible with this SRB"); + } + + const auto* pPSOGL = ValidatedCast(pPipelineState); + const auto& StaticResLayout = pPSOGL->GetStaticResourceLayout(); + +#ifdef DEVELOPMENT + if (!StaticResLayout.dvpVerifyBindings()) + { + LOG_ERROR_MESSAGE("Static resources in SRB of PSO '", pPSOGL->GetDesc().Name, "' will not be successfully initialized " + "because not all static resource bindings in shader '", pPSOGL->GetDesc().Name, "' are valid. " + "Please make sure you bind all static resources to PSO before calling InitializeStaticResources() " + "directly or indirectly by passing InitStaticResources=true to CreateShaderResourceBinding() method."); + } +#endif + + StaticResLayout.CopyResources(m_ResourceCache); + m_bIsStaticResourcesBound = true; } } diff --git a/Graphics/GraphicsEngineOpenGL/src/TexRegionRender.cpp b/Graphics/GraphicsEngineOpenGL/src/TexRegionRender.cpp index 9b5c56a8..3a82b31e 100644 --- a/Graphics/GraphicsEngineOpenGL/src/TexRegionRender.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/TexRegionRender.cpp @@ -145,10 +145,10 @@ namespace Diligent pDeviceGL->CreatePipelineState(PSODesc, &m_pPSO[Dim*3 + Fmt], IsInternalDeviceObject); } - m_pPSO[RESOURCE_DIM_TEX_2D*3]->CreateShaderResourceBinding(&m_pSRB); - m_pSRB->GetVariableByName(SHADER_TYPE_PIXEL, "cbConstants")->Set(m_pConstantBuffer); - m_pSrcTexVar = m_pSRB->GetVariableByName(SHADER_TYPE_PIXEL, "gSourceTex"); } + m_pPSO[RESOURCE_DIM_TEX_2D*3]->CreateShaderResourceBinding(&m_pSRB); + m_pSRB->GetVariableByName(SHADER_TYPE_PIXEL, "cbConstants")->Set(m_pConstantBuffer); + m_pSrcTexVar = m_pSRB->GetVariableByName(SHADER_TYPE_PIXEL, "gSourceTex"); } void TexRegionRender::SetStates( DeviceContextGLImpl *pCtxGL ) -- cgit v1.2.3