/* * Copyright (c) 2024 Huawei Device Co., Ltd. * 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. */ #ifndef DEVICE_SHADER_MANAGER_H #define DEVICE_SHADER_MANAGER_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "device/gpu_program.h" #include "device/gpu_resource_handle_util.h" // for hash CORE_BEGIN_NAMESPACE() class IFileManager; CORE_END_NAMESPACE() RENDER_BEGIN_NAMESPACE() class Device; class ShaderModule; class ShaderLoader; constexpr const uint32_t INVALID_SM_INDEX { ~0u }; struct ComputeShaderCreateData { BASE_NS::string_view path; uint32_t renderSlotId { INVALID_SM_INDEX }; uint32_t categoryId { INVALID_SM_INDEX }; uint32_t pipelineLayoutIndex { INVALID_SM_INDEX }; uint32_t shaderModuleIndex { INVALID_SM_INDEX }; BASE_NS::string_view shaderFileStr; BASE_NS::string_view materialMetadata; }; struct ShaderCreateData { BASE_NS::string_view path; uint32_t renderSlotId { INVALID_SM_INDEX }; uint32_t categoryId { INVALID_SM_INDEX }; uint32_t vertexInputDeclarationIndex { INVALID_SM_INDEX }; uint32_t pipelineLayoutIndex { INVALID_SM_INDEX }; uint32_t graphicsStateIndex { INVALID_SM_INDEX }; uint32_t vertShaderModuleIndex { INVALID_SM_INDEX }; uint32_t fragShaderModuleIndex { INVALID_SM_INDEX }; BASE_NS::string_view shaderFileStr; BASE_NS::string_view materialMetadata; }; struct ShaderReflectionData { BASE_NS::array_view reflectionData; bool IsValid() const; ShaderStageFlags GetStageFlags() const; PipelineLayout GetPipelineLayout() const; BASE_NS::vector GetSpecializationConstants() const; BASE_NS::vector GetInputDescriptions() const; BASE_NS::Math::UVec3 GetLocalSize() const; const uint8_t* GetPushConstants() const; }; struct ShaderModuleCreateInfo { ShaderStageFlags shaderStageFlags; BASE_NS::array_view spvData; ShaderReflectionData reflectionData; }; // Ref counted shaders are really not needed // For API consistency the same RenderHandleReference is used, but the counter is dummy class ShaderReferenceCounter final : public IRenderReferenceCounter { public: ShaderReferenceCounter() = default; ~ShaderReferenceCounter() override = default; ShaderReferenceCounter(const ShaderReferenceCounter&) = delete; ShaderReferenceCounter& operator=(const ShaderReferenceCounter&) = delete; void Ref() override { refcnt_.fetch_add(1, std::memory_order_relaxed); }; void Unref() override { if (std::atomic_fetch_sub_explicit(&refcnt_, 1, std::memory_order_release) == 1) { std::atomic_thread_fence(std::memory_order_acquire); delete this; } }; int32_t GetRefCount() const override { // NOTE: not destroyed based on ref count // the manager is always holding the reference // basically plugins could hold their shader data handles and release return 1; }; private: std::atomic refcnt_ { 0 }; }; /* ShaderManager implementation. Not internally synchronized. */ class ShaderManager final : public IShaderManager { public: static constexpr uint32_t MAX_DEFAULT_NAME_LENGTH { 128 }; explicit ShaderManager(Device& device); ~ShaderManager() override; RenderHandleReference Get(const RenderHandle& handle) const override; RenderHandleReference CreateComputeShader(const ComputeShaderCreateInfo& createInfo) override; RenderHandleReference CreateComputeShader(const ComputeShaderCreateInfo& createInfo, const BASE_NS::string_view baseShaderPath, const BASE_NS::string_view variantName) override; RenderHandleReference CreateShader(const ShaderCreateInfo& createInfo) override; RenderHandleReference CreateShader(const ShaderCreateInfo& createInfo, const BASE_NS::string_view baseShaderPath, const BASE_NS::string_view variantName) override; RenderHandleReference CreateVertexInputDeclaration(const VertexInputDeclarationCreateInfo& createInfo) override; RenderHandleReference CreatePipelineLayout(const PipelineLayoutCreateInfo& createInfo) override; RenderHandleReference CreateGraphicsState(const GraphicsStateCreateInfo& createInfo) override; RenderHandleReference CreateGraphicsState( const GraphicsStateCreateInfo& createInfo, const GraphicsStateVariantCreateInfo& variantCreateInfo) override; uint32_t CreateRenderSlotId(const BASE_NS::string_view renderSlot) override; void SetRenderSlotData(const uint32_t renderSlotId, const RenderHandleReference& shaderHandle, const RenderHandleReference& stateHandle) override; uint32_t CreateCategoryId(const BASE_NS::string_view category); RenderHandleReference GetShaderHandle(const BASE_NS::string_view path) const override; RenderHandleReference GetShaderHandle( const BASE_NS::string_view path, const BASE_NS::string_view variantName) const override; RenderHandleReference GetShaderHandle( const RenderHandleReference& handle, const uint32_t renderSlotId) const override; RenderHandleReference GetShaderHandle(const RenderHandle& handle, const uint32_t renderSlotId) const; BASE_NS::vector GetShaders(const uint32_t renderSlotId) const override; BASE_NS::vector GetShaderRawHandles(const uint32_t renderSlotId) const; RenderHandleReference GetGraphicsStateHandle(const BASE_NS::string_view path) const override; RenderHandleReference GetGraphicsStateHandle( const BASE_NS::string_view path, const BASE_NS::string_view variantName) const override; RenderHandleReference GetGraphicsStateHandle( const RenderHandleReference& handle, const uint32_t renderSlotId) const override; RenderHandleReference GetGraphicsStateHandle(const RenderHandle& handle, const uint32_t renderSlotId) const; RenderHandleReference GetGraphicsStateHandleByHash(const uint64_t hash) const override; RenderHandleReference GetGraphicsStateHandleByShaderHandle(const RenderHandleReference& handle) const override; RenderHandleReference GetGraphicsStateHandleByShaderHandle(const RenderHandle& handle) const; GraphicsState GetGraphicsState(const RenderHandleReference& handle) const override; const GraphicsState& GetGraphicsStateRef(const RenderHandleReference& handle) const; const GraphicsState& GetGraphicsStateRef(const RenderHandle& handle) const; BASE_NS::vector GetGraphicsStates(const uint32_t renderSlotId) const override; uint32_t GetRenderSlotId(const BASE_NS::string_view renderSlot) const override; uint32_t GetRenderSlotId(const RenderHandleReference& handle) const override; uint32_t GetRenderSlotId(const RenderHandle& handle) const; RenderSlotData GetRenderSlotData(const uint32_t renderSlotId) const override; BASE_NS::string GetRenderSlotName(const uint32_t renderSlotId) const override; RenderHandleReference GetVertexInputDeclarationHandleByShaderHandle( const RenderHandleReference& handle) const override; RenderHandleReference GetVertexInputDeclarationHandleByShaderHandle(const RenderHandle& handle) const; RenderHandleReference GetVertexInputDeclarationHandle(const BASE_NS::string_view path) const override; VertexInputDeclarationView GetVertexInputDeclarationView(const RenderHandleReference& handle) const override; VertexInputDeclarationView GetVertexInputDeclarationView(const RenderHandle& handle) const; RenderHandleReference GetPipelineLayoutHandleByShaderHandle(const RenderHandleReference& handle) const override; RenderHandleReference GetPipelineLayoutHandleByShaderHandle(const RenderHandle& handle) const; RenderHandleReference GetPipelineLayoutHandle(const BASE_NS::string_view path) const override; PipelineLayout GetPipelineLayout(const RenderHandleReference& handle) const override; PipelineLayout GetPipelineLayout(const RenderHandle& handle) const; const PipelineLayout& GetPipelineLayoutRef(const RenderHandle& handle) const; RenderHandleReference GetReflectionPipelineLayoutHandle(const RenderHandle& handle) const; RenderHandleReference GetReflectionPipelineLayoutHandle(const RenderHandleReference& handle) const override; PipelineLayout GetReflectionPipelineLayout(const RenderHandleReference& handle) const override; const PipelineLayout& GetReflectionPipelineLayoutRef(const RenderHandle& handle) const; ShaderSpecializationConstantView GetReflectionSpecialization(const RenderHandleReference& handle) const override; ShaderSpecializationConstantView GetReflectionSpecialization(const RenderHandle& handle) const; VertexInputDeclarationView GetReflectionVertexInputDeclaration(const RenderHandleReference& handle) const override; VertexInputDeclarationView GetReflectionVertexInputDeclaration(const RenderHandle& handle) const; ShaderThreadGroup GetReflectionThreadGroupSize(const RenderHandleReference& handle) const override; ShaderThreadGroup GetReflectionThreadGroupSize(const RenderHandle& handle) const; uint64_t HashGraphicsState(const GraphicsState& graphicsState) const override; struct ShaderPathCreateData { const BASE_NS::string_view baseShaderPath; const BASE_NS::string_view variantName; const BASE_NS::string_view displayName; }; RenderHandleReference Create(const ComputeShaderCreateData& createInfo, const ShaderPathCreateData& pathCreateInfo); RenderHandleReference Create(const ShaderCreateData& createInfo, const ShaderPathCreateData& pathCreateInfo); // NOTE: Can be used to add additional base uri name for a shader which has variant names void AddAdditionalNameForHandle(const RenderHandleReference& handle, const BASE_NS::string_view path); const GpuComputeProgram* GetGpuComputeProgram(const RenderHandle& gpuHandle) const; const GpuShaderProgram* GetGpuShaderProgram(const RenderHandle& gpuHandle) const; void HandlePendingAllocations(); // replaces if already found uint32_t CreateShaderModule(const BASE_NS::string_view path, const ShaderModuleCreateInfo& createInfo); // returns null if not found (i.e. needs to be created) ShaderModule* GetShaderModule(const uint32_t index) const; // returns ~0u if not found (i.e. needs to be created) uint32_t GetShaderModuleIndex(const BASE_NS::string_view path) const; bool IsComputeShader(const RenderHandleReference& handle) const override; bool IsShader(const RenderHandleReference& handle) const override; void LoadShaderFiles(const ShaderFilePathDesc& desc) override; void LoadShaderFile(const BASE_NS::string_view uri) override; void ReloadShaderFile(const BASE_NS::string_view uri) override; void UnloadShaderFiles(const ShaderFilePathDesc& desc) override; const BASE_NS::string_view GetShaderFile(const RenderHandleReference& handle) const override; const CORE_NS::json::value* GetMaterialMetadata(const RenderHandleReference& handle) const override; void Destroy(const RenderHandleReference& handle) override; BASE_NS::vector GetShaders( const RenderHandleReference& handle, const ShaderStageFlags shaderStageFlags) const override; BASE_NS::vector GetShaders(const RenderHandle& handle, const ShaderStageFlags shaderStageFlags) const; BASE_NS::vector GetShaders() const override; BASE_NS::vector GetGraphicsStates() const override; BASE_NS::vector GetPipelineLayouts() const override; BASE_NS::vector GetVertexInputDeclarations() const override; IdDesc GetIdDesc(const RenderHandleReference& handle) const override; uint64_t GetFrameIndex(const RenderHandleReference& handle) const override; IShaderPipelineBinder::Ptr CreateShaderPipelineBinder(const RenderHandleReference& handle) const override; IShaderPipelineBinder::Ptr CreateShaderPipelineBinder( const RenderHandleReference& handle, const RenderHandleReference& plHandle) const override; IShaderPipelineBinder::Ptr CreateShaderPipelineBinder( const RenderHandleReference& handle, const PipelineLayout& pipelineLayout) const override; CompatibilityFlags GetCompatibilityFlags(const RenderHandle& lhs, const RenderHandle& rhs) const; CompatibilityFlags GetCompatibilityFlags( const RenderHandleReference& lhs, const RenderHandleReference& rhs) const override; GraphicsStateFlags GetForcedGraphicsStateFlags(const RenderHandle& handle) const; GraphicsStateFlags GetForcedGraphicsStateFlags(const RenderHandleReference& handle) const override; GraphicsStateFlags GetForcedGraphicsStateFlags(const uint32_t renderSlotId) const override; // set (engine) file manager to be usable with shader loading with shader manager api void SetFileManager(CORE_NS::IFileManager& fileMgr); bool HasReloadedShaderForBackend() const; BASE_NS::array_view GetReloadedShadersForBackend() const; struct ShaderNameData { BASE_NS::fixed_string path; BASE_NS::fixed_string variantName; BASE_NS::fixed_string displayName; }; struct ComputeMappings { struct Data { RenderHandleReference rhr; RenderHandle baseShaderHandle; // provides a link to find related shaders uint32_t renderSlotId { INVALID_SM_INDEX }; uint32_t pipelineLayoutIndex { INVALID_SM_INDEX }; uint32_t reflectionPipelineLayoutIndex { INVALID_SM_INDEX }; uint32_t categoryId { INVALID_SM_INDEX }; uint64_t frameIndex { 0 }; }; BASE_NS::vector clientData; BASE_NS::vector nameData; }; struct GraphicsMappings { struct Data { RenderHandleReference rhr; RenderHandle baseShaderHandle; // provides a link to find related shaders uint32_t renderSlotId { INVALID_SM_INDEX }; uint32_t pipelineLayoutIndex { INVALID_SM_INDEX }; uint32_t reflectionPipelineLayoutIndex { INVALID_SM_INDEX }; uint32_t vertexInputDeclarationIndex { INVALID_SM_INDEX }; uint32_t graphicsStateIndex { INVALID_SM_INDEX }; uint32_t categoryId { INVALID_SM_INDEX }; uint64_t frameIndex { 0 }; }; BASE_NS::vector clientData; BASE_NS::vector nameData; }; struct GraphicsStateData { struct Indices { uint64_t hash { 0 }; uint32_t renderSlotId { INVALID_SM_INDEX }; uint32_t baseVariantIndex { INVALID_SM_INDEX }; // forced state flags GraphicsStateFlags stateFlags { 0u }; }; BASE_NS::vector rhr; BASE_NS::vector graphicsStates; BASE_NS::vector data; BASE_NS::unordered_map nameToIndex; BASE_NS::unordered_map hashToIndex; // hash based on base graphics state handle and render slot id BASE_NS::unordered_map variantHashToIndex; }; private: Device& device_; CORE_NS::IFileManager* fileMgr_ { nullptr }; // engine file manager to be used with shader loading from API BASE_NS::unique_ptr shaderLoader_; // for all shaders, names are unique BASE_NS::unordered_map nameToClientHandle_; // hash based on base shader handle id and render slot id BASE_NS::unordered_map hashToShaderVariant_; ComputeMappings computeShaderMappings_; GraphicsMappings shaderMappings_; struct ClientDataIndices { uint32_t renderSlotIndex { INVALID_SM_INDEX }; uint32_t pipelineLayoutIndex { INVALID_SM_INDEX }; uint32_t reflectionPipelineLayoutIndex { INVALID_SM_INDEX }; uint32_t categoryIndex { INVALID_SM_INDEX }; }; RenderHandle CreateClientData( const BASE_NS::string_view path, const RenderHandleType type, const ClientDataIndices& cdi); void DestroyShader(const RenderHandle handle); void DestroyGraphicsState(const RenderHandle handle); void DestroyPipelineLayout(const RenderHandle handle); void DestroyVertexInputDeclaration(const RenderHandle handle); IdDesc GetShaderIdDesc(const RenderHandle handle) const; uint64_t GetShaderFrameIndex(const RenderHandle handle) const; BASE_NS::string GetCategoryName(const uint32_t categoryId) const; // NOTE: ATM GpuComputeProgram and GpuShaderPrograms are currently re-created for every new shader created // will be stored and re-used in the future struct ComputeShaderData { BASE_NS::unique_ptr gsp; uint32_t pipelineLayoutIndex { INVALID_SM_INDEX }; uint32_t compModuleIndex { INVALID_SM_INDEX }; }; BASE_NS::vector computeShaders_; struct ShaderData { BASE_NS::unique_ptr gsp; uint32_t pipelineLayoutIndex { INVALID_SM_INDEX }; uint32_t vertexInputDeclIndex { INVALID_SM_INDEX }; uint32_t vertModuleIndex { INVALID_SM_INDEX }; uint32_t fragModuleIndex { INVALID_SM_INDEX }; }; BASE_NS::vector shaders_; struct RenderSlotIds { BASE_NS::vector data; BASE_NS::unordered_map nameToId; }; RenderSlotIds renderSlotIds_; struct Category { BASE_NS::vector data; BASE_NS::unordered_map nameToId; }; Category category_; struct ComputeShaderAllocs { RenderHandle handle; uint32_t computeModuleIndex { INVALID_SM_INDEX }; uint32_t pipelineLayoutIndex { INVALID_SM_INDEX }; }; struct ShaderAllocs { RenderHandle handle; uint32_t vertModuleIndex { INVALID_SM_INDEX }; uint32_t fragModuleIndex { INVALID_SM_INDEX }; uint32_t pipelineLayoutIndex { INVALID_SM_INDEX }; uint32_t vertexInputDeclIndex { INVALID_SM_INDEX }; }; struct Allocs { // client handle, object BASE_NS::vector computeShaders; BASE_NS::vector shaders; BASE_NS::vector recreatedComputeModuleIndices; BASE_NS::vector recreatedShaderModuleIndices; BASE_NS::vector destroyHandles; }; Allocs pendingAllocations_; // locks only pending allocations data std::mutex pendingMutex_; void HandlePendingShaders(Allocs& allocs); void HandlePendingModules(Allocs& allocs); struct DeferredDestructions { struct Module { uint64_t frameIndex { 0 }; BASE_NS::unique_ptr shaderModule; }; struct ComputeProgram { uint64_t frameIndex { 0 }; BASE_NS::unique_ptr computeProgram; }; struct ShaderProgram { uint64_t frameIndex { 0 }; BASE_NS::unique_ptr shaderProgram; }; BASE_NS::vector shaderModules; BASE_NS::vector computePrograms; BASE_NS::vector shaderPrograms; }; DeferredDestructions deferredDestructions_; struct PL { BASE_NS::vector rhr; BASE_NS::vector data; BASE_NS::unordered_map nameToIndex; BASE_NS::unordered_map computeShaderToIndex; BASE_NS::unordered_map shaderToIndex; }; struct VID { BASE_NS::vector rhr; BASE_NS::vector data; BASE_NS::unordered_map nameToIndex; BASE_NS::unordered_map shaderToIndex; }; VID shaderVid_; PL pl_; GraphicsStateData graphicsStates_; struct ShaderModules { BASE_NS::vector> shaderModules; BASE_NS::unordered_map nameToIndex; }; ShaderModules shaderModules_; PipelineLayout defaultPipelineLayout_; ComputeShaderReflection defaultComputeShaderReflection_; ShaderReflection defaultShaderReflection_; GraphicsState defaultGraphicsState_; ShaderSpecializationConstantView defaultSSCV_; VertexInputDeclarationView defaultVIDV_; ShaderThreadGroup defaultSTG_; struct MaterialMetadata { BASE_NS::string raw; CORE_NS::json::value json; }; BASE_NS::unordered_map shaderToMetadata_; BASE_NS::unordered_map handleToShaderDataFile_; BASE_NS::vector reloadedShaders_; BASE_NS::vector reloadedShadersForBackend_; }; class RenderNodeShaderManager final : public IRenderNodeShaderManager { public: explicit RenderNodeShaderManager(const ShaderManager& shaderMgr); ~RenderNodeShaderManager() = default; RenderHandle GetShaderHandle(const BASE_NS::string_view path) const override; RenderHandle GetShaderHandle( const BASE_NS::string_view path, const BASE_NS::string_view variantName) const override; RenderHandle GetShaderHandle(const RenderHandle& name, const uint32_t renderSlotId) const override; BASE_NS::vector GetShaders(const uint32_t renderSlotId) const override; RenderHandle GetGraphicsStateHandle(const BASE_NS::string_view path) const override; RenderHandle GetGraphicsStateHandle( const BASE_NS::string_view path, const BASE_NS::string_view variantName) const override; RenderHandle GetGraphicsStateHandle(const RenderHandle& handle, const uint32_t renderSlotId) const override; RenderHandle GetGraphicsStateHandleByHash(const uint64_t hash) const override; RenderHandle GetGraphicsStateHandleByShaderHandle(const RenderHandle& handle) const override; virtual const GraphicsState& GetGraphicsState(const RenderHandle& handle) const override; uint32_t GetRenderSlotId(const BASE_NS::string_view renderSlot) const override; uint32_t GetRenderSlotId(const RenderHandle& handle) const override; IShaderManager::RenderSlotData GetRenderSlotData(const uint32_t renderSlotId) const override; RenderHandle GetVertexInputDeclarationHandleByShaderHandle(const RenderHandle& handle) const override; RenderHandle GetVertexInputDeclarationHandle(const BASE_NS::string_view path) const override; VertexInputDeclarationView GetVertexInputDeclarationView(const RenderHandle& handle) const override; RenderHandle GetPipelineLayoutHandleByShaderHandle(const RenderHandle& handle) const override; const PipelineLayout& GetPipelineLayout(const RenderHandle& handle) const override; RenderHandle GetPipelineLayoutHandle(const BASE_NS::string_view path) const override; RenderHandle GetReflectionPipelineLayoutHandle(const RenderHandle& handle) const override; const PipelineLayout& GetReflectionPipelineLayout(const RenderHandle& handle) const override; ShaderSpecializationConstantView GetReflectionSpecialization(const RenderHandle& handle) const override; VertexInputDeclarationView GetReflectionVertexInputDeclaration(const RenderHandle& handle) const override; ShaderThreadGroup GetReflectionThreadGroupSize(const RenderHandle& handle) const override; uint64_t HashGraphicsState(const GraphicsState& graphicsState) const override; bool IsValid(const RenderHandle& handle) const override; bool IsComputeShader(const RenderHandle& handle) const override; bool IsShader(const RenderHandle& handle) const override; BASE_NS::vector GetShaders( const RenderHandle& handle, const ShaderStageFlags shaderStageFlags) const override; IShaderManager::CompatibilityFlags GetCompatibilityFlags( const RenderHandle& lhs, const RenderHandle& rhs) const override; GraphicsStateFlags GetForcedGraphicsStateFlags(const RenderHandle& handle) const override; GraphicsStateFlags GetForcedGraphicsStateFlags(const uint32_t renderSlotId) const override; private: const ShaderManager& shaderMgr_; }; RENDER_END_NAMESPACE() #endif // DEVICE_SHADER_MANAGER_H