blob: bfcb9080a71a9d2ff3ddb8b6c99d3b71bb4417f7 [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/ProgramExecutable.h"
#include "libANGLE/renderer/ProgramExecutableImpl.h"
#include "libANGLE/renderer/vulkan/ContextVk.h"
#include "libANGLE/renderer/vulkan/ShaderInterfaceVariableInfoMap.h"
#include "libANGLE/renderer/vulkan/spv_utils.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(vk::ErrorContext *context,
const gl::ShaderBitSet &linkedShaderStages,
const gl::ShaderMap<const angle::spirv::Blob *> &spirvBlobs,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
bool isGLES1);
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;
};
union ProgramTransformOptions final
{
struct
{
uint8_t surfaceRotation : 1;
uint8_t removeTransformFeedbackEmulation : 1;
uint8_t multiSampleFramebufferFetch : 1;
uint8_t enableSampleShading : 1;
uint8_t removeDepthStencilInput : 1;
uint8_t reserved : 3; // must initialize to zero
};
uint8_t permutationIndex;
static constexpr uint32_t kPermutationCount = 0x1 << 5;
};
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(vk::ErrorContext *context,
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;
vk::ShaderModuleMap mShaders;
};
using ImmutableSamplerIndexMap = angle::HashMap<vk::YcbcrConversionDesc, uint32_t>;
class ProgramExecutableVk : public ProgramExecutableImpl
{
public:
ProgramExecutableVk(const gl::ProgramExecutable *executable);
~ProgramExecutableVk() override;
void destroy(const gl::Context *context) override;
void save(ContextVk *contextVk, bool isSeparable, gl::BinaryOutputStream *stream);
angle::Result load(ContextVk *contextVk,
bool isSeparable,
gl::BinaryInputStream *stream,
egl::CacheGetResult *resultOut);
void setUniform1fv(GLint location, GLsizei count, const GLfloat *v) override;
void setUniform2fv(GLint location, GLsizei count, const GLfloat *v) override;
void setUniform3fv(GLint location, GLsizei count, const GLfloat *v) override;
void setUniform4fv(GLint location, GLsizei count, const GLfloat *v) override;
void setUniform1iv(GLint location, GLsizei count, const GLint *v) override;
void setUniform2iv(GLint location, GLsizei count, const GLint *v) override;
void setUniform3iv(GLint location, GLsizei count, const GLint *v) override;
void setUniform4iv(GLint location, GLsizei count, const GLint *v) override;
void setUniform1uiv(GLint location, GLsizei count, const GLuint *v) override;
void setUniform2uiv(GLint location, GLsizei count, const GLuint *v) override;
void setUniform3uiv(GLint location, GLsizei count, const GLuint *v) override;
void setUniform4uiv(GLint location, GLsizei count, const GLuint *v) override;
void setUniformMatrix2fv(GLint location,
GLsizei count,
GLboolean transpose,
const GLfloat *value) override;
void setUniformMatrix3fv(GLint location,
GLsizei count,
GLboolean transpose,
const GLfloat *value) override;
void setUniformMatrix4fv(GLint location,
GLsizei count,
GLboolean transpose,
const GLfloat *value) override;
void setUniformMatrix2x3fv(GLint location,
GLsizei count,
GLboolean transpose,
const GLfloat *value) override;
void setUniformMatrix3x2fv(GLint location,
GLsizei count,
GLboolean transpose,
const GLfloat *value) override;
void setUniformMatrix2x4fv(GLint location,
GLsizei count,
GLboolean transpose,
const GLfloat *value) override;
void setUniformMatrix4x2fv(GLint location,
GLsizei count,
GLboolean transpose,
const GLfloat *value) override;
void setUniformMatrix3x4fv(GLint location,
GLsizei count,
GLboolean transpose,
const GLfloat *value) override;
void setUniformMatrix4x3fv(GLint location,
GLsizei count,
GLboolean transpose,
const GLfloat *value) override;
void getUniformfv(const gl::Context *context, GLint location, GLfloat *params) const override;
void getUniformiv(const gl::Context *context, GLint location, GLint *params) const override;
void getUniformuiv(const gl::Context *context, GLint location, GLuint *params) const override;
void clearVariableInfoMap();
vk::BufferSerial getCurrentDefaultUniformBufferSerial() const
{
return mCurrentDefaultUniformBufferSerial;
}
// Get the graphics pipeline if already created.
angle::Result getGraphicsPipeline(ContextVk *contextVk,
vk::GraphicsPipelineSubset pipelineSubset,
const vk::GraphicsPipelineDesc &desc,
const vk::GraphicsPipelineDesc **descPtrOut,
vk::PipelineHelper **pipelineOut);
angle::Result createGraphicsPipeline(ContextVk *contextVk,
vk::GraphicsPipelineSubset pipelineSubset,
vk::PipelineCacheAccess *pipelineCache,
PipelineSource source,
const vk::GraphicsPipelineDesc &desc,
const vk::GraphicsPipelineDesc **descPtrOut,
vk::PipelineHelper **pipelineOut);
angle::Result createLinkedGraphicsPipeline(ContextVk *contextVk,
vk::PipelineCacheAccess *pipelineCache,
const vk::GraphicsPipelineDesc &desc,
vk::PipelineHelper *shadersPipeline,
const vk::GraphicsPipelineDesc **descPtrOut,
vk::PipelineHelper **pipelineOut);
angle::Result getOrCreateComputePipeline(vk::ErrorContext *context,
vk::PipelineCacheAccess *pipelineCache,
PipelineSource source,
vk::PipelineRobustness pipelineRobustness,
vk::PipelineProtectedAccess pipelineProtectedAccess,
vk::PipelineHelper **pipelineOut);
const vk::PipelineLayout &getPipelineLayout() const { return *mPipelineLayout; }
void resetLayout(ContextVk *contextVk);
angle::Result createPipelineLayout(vk::ErrorContext *context,
PipelineLayoutCache *pipelineLayoutCache,
DescriptorSetLayoutCache *descriptorSetLayoutCache,
gl::ActiveTextureArray<TextureVk *> *activeTextures);
angle::Result initializeDescriptorPools(
vk::ErrorContext *context,
DescriptorSetLayoutCache *descriptorSetLayoutCache,
vk::DescriptorSetArray<vk::MetaDescriptorPool> *metaDescriptorPools);
angle::Result updateTexturesDescriptorSet(vk::Context *context,
uint32_t currentFrame,
const gl::ActiveTextureArray<TextureVk *> &textures,
const gl::SamplerBindingVector &samplers,
PipelineType pipelineType,
UpdateDescriptorSetsBuilder *updateBuilder);
angle::Result updateShaderResourcesDescriptorSet(
vk::Context *context,
uint32_t currentFrame,
UpdateDescriptorSetsBuilder *updateBuilder,
const vk::WriteDescriptorDescs &writeDescriptorDescs,
const vk::DescriptorSetDescBuilder &shaderResourcesDesc,
vk::SharedDescriptorSetCacheKey *newSharedCacheKeyOut);
angle::Result updateUniformsAndXfbDescriptorSet(
vk::Context *context,
uint32_t currentFrame,
UpdateDescriptorSetsBuilder *updateBuilder,
const vk::WriteDescriptorDescs &writeDescriptorDescs,
vk::BufferHelper *defaultUniformBuffer,
vk::DescriptorSetDescBuilder *uniformsAndXfbDesc,
vk::SharedDescriptorSetCacheKey *sharedCacheKeyOut);
template <typename CommandBufferT>
angle::Result bindDescriptorSets(vk::ErrorContext *context,
uint32_t currentFrame,
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;
}
VkDescriptorType getAtomicCounterBufferDescriptorType() 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::ErrorContext *context, gl::ShaderType shaderType) const
{
vk::Renderer *renderer = context->getRenderer();
size_t alignment = static_cast<size_t>(
renderer->getPhysicalDeviceProperties().limits.minUniformBufferOffsetAlignment);
return roundUp(mDefaultUniformBlocks[shaderType]->uniformData.size(), alignment);
}
std::shared_ptr<BufferAndLayout> &getSharedDefaultUniformBlock(gl::ShaderType shaderType)
{
return mDefaultUniformBlocks[shaderType];
}
bool updateAndCheckDirtyUniforms()
{
if (ANGLE_LIKELY(!mExecutable->IsPPO()))
{
return mDefaultUniformBlocksDirty.any();
}
const auto &ppoExecutables = mExecutable->getPPOProgramExecutables();
for (gl::ShaderType shaderType : mExecutable->getLinkedShaderStages())
{
ProgramExecutableVk *executableVk = vk::GetImpl(ppoExecutables[shaderType].get());
if (executableVk->mDefaultUniformBlocksDirty.test(shaderType))
{
mDefaultUniformBlocksDirty.set(shaderType);
// Note: this relies on onProgramBind marking everything as dirty
executableVk->mDefaultUniformBlocksDirty.reset(shaderType);
}
}
return mDefaultUniformBlocksDirty.any();
}
void setAllDefaultUniformsDirty();
angle::Result updateUniforms(vk::Context *context,
uint32_t currentFrame,
UpdateDescriptorSetsBuilder *updateBuilder,
vk::BufferHelper *emptyBuffer,
vk::DynamicBuffer *defaultUniformStorage,
bool isTransformFeedbackActiveUnpaused,
TransformFeedbackVk *transformFeedbackVk);
void onProgramBind();
const ShaderInterfaceVariableInfoMap &getVariableInfoMap() const { return mVariableInfoMap; }
angle::Result warmUpPipelineCache(vk::Renderer *renderer,
vk::PipelineRobustness pipelineRobustness,
vk::PipelineProtectedAccess pipelineProtectedAccess)
{
return getPipelineCacheWarmUpTasks(renderer, pipelineRobustness, pipelineProtectedAccess,
nullptr);
}
angle::Result getPipelineCacheWarmUpTasks(
vk::Renderer *renderer,
vk::PipelineRobustness pipelineRobustness,
vk::PipelineProtectedAccess pipelineProtectedAccess,
std::vector<std::shared_ptr<LinkSubTask>> *postLinkSubTasksOut);
void waitForPostLinkTasks(const gl::Context *context) override
{
ContextVk *contextVk = vk::GetImpl(context);
waitForPostLinkTasksImpl(contextVk);
}
void waitForComputePostLinkTasks(ContextVk *contextVk)
{
ASSERT(mExecutable->hasLinkedShaderStage(gl::ShaderType::Compute));
waitForPostLinkTasksImpl(contextVk);
}
void waitForGraphicsPostLinkTasks(ContextVk *contextVk,
const vk::GraphicsPipelineDesc &currentGraphicsPipelineDesc);
angle::Result mergePipelineCacheToRenderer(vk::ErrorContext *context) const;
const vk::WriteDescriptorDescs &getShaderResourceWriteDescriptorDescs() const
{
return mShaderResourceWriteDescriptorDescs;
}
const vk::WriteDescriptorDescs &getDefaultUniformWriteDescriptorDescs(
TransformFeedbackVk *transformFeedbackVk) const
{
return transformFeedbackVk == nullptr ? mDefaultUniformWriteDescriptorDescs
: mDefaultUniformAndXfbWriteDescriptorDescs;
}
const vk::WriteDescriptorDescs &getTextureWriteDescriptorDescs() const
{
return mTextureWriteDescriptorDescs;
}
// The following functions are for internal use of programs, including from a threaded link job:
angle::Result resizeUniformBlockMemory(vk::ErrorContext *context,
const gl::ShaderMap<size_t> &requiredBufferSize);
void resolvePrecisionMismatch(const gl::ProgramMergedVaryings &mergedVaryings);
angle::Result initShaders(vk::ErrorContext *context,
const gl::ShaderBitSet &linkedShaderStages,
const gl::ShaderMap<const angle::spirv::Blob *> &spirvBlobs,
bool isGLES1)
{
return mOriginalShaderInfo.initShaders(context, linkedShaderStages, spirvBlobs,
mVariableInfoMap, isGLES1);
}
void assignAllSpvLocations(vk::ErrorContext *context,
const gl::ProgramState &programState,
const gl::ProgramLinkedResources &resources)
{
SpvSourceOptions options = SpvCreateSourceOptions(
context->getFeatures(), context->getRenderer()->getMaxColorInputAttachmentCount());
SpvAssignAllLocations(options, programState, resources, &mVariableInfoMap);
}
private:
class WarmUpTaskCommon;
class WarmUpComputeTask;
class WarmUpGraphicsTask;
friend class ProgramVk;
friend class ProgramPipelineVk;
void reset(ContextVk *contextVk);
void addInterfaceBlockDescriptorSetDesc(const std::vector<gl::InterfaceBlock> &blocks,
gl::ShaderBitSet shaderTypes,
VkDescriptorType descType,
vk::DescriptorSetLayoutDesc *descOut);
void addAtomicCounterBufferDescriptorSetDesc(
const std::vector<gl::AtomicCounterBuffer> &atomicCounterBuffers,
vk::DescriptorSetLayoutDesc *descOut);
void addImageDescriptorSetDesc(vk::DescriptorSetLayoutDesc *descOut);
void addInputAttachmentDescriptorSetDesc(vk::ErrorContext *context,
vk::DescriptorSetLayoutDesc *descOut);
angle::Result addTextureDescriptorSetDesc(
vk::ErrorContext *context,
const gl::ActiveTextureArray<TextureVk *> *activeTextures,
vk::DescriptorSetLayoutDesc *descOut);
size_t calcUniformUpdateRequiredSpace(vk::ErrorContext *context,
gl::ShaderMap<VkDeviceSize> *uniformOffsets) const;
ANGLE_INLINE angle::Result initProgram(vk::ErrorContext *context,
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(context, shaderType, isLastPreFragmentStage,
isTransformFeedbackProgram, mOriginalShaderInfo,
optionBits, variableInfoMap));
}
ASSERT(programInfo->valid(shaderType));
return angle::Result::Continue;
}
ANGLE_INLINE angle::Result initGraphicsShaderProgram(
vk::ErrorContext *context,
gl::ShaderType shaderType,
bool isLastPreFragmentStage,
bool isTransformFeedbackProgram,
ProgramTransformOptions optionBits,
ProgramInfo *programInfo,
const ShaderInterfaceVariableInfoMap &variableInfoMap)
{
mValidGraphicsPermutations.set(optionBits.permutationIndex);
return initProgram(context, shaderType, isLastPreFragmentStage, isTransformFeedbackProgram,
optionBits, programInfo, variableInfoMap);
}
ANGLE_INLINE angle::Result initComputeProgram(
vk::ErrorContext *context,
ProgramInfo *programInfo,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
const vk::ComputePipelineOptions &pipelineOptions)
{
mValidComputePermutations.set(pipelineOptions.permutationIndex);
ProgramTransformOptions optionBits = {};
return initProgram(context, gl::ShaderType::Compute, false, false, optionBits, programInfo,
variableInfoMap);
}
ProgramTransformOptions getTransformOptions(ContextVk *contextVk,
const vk::GraphicsPipelineDesc &desc);
angle::Result initGraphicsShaderPrograms(vk::ErrorContext *context,
ProgramTransformOptions transformOptions);
angle::Result initProgramThenCreateGraphicsPipeline(vk::ErrorContext *context,
ProgramTransformOptions transformOptions,
vk::GraphicsPipelineSubset pipelineSubset,
vk::PipelineCacheAccess *pipelineCache,
PipelineSource source,
const vk::GraphicsPipelineDesc &desc,
const vk::RenderPass &compatibleRenderPass,
const vk::GraphicsPipelineDesc **descPtrOut,
vk::PipelineHelper **pipelineOut);
angle::Result createGraphicsPipelineImpl(vk::ErrorContext *context,
ProgramTransformOptions transformOptions,
vk::GraphicsPipelineSubset pipelineSubset,
vk::PipelineCacheAccess *pipelineCache,
PipelineSource source,
const vk::GraphicsPipelineDesc &desc,
const vk::RenderPass &compatibleRenderPass,
const vk::GraphicsPipelineDesc **descPtrOut,
vk::PipelineHelper **pipelineOut);
angle::Result prepareForWarmUpPipelineCache(
vk::ErrorContext *context,
vk::PipelineRobustness pipelineRobustness,
vk::PipelineProtectedAccess pipelineProtectedAccess,
vk::GraphicsPipelineSubset subset,
bool *isComputeOut,
angle::FixedVector<bool, 2> *surfaceRotationVariationsOut,
vk::GraphicsPipelineDesc **graphicsPipelineDescOut,
vk::RenderPass *renderPassOut);
angle::Result warmUpComputePipelineCache(vk::ErrorContext *context,
vk::PipelineRobustness pipelineRobustness,
vk::PipelineProtectedAccess pipelineProtectedAccess);
angle::Result warmUpGraphicsPipelineCache(vk::ErrorContext *context,
vk::PipelineRobustness pipelineRobustness,
vk::PipelineProtectedAccess pipelineProtectedAccess,
vk::GraphicsPipelineSubset subset,
const bool isSurfaceRotated,
const vk::GraphicsPipelineDesc &graphicsPipelineDesc,
const vk::RenderPass &renderPass,
vk::PipelineHelper *placeholderPipelineHelper);
void waitForPostLinkTasksImpl(ContextVk *contextVk);
angle::Result getOrAllocateDescriptorSet(vk::Context *context,
uint32_t currentFrame,
UpdateDescriptorSetsBuilder *updateBuilder,
const vk::DescriptorSetDescBuilder &descriptorSetDesc,
const vk::WriteDescriptorDescs &writeDescriptorDescs,
DescriptorSetIndex setIndex,
vk::SharedDescriptorSetCacheKey *newSharedCacheKeyOut);
// When loading from cache / binary, initialize the pipeline cache with given data. Otherwise
// the cache is lazily created as needed.
angle::Result initializePipelineCache(vk::ErrorContext *context,
bool compressed,
const std::vector<uint8_t> &pipelineData);
angle::Result ensurePipelineCacheInitialized(vk::ErrorContext *context);
void initializeWriteDescriptorDesc(vk::ErrorContext *context);
// Descriptor sets and pools for shader resources for this program.
vk::DescriptorSetArray<vk::DescriptorSetPointer> mDescriptorSets;
vk::DescriptorSetArray<vk::DynamicDescriptorPoolPointer> mDynamicDescriptorPools;
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::PipelineLayoutPtr 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;
static_assert((ProgramTransformOptions::kPermutationCount == 32),
"ProgramTransformOptions::kPermutationCount must be 32.");
angle::BitSet32<ProgramTransformOptions::kPermutationCount> mValidGraphicsPermutations;
static_assert((vk::ComputePipelineOptions::kPermutationCount == 4),
"ComputePipelineOptions::kPermutationCount must be 4.");
angle::BitSet8<vk::ComputePipelineOptions::kPermutationCount> mValidComputePermutations;
// 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;
// Pipeline caches. The pipelines are tightly coupled with the shaders they are created for, so
// they live in the program executable. With VK_EXT_graphics_pipeline_library, the pipeline is
// divided in subsets; the "shaders" subset is created based on the shaders, so its cache lives
// in the program executable. The "vertex input" and "fragment output" pipelines are
// independent, and live in the context.
CompleteGraphicsPipelineCache
mCompleteGraphicsPipelines[ProgramTransformOptions::kPermutationCount];
ShadersGraphicsPipelineCache
mShadersGraphicsPipelines[ProgramTransformOptions::kPermutationCount];
ComputePipelineCache mComputePipelines;
DefaultUniformBlockMap mDefaultUniformBlocks;
gl::ShaderBitSet mDefaultUniformBlocksDirty;
ShaderInfo mOriginalShaderInfo;
// The pipeline cache specific to this program executable. Currently:
//
// - This is used during warm up (at link time)
// - The contents are merged to Renderer's pipeline cache immediately after warm up
// - The contents are returned as part of program binary
// - Draw-time pipeline creation uses Renderer's cache
//
// Without VK_EXT_graphics_pipeline_library, this cache is not used for draw-time pipeline
// creations to allow reuse of other blobs that are independent of the actual shaders; vertex
// input fetch, fragment output and blend.
//
// With VK_EXT_graphics_pipeline_library, this cache is used for the "shaders" subset of the
// pipeline.
vk::PipelineCache mPipelineCache;
vk::GraphicsPipelineDesc mWarmUpGraphicsPipelineDesc;
// The "layout" information for descriptorSets
vk::WriteDescriptorDescs mShaderResourceWriteDescriptorDescs;
vk::WriteDescriptorDescs mTextureWriteDescriptorDescs;
vk::WriteDescriptorDescs mDefaultUniformWriteDescriptorDescs;
vk::WriteDescriptorDescs mDefaultUniformAndXfbWriteDescriptorDescs;
vk::DescriptorSetLayoutDesc mShaderResourceSetDesc;
vk::DescriptorSetLayoutDesc mTextureSetDesc;
vk::DescriptorSetLayoutDesc mDefaultUniformAndXfbSetDesc;
};
} // namespace rx
#endif // LIBANGLE_RENDERER_VULKAN_PROGRAMEXECUTABLEVK_H_