From bef8cae7057fad87afc17f94ea57c6c1119ff3ea Mon Sep 17 00:00:00 2001 From: azhirnov Date: Fri, 26 Feb 2021 22:33:44 +0300 Subject: OpenGL: added resource signature --- Graphics/GraphicsEngineOpenGL/CMakeLists.txt | 14 +- .../include/AsyncWritableResource.hpp | 73 +- .../GraphicsEngineOpenGL/include/BufferGLImpl.hpp | 2 +- .../include/DeviceContextGLImpl.hpp | 39 +- .../include/GLContextState.hpp | 7 +- .../include/GLPipelineResourceLayout.hpp | 471 ------------ .../include/GLProgramResourceCache.hpp | 276 ------- .../include/GLProgramResources.hpp | 521 ------------- .../GraphicsEngineOpenGL/include/GLStubsAndroid.h | 4 + .../include/PipelineResourceSignatureGLImpl.hpp | 222 ++++++ .../include/PipelineStateGLImpl.hpp | 142 ++-- .../include/RenderDeviceGLImpl.hpp | 19 + .../GraphicsEngineOpenGL/include/ShaderGLImpl.hpp | 17 +- .../include/ShaderResourceBindingGLImpl.hpp | 34 +- .../include/ShaderResourceCacheGL.hpp | 299 ++++++++ .../include/ShaderResourcesGL.hpp | 421 +++++++++++ .../include/ShaderVariableGL.hpp | 473 ++++++++++++ .../GraphicsEngineOpenGL/include/TextureBaseGL.hpp | 2 +- Graphics/GraphicsEngineOpenGL/src/BufferGLImpl.cpp | 23 +- .../src/DeviceContextGLImpl.cpp | 272 ++++--- Graphics/GraphicsEngineOpenGL/src/FBOCache.cpp | 4 +- .../GraphicsEngineOpenGL/src/GLContextState.cpp | 8 +- .../src/GLPipelineResourceLayout.cpp | 838 --------------------- .../src/GLProgramResourceCache.cpp | 122 --- .../src/GLProgramResources.cpp | 747 ------------------ .../src/PipelineResourceSignatureGLImpl.cpp | 758 +++++++++++++++++++ .../src/PipelineStateGLImpl.cpp | 613 ++++++++++----- .../src/RenderDeviceGLImpl.cpp | 73 +- Graphics/GraphicsEngineOpenGL/src/ShaderGLImpl.cpp | 30 +- .../src/ShaderResourceBindingGLImpl.cpp | 146 ++-- .../src/ShaderResourceCacheGL.cpp | 122 +++ .../GraphicsEngineOpenGL/src/ShaderResourcesGL.cpp | 721 ++++++++++++++++++ .../GraphicsEngineOpenGL/src/ShaderVariableGL.cpp | 733 ++++++++++++++++++ .../GraphicsEngineOpenGL/src/TextureBaseGL.cpp | 13 +- Graphics/GraphicsEngineOpenGL/src/VAOCache.cpp | 16 +- 35 files changed, 4754 insertions(+), 3521 deletions(-) delete mode 100644 Graphics/GraphicsEngineOpenGL/include/GLPipelineResourceLayout.hpp delete mode 100644 Graphics/GraphicsEngineOpenGL/include/GLProgramResourceCache.hpp delete mode 100644 Graphics/GraphicsEngineOpenGL/include/GLProgramResources.hpp create mode 100644 Graphics/GraphicsEngineOpenGL/include/PipelineResourceSignatureGLImpl.hpp create mode 100644 Graphics/GraphicsEngineOpenGL/include/ShaderResourceCacheGL.hpp create mode 100644 Graphics/GraphicsEngineOpenGL/include/ShaderResourcesGL.hpp create mode 100644 Graphics/GraphicsEngineOpenGL/include/ShaderVariableGL.hpp delete mode 100644 Graphics/GraphicsEngineOpenGL/src/GLPipelineResourceLayout.cpp delete mode 100644 Graphics/GraphicsEngineOpenGL/src/GLProgramResourceCache.cpp delete mode 100644 Graphics/GraphicsEngineOpenGL/src/GLProgramResources.cpp create mode 100644 Graphics/GraphicsEngineOpenGL/src/PipelineResourceSignatureGLImpl.cpp create mode 100644 Graphics/GraphicsEngineOpenGL/src/ShaderResourceCacheGL.cpp create mode 100644 Graphics/GraphicsEngineOpenGL/src/ShaderResourcesGL.cpp create mode 100644 Graphics/GraphicsEngineOpenGL/src/ShaderVariableGL.cpp (limited to 'Graphics/GraphicsEngineOpenGL') diff --git a/Graphics/GraphicsEngineOpenGL/CMakeLists.txt b/Graphics/GraphicsEngineOpenGL/CMakeLists.txt index 8002e214..6890b22d 100644 --- a/Graphics/GraphicsEngineOpenGL/CMakeLists.txt +++ b/Graphics/GraphicsEngineOpenGL/CMakeLists.txt @@ -13,12 +13,13 @@ set(INCLUDE include/GLContext.hpp include/GLContextState.hpp include/GLObjectWrapper.hpp - include/GLProgramResourceCache.hpp - include/GLPipelineResourceLayout.hpp - include/GLProgramResources.hpp + include/ShaderResourceCacheGL.hpp + include/ShaderVariableGL.hpp + include/ShaderResourcesGL.hpp include/GLTypeConversions.hpp include/pch.h include/PipelineStateGLImpl.hpp + include/PipelineResourceSignatureGLImpl.hpp include/QueryGLImpl.hpp include/RenderDeviceGLImpl.hpp include/RenderPassGLImpl.hpp @@ -67,11 +68,12 @@ set(SOURCE src/FramebufferGLImpl.cpp src/GLContextState.cpp src/GLObjectWrapper.cpp - src/GLProgramResourceCache.cpp - src/GLPipelineResourceLayout.cpp - src/GLProgramResources.cpp + src/ShaderResourceCacheGL.cpp + src/ShaderVariableGL.cpp + src/ShaderResourcesGL.cpp src/GLTypeConversions.cpp src/PipelineStateGLImpl.cpp + src/PipelineResourceSignatureGLImpl.cpp src/QueryGLImpl.cpp src/RenderDeviceGLImpl.cpp src/RenderPassGLImpl.cpp diff --git a/Graphics/GraphicsEngineOpenGL/include/AsyncWritableResource.hpp b/Graphics/GraphicsEngineOpenGL/include/AsyncWritableResource.hpp index a5cdefcb..3802afe1 100644 --- a/Graphics/GraphicsEngineOpenGL/include/AsyncWritableResource.hpp +++ b/Graphics/GraphicsEngineOpenGL/include/AsyncWritableResource.hpp @@ -30,40 +30,63 @@ namespace Diligent { +enum MEMORY_BARRIER : Uint32 +{ + MEMORY_BARRIER_NONE = 0, + MEMORY_BARRIER_ALL = GL_ALL_BARRIER_BITS, + + // Buffer barriers. + // Driver does not handle buffer write access in shader and write/read access to a persistent mapped memory. + MEMORY_BARRIER_VERTEX_BUFFER = GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT, // map/storage -> vertex + MEMORY_BARRIER_INDEX_BUFFER = GL_ELEMENT_ARRAY_BARRIER_BIT, // map/storage -> index + MEMORY_BARRIER_UNIFORM_BUFFER = GL_UNIFORM_BARRIER_BIT, // map/storage -> uniform + MEMORY_BARRIER_BUFFER_UPDATE = GL_BUFFER_UPDATE_BARRIER_BIT, // map/storage -> host read/write/map or copy + MEMORY_BARRIER_CLIENT_MAPPED_BUFFER = GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT, // map/storage -> map, only for persistent mapped memory without GL_MAP_COHERENT_BIT + MEMORY_BARRIER_STORAGE_BUFFER = GL_SHADER_STORAGE_BARRIER_BIT, // map/storage -> storage + MEMORY_BARRIER_INDIRECT_BUFFER = GL_COMMAND_BARRIER_BIT, // map/storage -> indirect + MEMORY_BARRIER_TEXEL_BUFFER = GL_TEXTURE_FETCH_BARRIER_BIT, // map/storage -> texel buffer fetch + MEMORY_BARRIER_PIXEL_BUFFER = GL_PIXEL_BUFFER_BARRIER_BIT, // map/storage -> copy to/from texture + MEMORY_BARRIER_IMAGE_BUFFER = GL_SHADER_IMAGE_ACCESS_BARRIER_BIT, // map/storage -> image buffer + MEMORY_BARRIER_ALL_BUFFER_BARRIERS = + MEMORY_BARRIER_VERTEX_BUFFER | + MEMORY_BARRIER_INDEX_BUFFER | + MEMORY_BARRIER_UNIFORM_BUFFER | + MEMORY_BARRIER_BUFFER_UPDATE | + MEMORY_BARRIER_CLIENT_MAPPED_BUFFER | + MEMORY_BARRIER_STORAGE_BUFFER | + MEMORY_BARRIER_INDIRECT_BUFFER | + MEMORY_BARRIER_TEXEL_BUFFER | + MEMORY_BARRIER_IMAGE_BUFFER, + + + // Texture barriers + MEMORY_BARRIER_TEXTURE_FETCH = GL_TEXTURE_FETCH_BARRIER_BIT, // storage -> fetch + MEMORY_BARRIER_STORAGE_IMAGE = GL_SHADER_IMAGE_ACCESS_BARRIER_BIT, // storage -> storage + MEMORY_BARRIER_TEXTURE_UPDATE = GL_TEXTURE_UPDATE_BARRIER_BIT, // storage -> host read/write or copy + MEMORY_BARRIER_FRAMEBUFFER = GL_FRAMEBUFFER_BARRIER_BIT, // storage -> framebuffer + MEMORY_BARRIER_ALL_TEXTURE_BARRIERS = + MEMORY_BARRIER_TEXTURE_FETCH | + MEMORY_BARRIER_STORAGE_IMAGE | + MEMORY_BARRIER_TEXTURE_UPDATE | + MEMORY_BARRIER_FRAMEBUFFER, +}; +DEFINE_FLAG_ENUM_OPERATORS(MEMORY_BARRIER); + + class AsyncWritableResource { public: AsyncWritableResource() noexcept {} - void SetPendingMemoryBarriers(Uint32 Barriers) { m_PendingMemoryBarriers |= Barriers; } - Uint32 GetPendingMemortBarriers() { return m_PendingMemoryBarriers; } + void SetPendingMemoryBarriers(MEMORY_BARRIER Barriers) { m_PendingMemoryBarriers |= Barriers; } + MEMORY_BARRIER GetPendingMemortBarriers() { return m_PendingMemoryBarriers; } private: friend class GLContextState; - void ResetPendingMemoryBarriers(Uint32 Barriers) { m_PendingMemoryBarriers = Barriers; } - void ClearPendingMemoryBarriers(Uint32 Barriers) { m_PendingMemoryBarriers &= ~Barriers; } - - // Buffer barriers: - // GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT - // GL_ELEMENT_ARRAY_BARRIER_BIT - // GL_UNIFORM_BARRIER_BIT - // GL_BUFFER_UPDATE_BARRIER_BIT - // GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT - // GL_SHADER_STORAGE_BARRIER_BIT - - // Texture barriers: - // GL_TEXTURE_FETCH_BARRIER_BIT - // GL_SHADER_IMAGE_ACCESS_BARRIER_BIT - // GL_PIXEL_BUFFER_BARRIER_BIT - // GL_TEXTURE_UPDATE_BARRIER_BIT - - // Misc barriers: - // GL_FRAMEBUFFER_BARRIER_BIT - // GL_TRANSFORM_FEEDBACK_BARRIER_BIT - // GL_ATOMIC_COUNTER_BARRIER_BIT - // GL_QUERY_BUFFER_BARRIER_BIT + void ResetPendingMemoryBarriers(MEMORY_BARRIER Barriers) { m_PendingMemoryBarriers = Barriers; } + void ClearPendingMemoryBarriers(MEMORY_BARRIER Barriers) { m_PendingMemoryBarriers &= ~Barriers; } - Uint32 m_PendingMemoryBarriers = 0; + MEMORY_BARRIER m_PendingMemoryBarriers = MEMORY_BARRIER_NONE; }; } // namespace Diligent diff --git a/Graphics/GraphicsEngineOpenGL/include/BufferGLImpl.hpp b/Graphics/GraphicsEngineOpenGL/include/BufferGLImpl.hpp index cd060f30..9074f887 100644 --- a/Graphics/GraphicsEngineOpenGL/include/BufferGLImpl.hpp +++ b/Graphics/GraphicsEngineOpenGL/include/BufferGLImpl.hpp @@ -74,7 +74,7 @@ public: void MapRange(GLContextState& CtxState, MAP_TYPE MapType, Uint32 MapFlags, Uint32 Offset, Uint32 Length, PVoid& pMappedData); void Unmap(GLContextState& CtxState); - void BufferMemoryBarrier(Uint32 RequiredBarriers, class GLContextState& GLContextState); + void BufferMemoryBarrier(MEMORY_BARRIER RequiredBarriers, class GLContextState& GLContextState); const GLObjectWrappers::GLBufferObj& GetGLHandle() { return m_GlBuffer; } diff --git a/Graphics/GraphicsEngineOpenGL/include/DeviceContextGLImpl.hpp b/Graphics/GraphicsEngineOpenGL/include/DeviceContextGLImpl.hpp index c59a968a..dc27008f 100644 --- a/Graphics/GraphicsEngineOpenGL/include/DeviceContextGLImpl.hpp +++ b/Graphics/GraphicsEngineOpenGL/include/DeviceContextGLImpl.hpp @@ -42,6 +42,7 @@ #include "PipelineStateGLImpl.hpp" #include "BottomLevelASBase.hpp" #include "TopLevelASBase.hpp" +#include "ShaderResourceBindingGLImpl.hpp" namespace Diligent { @@ -272,8 +273,6 @@ public: /// Implementation of IDeviceContextGL::UpdateCurrentGLContext(). virtual bool DILIGENT_CALL_TYPE UpdateCurrentGLContext() override final; - void BindProgramResources(Uint32& NewMemoryBarriers, IShaderResourceBinding* pResBinding); - GLContextState& GetContextState() { return m_ContextState; } void CommitRenderTargets(); @@ -296,10 +295,44 @@ private: __forceinline void PrepareForIndirectDraw(IBuffer* pAttribsBuffer); __forceinline void PostDraw(); + using TBindings = PipelineResourceSignatureGLImpl::TBindings; + void BindProgramResources(MEMORY_BARRIER& NewMemoryBarriers, const ShaderResourceBindingGLImpl* pShaderResBindingGL, TBindings& Bindings); + void BindProgramResources(); + +#ifdef DILIGENT_DEVELOPMENT + void DvpValidateCommittedShaderResources(); +#endif + void BeginSubpass(); void EndSubpass(); - Uint32 m_CommitedResourcesTentativeBarriers = 0; + struct SRBState + { + // Do not use strong references! + std::array SRBs = {}; + + using Bitfield = Uint8; + static_assert(sizeof(Bitfield) * 8 >= MAX_RESOURCE_SIGNATURES, "not enought space to store MAX_RESOURCE_SIGNATURES bits"); + + Bitfield ActiveSRBMask = 0; // Indicates which SRBs are active in current PSO + Bitfield StaleSRBMask = 0; // Indicates stale SRBs that have descriptor sets that need to be bound + +#ifdef DILIGENT_DEVELOPMENT + bool CommittedResourcesValidated = false; + + // Binding offsets that was used at last BindProgramResources() call. + std::array BoundResOffsets = {}; +#endif + + SRBState() + {} + + void SetStaleSRBBit(Uint32 Index) { StaleSRBMask |= static_cast(1u << Index); } + void ClearStaleSRBBit(Uint32 Index) { StaleSRBMask &= static_cast(~(1u << Index)); } + + } m_BindInfo; + + MEMORY_BARRIER m_CommitedResourcesTentativeBarriers = MEMORY_BARRIER_NONE; std::vector m_BoundWritableTextures; std::vector m_BoundWritableBuffers; diff --git a/Graphics/GraphicsEngineOpenGL/include/GLContextState.hpp b/Graphics/GraphicsEngineOpenGL/include/GLContextState.hpp index 31e08912..084125a3 100644 --- a/Graphics/GraphicsEngineOpenGL/include/GLContextState.hpp +++ b/Graphics/GraphicsEngineOpenGL/include/GLContextState.hpp @@ -31,6 +31,7 @@ #include "GLObjectWrapper.hpp" #include "UniqueIdentifier.hpp" #include "GLContext.hpp" +#include "AsyncWritableResource.hpp" namespace Diligent { @@ -55,8 +56,8 @@ public: void BindImage (Uint32 Index, class BufferViewGLImpl* pBuffView, GLenum Access, GLenum Format); void BindStorageBlock (Int32 Index, const GLObjectWrappers::GLBufferObj& Buff, GLintptr Offset, GLsizeiptr Size); - void EnsureMemoryBarrier(Uint32 RequiredBarriers, class AsyncWritableResource *pRes = nullptr); - void SetPendingMemoryBarriers(Uint32 PendingBarriers); + void EnsureMemoryBarrier(MEMORY_BARRIER RequiredBarriers, class AsyncWritableResource *pRes = nullptr); + void SetPendingMemoryBarriers(MEMORY_BARRIER PendingBarriers); void EnableDepthTest (bool bEnable); void EnableDepthWrites (bool bEnable); @@ -202,7 +203,7 @@ private: }; std::vector m_BoundStorageBlocks; - Uint32 m_PendingMemoryBarriers = 0; + MEMORY_BARRIER m_PendingMemoryBarriers = MEMORY_BARRIER_NONE; class EnableStateHelper { diff --git a/Graphics/GraphicsEngineOpenGL/include/GLPipelineResourceLayout.hpp b/Graphics/GraphicsEngineOpenGL/include/GLPipelineResourceLayout.hpp deleted file mode 100644 index 701d2492..00000000 --- a/Graphics/GraphicsEngineOpenGL/include/GLPipelineResourceLayout.hpp +++ /dev/null @@ -1,471 +0,0 @@ -/* - * Copyright 2019-2021 Diligent Graphics LLC - * Copyright 2015-2019 Egor Yusov - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * In no event and under no legal theory, whether in tort (including negligence), - * contract, or otherwise, unless required by applicable law (such as deliberate - * and grossly negligent acts) or agreed to in writing, shall any Contributor be - * liable for any damages, including any direct, indirect, special, incidental, - * or consequential damages of any character arising as a result of this License or - * out of the use or inability to use the software (including but not limited to damages - * for loss of goodwill, work stoppage, computer failure or malfunction, or any and - * all other commercial damages or losses), even if such Contributor has been advised - * of the possibility of such damages. - */ - -#pragma once - -// GLPipelineResourceLayout class manages resource bindings for all stages in a pipeline - -// -// -// To program resource cache -// -// A A A A A A A A -// | | | | | | | | -// Binding Binding Binding Binding Binding Binding Binding Binding -// ___________________ ____|__________|__________________|________|______________|___________|______________|____________|____________ -// | | | | | | | | | | | | | | | -// |GLProgramResources |--------------->| UB[0] | UB[1] | ... | Sam[0] | Sam[1] | ... | Img[0] | Img[1] | ... | SSBOs[0] | SSBOs[1] | ... | -// |___________________| |__________|__________|_______|________|________|_______|________|________|_______|__________|__________|_______| -// A A A A -// | | | | -// Ref Ref Ref Ref -// .-==========================-. _____|____________________________________|________________________|____________________________|______________ -// || || | | | | | | | | | | | -// __|| GLPipelineResourceLayout ||------->| UBInfo[0] | UBInfo[1] | ... | SamInfo[0] | SamInfo[1] | ... | ImgInfo[0] | ... | SSBO[0] | ... | -// | || || |___________|___________|_______|____________|____________|_______|____________|_________|___________|__________| -// | '-==========================-' / \ -// | Ref Ref -// | / \ -// | ___________________ ________V________________________________________________V_____________________________________________________ -// | | | | | | | | | | | | | | | | -// | |GLProgramResources |--------------->| UB[0] | UB[1] | ... | Sam[0] | Sam[1] | ... | Img[0] | Img[1] | ... | SSBOs[0] | SSBOs[1] | ... | -// | |___________________| |__________|__________|_______|________|________|_______|________|________|_______|__________|__________|_______| -// | | | | | | | | | -// | Binding Binding Binding Binding Binding Binding Binding Binding -// | | | | | | | | | -// | _______________________ ____V___________V________________V_________V________________V________V________________V___________V_____________ -// | | | | | | | | -// '-->|GLProgramResourceCache |----------->| Uinform Buffers | Samplers | Images | Storge Buffers | -// |_______________________| |___________________________|___________________________|___________________________|___________________________| -// -// -// Note that GLProgramResources are kept by PipelineStateGLImpl. GLPipelineResourceLayout class is either part of the same PSO class, -// or part of ShaderResourceBindingGLImpl object that keeps a strong reference to the pipeline. So all references from GLVariableBase -// are always valid. - -#include - -#include "Object.h" -#include "ShaderResourceVariableBase.hpp" -#include "GLProgramResources.hpp" -#include "GLProgramResourceCache.hpp" - -namespace Diligent -{ - -class GLPipelineResourceLayout -{ -public: - GLPipelineResourceLayout(IObject& Owner) : - m_Owner(Owner) - { - m_ProgramIndex.fill(-1); - } - - ~GLPipelineResourceLayout(); - - // No copies, only moves are allowed - // clang-format off - GLPipelineResourceLayout (const GLPipelineResourceLayout&) = delete; - GLPipelineResourceLayout& operator = (const GLPipelineResourceLayout&) = delete; - GLPipelineResourceLayout ( GLPipelineResourceLayout&&) = default; - GLPipelineResourceLayout& operator = ( GLPipelineResourceLayout&&) = delete; - // clang-format on - - void Initialize(GLProgramResources* ProgramResources, - Uint32 NumPrograms, - PIPELINE_TYPE PipelineType, - 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 ImtblSamplerIdx) : - // clang-format off - TBase {ParentLayout}, - m_Attribs {ResourceAttribs }, - m_VariableType {VariableType }, - m_ImtblSamplerIdx {ImtblSamplerIdx} - // clang-format on - { - VERIFY_EXPR(ImtblSamplerIdx < 0 || ResourceAttribs.ResourceType == SHADER_RESOURCE_TYPE_TEXTURE_SRV); - } - - virtual SHADER_RESOURCE_VARIABLE_TYPE DILIGENT_CALL_TYPE GetType() const override final - { - return m_VariableType; - } - - virtual void DILIGENT_CALL_TYPE GetResourceDesc(ShaderResourceDesc& ResourceDesc) const override final - { - ResourceDesc = m_Attribs.GetResourceDesc(); - } - - virtual Uint32 DILIGENT_CALL_TYPE GetIndex() const override final - { - return m_ParentResLayout.GetVariableIndex(*this); - } - - const GLProgramResources::GLResourceAttribs& m_Attribs; - const SHADER_RESOURCE_VARIABLE_TYPE m_VariableType; - const Int32 m_ImtblSamplerIdx; - }; - - - 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 DILIGENT_CALL_TYPE Set(IDeviceObject* pObject) override final - { - BindResource(pObject, 0); - } - - virtual void DILIGENT_CALL_TYPE SetArray(IDeviceObject* const* ppObjects, - Uint32 FirstElement, - Uint32 NumElements) override final - { - VerifyAndCorrectSetArrayArguments(m_Attribs.Name, m_Attribs.ArraySize, FirstElement, NumElements); - for (Uint32 elem = 0; elem < NumElements; ++elem) - BindResource(ppObjects[elem], FirstElement + elem); - } - - virtual bool DILIGENT_CALL_TYPE 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 ImtblSamplerIdx) : - GLVariableBase{ResourceAttribs, ParentResLayout, VariableType, ImtblSamplerIdx} - {} - - // Non-virtual function - void BindResource(IDeviceObject* pObject, Uint32 ArrayIndex); - - virtual void DILIGENT_CALL_TYPE Set(IDeviceObject* pObject) override final - { - BindResource(pObject, 0); - } - - virtual void DILIGENT_CALL_TYPE SetArray(IDeviceObject* const* ppObjects, - Uint32 FirstElement, - Uint32 NumElements) override final - { - VerifyAndCorrectSetArrayArguments(m_Attribs.Name, m_Attribs.ArraySize, FirstElement, NumElements); - for (Uint32 elem = 0; elem < NumElements; ++elem) - BindResource(ppObjects[elem], FirstElement + elem); - } - - virtual bool DILIGENT_CALL_TYPE 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 DILIGENT_CALL_TYPE Set(IDeviceObject* pObject) override final - { - BindResource(pObject, 0); - } - - virtual void DILIGENT_CALL_TYPE SetArray(IDeviceObject* const* ppObjects, - Uint32 FirstElement, - Uint32 NumElements) override final - { - VerifyAndCorrectSetArrayArguments(m_Attribs.Name, m_Attribs.ArraySize, FirstElement, NumElements); - for (Uint32 elem = 0; elem < NumElements; ++elem) - BindResource(ppObjects[elem], FirstElement + elem); - } - - virtual bool DILIGENT_CALL_TYPE 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 DILIGENT_CALL_TYPE Set(IDeviceObject* pObject) override final - { - BindResource(pObject, 0); - } - - virtual void DILIGENT_CALL_TYPE SetArray(IDeviceObject* const* ppObjects, - Uint32 FirstElement, - Uint32 NumElements) override final - { - VerifyAndCorrectSetArrayArguments(m_Attribs.Name, m_Attribs.ArraySize, FirstElement, NumElements); - for (Uint32 elem = 0; elem < NumElements; ++elem) - BindResource(ppObjects[elem], FirstElement + elem); - } - - virtual bool DILIGENT_CALL_TYPE 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 DILIGENT_DEVELOPMENT - bool dvpVerifyBindings(const GLProgramResourceCache& ResourceCache) 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; - - // clang-format off - 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); } - // clang-format on - - template Uint32 GetNumResources() const; - - 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: - // clang-format off -/* 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 = {}; -/*45*/ Uint8 m_NumPrograms = 0; -/*46*/ Uint8 m_PipelineType = 255u; -/*47*/ -/*48*/ // End of structure - // clang-format on - - template OffsetType GetResourceOffset() const; - - 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; -}; - - - -template <> -inline Uint32 GLPipelineResourceLayout::GetNumResources() const -{ - return GetNumUBs(); -} - -template <> -inline Uint32 GLPipelineResourceLayout::GetNumResources() const -{ - return GetNumSamplers(); -} - -template <> -inline Uint32 GLPipelineResourceLayout::GetNumResources() const -{ - return GetNumImages(); -} - -template <> -inline Uint32 GLPipelineResourceLayout::GetNumResources() const -{ - return GetNumStorageBuffers(); -} - - - -template <> -inline GLPipelineResourceLayout::OffsetType GLPipelineResourceLayout:: - GetResourceOffset() const -{ - return m_UBOffset; -} - -template <> -inline GLPipelineResourceLayout::OffsetType GLPipelineResourceLayout:: - GetResourceOffset() const -{ - return m_SamplerOffset; -} - -template <> -inline GLPipelineResourceLayout::OffsetType GLPipelineResourceLayout:: - GetResourceOffset() const -{ - return m_ImageOffset; -} - -template <> -inline GLPipelineResourceLayout::OffsetType GLPipelineResourceLayout:: - GetResourceOffset() const -{ - return m_StorageBufferOffset; -} - -} // namespace Diligent diff --git a/Graphics/GraphicsEngineOpenGL/include/GLProgramResourceCache.hpp b/Graphics/GraphicsEngineOpenGL/include/GLProgramResourceCache.hpp deleted file mode 100644 index ebff4fed..00000000 --- a/Graphics/GraphicsEngineOpenGL/include/GLProgramResourceCache.hpp +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright 2019-2021 Diligent Graphics LLC - * Copyright 2015-2019 Egor Yusov - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * In no event and under no legal theory, whether in tort (including negligence), - * contract, or otherwise, unless required by applicable law (such as deliberate - * and grossly negligent acts) or agreed to in writing, shall any Contributor be - * liable for any damages, including any direct, indirect, special, incidental, - * or consequential damages of any character arising as a result of this License or - * out of the use or inability to use the software (including but not limited to damages - * for loss of goodwill, work stoppage, computer failure or malfunction, or any and - * all other commercial damages or losses), even if such Contributor has been advised - * of the possibility of such damages. - */ - -#pragma once - -#include "BufferGLImpl.hpp" -#include "TextureBaseGL.hpp" -#include "SamplerGLImpl.hpp" - -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 Storage Blocks | -// |----------------------------------------------------|--------------------------|---------------------------| -// | 0 | 1 | ... | UBCount-1 | 0 | 1 | ...| SmpCount-1 | 0 | 1 | ... | ImgCount-1 | 0 | 1 | ... | SBOCount-1 | -// ----------------------------------------------------------------------------------------------------------- -// -class GLProgramResourceCache -{ -public: - GLProgramResourceCache() noexcept - {} - - ~GLProgramResourceCache(); - - // clang-format off - GLProgramResourceCache (const GLProgramResourceCache&) = delete; - GLProgramResourceCache& operator = (const GLProgramResourceCache&) = delete; - GLProgramResourceCache (GLProgramResourceCache&&) = delete; - GLProgramResourceCache& operator = (GLProgramResourceCache&&) = delete; - // clang-format on - - /// 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 - { - /// We 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, IMemoryAllocator& MemAllocator); - void Destroy(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 SetImmutableSampler(Uint32 Binding, ISampler* pImtblSampler) - { - GetSampler(Binding).pSampler = ValidatedCast(pImtblSampler); - } - - 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 = GetConstUB(Binding); - return UB.pBuffer; - } - - bool IsSamplerBound(Uint32 Binding, bool dbgIsTextureView) const - { - if (Binding >= GetSamplerCount()) - return false; - - const auto& Sampler = GetConstSampler(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 = GetConstImage(Binding); - VERIFY_EXPR(dbgIsTextureView || Image.pTexture == nullptr); - return Image.pView; - } - - bool IsSSBOBound(Uint32 Binding) const - { - if (Binding >= GetSSBOCount()) - return false; - - const auto& SSBO = GetConstSSBO(Binding); - return SSBO.pBufferView; - } - - // clang-format off - 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); } - // clang-format on - - const CachedUB& GetConstUB(Uint32 Binding) const - { - VERIFY(Binding < GetUBCount(), "Uniform buffer binding (", Binding, ") is out of range"); - return reinterpret_cast(m_pResourceData + m_UBsOffset)[Binding]; - } - - const CachedResourceView& GetConstSampler(Uint32 Binding) const - { - VERIFY(Binding < GetSamplerCount(), "Sampler binding (", Binding, ") is out of range"); - return reinterpret_cast(m_pResourceData + m_SmplrsOffset)[Binding]; - } - - const CachedResourceView& GetConstImage(Uint32 Binding) const - { - VERIFY(Binding < GetImageCount(), "Image buffer binding (", Binding, ") is out of range"); - return reinterpret_cast(m_pResourceData + m_ImgsOffset)[Binding]; - } - - const CachedSSBO& GetConstSSBO(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)->GetConstUB(Binding)); - } - - CachedResourceView& GetSampler(Uint32 Binding) - { - return const_cast(const_cast(this)->GetConstSampler(Binding)); - } - - CachedResourceView& GetImage(Uint32 Binding) - { - return const_cast(const_cast(this)->GetConstImage(Binding)); - } - - CachedSSBO& GetSSBO(Uint32 Binding) - { - return const_cast(const_cast(this)->GetConstSSBO(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 DILIGENT_DEBUG - IMemoryAllocator* m_pdbgMemoryAllocator = nullptr; -#endif -}; - -} // namespace Diligent diff --git a/Graphics/GraphicsEngineOpenGL/include/GLProgramResources.hpp b/Graphics/GraphicsEngineOpenGL/include/GLProgramResources.hpp deleted file mode 100644 index 5b60cec2..00000000 --- a/Graphics/GraphicsEngineOpenGL/include/GLProgramResources.hpp +++ /dev/null @@ -1,521 +0,0 @@ -/* - * Copyright 2019-2021 Diligent Graphics LLC - * Copyright 2015-2019 Egor Yusov - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * In no event and under no legal theory, whether in tort (including negligence), - * contract, or otherwise, unless required by applicable law (such as deliberate - * and grossly negligent acts) or agreed to in writing, shall any Contributor be - * liable for any damages, including any direct, indirect, special, incidental, - * or consequential damages of any character arising as a result of this License or - * out of the use or inability to use the software (including but not limited to damages - * for loss of goodwill, work stoppage, computer failure or malfunction, or any and - * all other commercial damages or losses), even if such Contributor has been advised - * of the possibility of such damages. - */ - -#pragma once - -// GLProgramResources class allocates single continuous chunk of memory to store all program resources, as follows: -// -// -// m_UniformBuffers m_Samplers m_Images m_StorageBlocks -// | | | | | | -// | UB[0] ... UB[Nu-1] | Sam[0] ... Sam[Ns-1] | Img[0] ... Img[Ni-1] | SB[0] ... SB[Nsb-1] | Resource Names | -// -// Nu - number of uniform buffers -// Ns - number of samplers -// Ni - number of images -// Nsb - number of storage blocks - -#include - -#include "Object.h" -#include "StringPool.hpp" -#include "HashUtils.hpp" -#include "ShaderResourceVariableBase.hpp" - -namespace Diligent -{ - -class GLProgramResources -{ -public: - GLProgramResources() {} - ~GLProgramResources(); - // clang-format off - GLProgramResources (GLProgramResources&& Program)noexcept; - - GLProgramResources (const GLProgramResources&) = delete; - GLProgramResources& operator = (const GLProgramResources&) = delete; - GLProgramResources& operator = ( GLProgramResources&&) = delete; - // clang-format on - - /// Loads program uniforms and assigns bindings - void LoadUniforms(SHADER_TYPE ShaderStages, - const GLObjectWrappers::GLProgramObj& GLProgram, - class GLContextState& State, - Uint32& UniformBufferBinding, - Uint32& SamplerBinding, - Uint32& ImageBinding, - Uint32& StorageBufferBinding); - - struct GLResourceAttribs - { - // clang-format off -/* 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 - // clang-format on - - GLResourceAttribs(const Char* _Name, - SHADER_TYPE _ShaderStages, - SHADER_RESOURCE_TYPE _ResourceType, - Uint32 _Binding, - Uint32 _ArraySize) noexcept : - // clang-format off - Name {_Name }, - ShaderStages {_ShaderStages}, - ResourceType {_ResourceType}, - Binding {_Binding }, - ArraySize {_ArraySize } - // clang-format on - { - 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"); - } - - GLResourceAttribs(const GLResourceAttribs& Attribs, - StringPool& NamesPool) noexcept : - // clang-format off - GLResourceAttribs - { - NamesPool.CopyString(Attribs.Name), - Attribs.ShaderStages, - Attribs.ResourceType, - Attribs.Binding, - Attribs.ArraySize - } - // clang-format on - { - } - - bool IsCompatibleWith(const GLResourceAttribs& Var)const - { - // clang-format off - return ShaderStages == Var.ShaderStages && - ResourceType == Var.ResourceType && - Binding == Var.Binding && - ArraySize == Var.ArraySize; - // clang-format on - } - - size_t GetHash() const - { - return ComputeHash(static_cast(ShaderStages), static_cast(ResourceType), Binding, ArraySize); - } - - String GetPrintName(Uint32 ArrayInd) const - { - VERIFY_EXPR(ArrayInd < ArraySize); - if (ArraySize > 1) - return String(Name) + '[' + std::to_string(ArrayInd) + ']'; - else - return Name; - } - - ShaderResourceDesc GetResourceDesc() const - { - ShaderResourceDesc ResourceDesc; - ResourceDesc.Name = Name; - ResourceDesc.ArraySize = ArraySize; - ResourceDesc.Type = ResourceType; - return ResourceDesc; - } - - RESOURCE_DIMENSION GetResourceDimension() const - { - return RESOURCE_DIM_UNDEFINED; - } - - bool IsMultisample() const - { - return false; - } - }; - - struct UniformBufferInfo final : GLResourceAttribs - { - // clang-format off - UniformBufferInfo (const UniformBufferInfo&) = delete; - UniformBufferInfo& operator= (const UniformBufferInfo&) = delete; - UniformBufferInfo ( UniformBufferInfo&&) = default; - UniformBufferInfo& operator= ( UniformBufferInfo&&) = delete; - - 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 } - {} - // clang-format on - - bool IsCompatibleWith(const UniformBufferInfo& UB) const - { - return UBIndex == UB.UBIndex && - GLResourceAttribs::IsCompatibleWith(UB); - } - - size_t GetHash() const - { - return ComputeHash(UBIndex, GLResourceAttribs::GetHash()); - } - - const GLuint UBIndex; - }; - static_assert((sizeof(UniformBufferInfo) % sizeof(void*)) == 0, "sizeof(UniformBufferInfo) must be multiple of sizeof(void*)"); - - - struct SamplerInfo final : GLResourceAttribs - { - // clang-format off - SamplerInfo (const SamplerInfo&) = delete; - SamplerInfo& operator= (const SamplerInfo&) = delete; - SamplerInfo ( SamplerInfo&&) = default; - SamplerInfo& operator= ( SamplerInfo&&) = delete; - - 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} - {} - - SamplerInfo(const SamplerInfo& Sam, - StringPool& NamesPool)noexcept : - GLResourceAttribs{Sam, NamesPool}, - Location {Sam.Location }, - SamplerType {Sam.SamplerType} - {} - - bool IsCompatibleWith(const SamplerInfo& Sam)const - { - return Location == Sam.Location && - SamplerType == Sam.SamplerType && - GLResourceAttribs::IsCompatibleWith(Sam); - } - // clang-format on - - size_t GetHash() const - { - return ComputeHash(Location, SamplerType, GLResourceAttribs::GetHash()); - } - - const GLint Location; - const GLenum SamplerType; - }; - static_assert((sizeof(SamplerInfo) % sizeof(void*)) == 0, "sizeof(SamplerInfo) must be multiple of sizeof(void*)"); - - - struct ImageInfo final : GLResourceAttribs - { - // clang-format off - ImageInfo (const ImageInfo&) = delete; - ImageInfo& operator= (const ImageInfo&) = delete; - ImageInfo ( ImageInfo&&) = default; - ImageInfo& operator= ( ImageInfo&&) = delete; - - 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& Img)const - { - return Location == Img.Location && - ImageType == Img.ImageType && - GLResourceAttribs::IsCompatibleWith(Img); - } - // clang-format on - - size_t GetHash() const - { - return ComputeHash(Location, ImageType, GLResourceAttribs::GetHash()); - } - - const GLint Location; - const GLenum ImageType; - }; - static_assert((sizeof(ImageInfo) % sizeof(void*)) == 0, "sizeof(ImageInfo) must be multiple of sizeof(void*)"); - - - struct StorageBlockInfo final : GLResourceAttribs - { - // clang-format off - StorageBlockInfo (const StorageBlockInfo&) = delete; - StorageBlockInfo& operator= (const StorageBlockInfo&) = delete; - StorageBlockInfo ( StorageBlockInfo&&) = default; - StorageBlockInfo& operator= ( StorageBlockInfo&&) = delete; - - 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} - {} - - StorageBlockInfo(const StorageBlockInfo& SB, - StringPool& NamesPool)noexcept : - GLResourceAttribs{SB, NamesPool}, - SBIndex {SB.SBIndex} - {} - - bool IsCompatibleWith(const StorageBlockInfo& SB)const - { - return SBIndex == SB.SBIndex && - GLResourceAttribs::IsCompatibleWith(SB); - } - // clang-format on - - size_t GetHash() const - { - return ComputeHash(SBIndex, GLResourceAttribs::GetHash()); - } - - const GLint SBIndex; - }; - static_assert((sizeof(StorageBlockInfo) % sizeof(void*)) == 0, "sizeof(StorageBlockInfo) must be multiple of sizeof(void*)"); - - - // clang-format off - Uint32 GetNumUniformBuffers()const { return m_NumUniformBuffers; } - Uint32 GetNumSamplers() const { return m_NumSamplers; } - Uint32 GetNumImages() const { return m_NumImages; } - Uint32 GetNumStorageBlocks() const { return m_NumStorageBlocks; } - // clang-format on - - UniformBufferInfo& GetUniformBuffer(Uint32 Index) - { - VERIFY(Index < m_NumUniformBuffers, "Uniform buffer index (", Index, ") is out of range"); - return m_UniformBuffers[Index]; - } - - SamplerInfo& GetSampler(Uint32 Index) - { - VERIFY(Index < m_NumSamplers, "Sampler index (", Index, ") is out of range"); - return m_Samplers[Index]; - } - - ImageInfo& GetImage(Uint32 Index) - { - VERIFY(Index < m_NumImages, "Image index (", Index, ") is out of range"); - return m_Images[Index]; - } - - StorageBlockInfo& GetStorageBlock(Uint32 Index) - { - VERIFY(Index < m_NumStorageBlocks, "Storage block index (", Index, ") is out of range"); - return m_StorageBlocks[Index]; - } - - - const UniformBufferInfo& GetUniformBuffer(Uint32 Index) const - { - VERIFY(Index < m_NumUniformBuffers, "Uniform buffer index (", Index, ") is out of range"); - return m_UniformBuffers[Index]; - } - - const SamplerInfo& GetSampler(Uint32 Index) const - { - VERIFY(Index < m_NumSamplers, "Sampler index (", Index, ") is out of range"); - return m_Samplers[Index]; - } - - const ImageInfo& GetImage(Uint32 Index) const - { - VERIFY(Index < m_NumImages, "Image index (", Index, ") is out of range"); - return m_Images[Index]; - } - - const StorageBlockInfo& GetStorageBlock(Uint32 Index) const - { - VERIFY(Index < m_NumStorageBlocks, "Storage block index (", Index, ") is out of range"); - return m_StorageBlocks[Index]; - } - - Uint32 GetVariableCount() const - { - return m_NumUniformBuffers + m_NumSamplers + m_NumImages + m_NumStorageBlocks; - } - - ShaderResourceDesc GetResourceDesc(Uint32 Index) const; - - bool IsCompatibleWith(const GLProgramResources& Res) const; - size_t GetHash() const; - - SHADER_TYPE GetShaderStages() const { return m_ShaderStages; } - - template - void ProcessConstResources(THandleUB HandleUB, - THandleSampler HandleSampler, - THandleImg HandleImg, - 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) - { - const auto& UB = GetUniformBuffer(ub); - if (CheckResourceType(UB.Name)) - HandleUB(UB); - } - - for (Uint32 s = 0; s < m_NumSamplers; ++s) - { - const auto& Sam = GetSampler(s); - if (CheckResourceType(Sam.Name)) - HandleSampler(Sam); - } - - for (Uint32 img = 0; img < m_NumImages; ++img) - { - const auto& Img = GetImage(img); - if (CheckResourceType(Img.Name)) - HandleImg(Img); - } - - for (Uint32 sb = 0; sb < m_NumStorageBlocks; ++sb) - { - const auto& SB = GetStorageBlock(sb); - if (CheckResourceType(SB.Name)) - HandleSB(SB); - } - } - - template - void ProcessResources(THandleUB HandleUB, - THandleSampler HandleSampler, - THandleImg HandleImg, - THandleSB HandleSB) - { - for (Uint32 ub = 0; ub < m_NumUniformBuffers; ++ub) - HandleUB(GetUniformBuffer(ub)); - - for (Uint32 s = 0; s < m_NumSamplers; ++s) - HandleSampler(GetSampler(s)); - - for (Uint32 img = 0; img < m_NumImages; ++img) - HandleImg(GetImage(img)); - - for (Uint32 sb = 0; sb < m_NumStorageBlocks; ++sb) - HandleSB(GetStorageBlock(sb)); - } - - struct ResourceCounters - { - Uint32 NumUBs = 0; - Uint32 NumSamplers = 0; - Uint32 NumImages = 0; - Uint32 NumStorageBlocks = 0; - }; - void CountResources(const PipelineResourceLayoutDesc& ResourceLayout, - const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, - Uint32 NumAllowedTypes, - ResourceCounters& Counters) const; - - -private: - void AllocateResources(std::vector& UniformBlocks, - std::vector& Samplers, - std::vector& Images, - std::vector& StorageBlocks); - - // clang-format off - // 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 | String Pool Data | - // - - UniformBufferInfo* m_UniformBuffers = nullptr; - SamplerInfo* m_Samplers = nullptr; - ImageInfo* m_Images = nullptr; - StorageBlockInfo* m_StorageBlocks = nullptr; - - Uint32 m_NumUniformBuffers = 0; - Uint32 m_NumSamplers = 0; - Uint32 m_NumImages = 0; - Uint32 m_NumStorageBlocks = 0; - // clang-format on - // When adding new member DO NOT FORGET TO UPDATE GLProgramResources( GLProgramResources&& ProgramResources )!!! -}; - -} // namespace Diligent diff --git a/Graphics/GraphicsEngineOpenGL/include/GLStubsAndroid.h b/Graphics/GraphicsEngineOpenGL/include/GLStubsAndroid.h index cedc6e6c..e413d0a6 100644 --- a/Graphics/GraphicsEngineOpenGL/include/GLStubsAndroid.h +++ b/Graphics/GraphicsEngineOpenGL/include/GLStubsAndroid.h @@ -516,6 +516,10 @@ extern PFNGLMEMORYBARRIERPROC glMemoryBarrier; # define GL_ARB_shader_image_load_store 1 #endif +#ifndef GL_MAX_IMAGE_UNITS +# define GL_MAX_IMAGE_UNITS 0x8F38 +#endif + #ifndef GL_ARB_shader_storage_buffer_object # define GL_ARB_shader_storage_buffer_object 1 #endif diff --git a/Graphics/GraphicsEngineOpenGL/include/PipelineResourceSignatureGLImpl.hpp b/Graphics/GraphicsEngineOpenGL/include/PipelineResourceSignatureGLImpl.hpp new file mode 100644 index 00000000..852e24e6 --- /dev/null +++ b/Graphics/GraphicsEngineOpenGL/include/PipelineResourceSignatureGLImpl.hpp @@ -0,0 +1,222 @@ +/* + * Copyright 2019-2021 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +/// \file +/// Declaration of Diligent::PipelineResourceSignatureGLImpl class + +#include + +#include "PipelineResourceSignatureBase.hpp" +#include "ShaderResourceCacheGL.hpp" +#include "ShaderResourcesGL.hpp" + +namespace Diligent +{ +class RenderDeviceGLImpl; +class ShaderVariableGL; + +enum BINDING_RANGE : Uint32 +{ + BINDING_RANGE_UNIFORM_BUFFER = 0, + BINDING_RANGE_TEXTURE, + BINDING_RANGE_IMAGE, + BINDING_RANGE_STORAGE_BUFFER, + BINDING_RANGE_COUNT, + BINDING_RANGE_UNKNOWN = ~0u +}; +BINDING_RANGE PipelineResourceToBindingRange(const PipelineResourceDesc& Desc); +const char* GetBindingRangeName(BINDING_RANGE Range); + + +/// Implementation of the Diligent::PipelineResourceSignatureGLImpl class +class PipelineResourceSignatureGLImpl final : public PipelineResourceSignatureBase +{ +public: + using TPipelineResourceSignatureBase = PipelineResourceSignatureBase; + + PipelineResourceSignatureGLImpl(IReferenceCounters* pRefCounters, + RenderDeviceGLImpl* pDevice, + const PipelineResourceSignatureDesc& Desc, + bool bIsDeviceInternal = false); + ~PipelineResourceSignatureGLImpl(); + + // sizeof(ResourceAttribs) == 8, x64 + struct ResourceAttribs + { + private: + static constexpr Uint32 _SamplerIndBits = 31; + static constexpr Uint32 _SamplerAssignedBits = 1; + + public: + static constexpr Uint32 InvalidCacheOffset = ~0u; + static constexpr Uint32 InvalidSamplerInd = (1u << _SamplerIndBits) - 1; + + // clang-format off + const Uint32 CacheOffset; // SRB and Signature has the same cache offsets for static resources. + // Binding = m_FirstBinding[Range] + CacheOffset + const Uint32 SamplerInd : _SamplerIndBits; // ImtblSamplerAssigned == true: index of the immutable sampler in m_ImmutableSamplers. + // ImtblSamplerAssigned == false: index of the assigned sampler in m_Desc.Resources. + const Uint32 ImtblSamplerAssigned : _SamplerAssignedBits; // Immutable sampler flag + // clang-format on + + ResourceAttribs(Uint32 _CacheOffset, + Uint32 _SamplerInd, + bool _ImtblSamplerAssigned) noexcept : + // clang-format off + CacheOffset {_CacheOffset }, + SamplerInd {_SamplerInd }, + ImtblSamplerAssigned{_ImtblSamplerAssigned ? 1u : 0u} + // clang-format on + { + VERIFY(SamplerInd == _SamplerInd, "Sampler index (", _SamplerInd, ") exceeds maximum representable value"); + VERIFY(!_ImtblSamplerAssigned || SamplerInd != InvalidSamplerInd, "Immutable sampler assigned, but sampler index is not valid"); + } + + bool IsSamplerAssigned() const { return SamplerInd != InvalidSamplerInd; } + bool IsImmutableSamplerAssigned() const { return ImtblSamplerAssigned != 0; } + }; + + const ResourceAttribs& GetResourceAttribs(Uint32 ResIndex) const + { + VERIFY_EXPR(ResIndex < m_Desc.NumResources); + return m_pResourceAttribs[ResIndex]; + } + + const PipelineResourceDesc& GetResourceDesc(Uint32 ResIndex) const + { + VERIFY_EXPR(ResIndex < m_Desc.NumResources); + return m_Desc.Resources[ResIndex]; + } + + bool HasDynamicResources() const + { + auto IndexRange = GetResourceIndexRange(SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC); + return IndexRange.second > IndexRange.first; + } + + using TBindings = std::array; + + void ApplyBindings(GLObjectWrappers::GLProgramObj& GLProgram, + class GLContextState& State, + SHADER_TYPE Stages, + const TBindings& Bindings) const; + + void AddBindings(TBindings& Bindings) const; + + /// Implementation of IPipelineResourceSignature::CreateShaderResourceBinding. + virtual void DILIGENT_CALL_TYPE CreateShaderResourceBinding(IShaderResourceBinding** ppShaderResourceBinding, + bool InitStaticResources) override final; + + /// Implementation of IPipelineResourceSignature::GetStaticVariableByName. + virtual IShaderResourceVariable* DILIGENT_CALL_TYPE GetStaticVariableByName(SHADER_TYPE ShaderType, const Char* Name) override final; + + /// Implementation of IPipelineResourceSignature::GetStaticVariableByIndex. + virtual IShaderResourceVariable* DILIGENT_CALL_TYPE GetStaticVariableByIndex(SHADER_TYPE ShaderType, Uint32 Index) override final; + + /// Implementation of IPipelineResourceSignature::GetStaticVariableCount. + virtual Uint32 DILIGENT_CALL_TYPE GetStaticVariableCount(SHADER_TYPE ShaderType) const override final; + + /// Implementation of IPipelineResourceSignature::BindStaticResources. + virtual void DILIGENT_CALL_TYPE BindStaticResources(Uint32 ShaderFlags, + IResourceMapping* pResourceMapping, + Uint32 Flags) override final; + + /// Implementation of IPipelineResourceSignature::IsCompatibleWith. + virtual bool DILIGENT_CALL_TYPE IsCompatibleWith(const IPipelineResourceSignature* pPRS) const override final + { + if (pPRS == nullptr) + { + return GetHash() == 0; + } + return IsCompatibleWith(*ValidatedCast(pPRS)); + } + + /// Implementation of IPipelineResourceSignature::InitializeStaticSRBResources. + virtual void DILIGENT_CALL_TYPE InitializeStaticSRBResources(IShaderResourceBinding* pSRB) const override final; + + bool IsCompatibleWith(const PipelineResourceSignatureGLImpl& Other) const; + + bool IsIncompatibleWith(const PipelineResourceSignatureGLImpl& Other) const + { + return GetHash() != Other.GetHash() || m_BindingCount != Other.m_BindingCount; + } + + void InitSRBResourceCache(ShaderResourceCacheGL& ResourceCache) const; + +#ifdef DILIGENT_DEVELOPMENT + /// Verifies committed resource attribs using the SPIRV resource attributes from the PSO. + bool DvpValidateCommittedResource(const ShaderResourcesGL::GLResourceAttribs& GLAttribs, + RESOURCE_DIMENSION ResourceDim, + bool IsMultisample, + Uint32 ResIndex, + const ShaderResourceCacheGL& ResourceCache, + const char* ShaderName, + const char* PSOName) const; +#endif + +private: + PipelineResourceSignatureGLImpl(IReferenceCounters* pRefCounters, + RenderDeviceGLImpl* pDevice, + const PipelineResourceSignatureDesc& Desc, + bool bIsDeviceInternal, + int Internal); + + // Copies static resources from the static resource cache to the destination cache + void CopyStaticResources(ShaderResourceCacheGL& ResourceCache) const; + + void CreateLayouts(); + + void Destruct(); + + size_t CalculateHash() const; + +private: + TBindings m_BindingCount = {}; + + ResourceAttribs* m_pResourceAttribs = nullptr; // [m_Desc.NumResources] + + // Resource cache for static resource variables only + ShaderResourceCacheGL* m_pStaticResCache = nullptr; + + ShaderVariableGL* m_StaticVarsMgrs = nullptr; // [GetNumStaticResStages()] + + using SamplerPtr = RefCntAutoPtr; + SamplerPtr* m_ImmutableSamplers = nullptr; // [m_Desc.NumImmutableSamplers] +}; + + +__forceinline void PipelineResourceSignatureGLImpl::AddBindings(TBindings& Bindings) const +{ + for (Uint32 i = 0; i < Bindings.size(); ++i) + { + Bindings[i] += m_BindingCount[i]; + } +} + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineOpenGL/include/PipelineStateGLImpl.hpp b/Graphics/GraphicsEngineOpenGL/include/PipelineStateGLImpl.hpp index 554ed41c..40c536e6 100644 --- a/Graphics/GraphicsEngineOpenGL/include/PipelineStateGLImpl.hpp +++ b/Graphics/GraphicsEngineOpenGL/include/PipelineStateGLImpl.hpp @@ -34,10 +34,9 @@ #include "GLObjectWrapper.hpp" #include "GLContext.hpp" #include "RenderDeviceGLImpl.hpp" -#include "GLProgramResources.hpp" -#include "GLPipelineResourceLayout.hpp" -#include "GLProgramResourceCache.hpp" +#include "ShaderVariableGL.hpp" #include "ShaderGLImpl.hpp" +#include "PipelineResourceSignatureGLImpl.hpp" namespace Diligent { @@ -63,79 +62,126 @@ public: /// Queries the specific interface, see IObject::QueryInterface() for details virtual void DILIGENT_CALL_TYPE QueryInterface(const INTERFACE_ID& IID, IObject** ppInterface) override; - /// Implementation of IPipelineState::BindStaticResources() in OpenGL backend. - virtual void DILIGENT_CALL_TYPE BindStaticResources(Uint32 ShaderFlags, - IResourceMapping* pResourceMapping, - Uint32 Flags) override final; + /// Implementation of IPipelineState::GetResourceSignatureCount() in OpenGL backend. + virtual Uint32 DILIGENT_CALL_TYPE GetResourceSignatureCount() const override final { return m_SignatureCount; } - /// Implementation of IPipelineState::GetStaticVariableCount() in OpenGL backend. - virtual Uint32 DILIGENT_CALL_TYPE GetStaticVariableCount(SHADER_TYPE ShaderType) const override final; - - /// Implementation of IPipelineState::GetStaticVariableByName() in OpenGL backend. - virtual IShaderResourceVariable* DILIGENT_CALL_TYPE GetStaticVariableByName(SHADER_TYPE ShaderType, const Char* Name) override final; - - /// Implementation of IPipelineState::GetStaticVariableByIndex() in OpenGL backend. - virtual IShaderResourceVariable* DILIGENT_CALL_TYPE GetStaticVariableByIndex(SHADER_TYPE ShaderType, Uint32 Index) override final; - - /// Implementation of IPipelineState::CreateShaderResourceBinding() in OpenGL backend. - virtual void DILIGENT_CALL_TYPE CreateShaderResourceBinding(IShaderResourceBinding** ppShaderResourceBinding, - bool InitStaticResources) override final; + /// Implementation of IPipelineState::GetResourceSignature() in OpenGL backend. + virtual IPipelineResourceSignature* DILIGENT_CALL_TYPE GetResourceSignature(Uint32 Index) const override final + { + VERIFY_EXPR(Index < m_SignatureCount); + return m_Signatures[Index].RawPtr(); + } /// Implementation of IPipelineState::IsCompatibleWith() in OpenGL backend. virtual bool DILIGENT_CALL_TYPE IsCompatibleWith(const IPipelineState* pPSO) const override final; void CommitProgram(GLContextState& State); - void InitializeSRBResourceCache(GLProgramResourceCache& ResourceCache) const; + Uint32 GetSignatureCount() const { return m_SignatureCount; } - const GLPipelineResourceLayout& GetResourceLayout() const { return m_ResourceLayout; } - const GLPipelineResourceLayout& GetStaticResourceLayout() const { return m_StaticResourceLayout; } - const GLProgramResourceCache& GetStaticResourceCache() const { return m_StaticResourceCache; } + PipelineResourceSignatureGLImpl* GetSignature(Uint32 index) const + { + VERIFY_EXPR(index < m_SignatureCount); + return m_Signatures[index].RawPtr(); + } + +#ifdef DILIGENT_DEVELOPMENT + using TBindings = PipelineResourceSignatureGLImpl::TBindings; + void DvpVerifySRBResources(class ShaderResourceBindingGLImpl* pSRBs[], const TBindings BoundResOffsets[], Uint32 NumSRBs) const; +#endif private: - GLObjectWrappers::GLPipelineObj& GetGLProgramPipeline(GLContext::NativeGLContextType Context); + using ShaderStageInfo = ShaderGLImpl::ShaderStageInfo; + using TShaderStages = std::vector; - void InitImmutableSamplersInResourceCache(const GLPipelineResourceLayout& ResourceLayout, GLProgramResourceCache& Cache) const; + GLObjectWrappers::GLPipelineObj& GetGLProgramPipeline(GLContext::NativeGLContextType Context); template - void Initialize(const PSOCreateInfoType& CreateInfo, std::vector& Shaders); + void InitInternalObjects(const PSOCreateInfoType& CreateInfo, const TShaderStages& ShaderStages); + + void InitResourceLayouts(const PipelineStateCreateInfo& CreateInfo, + const TShaderStages& ShaderStages, + SHADER_TYPE ActiveStages); - void InitResourceLayouts(std::vector& Shaders, - FixedLinearAllocator& MemPool); + void CreateDefaultSignature(const PipelineStateCreateInfo& CreateInfo, + const TShaderStages& ShaderStages, + SHADER_TYPE ActiveStages, + IPipelineResourceSignature** ppSignature); void Destruct(); + SHADER_TYPE GetShaderStageType(Uint32 Index) const; + Uint32 GetNumShaderStages() const { return m_NumPrograms; } + +#ifdef DILIGENT_DEVELOPMENT + struct ResourceAttribution + { + static constexpr Uint32 InvalidSignatureIndex = ~0u; + static constexpr Uint32 InvalidResourceIndex = PipelineResourceSignatureGLImpl::InvalidResourceIndex; + static constexpr Uint32 InvalidSamplerIndex = InvalidImmutableSamplerIndex; + + const PipelineResourceSignatureGLImpl* pSignature = nullptr; + + Uint32 SignatureIndex = InvalidSignatureIndex; + Uint32 ResourceIndex = InvalidResourceIndex; + Uint32 ImmutableSamplerIndex = InvalidSamplerIndex; + + ResourceAttribution() noexcept {} + ResourceAttribution(const PipelineResourceSignatureGLImpl* _pSignature, + Uint32 _SignatureIndex, + Uint32 _ResourceIndex, + Uint32 _ImmutableSamplerIndex = InvalidResourceIndex) noexcept : + pSignature{_pSignature}, + SignatureIndex{_SignatureIndex}, + ResourceIndex{_ResourceIndex}, + ImmutableSamplerIndex{_ImmutableSamplerIndex} + { + VERIFY_EXPR(pSignature == nullptr || pSignature->GetDesc().BindingIndex == SignatureIndex); + VERIFY_EXPR((ResourceIndex == InvalidResourceIndex) || (ImmutableSamplerIndex == InvalidSamplerIndex)); + } + + explicit operator bool() const + { + return SignatureIndex != InvalidSignatureIndex && (ResourceIndex != InvalidResourceIndex || ImmutableSamplerIndex != InvalidSamplerIndex); + } + + bool IsImmutableSampler() const + { + return operator bool() && ImmutableSamplerIndex != InvalidSamplerIndex; + } + }; + ResourceAttribution GetResourceAttribution(const char* Name, SHADER_TYPE Stage) const; + + void DvpValidateShaderResources(const std::shared_ptr& pShaderResources, const char* ShaderName, SHADER_TYPE ShaderStages); +#endif + +private: // Linked GL programs for every shader stage. Every pipeline needs to have its own programs - // because resource bindings assigned by GLProgramResources::LoadUniforms depend on other + // because resource bindings assigned by PipelineResourceSignatureGLImpl::ApplyBindings depend on other // shader stages. using GLProgramObj = GLObjectWrappers::GLProgramObj; - GLProgramObj* m_GLPrograms = nullptr; // [m_NumShaderStages] + GLProgramObj* m_GLPrograms = nullptr; // [m_NumPrograms] ThreadingTools::LockFlag m_ProgPipelineLockFlag; std::vector> m_GLProgPipelines; - // Resource layout that keeps variables of all types, but does not reference a - // resource cache. - // This layout is used by SRB objects to initialize only mutable and dynamic variables and by - // DeviceContextGLImpl::BindProgramResources to verify resource bindings. - GLPipelineResourceLayout m_ResourceLayout; - - // Resource layout that only keeps static variables - GLPipelineResourceLayout m_StaticResourceLayout; - // Resource cache for static resource variables only - GLProgramResourceCache m_StaticResourceCache; + using SignatureArrayType = std::array, MAX_RESOURCE_SIGNATURES>; + SignatureArrayType m_Signatures = {}; + Uint8 m_SignatureCount = 0; - // Program resources for all shader stages in the pipeline - GLProgramResources* m_ProgramResources = nullptr; // [m_NumShaderStages] + Uint8 m_NumPrograms = 0; + bool m_IsProgramPipelineSupported = false; + std::array m_ShaderTypes = {}; - Uint32 m_TotalUniformBufferBindings = 0; - Uint32 m_TotalSamplerBindings = 0; - Uint32 m_TotalImageBindings = 0; - Uint32 m_TotalStorageBufferBindings = 0; +#ifdef DILIGENT_DEVELOPMENT + // Shader resources for all shaders in all shader stages in the pipeline. + std::vector> m_ShaderResources; + std::vector m_ShaderNames; - using SamplerPtr = RefCntAutoPtr; - SamplerPtr* m_ImmutableSamplers = nullptr; // [m_Desc.ResourceLayout.NumImmutableSamplers] + // Shader resource attributions for every resource in m_ShaderResources, in the same order. + std::vector m_ResourceAttibutions; +#endif }; } // namespace Diligent diff --git a/Graphics/GraphicsEngineOpenGL/include/RenderDeviceGLImpl.hpp b/Graphics/GraphicsEngineOpenGL/include/RenderDeviceGLImpl.hpp index 35c12a24..e6d9557f 100644 --- a/Graphics/GraphicsEngineOpenGL/include/RenderDeviceGLImpl.hpp +++ b/Graphics/GraphicsEngineOpenGL/include/RenderDeviceGLImpl.hpp @@ -131,6 +131,14 @@ public: virtual void DILIGENT_CALL_TYPE CreateSBT(const ShaderBindingTableDesc& Desc, IShaderBindingTable** ppSBT) override final; + /// Implementation of IRenderDevice::CreatePipelineResourceSignature() in OpenGL backend. + virtual void DILIGENT_CALL_TYPE CreatePipelineResourceSignature(const PipelineResourceSignatureDesc& Desc, + IPipelineResourceSignature** ppSignature) override final; + + void CreatePipelineResourceSignature(const PipelineResourceSignatureDesc& Desc, + IPipelineResourceSignature** ppSignature, + bool IsDeviceInternal); + /// Implementation of IRenderDeviceGL::CreateTextureFromGLHandle(). virtual void DILIGENT_CALL_TYPE CreateTextureFromGLHandle(Uint32 GLHandle, Uint32 GLBindTarget, @@ -167,6 +175,15 @@ public: void InitTexRegionRender(); + struct DeviceLimits + { + GLint MaxUniformBlocks; + GLint MaxTextureUnits; + GLint MaxStorageBlock; + GLint MaxImagesUnits; + }; + const DeviceLimits& GetDeviceLimits() const { return m_DeviceLimits; } + protected: friend class DeviceContextGLImpl; friend class TextureBaseGL; @@ -199,6 +216,8 @@ private: void FlagSupportedTexFormats(); int m_ShowDebugGLOutput = 1; + + DeviceLimits m_DeviceLimits = {}; }; } // namespace Diligent diff --git a/Graphics/GraphicsEngineOpenGL/include/ShaderGLImpl.hpp b/Graphics/GraphicsEngineOpenGL/include/ShaderGLImpl.hpp index d76cac2f..263d0518 100644 --- a/Graphics/GraphicsEngineOpenGL/include/ShaderGLImpl.hpp +++ b/Graphics/GraphicsEngineOpenGL/include/ShaderGLImpl.hpp @@ -33,7 +33,7 @@ #include "RenderDevice.h" #include "GLObjectWrapper.hpp" #include "RenderDeviceGLImpl.hpp" -#include "GLProgramResources.hpp" +#include "ShaderResourcesGL.hpp" namespace Diligent { @@ -92,11 +92,20 @@ public: /// Implementation of IShader::GetResource() in OpenGL backend. virtual void DILIGENT_CALL_TYPE GetResourceDesc(Uint32 Index, ShaderResourceDesc& ResourceDesc) const override final; - static GLObjectWrappers::GLProgramObj LinkProgram(ShaderGLImpl** ppShaders, Uint32 NumShaders, bool IsSeparableProgram); + struct ShaderStageInfo + { + ShaderStageInfo(const ShaderGLImpl* _pShader); + + SHADER_TYPE Type = SHADER_TYPE_UNKNOWN; + const ShaderGLImpl* pShader = nullptr; + }; + static GLObjectWrappers::GLProgramObj LinkProgram(const ShaderStageInfo* pShaderStagess, Uint32 NumShaders, bool IsSeparableProgram); + + const std::shared_ptr& GetShaderResources() const { return m_pShaderResources; } private: - GLObjectWrappers::GLShaderObj m_GLShaderObj; - GLProgramResources m_Resources; + GLObjectWrappers::GLShaderObj m_GLShaderObj; + std::shared_ptr m_pShaderResources; }; } // namespace Diligent diff --git a/Graphics/GraphicsEngineOpenGL/include/ShaderResourceBindingGLImpl.hpp b/Graphics/GraphicsEngineOpenGL/include/ShaderResourceBindingGLImpl.hpp index 1420ed42..6442b7ee 100644 --- a/Graphics/GraphicsEngineOpenGL/include/ShaderResourceBindingGLImpl.hpp +++ b/Graphics/GraphicsEngineOpenGL/include/ShaderResourceBindingGLImpl.hpp @@ -33,26 +33,22 @@ #include "ShaderResourceBindingGL.h" #include "RenderDeviceGL.h" #include "ShaderResourceBindingBase.hpp" -#include "GLProgramResources.hpp" -#include "ShaderBase.hpp" -#include "GLProgramResourceCache.hpp" -#include "GLPipelineResourceLayout.hpp" +#include "ShaderResourcesGL.hpp" +#include "ShaderResourceCacheGL.hpp" +#include "ShaderVariableGL.hpp" +#include "PipelineResourceSignatureGLImpl.hpp" namespace Diligent { -class PipelineStateGLImpl; - /// Shader resource binding object implementation in OpenGL backend. -class ShaderResourceBindingGLImpl final : public ShaderResourceBindingBase +class ShaderResourceBindingGLImpl final : public ShaderResourceBindingBase { public: - using TBase = ShaderResourceBindingBase; + using TBase = ShaderResourceBindingBase; - ShaderResourceBindingGLImpl(IReferenceCounters* pRefCounters, - PipelineStateGLImpl* pPSO, - GLProgramResources* ProgramResources, - Uint32 NumPrograms); + ShaderResourceBindingGLImpl(IReferenceCounters* pRefCounters, + PipelineResourceSignatureGLImpl* pPRS); ~ShaderResourceBindingGLImpl(); virtual void DILIGENT_CALL_TYPE QueryInterface(const INTERFACE_ID& IID, IObject** ppInterface) override final; @@ -71,19 +67,17 @@ public: /// Implementation of IShaderResourceBinding::GetVariableByIndex() in OpenGL backend. virtual IShaderResourceVariable* DILIGENT_CALL_TYPE GetVariableByIndex(SHADER_TYPE ShaderType, Uint32 Index) override final; - /// Implementation of IShaderResourceBinding::InitializeStaticResources() in OpenGL backend. - virtual void DILIGENT_CALL_TYPE InitializeStaticResources(const IPipelineState* pPipelineState) override final; - - const GLProgramResourceCache& GetResourceCache(PipelineStateGLImpl* pdbgPSO); + ShaderResourceCacheGL& GetResourceCache() { return m_ShaderResourceCache; } + ShaderResourceCacheGL const& GetResourceCache() const { return m_ShaderResourceCache; } private: - // The resource layout only references mutable and dynamic variables - GLPipelineResourceLayout m_ResourceLayout; + void Destruct(); // The resource cache holds resource bindings for all variables - GLProgramResourceCache m_ResourceCache; + ShaderResourceCacheGL m_ShaderResourceCache; - bool m_bIsStaticResourcesBound = false; + // The resource layout only references mutable and dynamic variables + ShaderVariableGL* m_pShaderVarMgrs = nullptr; // [GetNumShaders()] }; } // namespace Diligent diff --git a/Graphics/GraphicsEngineOpenGL/include/ShaderResourceCacheGL.hpp b/Graphics/GraphicsEngineOpenGL/include/ShaderResourceCacheGL.hpp new file mode 100644 index 00000000..be86d3c3 --- /dev/null +++ b/Graphics/GraphicsEngineOpenGL/include/ShaderResourceCacheGL.hpp @@ -0,0 +1,299 @@ +/* + * Copyright 2019-2021 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +#include "BufferGLImpl.hpp" +#include "TextureBaseGL.hpp" +#include "SamplerGLImpl.hpp" + +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 Textures | Cached Images | Cached Storage Blocks | +// |----------------------------------------------------|--------------------------|---------------------------| +// | 0 | 1 | ... | UBCount-1 | 0 | 1 | ...| SmpCount-1 | 0 | 1 | ... | ImgCount-1 | 0 | 1 | ... | SBOCount-1 | +// ----------------------------------------------------------------------------------------------------------- +// +class ShaderResourceCacheGL +{ +public: + enum class CacheContentType : Uint8 + { + Signature = 0, // The cache is used by the pipeline resource signature to hold static resources. + SRB = 1 // The cache is used by SRB to hold resources of all types (static, mutable, dynamic). + }; + + explicit ShaderResourceCacheGL(CacheContentType ContentType) noexcept : + m_ContentType{ContentType} + {} + + ~ShaderResourceCacheGL(); + + // clang-format off + ShaderResourceCacheGL (const ShaderResourceCacheGL&) = delete; + ShaderResourceCacheGL& operator = (const ShaderResourceCacheGL&) = delete; + ShaderResourceCacheGL (ShaderResourceCacheGL&&) = delete; + ShaderResourceCacheGL& operator = (ShaderResourceCacheGL&&) = delete; + // clang-format on + + /// 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 + { + /// We 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 TextureCount, Uint32 ImageCount, Uint32 SSBOCount); + + void Initialize(Uint32 UBCount, Uint32 TextureCount, Uint32 ImageCount, Uint32 SSBOCount, IMemoryAllocator& MemAllocator); + void Destroy(IMemoryAllocator& MemAllocator); + + void SetUniformBuffer(Uint32 CacheOffset, RefCntAutoPtr&& pBuff) + { + GetUB(CacheOffset).pBuffer = std::move(pBuff); + } + + void SetTexture(Uint32 CacheOffset, RefCntAutoPtr&& pTexView, bool SetSampler) + { + GetTexture(CacheOffset).Set(std::move(pTexView), SetSampler); + } + + void SetSampler(Uint32 CacheOffset, ISampler* pSampler) + { + GetTexture(CacheOffset).pSampler = ValidatedCast(pSampler); + } + + void SetTexelBuffer(Uint32 CacheOffset, RefCntAutoPtr&& pBuffView) + { + GetTexture(CacheOffset).Set(std::move(pBuffView)); + } + + void SetTexImage(Uint32 CacheOffset, RefCntAutoPtr&& pTexView) + { + GetImage(CacheOffset).Set(std::move(pTexView), false); + } + + void SetBufImage(Uint32 CacheOffset, RefCntAutoPtr&& pBuffView) + { + GetImage(CacheOffset).Set(std::move(pBuffView)); + } + + void SetSSBO(Uint32 CacheOffset, RefCntAutoPtr&& pBuffView) + { + GetSSBO(CacheOffset).pBufferView = std::move(pBuffView); + } + + void CopyTexture(Uint32 Binding, const CachedResourceView& SrcSam) + { + GetTexture(Binding) = SrcSam; + } + + void CopyImage(Uint32 CacheOffset, const CachedResourceView& SrcImg) + { + GetImage(CacheOffset) = SrcImg; + } + + bool IsUBBound(Uint32 CacheOffset) const + { + if (CacheOffset >= GetUBCount()) + return false; + + const auto& UB = GetConstUB(CacheOffset); + return UB.pBuffer; + } + + bool IsTextureBound(Uint32 CacheOffset, bool dbgIsTextureView) const + { + if (CacheOffset >= GetTextureCount()) + return false; + + const auto& Texture = GetConstTexture(CacheOffset); + VERIFY_EXPR(dbgIsTextureView || Texture.pTexture == nullptr); + return Texture.pView; + } + + bool IsImageBound(Uint32 CacheOffset, bool dbgIsTextureView) const + { + if (CacheOffset >= GetImageCount()) + return false; + + const auto& Image = GetConstImage(CacheOffset); + VERIFY_EXPR(dbgIsTextureView || Image.pTexture == nullptr); + return Image.pView; + } + + bool IsSSBOBound(Uint32 CacheOffset) const + { + if (CacheOffset >= GetSSBOCount()) + return false; + + const auto& SSBO = GetConstSSBO(CacheOffset); + return SSBO.pBufferView; + } + + // clang-format off + Uint32 GetUBCount() const { return (m_TexturesOffset - m_UBsOffset) / sizeof(CachedUB); } + Uint32 GetTextureCount() const { return (m_ImagesOffset - m_TexturesOffset) / sizeof(CachedResourceView); } + Uint32 GetImageCount() const { return (m_SSBOsOffset - m_ImagesOffset) / sizeof(CachedResourceView); } + Uint32 GetSSBOCount() const { return (m_MemoryEndOffset - m_SSBOsOffset) / sizeof(CachedSSBO); } + // clang-format on + + const CachedUB& GetConstUB(Uint32 CacheOffset) const + { + VERIFY(CacheOffset < GetUBCount(), "Uniform buffer index (", CacheOffset, ") is out of range"); + return reinterpret_cast(m_pResourceData + m_UBsOffset)[CacheOffset]; + } + + const CachedResourceView& GetConstTexture(Uint32 CacheOffset) const + { + VERIFY(CacheOffset < GetTextureCount(), "Texture index (", CacheOffset, ") is out of range"); + return reinterpret_cast(m_pResourceData + m_TexturesOffset)[CacheOffset]; + } + + const CachedResourceView& GetConstImage(Uint32 CacheOffset) const + { + VERIFY(CacheOffset < GetImageCount(), "Image buffer index (", CacheOffset, ") is out of range"); + return reinterpret_cast(m_pResourceData + m_ImagesOffset)[CacheOffset]; + } + + const CachedSSBO& GetConstSSBO(Uint32 CacheOffset) const + { + VERIFY(CacheOffset < GetSSBOCount(), "Shader storage block index (", CacheOffset, ") is out of range"); + return reinterpret_cast(m_pResourceData + m_SSBOsOffset)[CacheOffset]; + } + + bool IsInitialized() const + { + return m_MemoryEndOffset != InvalidResourceOffset; + } + + CacheContentType GetContentType() const { return m_ContentType; } + +#ifdef DILIGENT_DEVELOPMENT + void SetStaticResourcesInitialized() + { + m_bStaticResourcesInitialized = true; + } + bool StaticResourcesInitialized() const { return m_bStaticResourcesInitialized; } +#endif + +private: + CachedUB& GetUB(Uint32 CacheOffset) + { + return const_cast(const_cast(this)->GetConstUB(CacheOffset)); + } + + CachedResourceView& GetTexture(Uint32 CacheOffset) + { + return const_cast(const_cast(this)->GetConstTexture(CacheOffset)); + } + + CachedResourceView& GetImage(Uint32 CacheOffset) + { + return const_cast(const_cast(this)->GetConstImage(CacheOffset)); + } + + CachedSSBO& GetSSBO(Uint32 CacheOffset) + { + return const_cast(const_cast(this)->GetConstSSBO(CacheOffset)); + } + + static constexpr const Uint16 InvalidResourceOffset = 0xFFFF; + static constexpr const Uint16 m_UBsOffset = 0; + + Uint16 m_TexturesOffset = InvalidResourceOffset; + Uint16 m_ImagesOffset = InvalidResourceOffset; + Uint16 m_SSBOsOffset = InvalidResourceOffset; + Uint16 m_MemoryEndOffset = InvalidResourceOffset; + + Uint8* m_pResourceData = nullptr; + + // Indicates what types of resources are stored in the cache + const CacheContentType m_ContentType; + +#ifdef DILIGENT_DEBUG + IMemoryAllocator* m_pdbgMemoryAllocator = nullptr; +#endif +#ifdef DILIGENT_DEVELOPMENT + bool m_bStaticResourcesInitialized = false; +#endif +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineOpenGL/include/ShaderResourcesGL.hpp b/Graphics/GraphicsEngineOpenGL/include/ShaderResourcesGL.hpp new file mode 100644 index 00000000..602bd107 --- /dev/null +++ b/Graphics/GraphicsEngineOpenGL/include/ShaderResourcesGL.hpp @@ -0,0 +1,421 @@ +/* + * Copyright 2019-2021 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +// ShaderResourcesGL class allocates single continuous chunk of memory to store all program resources, as follows: +// +// +// m_UniformBuffers m_Textures m_Images m_StorageBlocks +// | | | | | | +// | UB[0] ... UB[Nu-1] | Tex[0] ... Tex[Ns-1] | Img[0] ... Img[Ni-1] | SB[0] ... SB[Nsb-1] | Resource Names | +// +// Nu - number of uniform buffers +// Ns - number of samplers +// Ni - number of images +// Nsb - number of storage blocks + +#include + +#include "Object.h" +#include "StringPool.hpp" +#include "HashUtils.hpp" +#include "ShaderResourceVariableBase.hpp" + +namespace Diligent +{ + +class ShaderResourcesGL +{ +public: + ShaderResourcesGL() {} + ~ShaderResourcesGL(); + // clang-format off + ShaderResourcesGL (ShaderResourcesGL&& Program)noexcept; + + ShaderResourcesGL (const ShaderResourcesGL&) = delete; + ShaderResourcesGL& operator = (const ShaderResourcesGL&) = delete; + ShaderResourcesGL& operator = ( ShaderResourcesGL&&) = delete; + // clang-format on + + /// Loads program uniforms and assigns bindings + void LoadUniforms(SHADER_TYPE ShaderStages, + const GLObjectWrappers::GLProgramObj& GLProgram, + class GLContextState& State); + + struct GLResourceAttribs + { + // clang-format off +/* 0 */ const Char* Name; +/* 8 */ const SHADER_TYPE ShaderStages; +/* 12 */ const SHADER_RESOURCE_TYPE ResourceType; +/* 16 */ Uint32 ArraySize; +/* 20 */ // End of data + // clang-format on + + GLResourceAttribs(const Char* _Name, + SHADER_TYPE _ShaderStages, + SHADER_RESOURCE_TYPE _ResourceType, + Uint32 _ArraySize) noexcept : + // clang-format off + Name {_Name }, + ShaderStages {_ShaderStages}, + ResourceType {_ResourceType}, + ArraySize {_ArraySize } + // clang-format on + { + 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"); + } + + GLResourceAttribs(const GLResourceAttribs& Attribs, + StringPool& NamesPool) noexcept : + // clang-format off + GLResourceAttribs + { + NamesPool.CopyString(Attribs.Name), + Attribs.ShaderStages, + Attribs.ResourceType, + Attribs.ArraySize + } + // clang-format on + { + } + + ShaderResourceDesc GetResourceDesc() const + { + ShaderResourceDesc ResourceDesc; + ResourceDesc.Name = Name; + ResourceDesc.ArraySize = ArraySize; + ResourceDesc.Type = ResourceType; + return ResourceDesc; + } + }; + + struct UniformBufferInfo final : GLResourceAttribs + { + // clang-format off + UniformBufferInfo (const UniformBufferInfo&) = delete; + UniformBufferInfo& operator= (const UniformBufferInfo&) = delete; + UniformBufferInfo ( UniformBufferInfo&&) = default; + UniformBufferInfo& operator= ( UniformBufferInfo&&) = delete; + + UniformBufferInfo(const Char* _Name, + SHADER_TYPE _ShaderStages, + SHADER_RESOURCE_TYPE _ResourceType, + Uint32 _ArraySize, + GLuint _UBIndex)noexcept : + GLResourceAttribs{_Name, _ShaderStages, _ResourceType, _ArraySize}, + UBIndex {_UBIndex} + {} + + UniformBufferInfo(const UniformBufferInfo& UB, + StringPool& NamesPool)noexcept : + GLResourceAttribs{UB, NamesPool}, + UBIndex {UB.UBIndex } + {} + // clang-format on + + const GLuint UBIndex; + }; + static_assert((sizeof(UniformBufferInfo) % sizeof(void*)) == 0, "sizeof(UniformBufferInfo) must be multiple of sizeof(void*)"); + + + struct TextureInfo final : GLResourceAttribs + { + // clang-format off + TextureInfo (const TextureInfo&) = delete; + TextureInfo& operator= (const TextureInfo&) = delete; + TextureInfo ( TextureInfo&&) = default; + TextureInfo& operator= ( TextureInfo&&) = delete; + + TextureInfo(const Char* _Name, + SHADER_TYPE _ShaderStages, + SHADER_RESOURCE_TYPE _ResourceType, + Uint32 _ArraySize, + GLenum _TextureType, + RESOURCE_DIMENSION _ResourceDim, + bool _IsMultisample) noexcept : + GLResourceAttribs{_Name, _ShaderStages, _ResourceType, _ArraySize}, + TextureType {_TextureType}, + ResourceDim {_ResourceDim}, + IsMultisample {_IsMultisample} + {} + + TextureInfo(const TextureInfo& Tex, + StringPool& NamesPool) noexcept : + GLResourceAttribs{Tex, NamesPool}, + TextureType {Tex.TextureType}, + ResourceDim {Tex.ResourceDim}, + IsMultisample {Tex.IsMultisample} + {} + // clang-format on + + const GLenum TextureType; + const RESOURCE_DIMENSION ResourceDim; + const bool IsMultisample; + }; + static_assert((sizeof(TextureInfo) % sizeof(void*)) == 0, "sizeof(TextureInfo) must be multiple of sizeof(void*)"); + + + struct ImageInfo final : GLResourceAttribs + { + // clang-format off + ImageInfo (const ImageInfo&) = delete; + ImageInfo& operator= (const ImageInfo&) = delete; + ImageInfo ( ImageInfo&&) = default; + ImageInfo& operator= ( ImageInfo&&) = delete; + + ImageInfo(const Char* _Name, + SHADER_TYPE _ShaderStages, + SHADER_RESOURCE_TYPE _ResourceType, + Uint32 _ArraySize, + GLenum _ImageType, + RESOURCE_DIMENSION _ResourceDim, + bool _IsMultisample) noexcept : + GLResourceAttribs{_Name, _ShaderStages, _ResourceType, _ArraySize}, + ImageType {_ImageType}, + ResourceDim {_ResourceDim}, + IsMultisample {_IsMultisample} + {} + + ImageInfo(const ImageInfo& Img, + StringPool& NamesPool) noexcept : + GLResourceAttribs{Img, NamesPool}, + ImageType {Img.ImageType}, + ResourceDim {Img.ResourceDim}, + IsMultisample {Img.IsMultisample} + {} + // clang-format on + + const GLenum ImageType; + const RESOURCE_DIMENSION ResourceDim; + const bool IsMultisample; + }; + static_assert((sizeof(ImageInfo) % sizeof(void*)) == 0, "sizeof(ImageInfo) must be multiple of sizeof(void*)"); + + + struct StorageBlockInfo final : GLResourceAttribs + { + // clang-format off + StorageBlockInfo (const StorageBlockInfo&) = delete; + StorageBlockInfo& operator= (const StorageBlockInfo&) = delete; + StorageBlockInfo ( StorageBlockInfo&&) = default; + StorageBlockInfo& operator= ( StorageBlockInfo&&) = delete; + + StorageBlockInfo(const Char* _Name, + SHADER_TYPE _ShaderStages, + SHADER_RESOURCE_TYPE _ResourceType, + Uint32 _ArraySize, + GLint _SBIndex)noexcept : + GLResourceAttribs{_Name, _ShaderStages, _ResourceType, _ArraySize}, + SBIndex {_SBIndex} + {} + + StorageBlockInfo(const StorageBlockInfo& SB, + StringPool& NamesPool)noexcept : + GLResourceAttribs{SB, NamesPool}, + SBIndex {SB.SBIndex} + {} + // clang-format on + + const GLint SBIndex; + }; + static_assert((sizeof(StorageBlockInfo) % sizeof(void*)) == 0, "sizeof(StorageBlockInfo) must be multiple of sizeof(void*)"); + + + // clang-format off + Uint32 GetNumUniformBuffers()const { return m_NumUniformBuffers; } + Uint32 GetNumTextures() const { return m_NumTextures; } + Uint32 GetNumImages() const { return m_NumImages; } + Uint32 GetNumStorageBlocks() const { return m_NumStorageBlocks; } + // clang-format on + + UniformBufferInfo& GetUniformBuffer(Uint32 Index) + { + VERIFY(Index < m_NumUniformBuffers, "Uniform buffer index (", Index, ") is out of range"); + return m_UniformBuffers[Index]; + } + + TextureInfo& GetTexture(Uint32 Index) + { + VERIFY(Index < m_NumTextures, "Texture index (", Index, ") is out of range"); + return m_Textures[Index]; + } + + ImageInfo& GetImage(Uint32 Index) + { + VERIFY(Index < m_NumImages, "Image index (", Index, ") is out of range"); + return m_Images[Index]; + } + + StorageBlockInfo& GetStorageBlock(Uint32 Index) + { + VERIFY(Index < m_NumStorageBlocks, "Storage block index (", Index, ") is out of range"); + return m_StorageBlocks[Index]; + } + + + const UniformBufferInfo& GetUniformBuffer(Uint32 Index) const + { + VERIFY(Index < m_NumUniformBuffers, "Uniform buffer index (", Index, ") is out of range"); + return m_UniformBuffers[Index]; + } + + const TextureInfo& GetTexture(Uint32 Index) const + { + VERIFY(Index < m_NumTextures, "Texture index (", Index, ") is out of range"); + return m_Textures[Index]; + } + + const ImageInfo& GetImage(Uint32 Index) const + { + VERIFY(Index < m_NumImages, "Image index (", Index, ") is out of range"); + return m_Images[Index]; + } + + const StorageBlockInfo& GetStorageBlock(Uint32 Index) const + { + VERIFY(Index < m_NumStorageBlocks, "Storage block index (", Index, ") is out of range"); + return m_StorageBlocks[Index]; + } + + Uint32 GetVariableCount() const + { + return m_NumUniformBuffers + m_NumTextures + m_NumImages + m_NumStorageBlocks; + } + + ShaderResourceDesc GetResourceDesc(Uint32 Index) const; + + SHADER_TYPE GetShaderStages() const { return m_ShaderStages; } + + template + void ProcessConstResources(THandleUB HandleUB, + THandleTexture HandleTexture, + THandleImg HandleImg, + 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) + { + const auto& UB = GetUniformBuffer(ub); + if (CheckResourceType(UB.Name)) + HandleUB(UB); + } + + for (Uint32 s = 0; s < m_NumTextures; ++s) + { + const auto& Sam = GetTexture(s); + if (CheckResourceType(Sam.Name)) + HandleTexture(Sam); + } + + for (Uint32 img = 0; img < m_NumImages; ++img) + { + const auto& Img = GetImage(img); + if (CheckResourceType(Img.Name)) + HandleImg(Img); + } + + for (Uint32 sb = 0; sb < m_NumStorageBlocks; ++sb) + { + const auto& SB = GetStorageBlock(sb); + if (CheckResourceType(SB.Name)) + HandleSB(SB); + } + } + + template + void ProcessResources(THandleUB HandleUB, + THandleTexture HandleTexture, + THandleImg HandleImg, + THandleSB HandleSB) + { + for (Uint32 ub = 0; ub < m_NumUniformBuffers; ++ub) + HandleUB(GetUniformBuffer(ub)); + + for (Uint32 s = 0; s < m_NumTextures; ++s) + HandleTexture(GetTexture(s)); + + for (Uint32 img = 0; img < m_NumImages; ++img) + HandleImg(GetImage(img)); + + for (Uint32 sb = 0; sb < m_NumStorageBlocks; ++sb) + HandleSB(GetStorageBlock(sb)); + } + +private: + void AllocateResources(std::vector& UniformBlocks, + std::vector& Textures, + std::vector& Images, + std::vector& StorageBlocks); + + // clang-format off + // There could be more than one stage if using non-separable programs + SHADER_TYPE m_ShaderStages = SHADER_TYPE_UNKNOWN; + + // Memory layout: + // + // | Uniform buffers | Textures | Images | Storage Blocks | String Pool Data | + // + + UniformBufferInfo* m_UniformBuffers = nullptr; + TextureInfo* m_Textures = nullptr; + ImageInfo* m_Images = nullptr; + StorageBlockInfo* m_StorageBlocks = nullptr; + + Uint32 m_NumUniformBuffers = 0; + Uint32 m_NumTextures = 0; + Uint32 m_NumImages = 0; + Uint32 m_NumStorageBlocks = 0; + // clang-format on + // When adding new member DO NOT FORGET TO UPDATE ShaderResourcesGL( ShaderResourcesGL&& ProgramResources )!!! +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineOpenGL/include/ShaderVariableGL.hpp b/Graphics/GraphicsEngineOpenGL/include/ShaderVariableGL.hpp new file mode 100644 index 00000000..18fd9665 --- /dev/null +++ b/Graphics/GraphicsEngineOpenGL/include/ShaderVariableGL.hpp @@ -0,0 +1,473 @@ +/* + * Copyright 2019-2021 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +// ShaderVariableGL class manages resource bindings for all stages in a pipeline + +// +// +// To program resource cache +// +// A A A A A A A A +// | | | | | | | | +// Binding Binding Binding Binding Binding Binding Binding Binding +// ___________________ ____|__________|__________________|________|______________|___________|______________|____________|____________ +// | | | | | | | | | | | | | | | +// | ShaderResourcesGL |--------------->| UB[0] | UB[1] | ... | Sam[0] | Sam[1] | ... | Img[0] | Img[1] | ... | SSBOs[0] | SSBOs[1] | ... | +// |___________________| |__________|__________|_______|________|________|_______|________|________|_______|__________|__________|_______| +// A A A A +// | | | | +// Ref Ref Ref Ref +// .-==========================-. _____|____________________________________|________________________|____________________________|______________ +// || || | | | | | | | | | | | +// __|| ShaderVariableGL ||------->| UBInfo[0] | UBInfo[1] | ... | SamInfo[0] | SamInfo[1] | ... | ImgInfo[0] | ... | SSBO[0] | ... | +// | || || |___________|___________|_______|____________|____________|_______|____________|_________|___________|__________| +// | '-==========================-' / \ +// | Ref Ref +// | / \ +// | ___________________ ________V________________________________________________V_____________________________________________________ +// | | | | | | | | | | | | | | | | +// | | ShaderResourcesGL |--------------->| UB[0] | UB[1] | ... | Sam[0] | Sam[1] | ... | Img[0] | Img[1] | ... | SSBOs[0] | SSBOs[1] | ... | +// | |___________________| |__________|__________|_______|________|________|_______|________|________|_______|__________|__________|_______| +// | | | | | | | | | +// | Binding Binding Binding Binding Binding Binding Binding Binding +// | | | | | | | | | +// | _______________________ ____V___________V________________V_________V________________V________V________________V___________V_____________ +// | | | | | | | | +// '-->| ShaderResourceCacheGL |----------->| Uinform Buffers | Textures | Images | Storge Buffers | +// |_______________________| |___________________________|___________________________|___________________________|___________________________| +// +// +// Note that ShaderResourcesGL are kept by PipelineStateGLImpl. ShaderVariableGL class is either part of the same PSO class, +// or part of ShaderResourceBindingGLImpl object that keeps a strong reference to the pipeline. So all references from GLVariableBase +// are always valid. + +#include + +#include "Object.h" +#include "ShaderResourceVariableBase.hpp" +#include "ShaderResourceCacheGL.hpp" +#include "PipelineResourceSignatureGLImpl.hpp" + +namespace Diligent +{ + +// sizeof(ShaderVariableGL) == 48 (x64, msvc, Release) +class ShaderVariableGL +{ +public: + ShaderVariableGL(IObject& Owner, ShaderResourceCacheGL& ResourceCache) noexcept : + m_Owner(Owner), + m_ResourceCache{ResourceCache} + {} + + ~ShaderVariableGL(); + + // No copies, only moves are allowed + // clang-format off + ShaderVariableGL (const ShaderVariableGL&) = delete; + ShaderVariableGL& operator = (const ShaderVariableGL&) = delete; + ShaderVariableGL ( ShaderVariableGL&&) = default; + ShaderVariableGL& operator = ( ShaderVariableGL&&) = delete; + // clang-format on + + void Initialize(const PipelineResourceSignatureGLImpl& Signature, + const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, + Uint32 NumAllowedTypes, + SHADER_TYPE ShaderType); + + static size_t GetRequiredMemorySize(const PipelineResourceSignatureGLImpl& Signature, + const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, + Uint32 NumAllowedTypes, + SHADER_TYPE ShaderType); + + using ResourceAttribs = PipelineResourceSignatureGLImpl::ResourceAttribs; + + const PipelineResourceDesc& GetResourceDesc(Uint32 Index) const + { + VERIFY_EXPR(m_pSignature); + return m_pSignature->GetResourceDesc(Index); + } + const ResourceAttribs& GetAttribs(Uint32 Index) const + { + VERIFY_EXPR(m_pSignature); + return m_pSignature->GetResourceAttribs(Index); + } + + + struct GLVariableBase : public ShaderVariableBase + { + using TBase = ShaderVariableBase; + GLVariableBase(ShaderVariableGL& ParentLayout, Uint32 ResIndex) : + TBase{ParentLayout}, + m_ResIndex{ResIndex} + {} + + const PipelineResourceDesc& GetDesc() const { return m_ParentManager.GetResourceDesc(m_ResIndex); } + const ResourceAttribs& GetAttribs() const { return m_ParentManager.GetAttribs(m_ResIndex); } + + virtual SHADER_RESOURCE_VARIABLE_TYPE DILIGENT_CALL_TYPE GetType() const override final + { + return GetDesc().VarType; + } + + virtual void DILIGENT_CALL_TYPE GetResourceDesc(ShaderResourceDesc& ResourceDesc) const override final + { + const auto& Desc = GetDesc(); + ResourceDesc.Name = Desc.Name; + ResourceDesc.Type = Desc.ResourceType; + ResourceDesc.ArraySize = Desc.ArraySize; + } + + virtual Uint32 DILIGENT_CALL_TYPE GetIndex() const override final + { + return m_ParentManager.GetVariableIndex(*this); + } + + const Uint32 m_ResIndex; + }; + + + struct UniformBuffBindInfo final : GLVariableBase + { + UniformBuffBindInfo(ShaderVariableGL& ParentLayout, Uint32 ResIndex) : + GLVariableBase{ParentLayout, ResIndex} + {} + + // Non-virtual function + void BindResource(IDeviceObject* pObject, Uint32 ArrayIndex); + + virtual void DILIGENT_CALL_TYPE Set(IDeviceObject* pObject) override final + { + BindResource(pObject, 0); + } + + virtual void DILIGENT_CALL_TYPE SetArray(IDeviceObject* const* ppObjects, + Uint32 FirstElement, + Uint32 NumElements) override final + { + const auto& Desc = GetDesc(); + VerifyAndCorrectSetArrayArguments(Desc.Name, Desc.ArraySize, FirstElement, NumElements); + for (Uint32 elem = 0; elem < NumElements; ++elem) + BindResource(ppObjects[elem], FirstElement + elem); + } + + virtual bool DILIGENT_CALL_TYPE IsBound(Uint32 ArrayIndex) const override final + { + VERIFY_EXPR(ArrayIndex < GetDesc().ArraySize); + return m_ParentManager.m_ResourceCache.IsUBBound(GetAttribs().CacheOffset + ArrayIndex); + } + }; + + + struct SamplerBindInfo final : GLVariableBase + { + SamplerBindInfo(ShaderVariableGL& ParentLayout, Uint32 ResIndex) : + GLVariableBase{ParentLayout, ResIndex} + {} + + // Non-virtual function + void BindResource(IDeviceObject* pObject, Uint32 ArrayIndex); + + virtual void DILIGENT_CALL_TYPE Set(IDeviceObject* pObject) override final + { + BindResource(pObject, 0); + } + + virtual void DILIGENT_CALL_TYPE SetArray(IDeviceObject* const* ppObjects, + Uint32 FirstElement, + Uint32 NumElements) override final + { + const auto& Desc = GetDesc(); + VerifyAndCorrectSetArrayArguments(Desc.Name, Desc.ArraySize, FirstElement, NumElements); + for (Uint32 elem = 0; elem < NumElements; ++elem) + BindResource(ppObjects[elem], FirstElement + elem); + } + + virtual bool DILIGENT_CALL_TYPE IsBound(Uint32 ArrayIndex) const override final + { + VERIFY_EXPR(ArrayIndex < GetDesc().ArraySize); + return m_ParentManager.m_ResourceCache.IsTextureBound(GetAttribs().CacheOffset + ArrayIndex, GetDesc().ResourceType == SHADER_RESOURCE_TYPE_TEXTURE_SRV); + } + }; + + + struct ImageBindInfo final : GLVariableBase + { + ImageBindInfo(ShaderVariableGL& ParentLayout, Uint32 ResIndex) : + GLVariableBase{ParentLayout, ResIndex} + {} + + // Provide non-virtual function + void BindResource(IDeviceObject* pObject, Uint32 ArrayIndex); + + virtual void DILIGENT_CALL_TYPE Set(IDeviceObject* pObject) override final + { + BindResource(pObject, 0); + } + + virtual void DILIGENT_CALL_TYPE SetArray(IDeviceObject* const* ppObjects, + Uint32 FirstElement, + Uint32 NumElements) override final + { + const auto& Desc = GetDesc(); + VerifyAndCorrectSetArrayArguments(Desc.Name, Desc.ArraySize, FirstElement, NumElements); + for (Uint32 elem = 0; elem < NumElements; ++elem) + BindResource(ppObjects[elem], FirstElement + elem); + } + + virtual bool DILIGENT_CALL_TYPE IsBound(Uint32 ArrayIndex) const override final + { + VERIFY_EXPR(ArrayIndex < GetDesc().ArraySize); + return m_ParentManager.m_ResourceCache.IsImageBound(GetAttribs().CacheOffset + ArrayIndex, GetDesc().ResourceType == SHADER_RESOURCE_TYPE_TEXTURE_UAV); + } + }; + + + struct StorageBufferBindInfo final : GLVariableBase + { + StorageBufferBindInfo(ShaderVariableGL& ParentLayout, Uint32 ResIndex) : + GLVariableBase{ParentLayout, ResIndex} + {} + + // Non-virtual function + void BindResource(IDeviceObject* pObject, Uint32 ArrayIndex); + + virtual void DILIGENT_CALL_TYPE Set(IDeviceObject* pObject) override final + { + BindResource(pObject, 0); + } + + virtual void DILIGENT_CALL_TYPE SetArray(IDeviceObject* const* ppObjects, + Uint32 FirstElement, + Uint32 NumElements) override final + { + const auto& Desc = GetDesc(); + VerifyAndCorrectSetArrayArguments(Desc.Name, Desc.ArraySize, FirstElement, NumElements); + for (Uint32 elem = 0; elem < NumElements; ++elem) + BindResource(ppObjects[elem], FirstElement + elem); + } + + virtual bool DILIGENT_CALL_TYPE IsBound(Uint32 ArrayIndex) const override final + { + VERIFY_EXPR(ArrayIndex < GetDesc().ArraySize); + return m_ParentManager.m_ResourceCache.IsSSBOBound(GetAttribs().CacheOffset + 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(IResourceMapping* pResourceMapping, Uint32 Flags); + +#ifdef DILIGENT_DEVELOPMENT + bool dvpVerifyBindings(const ShaderResourceCacheGL& ResourceCache) const; +#endif + + IShaderResourceVariable* GetVariable(const Char* Name) const; + IShaderResourceVariable* GetVariable(Uint32 Index) const; + + IObject& GetOwner() { return m_Owner; } + + Uint32 GetVariableCount() const; + + // clang-format off + Uint32 GetNumUBs() const { return (m_TextureOffset - m_UBOffset) / sizeof(UniformBuffBindInfo); } + Uint32 GetNumTextures() const { return (m_ImageOffset - m_TextureOffset) / sizeof(SamplerBindInfo); } + Uint32 GetNumImages() const { return (m_StorageBufferOffset - m_ImageOffset) / sizeof(ImageBindInfo) ; } + Uint32 GetNumStorageBuffers() const { return (m_VariableEndOffset - m_StorageBufferOffset) / sizeof(StorageBufferBindInfo); } + // clang-format on + + template Uint32 GetNumResources() const; + + 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: + struct ResourceCounters + { + Uint32 NumUBs = 0; + Uint32 NumTextures = 0; + Uint32 NumImages = 0; + Uint32 NumStorageBlocks = 0; + }; + static void CountResources(const PipelineResourceSignatureGLImpl& Signature, + const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, + Uint32 NumAllowedTypes, + SHADER_TYPE ShaderType, + ResourceCounters& Counters); + + template + static void ProcessSignatureResources(const PipelineResourceSignatureGLImpl& Signature, + const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, + Uint32 NumAllowedTypes, + SHADER_TYPE ShaderType, + HandlerType Handler); + + // Offsets in bytes + using OffsetType = Uint16; + + template OffsetType GetResourceOffset() const; + + template + ResourceType& GetResource(Uint32 ResIndex) const + { + 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]; + } + + template + IShaderResourceVariable* GetResourceByName(const Char* Name) const; + + 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; + +private: + PipelineResourceSignatureGLImpl const* m_pSignature = nullptr; + + IObject& m_Owner; + // No need to use shared pointer, as the resource cache is either part of the same + // ShaderGLImpl object, or ShaderResourceBindingGLImpl object + ShaderResourceCacheGL& m_ResourceCache; + std::unique_ptr> m_ResourceBuffer; + + static constexpr OffsetType m_UBOffset = 0; + OffsetType m_TextureOffset = 0; + OffsetType m_ImageOffset = 0; + OffsetType m_StorageBufferOffset = 0; + OffsetType m_VariableEndOffset = 0; +}; + + +template <> +inline Uint32 ShaderVariableGL::GetNumResources() const +{ + return GetNumUBs(); +} + +template <> +inline Uint32 ShaderVariableGL::GetNumResources() const +{ + return GetNumTextures(); +} + +template <> +inline Uint32 ShaderVariableGL::GetNumResources() const +{ + return GetNumImages(); +} + +template <> +inline Uint32 ShaderVariableGL::GetNumResources() const +{ + return GetNumStorageBuffers(); +} + + + +template <> +inline ShaderVariableGL::OffsetType ShaderVariableGL:: + GetResourceOffset() const +{ + return m_UBOffset; +} + +template <> +inline ShaderVariableGL::OffsetType ShaderVariableGL:: + GetResourceOffset() const +{ + return m_TextureOffset; +} + +template <> +inline ShaderVariableGL::OffsetType ShaderVariableGL:: + GetResourceOffset() const +{ + return m_ImageOffset; +} + +template <> +inline ShaderVariableGL::OffsetType ShaderVariableGL:: + GetResourceOffset() const +{ + return m_StorageBufferOffset; +} + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineOpenGL/include/TextureBaseGL.hpp b/Graphics/GraphicsEngineOpenGL/include/TextureBaseGL.hpp index 805cfc50..6b507c67 100644 --- a/Graphics/GraphicsEngineOpenGL/include/TextureBaseGL.hpp +++ b/Graphics/GraphicsEngineOpenGL/include/TextureBaseGL.hpp @@ -84,7 +84,7 @@ public: GLenum GetGLTexFormat() const { return m_GLTexFormat; } - void TextureMemoryBarrier(Uint32 RequiredBarriers, class GLContextState& GLContextState); + void TextureMemoryBarrier(MEMORY_BARRIER RequiredBarriers, class GLContextState& GLContextState); virtual void AttachToFramebuffer(const struct TextureViewDesc& ViewDesc, GLenum AttachmentPoint) = 0; diff --git a/Graphics/GraphicsEngineOpenGL/src/BufferGLImpl.cpp b/Graphics/GraphicsEngineOpenGL/src/BufferGLImpl.cpp index 6c418533..d5812133 100644 --- a/Graphics/GraphicsEngineOpenGL/src/BufferGLImpl.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/BufferGLImpl.cpp @@ -220,7 +220,7 @@ IMPLEMENT_QUERY_INTERFACE(BufferGLImpl, IID_BufferGL, TBufferBase) void BufferGLImpl::UpdateData(GLContextState& CtxState, Uint32 Offset, Uint32 Size, const void* pData) { BufferMemoryBarrier( - GL_BUFFER_UPDATE_BARRIER_BIT, // Reads or writes to buffer objects via any OpenGL API functions that allow + MEMORY_BARRIER_BUFFER_UPDATE, // Reads or writes to buffer objects via any OpenGL API functions that allow // modifying their contents will reflect data written by shaders prior to the barrier. // Additionally, writes via these commands issued after the barrier will wait on // the completion of any shader writes to the same memory initiated prior to the barrier. @@ -240,13 +240,13 @@ void BufferGLImpl::UpdateData(GLContextState& CtxState, Uint32 Offset, Uint32 Si void BufferGLImpl::CopyData(GLContextState& CtxState, BufferGLImpl& SrcBufferGL, Uint32 SrcOffset, Uint32 DstOffset, Uint32 Size) { BufferMemoryBarrier( - GL_BUFFER_UPDATE_BARRIER_BIT, // Reads or writes to buffer objects via any OpenGL API functions that allow + MEMORY_BARRIER_BUFFER_UPDATE, // Reads or writes to buffer objects via any OpenGL API functions that allow // modifying their contents will reflect data written by shaders prior to the barrier. // Additionally, writes via these commands issued after the barrier will wait on // the completion of any shader writes to the same memory initiated prior to the barrier. CtxState); SrcBufferGL.BufferMemoryBarrier( - GL_BUFFER_UPDATE_BARRIER_BIT, + MEMORY_BARRIER_BUFFER_UPDATE, CtxState); // Whilst glCopyBufferSubData() can be used to copy data between buffers bound to any two targets, @@ -271,7 +271,7 @@ void BufferGLImpl::Map(GLContextState& CtxState, MAP_TYPE MapType, Uint32 MapFla void BufferGLImpl::MapRange(GLContextState& CtxState, MAP_TYPE MapType, Uint32 MapFlags, Uint32 Offset, Uint32 Length, PVoid& pMappedData) { BufferMemoryBarrier( - GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT, // Access by the client to persistent mapped regions of buffer + MEMORY_BARRIER_CLIENT_MAPPED_BUFFER, // Access by the client to persistent mapped regions of buffer // objects will reflect data written by shaders prior to the barrier. // Note that this may cause additional synchronization operations. CtxState); @@ -346,23 +346,12 @@ void BufferGLImpl::Unmap(GLContextState& CtxState) (void)Result; } -void BufferGLImpl::BufferMemoryBarrier(Uint32 RequiredBarriers, GLContextState& GLContextState) +void BufferGLImpl::BufferMemoryBarrier(MEMORY_BARRIER RequiredBarriers, GLContextState& GLContextState) { #if GL_ARB_shader_image_load_store # ifdef DILIGENT_DEBUG { - // clang-format off - constexpr Uint32 BufferBarriers = - GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT | - GL_ELEMENT_ARRAY_BARRIER_BIT | - GL_UNIFORM_BARRIER_BIT | - GL_COMMAND_BARRIER_BIT | - GL_BUFFER_UPDATE_BARRIER_BIT | - GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT | - GL_SHADER_STORAGE_BARRIER_BIT | - GL_TEXTURE_FETCH_BARRIER_BIT | - GL_SHADER_IMAGE_ACCESS_BARRIER_BIT; - // clang-format on + constexpr Uint32 BufferBarriers = MEMORY_BARRIER_ALL_BUFFER_BARRIERS; VERIFY((RequiredBarriers & BufferBarriers) != 0, "At least one buffer memory barrier flag should be set"); VERIFY((RequiredBarriers & ~BufferBarriers) == 0, "Inappropriate buffer memory barrier flag"); } diff --git a/Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp b/Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp index 530bc006..1c20b5bb 100644 --- a/Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp @@ -64,9 +64,8 @@ DeviceContextGLImpl::DeviceContextGLImpl(IReferenceCounters* pRefCounters, class pDeviceGL, bIsDeferred }, - m_ContextState {pDeviceGL}, - m_CommitedResourcesTentativeBarriers {0 }, - m_DefaultFBO {false } + m_ContextState{pDeviceGL}, + m_DefaultFBO {false } // clang-format on { m_BoundWritableTextures.reserve(16); @@ -78,6 +77,8 @@ IMPLEMENT_QUERY_INTERFACE(DeviceContextGLImpl, IID_DeviceContextGL, TDeviceConte void DeviceContextGLImpl::SetPipelineState(IPipelineState* pPipelineState) { + VERIFY_EXPR(pPipelineState != nullptr); + auto* pPipelineStateGLImpl = ValidatedCast(pPipelineState); if (PipelineStateGLImpl::IsSameObject(m_pPipelineState, pPipelineStateGLImpl)) return; @@ -151,9 +152,53 @@ void DeviceContextGLImpl::SetPipelineState(IPipelineState* pPipelineState) } // Note that the program may change if a shader is created after the call - // (GLProgramResources needs to bind a program to load uniforms), but before + // (ShaderResourcesGL needs to bind a program to load uniforms), but before // the draw command. m_pPipelineState->CommitProgram(m_ContextState); + + const auto SignCount = m_pPipelineState->GetSignatureCount(); + + m_BindInfo.ActiveSRBMask = 0; + for (Uint32 s = 0; s < SignCount; ++s) + { + const auto* pLayoutSign = m_pPipelineState->GetSignature(s); + if (pLayoutSign == nullptr || pLayoutSign->GetTotalResourceCount() == 0) + continue; + + m_BindInfo.ActiveSRBMask |= (1u << s); + } + +#ifdef DILIGENT_DEVELOPMENT + // Find the first incompatible shader resource bindings + Uint32 sign = 0; + for (; sign < SignCount; ++sign) + { + const auto* pLayoutSign = m_pPipelineState->GetSignature(sign); + const auto* pSRBSign = m_BindInfo.SRBs[sign] != nullptr ? m_BindInfo.SRBs[sign]->GetSignature() : nullptr; + + if ((pLayoutSign == nullptr || pLayoutSign->GetTotalResourceCount() == 0) != (pSRBSign == nullptr || pSRBSign->GetTotalResourceCount() == 0)) + { + // One signature is null or empty while the other is not - SRB is not compatible with the layout. + break; + } + + if (pLayoutSign != nullptr && pSRBSign != nullptr && pLayoutSign->IsIncompatibleWith(*pSRBSign)) + { + // Signatures are incompatible + break; + } + } + + // Unbind incompatible SRB and SRB with a higher binding index. + // This is the same behavior that used in Vulkan backend. + for (; sign < SignCount; ++sign) + { + m_BindInfo.SRBs[sign] = nullptr; + m_BindInfo.ClearStaleSRBBit(sign); + } + + m_BindInfo.CommittedResourcesValidated = false; +#endif } void DeviceContextGLImpl::TransitionShaderResources(IPipelineState* pPipelineState, IShaderResourceBinding* pShaderResourceBinding) @@ -170,13 +215,15 @@ void DeviceContextGLImpl::CommitShaderResources(IShaderResourceBinding* pShaderR if (!DeviceContextBase::CommitShaderResources(pShaderResourceBinding, StateTransitionMode, 0)) return; - if (m_CommitedResourcesTentativeBarriers != 0) - LOG_INFO_MESSAGE("Not all tentative resource barriers have been executed since the last call to CommitShaderResources(). Did you forget to call Draw()/DispatchCompute() ?"); + auto* pShaderResBindingGL = ValidatedCast(pShaderResourceBinding); + const auto SRBIndex = pShaderResBindingGL->GetBindingIndex(); + + m_BindInfo.SRBs[SRBIndex] = pShaderResBindingGL; + m_BindInfo.SetStaleSRBBit(SRBIndex); - m_CommitedResourcesTentativeBarriers = 0; - BindProgramResources(m_CommitedResourcesTentativeBarriers, pShaderResourceBinding); - // m_CommitedResourcesTentativeBarriers will contain memory barriers that will be required - // AFTER the actual draw/dispatch command is executed. Before that they have no meaning +#ifdef DILIGENT_DEVELOPMENT + m_BindInfo.CommittedResourcesValidated = false; +#endif } void DeviceContextGLImpl::SetStencilRef(Uint32 StencilRef) @@ -464,7 +511,7 @@ void DeviceContextGLImpl::BeginSubpass() auto* const pColorTexGL = pRTV->GetTexture(); pColorTexGL->TextureMemoryBarrier( - GL_FRAMEBUFFER_BARRIER_BIT, // Reads and writes via framebuffer object attachments after the + MEMORY_BARRIER_FRAMEBUFFER, // Reads and writes via framebuffer object attachments after the // barrier will reflect data written by shaders prior to the barrier. // Additionally, framebuffer writes issued after the barrier will wait // on the completion of all shader writes issued prior to the barrier. @@ -488,7 +535,7 @@ void DeviceContextGLImpl::BeginSubpass() if (pDSV != nullptr) { auto* pDepthTexGL = pDSV->GetTexture(); - pDepthTexGL->TextureMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT, m_ContextState); + pDepthTexGL->TextureMemoryBarrier(MEMORY_BARRIER_FRAMEBUFFER, m_ContextState); const auto& AttachmentDesc = RPDesc.pAttachments[DepthAttachmentIndex]; auto FirstLastUse = m_pActiveRenderPass->GetAttachmentFirstLastUse(DepthAttachmentIndex); @@ -634,27 +681,65 @@ void DeviceContextGLImpl::EndRenderPass() m_ContextState.InvalidateFBO(); } -void DeviceContextGLImpl::BindProgramResources(Uint32& NewMemoryBarriers, IShaderResourceBinding* pResBinding) +#ifdef DILIGENT_DEVELOPMENT +void DeviceContextGLImpl::DvpValidateCommittedShaderResources() { - if (!m_pPipelineState) - { - LOG_ERROR_MESSAGE("No pipeline state is bound"); + if (m_BindInfo.CommittedResourcesValidated) return; - } - if (pResBinding == nullptr) + m_pPipelineState->DvpVerifySRBResources(m_BindInfo.SRBs.data(), m_BindInfo.BoundResOffsets.data(), static_cast(m_BindInfo.SRBs.size())); + m_BindInfo.CommittedResourcesValidated = true; +} +#endif + +void DeviceContextGLImpl::BindProgramResources() +{ + //if (m_CommitedResourcesTentativeBarriers != 0) + // LOG_INFO_MESSAGE("Not all tentative resource barriers have been executed since the last call to CommitShaderResources(). Did you forget to call Draw()/DispatchCompute() ?"); + + if ((m_BindInfo.StaleSRBMask & m_BindInfo.ActiveSRBMask) == 0) return; - auto* pShaderResBindingGL = ValidatedCast(pResBinding); - const auto& ResourceCache = pShaderResBindingGL->GetResourceCache(m_pPipelineState); + m_CommitedResourcesTentativeBarriers = MEMORY_BARRIER_NONE; + TBindings Bindings = {}; + + auto ActiveSRBMask = Uint32{m_BindInfo.ActiveSRBMask}; + while (ActiveSRBMask != 0) + { + Uint32 sign = PlatformMisc::GetLSB(ActiveSRBMask); + Uint32 SigBit = (1u << sign); + VERIFY_EXPR(sign < m_pPipelineState->GetSignatureCount()); + + ActiveSRBMask &= ~SigBit; + + const auto* pSRB = m_BindInfo.SRBs[sign]; + VERIFY_EXPR(pSRB); + + //if (m_BindInfo.StaleSRBMask & SigBit) + { + BindProgramResources(m_CommitedResourcesTentativeBarriers, pSRB, Bindings); + #ifdef DILIGENT_DEVELOPMENT - m_pPipelineState->GetResourceLayout().dvpVerifyBindings(ResourceCache); + m_BindInfo.BoundResOffsets[sign] = Bindings; #endif + } + + pSRB->GetSignature()->AddBindings(Bindings); + } + + m_BindInfo.StaleSRBMask &= ~m_BindInfo.ActiveSRBMask; +} + +void DeviceContextGLImpl::BindProgramResources(MEMORY_BARRIER& NewMemoryBarriers, const ShaderResourceBindingGLImpl* pShaderResBindingGL, TBindings& Bindings) +{ + const auto SRBIndex = pShaderResBindingGL->GetBindingIndex(); + const auto& ResourceCache = pShaderResBindingGL->GetResourceCache(); + VERIFY_EXPR(m_BoundWritableTextures.empty()); VERIFY_EXPR(m_BoundWritableBuffers.empty()); - for (Uint32 ub = 0; ub < ResourceCache.GetUBCount(); ++ub) + for (Uint32 ub = 0, binding = Bindings[BINDING_RANGE_UNIFORM_BUFFER]; ub < ResourceCache.GetUBCount(); ++ub, ++binding) { const auto& UB = ResourceCache.GetConstUB(ub); if (!UB.pBuffer) @@ -662,17 +747,17 @@ void DeviceContextGLImpl::BindProgramResources(Uint32& NewMemoryBarriers, IShade 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 + MEMORY_BARRIER_UNIFORM_BUFFER, // Shader uniforms sourced from buffer objects after the barrier + // will reflect data written by shaders prior to the barrier m_ContextState); - m_ContextState.BindUniformBuffer(ub, pBufferGL->m_GlBuffer); + m_ContextState.BindUniformBuffer(binding, pBufferGL->m_GlBuffer); //glBindBufferRange(GL_UNIFORM_BUFFER, it->Index, pBufferGL->m_GlBuffer, 0, pBufferGL->GetDesc().uiSizeInBytes); } - for (Uint32 s = 0; s < ResourceCache.GetSamplerCount(); ++s) + for (Uint32 s = 0, binding = Bindings[BINDING_RANGE_TEXTURE]; s < ResourceCache.GetTextureCount(); ++s, ++binding) { - const auto& Sam = ResourceCache.GetConstSampler(s); + const auto& Sam = ResourceCache.GetConstTexture(s); if (!Sam.pView) continue; @@ -682,21 +767,21 @@ void DeviceContextGLImpl::BindProgramResources(Uint32& NewMemoryBarriers, IShade auto* pTexViewGL = Sam.pView.RawPtr(); auto* pTextureGL = ValidatedCast(Sam.pTexture); VERIFY_EXPR(pTextureGL == pTexViewGL->GetTexture()); - m_ContextState.BindTexture(s, pTexViewGL->GetBindTarget(), pTexViewGL->GetHandle()); + m_ContextState.BindTexture(binding, pTexViewGL->GetBindTarget(), pTexViewGL->GetHandle()); pTextureGL->TextureMemoryBarrier( - GL_TEXTURE_FETCH_BARRIER_BIT, // Texture fetches from shaders, including fetches from buffer object + MEMORY_BARRIER_TEXTURE_FETCH, // 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) { - m_ContextState.BindSampler(s, Sam.pSampler->GetHandle()); + m_ContextState.BindSampler(binding, Sam.pSampler->GetHandle()); } else { - m_ContextState.BindSampler(s, GLObjectWrappers::GLSamplerObj(false)); + m_ContextState.BindSampler(binding, GLObjectWrappers::GLSamplerObj(false)); } } else if (Sam.pBuffer != nullptr) @@ -705,19 +790,19 @@ void DeviceContextGLImpl::BindProgramResources(Uint32& NewMemoryBarriers, IShade 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 + m_ContextState.BindTexture(binding, GL_TEXTURE_BUFFER, pBufViewGL->GetTexBufferHandle()); + m_ContextState.BindSampler(binding, 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 + MEMORY_BARRIER_TEXEL_BUFFER, // 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 < ResourceCache.GetImageCount(); ++img) + for (Uint32 img = 0, binding = Bindings[BINDING_RANGE_IMAGE]; img < ResourceCache.GetImageCount(); ++img, ++binding) { const auto& Img = ResourceCache.GetConstImage(img); if (!Img.pView) @@ -736,12 +821,12 @@ void DeviceContextGLImpl::BindProgramResources(Uint32& NewMemoryBarriers, IShade if (ViewDesc.AccessFlags & UAV_ACCESS_FLAG_WRITE) { 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. + MEMORY_BARRIER_STORAGE_IMAGE, // 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 @@ -774,7 +859,7 @@ void DeviceContextGLImpl::BindProgramResources(Uint32& NewMemoryBarriers, IShade // 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); + m_ContextState.BindImage(binding, 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) @@ -787,25 +872,25 @@ void DeviceContextGLImpl::BindProgramResources(Uint32& NewMemoryBarriers, IShade 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. + MEMORY_BARRIER_IMAGE_BUFFER, // 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); + m_ContextState.BindImage(binding, pBuffViewGL, GL_READ_WRITE, GlFormat); } } #endif #if GL_ARB_shader_storage_buffer_object - for (Uint32 ssbo = 0; ssbo < ResourceCache.GetSSBOCount(); ++ssbo) + for (Uint32 ssbo = 0, binding = Bindings[BINDING_RANGE_STORAGE_BUFFER]; ssbo < ResourceCache.GetSSBOCount(); ++ssbo, ++binding) { const auto& SSBO = ResourceCache.GetConstSSBO(ssbo); if (!SSBO.pBufferView) @@ -817,11 +902,11 @@ void DeviceContextGLImpl::BindProgramResources(Uint32& NewMemoryBarriers, IShade auto* pBufferGL = pBufferViewGL->GetBuffer(); pBufferGL->BufferMemoryBarrier( - GL_SHADER_STORAGE_BARRIER_BIT, // Accesses to shader storage blocks after the barrier + MEMORY_BARRIER_STORAGE_BUFFER, // Accesses to shader storage blocks after the barrier // will reflect writes prior to the barrier m_ContextState); - m_ContextState.BindStorageBlock(ssbo, pBufferGL->m_GlBuffer, ViewDesc.ByteOffset, ViewDesc.ByteWidth); + m_ContextState.BindStorageBlock(binding, pBufferGL->m_GlBuffer, ViewDesc.ByteOffset, ViewDesc.ByteWidth); if (ViewDesc.ViewType == BUFFER_VIEW_UNORDERED_ACCESS) m_BoundWritableBuffers.push_back(pBufferGL); @@ -833,26 +918,7 @@ void DeviceContextGLImpl::BindProgramResources(Uint32& NewMemoryBarriers, IShade // Go through the list of textures bound as AUVs and set the required memory barriers for (auto* pWritableTex : m_BoundWritableTextures) { - Uint32 TextureMemBarriers = - GL_TEXTURE_UPDATE_BARRIER_BIT // Writes to a texture via glTex(Sub)Image*, glCopyTex(Sub)Image*, - // glClearTex*Image, glCompressedTex(Sub)Image*, and reads via - // glGetTexImage() after the barrier will reflect data written by - // shaders prior to the barrier - - | 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 - - | GL_PIXEL_BUFFER_BARRIER_BIT // Reads and writes of buffer objects via the GL_PIXEL_PACK_BUFFER and - // GL_PIXEL_UNPACK_BUFFER bidnings after the barrier will reflect data - // written by shaders prior to the barrier - - | GL_FRAMEBUFFER_BARRIER_BIT // Reads and writes via framebuffer object attachments after the - // barrier will reflect data written by shaders prior to the barrier. - // Additionally, framebuffer writes issued after the barrier will wait - // on the completion of all shader writes issued prior to the barrier. - - | GL_SHADER_IMAGE_ACCESS_BARRIER_BIT; + constexpr MEMORY_BARRIER TextureMemBarriers = MEMORY_BARRIER_ALL_TEXTURE_BARRIERS; NewMemoryBarriers |= TextureMemBarriers; @@ -863,18 +929,7 @@ void DeviceContextGLImpl::BindProgramResources(Uint32& NewMemoryBarriers, IShade for (auto* pWritableBuff : m_BoundWritableBuffers) { - // clang-format off - Uint32 BufferMemoryBarriers = - GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT | - GL_ELEMENT_ARRAY_BARRIER_BIT | - GL_UNIFORM_BARRIER_BIT | - GL_COMMAND_BARRIER_BIT | - GL_BUFFER_UPDATE_BARRIER_BIT | - GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT | - GL_SHADER_STORAGE_BARRIER_BIT | - GL_TEXTURE_FETCH_BARRIER_BIT | - GL_SHADER_IMAGE_ACCESS_BARRIER_BIT; - // clang-format on + constexpr MEMORY_BARRIER BufferMemoryBarriers = MEMORY_BARRIER_ALL_BUFFER_BARRIERS; NewMemoryBarriers |= BufferMemoryBarriers; // Set new required barriers for the time when buffer is used next time @@ -892,8 +947,9 @@ void DeviceContextGLImpl::PrepareForDraw(DRAW_FLAGS Flags, bool IsIndexed, GLenu #endif // The program might have changed since the last SetPipelineState call if a shader was - // created after the call (GLProgramResources needs to bind a program to load uniforms). + // created after the call (ShaderResourcesGL needs to bind a program to load uniforms). m_pPipelineState->CommitProgram(m_ContextState); + BindProgramResources(); auto CurrNativeGLContext = m_pDevice->m_GLContext.GetCurrentNativeGLContext(); const auto& PipelineDesc = m_pPipelineState->GetGraphicsPipelineDesc(); @@ -931,6 +987,10 @@ void DeviceContextGLImpl::PrepareForDraw(DRAW_FLAGS Flags, bool IsIndexed, GLenu { GlTopology = PrimitiveTopologyToGLTopology(Topology); } + +#ifdef DILIGENT_DEVELOPMENT + DvpValidateCommittedShaderResources(); +#endif } void DeviceContextGLImpl::PrepareForIndexedDraw(VALUE_TYPE IndexType, Uint32 FirstIndexLocation, GLenum& GLIndexType, Uint32& FirstIndexByteOffset) @@ -949,7 +1009,7 @@ void DeviceContextGLImpl::PostDraw() // m_CommitedResourcesTentativeBarriers contains memory barriers that will be required // AFTER the actual draw/dispatch command is executed. m_ContextState.SetPendingMemoryBarriers(m_CommitedResourcesTentativeBarriers); - m_CommitedResourcesTentativeBarriers = 0; + m_CommitedResourcesTentativeBarriers = MEMORY_BARRIER_NONE; } void DeviceContextGLImpl::Draw(const DrawAttribs& Attribs) @@ -1029,11 +1089,11 @@ void DeviceContextGLImpl::PrepareForIndirectDraw(IBuffer* pAttribsBuffer) // GL_DRAW_INDIRECT_BUFFER binding. Thus, any of indirect draw functions will fail if no buffer is // bound to that binding. pIndirectDrawAttribsGL->BufferMemoryBarrier( - GL_COMMAND_BARRIER_BIT, // Command data sourced from buffer objects by - // Draw*Indirect and DispatchComputeIndirect commands after the barrier - // will reflect data written by shaders prior to the barrier.The buffer - // objects affected by this bit are derived from the DRAW_INDIRECT_BUFFER - // and DISPATCH_INDIRECT_BUFFER bindings. + MEMORY_BARRIER_INDIRECT_BUFFER, // Command data sourced from buffer objects by + // Draw*Indirect and DispatchComputeIndirect commands after the barrier + // will reflect data written by shaders prior to the barrier.The buffer + // objects affected by this bit are derived from the DRAW_INDIRECT_BUFFER + // and DISPATCH_INDIRECT_BUFFER bindings. m_ContextState); constexpr bool ResetVAO = false; // GL_DRAW_INDIRECT_BUFFER does not affect VAO m_ContextState.BindBuffer(GL_DRAW_INDIRECT_BUFFER, pIndirectDrawAttribsGL->m_GlBuffer, ResetVAO); @@ -1122,10 +1182,15 @@ void DeviceContextGLImpl::DispatchCompute(const DispatchComputeAttribs& Attribs) if (!DvpVerifyDispatchArguments(Attribs)) return; +#ifdef DILIGENT_DEVELOPMENT + DvpValidateCommittedShaderResources(); +#endif + #if GL_ARB_compute_shader // The program might have changed since the last SetPipelineState call if a shader was - // created after the call (GLProgramResources needs to bind a program to load uniforms). + // created after the call (ShaderResourcesGL needs to bind a program to load uniforms). m_pPipelineState->CommitProgram(m_ContextState); + BindProgramResources(); glDispatchCompute(Attribs.ThreadGroupCountX, Attribs.ThreadGroupCountY, Attribs.ThreadGroupCountZ); DEV_CHECK_GL_ERROR("glDispatchCompute() failed"); @@ -1140,18 +1205,23 @@ void DeviceContextGLImpl::DispatchComputeIndirect(const DispatchComputeIndirectA if (!DvpVerifyDispatchIndirectArguments(Attribs, pAttribsBuffer)) return; +#ifdef DILIGENT_DEVELOPMENT + DvpValidateCommittedShaderResources(); +#endif + #if GL_ARB_compute_shader // The program might have changed since the last SetPipelineState call if a shader was - // created after the call (GLProgramResources needs to bind a program to load uniforms). + // created after the call (ShaderResourcesGL needs to bind a program to load uniforms). m_pPipelineState->CommitProgram(m_ContextState); + BindProgramResources(); auto* pBufferGL = ValidatedCast(pAttribsBuffer); pBufferGL->BufferMemoryBarrier( - GL_COMMAND_BARRIER_BIT, // Command data sourced from buffer objects by - // Draw*Indirect and DispatchComputeIndirect commands after the barrier - // will reflect data written by shaders prior to the barrier.The buffer - // objects affected by this bit are derived from the DRAW_INDIRECT_BUFFER - // and DISPATCH_INDIRECT_BUFFER bindings. + MEMORY_BARRIER_INDIRECT_BUFFER, // Command data sourced from buffer objects by + // Draw*Indirect and DispatchComputeIndirect commands after the barrier + // will reflect data written by shaders prior to the barrier.The buffer + // objects affected by this bit are derived from the DRAW_INDIRECT_BUFFER + // and DISPATCH_INDIRECT_BUFFER bindings. m_ContextState); constexpr bool ResetVAO = false; // GL_DISPATCH_INDIRECT_BUFFER does not affect VAO @@ -1270,6 +1340,8 @@ void DeviceContextGLImpl::Flush() } glFlush(); + + m_BindInfo.SRBs.fill(nullptr); } void DeviceContextGLImpl::FinishFrame() diff --git a/Graphics/GraphicsEngineOpenGL/src/FBOCache.cpp b/Graphics/GraphicsEngineOpenGL/src/FBOCache.cpp index 25c9bd5f..e09712e0 100644 --- a/Graphics/GraphicsEngineOpenGL/src/FBOCache.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/FBOCache.cpp @@ -246,7 +246,7 @@ const GLObjectWrappers::GLFrameBufferObj& FBOCache::GetFBO(Uint32 Nu auto* pColorTexGL = pRTView->GetTexture(); pColorTexGL->TextureMemoryBarrier( - GL_FRAMEBUFFER_BARRIER_BIT, // Reads and writes via framebuffer object attachments after the + MEMORY_BARRIER_FRAMEBUFFER, // Reads and writes via framebuffer object attachments after the // barrier will reflect data written by shaders prior to the barrier. // Additionally, framebuffer writes issued after the barrier will wait // on the completion of all shader writes issued prior to the barrier. @@ -259,7 +259,7 @@ const GLObjectWrappers::GLFrameBufferObj& FBOCache::GetFBO(Uint32 Nu if (pDSV) { auto* pDepthTexGL = pDSV->GetTexture(); - pDepthTexGL->TextureMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT, ContextState); + pDepthTexGL->TextureMemoryBarrier(MEMORY_BARRIER_FRAMEBUFFER, ContextState); Key.DSId = pDepthTexGL->GetUniqueID(); Key.DSVDesc = pDSV->GetDesc(); } diff --git a/Graphics/GraphicsEngineOpenGL/src/GLContextState.cpp b/Graphics/GraphicsEngineOpenGL/src/GLContextState.cpp index 21220f32..fa60c34a 100644 --- a/Graphics/GraphicsEngineOpenGL/src/GLContextState.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/GLContextState.cpp @@ -80,7 +80,7 @@ void GLContextState::Invalidate() // executed next frame when needed if (m_PendingMemoryBarriers != 0) EnsureMemoryBarrier(m_PendingMemoryBarriers); - m_PendingMemoryBarriers = 0; + m_PendingMemoryBarriers = MEMORY_BARRIER_NONE; #endif // Unity messes up at least VAO left in the context, @@ -378,7 +378,7 @@ void GLContextState::BindBuffer(GLenum BindTarget, const GLObjectWrappers::GLBuf DEV_CHECK_GL_ERROR("Failed to bind buffer ", static_cast(Buff), " to target ", BindTarget); } -void GLContextState::EnsureMemoryBarrier(Uint32 RequiredBarriers, AsyncWritableResource* pRes /* = nullptr */) +void GLContextState::EnsureMemoryBarrier(MEMORY_BARRIER RequiredBarriers, AsyncWritableResource* pRes /* = nullptr */) { #if GL_ARB_shader_image_load_store // Every resource tracks its own pending memory barriers. @@ -401,7 +401,7 @@ void GLContextState::EnsureMemoryBarrier(Uint32 RequiredBarriers, AsyncWritableR // This situation does not seem to be a problem though since a barier cannot be executed // twice in any situation - Uint32 ResourcePendingBarriers = 0; + MEMORY_BARRIER ResourcePendingBarriers = MEMORY_BARRIER_NONE; if (pRes) { // If resource is specified, only set up memory barriers @@ -427,7 +427,7 @@ void GLContextState::EnsureMemoryBarrier(Uint32 RequiredBarriers, AsyncWritableR #endif } -void GLContextState::SetPendingMemoryBarriers(Uint32 PendingBarriers) +void GLContextState::SetPendingMemoryBarriers(MEMORY_BARRIER PendingBarriers) { m_PendingMemoryBarriers |= PendingBarriers; } diff --git a/Graphics/GraphicsEngineOpenGL/src/GLPipelineResourceLayout.cpp b/Graphics/GraphicsEngineOpenGL/src/GLPipelineResourceLayout.cpp deleted file mode 100644 index a6c09f06..00000000 --- a/Graphics/GraphicsEngineOpenGL/src/GLPipelineResourceLayout.cpp +++ /dev/null @@ -1,838 +0,0 @@ -/* - * Copyright 2019-2021 Diligent Graphics LLC - * Copyright 2015-2019 Egor Yusov - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * In no event and under no legal theory, whether in tort (including negligence), - * contract, or otherwise, unless required by applicable law (such as deliberate - * and grossly negligent acts) or agreed to in writing, shall any Contributor be - * liable for any damages, including any direct, indirect, special, incidental, - * or consequential damages of any character arising as a result of this License or - * out of the use or inability to use the software (including but not limited to damages - * for loss of goodwill, work stoppage, computer failure or malfunction, or any and - * all other commercial damages or losses), even if such Contributor has been advised - * of the possibility of such damages. - */ - -#include "pch.h" -#include -#include "GLPipelineResourceLayout.hpp" -#include "Align.hpp" -#include "PlatformMisc.hpp" -#include "ShaderBase.hpp" - -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); - } - - // clang-format off - size_t RequiredSize = Counters.NumUBs * sizeof(UniformBuffBindInfo) + - Counters.NumSamplers * sizeof(SamplerBindInfo) + - Counters.NumImages * sizeof(ImageBindInfo) + - Counters.NumStorageBlocks * sizeof(StorageBufferBindInfo) + - NumPrograms * sizeof(GLProgramResources::ResourceCounters); - // clang-format on - return RequiredSize; -} - -void GLPipelineResourceLayout::Initialize(GLProgramResources* ProgramResources, - Uint32 NumPrograms, - PIPELINE_TYPE PipelineType, - 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, ")"); - (void)MaxOffset; - auto Offset = static_cast(CurrentOffset); - CurrentOffset += NumBytes; - return Offset; - }; - - // clang-format off - 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); - // clang-format off - 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)); - - m_PipelineType = PipelineType; - - 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); - } - - // clang-format off - VERIFY_EXPR(Counters.NumUBs == GetNumUBs() ); - VERIFY_EXPR(Counters.NumSamplers == GetNumSamplers() ); - VERIFY_EXPR(Counters.NumImages == GetNumImages() ); - VERIFY_EXPR(Counters.NumStorageBlocks == GetNumStorageBuffers()); - // clang-format on - - // Current resource index for every resource type - GLProgramResources::ResourceCounters VarCounters = {}; - - Uint32 UniformBindingSlots = 0; - Uint32 SamplerBindingSlots = 0; - Uint32 ImageBindingSlots = 0; - Uint32 SSBOBindingSlots = 0; - -#ifdef DILIGENT_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 ImtblSamplerIdx = -1; - if (Sam.ResourceType == SHADER_RESOURCE_TYPE_TEXTURE_SRV) - { - ImtblSamplerIdx = FindImmutableSampler(ResourceLayout.ImmutableSamplers, ResourceLayout.NumImmutableSamplers, ShaderStages, - Sam.Name, nullptr); - } - auto* pSamVar = new (&GetResource(VarCounters.NumSamplers++)) - SamplerBindInfo // - { - Sam, - *this, - VarType, - ImtblSamplerIdx // - }; - 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 = GetShaderTypePipelineIndex(Stage, PipelineType); - m_ProgramIndex[ShaderInd] = static_cast(prog); - ShaderStages = static_cast(Uint32{ShaderStages} & ~Stage); - } - } - - // clang-format off - 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"); - // clang-format on - - 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() -{ - // clang-format off - HandleResources( - [&](UniformBuffBindInfo& ub) - { - ub.~UniformBuffBindInfo(); - }, - - [&](SamplerBindInfo& sam) - { - sam.~SamplerBindInfo(); - }, - - [&](ImageBindInfo& img) - { - img.~ImageBindInfo(); - }, - - [&](StorageBufferBindInfo& ssbo) - { - ssbo.~StorageBufferBindInfo(); - } - ); - // clang-format on -} - - -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 DILIGENT_DEVELOPMENT - { - const auto& CachedUB = ResourceCache.GetConstUB(m_Attribs.Binding + ArrayIndex); - VerifyConstantBufferBinding(m_Attribs, GetType(), ArrayIndex, pBuffer, pBuffGLImpl.RawPtr(), CachedUB.pBuffer.RawPtr()); - } -#endif - - ResourceCache.SetUniformBuffer(m_Attribs.Binding + ArrayIndex, std::move(pBuffGLImpl)); -} - - - -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 DILIGENT_DEVELOPMENT - { - auto& CachedTexSampler = ResourceCache.GetConstSampler(m_Attribs.Binding + ArrayIndex); - VerifyResourceViewBinding(m_Attribs, GetType(), ArrayIndex, pView, pViewGL.RawPtr(), {TEXTURE_VIEW_SHADER_RESOURCE}, CachedTexSampler.pView.RawPtr()); - if (m_ImtblSamplerIdx >= 0) - { - VERIFY(CachedTexSampler.pSampler != nullptr, "Immutable samplers must be initialized by PipelineStateGLImpl::InitializeSRBResourceCache!"); - } - } -#endif - ResourceCache.SetTexSampler(m_Attribs.Binding + ArrayIndex, std::move(pViewGL), m_ImtblSamplerIdx < 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 DILIGENT_DEVELOPMENT - { - auto& CachedBuffSampler = ResourceCache.GetConstSampler(m_Attribs.Binding + ArrayIndex); - VerifyResourceViewBinding(m_Attribs, GetType(), ArrayIndex, pView, pViewGL.RawPtr(), {BUFFER_VIEW_SHADER_RESOURCE}, CachedBuffSampler.pView.RawPtr()); - if (pViewGL != nullptr) - { - const auto& ViewDesc = pViewGL->GetDesc(); - const auto& BuffDesc = pViewGL->GetBuffer()->GetDesc(); - if (!((BuffDesc.Mode == BUFFER_MODE_FORMATTED && ViewDesc.Format.ValueType != VT_UNDEFINED) || BuffDesc.Mode == BUFFER_MODE_RAW)) - { - LOG_ERROR_MESSAGE("Error binding buffer view '", ViewDesc.Name, "' of buffer '", BuffDesc.Name, "' to shader variable '", - m_Attribs.Name, ": formatted buffer view is expected."); - } - } - } -#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 DILIGENT_DEVELOPMENT - { - auto& CachedUAV = ResourceCache.GetConstImage(m_Attribs.Binding + ArrayIndex); - VerifyResourceViewBinding(m_Attribs, GetType(), ArrayIndex, pView, pViewGL.RawPtr(), {TEXTURE_VIEW_UNORDERED_ACCESS}, CachedUAV.pView.RawPtr()); - } -#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 DILIGENT_DEVELOPMENT - { - auto& CachedUAV = ResourceCache.GetConstImage(m_Attribs.Binding + ArrayIndex); - VerifyResourceViewBinding(m_Attribs, GetType(), ArrayIndex, pView, pViewGL.RawPtr(), {BUFFER_VIEW_UNORDERED_ACCESS}, CachedUAV.pView.RawPtr()); - if (pViewGL != nullptr) - { - const auto& ViewDesc = pViewGL->GetDesc(); - const auto& BuffDesc = pViewGL->GetBuffer()->GetDesc(); - if (!((BuffDesc.Mode == BUFFER_MODE_FORMATTED && ViewDesc.Format.ValueType != VT_UNDEFINED) || BuffDesc.Mode == BUFFER_MODE_RAW)) - { - LOG_ERROR_MESSAGE("Error binding buffer view '", ViewDesc.Name, "' of buffer '", BuffDesc.Name, "' to shader variable '", - m_Attribs.Name, ": formatted buffer view is expected."); - } - } - } -#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 DILIGENT_DEVELOPMENT - { - auto& CachedSSBO = ResourceCache.GetConstSSBO(m_Attribs.Binding + ArrayIndex); - // HLSL structured buffers are mapped to SSBOs in GLSL - VerifyResourceViewBinding(m_Attribs, GetType(), ArrayIndex, pView, pViewGL.RawPtr(), {BUFFER_VIEW_SHADER_RESOURCE, BUFFER_VIEW_UNORDERED_ACCESS}, CachedSSBO.pBufferView.RawPtr()); - if (pViewGL != nullptr) - { - const auto& ViewDesc = pViewGL->GetDesc(); - const auto& BuffDesc = pViewGL->GetBuffer()->GetDesc(); - if (BuffDesc.Mode != BUFFER_MODE_STRUCTURED && BuffDesc.Mode != BUFFER_MODE_RAW) - { - LOG_ERROR_MESSAGE("Error binding buffer view '", ViewDesc.Name, "' of buffer '", BuffDesc.Name, "' to shader variable '", - m_Attribs.Name, ": structured buffer view is expected."); - } - } - } -#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) : - // clang-format off - ResourceMapping {RM}, - ShaderStage {SS}, - Flags {Fl} - // clang-format on - { - } - - 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); - - // clang-format off - HandleResources( - [&](UniformBuffBindInfo& ub) - { - BindResHelper.Bind(ub); - }, - - [&](SamplerBindInfo& sam) - { - BindResHelper.Bind(sam); - }, - - [&](ImageBindInfo& img) - { - BindResHelper.Bind(img); - }, - - [&](StorageBufferBindInfo& ssbo) - { - BindResHelper.Bind(ssbo); - } - ); - // clang-format on -} - - -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) -{ - VERIFY_EXPR(IsConsistentShaderType(ShaderStage, static_cast(m_PipelineType))); - - 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_EXPR(IsConsistentShaderType(ShaderStage, static_cast(m_PipelineType))); - VERIFY(IsPowerOfTwo(Uint32{ShaderStage}), "Only one shader stage must be specified"); - auto ShaderInd = GetShaderTypePipelineIndex(ShaderStage, static_cast(m_PipelineType)); - auto ProgIdx = m_ProgramIndex[ShaderInd]; - - if (ProgIdx < 0) - { - LOG_WARNING_MESSAGE("Unable to get the number of variables in shader stage ", GetShaderTypeLiteralName(ShaderStage), - " as the stage is inactive"); - return 0; - } - - const auto& VariableEndOffset = GetProgramVarEndOffsets(ProgIdx); - const auto& VariableStartOffset = ProgIdx > 0 ? GetProgramVarEndOffsets(ProgIdx - 1) : GLProgramResources::ResourceCounters{}; - - // clang-format off - Uint32 NumVars = VariableEndOffset.NumUBs - VariableStartOffset.NumUBs + - VariableEndOffset.NumSamplers - VariableStartOffset.NumSamplers + - VariableEndOffset.NumImages - VariableStartOffset.NumImages + - VariableEndOffset.NumStorageBlocks - VariableStartOffset.NumStorageBlocks; - // clang-format on - -#ifdef DILIGENT_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) : - // clang-format off - Layout {_Layout}, - Index {_Index} - // clang-format on - { - } - - 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 = GetShaderTypePipelineIndex(ShaderStage, static_cast(m_PipelineType)); - 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) : - // clang-format off - Layout {_Layout}, - VarOffset(reinterpret_cast(&Variable) - reinterpret_cast(_Layout.m_ResourceBuffer.get())) - // clang-format on - {} - - 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[GetShaderTypePipelineIndex(FirstStage, static_cast(m_PipelineType))]; - 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; - // clang-format off - 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" ); - // clang-format on - - HandleConstResources( - [&](const UniformBuffBindInfo& UB) // - { - for (auto binding = UB.m_Attribs.Binding; binding < UB.m_Attribs.Binding + UB.m_Attribs.ArraySize; ++binding) - { - auto pBufferGL = SrcCache.GetConstUB(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.GetConstSampler(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.GetConstImage(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.GetConstSSBO(binding).pBufferView; - DstCache.SetSSBO(binding, std::move(pBufferView)); - } - } // - ); -} - -#ifdef DILIGENT_DEVELOPMENT -bool GLPipelineResourceLayout::dvpVerifyBindings(const GLProgramResourceCache& ResourceCache) const -{ -# define LOG_MISSING_BINDING(VarType, BindInfo, BindPt) LOG_ERROR_MESSAGE("No resource is bound to ", VarType, " variable '", BindInfo.m_Attribs.GetPrintName(BindPt - BindInfo.m_Attribs.Binding), "'") - - 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 (!ResourceCache.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 (!ResourceCache.IsSamplerBound(BindPoint, sam.m_Attribs.ResourceType == SHADER_RESOURCE_TYPE_TEXTURE_SRV)) - { - LOG_MISSING_BINDING("texture", sam, BindPoint); - BindingsOK = false; - } - else - { - const auto& CachedSampler = ResourceCache.GetConstSampler(BindPoint); - if (sam.m_ImtblSamplerIdx >= 0 && CachedSampler.pSampler == nullptr) - { - LOG_ERROR_MESSAGE("Immutable 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 (!ResourceCache.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 (!ResourceCache.IsSSBOBound(BindPoint)) - { - LOG_MISSING_BINDING("buffer", ssbo, BindPoint); - BindingsOK = false; - } - } - }); -# undef LOG_MISSING_BINDING - - return BindingsOK; -} - -#endif - -} // namespace Diligent diff --git a/Graphics/GraphicsEngineOpenGL/src/GLProgramResourceCache.cpp b/Graphics/GraphicsEngineOpenGL/src/GLProgramResourceCache.cpp deleted file mode 100644 index 125958d4..00000000 --- a/Graphics/GraphicsEngineOpenGL/src/GLProgramResourceCache.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2019-2021 Diligent Graphics LLC - * Copyright 2015-2019 Egor Yusov - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * In no event and under no legal theory, whether in tort (including negligence), - * contract, or otherwise, unless required by applicable law (such as deliberate - * and grossly negligent acts) or agreed to in writing, shall any Contributor be - * liable for any damages, including any direct, indirect, special, incidental, - * or consequential damages of any character arising as a result of this License or - * out of the use or inability to use the software (including but not limited to damages - * for loss of goodwill, work stoppage, computer failure or malfunction, or any and - * all other commercial damages or losses), even if such Contributor has been advised - * of the possibility of such damages. - */ - -#include "pch.h" -#include "GLProgramResourceCache.hpp" - -namespace Diligent -{ - -size_t GLProgramResourceCache::GetRequriedMemorySize(Uint32 UBCount, Uint32 SamplerCount, Uint32 ImageCount, Uint32 SSBOCount) -{ - // clang-format off - auto MemSize = - sizeof(CachedUB) * UBCount + - sizeof(CachedResourceView) * SamplerCount + - sizeof(CachedResourceView) * ImageCount + - sizeof(CachedSSBO) * SSBOCount; - // clang-format on - return MemSize; -} - -void GLProgramResourceCache::Initialize(Uint32 UBCount, Uint32 SamplerCount, Uint32 ImageCount, Uint32 SSBOCount, IMemoryAllocator& MemAllocator) -{ - // clang-format off - 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)); - // clang-format on - - VERIFY_EXPR(m_pResourceData == nullptr); - size_t BufferSize = m_MemoryEndOffset; - - VERIFY_EXPR(BufferSize == GetRequriedMemorySize(UBCount, SamplerCount, ImageCount, SSBOCount)); - -#ifdef DILIGENT_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(IMemoryAllocator& MemAllocator) -{ - if (!IsInitialized()) - return; - - 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; -} - -} // namespace Diligent diff --git a/Graphics/GraphicsEngineOpenGL/src/GLProgramResources.cpp b/Graphics/GraphicsEngineOpenGL/src/GLProgramResources.cpp deleted file mode 100644 index 53ce4963..00000000 --- a/Graphics/GraphicsEngineOpenGL/src/GLProgramResources.cpp +++ /dev/null @@ -1,747 +0,0 @@ -/* - * Copyright 2019-2021 Diligent Graphics LLC - * Copyright 2015-2019 Egor Yusov - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * In no event and under no legal theory, whether in tort (including negligence), - * contract, or otherwise, unless required by applicable law (such as deliberate - * and grossly negligent acts) or agreed to in writing, shall any Contributor be - * liable for any damages, including any direct, indirect, special, incidental, - * or consequential damages of any character arising as a result of this License or - * out of the use or inability to use the software (including but not limited to damages - * for loss of goodwill, work stoppage, computer failure or malfunction, or any and - * all other commercial damages or losses), even if such Contributor has been advised - * of the possibility of such damages. - */ - -#include "pch.h" -#include -#include "GLContextState.hpp" -#include "GLProgramResources.hpp" -#include "RenderDeviceGLImpl.hpp" -#include "ShaderResourceBindingBase.hpp" -#include "ShaderResourceVariableBase.hpp" -#include "Align.hpp" - -namespace Diligent -{ - -GLProgramResources::GLProgramResources(GLProgramResources&& Program) noexcept : - // clang-format off - 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_NumUniformBuffers{Program.m_NumUniformBuffers }, - m_NumSamplers {Program.m_NumSamplers }, - m_NumImages {Program.m_NumImages }, - m_NumStorageBlocks {Program.m_NumStorageBlocks } -// clang-format on -{ - 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; -} - -inline void RemoveArrayBrackets(char* Str) -{ - auto* OpenBacketPtr = strchr(Str, '['); - if (OpenBacketPtr != nullptr) - *OpenBacketPtr = 0; -} - -void GLProgramResources::AllocateResources(std::vector& UniformBlocks, - std::vector& Samplers, - std::vector& Images, - std::vector& StorageBlocks) -{ - VERIFY(m_UniformBuffers == nullptr, "Resources have already been allocated!"); - - m_NumUniformBuffers = static_cast(UniformBlocks.size()); - m_NumSamplers = static_cast(Samplers.size()); - m_NumImages = static_cast(Images.size()); - m_NumStorageBlocks = static_cast(StorageBlocks.size()); - - size_t StringPoolDataSize = 0; - for (const auto& ub : UniformBlocks) - { - StringPoolDataSize += strlen(ub.Name) + 1; - } - - for (const auto& sam : Samplers) - { - StringPoolDataSize += strlen(sam.Name) + 1; - } - - for (const auto& img : Images) - { - StringPoolDataSize += strlen(img.Name) + 1; - } - - for (const auto& sb : StorageBlocks) - { - StringPoolDataSize += strlen(sb.Name) + 1; - } - - auto AlignedStringPoolDataSize = Align(StringPoolDataSize, sizeof(void*)); - - // clang-format off - size_t TotalMemorySize = - m_NumUniformBuffers * sizeof(UniformBufferInfo) + - m_NumSamplers * sizeof(SamplerInfo) + - m_NumImages * sizeof(ImageInfo) + - m_NumStorageBlocks * sizeof(StorageBlockInfo); - // clang-format on - - if (TotalMemorySize == 0) - { - m_UniformBuffers = nullptr; - m_Samplers = nullptr; - m_Images = nullptr; - m_StorageBlocks = nullptr; - - m_NumUniformBuffers = 0; - m_NumSamplers = 0; - m_NumImages = 0; - m_NumStorageBlocks = 0; - - return; - } - - TotalMemorySize += AlignedStringPoolDataSize * sizeof(Char); - - auto& MemAllocator = GetRawAllocator(); - void* RawMemory = ALLOCATE_RAW(MemAllocator, "Memory buffer for GLProgramResources", TotalMemorySize); - - // clang-format off - 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); - // clang-format on - - // The pool is only needed to facilitate string allocation. - StringPool TmpStringPool; - TmpStringPool.AssignMemory(StringPoolData, StringPoolDataSize); - - for (Uint32 ub = 0; ub < m_NumUniformBuffers; ++ub) - { - auto& SrcUB = UniformBlocks[ub]; - new (m_UniformBuffers + ub) UniformBufferInfo{SrcUB, TmpStringPool}; - } - - for (Uint32 s = 0; s < m_NumSamplers; ++s) - { - auto& SrcSam = Samplers[s]; - new (m_Samplers + s) SamplerInfo{SrcSam, TmpStringPool}; - } - - for (Uint32 img = 0; img < m_NumImages; ++img) - { - auto& SrcImg = Images[img]; - new (m_Images + img) ImageInfo{SrcImg, TmpStringPool}; - } - - for (Uint32 sb = 0; sb < m_NumStorageBlocks; ++sb) - { - auto& SrcSB = StorageBlocks[sb]; - new (m_StorageBlocks + sb) StorageBlockInfo{SrcSB, TmpStringPool}; - } - - VERIFY_EXPR(TmpStringPool.GetRemainingSize() == 0); -} - -GLProgramResources::~GLProgramResources() -{ - // clang-format off - ProcessResources( - [&](UniformBufferInfo& UB) - { - UB.~UniformBufferInfo(); - }, - [&](SamplerInfo& Sam) - { - Sam.~SamplerInfo(); - }, - [&](ImageInfo& Img) - { - Img.~ImageInfo(); - }, - [&](StorageBlockInfo& SB) - { - SB.~StorageBlockInfo(); - } - ); - // clang-format on - - void* RawMemory = m_UniformBuffers; - if (RawMemory != nullptr) - { - auto& MemAllocator = GetRawAllocator(); - MemAllocator.Free(RawMemory); - } -} - - -void GLProgramResources::LoadUniforms(SHADER_TYPE ShaderStages, - const GLObjectWrappers::GLProgramObj& GLProgram, - GLContextState& State, - Uint32& UniformBufferBinding, - Uint32& SamplerBinding, - Uint32& ImageBinding, - Uint32& StorageBufferBinding) -{ - // Load uniforms to temporary arrays. We will then pack all variables into a single chunk of memory. - 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); - -#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); - } -#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) - { - 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_SAMPLER_EXTERNAL_OES: - - 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 - - // clang-format off - 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; - // clang-format on - - RemoveArrayBrackets(Name.data()); - - 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: - { - auto UniformLocation = glGetUniformLocation(GLProgram, Name.data()); - - // clang-format off - 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; - // clang-format on - - 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) - { - // 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 shader source code." - " Note that if the source code is converted from HLSL and if images are only used" - " by a single shader stage, then bindings automatically assigned by HLSL->GLSL" - " converter will work fine."); - } - 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 shader source code." - " Note that if the source code is converted from HLSL and if images are only used" - " by a single shader stage, then bindings automatically assigned by HLSL->GLSL" - " converter will work fine."); - } - } - ++ImageBinding; - } - - break; - } -#endif - default: - // Some other uniform type like scalar, matrix etc. - break; - } - } - - 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) - { - auto Ind = atoi(OpenBacketPtr + 1); - ArraySize = std::max(ArraySize, Ind + 1); - *OpenBacketPtr = 0; - if (!UniformBlocks.empty()) - { - // 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 - { -#ifdef DILIGENT_DEBUG - 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( - NamesPool.emplace(Name.data()).first->c_str(), - ShaderStages, - SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, - UniformBufferBinding, - static_cast(ArraySize), - 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"); - - auto SBIndex = glGetProgramResourceIndex(GLProgram, GL_SHADER_STORAGE_BLOCK, Name.data()); - CHECK_GL_ERROR_AND_THROW("Unable to get shader storage block index\n"); - - 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()) - { - // 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 - { -# ifdef DILIGENT_DEBUG - 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( - NamesPool.emplace(Name.data()).first->c_str(), - ShaderStages, - SHADER_RESOURCE_TYPE_BUFFER_UAV, - StorageBufferBinding, - static_cast(ArraySize), - SBIndex // - ); - } - - if (glShaderStorageBlockBinding) - { - glShaderStorageBlockBinding(GLProgram, SBIndex, StorageBufferBinding); - CHECK_GL_ERROR("glShaderStorageBlockBinding() failed"); - } - else - { - 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 shader source code." - " Note that if the source code is converted from HLSL and if storage blocks are only used" - " by a single shader stage, then bindings automatically assigned by HLSL->GLSL" - " converter will work fine."); - } - ++StorageBufferBinding; - } -#endif - - State.SetProgram(GLObjectWrappers::GLProgramObj::Null()); - - AllocateResources(UniformBlocks, Samplers, Images, StorageBlocks); -} - -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{}; -} - -void GLProgramResources::CountResources(const PipelineResourceLayoutDesc& ResourceLayout, - const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, - Uint32 NumAllowedTypes, - ResourceCounters& Counters) const -{ - // clang-format off - 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 - ); - // clang-format on -} - -bool GLProgramResources::IsCompatibleWith(const GLProgramResources& Res) const -{ - // clang-format off - if (GetNumUniformBuffers() != Res.GetNumUniformBuffers() || - GetNumSamplers() != Res.GetNumSamplers() || - GetNumImages() != Res.GetNumImages() || - GetNumStorageBlocks() != Res.GetNumStorageBlocks()) - return false; - // clang-format on - - for (Uint32 ub = 0; ub < GetNumUniformBuffers(); ++ub) - { - const auto& UB0 = GetUniformBuffer(ub); - const auto& UB1 = Res.GetUniformBuffer(ub); - if (!UB0.IsCompatibleWith(UB1)) - return false; - } - - for (Uint32 sam = 0; sam < GetNumSamplers(); ++sam) - { - const auto& Sam0 = GetSampler(sam); - const auto& Sam1 = Res.GetSampler(sam); - if (!Sam0.IsCompatibleWith(Sam1)) - return false; - } - - for (Uint32 img = 0; img < GetNumImages(); ++img) - { - const auto& Img0 = GetImage(img); - const auto& Img1 = Res.GetImage(img); - if (!Img0.IsCompatibleWith(Img1)) - return false; - } - - for (Uint32 sb = 0; sb < GetNumStorageBlocks(); ++sb) - { - 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()); - - // clang-format off - 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()); - } - ); - // clang-format on - - return hash; -} - -} // namespace Diligent diff --git a/Graphics/GraphicsEngineOpenGL/src/PipelineResourceSignatureGLImpl.cpp b/Graphics/GraphicsEngineOpenGL/src/PipelineResourceSignatureGLImpl.cpp new file mode 100644 index 00000000..42131f1e --- /dev/null +++ b/Graphics/GraphicsEngineOpenGL/src/PipelineResourceSignatureGLImpl.cpp @@ -0,0 +1,758 @@ +/* + * Copyright 2019-2021 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "pch.h" +#include "PipelineResourceSignatureGLImpl.hpp" +#include "RenderDeviceGLImpl.hpp" +#include "ShaderResourceBindingGLImpl.hpp" +#include "ShaderVariableGL.hpp" + +namespace Diligent +{ +namespace +{ + +inline bool ResourcesCompatible(const PipelineResourceSignatureGLImpl::ResourceAttribs& lhs, + const PipelineResourceSignatureGLImpl::ResourceAttribs& rhs) +{ + // Ignore sampler index. + // clang-format off + return lhs.CacheOffset == rhs.CacheOffset && + lhs.ImtblSamplerAssigned == rhs.ImtblSamplerAssigned; + // clang-format on +} + +struct PatchedPipelineResourceSignatureDesc : PipelineResourceSignatureDesc +{ + std::vector m_ImmutableSamplers; + + PatchedPipelineResourceSignatureDesc(RenderDeviceGLImpl* pDeviceGL, const PipelineResourceSignatureDesc& Desc) : + PipelineResourceSignatureDesc{Desc} + { + if (NumImmutableSamplers > 0 && !pDeviceGL->GetDeviceCaps().Features.SeparablePrograms) + { + m_ImmutableSamplers.resize(NumImmutableSamplers); + + SHADER_TYPE ActiveStages = SHADER_TYPE_UNKNOWN; + for (Uint32 r = 0; r < NumResources; ++r) + ActiveStages |= Resources[r].ShaderStages; + + for (Uint32 s = 0; s < NumImmutableSamplers; ++s) + { + m_ImmutableSamplers[s] = ImmutableSamplers[s]; + m_ImmutableSamplers[s].ShaderStages |= ActiveStages; + } + + ImmutableSamplers = m_ImmutableSamplers.data(); + } + } +}; + +} // namespace + + +const char* GetBindingRangeName(BINDING_RANGE Range) +{ + static_assert(BINDING_RANGE_COUNT == 4, "Please update the switch below to handle the new shader resource range"); + switch (Range) + { + // clang-format off + case BINDING_RANGE_UNIFORM_BUFFER: return "Uniform buffer"; + case BINDING_RANGE_TEXTURE: return "Texture"; + case BINDING_RANGE_IMAGE: return "Image"; + case BINDING_RANGE_STORAGE_BUFFER: return "Storage buffer"; + // clang-format on + default: + return "Unknown"; + } +} + +BINDING_RANGE PipelineResourceToBindingRange(const PipelineResourceDesc& Desc) +{ + static_assert(SHADER_RESOURCE_TYPE_LAST == SHADER_RESOURCE_TYPE_ACCEL_STRUCT, "Please update the switch below to handle the new shader resource type"); + switch (Desc.ResourceType) + { + // clang-format off + case SHADER_RESOURCE_TYPE_CONSTANT_BUFFER: return BINDING_RANGE_UNIFORM_BUFFER; + case SHADER_RESOURCE_TYPE_TEXTURE_SRV: return BINDING_RANGE_TEXTURE; + case SHADER_RESOURCE_TYPE_BUFFER_SRV: return (Desc.Flags & PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER) ? BINDING_RANGE_TEXTURE : BINDING_RANGE_STORAGE_BUFFER; + case SHADER_RESOURCE_TYPE_TEXTURE_UAV: return BINDING_RANGE_IMAGE; + case SHADER_RESOURCE_TYPE_BUFFER_UAV: return (Desc.Flags & PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER) ? BINDING_RANGE_IMAGE : BINDING_RANGE_STORAGE_BUFFER; + // clang-format on + case SHADER_RESOURCE_TYPE_SAMPLER: + case SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT: + case SHADER_RESOURCE_TYPE_ACCEL_STRUCT: + default: + return BINDING_RANGE_UNKNOWN; + } +} + + +PipelineResourceSignatureGLImpl::PipelineResourceSignatureGLImpl(IReferenceCounters* pRefCounters, + RenderDeviceGLImpl* pDeviceGL, + const PipelineResourceSignatureDesc& Desc, + bool bIsDeviceInternal) : + PipelineResourceSignatureGLImpl{pRefCounters, pDeviceGL, PatchedPipelineResourceSignatureDesc{pDeviceGL, Desc}, bIsDeviceInternal, 0} +{} + +PipelineResourceSignatureGLImpl::PipelineResourceSignatureGLImpl(IReferenceCounters* pRefCounters, + RenderDeviceGLImpl* pDeviceGL, + const PipelineResourceSignatureDesc& Desc, + bool bIsDeviceInternal, + int) : + TPipelineResourceSignatureBase{pRefCounters, pDeviceGL, Desc, bIsDeviceInternal} +{ + try + { + FixedLinearAllocator MemPool{GetRawAllocator()}; + + // Reserve at least 1 element because m_pResourceAttribs must hold a pointer to memory + MemPool.AddSpace(std::max(1u, Desc.NumResources)); + MemPool.AddSpace(Desc.NumImmutableSamplers); + + ReserveSpaceForDescription(MemPool, Desc); + + const auto NumStaticResStages = GetNumStaticResStages(); + if (NumStaticResStages > 0) + { + MemPool.AddSpace(1); + MemPool.AddSpace(NumStaticResStages); + } + + MemPool.Reserve(); + + m_pResourceAttribs = MemPool.Allocate(std::max(1u, m_Desc.NumResources)); + m_ImmutableSamplers = MemPool.ConstructArray(m_Desc.NumImmutableSamplers); + + // The memory is now owned by PipelineResourceSignatureGLImpl and will be freed by Destruct(). + auto* Ptr = MemPool.ReleaseOwnership(); + VERIFY_EXPR(Ptr == m_pResourceAttribs); + (void)Ptr; + + CopyDescription(MemPool, Desc); + + if (NumStaticResStages > 0) + { + m_pStaticResCache = MemPool.Construct(ShaderResourceCacheGL::CacheContentType::Signature); + m_StaticVarsMgrs = MemPool.ConstructArray(NumStaticResStages, std::ref(*this), std::ref(*m_pStaticResCache)); + } + + CreateLayouts(); + + if (NumStaticResStages > 0) + { + constexpr SHADER_RESOURCE_VARIABLE_TYPE AllowedVarTypes[] = {SHADER_RESOURCE_VARIABLE_TYPE_STATIC}; + for (Uint32 i = 0; i < m_StaticResStageIndex.size(); ++i) + { + Int8 Idx = m_StaticResStageIndex[i]; + if (Idx >= 0) + { + VERIFY_EXPR(static_cast(Idx) < NumStaticResStages); + const auto ShaderType = GetShaderTypeFromPipelineIndex(i, GetPipelineType()); + m_StaticVarsMgrs[Idx].Initialize(*this, AllowedVarTypes, _countof(AllowedVarTypes), ShaderType); + } + } + } + + m_Hash = CalculateHash(); + } + catch (...) + { + Destruct(); + throw; + } +} + +void PipelineResourceSignatureGLImpl::CreateLayouts() +{ + std::array StaticCounter = {}; + + for (Uint32 s = 0; s < m_Desc.NumImmutableSamplers; ++s) + GetDevice()->CreateSampler(m_Desc.ImmutableSamplers[s].Desc, &m_ImmutableSamplers[s]); + + for (Uint32 i = 0; i < m_Desc.NumResources; ++i) + { + const auto& ResDesc = m_Desc.Resources[i]; + VERIFY(i == 0 || ResDesc.VarType >= m_Desc.Resources[i - 1].VarType, "Resources must be sorted by variable type"); + + if (ResDesc.ResourceType == SHADER_RESOURCE_TYPE_SAMPLER) + { + Int32 ImtblSamplerIdx = FindImmutableSampler(ResDesc.ShaderStages, ResDesc.Name); + if (ImtblSamplerIdx < 0) + { + LOG_WARNING_MESSAGE("Pipeline resource signature '", m_Desc.Name, "' has separate sampler with name '", ResDesc.Name, "' that is not supported in OpenGL."); + } + + new (m_pResourceAttribs + i) ResourceAttribs // + { + ResourceAttribs::InvalidCacheOffset, + ImtblSamplerIdx < 0 ? ResourceAttribs::InvalidSamplerInd : static_cast(ImtblSamplerIdx), + ImtblSamplerIdx >= 0 // + }; + } + else + { + const auto Range = PipelineResourceToBindingRange(ResDesc); + VERIFY_EXPR(Range != BINDING_RANGE_UNKNOWN); + + const Uint32 CacheOffset = m_BindingCount[Range]; + Uint32 SamplerIdx = ResourceAttribs::InvalidSamplerInd; + Int32 ImtblSamplerIdx = -1; + + if (ResDesc.ResourceType == SHADER_RESOURCE_TYPE_TEXTURE_SRV) + { + ImtblSamplerIdx = FindImmutableSampler(ResDesc.ShaderStages, ResDesc.Name); + if (ImtblSamplerIdx < 0) + SamplerIdx = FindAssignedSampler(ResDesc, ResourceAttribs::InvalidSamplerInd); + else + SamplerIdx = static_cast(ImtblSamplerIdx); + } + + new (m_pResourceAttribs + i) ResourceAttribs // + { + CacheOffset, + SamplerIdx, + ImtblSamplerIdx >= 0 // + }; + + m_BindingCount[Range] += ResDesc.ArraySize; + + if (ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) + StaticCounter[Range] += ResDesc.ArraySize; + } + } + + if (m_pStaticResCache) + { + m_pStaticResCache->Initialize(StaticCounter[BINDING_RANGE_UNIFORM_BUFFER], + StaticCounter[BINDING_RANGE_TEXTURE], + StaticCounter[BINDING_RANGE_IMAGE], + StaticCounter[BINDING_RANGE_STORAGE_BUFFER], + GetRawAllocator()); + // Set immutable samplers for static resources. + const auto ResIdxRange = GetResourceIndexRange(SHADER_RESOURCE_VARIABLE_TYPE_STATIC); + + for (Uint32 r = ResIdxRange.first; r < ResIdxRange.second; ++r) + { + const auto& ResDesc = GetResourceDesc(r); + const auto& ResAttr = GetResourceAttribs(r); + + if (ResDesc.ResourceType != SHADER_RESOURCE_TYPE_TEXTURE_SRV || !ResAttr.IsSamplerAssigned()) + continue; + + ISampler* Sampler = nullptr; + if (ResAttr.IsImmutableSamplerAssigned()) + { + VERIFY_EXPR(ResAttr.SamplerInd < GetImmutableSamplerCount()); + + Sampler = m_ImmutableSamplers[ResAttr.SamplerInd].RawPtr(); + } + else + { + const auto& SampAttr = GetResourceAttribs(ResAttr.SamplerInd); + if (!SampAttr.IsImmutableSamplerAssigned()) + continue; + + Sampler = m_ImmutableSamplers[SampAttr.SamplerInd].RawPtr(); + } + + for (Uint32 ArrInd = 0; ArrInd < ResDesc.ArraySize; ++ArrInd) + m_pStaticResCache->SetSampler(ResAttr.CacheOffset + ArrInd, Sampler); + } +#ifdef DILIGENT_DEVELOPMENT + m_pStaticResCache->SetStaticResourcesInitialized(); +#endif + } +} + +size_t PipelineResourceSignatureGLImpl::CalculateHash() const +{ + if (m_Desc.NumResources == 0 && m_Desc.NumImmutableSamplers == 0) + return 0; + + auto Hash = CalculatePipelineResourceSignatureDescHash(m_Desc); + for (Uint32 i = 0; i < m_Desc.NumResources; ++i) + { + const auto& Attr = m_pResourceAttribs[i]; + HashCombine(Hash, Attr.CacheOffset); + } + + return Hash; +} + +PipelineResourceSignatureGLImpl::~PipelineResourceSignatureGLImpl() +{ + Destruct(); +} + +void PipelineResourceSignatureGLImpl::Destruct() +{ + auto& RawAllocator = GetRawAllocator(); + + if (m_ImmutableSamplers != nullptr) + { + for (Uint32 s = 0; s < m_Desc.NumImmutableSamplers; ++s) + m_ImmutableSamplers[s].~SamplerPtr(); + + m_ImmutableSamplers = nullptr; + } + + if (m_StaticVarsMgrs) + { + for (auto Idx : m_StaticResStageIndex) + { + if (Idx >= 0) + m_StaticVarsMgrs[Idx].~ShaderVariableGL(); + } + m_StaticVarsMgrs = nullptr; + } + + if (m_pStaticResCache) + { + m_pStaticResCache->Destroy(RawAllocator); + m_pStaticResCache = nullptr; + } + + if (void* pRawMem = m_pResourceAttribs) + { + RawAllocator.Free(pRawMem); + m_pResourceAttribs = nullptr; + } + + TPipelineResourceSignatureBase::Destruct(); +} + +void PipelineResourceSignatureGLImpl::ApplyBindings(GLObjectWrappers::GLProgramObj& GLProgram, + GLContextState& State, + SHADER_TYPE Stages, + const TBindings& Bindings) const +{ + VERIFY(GLProgram != 0, "Null GL program"); + State.SetProgram(GLProgram); + + for (Uint32 r = 0; r < GetTotalResourceCount(); ++r) + { + const auto& ResDesc = m_Desc.Resources[r]; + const auto& ResAttr = m_pResourceAttribs[r]; + const auto Range = PipelineResourceToBindingRange(ResDesc); + + if (Range == BINDING_RANGE_UNKNOWN) + continue; + + if ((ResDesc.ShaderStages & Stages) == 0) + continue; + + const Uint32 BindingIndex = Bindings[Range] + ResAttr.CacheOffset; + + static_assert(BINDING_RANGE_COUNT == 4, "Please update the switch below to handle the new shader resource range"); + switch (Range) + { + case BINDING_RANGE_UNIFORM_BUFFER: + { + auto UniformBlockIndex = glGetUniformBlockIndex(GLProgram, ResDesc.Name); + if (UniformBlockIndex == GL_INVALID_INDEX) + break; // Uniform block defined in resource signature, but not presented in shader program. + + for (Uint32 ArrInd = 0; ArrInd < ResDesc.ArraySize; ++ArrInd) + { + glUniformBlockBinding(GLProgram, UniformBlockIndex + ArrInd, BindingIndex + ArrInd); + CHECK_GL_ERROR("glUniformBlockBinding() failed"); + } + break; + } + case BINDING_RANGE_TEXTURE: + { + auto UniformLocation = glGetUniformLocation(GLProgram, ResDesc.Name); + if (UniformLocation < 0) + break; // Uniform defined in resource signature, but not presented in shader program. + + for (Uint32 ArrInd = 0; ArrInd < ResDesc.ArraySize; ++ArrInd) + { + glUniform1i(UniformLocation + ArrInd, BindingIndex + ArrInd); + CHECK_GL_ERROR("Failed to set binding point for sampler uniform '", ResDesc.Name, '\''); + } + break; + } +#if GL_ARB_shader_image_load_store + case BINDING_RANGE_IMAGE: + { + auto UniformLocation = glGetUniformLocation(GLProgram, ResDesc.Name); + if (UniformLocation < 0) + break; // Uniform defined in resource signature, but not presented in shader program. + + for (Uint32 ArrInd = 0; ArrInd < ResDesc.ArraySize; ++ArrInd) + { + // glUniform1i for image uniforms is not supported in at least GLES3.2. + // glProgramUniform1i is not available in GLES3.0 + const Uint32 ImgBinding = BindingIndex + ArrInd; + glUniform1i(UniformLocation + ArrInd, ImgBinding); + if (glGetError() != GL_NO_ERROR) + { + if (ResDesc.ArraySize > 1) + { + LOG_WARNING_MESSAGE("Failed to set binding for image uniform '", ResDesc.Name, "'[", ArrInd, + "]. Expected binding: ", ImgBinding, + ". Make sure that this binding is explicitly assigned in shader source code." + " Note that if the source code is converted from HLSL and if images are only used" + " by a single shader stage, then bindings automatically assigned by HLSL->GLSL" + " converter will work fine."); + } + else + { + LOG_WARNING_MESSAGE("Failed to set binding for image uniform '", ResDesc.Name, + "'. Expected binding: ", ImgBinding, + ". Make sure that this binding is explicitly assigned in shader source code." + " Note that if the source code is converted from HLSL and if images are only used" + " by a single shader stage, then bindings automatically assigned by HLSL->GLSL" + " converter will work fine."); + } + } + } + break; + } +#endif +#if GL_ARB_shader_storage_buffer_object + case BINDING_RANGE_STORAGE_BUFFER: + { + auto SBIndex = glGetProgramResourceIndex(GLProgram, GL_SHADER_STORAGE_BLOCK, ResDesc.Name); + if (SBIndex == GL_INVALID_INDEX) + break; // Storage block defined in resource signature, but not presented in shader program. + + if (glShaderStorageBlockBinding) + { + for (Uint32 ArrInd = 0; ArrInd < ResDesc.ArraySize; ++ArrInd) + { + glShaderStorageBlockBinding(GLProgram, SBIndex + ArrInd, BindingIndex + ArrInd); + CHECK_GL_ERROR("glShaderStorageBlockBinding() failed"); + } + } + else + { + const GLenum props[] = {GL_BUFFER_BINDING}; + GLint params[_countof(props)] = {}; + glGetProgramResourceiv(GLProgram, GL_SHADER_STORAGE_BLOCK, SBIndex, _countof(props), props, _countof(params), nullptr, params); + CHECK_GL_ERROR("glGetProgramResourceiv() failed"); + + if (BindingIndex != static_cast(params[0])) + { + LOG_WARNING_MESSAGE("glShaderStorageBlockBinding is not available on this device and " + "the engine is unable to automatically assign shader storage block bindindg for '", + ResDesc.Name, "' variable. Expected binding: ", BindingIndex, ", actual binding: ", params[0], + ". Make sure that this binding is explicitly assigned in shader source code." + " Note that if the source code is converted from HLSL and if storage blocks are only used" + " by a single shader stage, then bindings automatically assigned by HLSL->GLSL" + " converter will work fine."); + } + } + break; + } +#endif + default: + UNEXPECTED("Unsupported shader resource range type."); + } + } + + State.SetProgram(GLObjectWrappers::GLProgramObj::Null()); +} + +void PipelineResourceSignatureGLImpl::CreateShaderResourceBinding(IShaderResourceBinding** ppShaderResourceBinding, + bool InitStaticResources) +{ + auto* pRenderDeviceGL = GetDevice(); + auto& SRBAllocator = pRenderDeviceGL->GetSRBAllocator(); + auto pResBinding = NEW_RC_OBJ(SRBAllocator, "ShaderResourceBindingGLImpl instance", ShaderResourceBindingGLImpl)(this); + if (InitStaticResources) + InitializeStaticSRBResources(pResBinding); + pResBinding->QueryInterface(IID_ShaderResourceBinding, reinterpret_cast(ppShaderResourceBinding)); +} + +void PipelineResourceSignatureGLImpl::InitializeStaticSRBResources(IShaderResourceBinding* pSRB) const +{ + InitializeStaticSRBResourcesImpl(ValidatedCast(pSRB), + [&](ShaderResourceBindingGLImpl* pSRBGL) // + { + CopyStaticResources(pSRBGL->GetResourceCache()); + } // + ); +} + +Uint32 PipelineResourceSignatureGLImpl::GetStaticVariableCount(SHADER_TYPE ShaderType) const +{ + return GetStaticVariableCountImpl(ShaderType, m_StaticVarsMgrs); +} + +IShaderResourceVariable* PipelineResourceSignatureGLImpl::GetStaticVariableByName(SHADER_TYPE ShaderType, const Char* Name) +{ + return GetStaticVariableByNameImpl(ShaderType, Name, m_StaticVarsMgrs); +} + +IShaderResourceVariable* PipelineResourceSignatureGLImpl::GetStaticVariableByIndex(SHADER_TYPE ShaderType, Uint32 Index) +{ + return GetStaticVariableByIndexImpl(ShaderType, Index, m_StaticVarsMgrs); +} + +void PipelineResourceSignatureGLImpl::BindStaticResources(Uint32 ShaderFlags, + IResourceMapping* pResMapping, + Uint32 Flags) +{ + BindStaticResourcesImpl(ShaderFlags, pResMapping, Flags, m_StaticVarsMgrs); +} + +void PipelineResourceSignatureGLImpl::CopyStaticResources(ShaderResourceCacheGL& DstResourceCache) const +{ + if (m_pStaticResCache == nullptr) + return; + + // SrcResourceCache contains only static resources. + // DstResourceCache contains static, mutable and dynamic resources. + const auto& SrcResourceCache = *m_pStaticResCache; + const auto ResIdxRange = GetResourceIndexRange(SHADER_RESOURCE_VARIABLE_TYPE_STATIC); + + VERIFY_EXPR(SrcResourceCache.GetContentType() == ShaderResourceCacheGL::CacheContentType::Signature); + VERIFY_EXPR(DstResourceCache.GetContentType() == ShaderResourceCacheGL::CacheContentType::SRB); + + for (Uint32 r = ResIdxRange.first; r < ResIdxRange.second; ++r) + { + const auto& ResDesc = GetResourceDesc(r); + const auto& ResAttr = GetResourceAttribs(r); + VERIFY_EXPR(ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC); + + if (ResDesc.ResourceType == SHADER_RESOURCE_TYPE_SAMPLER) + continue; // Skip separate samplers + + static_assert(BINDING_RANGE_COUNT == 4, "Please update the switch below to handle the new shader resource range"); + switch (PipelineResourceToBindingRange(ResDesc)) + { + case BINDING_RANGE_UNIFORM_BUFFER: + for (Uint32 ArrInd = 0; ArrInd < ResDesc.ArraySize; ++ArrInd) + { + const auto& SrcCachedRes = SrcResourceCache.GetConstUB(ResAttr.CacheOffset + ArrInd); + if (!SrcCachedRes.pBuffer) + LOG_ERROR_MESSAGE("No resource is assigned to static shader variable '", GetShaderResourcePrintName(ResDesc, ArrInd), "' in pipeline resource signature '", m_Desc.Name, "'."); + + DstResourceCache.SetUniformBuffer(ResAttr.CacheOffset + ArrInd, RefCntAutoPtr{SrcCachedRes.pBuffer}); + } + break; + case BINDING_RANGE_STORAGE_BUFFER: + for (Uint32 ArrInd = 0; ArrInd < ResDesc.ArraySize; ++ArrInd) + { + const auto& SrcCachedRes = SrcResourceCache.GetConstSSBO(ResAttr.CacheOffset + ArrInd); + if (!SrcCachedRes.pBufferView) + LOG_ERROR_MESSAGE("No resource is assigned to static shader variable '", GetShaderResourcePrintName(ResDesc, ArrInd), "' in pipeline resource signature '", m_Desc.Name, "'."); + + DstResourceCache.SetSSBO(ResAttr.CacheOffset + ArrInd, RefCntAutoPtr{SrcCachedRes.pBufferView}); + } + break; + case BINDING_RANGE_TEXTURE: + for (Uint32 ArrInd = 0; ArrInd < ResDesc.ArraySize; ++ArrInd) + { + const auto& SrcCachedRes = SrcResourceCache.GetConstTexture(ResAttr.CacheOffset + ArrInd); + if (!SrcCachedRes.pView) + LOG_ERROR_MESSAGE("No resource is assigned to static shader variable '", GetShaderResourcePrintName(ResDesc, ArrInd), "' in pipeline resource signature '", m_Desc.Name, "'."); + + DstResourceCache.CopyTexture(ResAttr.CacheOffset + ArrInd, SrcCachedRes); + } + break; + case BINDING_RANGE_IMAGE: + for (Uint32 ArrInd = 0; ArrInd < ResDesc.ArraySize; ++ArrInd) + { + const auto& SrcCachedRes = SrcResourceCache.GetConstImage(ResAttr.CacheOffset + ArrInd); + if (!SrcCachedRes.pView) + LOG_ERROR_MESSAGE("No resource is assigned to static shader variable '", GetShaderResourcePrintName(ResDesc, ArrInd), "' in pipeline resource signature '", m_Desc.Name, "'."); + + DstResourceCache.CopyImage(ResAttr.CacheOffset + ArrInd, SrcCachedRes); + } + break; + default: + UNEXPECTED("Unsupported shader resource range type."); + } + } + + // Copy immutable samplers. + for (Uint32 r = 0; r < m_Desc.NumResources; ++r) + { + const auto& ResDesc = GetResourceDesc(r); + const auto& ResAttr = GetResourceAttribs(r); + + if (ResDesc.ResourceType != SHADER_RESOURCE_TYPE_TEXTURE_SRV || + ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_STATIC) + continue; + + if (!ResAttr.IsSamplerAssigned()) + continue; + + ISampler* Sampler = nullptr; + if (ResAttr.IsImmutableSamplerAssigned()) + { + VERIFY_EXPR(ResAttr.SamplerInd < GetImmutableSamplerCount()); + + Sampler = m_ImmutableSamplers[ResAttr.SamplerInd].RawPtr(); + } + else + { + const auto& SampAttr = GetResourceAttribs(ResAttr.SamplerInd); + if (!SampAttr.IsImmutableSamplerAssigned()) + continue; + + Sampler = m_ImmutableSamplers[SampAttr.SamplerInd].RawPtr(); + } + + for (Uint32 ArrInd = 0; ArrInd < ResDesc.ArraySize; ++ArrInd) + DstResourceCache.SetSampler(ResAttr.CacheOffset + ArrInd, Sampler); + } + +#ifdef DILIGENT_DEVELOPMENT + DstResourceCache.SetStaticResourcesInitialized(); +#endif +} + +void PipelineResourceSignatureGLImpl::InitSRBResourceCache(ShaderResourceCacheGL& ResourceCache) const +{ + ResourceCache.Initialize(m_BindingCount[BINDING_RANGE_UNIFORM_BUFFER], + m_BindingCount[BINDING_RANGE_TEXTURE], + m_BindingCount[BINDING_RANGE_IMAGE], + m_BindingCount[BINDING_RANGE_STORAGE_BUFFER], + GetRawAllocator()); +} + +bool PipelineResourceSignatureGLImpl::IsCompatibleWith(const PipelineResourceSignatureGLImpl& Other) const +{ + if (this == &Other) + return true; + + if (GetHash() != Other.GetHash()) + return false; + + if (m_BindingCount != Other.m_BindingCount) + return false; + + if (!PipelineResourceSignaturesCompatible(GetDesc(), Other.GetDesc())) + return false; + + const auto ResCount = GetTotalResourceCount(); + VERIFY_EXPR(ResCount == Other.GetTotalResourceCount()); + for (Uint32 r = 0; r < ResCount; ++r) + { + if (!ResourcesCompatible(GetResourceAttribs(r), Other.GetResourceAttribs(r))) + return false; + } + + return true; +} + +#ifdef DILIGENT_DEVELOPMENT +bool PipelineResourceSignatureGLImpl::DvpValidateCommittedResource(const ShaderResourcesGL::GLResourceAttribs& GLAttribs, + RESOURCE_DIMENSION ResourceDim, + bool IsMultisample, + Uint32 ResIndex, + const ShaderResourceCacheGL& ResourceCache, + const char* ShaderName, + const char* PSOName) const +{ + VERIFY_EXPR(ResIndex < m_Desc.NumResources); + const auto& ResDesc = m_Desc.Resources[ResIndex]; + const auto& ResAttr = m_pResourceAttribs[ResIndex]; + VERIFY(strcmp(ResDesc.Name, GLAttribs.Name) == 0, "Inconsistent resource names"); + + if (ResDesc.ResourceType == SHADER_RESOURCE_TYPE_SAMPLER) + return true; // Skip separate samplers + + VERIFY_EXPR(GLAttribs.ArraySize <= ResDesc.ArraySize); + + bool BindingsOK = true; + + static_assert(BINDING_RANGE_COUNT == 4, "Please update the switch below to handle the new shader resource range"); + switch (PipelineResourceToBindingRange(ResDesc)) + { + case BINDING_RANGE_UNIFORM_BUFFER: + for (Uint32 ArrInd = 0; ArrInd < ResDesc.ArraySize; ++ArrInd) + { + if (!ResourceCache.IsUBBound(ResAttr.CacheOffset + ArrInd)) + { + LOG_ERROR_MESSAGE("No resource is bound to variable '", GetShaderResourcePrintName(GLAttribs, ArrInd), + "' in shader '", ShaderName, "' of PSO '", PSOName, "'"); + BindingsOK = false; + continue; + } + } + break; + case BINDING_RANGE_STORAGE_BUFFER: + for (Uint32 ArrInd = 0; ArrInd < ResDesc.ArraySize; ++ArrInd) + { + if (!ResourceCache.IsSSBOBound(ResAttr.CacheOffset + ArrInd)) + { + LOG_ERROR_MESSAGE("No resource is bound to variable '", GetShaderResourcePrintName(GLAttribs, ArrInd), + "' in shader '", ShaderName, "' of PSO '", PSOName, "'"); + BindingsOK = false; + continue; + } + } + break; + case BINDING_RANGE_TEXTURE: + for (Uint32 ArrInd = 0; ArrInd < ResDesc.ArraySize; ++ArrInd) + { + if (!ResourceCache.IsTextureBound(ResAttr.CacheOffset + ArrInd, ResDesc.ResourceType == SHADER_RESOURCE_TYPE_TEXTURE_SRV)) + { + LOG_ERROR_MESSAGE("No resource is bound to variable '", GetShaderResourcePrintName(GLAttribs, ArrInd), + "' in shader '", ShaderName, "' of PSO '", PSOName, "'"); + BindingsOK = false; + continue; + } + + const auto& Tex = ResourceCache.GetConstTexture(ResAttr.CacheOffset + ArrInd); + if (Tex.pTexture) + ValidateResourceViewDimension(ResDesc.Name, ResDesc.ArraySize, ArrInd, Tex.pView.RawPtr(), ResourceDim, IsMultisample); + else + ValidateResourceViewDimension(ResDesc.Name, ResDesc.ArraySize, ArrInd, Tex.pView.RawPtr(), ResourceDim, IsMultisample); + } + break; + case BINDING_RANGE_IMAGE: + for (Uint32 ArrInd = 0; ArrInd < ResDesc.ArraySize; ++ArrInd) + { + if (!ResourceCache.IsImageBound(ResAttr.CacheOffset + ArrInd, ResDesc.ResourceType == SHADER_RESOURCE_TYPE_TEXTURE_UAV)) + { + LOG_ERROR_MESSAGE("No resource is bound to variable '", GetShaderResourcePrintName(GLAttribs, ArrInd), + "' in shader '", ShaderName, "' of PSO '", PSOName, "'"); + BindingsOK = false; + continue; + } + + const auto& Img = ResourceCache.GetConstImage(ResAttr.CacheOffset + ArrInd); + if (Img.pTexture) + ValidateResourceViewDimension(ResDesc.Name, ResDesc.ArraySize, ArrInd, Img.pView.RawPtr(), ResourceDim, IsMultisample); + else + ValidateResourceViewDimension(ResDesc.Name, ResDesc.ArraySize, ArrInd, Img.pView.RawPtr(), ResourceDim, IsMultisample); + } + break; + default: + UNEXPECTED("Unsupported shader resource range type."); + } + return BindingsOK; +} +#endif // DILIGENT_DEVELOPMENT + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineOpenGL/src/PipelineStateGLImpl.cpp b/Graphics/GraphicsEngineOpenGL/src/PipelineStateGLImpl.cpp index 43cfc9e0..4aff0b55 100644 --- a/Graphics/GraphicsEngineOpenGL/src/PipelineStateGLImpl.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/PipelineStateGLImpl.cpp @@ -36,41 +36,270 @@ namespace Diligent { +void PipelineStateGLImpl::CreateDefaultSignature(const PipelineStateCreateInfo& CreateInfo, + const TShaderStages& ShaderStages, + SHADER_TYPE ActiveStages, + IPipelineResourceSignature** ppSignature) +{ + std::vector Resources; -template -void PipelineStateGLImpl::Initialize(const PSOCreateInfoType& CreateInfo, std::vector& Shaders) + const auto& LayoutDesc = CreateInfo.PSODesc.ResourceLayout; + const auto DefaultVarType = LayoutDesc.DefaultVariableType; + ShaderResourcesGL ProgramResources; + + struct UniqueResource + { + const ShaderResourcesGL::GLResourceAttribs& Attribs; + const SHADER_TYPE ShaderStages; + + bool operator==(const UniqueResource& Res) const + { + return strcmp(Attribs.Name, Res.Attribs.Name) == 0 && ShaderStages == Res.ShaderStages; + } + + struct Hasher + { + size_t operator()(const UniqueResource& Res) const + { + return ComputeHash(CStringHash{}(Res.Attribs.Name), Uint32{Res.ShaderStages}); + } + }; + }; + std::unordered_set UniqueResources; + + const auto HandleResource = [&](const ShaderResourcesGL::GLResourceAttribs& Attribs, PIPELINE_RESOURCE_FLAGS Flags) // + { + PipelineResourceDesc ResDesc = {}; + + ResDesc.Name = Attribs.Name; + ResDesc.ShaderStages = Attribs.ShaderStages; + ResDesc.ArraySize = Attribs.ArraySize; + ResDesc.ResourceType = Attribs.ResourceType; + ResDesc.VarType = DefaultVarType; + ResDesc.Flags = Flags; + + if (m_IsProgramPipelineSupported) + { + const auto VarIndex = FindPipelineResourceLayoutVariable(LayoutDesc, Attribs.Name, ResDesc.ShaderStages, nullptr); + if (VarIndex != InvalidPipelineResourceLayoutVariableIndex) + { + const auto& Var = LayoutDesc.Variables[VarIndex]; + ResDesc.ShaderStages = Var.ShaderStages; + ResDesc.VarType = Var.Type; + } + + auto IterAndAssigned = UniqueResources.emplace(UniqueResource{Attribs, ResDesc.ShaderStages}); + if (IterAndAssigned.second) + { + Resources.push_back(ResDesc); + } + else + { + DEV_CHECK_ERR(IterAndAssigned.first->Attribs.ResourceType == Attribs.ResourceType, + "Shader variable '", Attribs.Name, + "' exists in multiple shaders from the same shader stage, but its type is not consistent between " + "shaders. All variables with the same name from the same shader stage must have the same type."); + } + } + else + { + for (Uint32 i = 0; i < LayoutDesc.NumVariables; ++i) + { + const auto& Var = LayoutDesc.Variables[i]; + if ((Var.ShaderStages & Attribs.ShaderStages) != 0 && + std::strcmp(Attribs.Name, Var.Name) == 0) + { + ResDesc.VarType = Var.Type; + break; + } + } + Resources.push_back(ResDesc); + } + }; + const auto HandleUB = [&](const ShaderResourcesGL::UniformBufferInfo& Attribs) { + HandleResource(Attribs, PIPELINE_RESOURCE_FLAG_UNKNOWN); + }; + const auto HandleTexture = [&](const ShaderResourcesGL::TextureInfo& Attribs) { + HandleResource(Attribs, Attribs.ResourceType == SHADER_RESOURCE_TYPE_TEXTURE_SRV ? PIPELINE_RESOURCE_FLAG_COMBINED_SAMPLER : PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER); + }; + const auto HandleImage = [&](const ShaderResourcesGL::ImageInfo& Attribs) { + HandleResource(Attribs, Attribs.ResourceType == SHADER_RESOURCE_TYPE_TEXTURE_UAV ? PIPELINE_RESOURCE_FLAG_UNKNOWN : PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER); + }; + const auto HandleSB = [&](const ShaderResourcesGL::StorageBlockInfo& Attribs) { + HandleResource(Attribs, PIPELINE_RESOURCE_FLAG_UNKNOWN); + }; + + if (m_IsProgramPipelineSupported) + { + for (size_t i = 0; i < ShaderStages.size(); ++i) + { + auto* pShaderGL = ShaderStages[i].pShader; + pShaderGL->GetShaderResources()->ProcessConstResources(HandleUB, HandleTexture, HandleImage, HandleSB); + } + } + else + { + auto pImmediateCtx = m_pDevice->GetImmediateContext(); + VERIFY_EXPR(pImmediateCtx); + VERIFY_EXPR(m_GLPrograms[0] != 0); + + ProgramResources.LoadUniforms(ActiveStages, m_GLPrograms[0], pImmediateCtx.RawPtr()->GetContextState()); + ProgramResources.ProcessConstResources(HandleUB, HandleTexture, HandleImage, HandleSB); + } + + if (Resources.size()) + { + String SignName = String{"Implicit signature for PSO '"} + m_Desc.Name + '\''; + + PipelineResourceSignatureDesc ResSignDesc = {}; + + ResSignDesc.Name = SignName.c_str(); + ResSignDesc.Resources = Resources.data(); + ResSignDesc.NumResources = static_cast(Resources.size()); + ResSignDesc.ImmutableSamplers = LayoutDesc.ImmutableSamplers; + ResSignDesc.NumImmutableSamplers = LayoutDesc.NumImmutableSamplers; + ResSignDesc.BindingIndex = 0; + ResSignDesc.SRBAllocationGranularity = CreateInfo.PSODesc.SRBAllocationGranularity; + ResSignDesc.UseCombinedTextureSamplers = true; + + GetDevice()->CreatePipelineResourceSignature(ResSignDesc, ppSignature, true); + + if (*ppSignature == nullptr) + LOG_ERROR_AND_THROW("Failed to create resource signature for pipeline state"); + } +} + +void PipelineStateGLImpl::InitResourceLayouts(const PipelineStateCreateInfo& CreateInfo, + const TShaderStages& ShaderStages, + SHADER_TYPE ActiveStages) { - FixedLinearAllocator MemPool{GetRawAllocator()}; - VERIFY_EXPR(m_NumShaderStages > 0 && m_NumShaderStages == Shaders.size()); - if (!GetDevice()->GetDeviceCaps().Features.SeparablePrograms) - m_NumShaderStages = 1; + const Uint32 SignatureCount = CreateInfo.ResourceSignaturesCount; + RefCntAutoPtr pImplicitSignature; - const auto NumPrograms = GetNumShaderStages(); + if (SignatureCount == 0 || CreateInfo.ppResourceSignatures == nullptr) + { + CreateDefaultSignature(CreateInfo, ShaderStages, ActiveStages, &pImplicitSignature); + if (pImplicitSignature != nullptr) + { + VERIFY_EXPR(pImplicitSignature->GetDesc().BindingIndex == 0); + m_Signatures[0] = ValidatedCast(pImplicitSignature.RawPtr()); + m_SignatureCount = 1; + } + } + else + { + const auto MaxBindingIndex = + PipelineResourceSignatureGLImpl::CopyResourceSignatures(CreateInfo.PSODesc.PipelineType, SignatureCount, CreateInfo.ppResourceSignatures, + m_Signatures.data(), m_Signatures.size()); + m_SignatureCount = static_cast(MaxBindingIndex + 1); + VERIFY_EXPR(m_SignatureCount == MaxBindingIndex + 1); + } - MemPool.AddSpace(NumPrograms); - MemPool.AddSpace(NumPrograms); - MemPool.AddSpace(m_Desc.ResourceLayout.NumImmutableSamplers); + // Apply resource bindings to programs. + auto& CtxState = m_pDevice->GetImmediateContext().RawPtr()->GetContextState(); - ReserveSpaceForPipelineDesc(CreateInfo, MemPool); + PipelineResourceSignatureGLImpl::TBindings Bindings = {}; - MemPool.Reserve(); + for (Uint32 s = 0; s < m_SignatureCount; ++s) + { + const auto& pSignature = m_Signatures[s]; + if (pSignature == nullptr) + continue; - m_GLPrograms = MemPool.ConstructArray(NumPrograms, false); + if (m_IsProgramPipelineSupported) + { + for (Uint32 p = 0; p < m_NumPrograms; ++p) + pSignature->ApplyBindings(m_GLPrograms[p], CtxState, GetShaderStageType(p), Bindings); + } + else + { + pSignature->ApplyBindings(m_GLPrograms[0], CtxState, ActiveStages, Bindings); + } + pSignature->AddBindings(Bindings); + } - // The memory is now owned by PipelineStateVkImpl and will be freed by Destruct(). - auto* Ptr = MemPool.ReleaseOwnership(); - VERIFY_EXPR(Ptr == m_GLPrograms); - (void)Ptr; +#ifdef DILIGENT_DEVELOPMENT + const auto& Limits = GetDevice()->GetDeviceLimits(); - m_ProgramResources = MemPool.ConstructArray(NumPrograms); + DEV_CHECK_ERR(Bindings[BINDING_RANGE_UNIFORM_BUFFER] <= static_cast(Limits.MaxUniformBlocks), + "Number of bindings in range '", GetBindingRangeName(BINDING_RANGE_UNIFORM_BUFFER), "' is greater than maximum allowed (", Limits.MaxUniformBlocks, ")."); + DEV_CHECK_ERR(Bindings[BINDING_RANGE_TEXTURE] <= static_cast(Limits.MaxTextureUnits), + "Number of bindings in range '", GetBindingRangeName(BINDING_RANGE_TEXTURE), "' is greater than maximum allowed (", Limits.MaxTextureUnits, ")."); + DEV_CHECK_ERR(Bindings[BINDING_RANGE_STORAGE_BUFFER] <= static_cast(Limits.MaxStorageBlock), + "Number of bindings in range '", GetBindingRangeName(BINDING_RANGE_STORAGE_BUFFER), "' is greater than maximum allowed (", Limits.MaxStorageBlock, ")."); + DEV_CHECK_ERR(Bindings[BINDING_RANGE_IMAGE] <= static_cast(Limits.MaxImagesUnits), + "Number of bindings in range '", GetBindingRangeName(BINDING_RANGE_IMAGE), "' is greater than maximum allowed (", Limits.MaxImagesUnits, ")."); - m_ImmutableSamplers = MemPool.ConstructArray(m_Desc.ResourceLayout.NumImmutableSamplers); + if (m_IsProgramPipelineSupported) + { + for (size_t i = 0; i < ShaderStages.size(); ++i) + { + auto* pShaderGL = ShaderStages[i].pShader; + DvpValidateShaderResources(pShaderGL->GetShaderResources(), pShaderGL->GetDesc().Name, pShaderGL->GetDesc().ShaderType); + } + } + else + { + auto pImmediateCtx = m_pDevice->GetImmediateContext(); + VERIFY_EXPR(pImmediateCtx); + VERIFY_EXPR(m_GLPrograms[0] != 0); + + std::unique_ptr pResources{new ShaderResourcesGL{}}; + pResources->LoadUniforms(ActiveStages, m_GLPrograms[0], pImmediateCtx.RawPtr()->GetContextState()); + + std::shared_ptr pShaderResources{pResources.release()}; + DvpValidateShaderResources(pShaderResources, m_Desc.Name, ActiveStages); + } +#endif +} + +template +void PipelineStateGLImpl::InitInternalObjects(const PSOCreateInfoType& CreateInfo, const TShaderStages& ShaderStages) +{ + const auto& deviceCaps = GetDevice()->GetDeviceCaps(); + VERIFY(deviceCaps.DevType != RENDER_DEVICE_TYPE_UNDEFINED, "Device caps are not initialized"); + + m_IsProgramPipelineSupported = deviceCaps.Features.SeparablePrograms != DEVICE_FEATURE_STATE_DISABLED; + + FixedLinearAllocator MemPool{GetRawAllocator()}; + + ReserveSpaceForPipelineDesc(CreateInfo, MemPool); + MemPool.AddSpace(m_IsProgramPipelineSupported ? ShaderStages.size() : 1); - // It is important to construct all objects before initializing them because if an exception is thrown, - // destructors will be called for all objects + MemPool.Reserve(); - InitResourceLayouts(Shaders, MemPool); InitializePipelineDesc(CreateInfo, MemPool); + + // Get active shader stages. + SHADER_TYPE ActiveStages = SHADER_TYPE_UNKNOWN; + for (auto& Stage : ShaderStages) + { + const auto ShaderType = Stage.pShader->GetDesc().ShaderType; + VERIFY((ActiveStages & ShaderType) == 0, "Shader stage ", GetShaderTypeLiteralName(ShaderType), " is already active"); + ActiveStages |= ShaderType; + } + + // Create programs. + if (m_IsProgramPipelineSupported) + { + m_GLPrograms = MemPool.ConstructArray(ShaderStages.size(), false); + for (size_t i = 0; i < ShaderStages.size(); ++i) + { + auto* pShaderGL = ShaderStages[i].pShader; + m_GLPrograms[i] = GLProgramObj{ShaderGLImpl::LinkProgram(&ShaderStages[i], 1, true)}; + m_ShaderTypes[i] = pShaderGL->GetDesc().ShaderType; + } + m_NumPrograms = static_cast(ShaderStages.size()); + } + else + { + m_GLPrograms = MemPool.ConstructArray(1, false); + m_GLPrograms[0] = ShaderGLImpl::LinkProgram(ShaderStages.data(), static_cast(ShaderStages.size()), false); + m_ShaderTypes[0] = ActiveStages; + m_NumPrograms = 1; + } + + InitResourceLayouts(CreateInfo, ShaderStages, ActiveStages); } PipelineStateGLImpl::PipelineStateGLImpl(IReferenceCounters* pRefCounters, @@ -84,14 +313,12 @@ PipelineStateGLImpl::PipelineStateGLImpl(IReferenceCounters* pDeviceGL, CreateInfo, bIsDeviceInternal - }, - m_ResourceLayout {*this}, - m_StaticResourceLayout{*this} + } // clang-format on { try { - std::vector Shaders; + TShaderStages Shaders; ExtractShaders(CreateInfo, Shaders); RefCntAutoPtr pTempPS; @@ -107,14 +334,14 @@ PipelineStateGLImpl::PipelineStateGLImpl(IReferenceCounters* pDeviceGL->CreateShader(ShaderCI, reinterpret_cast(static_cast(&pTempPS))); Shaders.emplace_back(pTempPS); - m_ShaderStageTypes[m_NumShaderStages++] = SHADER_TYPE_PIXEL; } - Initialize(CreateInfo, Shaders); + InitInternalObjects(CreateInfo, Shaders); } catch (...) { Destruct(); + throw; } } @@ -129,21 +356,20 @@ PipelineStateGLImpl::PipelineStateGLImpl(IReferenceCounters* p pDeviceGL, CreateInfo, bIsDeviceInternal - }, - m_ResourceLayout {*this}, - m_StaticResourceLayout{*this} + } // clang-format on { try { - std::vector Shaders; + TShaderStages Shaders; ExtractShaders(CreateInfo, Shaders); - Initialize(CreateInfo, Shaders); + InitInternalObjects(CreateInfo, Shaders); } catch (...) { Destruct(); + throw; } } @@ -154,123 +380,31 @@ PipelineStateGLImpl::~PipelineStateGLImpl() void PipelineStateGLImpl::Destruct() { - auto& RawAllocator = GetRawAllocator(); - m_StaticResourceCache.Destroy(RawAllocator); GetDevice()->OnDestroyPSO(this); - if (m_ImmutableSamplers != nullptr) + if (m_GLPrograms) { - for (Uint32 i = 0; i < m_Desc.ResourceLayout.NumImmutableSamplers; ++i) - { - m_ImmutableSamplers[i].~SamplerPtr(); - } - } - - for (Uint32 i = 0; i < GetNumShaderStages(); ++i) - { - if (m_GLPrograms != nullptr) + for (Uint32 i = 0; i < m_NumPrograms; ++i) { m_GLPrograms[i].~GLProgramObj(); } - if (m_ProgramResources != nullptr) - { - m_ProgramResources[i].~GLProgramResources(); - } + m_GLPrograms = nullptr; } - if (void* pRawMem = m_GLPrograms) - { - RawAllocator.Free(pRawMem); - } + m_Signatures.fill({}); + + m_SignatureCount = 0; + m_NumPrograms = 0; TPipelineStateBase::Destruct(); } IMPLEMENT_QUERY_INTERFACE(PipelineStateGLImpl, IID_PipelineStateGL, TPipelineStateBase) - -void PipelineStateGLImpl::InitResourceLayouts(std::vector& Shaders, - FixedLinearAllocator& MemPool) +SHADER_TYPE PipelineStateGLImpl::GetShaderStageType(Uint32 Index) const { - auto* const pDeviceGL = GetDevice(); - const auto& deviceCaps = pDeviceGL->GetDeviceCaps(); - VERIFY(deviceCaps.DevType != RENDER_DEVICE_TYPE_UNDEFINED, "Device caps are not initialized"); - - auto pImmediateCtx = m_pDevice->GetImmediateContext(); - VERIFY_EXPR(pImmediateCtx); - auto& GLState = pImmediateCtx.RawPtr()->GetContextState(); - - { - m_TotalUniformBufferBindings = 0; - m_TotalSamplerBindings = 0; - m_TotalImageBindings = 0; - m_TotalStorageBufferBindings = 0; - if (deviceCaps.Features.SeparablePrograms) - { - // Program pipelines are not shared between GL contexts, so we cannot create - // it now - m_ShaderResourceLayoutHash = 0; - for (size_t i = 0; i < Shaders.size(); ++i) - { - auto* pShaderGL = Shaders[i]; - const auto& ShaderDesc = pShaderGL->GetDesc(); - m_GLPrograms[i] = GLProgramObj{ShaderGLImpl::LinkProgram(&pShaderGL, 1, true)}; - // Load uniforms and assign bindings - 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 - { - SHADER_TYPE ActiveStages = SHADER_TYPE_UNKNOWN; - for (const auto* pShader : Shaders) - { - const auto ShaderType = pShader->GetDesc().ShaderType; - VERIFY((ActiveStages & ShaderType) == 0, "Shader stage ", GetShaderTypeLiteralName(ShaderType), " is already active"); - ActiveStages |= ShaderType; - } - - m_GLPrograms[0] = ShaderGLImpl::LinkProgram(Shaders.data(), static_cast(Shaders.size()), false); - - m_ProgramResources[0].LoadUniforms(ActiveStages, m_GLPrograms[0], GLState, - m_TotalUniformBufferBindings, - m_TotalSamplerBindings, - m_TotalImageBindings, - m_TotalStorageBufferBindings); - - m_ShaderResourceLayoutHash = m_ProgramResources[0].GetHash(); - } - - // Initialize master resource layout that keeps all variable types and does not reference a resource cache - m_ResourceLayout.Initialize(m_ProgramResources, GetNumShaderStages(), m_Desc.PipelineType, m_Desc.ResourceLayout, nullptr, 0, nullptr); - } - - for (Uint32 s = 0; s < m_Desc.ResourceLayout.NumImmutableSamplers; ++s) - { - pDeviceGL->CreateSampler(m_Desc.ResourceLayout.ImmutableSamplers[s].Desc, &m_ImmutableSamplers[s]); - } - - { - // Clone only static variables into static resource layout, assign and initialize static resource cache - const SHADER_RESOURCE_VARIABLE_TYPE StaticVars[] = {SHADER_RESOURCE_VARIABLE_TYPE_STATIC}; - m_StaticResourceLayout.Initialize(m_ProgramResources, GetNumShaderStages(), m_Desc.PipelineType, m_Desc.ResourceLayout, StaticVars, _countof(StaticVars), &m_StaticResourceCache); - InitImmutableSamplersInResourceCache(m_StaticResourceLayout, m_StaticResourceCache); - } -} - -void PipelineStateGLImpl::CreateShaderResourceBinding(IShaderResourceBinding** ppShaderResourceBinding, bool InitStaticResources) -{ - auto* pRenderDeviceGL = GetDevice(); - auto& SRBAllocator = pRenderDeviceGL->GetSRBAllocator(); - auto pResBinding = NEW_RC_OBJ(SRBAllocator, "ShaderResourceBindingGLImpl instance", ShaderResourceBindingGLImpl)(this, m_ProgramResources, GetNumShaderStages()); - if (InitStaticResources) - pResBinding->InitializeStaticResources(this); - pResBinding->QueryInterface(IID_ShaderResourceBinding, reinterpret_cast(ppShaderResourceBinding)); + VERIFY(Index < m_NumPrograms, "Index is out of range"); + return m_ShaderTypes[Index]; } bool PipelineStateGLImpl::IsCompatibleWith(const IPipelineState* pPSO) const @@ -280,27 +414,23 @@ bool PipelineStateGLImpl::IsCompatibleWith(const IPipelineState* pPSO) const if (pPSO == this) return true; - const PipelineStateGLImpl* pPSOGL = ValidatedCast(pPSO); - if (m_ShaderResourceLayoutHash != pPSOGL->m_ShaderResourceLayoutHash) - return false; + const auto& lhs = *this; + const auto& rhs = *ValidatedCast(pPSO); - if (GetNumShaderStages() != pPSOGL->GetNumShaderStages()) + if (lhs.GetSignatureCount() != rhs.GetSignatureCount()) return false; - for (size_t i = 0; i < GetNumShaderStages(); ++i) + for (Uint32 s = 0, SigCount = lhs.GetSignatureCount(); s < SigCount; ++s) { - if (!m_ProgramResources[i].IsCompatibleWith(pPSOGL->m_ProgramResources[i])) + if (!lhs.GetSignature(s)->IsCompatibleWith(*rhs.GetSignature(s))) return false; } - return true; } void PipelineStateGLImpl::CommitProgram(GLContextState& State) { - auto ProgramPipelineSupported = m_pDevice->GetDeviceCaps().Features.SeparablePrograms; - - if (ProgramPipelineSupported) + if (m_IsProgramPipelineSupported) { // 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! @@ -342,65 +472,180 @@ GLObjectWrappers::GLPipelineObj& PipelineStateGLImpl::GetGLProgramPipeline(GLCon return ctx_pipeline.second; } -void PipelineStateGLImpl::InitializeSRBResourceCache(GLProgramResourceCache& ResourceCache) const +#ifdef DILIGENT_DEVELOPMENT +PipelineStateGLImpl::ResourceAttribution PipelineStateGLImpl::GetResourceAttribution(const char* Name, SHADER_TYPE Stage) const { - ResourceCache.Initialize(m_TotalUniformBufferBindings, m_TotalSamplerBindings, m_TotalImageBindings, m_TotalStorageBufferBindings, GetRawAllocator()); - InitImmutableSamplersInResourceCache(m_ResourceLayout, ResourceCache); -} - -void PipelineStateGLImpl::InitImmutableSamplersInResourceCache(const GLPipelineResourceLayout& ResourceLayout, GLProgramResourceCache& Cache) const -{ - for (Uint32 s = 0; s < ResourceLayout.GetNumResources(); ++s) + const auto SignCount = GetSignatureCount(); + for (Uint32 sign = 0; sign < SignCount; ++sign) { - const auto& Sam = ResourceLayout.GetConstResource(s); - if (Sam.m_ImtblSamplerIdx >= 0) + const auto* const pSignature = GetSignature(sign); + if (pSignature == nullptr) + continue; + + const auto ResIndex = pSignature->FindResource(Stage, Name); + if (ResIndex != ResourceAttribution::InvalidResourceIndex) + return ResourceAttribution{pSignature, sign, ResIndex}; + else { - ISampler* pSampler = m_ImmutableSamplers[Sam.m_ImtblSamplerIdx].RawPtr(); - for (Uint32 binding = Sam.m_Attribs.Binding; binding < Sam.m_Attribs.Binding + Sam.m_Attribs.ArraySize; ++binding) - Cache.SetImmutableSampler(binding, pSampler); + const auto ImtblSamIndex = pSignature->FindImmutableSampler(Stage, Name); + if (ImtblSamIndex != ResourceAttribution::InvalidSamplerIndex) + return ResourceAttribution{pSignature, sign, ResourceAttribution::InvalidResourceIndex, ImtblSamIndex}; } } + return ResourceAttribution{}; } -void PipelineStateGLImpl::BindStaticResources(Uint32 ShaderFlags, IResourceMapping* pResourceMapping, Uint32 Flags) +void PipelineStateGLImpl::DvpValidateShaderResources(const std::shared_ptr& pShaderResources, const char* ShaderName, SHADER_TYPE ShaderStages) { - m_StaticResourceLayout.BindResources(static_cast(ShaderFlags), pResourceMapping, Flags, m_StaticResourceCache); -} + m_ShaderResources.emplace_back(pShaderResources); + m_ShaderNames.emplace_back(ShaderName); -Uint32 PipelineStateGLImpl::GetStaticVariableCount(SHADER_TYPE ShaderType) const -{ - if (!IsConsistentShaderType(ShaderType, m_Desc.PipelineType)) + const auto HandleResource = [&](const ShaderResourcesGL::GLResourceAttribs& Attribs, SHADER_RESOURCE_TYPE ReadOnlyResourceType, PIPELINE_RESOURCE_FLAGS Flags) // { - LOG_WARNING_MESSAGE("Unable to get the number of static variables in shader stage ", GetShaderTypeLiteralName(ShaderType), - " as the stage is invalid for ", GetPipelineTypeString(m_Desc.PipelineType), " pipeline '", m_Desc.Name, "'"); - return 0; - } + m_ResourceAttibutions.emplace_back(); + auto& ResAttribution = m_ResourceAttibutions.back(); + + ResAttribution = GetResourceAttribution(Attribs.Name, ShaderStages); + if (!ResAttribution) + { + LOG_ERROR_AND_THROW("Shader '", ShaderName, "' contains resource '", Attribs.Name, + "' that is not present in any pipeline resource signature used to create pipeline state '", + m_Desc.Name, "'."); + } + + const auto* const pSignature = ResAttribution.pSignature; + VERIFY_EXPR(pSignature != nullptr); - return m_StaticResourceLayout.GetNumVariables(ShaderType); + if (ResAttribution.ResourceIndex != ResourceAttribution::InvalidResourceIndex) + { + const auto& ResDesc = pSignature->GetResourceDesc(ResAttribution.ResourceIndex); + + // Shader reflection does not contain read-only flag, so image and storage buffer can be UAV or SRV. + if (Attribs.ResourceType != ResDesc.ResourceType && + ReadOnlyResourceType != ResDesc.ResourceType) + { + LOG_ERROR_AND_THROW("Shader '", ShaderName, "' contains resource with name '", Attribs.Name, + "' and type '", GetShaderResourceTypeLiteralName(Attribs.ResourceType), "' that is not compatible with type '", + GetShaderResourceTypeLiteralName(ResDesc.ResourceType), "' in pipeline resource signature '", pSignature->GetDesc().Name, "'."); + } + + if ((Flags & PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER) != (ResDesc.Flags & PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER)) + { + LOG_ERROR_AND_THROW("Shader '", ShaderName, "' contains resource '", Attribs.Name, + "' that is", ((Flags & PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER) ? "" : " not"), + " labeled as formatted buffer, while the same resource specified by the pipeline resource signature '", + pSignature->GetDesc().Name, "' is", ((ResDesc.Flags & PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER) ? "" : " not"), + " labeled as such."); + } + } + }; + + const auto HandleUB = [&](const ShaderResourcesGL::UniformBufferInfo& Attribs) { + HandleResource(Attribs, Attribs.ResourceType, PIPELINE_RESOURCE_FLAG_UNKNOWN); + }; + + const auto HandleTexture = [&](const ShaderResourcesGL::TextureInfo& Attribs) { + const bool IsTexelBuffer = (Attribs.ResourceType != SHADER_RESOURCE_TYPE_TEXTURE_SRV); + HandleResource(Attribs, + Attribs.ResourceType, + IsTexelBuffer ? PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER : PIPELINE_RESOURCE_FLAG_COMBINED_SAMPLER); + }; + + const auto HandleImage = [&](const ShaderResourcesGL::ImageInfo& Attribs) { + const bool IsImageBuffer = (Attribs.ResourceType != SHADER_RESOURCE_TYPE_TEXTURE_UAV); + HandleResource(Attribs, + IsImageBuffer ? SHADER_RESOURCE_TYPE_BUFFER_SRV : SHADER_RESOURCE_TYPE_TEXTURE_SRV, + IsImageBuffer ? PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER : PIPELINE_RESOURCE_FLAG_UNKNOWN); + }; + + const auto HandleSB = [&](const ShaderResourcesGL::StorageBlockInfo& Attribs) { + HandleResource(Attribs, SHADER_RESOURCE_TYPE_BUFFER_SRV, PIPELINE_RESOURCE_FLAG_UNKNOWN); + }; + + pShaderResources->ProcessConstResources(HandleUB, HandleTexture, HandleImage, HandleSB); } -IShaderResourceVariable* PipelineStateGLImpl::GetStaticVariableByName(SHADER_TYPE ShaderType, const Char* Name) +void PipelineStateGLImpl::DvpVerifySRBResources(ShaderResourceBindingGLImpl* pSRBs[], + const TBindings BoundResOffsets[], + Uint32 NumSRBs) const { - if (!IsConsistentShaderType(ShaderType, m_Desc.PipelineType)) + // Verify SRB compatibility with this pipeline + const auto SignCount = GetResourceSignatureCount(); + TBindings Bindings = {}; + for (Uint32 sign = 0; sign < SignCount; ++sign) { - LOG_WARNING_MESSAGE("Unable to find static variable '", Name, "' in shader stage ", GetShaderTypeLiteralName(ShaderType), - " as the stage is invalid for ", GetPipelineTypeString(m_Desc.PipelineType), " pipeline '", m_Desc.Name, "'"); - return nullptr; + // Get resource signature from the root signature + const auto& pSignature = GetSignature(sign); + if (pSignature == nullptr || pSignature->GetTotalResourceCount() == 0) + continue; // Skip null and empty signatures + + VERIFY_EXPR(pSignature->GetDesc().BindingIndex == sign); + const auto* const pSRB = pSRBs[sign]; + if (pSRB == nullptr) + { + LOG_ERROR_MESSAGE("Pipeline state '", m_Desc.Name, "' requires SRB at index ", sign, " but none is bound in the device context."); + continue; + } + + const auto* const pSRBSign = pSRB->GetSignature(); + if (!pSignature->IsCompatibleWith(pSRBSign)) + { + LOG_ERROR_MESSAGE("Shader resource binding at index ", sign, " with signature '", pSRBSign->GetDesc().Name, + "' is not compatible with pipeline layout in current pipeline '", m_Desc.Name, "'."); + } + + DEV_CHECK_ERR(Bindings == BoundResOffsets[sign], + "Bound resources has incorrect base binding indices, this may indicate a bug in resource signature compatibility comparison."); + + pSignature->AddBindings(Bindings); } - return m_StaticResourceLayout.GetShaderVariable(ShaderType, Name); -} -IShaderResourceVariable* PipelineStateGLImpl::GetStaticVariableByIndex(SHADER_TYPE ShaderType, Uint32 Index) -{ - if (!IsConsistentShaderType(ShaderType, m_Desc.PipelineType)) + using AttribIter = std::vector::const_iterator; + struct HandleResourceHelper { - LOG_WARNING_MESSAGE("Unable to get static variable at index ", Index, " in shader stage ", GetShaderTypeLiteralName(ShaderType), - " as the stage is invalid for ", GetPipelineTypeString(m_Desc.PipelineType), " pipeline '", m_Desc.Name, "'"); - return nullptr; - } + PipelineStateGLImpl const& PSO; + ShaderResourceBindingGLImpl** ppSRBs; + const Uint32 NumSRBs; + AttribIter attrib_it; + Uint32& shader_ind; + + HandleResourceHelper(const PipelineStateGLImpl& _PSO, ShaderResourceBindingGLImpl** _ppSRBs, Uint32 _NumSRBs, AttribIter iter, Uint32& ind) : + PSO{_PSO}, ppSRBs{_ppSRBs}, NumSRBs{_NumSRBs}, attrib_it{iter}, shader_ind{ind} + {} - return m_StaticResourceLayout.GetShaderVariable(ShaderType, Index); + void Validate(const ShaderResourcesGL::GLResourceAttribs& Attribs, RESOURCE_DIMENSION ResDim, bool IsMS) + { + if (*attrib_it && !attrib_it->IsImmutableSampler()) + { + if (attrib_it->SignatureIndex >= NumSRBs || ppSRBs[attrib_it->SignatureIndex] == nullptr) + { + LOG_ERROR_MESSAGE("No resource is bound to variable '", Attribs.Name, "' in shader '", PSO.m_ShaderNames[shader_ind], + "' of PSO '", PSO.m_Desc.Name, "': SRB at index ", attrib_it->SignatureIndex, " is not bound in the context."); + return; + } + + const auto& SRBCache = ppSRBs[attrib_it->SignatureIndex]->GetResourceCache(); + attrib_it->pSignature->DvpValidateCommittedResource(Attribs, ResDim, IsMS, attrib_it->ResourceIndex, SRBCache, PSO.m_ShaderNames[shader_ind].c_str(), PSO.m_Desc.Name); + } + ++attrib_it; + } + + void operator()(const ShaderResourcesGL::GLResourceAttribs& Attribs) { Validate(Attribs, RESOURCE_DIM_UNDEFINED, false); } + void operator()(const ShaderResourcesGL::TextureInfo& Attribs) { Validate(Attribs, Attribs.ResourceDim, Attribs.IsMultisample); } + void operator()(const ShaderResourcesGL::ImageInfo& Attribs) { Validate(Attribs, Attribs.ResourceDim, Attribs.IsMultisample); } + }; + + Uint32 i = 0; + HandleResourceHelper HandleResource{*this, pSRBs, NumSRBs, m_ResourceAttibutions.begin(), i}; + + VERIFY_EXPR(m_ShaderResources.size() == m_ShaderNames.size()); + for (; i < m_ShaderResources.size(); ++i) + { + m_ShaderResources[i]->ProcessConstResources(std::ref(HandleResource), std::ref(HandleResource), std::ref(HandleResource), std::ref(HandleResource)); + } + VERIFY_EXPR(HandleResource.attrib_it == m_ResourceAttibutions.end()); } +#endif // DILIGENT_DEVELOPMENT } // namespace Diligent diff --git a/Graphics/GraphicsEngineOpenGL/src/RenderDeviceGLImpl.cpp b/Graphics/GraphicsEngineOpenGL/src/RenderDeviceGLImpl.cpp index fd30dbb2..789a4760 100644 --- a/Graphics/GraphicsEngineOpenGL/src/RenderDeviceGLImpl.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/RenderDeviceGLImpl.cpp @@ -48,6 +48,7 @@ #include "QueryGLImpl.hpp" #include "RenderPassGLImpl.hpp" #include "FramebufferGLImpl.hpp" +#include "PipelineResourceSignatureGLImpl.hpp" #include "EngineMemory.h" #include "StringTools.hpp" @@ -152,14 +153,15 @@ RenderDeviceGLImpl::RenderDeviceGLImpl(IReferenceCounters* pRefCounters, sizeof(FramebufferGLImpl), 0, 0, - 0 + 0, + sizeof(PipelineResourceSignatureGLImpl) } }, // Device caps must be filled in before the constructor of Pipeline Cache is called! m_GLContext{InitAttribs, m_DeviceCaps, pSCDesc} // clang-format on { - static_assert(sizeof(DeviceObjectSizes) == sizeof(size_t) * 15, "Please add new objects to DeviceObjectSizes constructor"); + static_assert(sizeof(DeviceObjectSizes) == sizeof(size_t) * 16, "Please add new objects to DeviceObjectSizes constructor"); GLint NumExtensions = 0; glGetIntegerv(GL_NUM_EXTENSIONS, &NumExtensions); @@ -440,6 +442,7 @@ RenderDeviceGLImpl::RenderDeviceGLImpl(IReferenceCounters* pRefCounters, SET_FEATURE_STATE(ShaderInt8, strstr(Extensions, "shader_explicit_arithmetic_types_int8"), "8-bit integer shader operations are"); SET_FEATURE_STATE(ResourceBuffer8BitAccess, strstr(Extensions, "shader_8bit_storage"), "8-bit resoure buffer access is"); SET_FEATURE_STATE(UniformBuffer8BitAccess, strstr(Extensions, "shader_8bit_storage"), "8-bit uniform buffer access is"); + SET_FEATURE_STATE(ShaderResourceRuntimeArray, false, "runtime-sized array is"); // clang-format on TexCaps.MaxTexture1DDimension = 0; // Not supported in GLES 3.2 @@ -467,8 +470,29 @@ RenderDeviceGLImpl::RenderDeviceGLImpl(IReferenceCounters* pRefCounters, #undef SET_FEATURE_STATE #if defined(_MSC_VER) && defined(_WIN64) - static_assert(sizeof(DeviceFeatures) == 32, "Did you add a new feature to DeviceFeatures? Please handle its satus here."); + static_assert(sizeof(DeviceFeatures) == 33, "Did you add a new feature to DeviceFeatures? Please handle its satus here."); +#endif + + // get device limits + { + glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &m_DeviceLimits.MaxUniformBlocks); + CHECK_GL_ERROR("glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS) failed"); + + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &m_DeviceLimits.MaxTextureUnits); + CHECK_GL_ERROR("glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS) failed"); + + if (Features.ComputeShaders) + { +#if GL_ARB_shader_storage_buffer_object + glGetIntegerv(GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS, &m_DeviceLimits.MaxStorageBlock); + CHECK_GL_ERROR("glGetIntegerv(GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS) failed"); #endif +#if GL_ARB_shader_image_load_store + glGetIntegerv(GL_MAX_IMAGE_UNITS, &m_DeviceLimits.MaxImagesUnits); + CHECK_GL_ERROR("glGetIntegerv(GL_MAX_IMAGE_UNITS) failed"); +#endif + } + } } RenderDeviceGLImpl::~RenderDeviceGLImpl() @@ -788,17 +812,38 @@ void RenderDeviceGLImpl::CreateRenderPass(const RenderPassDesc& Desc, IRenderPas void RenderDeviceGLImpl::CreateFramebuffer(const FramebufferDesc& Desc, IFramebuffer** ppFramebuffer) { - CreateDeviceObject("Framebuffer", Desc, ppFramebuffer, - [&]() // - { - auto spDeviceContext = GetImmediateContext(); - VERIFY(spDeviceContext, "Immediate device context has been destroyed"); - auto& GLState = spDeviceContext.RawPtr()->GetContextState(); - - FramebufferGLImpl* pFramebufferGL(NEW_RC_OBJ(m_FramebufferAllocator, "FramebufferGLImpl instance", FramebufferGLImpl)(this, GLState, Desc)); - pFramebufferGL->QueryInterface(IID_Framebuffer, reinterpret_cast(ppFramebuffer)); - OnCreateDeviceObject(pFramebufferGL); - }); + CreateDeviceObject( + "Framebuffer", Desc, ppFramebuffer, + [&]() // + { + auto spDeviceContext = GetImmediateContext(); + VERIFY(spDeviceContext, "Immediate device context has been destroyed"); + auto& GLState = spDeviceContext.RawPtr()->GetContextState(); + + FramebufferGLImpl* pFramebufferGL(NEW_RC_OBJ(m_FramebufferAllocator, "FramebufferGLImpl instance", FramebufferGLImpl)(this, GLState, Desc)); + pFramebufferGL->QueryInterface(IID_Framebuffer, reinterpret_cast(ppFramebuffer)); + OnCreateDeviceObject(pFramebufferGL); + }); +} + +void RenderDeviceGLImpl::CreatePipelineResourceSignature(const PipelineResourceSignatureDesc& Desc, + IPipelineResourceSignature** ppSignature) +{ + CreatePipelineResourceSignature(Desc, ppSignature, false); +} + +void RenderDeviceGLImpl::CreatePipelineResourceSignature(const PipelineResourceSignatureDesc& Desc, + IPipelineResourceSignature** ppSignature, + bool IsDeviceInternal) +{ + CreateDeviceObject( + "PipelineResourceSignature", Desc, ppSignature, + [&]() // + { + PipelineResourceSignatureGLImpl* pPRSGL(NEW_RC_OBJ(m_PipeResSignAllocator, "PipelineResourceSignatureGLImpl instance", PipelineResourceSignatureGLImpl)(this, Desc, IsDeviceInternal)); + pPRSGL->QueryInterface(IID_PipelineResourceSignature, reinterpret_cast(ppSignature)); + OnCreateDeviceObject(pPRSGL); + }); } void RenderDeviceGLImpl::CreateBLAS(const BottomLevelASDesc& Desc, diff --git a/Graphics/GraphicsEngineOpenGL/src/ShaderGLImpl.cpp b/Graphics/GraphicsEngineOpenGL/src/ShaderGLImpl.cpp index 3418875e..bc480a06 100644 --- a/Graphics/GraphicsEngineOpenGL/src/ShaderGLImpl.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/ShaderGLImpl.cpp @@ -41,6 +41,11 @@ using namespace Diligent; namespace Diligent { +ShaderGLImpl::ShaderStageInfo::ShaderStageInfo(const ShaderGLImpl* _pShader) : + Type{_pShader->GetDesc().ShaderType}, + pShader{_pShader} +{} + ShaderGLImpl::ShaderGLImpl(IReferenceCounters* pRefCounters, RenderDeviceGLImpl* pDeviceGL, const ShaderCreateInfo& ShaderCI, @@ -158,16 +163,15 @@ ShaderGLImpl::ShaderGLImpl(IReferenceCounters* pRefCounters, if (deviceCaps.Features.SeparablePrograms) { - ShaderGLImpl* 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(); + ShaderStageInfo ThisShader[] = {ShaderStageInfo{this}}; + GLObjectWrappers::GLProgramObj Program = LinkProgram(ThisShader, 1, true); + 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); + + std::unique_ptr pResources{new ShaderResourcesGL{}}; + pResources->LoadUniforms(m_Desc.ShaderType, Program, GLState); + m_pShaderResources.reset(pResources.release()); } } @@ -178,7 +182,7 @@ ShaderGLImpl::~ShaderGLImpl() IMPLEMENT_QUERY_INTERFACE(ShaderGLImpl, IID_ShaderGL, TShaderBase) -GLObjectWrappers::GLProgramObj ShaderGLImpl::LinkProgram(ShaderGLImpl** ppShaders, Uint32 NumShaders, bool IsSeparableProgram) +GLObjectWrappers::GLProgramObj ShaderGLImpl::LinkProgram(const ShaderStageInfo* pShaderStagess, Uint32 NumShaders, bool IsSeparableProgram) { VERIFY(!IsSeparableProgram || NumShaders == 1, "Number of shaders must be 1 when separable program is created"); @@ -190,7 +194,7 @@ GLObjectWrappers::GLProgramObj ShaderGLImpl::LinkProgram(ShaderGLImpl** ppShader for (Uint32 i = 0; i < NumShaders; ++i) { - auto* pCurrShader = ppShaders[i]; + auto* pCurrShader = pShaderStagess[i].pShader; glAttachShader(GLProg, pCurrShader->m_GLShaderObj); CHECK_GL_ERROR("glAttachShader() failed"); } @@ -229,7 +233,7 @@ GLObjectWrappers::GLProgramObj ShaderGLImpl::LinkProgram(ShaderGLImpl** ppShader for (Uint32 i = 0; i < NumShaders; ++i) { - auto* pCurrShader = ValidatedCast(ppShaders[i]); + auto* pCurrShader = ValidatedCast(pShaderStagess[i].pShader); glDetachShader(GLProg, pCurrShader->m_GLShaderObj); CHECK_GL_ERROR("glDetachShader() failed"); } @@ -241,7 +245,7 @@ Uint32 ShaderGLImpl::GetResourceCount() const { if (m_pDevice->GetDeviceCaps().Features.SeparablePrograms) { - return m_Resources.GetVariableCount(); + return m_pShaderResources->GetVariableCount(); } else { @@ -255,7 +259,7 @@ void ShaderGLImpl::GetResourceDesc(Uint32 Index, ShaderResourceDesc& ResourceDes if (m_pDevice->GetDeviceCaps().Features.SeparablePrograms) { DEV_CHECK_ERR(Index < GetResourceCount(), "Index is out of range"); - ResourceDesc = m_Resources.GetResourceDesc(Index); + ResourceDesc = m_pShaderResources->GetResourceDesc(Index); } else { diff --git a/Graphics/GraphicsEngineOpenGL/src/ShaderResourceBindingGLImpl.cpp b/Graphics/GraphicsEngineOpenGL/src/ShaderResourceBindingGLImpl.cpp index c5b6fab1..a2713fbf 100644 --- a/Graphics/GraphicsEngineOpenGL/src/ShaderResourceBindingGLImpl.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/ShaderResourceBindingGLImpl.cpp @@ -34,119 +34,99 @@ namespace Diligent { -ShaderResourceBindingGLImpl::ShaderResourceBindingGLImpl(IReferenceCounters* pRefCounters, - PipelineStateGLImpl* pPSO, - GLProgramResources* ProgramResources, - Uint32 NumPrograms) : +ShaderResourceBindingGLImpl::ShaderResourceBindingGLImpl(IReferenceCounters* pRefCounters, + PipelineResourceSignatureGLImpl* pPRS) : // clang-format off TBase { pRefCounters, - pPSO + pPRS }, - m_ResourceLayout{*this} + m_ShaderResourceCache{ShaderResourceCacheGL::CacheContentType::SRB} // clang-format on { - pPSO->InitializeSRBResourceCache(m_ResourceCache); - - // Copy only mutable and dynamic variables from master resource layout - const SHADER_RESOURCE_VARIABLE_TYPE SRBVarTypes[] = {SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE, SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC}; - - const auto& ResourceLayout = pPSO->GetDesc().ResourceLayout; - m_ResourceLayout.Initialize(ProgramResources, NumPrograms, pPSO->GetDesc().PipelineType, ResourceLayout, SRBVarTypes, _countof(SRBVarTypes), &m_ResourceCache); + try + { + const auto NumShaders = GetNumShaders(); + + FixedLinearAllocator MemPool{GetRawAllocator()}; + MemPool.AddSpace(NumShaders); + MemPool.Reserve(); + m_pShaderVarMgrs = MemPool.ConstructArray(NumShaders, std::ref(*this), std::ref(m_ShaderResourceCache)); + + // The memory is now owned by ShaderResourceBindingVkImpl and will be freed by Destruct(). + auto* Ptr = MemPool.ReleaseOwnership(); + VERIFY_EXPR(Ptr == m_pShaderVarMgrs); + (void)Ptr; + + // It is important to construct all objects before initializing them because if an exception is thrown, + // destructors will be called for all objects + + pPRS->InitSRBResourceCache(m_ShaderResourceCache); + + for (Uint32 s = 0; s < NumShaders; ++s) + { + const auto ShaderType = pPRS->GetActiveShaderStageType(s); + const auto ShaderInd = GetShaderTypePipelineIndex(ShaderType, pPRS->GetPipelineType()); + const auto MgrInd = m_ActiveShaderStageIndex[ShaderInd]; + VERIFY_EXPR(MgrInd >= 0 && MgrInd < static_cast(NumShaders)); + + // Create shader variable manager in place + // Initialize vars manager to reference mutable and dynamic variables + // Note that the cache has space for all variable types + const SHADER_RESOURCE_VARIABLE_TYPE VarTypes[] = {SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE, SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC}; + m_pShaderVarMgrs[MgrInd].Initialize(*pPRS, VarTypes, _countof(VarTypes), ShaderType); + } + } + catch (...) + { + Destruct(); + throw; + } } ShaderResourceBindingGLImpl::~ShaderResourceBindingGLImpl() { - m_ResourceCache.Destroy(GetRawAllocator()); + Destruct(); } -IMPLEMENT_QUERY_INTERFACE(ShaderResourceBindingGLImpl, IID_ShaderResourceBindingGL, TBase) - -void ShaderResourceBindingGLImpl::BindResources(Uint32 ShaderFlags, IResourceMapping* pResMapping, Uint32 Flags) +void ShaderResourceBindingGLImpl::Destruct() { - m_ResourceLayout.BindResources(static_cast(ShaderFlags), pResMapping, Flags, m_ResourceCache); -} + auto& RawAllocator = GetRawAllocator(); -IShaderResourceVariable* ShaderResourceBindingGLImpl::GetVariableByName(SHADER_TYPE ShaderType, const char* Name) -{ - if (!IsConsistentShaderType(ShaderType, m_pPSO->GetDesc().PipelineType)) + if (m_pShaderVarMgrs != nullptr) { - LOG_WARNING_MESSAGE("Unable to find mutable/dynamic variable '", Name, "' in shader stage ", GetShaderTypeLiteralName(ShaderType), - " as the stage is invalid for ", GetPipelineTypeString(m_pPSO->GetDesc().PipelineType), " pipeline '", m_pPSO->GetDesc().Name, "'"); - return nullptr; + const auto NumShaders = GetNumShaders(); + for (Uint32 s = 0; s < NumShaders; ++s) + m_pShaderVarMgrs[s].~ShaderVariableGL(); + + RawAllocator.Free(m_pShaderVarMgrs); + m_pShaderVarMgrs = nullptr; } - return m_ResourceLayout.GetShaderVariable(ShaderType, Name); + m_ShaderResourceCache.Destroy(RawAllocator); } -Uint32 ShaderResourceBindingGLImpl::GetVariableCount(SHADER_TYPE ShaderType) const -{ - if (!IsConsistentShaderType(ShaderType, m_pPSO->GetDesc().PipelineType)) - { - LOG_WARNING_MESSAGE("Unable to get the number of mutable/dynamic variables in shader stage ", GetShaderTypeLiteralName(ShaderType), - " as the stage is invalid for ", GetPipelineTypeString(m_pPSO->GetDesc().PipelineType), " pipeline '", m_pPSO->GetDesc().Name, "'"); - return 0; - } +IMPLEMENT_QUERY_INTERFACE(ShaderResourceBindingGLImpl, IID_ShaderResourceBindingGL, TBase) - return m_ResourceLayout.GetNumVariables(ShaderType); +void ShaderResourceBindingGLImpl::BindResources(Uint32 ShaderFlags, IResourceMapping* pResMapping, Uint32 Flags) +{ + BindResourcesImpl(ShaderFlags, pResMapping, Flags, m_pShaderVarMgrs); } -IShaderResourceVariable* ShaderResourceBindingGLImpl::GetVariableByIndex(SHADER_TYPE ShaderType, Uint32 Index) +IShaderResourceVariable* ShaderResourceBindingGLImpl::GetVariableByName(SHADER_TYPE ShaderType, const char* Name) { - if (!IsConsistentShaderType(ShaderType, m_pPSO->GetDesc().PipelineType)) - { - LOG_WARNING_MESSAGE("Unable to get mutable/dynamic variable at index ", Index, " in shader stage ", GetShaderTypeLiteralName(ShaderType), - " as the stage is invalid for ", GetPipelineTypeString(m_pPSO->GetDesc().PipelineType), " pipeline '", m_pPSO->GetDesc().Name, "'"); - return nullptr; - } - - return m_ResourceLayout.GetShaderVariable(ShaderType, Index); + return GetVariableByNameImpl(ShaderType, Name, m_pShaderVarMgrs); } -const GLProgramResourceCache& ShaderResourceBindingGLImpl::GetResourceCache(PipelineStateGLImpl* pdbgPSO) +Uint32 ShaderResourceBindingGLImpl::GetVariableCount(SHADER_TYPE ShaderType) const { -#ifdef DILIGENT_DEBUG - if (pdbgPSO->IsIncompatibleWith(GetPipelineState())) - { - LOG_ERROR("Shader resource binding is incompatible with the currently bound pipeline state."); - } -#endif - return m_ResourceCache; + return GetVariableCountImpl(ShaderType, m_pShaderVarMgrs); } -void ShaderResourceBindingGLImpl::InitializeStaticResources(const IPipelineState* pPipelineState) +IShaderResourceVariable* ShaderResourceBindingGLImpl::GetVariableByIndex(SHADER_TYPE ShaderType, Uint32 Index) { - 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 DILIGENT_DEVELOPMENT - if (!StaticResLayout.dvpVerifyBindings(pPSOGL->GetStaticResourceCache())) - { - 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; + return GetVariableByIndexImpl(ShaderType, Index, m_pShaderVarMgrs); } } // namespace Diligent diff --git a/Graphics/GraphicsEngineOpenGL/src/ShaderResourceCacheGL.cpp b/Graphics/GraphicsEngineOpenGL/src/ShaderResourceCacheGL.cpp new file mode 100644 index 00000000..05a6f038 --- /dev/null +++ b/Graphics/GraphicsEngineOpenGL/src/ShaderResourceCacheGL.cpp @@ -0,0 +1,122 @@ +/* + * Copyright 2019-2021 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "pch.h" +#include "ShaderResourceCacheGL.hpp" + +namespace Diligent +{ + +size_t ShaderResourceCacheGL::GetRequriedMemorySize(Uint32 UBCount, Uint32 TextureCount, Uint32 ImageCount, Uint32 SSBOCount) +{ + // clang-format off + auto MemSize = + sizeof(CachedUB) * UBCount + + sizeof(CachedResourceView) * TextureCount + + sizeof(CachedResourceView) * ImageCount + + sizeof(CachedSSBO) * SSBOCount; + // clang-format on + return MemSize; +} + +void ShaderResourceCacheGL::Initialize(Uint32 UBCount, Uint32 TextureCount, Uint32 ImageCount, Uint32 SSBOCount, IMemoryAllocator& MemAllocator) +{ + // clang-format off + m_TexturesOffset = static_cast(m_UBsOffset + sizeof(CachedUB) * UBCount); + m_ImagesOffset = static_cast(m_TexturesOffset + sizeof(CachedResourceView) * TextureCount); + m_SSBOsOffset = static_cast(m_ImagesOffset + sizeof(CachedResourceView) * ImageCount); + m_MemoryEndOffset = static_cast(m_SSBOsOffset + sizeof(CachedSSBO) * SSBOCount); + + VERIFY_EXPR(GetUBCount() == static_cast(UBCount)); + VERIFY_EXPR(GetTextureCount() == static_cast(TextureCount)); + VERIFY_EXPR(GetImageCount() == static_cast(ImageCount)); + VERIFY_EXPR(GetSSBOCount() == static_cast(SSBOCount)); + // clang-format on + + VERIFY_EXPR(m_pResourceData == nullptr); + size_t BufferSize = m_MemoryEndOffset; + + VERIFY_EXPR(BufferSize == GetRequriedMemorySize(UBCount, TextureCount, ImageCount, SSBOCount)); + +#ifdef DILIGENT_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 < TextureCount; ++s) + new (&GetTexture(s)) CachedResourceView; + + for (Uint32 i = 0; i < ImageCount; ++i) + new (&GetImage(i)) CachedResourceView; + + for (Uint32 s = 0; s < SSBOCount; ++s) + new (&GetSSBO(s)) CachedSSBO; +} + +ShaderResourceCacheGL::~ShaderResourceCacheGL() +{ + VERIFY(!IsInitialized(), "Shader resource cache memory must be released with ShaderResourceCacheGL::Destroy()"); +} + +void ShaderResourceCacheGL::Destroy(IMemoryAllocator& MemAllocator) +{ + if (!IsInitialized()) + return; + + 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 < GetTextureCount(); ++s) + GetTexture(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_TexturesOffset = InvalidResourceOffset; + m_ImagesOffset = InvalidResourceOffset; + m_SSBOsOffset = InvalidResourceOffset; + m_MemoryEndOffset = InvalidResourceOffset; +} + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineOpenGL/src/ShaderResourcesGL.cpp b/Graphics/GraphicsEngineOpenGL/src/ShaderResourcesGL.cpp new file mode 100644 index 00000000..a0eb2e25 --- /dev/null +++ b/Graphics/GraphicsEngineOpenGL/src/ShaderResourcesGL.cpp @@ -0,0 +1,721 @@ +/* + * Copyright 2019-2021 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "pch.h" +#include +#include "GLContextState.hpp" +#include "ShaderResourcesGL.hpp" +#include "RenderDeviceGLImpl.hpp" +#include "ShaderResourceBindingBase.hpp" +#include "ShaderResourceVariableBase.hpp" +#include "Align.hpp" + +namespace Diligent +{ +namespace +{ +void GLTextureTypeToResourceDim(GLenum TextureType, RESOURCE_DIMENSION& ResDim, bool& IsMS) +{ + IsMS = false; + ResDim = RESOURCE_DIM_UNDEFINED; + switch (TextureType) + { + case GL_SAMPLER_1D: + case GL_SAMPLER_1D_SHADOW: + case GL_INT_SAMPLER_1D: + case GL_UNSIGNED_INT_SAMPLER_1D: + ResDim = RESOURCE_DIM_TEX_1D; + return; + + case GL_SAMPLER_2D: + case GL_SAMPLER_2D_SHADOW: + case GL_INT_SAMPLER_2D: + case GL_UNSIGNED_INT_SAMPLER_2D: + case GL_SAMPLER_EXTERNAL_OES: + ResDim = RESOURCE_DIM_TEX_2D; + return; + + case GL_SAMPLER_3D: + case GL_INT_SAMPLER_3D: + case GL_UNSIGNED_INT_SAMPLER_3D: + ResDim = RESOURCE_DIM_TEX_3D; + return; + + case GL_SAMPLER_CUBE: + case GL_SAMPLER_CUBE_SHADOW: + case GL_INT_SAMPLER_CUBE: + case GL_UNSIGNED_INT_SAMPLER_CUBE: + ResDim = RESOURCE_DIM_TEX_CUBE; + return; + + case GL_SAMPLER_1D_ARRAY: + case GL_SAMPLER_1D_ARRAY_SHADOW: + case GL_INT_SAMPLER_1D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: + ResDim = RESOURCE_DIM_TEX_1D_ARRAY; + return; + + case GL_SAMPLER_2D_ARRAY: + case GL_SAMPLER_2D_ARRAY_SHADOW: + case GL_INT_SAMPLER_2D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: + ResDim = RESOURCE_DIM_TEX_2D_ARRAY; + return; + + 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: + ResDim = RESOURCE_DIM_TEX_CUBE_ARRAY; + return; + + case GL_SAMPLER_2D_MULTISAMPLE: + case GL_INT_SAMPLER_2D_MULTISAMPLE: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: + ResDim = RESOURCE_DIM_TEX_2D; + IsMS = true; + return; + + case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + ResDim = RESOURCE_DIM_TEX_2D_ARRAY; + IsMS = true; + return; + + case GL_SAMPLER_BUFFER: + case GL_INT_SAMPLER_BUFFER: + case GL_UNSIGNED_INT_SAMPLER_BUFFER: + ResDim = RESOURCE_DIM_BUFFER; + return; + +#if GL_ARB_shader_image_load_store + case GL_IMAGE_1D: + case GL_INT_IMAGE_1D: + case GL_UNSIGNED_INT_IMAGE_1D: + ResDim = RESOURCE_DIM_TEX_1D; + return; + + case GL_IMAGE_2D: + case GL_IMAGE_2D_RECT: + case GL_INT_IMAGE_2D: + case GL_INT_IMAGE_2D_RECT: + case GL_UNSIGNED_INT_IMAGE_2D: + case GL_UNSIGNED_INT_IMAGE_2D_RECT: + ResDim = RESOURCE_DIM_TEX_2D; + return; + + case GL_IMAGE_3D: + case GL_INT_IMAGE_3D: + case GL_UNSIGNED_INT_IMAGE_3D: + ResDim = RESOURCE_DIM_TEX_3D; + return; + + case GL_IMAGE_CUBE: + case GL_INT_IMAGE_CUBE: + case GL_UNSIGNED_INT_IMAGE_CUBE: + ResDim = RESOURCE_DIM_TEX_CUBE; + return; + + case GL_IMAGE_BUFFER: + case GL_INT_IMAGE_BUFFER: + case GL_UNSIGNED_INT_IMAGE_BUFFER: + ResDim = RESOURCE_DIM_BUFFER; + return; + + case GL_IMAGE_1D_ARRAY: + case GL_INT_IMAGE_1D_ARRAY: + case GL_UNSIGNED_INT_IMAGE_1D_ARRAY: + ResDim = RESOURCE_DIM_TEX_1D_ARRAY; + return; + + case GL_IMAGE_2D_ARRAY: + case GL_INT_IMAGE_2D_ARRAY: + case GL_UNSIGNED_INT_IMAGE_2D_ARRAY: + ResDim = RESOURCE_DIM_TEX_2D_ARRAY; + return; + + case GL_IMAGE_CUBE_MAP_ARRAY: + case GL_INT_IMAGE_CUBE_MAP_ARRAY: + case GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY: + ResDim = RESOURCE_DIM_TEX_CUBE_ARRAY; + return; + + case GL_IMAGE_2D_MULTISAMPLE: + case GL_INT_IMAGE_2D_MULTISAMPLE: + case GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE: + ResDim = RESOURCE_DIM_TEX_2D; + IsMS = true; + return; + + case GL_IMAGE_2D_MULTISAMPLE_ARRAY: + case GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY: + case GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY: + ResDim = RESOURCE_DIM_TEX_2D_ARRAY; + IsMS = true; + return; +#endif + } +} +} // namespace + +ShaderResourcesGL::ShaderResourcesGL(ShaderResourcesGL&& Program) noexcept : + // clang-format off + m_ShaderStages {Program.m_ShaderStages }, + m_UniformBuffers {Program.m_UniformBuffers }, + m_Textures {Program.m_Textures }, + m_Images {Program.m_Images }, + m_StorageBlocks {Program.m_StorageBlocks }, + m_NumUniformBuffers{Program.m_NumUniformBuffers }, + m_NumTextures {Program.m_NumTextures }, + m_NumImages {Program.m_NumImages }, + m_NumStorageBlocks {Program.m_NumStorageBlocks } +// clang-format on +{ + Program.m_UniformBuffers = nullptr; + Program.m_Textures = nullptr; + Program.m_Images = nullptr; + Program.m_StorageBlocks = nullptr; + + Program.m_NumUniformBuffers = 0; + Program.m_NumTextures = 0; + Program.m_NumImages = 0; + Program.m_NumStorageBlocks = 0; +} + +inline void RemoveArrayBrackets(char* Str) +{ + auto* OpenBacketPtr = strchr(Str, '['); + if (OpenBacketPtr != nullptr) + *OpenBacketPtr = 0; +} + +void ShaderResourcesGL::AllocateResources(std::vector& UniformBlocks, + std::vector& Textures, + std::vector& Images, + std::vector& StorageBlocks) +{ + VERIFY(m_UniformBuffers == nullptr, "Resources have already been allocated!"); + + m_NumUniformBuffers = static_cast(UniformBlocks.size()); + m_NumTextures = static_cast(Textures.size()); + m_NumImages = static_cast(Images.size()); + m_NumStorageBlocks = static_cast(StorageBlocks.size()); + + size_t StringPoolDataSize = 0; + for (const auto& ub : UniformBlocks) + { + StringPoolDataSize += strlen(ub.Name) + 1; + } + + for (const auto& sam : Textures) + { + StringPoolDataSize += strlen(sam.Name) + 1; + } + + for (const auto& img : Images) + { + StringPoolDataSize += strlen(img.Name) + 1; + } + + for (const auto& sb : StorageBlocks) + { + StringPoolDataSize += strlen(sb.Name) + 1; + } + + auto AlignedStringPoolDataSize = Align(StringPoolDataSize, sizeof(void*)); + + // clang-format off + size_t TotalMemorySize = + m_NumUniformBuffers * sizeof(UniformBufferInfo) + + m_NumTextures * sizeof(TextureInfo) + + m_NumImages * sizeof(ImageInfo) + + m_NumStorageBlocks * sizeof(StorageBlockInfo); + // clang-format on + + if (TotalMemorySize == 0) + { + m_UniformBuffers = nullptr; + m_Textures = nullptr; + m_Images = nullptr; + m_StorageBlocks = nullptr; + + m_NumUniformBuffers = 0; + m_NumTextures = 0; + m_NumImages = 0; + m_NumStorageBlocks = 0; + + return; + } + + TotalMemorySize += AlignedStringPoolDataSize * sizeof(Char); + + auto& MemAllocator = GetRawAllocator(); + void* RawMemory = ALLOCATE_RAW(MemAllocator, "Memory buffer for ShaderResourcesGL", TotalMemorySize); + + // clang-format off + m_UniformBuffers = reinterpret_cast(RawMemory); + m_Textures = reinterpret_cast (m_UniformBuffers + m_NumUniformBuffers); + m_Images = reinterpret_cast (m_Textures + m_NumTextures); + m_StorageBlocks = reinterpret_cast(m_Images + m_NumImages); + void* EndOfResourceData = m_StorageBlocks + m_NumStorageBlocks; + Char* StringPoolData = reinterpret_cast(EndOfResourceData); + // clang-format on + + // The pool is only needed to facilitate string allocation. + StringPool TmpStringPool; + TmpStringPool.AssignMemory(StringPoolData, StringPoolDataSize); + + for (Uint32 ub = 0; ub < m_NumUniformBuffers; ++ub) + { + auto& SrcUB = UniformBlocks[ub]; + new (m_UniformBuffers + ub) UniformBufferInfo{SrcUB, TmpStringPool}; + } + + for (Uint32 s = 0; s < m_NumTextures; ++s) + { + auto& SrcSam = Textures[s]; + new (m_Textures + s) TextureInfo{SrcSam, TmpStringPool}; + } + + for (Uint32 img = 0; img < m_NumImages; ++img) + { + auto& SrcImg = Images[img]; + new (m_Images + img) ImageInfo{SrcImg, TmpStringPool}; + } + + for (Uint32 sb = 0; sb < m_NumStorageBlocks; ++sb) + { + auto& SrcSB = StorageBlocks[sb]; + new (m_StorageBlocks + sb) StorageBlockInfo{SrcSB, TmpStringPool}; + } + + VERIFY_EXPR(TmpStringPool.GetRemainingSize() == 0); +} + +ShaderResourcesGL::~ShaderResourcesGL() +{ + // clang-format off + ProcessResources( + [&](UniformBufferInfo& UB) + { + UB.~UniformBufferInfo(); + }, + [&](TextureInfo& Sam) + { + Sam.~TextureInfo(); + }, + [&](ImageInfo& Img) + { + Img.~ImageInfo(); + }, + [&](StorageBlockInfo& SB) + { + SB.~StorageBlockInfo(); + } + ); + // clang-format on + + void* RawMemory = m_UniformBuffers; + if (RawMemory != nullptr) + { + auto& MemAllocator = GetRawAllocator(); + MemAllocator.Free(RawMemory); + } +} + + +void ShaderResourcesGL::LoadUniforms(SHADER_TYPE ShaderStages, + const GLObjectWrappers::GLProgramObj& GLProgram, + GLContextState& State) +{ + // Load uniforms to temporary arrays. We will then pack all variables into a single chunk of memory. + std::vector UniformBlocks; + std::vector Textures; + 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); + +#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); + } +#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 + + RESOURCE_DIMENSION ResDim; + bool IsMS; + GLTextureTypeToResourceDim(dataType, ResDim, IsMS); + + 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_SAMPLER_EXTERNAL_OES: + + 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: + { + // clang-format off + 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; + // clang-format on + + RemoveArrayBrackets(Name.data()); + + Textures.emplace_back( + NamesPool.emplace(Name.data()).first->c_str(), + ShaderStages, + ResourceType, + static_cast(size), + dataType, + ResDim, + IsMS // + ); + 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: + { + // clang-format off + 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; + // clang-format on + + RemoveArrayBrackets(Name.data()); + + Images.emplace_back( + NamesPool.emplace(Name.data()).first->c_str(), + ShaderStages, + ResourceType, + static_cast(size), + dataType, + ResDim, + IsMS // + ); + break; + } +#endif + default: + // Some other uniform type like scalar, matrix etc. + break; + } + } + + 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) + { + auto Ind = atoi(OpenBacketPtr + 1); + ArraySize = std::max(ArraySize, Ind + 1); + *OpenBacketPtr = 0; + if (!UniformBlocks.empty()) + { + // 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 + { +#ifdef DILIGENT_DEBUG + 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( + NamesPool.emplace(Name.data()).first->c_str(), + ShaderStages, + SHADER_RESOURCE_TYPE_CONSTANT_BUFFER, + static_cast(ArraySize), + UniformBlockIndex // + ); + } + } + +#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"); + + auto SBIndex = glGetProgramResourceIndex(GLProgram, GL_SHADER_STORAGE_BLOCK, Name.data()); + CHECK_GL_ERROR_AND_THROW("Unable to get shader storage block index\n"); + + 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()) + { + // 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 + { +# ifdef DILIGENT_DEBUG + 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( + NamesPool.emplace(Name.data()).first->c_str(), + ShaderStages, + SHADER_RESOURCE_TYPE_BUFFER_UAV, + static_cast(ArraySize), + SBIndex // + ); + } + } +#endif + + State.SetProgram(GLObjectWrappers::GLProgramObj::Null()); + + AllocateResources(UniformBlocks, Textures, Images, StorageBlocks); +} + +ShaderResourceDesc ShaderResourcesGL::GetResourceDesc(Uint32 Index) const +{ + if (Index < m_NumUniformBuffers) + return GetUniformBuffer(Index).GetResourceDesc(); + else + Index -= m_NumUniformBuffers; + + if (Index < m_NumTextures) + return GetTexture(Index).GetResourceDesc(); + else + Index -= m_NumTextures; + + 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{}; +} + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineOpenGL/src/ShaderVariableGL.cpp b/Graphics/GraphicsEngineOpenGL/src/ShaderVariableGL.cpp new file mode 100644 index 00000000..68485b78 --- /dev/null +++ b/Graphics/GraphicsEngineOpenGL/src/ShaderVariableGL.cpp @@ -0,0 +1,733 @@ +/* + * Copyright 2019-2021 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "pch.h" +#include +#include "ShaderVariableGL.hpp" +#include "PipelineResourceSignatureGLImpl.hpp" +#include "Align.hpp" +#include "PlatformMisc.hpp" +#include "ShaderBase.hpp" + +namespace Diligent +{ + +void ShaderVariableGL::CountResources(const PipelineResourceSignatureGLImpl& Signature, + const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, + Uint32 NumAllowedTypes, + const SHADER_TYPE ShaderType, + ResourceCounters& Counters) +{ + ProcessSignatureResources( + Signature, AllowedVarTypes, NumAllowedTypes, ShaderType, + [&](Uint32 Index) { + const auto& ResDesc = Signature.GetResourceDesc(Index); + static_assert(BINDING_RANGE_COUNT == 4, "Please update the switch below to handle the new shader resource range"); + switch (PipelineResourceToBindingRange(ResDesc)) + { + // clang-format off + case BINDING_RANGE_UNIFORM_BUFFER: ++Counters.NumUBs; break; + case BINDING_RANGE_TEXTURE: ++Counters.NumTextures; break; + case BINDING_RANGE_IMAGE: ++Counters.NumImages; break; + case BINDING_RANGE_STORAGE_BUFFER: ++Counters.NumStorageBlocks; break; + // clang-format on + default: + UNEXPECTED("Unsupported resource type."); + } + }); +} + +template +void ShaderVariableGL::ProcessSignatureResources(const PipelineResourceSignatureGLImpl& Signature, + const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, + Uint32 NumAllowedTypes, + SHADER_TYPE ShaderType, + HandlerType Handler) +{ + const Uint32 AllowedTypeBits = GetAllowedTypeBits(AllowedVarTypes, NumAllowedTypes); + + for (Uint32 var_type = 0; var_type < SHADER_RESOURCE_VARIABLE_TYPE_NUM_TYPES; ++var_type) + { + const auto VarType = static_cast(var_type); + if (IsAllowedType(VarType, AllowedTypeBits)) + { + const auto ResIdxRange = Signature.GetResourceIndexRange(VarType); + for (Uint32 r = ResIdxRange.first; r < ResIdxRange.second; ++r) + { + const auto& Res = Signature.GetResourceDesc(r); + VERIFY_EXPR(Res.VarType == VarType); + + if ((Res.ShaderStages & ShaderType) == 0) + continue; + + if (Res.ResourceType == SHADER_RESOURCE_TYPE_SAMPLER) + continue; + + Handler(r); + } + } + } +} + +size_t ShaderVariableGL::GetRequiredMemorySize(const PipelineResourceSignatureGLImpl& Signature, + const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, + Uint32 NumAllowedTypes, + SHADER_TYPE ShaderType) +{ + ResourceCounters Counters; + CountResources(Signature, AllowedVarTypes, NumAllowedTypes, ShaderType, Counters); + + // clang-format off + size_t RequiredSize = Counters.NumUBs * sizeof(UniformBuffBindInfo) + + Counters.NumTextures * sizeof(SamplerBindInfo) + + Counters.NumImages * sizeof(ImageBindInfo) + + Counters.NumStorageBlocks * sizeof(StorageBufferBindInfo); + // clang-format on + return RequiredSize; +} + +void ShaderVariableGL::Initialize(const PipelineResourceSignatureGLImpl& Signature, + const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, + Uint32 NumAllowedTypes, + SHADER_TYPE ShaderType) +{ + ResourceCounters Counters; + CountResources(Signature, AllowedVarTypes, NumAllowedTypes, ShaderType, Counters); + + m_pSignature = &Signature; + + // 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, ")"); + (void)MaxOffset; + auto Offset = static_cast(CurrentOffset); + CurrentOffset += NumBytes; + return Offset; + }; + + // clang-format off + auto UBOffset = AdvanceOffset(Counters.NumUBs * sizeof(UniformBuffBindInfo) ); (void)UBOffset; // To suppress warning + m_TextureOffset = AdvanceOffset(Counters.NumTextures * sizeof(SamplerBindInfo) ); + m_ImageOffset = AdvanceOffset(Counters.NumImages * sizeof(ImageBindInfo) ); + m_StorageBufferOffset = AdvanceOffset(Counters.NumStorageBlocks * sizeof(StorageBufferBindInfo)); + m_VariableEndOffset = AdvanceOffset(0); + // clang-format off + auto TotalMemorySize = m_VariableEndOffset; + VERIFY_EXPR(TotalMemorySize == GetRequiredMemorySize(Signature, AllowedVarTypes, NumAllowedTypes, ShaderType)); + + 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); + } + + // clang-format off + VERIFY_EXPR(Counters.NumUBs == GetNumUBs() ); + VERIFY_EXPR(Counters.NumTextures == GetNumTextures() ); + VERIFY_EXPR(Counters.NumImages == GetNumImages() ); + VERIFY_EXPR(Counters.NumStorageBlocks == GetNumStorageBuffers()); + // clang-format on + + // Current resource index for every resource type + ResourceCounters VarCounters = {}; + + ProcessSignatureResources( + Signature, AllowedVarTypes, NumAllowedTypes, ShaderType, + [&](Uint32 Index) { + const auto& ResDesc = Signature.GetResourceDesc(Index); + static_assert(BINDING_RANGE_COUNT == 4, "Please update the switch below to handle the new shader resource range"); + switch (PipelineResourceToBindingRange(ResDesc)) + { + case BINDING_RANGE_UNIFORM_BUFFER: + new (&GetResource(VarCounters.NumUBs++)) UniformBuffBindInfo{*this, Index}; + break; + case BINDING_RANGE_TEXTURE: + new (&GetResource(VarCounters.NumTextures++)) SamplerBindInfo{*this, Index}; + break; + case BINDING_RANGE_IMAGE: + new (&GetResource(VarCounters.NumImages++)) ImageBindInfo{*this, Index}; + break; + case BINDING_RANGE_STORAGE_BUFFER: + new (&GetResource(VarCounters.NumStorageBlocks++)) StorageBufferBindInfo{*this, Index}; + break; + default: + UNEXPECTED("Unsupported resource type."); + } + }); + + // clang-format off + VERIFY(VarCounters.NumUBs == GetNumUBs(), "Not all UBs are initialized which will cause a crash when dtor is called"); + VERIFY(VarCounters.NumTextures == GetNumTextures(), "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"); + // clang-format on +} + +ShaderVariableGL::~ShaderVariableGL() +{ + // clang-format off + HandleResources( + [&](UniformBuffBindInfo& ub) + { + ub.~UniformBuffBindInfo(); + }, + + [&](SamplerBindInfo& sam) + { + sam.~SamplerBindInfo(); + }, + + [&](ImageBindInfo& img) + { + img.~ImageBindInfo(); + }, + + [&](StorageBufferBindInfo& ssbo) + { + ssbo.~StorageBufferBindInfo(); + } + ); + // clang-format on +} + +void ShaderVariableGL::UniformBuffBindInfo::BindResource(IDeviceObject* pBuffer, + Uint32 ArrayIndex) +{ + const auto& Desc = GetDesc(); + const auto& Attr = GetAttribs(); + + DEV_CHECK_ERR(ArrayIndex < Desc.ArraySize, "Array index (", ArrayIndex, ") is out of range for variable '", Desc.Name, "'. Max allowed index: ", Desc.ArraySize - 1); + auto& ResourceCache = m_ParentManager.m_ResourceCache; + + VERIFY_EXPR(Desc.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 DILIGENT_DEVELOPMENT + { + const auto& CachedUB = ResourceCache.GetConstUB(Attr.CacheOffset + ArrayIndex); + VerifyConstantBufferBinding(Desc.Name, Desc.ArraySize, Desc.VarType, Desc.Flags, ArrayIndex, pBuffer, pBuffGLImpl.RawPtr(), CachedUB.pBuffer.RawPtr()); + } +#endif + + ResourceCache.SetUniformBuffer(Attr.CacheOffset + ArrayIndex, std::move(pBuffGLImpl)); +} + + + +void ShaderVariableGL::SamplerBindInfo::BindResource(IDeviceObject* pView, + Uint32 ArrayIndex) +{ + const auto& Desc = GetDesc(); + const auto& Attr = GetAttribs(); + + DEV_CHECK_ERR(ArrayIndex < Desc.ArraySize, "Array index (", ArrayIndex, ") is out of range for variable '", Desc.Name, "'. Max allowed index: ", Desc.ArraySize - 1); + auto& ResourceCache = m_ParentManager.m_ResourceCache; + + VERIFY_EXPR(Desc.ResourceType == SHADER_RESOURCE_TYPE_BUFFER_SRV || + Desc.ResourceType == SHADER_RESOURCE_TYPE_TEXTURE_SRV); + + if (Desc.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 DILIGENT_DEVELOPMENT + { + auto& CachedTexSampler = ResourceCache.GetConstTexture(Attr.CacheOffset + ArrayIndex); + VerifyResourceViewBinding(Desc.Name, Desc.ArraySize, Desc.VarType, ArrayIndex, + pView, pViewGL.RawPtr(), {TEXTURE_VIEW_SHADER_RESOURCE}, + RESOURCE_DIM_UNDEFINED, false, CachedTexSampler.pView.RawPtr()); + if (Attr.IsImmutableSamplerAssigned() && ResourceCache.StaticResourcesInitialized()) + { + VERIFY(CachedTexSampler.pSampler != nullptr, "Immutable samplers must be initialized by PipelineStateGLImpl::InitializeSRBResourceCache!"); + } + } +#endif + ResourceCache.SetTexture(Attr.CacheOffset + ArrayIndex, std::move(pViewGL), !Attr.IsImmutableSamplerAssigned()); + } + else if (Desc.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 DILIGENT_DEVELOPMENT + { + auto& CachedBuffSampler = ResourceCache.GetConstTexture(Attr.CacheOffset + ArrayIndex); + VerifyResourceViewBinding(Desc.Name, Desc.ArraySize, Desc.VarType, ArrayIndex, + pView, pViewGL.RawPtr(), {BUFFER_VIEW_SHADER_RESOURCE}, + RESOURCE_DIM_BUFFER, false, CachedBuffSampler.pView.RawPtr()); + if (pViewGL != nullptr) + { + const auto& ViewDesc = pViewGL->GetDesc(); + const auto& BuffDesc = pViewGL->GetBuffer()->GetDesc(); + if (!((BuffDesc.Mode == BUFFER_MODE_FORMATTED && ViewDesc.Format.ValueType != VT_UNDEFINED) || BuffDesc.Mode == BUFFER_MODE_RAW)) + { + LOG_ERROR_MESSAGE("Error binding buffer view '", ViewDesc.Name, "' of buffer '", BuffDesc.Name, "' to shader variable '", + Desc.Name, ": formatted buffer view is expected."); + } + } + } +#endif + VERIFY_EXPR((Desc.Flags & PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER) != 0); + ResourceCache.SetTexelBuffer(Attr.CacheOffset + ArrayIndex, std::move(pViewGL)); + } + else + { + UNEXPECTED("Unexpected resource type ", GetShaderResourceTypeLiteralName(Desc.ResourceType), ". Texture SRV or buffer SRV is expected."); + } +} + + +void ShaderVariableGL::ImageBindInfo::BindResource(IDeviceObject* pView, + Uint32 ArrayIndex) +{ + const auto& Desc = GetDesc(); + const auto& Attr = GetAttribs(); + + DEV_CHECK_ERR(ArrayIndex < Desc.ArraySize, "Array index (", ArrayIndex, ") is out of range for variable '", Desc.Name, "'. Max allowed index: ", Desc.ArraySize - 1); + auto& ResourceCache = m_ParentManager.m_ResourceCache; + + if (Desc.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 DILIGENT_DEVELOPMENT + { + auto& CachedUAV = ResourceCache.GetConstImage(Attr.CacheOffset + ArrayIndex); + VerifyResourceViewBinding(Desc.Name, Desc.ArraySize, Desc.VarType, ArrayIndex, + pView, pViewGL.RawPtr(), {TEXTURE_VIEW_UNORDERED_ACCESS}, + RESOURCE_DIM_UNDEFINED, false, CachedUAV.pView.RawPtr()); + } +#endif + ResourceCache.SetTexImage(Attr.CacheOffset + ArrayIndex, std::move(pViewGL)); + } + else if (Desc.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 DILIGENT_DEVELOPMENT + { + auto& CachedUAV = ResourceCache.GetConstImage(Attr.CacheOffset + ArrayIndex); + VerifyResourceViewBinding(Desc.Name, Desc.ArraySize, Desc.VarType, ArrayIndex, + pView, pViewGL.RawPtr(), {BUFFER_VIEW_UNORDERED_ACCESS}, + RESOURCE_DIM_BUFFER, false, CachedUAV.pView.RawPtr()); + if (pViewGL != nullptr) + { + const auto& ViewDesc = pViewGL->GetDesc(); + const auto& BuffDesc = pViewGL->GetBuffer()->GetDesc(); + if (!((BuffDesc.Mode == BUFFER_MODE_FORMATTED && ViewDesc.Format.ValueType != VT_UNDEFINED) || BuffDesc.Mode == BUFFER_MODE_RAW)) + { + LOG_ERROR_MESSAGE("Error binding buffer view '", ViewDesc.Name, "' of buffer '", BuffDesc.Name, "' to shader variable '", + Desc.Name, ": formatted buffer view is expected."); + } + } + } +#endif + VERIFY_EXPR((Desc.Flags & PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER) != 0); + ResourceCache.SetBufImage(Attr.CacheOffset + ArrayIndex, std::move(pViewGL)); + } + else + { + UNEXPECTED("Unexpected resource type ", GetShaderResourceTypeLiteralName(Desc.ResourceType), ". Texture UAV or buffer UAV is expected."); + } +} + + + +void ShaderVariableGL::StorageBufferBindInfo::BindResource(IDeviceObject* pView, + Uint32 ArrayIndex) +{ + const auto& Desc = GetDesc(); + const auto& Attr = GetAttribs(); + + DEV_CHECK_ERR(ArrayIndex < Desc.ArraySize, "Array index (", ArrayIndex, ") is out of range for variable '", Desc.Name, "'. Max allowed index: ", Desc.ArraySize - 1); + auto& ResourceCache = m_ParentManager.m_ResourceCache; + VERIFY_EXPR(Desc.ResourceType == SHADER_RESOURCE_TYPE_BUFFER_SRV || + Desc.ResourceType == SHADER_RESOURCE_TYPE_BUFFER_UAV); + VERIFY_EXPR((Desc.Flags & PIPELINE_RESOURCE_FLAG_FORMATTED_BUFFER) == 0); + + // We cannot use ValidatedCast<> here as the resource retrieved from the + // resource mapping can be of wrong type + RefCntAutoPtr pViewGL(pView, IID_BufferViewGL); +#ifdef DILIGENT_DEVELOPMENT + { + auto& CachedSSBO = ResourceCache.GetConstSSBO(Attr.CacheOffset + ArrayIndex); + // HLSL structured buffers are mapped to SSBOs in GLSL + VerifyResourceViewBinding(Desc.Name, Desc.ArraySize, Desc.VarType, ArrayIndex, + pView, pViewGL.RawPtr(), {BUFFER_VIEW_SHADER_RESOURCE, BUFFER_VIEW_UNORDERED_ACCESS}, + RESOURCE_DIM_BUFFER, false, CachedSSBO.pBufferView.RawPtr()); + if (pViewGL != nullptr) + { + const auto& ViewDesc = pViewGL->GetDesc(); + const auto& BuffDesc = pViewGL->GetBuffer()->GetDesc(); + if (BuffDesc.Mode != BUFFER_MODE_STRUCTURED && BuffDesc.Mode != BUFFER_MODE_RAW) + { + LOG_ERROR_MESSAGE("Error binding buffer view '", ViewDesc.Name, "' of buffer '", BuffDesc.Name, "' to shader variable '", + Desc.Name, ": structured buffer view is expected."); + } + } + } +#endif + ResourceCache.SetSSBO(Attr.CacheOffset + ArrayIndex, std::move(pViewGL)); +} + + + +// Helper template class that facilitates binding CBs, SRVs, and UAVs +class BindResourceHelper +{ +public: + BindResourceHelper(IResourceMapping& RM, Uint32 Fl) : + // clang-format off + ResourceMapping {RM}, + Flags {Fl} + // clang-format on + { + } + + template + void Bind(ResourceType& Res) + { + if ((Flags & (1 << Res.GetType())) == 0) + return; + + const auto& ResDesc = Res.GetDesc(); + + for (Uint16 elem = 0; elem < ResDesc.ArraySize; ++elem) + { + if ((Flags & BIND_SHADER_RESOURCES_KEEP_EXISTING) && Res.IsBound(elem)) + continue; + + const auto* VarName = ResDesc.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 Uint32 Flags; +}; + + +void ShaderVariableGL::BindResources(IResourceMapping* pResourceMapping, Uint32 Flags) +{ + 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, Flags); + + // clang-format off + HandleResources( + [&](UniformBuffBindInfo& ub) + { + BindResHelper.Bind(ub); + }, + + [&](SamplerBindInfo& sam) + { + BindResHelper.Bind(sam); + }, + + [&](ImageBindInfo& img) + { + BindResHelper.Bind(img); + }, + + [&](StorageBufferBindInfo& ssbo) + { + BindResHelper.Bind(ssbo); + } + ); + // clang-format on +} + + +template +IShaderResourceVariable* ShaderVariableGL::GetResourceByName(const Char* Name) const +{ + auto NumResources = GetNumResources(); + for (Uint32 res = 0; res < NumResources; ++res) + { + auto& Resource = GetResource(res); + const auto& ResDesc = Resource.GetDesc(); + if (strcmp(ResDesc.Name, Name) == 0) + return &Resource; + } + + return nullptr; +} + + +IShaderResourceVariable* ShaderVariableGL::GetVariable(const Char* Name) const +{ + if (auto* pUB = GetResourceByName(Name)) + return pUB; + + if (auto* pSampler = GetResourceByName(Name)) + return pSampler; + + if (auto* pImage = GetResourceByName(Name)) + return pImage; + + if (auto* pSSBO = GetResourceByName(Name)) + return pSSBO; + + return nullptr; +} + +Uint32 ShaderVariableGL::GetVariableCount() const +{ + return GetNumUBs() + GetNumTextures() + GetNumImages() + GetNumStorageBuffers(); +} + +class ShaderVariableLocator +{ +public: + ShaderVariableLocator(const ShaderVariableGL& _Layout, + Uint32 _Index) : + // clang-format off + Layout {_Layout}, + Index {_Index} + // clang-format on + { + } + + template + IShaderResourceVariable* TryResource(Uint32 NumResources) + { + if (Index < NumResources) + return &Layout.GetResource(Index); + else + { + Index -= NumResources; + return nullptr; + } + } + +private: + ShaderVariableGL const& Layout; + Uint32 Index; +}; + + +IShaderResourceVariable* ShaderVariableGL::GetVariable(Uint32 Index) const +{ + ShaderVariableLocator VarLocator(*this, Index); + + if (auto* pUB = VarLocator.TryResource(GetNumUBs())) + return pUB; + + if (auto* pSampler = VarLocator.TryResource(GetNumTextures())) + return pSampler; + + if (auto* pImage = VarLocator.TryResource(GetNumImages())) + return pImage; + + if (auto* pSSBO = VarLocator.TryResource(GetNumStorageBuffers())) + return pSSBO; + + LOG_ERROR(Index, " is not a valid variable index."); + return nullptr; +} + + + +class ShaderVariableIndexLocator +{ +public: + ShaderVariableIndexLocator(const ShaderVariableGL& _Layout, const ShaderVariableGL::GLVariableBase& Variable) : + // clang-format off + Layout {_Layout}, + VarOffset(reinterpret_cast(&Variable) - reinterpret_cast(_Layout.m_ResourceBuffer.get())) + // clang-format on + {} + + template + bool TryResource(Uint32 NextResourceTypeOffset, Uint32 VarCount) + { + 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 >= 0 && RelativeOffset < VarCount, + "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); + return true; + } + else + { + Index += VarCount; + return false; + } + } + + Uint32 GetIndex() const { return Index; } + +private: + const ShaderVariableGL& Layout; + const size_t VarOffset; + Uint32 Index = 0; +}; + + +Uint32 ShaderVariableGL::GetVariableIndex(const GLVariableBase& Var) const +{ + if (!m_ResourceBuffer) + { + LOG_ERROR("This shader resource layout does not have resources"); + return static_cast(-1); + } + + ShaderVariableIndexLocator IdxLocator(*this, Var); + + if (IdxLocator.TryResource(m_TextureOffset, GetNumUBs())) + return IdxLocator.GetIndex(); + + if (IdxLocator.TryResource(m_ImageOffset, GetNumTextures())) + return IdxLocator.GetIndex(); + + if (IdxLocator.TryResource(m_StorageBufferOffset, GetNumImages())) + return IdxLocator.GetIndex(); + + if (IdxLocator.TryResource(m_VariableEndOffset, GetNumStorageBuffers())) + return IdxLocator.GetIndex(); + + LOG_ERROR("Failed to get variable index. The variable ", &Var, " does not belong to this shader resource layout"); + return ~0u; +} + +#ifdef DILIGENT_DEVELOPMENT +bool ShaderVariableGL::dvpVerifyBindings(const ShaderResourceCacheGL& ResourceCache) const +{ +# define LOG_MISSING_BINDING(VarType, BindInfo, ArrIndex) LOG_ERROR_MESSAGE("No resource is bound to ", VarType, " variable '", GetShaderResourcePrintName(Desc, ArrIndex), "'") + + bool BindingsOK = true; + HandleConstResources( + [&](const UniformBuffBindInfo& ub) // + { + const auto& Desc = GetResourceDesc(ub.m_ResIndex); + const auto& Attr = GetAttribs(ub.m_ResIndex); + for (Uint32 ArrInd = 0; ArrInd < Desc.ArraySize; ++ArrInd) + { + if (!ResourceCache.IsUBBound(Attr.CacheOffset + ArrInd)) + { + LOG_MISSING_BINDING("constant buffer", ub, ArrInd); + BindingsOK = false; + } + } + }, + + [&](const SamplerBindInfo& sam) // + { + const auto& Desc = GetResourceDesc(sam.m_ResIndex); + const auto& Attr = GetAttribs(sam.m_ResIndex); + for (Uint32 ArrInd = 0; ArrInd < Desc.ArraySize; ++ArrInd) + { + VERIFY_EXPR(Desc.ResourceType == SHADER_RESOURCE_TYPE_TEXTURE_SRV || + Desc.ResourceType == SHADER_RESOURCE_TYPE_BUFFER_SRV); + if (!ResourceCache.IsTextureBound(Attr.CacheOffset + ArrInd, Desc.ResourceType == SHADER_RESOURCE_TYPE_TEXTURE_SRV)) + { + LOG_MISSING_BINDING("texture", sam, ArrInd); + BindingsOK = false; + } + else + { + const auto& CachedSampler = ResourceCache.GetConstTexture(Attr.CacheOffset + ArrInd); + if (Attr.IsImmutableSamplerAssigned() && CachedSampler.pSampler == nullptr) + { + LOG_ERROR_MESSAGE("Immutable sampler is not initialized for texture '", Desc.Name, "'"); + BindingsOK = false; + } + } + } + }, + + [&](const ImageBindInfo& img) // + { + const auto& Desc = GetResourceDesc(img.m_ResIndex); + const auto& Attr = GetAttribs(img.m_ResIndex); + for (Uint32 ArrInd = 0; ArrInd < Desc.ArraySize; ++ArrInd) + { + VERIFY_EXPR(Desc.ResourceType == SHADER_RESOURCE_TYPE_TEXTURE_UAV || + Desc.ResourceType == SHADER_RESOURCE_TYPE_BUFFER_UAV); + if (!ResourceCache.IsImageBound(Attr.CacheOffset + ArrInd, Desc.ResourceType == SHADER_RESOURCE_TYPE_TEXTURE_UAV)) + { + LOG_MISSING_BINDING("texture UAV", img, ArrInd); + BindingsOK = false; + } + } + }, + + [&](const StorageBufferBindInfo& ssbo) // + { + const auto& Desc = GetResourceDesc(ssbo.m_ResIndex); + const auto& Attr = GetAttribs(ssbo.m_ResIndex); + for (Uint32 ArrInd = 0; ArrInd < Desc.ArraySize; ++ArrInd) + { + if (!ResourceCache.IsSSBOBound(Attr.CacheOffset + ArrInd)) + { + LOG_MISSING_BINDING("buffer", ssbo, ArrInd); + BindingsOK = false; + } + } + }); +# undef LOG_MISSING_BINDING + + return BindingsOK; +} + +#endif // DILIGENT_DEVELOPMENT + +} // namespace Diligent diff --git a/Graphics/GraphicsEngineOpenGL/src/TextureBaseGL.cpp b/Graphics/GraphicsEngineOpenGL/src/TextureBaseGL.cpp index 33be0cfc..67a69a61 100644 --- a/Graphics/GraphicsEngineOpenGL/src/TextureBaseGL.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/TextureBaseGL.cpp @@ -503,7 +503,7 @@ void TextureBaseGL::UpdateData(GLContextState& CtxState, Uint32 MipLevel, Uint32 // data written by shaders prior to the barrier. Additionally, texture writes from these // commands issued after the barrier will not execute until all shader writes initiated prior // to the barrier complete - TextureMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT, CtxState); + TextureMemoryBarrier(MEMORY_BARRIER_TEXTURE_UPDATE, CtxState); } //void TextureBaseGL::UpdateData(Uint32 Offset, Uint32 Size, const void* pData) @@ -647,19 +647,12 @@ void TextureBaseGL::CopyData(DeviceContextGLImpl* pDeviceCtxGL, } -void TextureBaseGL::TextureMemoryBarrier(Uint32 RequiredBarriers, GLContextState& GLContextState) +void TextureBaseGL::TextureMemoryBarrier(MEMORY_BARRIER RequiredBarriers, GLContextState& GLContextState) { #if GL_ARB_shader_image_load_store # ifdef DILIGENT_DEBUG { - // clang-format off - constexpr Uint32 TextureBarriers = - GL_TEXTURE_FETCH_BARRIER_BIT | - GL_SHADER_IMAGE_ACCESS_BARRIER_BIT | - GL_PIXEL_BUFFER_BARRIER_BIT | - GL_TEXTURE_UPDATE_BARRIER_BIT | - GL_FRAMEBUFFER_BARRIER_BIT; - // clang-format on + constexpr Uint32 TextureBarriers = MEMORY_BARRIER_ALL_TEXTURE_BARRIERS; VERIFY((RequiredBarriers & TextureBarriers) != 0, "At least one texture memory barrier flag should be set"); VERIFY((RequiredBarriers & ~TextureBarriers) == 0, "Inappropriate texture memory barrier flag"); } diff --git a/Graphics/GraphicsEngineOpenGL/src/VAOCache.cpp b/Graphics/GraphicsEngineOpenGL/src/VAOCache.cpp index 6a977e64..43bd9347 100644 --- a/Graphics/GraphicsEngineOpenGL/src/VAOCache.cpp +++ b/Graphics/GraphicsEngineOpenGL/src/VAOCache.cpp @@ -129,10 +129,10 @@ const GLObjectWrappers::GLVertexArrayObj& VAOCache::GetVAO(IPipelineState* VERIFY(pCurrBuf != nullptr, "No buffer bound to slot ", BuffSlot); ValidatedCast(pCurrBuf)->BufferMemoryBarrier( - GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT, // Vertex data sourced from buffer objects after the barrier - // will reflect data written by shaders prior to the barrier. - // The set of buffer objects affected by this bit is derived - // from the GL_VERTEX_ARRAY_BUFFER_BINDING bindings + MEMORY_BARRIER_VERTEX_BUFFER, // Vertex data sourced from buffer objects after the barrier + // will reflect data written by shaders prior to the barrier. + // The set of buffer objects affected by this bit is derived + // from the GL_VERTEX_ARRAY_BUFFER_BINDING bindings GLState); CurrStreamKey.BufferUId = pCurrBuf ? pCurrBuf->GetUniqueID() : 0; @@ -151,10 +151,10 @@ const GLObjectWrappers::GLVertexArrayObj& VAOCache::GetVAO(IPipelineState* if (pIndexBuffer) { pIndexBufferGL->BufferMemoryBarrier( - GL_ELEMENT_ARRAY_BARRIER_BIT, // Vertex array indices sourced from buffer objects after the barrier - // will reflect data written by shaders prior to the barrier. - // The buffer objects affected by this bit are derived from the - // ELEMENT_ARRAY_BUFFER binding. + MEMORY_BARRIER_INDEX_BUFFER, // Vertex array indices sourced from buffer objects after the barrier + // will reflect data written by shaders prior to the barrier. + // The buffer objects affected by this bit are derived from the + // ELEMENT_ARRAY_BUFFER binding. GLState); } -- cgit v1.2.3