blob: 6f9ca86a6f9832c5a2a586ad2928d0183d65ef2a [file] [log] [blame]
//
// 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_