| // |
| // Copyright 2020 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| |
| // ProgramExecutableVk.h: Collects the information and interfaces common to both ProgramVks and |
| // ProgramPipelineVks in order to execute/draw with either. |
| |
| #ifndef LIBANGLE_RENDERER_VULKAN_PROGRAMEXECUTABLEVK_H_ |
| #define LIBANGLE_RENDERER_VULKAN_PROGRAMEXECUTABLEVK_H_ |
| |
| #include "common/bitset_utils.h" |
| #include "common/mathutil.h" |
| #include "common/utilities.h" |
| #include "libANGLE/Context.h" |
| #include "libANGLE/InfoLog.h" |
| #include "libANGLE/renderer/ShaderInterfaceVariableInfoMap.h" |
| #include "libANGLE/renderer/glslang_wrapper_utils.h" |
| #include "libANGLE/renderer/vulkan/ContextVk.h" |
| #include "libANGLE/renderer/vulkan/vk_cache_utils.h" |
| #include "libANGLE/renderer/vulkan/vk_helpers.h" |
| |
| namespace rx |
| { |
| |
| class ShaderInfo final : angle::NonCopyable |
| { |
| public: |
| ShaderInfo(); |
| ~ShaderInfo(); |
| |
| angle::Result initShaders(const gl::ShaderBitSet &linkedShaderStages, |
| const gl::ShaderMap<const angle::spirv::Blob *> &spirvBlobs, |
| const ShaderInterfaceVariableInfoMap &variableInfoMap); |
| void initShaderFromProgram(gl::ShaderType shaderType, const ShaderInfo &programShaderInfo); |
| void clear(); |
| |
| ANGLE_INLINE bool valid() const { return mIsInitialized; } |
| |
| const gl::ShaderMap<angle::spirv::Blob> &getSpirvBlobs() const { return mSpirvBlobs; } |
| |
| // Save and load implementation for GLES Program Binary support. |
| void load(gl::BinaryInputStream *stream); |
| void save(gl::BinaryOutputStream *stream); |
| |
| private: |
| gl::ShaderMap<angle::spirv::Blob> mSpirvBlobs; |
| bool mIsInitialized = false; |
| }; |
| |
| struct ProgramTransformOptions final |
| { |
| uint8_t enableLineRasterEmulation : 1; |
| uint8_t surfaceRotation : 1; |
| uint8_t enableDepthCorrection : 1; |
| uint8_t removeTransformFeedbackEmulation : 1; |
| uint8_t reserved : 4; // must initialize to zero |
| static constexpr uint32_t kPermutationCount = 0x1 << 4; |
| }; |
| static_assert(sizeof(ProgramTransformOptions) == 1, "Size check failed"); |
| static_assert(static_cast<int>(SurfaceRotation::EnumCount) <= 8, "Size check failed"); |
| |
| class ProgramInfo final : angle::NonCopyable |
| { |
| public: |
| ProgramInfo(); |
| ~ProgramInfo(); |
| |
| angle::Result initProgram(ContextVk *contextVk, |
| gl::ShaderType shaderType, |
| bool isLastPreFragmentStage, |
| bool isTransformFeedbackProgram, |
| const ShaderInfo &shaderInfo, |
| ProgramTransformOptions optionBits, |
| const ShaderInterfaceVariableInfoMap &variableInfoMap); |
| void release(ContextVk *contextVk); |
| |
| ANGLE_INLINE bool valid(gl::ShaderType shaderType) const |
| { |
| return mProgramHelper.valid(shaderType); |
| } |
| |
| vk::ShaderProgramHelper *getShaderProgram() { return &mProgramHelper; } |
| |
| private: |
| vk::ShaderProgramHelper mProgramHelper; |
| gl::ShaderMap<vk::RefCounted<vk::ShaderAndSerial>> mShaders; |
| }; |
| |
| // State for the default uniform blocks. |
| struct DefaultUniformBlock final : private angle::NonCopyable |
| { |
| DefaultUniformBlock(); |
| ~DefaultUniformBlock(); |
| |
| // Shadow copies of the shader uniform data. |
| angle::MemoryBuffer uniformData; |
| |
| // Since the default blocks are laid out in std140, this tells us where to write on a call |
| // to a setUniform method. They are arranged in uniform location order. |
| std::vector<sh::BlockMemberInfo> uniformLayout; |
| }; |
| |
| // Performance and resource counters. |
| using DescriptorSetCountList = angle::PackedEnumMap<DescriptorSetIndex, uint32_t>; |
| using ImmutableSamplerIndexMap = angle::HashMap<vk::YcbcrConversionDesc, uint32_t>; |
| |
| using DefaultUniformBlockMap = gl::ShaderMap<std::shared_ptr<DefaultUniformBlock>>; |
| |
| class ProgramExecutableVk |
| { |
| public: |
| ProgramExecutableVk(); |
| virtual ~ProgramExecutableVk(); |
| |
| void reset(ContextVk *contextVk); |
| |
| void save(gl::BinaryOutputStream *stream); |
| std::unique_ptr<rx::LinkEvent> load(ContextVk *contextVk, |
| const gl::ProgramExecutable &glExecutable, |
| gl::BinaryInputStream *stream); |
| |
| void clearVariableInfoMap(); |
| |
| ProgramInfo &getGraphicsProgramInfo() |
| { |
| uint8_t index = gl::bitCast<uint8_t, ProgramTransformOptions>(mTransformOptions); |
| return mGraphicsProgramInfos[index]; |
| } |
| ProgramInfo &getComputeProgramInfo() { return mComputeProgramInfo; } |
| vk::BufferSerial getCurrentDefaultUniformBufferSerial() const |
| { |
| return mCurrentDefaultUniformBufferSerial; |
| } |
| |
| angle::Result getGraphicsPipeline(ContextVk *contextVk, |
| gl::PrimitiveMode mode, |
| const vk::GraphicsPipelineDesc &desc, |
| const gl::ProgramExecutable &glExecutable, |
| const vk::GraphicsPipelineDesc **descPtrOut, |
| vk::PipelineHelper **pipelineOut); |
| |
| angle::Result getComputePipeline(ContextVk *contextVk, vk::PipelineHelper **pipelineOut); |
| |
| const vk::PipelineLayout &getPipelineLayout() const { return mPipelineLayout.get(); } |
| angle::Result createPipelineLayout(ContextVk *contextVk, |
| const gl::ProgramExecutable &glExecutable, |
| gl::ActiveTextureArray<TextureVk *> *activeTextures); |
| |
| angle::Result updateTexturesDescriptorSet(vk::Context *context, |
| const gl::ProgramExecutable &executable, |
| const gl::ActiveTextureArray<TextureVk *> &textures, |
| const gl::SamplerBindingVector &samplers, |
| bool emulateSeamfulCubeMapSampling, |
| PipelineType pipelineType, |
| UpdateDescriptorSetsBuilder *updateBuilder, |
| vk::CommandBufferHelperCommon *commandBufferHelper, |
| const vk::DescriptorSetDesc &texturesDesc); |
| angle::Result updateShaderResourcesDescriptorSet( |
| ContextVk *contextVk, |
| UpdateDescriptorSetsBuilder *updateBuilder, |
| vk::CommandBufferHelperCommon *commandBufferHelper, |
| const vk::DescriptorSetDescBuilder &shaderResourcesDesc); |
| angle::Result updateUniformsAndXfbDescriptorSet( |
| vk::Context *context, |
| UpdateDescriptorSetsBuilder *updateBuilder, |
| vk::CommandBufferHelperCommon *commandBufferHelper, |
| vk::BufferHelper *defaultUniformBuffer, |
| const vk::DescriptorSetDescBuilder &uniformsAndXfbDesc); |
| |
| template <typename CommandBufferT> |
| angle::Result bindDescriptorSets(vk::Context *context, |
| vk::CommandBufferHelperCommon *commandBufferHelper, |
| CommandBufferT *commandBuffer, |
| PipelineType pipelineType); |
| |
| bool usesDynamicUniformBufferDescriptors() const |
| { |
| return mUniformBufferDescriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; |
| } |
| VkDescriptorType getUniformBufferDescriptorType() const { return mUniformBufferDescriptorType; } |
| bool usesDynamicShaderStorageBufferDescriptors() const { return false; } |
| VkDescriptorType getStorageBufferDescriptorType() const |
| { |
| return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; |
| } |
| bool usesDynamicAtomicCounterBufferDescriptors() const { return false; } |
| |
| bool areImmutableSamplersCompatible( |
| const ImmutableSamplerIndexMap &immutableSamplerIndexMap) const |
| { |
| return (mImmutableSamplerIndexMap == immutableSamplerIndexMap); |
| } |
| |
| size_t getDefaultUniformAlignedSize(vk::Context *context, gl::ShaderType shaderType) const |
| { |
| RendererVk *renderer = context->getRenderer(); |
| size_t alignment = static_cast<size_t>( |
| renderer->getPhysicalDeviceProperties().limits.minUniformBufferOffsetAlignment); |
| return roundUp(mDefaultUniformBlocks[shaderType]->uniformData.size(), alignment); |
| } |
| |
| std::shared_ptr<DefaultUniformBlock> &getSharedDefaultUniformBlock(gl::ShaderType shaderType) |
| { |
| return mDefaultUniformBlocks[shaderType]; |
| } |
| |
| bool hasDirtyUniforms() const { return mDefaultUniformBlocksDirty.any(); } |
| |
| void setAllDefaultUniformsDirty(const gl::ProgramExecutable &executable); |
| angle::Result updateUniforms(vk::Context *context, |
| UpdateDescriptorSetsBuilder *updateBuilder, |
| vk::CommandBufferHelperCommon *commandBufferHelper, |
| vk::BufferHelper *emptyBuffer, |
| const gl::ProgramExecutable &glExecutable, |
| vk::DynamicBuffer *defaultUniformStorage, |
| bool isTransformFeedbackActiveUnpaused, |
| TransformFeedbackVk *transformFeedbackVk); |
| void onProgramBind(const gl::ProgramExecutable &glExecutable); |
| |
| const ShaderInterfaceVariableInfoMap &getVariableInfoMap() const { return mVariableInfoMap; } |
| |
| private: |
| friend class ProgramVk; |
| friend class ProgramPipelineVk; |
| |
| void addInterfaceBlockDescriptorSetDesc(const std::vector<gl::InterfaceBlock> &blocks, |
| gl::ShaderType shaderType, |
| ShaderVariableType variableType, |
| VkDescriptorType descType, |
| vk::DescriptorSetLayoutDesc *descOut); |
| void addAtomicCounterBufferDescriptorSetDesc( |
| const std::vector<gl::AtomicCounterBuffer> &atomicCounterBuffers, |
| gl::ShaderType shaderType, |
| vk::DescriptorSetLayoutDesc *descOut); |
| void addImageDescriptorSetDesc(const gl::ProgramExecutable &executable, |
| vk::DescriptorSetLayoutDesc *descOut); |
| void addInputAttachmentDescriptorSetDesc(const gl::ProgramExecutable &executable, |
| gl::ShaderType shaderType, |
| vk::DescriptorSetLayoutDesc *descOut); |
| angle::Result addTextureDescriptorSetDesc( |
| ContextVk *contextVk, |
| const gl::ProgramExecutable &executable, |
| const gl::ActiveTextureArray<TextureVk *> *activeTextures, |
| vk::DescriptorSetLayoutDesc *descOut); |
| |
| void resolvePrecisionMismatch(const gl::ProgramMergedVaryings &mergedVaryings); |
| |
| size_t calcUniformUpdateRequiredSpace(vk::Context *context, |
| const gl::ProgramExecutable &glExecutable, |
| gl::ShaderMap<VkDeviceSize> *uniformOffsets) const; |
| |
| ANGLE_INLINE angle::Result initProgram(ContextVk *contextVk, |
| gl::ShaderType shaderType, |
| bool isLastPreFragmentStage, |
| bool isTransformFeedbackProgram, |
| ProgramTransformOptions optionBits, |
| ProgramInfo *programInfo, |
| const ShaderInterfaceVariableInfoMap &variableInfoMap) |
| { |
| ASSERT(mOriginalShaderInfo.valid()); |
| |
| // Create the program pipeline. This is done lazily and once per combination of |
| // specialization constants. |
| if (!programInfo->valid(shaderType)) |
| { |
| ANGLE_TRY(programInfo->initProgram(contextVk, shaderType, isLastPreFragmentStage, |
| isTransformFeedbackProgram, mOriginalShaderInfo, |
| optionBits, variableInfoMap)); |
| } |
| ASSERT(programInfo->valid(shaderType)); |
| |
| return angle::Result::Continue; |
| } |
| |
| ANGLE_INLINE angle::Result initGraphicsShaderProgram( |
| ContextVk *contextVk, |
| gl::ShaderType shaderType, |
| bool isLastPreFragmentStage, |
| bool isTransformFeedbackProgram, |
| ProgramTransformOptions optionBits, |
| ProgramInfo *programInfo, |
| const ShaderInterfaceVariableInfoMap &variableInfoMap) |
| { |
| return initProgram(contextVk, shaderType, isLastPreFragmentStage, |
| isTransformFeedbackProgram, optionBits, programInfo, variableInfoMap); |
| } |
| |
| ANGLE_INLINE angle::Result initComputeProgram( |
| ContextVk *contextVk, |
| ProgramInfo *programInfo, |
| const ShaderInterfaceVariableInfoMap &variableInfoMap) |
| { |
| ProgramTransformOptions optionBits = {}; |
| return initProgram(contextVk, gl::ShaderType::Compute, false, false, optionBits, |
| programInfo, variableInfoMap); |
| } |
| |
| angle::Result resizeUniformBlockMemory(ContextVk *contextVk, |
| const gl::ProgramExecutable &glExecutable, |
| const gl::ShaderMap<size_t> &requiredBufferSize); |
| |
| angle::Result getOrAllocateDescriptorSet(vk::Context *context, |
| UpdateDescriptorSetsBuilder *updateBuilder, |
| vk::CommandBufferHelperCommon *commandBufferHelper, |
| const vk::DescriptorSetDescBuilder &descriptorSetDesc, |
| DescriptorSetIndex setIndex); |
| |
| // Descriptor sets and pools for shader resources for this program. |
| vk::DescriptorSetArray<VkDescriptorSet> mDescriptorSets; |
| vk::DescriptorSetArray<VkDescriptorSet> mEmptyDescriptorSets; |
| vk::DescriptorSetArray<vk::DescriptorPoolPointer> mDescriptorPools; |
| vk::DescriptorSetArray<vk::RefCountedDescriptorPoolBinding> mDescriptorPoolBindings; |
| uint32_t mNumDefaultUniformDescriptors; |
| vk::BufferSerial mCurrentDefaultUniformBufferSerial; |
| |
| // We keep a reference to the pipeline and descriptor set layouts. This ensures they don't get |
| // deleted while this program is in use. |
| uint32_t mImmutableSamplersMaxDescriptorCount; |
| ImmutableSamplerIndexMap mImmutableSamplerIndexMap; |
| vk::BindingPointer<vk::PipelineLayout> mPipelineLayout; |
| vk::DescriptorSetLayoutPointerArray mDescriptorSetLayouts; |
| |
| // A set of dynamic offsets used with vkCmdBindDescriptorSets for the default uniform buffers. |
| VkDescriptorType mUniformBufferDescriptorType; |
| gl::ShaderVector<uint32_t> mDynamicUniformDescriptorOffsets; |
| std::vector<uint32_t> mDynamicShaderResourceDescriptorOffsets; |
| |
| ShaderInterfaceVariableInfoMap mVariableInfoMap; |
| |
| // We store all permutations of surface rotation and transformed SPIR-V programs here. We may |
| // need some LRU algorithm to free least used programs to reduce the number of programs. |
| ProgramInfo mGraphicsProgramInfos[ProgramTransformOptions::kPermutationCount]; |
| ProgramInfo mComputeProgramInfo; |
| |
| ProgramTransformOptions mTransformOptions; |
| |
| DefaultUniformBlockMap mDefaultUniformBlocks; |
| gl::ShaderBitSet mDefaultUniformBlocksDirty; |
| |
| ShaderInfo mOriginalShaderInfo; |
| }; |
| |
| } // namespace rx |
| |
| #endif // LIBANGLE_RENDERER_VULKAN_PROGRAMEXECUTABLEVK_H_ |