Commit abaa3573 by Tim Van Patten Committed by Commit Bot

Vulkan: Only calculate variable locations once

Variable locations and descriptor set/binding values are calculated multiple times: - Compiling GLSL->SPIR-V - Creating the Vulkan pipeline layout - Updating descriptor sets These values should instead be calculated once and reused throughout since they won't change without recompiling the shader program. Bug: angleproject:3570 Change-Id: I5d8767b3b2e2f741aade7fec9991eea53ee2eb98 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2067101 Commit-Queue: Tim Van Patten <timvp@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 10ade024
......@@ -55,13 +55,6 @@ constexpr char kXfbDeclMarker[] = "@@ XFB-DECL @@";
constexpr char kXfbOutMarker[] = "@@ XFB-OUT @@;";
constexpr char kXfbBuiltInPrefix[] = "xfbANGLE";
constexpr gl::ShaderMap<const char *> kDefaultUniformNames = {
{gl::ShaderType::Vertex, sh::vk::kDefaultUniformsNameVS},
{gl::ShaderType::Geometry, sh::vk::kDefaultUniformsNameGS},
{gl::ShaderType::Fragment, sh::vk::kDefaultUniformsNameFS},
{gl::ShaderType::Compute, sh::vk::kDefaultUniformsNameCS},
};
template <size_t N>
constexpr size_t ConstStrLen(const char (&)[N])
{
......@@ -146,49 +139,11 @@ bool UniformNameIsIndexZero(const std::string &name, bool excludeCheckForOwningS
return true;
}
// Strip indices from the name. If there are non-zero indices, return false to indicate that this
// image uniform doesn't require set/binding. That is done on index 0.
bool GetImageNameWithoutIndices(std::string *name)
{
if (name->back() != ']')
{
return true;
}
if (!UniformNameIsIndexZero(*name, false))
{
return false;
}
// Strip all indices
*name = name->substr(0, name->find('['));
return true;
}
bool MappedSamplerNameNeedsUserDefinedPrefix(const std::string &originalName)
{
return originalName.find('.') == std::string::npos;
}
std::string GetMappedSamplerNameOld(const std::string &originalName)
{
std::string samplerName = gl::ParseResourceName(originalName, nullptr);
// Samplers in structs are extracted.
std::replace(samplerName.begin(), samplerName.end(), '.', '_');
// Samplers in arrays of structs are also extracted.
std::replace(samplerName.begin(), samplerName.end(), '[', '_');
samplerName.erase(std::remove(samplerName.begin(), samplerName.end(), ']'), samplerName.end());
if (MappedSamplerNameNeedsUserDefinedPrefix(originalName))
{
samplerName = sh::kUserDefinedNamePrefix + samplerName;
}
return samplerName;
}
template <typename OutputIter, typename ImplicitIter>
uint32_t CountExplicitOutputs(OutputIter outputsBegin,
OutputIter outputsEnd,
......@@ -217,10 +172,10 @@ ShaderInterfaceVariableInfo *GetShaderInterfaceVariable(ShaderInterfaceVariableI
return &(*infoMap)[varName];
}
ShaderInterfaceVariableInfo *AddResourceInfo(ShaderInterfaceVariableInfoMap *infoMap,
const std::string &varName,
uint32_t descriptorSet,
uint32_t binding)
ShaderInterfaceVariableInfo *AddResourceInfoToAllStages(ShaderInterfaceVariableInfoMap *infoMap,
const std::string &varName,
uint32_t descriptorSet,
uint32_t binding)
{
gl::ShaderBitSet allStages;
allStages.set();
......@@ -232,6 +187,22 @@ ShaderInterfaceVariableInfo *AddResourceInfo(ShaderInterfaceVariableInfoMap *inf
return info;
}
ShaderInterfaceVariableInfo *AddResourceInfo(ShaderInterfaceVariableInfoMap *infoMap,
const std::string &varName,
uint32_t descriptorSet,
uint32_t binding,
const gl::ShaderType shaderType)
{
gl::ShaderBitSet stages;
stages.set(shaderType);
ShaderInterfaceVariableInfo *info = AddShaderInterfaceVariable(infoMap, varName);
info->descriptorSet = descriptorSet;
info->binding = binding;
info->activeStages = stages;
return info;
}
// Add location information for an in/out variable.
ShaderInterfaceVariableInfo *AddLocationInfo(ShaderInterfaceVariableInfoMap *infoMap,
const std::string &varName,
......@@ -356,7 +327,7 @@ std::string GenerateTransformFeedbackVaryingOutput(const gl::TransformFeedbackVa
return result.str();
}
void GenerateTransformFeedbackEmulationOutputs(const GlslangSourceOptions &options,
void GenerateTransformFeedbackEmulationOutputs(GlslangSourceOptions &options,
const gl::ProgramState &programState,
GlslangProgramInterfaceInfo *programInterfaceInfo,
std::string *vertexShader,
......@@ -376,20 +347,20 @@ void GenerateTransformFeedbackEmulationOutputs(const GlslangSourceOptions &optio
for (uint32_t bufferIndex = 0; bufferIndex < bufferCount; ++bufferIndex)
{
const std::string xfbBinding =
Str(programInterfaceInfo->xfbBindingIndexStart + bufferIndex);
xfbIndices[bufferIndex] = Str(bufferIndex);
const std::string xfbBinding = Str(programInterfaceInfo->currentUniformBindingIndex);
xfbIndices[bufferIndex] = Str(bufferIndex);
std::string bufferName = "xfbBuffer" + xfbIndices[bufferIndex];
std::string bufferName = GetXfbBufferName(bufferIndex);
xfbDecl += "layout(set = " + xfbSet + ", binding = " + xfbBinding + ") buffer " +
bufferName + " { float xfbOut" + xfbIndices[bufferIndex] + "[]; };\n";
bufferName + " { float xfbOut" + Str(bufferIndex) + "[]; };\n";
// Add this entry to the info map, so we can easily assert that every resource has an entry
// in this map.
AddResourceInfo(variableInfoMapOut, bufferName,
programInterfaceInfo->uniformsAndXfbDescriptorSetIndex,
programInterfaceInfo->xfbBindingIndexStart + bufferIndex);
programInterfaceInfo->currentUniformBindingIndex, gl::ShaderType::Vertex);
++programInterfaceInfo->currentUniformBindingIndex;
}
std::string xfbOut =
......@@ -733,74 +704,77 @@ void AssignTransformFeedbackExtensionQualifiers(const gl::ProgramState &programS
}
}
void AssignUniformBindings(const GlslangSourceOptions &options,
gl::ShaderMap<std::string> *shaderSources,
void AssignUniformBindings(GlslangSourceOptions &options,
const gl::ProgramState &programState,
GlslangProgramInterfaceInfo *programInterfaceInfo,
ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
// Assign binding to the default uniforms block of each shader stage.
uint32_t bindingIndex = 0;
for (const gl::ShaderType shaderType : gl::AllShaderTypes())
{
const std::string &shaderSource = (*shaderSources)[shaderType];
if (!shaderSource.empty())
if (programState.getAttachedShader(shaderType) ||
programState.getProgramExecutable().hasLinkedShaderStage(shaderType))
{
AddResourceInfo(variableInfoMapOut, kDefaultUniformNames[shaderType],
programInterfaceInfo->uniformsAndXfbDescriptorSetIndex, bindingIndex);
++bindingIndex;
programInterfaceInfo->uniformsAndXfbDescriptorSetIndex,
programInterfaceInfo->currentUniformBindingIndex, shaderType);
++programInterfaceInfo->currentUniformBindingIndex;
}
}
// Assign binding to the driver uniforms block
AddResourceInfo(variableInfoMapOut, sh::vk::kDriverUniformsBlockName,
programInterfaceInfo->driverUniformsDescriptorSetIndex, 0);
AddResourceInfoToAllStages(variableInfoMapOut, sh::vk::kDriverUniformsBlockName,
programInterfaceInfo->driverUniformsDescriptorSetIndex, 0);
}
uint32_t AssignInterfaceBlockBindings(const GlslangSourceOptions &options,
const std::vector<gl::InterfaceBlock> &blocks,
uint32_t bindingStart,
GlslangProgramInterfaceInfo *programInterfaceInfo,
ShaderInterfaceVariableInfoMap *variableInfoMapOut)
// TODO: http://anglebug.com/4512: Need to combine descriptor set bindings across
// shader stages.
void AssignInterfaceBlockBindings(GlslangSourceOptions &options,
const std::vector<gl::InterfaceBlock> &blocks,
GlslangProgramInterfaceInfo *programInterfaceInfo,
ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
uint32_t bindingIndex = bindingStart;
for (const gl::InterfaceBlock &block : blocks)
{
if (!block.isArray || block.arrayElement == 0)
{
AddResourceInfo(variableInfoMapOut, block.mappedName,
programInterfaceInfo->shaderResourceDescriptorSetIndex, bindingIndex);
++bindingIndex;
// TODO: http://anglebug.com/4523: All blocks should be active
if (block.activeShaders().any())
{
AddResourceInfoToAllStages(variableInfoMapOut, block.mappedName,
programInterfaceInfo->shaderResourceDescriptorSetIndex,
programInterfaceInfo->currentShaderResourceBindingIndex);
++programInterfaceInfo->currentShaderResourceBindingIndex;
}
}
}
return bindingIndex;
}
uint32_t AssignAtomicCounterBufferBindings(const GlslangSourceOptions &options,
const std::vector<gl::AtomicCounterBuffer> &buffers,
uint32_t bindingStart,
GlslangProgramInterfaceInfo *programInterfaceInfo,
ShaderInterfaceVariableInfoMap *variableInfoMapOut)
// TODO: http://anglebug.com/4512: Need to combine descriptor set bindings across
// shader stages.
void AssignAtomicCounterBufferBindings(GlslangSourceOptions &options,
const std::vector<gl::AtomicCounterBuffer> &buffers,
GlslangProgramInterfaceInfo *programInterfaceInfo,
ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
if (buffers.size() == 0)
{
return bindingStart;
return;
}
AddResourceInfo(variableInfoMapOut, sh::vk::kAtomicCountersBlockName,
programInterfaceInfo->shaderResourceDescriptorSetIndex, bindingStart);
return bindingStart + 1;
AddResourceInfoToAllStages(variableInfoMapOut, sh::vk::kAtomicCountersBlockName,
programInterfaceInfo->shaderResourceDescriptorSetIndex,
programInterfaceInfo->currentShaderResourceBindingIndex);
++programInterfaceInfo->currentShaderResourceBindingIndex;
}
uint32_t AssignImageBindings(const GlslangSourceOptions &options,
const std::vector<gl::LinkedUniform> &uniforms,
const gl::RangeUI &imageUniformRange,
uint32_t bindingStart,
GlslangProgramInterfaceInfo *programInterfaceInfo,
ShaderInterfaceVariableInfoMap *variableInfoMapOut)
// TODO: http://anglebug.com/4512: Need to combine descriptor set bindings across
// shader stages.
void AssignImageBindings(GlslangSourceOptions &options,
const std::vector<gl::LinkedUniform> &uniforms,
const gl::RangeUI &imageUniformRange,
GlslangProgramInterfaceInfo *programInterfaceInfo,
ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
uint32_t bindingIndex = bindingStart;
for (unsigned int uniformIndex : imageUniformRange)
{
const gl::LinkedUniform &imageUniform = uniforms[uniformIndex];
......@@ -808,48 +782,44 @@ uint32_t AssignImageBindings(const GlslangSourceOptions &options,
std::string name = imageUniform.mappedName;
if (GetImageNameWithoutIndices(&name))
{
AddResourceInfo(variableInfoMapOut, name,
programInterfaceInfo->shaderResourceDescriptorSetIndex, bindingIndex);
AddResourceInfoToAllStages(variableInfoMapOut, name,
programInterfaceInfo->shaderResourceDescriptorSetIndex,
programInterfaceInfo->currentShaderResourceBindingIndex);
++programInterfaceInfo->currentShaderResourceBindingIndex;
}
++bindingIndex;
}
return bindingIndex;
}
void AssignNonTextureBindings(const GlslangSourceOptions &options,
void AssignNonTextureBindings(GlslangSourceOptions &options,
const gl::ProgramState &programState,
GlslangProgramInterfaceInfo *programInterfaceInfo,
ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
uint32_t bindingStart = 0;
const std::vector<gl::InterfaceBlock> &uniformBlocks = programState.getUniformBlocks();
bindingStart = AssignInterfaceBlockBindings(options, uniformBlocks, bindingStart,
programInterfaceInfo, variableInfoMapOut);
AssignInterfaceBlockBindings(options, uniformBlocks, programInterfaceInfo, variableInfoMapOut);
const std::vector<gl::InterfaceBlock> &storageBlocks = programState.getShaderStorageBlocks();
bindingStart = AssignInterfaceBlockBindings(options, storageBlocks, bindingStart,
programInterfaceInfo, variableInfoMapOut);
AssignInterfaceBlockBindings(options, storageBlocks, programInterfaceInfo, variableInfoMapOut);
const std::vector<gl::AtomicCounterBuffer> &atomicCounterBuffers =
programState.getAtomicCounterBuffers();
bindingStart = AssignAtomicCounterBufferBindings(options, atomicCounterBuffers, bindingStart,
programInterfaceInfo, variableInfoMapOut);
AssignAtomicCounterBufferBindings(options, atomicCounterBuffers, programInterfaceInfo,
variableInfoMapOut);
const std::vector<gl::LinkedUniform> &uniforms = programState.getUniforms();
const gl::RangeUI &imageUniformRange = programState.getImageUniformRange();
bindingStart = AssignImageBindings(options, uniforms, imageUniformRange, bindingStart,
programInterfaceInfo, variableInfoMapOut);
AssignImageBindings(options, uniforms, imageUniformRange, programInterfaceInfo,
variableInfoMapOut);
}
void AssignTextureBindings(const GlslangSourceOptions &options,
// TODO: http://anglebug.com/4512: Need to combine descriptor set bindings across
// shader stages.
void AssignTextureBindings(GlslangSourceOptions &options,
const gl::ProgramState &programState,
GlslangProgramInterfaceInfo *programInterfaceInfo,
ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
// Assign textures to a descriptor set and binding.
uint32_t bindingIndex = 0;
const std::vector<gl::LinkedUniform> &uniforms = programState.getUniforms();
for (unsigned int uniformIndex : programState.getSamplerUniformRange())
......@@ -869,11 +839,15 @@ void AssignTextureBindings(const GlslangSourceOptions &options,
? GetMappedSamplerNameOld(samplerUniform.name)
: GlslangGetMappedSamplerName(samplerUniform.name);
AddResourceInfo(variableInfoMapOut, samplerName,
programInterfaceInfo->textureDescriptorSetIndex, bindingIndex);
// TODO: http://anglebug.com/4523: All uniforms should be active
if (samplerUniform.activeShaders().any())
{
AddResourceInfoToAllStages(variableInfoMapOut, samplerName,
programInterfaceInfo->textureDescriptorSetIndex,
programInterfaceInfo->currentTextureBindingIndex);
++programInterfaceInfo->currentTextureBindingIndex;
}
}
++bindingIndex;
}
}
......@@ -1755,6 +1729,44 @@ void GlslangRelease()
ASSERT(result != 0);
}
// Strip indices from the name. If there are non-zero indices, return false to indicate that this
// image uniform doesn't require set/binding. That is done on index 0.
bool GetImageNameWithoutIndices(std::string *name)
{
if (name->back() != ']')
{
return true;
}
if (!UniformNameIsIndexZero(*name, false))
{
return false;
}
// Strip all indices
*name = name->substr(0, name->find('['));
return true;
}
std::string GetMappedSamplerNameOld(const std::string &originalName)
{
std::string samplerName = gl::ParseResourceName(originalName, nullptr);
// Samplers in structs are extracted.
std::replace(samplerName.begin(), samplerName.end(), '.', '_');
// Samplers in arrays of structs are also extracted.
std::replace(samplerName.begin(), samplerName.end(), '[', '_');
samplerName.erase(std::remove(samplerName.begin(), samplerName.end(), ']'), samplerName.end());
if (MappedSamplerNameNeedsUserDefinedPrefix(originalName))
{
samplerName = sh::kUserDefinedNamePrefix + samplerName;
}
return samplerName;
}
std::string GlslangGetMappedSamplerName(const std::string &originalName)
{
std::string samplerName = originalName;
......@@ -1790,24 +1802,25 @@ std::string GlslangGetMappedSamplerName(const std::string &originalName)
return samplerName;
}
void GlslangGetShaderSource(const GlslangSourceOptions &options,
std::string GetXfbBufferName(const uint32_t bufferIndex)
{
return "xfbBuffer" + Str(bufferIndex);
}
void GlslangGetShaderSource(GlslangSourceOptions &options,
const gl::ProgramState &programState,
const gl::ProgramLinkedResources &resources,
GlslangProgramInterfaceInfo *programInterfaceInfo,
gl::ShaderMap<std::string> *shaderSourcesOut,
ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
variableInfoMapOut->clear();
for (const gl::ShaderType shaderType : gl::AllShaderTypes())
{
gl::Shader *glShader = programState.getAttachedShader(shaderType);
(*shaderSourcesOut)[shaderType] = glShader ? glShader->getTranslatedSource() : "";
}
std::string *vertexSource = &(*shaderSourcesOut)[gl::ShaderType::Vertex];
const std::string &fragmentSource = (*shaderSourcesOut)[gl::ShaderType::Fragment];
const std::string &computeSource = (*shaderSourcesOut)[gl::ShaderType::Compute];
std::string *vertexSource = &(*shaderSourcesOut)[gl::ShaderType::Vertex];
// Write transform feedback output code.
if (!vertexSource->empty())
......@@ -1832,19 +1845,24 @@ void GlslangGetShaderSource(const GlslangSourceOptions &options,
}
}
const gl::ProgramExecutable &executable = programState.getProgramExecutable();
// Assign outputs to the fragment shader, if any.
if (!fragmentSource.empty())
if (programState.getAttachedShader(gl::ShaderType::Fragment) ||
executable.hasLinkedShaderStage(gl::ShaderType::Fragment))
{
AssignOutputLocations(programState, variableInfoMapOut);
}
// Assign attributes to the vertex shader, if any.
if (!vertexSource->empty())
if (programState.getAttachedShader(gl::ShaderType::Vertex) ||
executable.hasLinkedShaderStage(gl::ShaderType::Vertex))
{
AssignAttributeLocations(programState, variableInfoMapOut);
}
if (computeSource.empty())
if (!programState.getAttachedShader(gl::ShaderType::Compute) &&
!executable.hasLinkedShaderStage(gl::ShaderType::Compute))
{
// Assign varying locations.
AssignVaryingLocations(options, programState, resources, programInterfaceInfo,
......@@ -1859,18 +1877,18 @@ void GlslangGetShaderSource(const GlslangSourceOptions &options,
}
}
AssignUniformBindings(options, shaderSourcesOut, programInterfaceInfo, variableInfoMapOut);
AssignUniformBindings(options, programState, programInterfaceInfo, variableInfoMapOut);
AssignTextureBindings(options, programState, programInterfaceInfo, variableInfoMapOut);
AssignNonTextureBindings(options, programState, programInterfaceInfo, variableInfoMapOut);
}
angle::Result GlslangGetShaderSpirvCode(GlslangErrorCallback callback,
angle::Result GlslangGetShaderSpirvCode(const GlslangErrorCallback &callback,
const gl::Caps &glCaps,
const gl::ShaderMap<std::string> &shaderSources,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
gl::ShaderMap<SpirvBlob> *spirvBlobsOut)
{
gl::ShaderMap<std::vector<uint32_t>> initialSpirvBlobs;
gl::ShaderMap<SpirvBlob> initialSpirvBlobs;
ANGLE_TRY(GetShaderSpirvCode(callback, glCaps, shaderSources, &initialSpirvBlobs));
// Transform the SPIR-V code by assigning location/set/binding values.
......
......@@ -21,20 +21,27 @@ enum class GlslangError
InvalidSpirv,
};
constexpr gl::ShaderMap<const char *> kDefaultUniformNames = {
{gl::ShaderType::Vertex, sh::vk::kDefaultUniformsNameVS},
{gl::ShaderType::Geometry, sh::vk::kDefaultUniformsNameGS},
{gl::ShaderType::Fragment, sh::vk::kDefaultUniformsNameFS},
{gl::ShaderType::Compute, sh::vk::kDefaultUniformsNameCS},
};
struct GlslangProgramInterfaceInfo
{
// Uniforms set index:
uint32_t uniformsAndXfbDescriptorSetIndex;
uint32_t currentUniformBindingIndex;
// Textures set index:
uint32_t textureDescriptorSetIndex;
uint32_t currentTextureBindingIndex;
// Other shader resources set index:
uint32_t shaderResourceDescriptorSetIndex;
uint32_t currentShaderResourceBindingIndex;
// ANGLE driver uniforms set index:
uint32_t driverUniformsDescriptorSetIndex;
// Binding index start for transform feedback buffers:
uint32_t xfbBindingIndexStart;
uint32_t locationsUsedForXfbExtension;
};
......@@ -76,26 +83,32 @@ struct ShaderInterfaceVariableInfo
uint32_t xfbStride = kInvalid;
};
// TODO: http://anglebug.com/4524: Need a different hash key than a string, since
// that's slow to calculate.
using ShaderInterfaceVariableInfoMap = std::unordered_map<std::string, ShaderInterfaceVariableInfo>;
void GlslangInitialize();
void GlslangRelease();
bool GetImageNameWithoutIndices(std::string *name);
// Get the mapped sampler name after the soure is transformed by GlslangGetShaderSource()
std::string GetMappedSamplerNameOld(const std::string &originalName);
std::string GlslangGetMappedSamplerName(const std::string &originalName);
std::string GetXfbBufferName(const uint32_t bufferIndex);
// Transform the source to include actual binding points for various shader resources (textures,
// buffers, xfb, etc). For some variables, these values are instead output to the variableInfoMap
// to be set during a SPIR-V transformation. This is a transitory step towards moving all variables
// to this map, at which point GlslangGetShaderSpirvCode will also be called by this function.
void GlslangGetShaderSource(const GlslangSourceOptions &options,
void GlslangGetShaderSource(GlslangSourceOptions &options,
const gl::ProgramState &programState,
const gl::ProgramLinkedResources &resources,
GlslangProgramInterfaceInfo *programInterfaceInfo,
gl::ShaderMap<std::string> *shaderSourcesOut,
ShaderInterfaceVariableInfoMap *variableInfoMapOut);
angle::Result GlslangGetShaderSpirvCode(GlslangErrorCallback callback,
angle::Result GlslangGetShaderSpirvCode(const GlslangErrorCallback &callback,
const gl::Caps &glCaps,
const gl::ShaderMap<std::string> &shaderSources,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
......
......@@ -27,12 +27,14 @@ void ResetGlslangProgramInterfaceInfo(GlslangProgramInterfaceInfo *programInterf
// We don't actually use descriptor set for now, the actual binding will be done inside
// ProgramMtl using spirv-cross.
programInterfaceInfo->uniformsAndXfbDescriptorSetIndex = kDefaultUniformsBindingIndex;
programInterfaceInfo->currentUniformBindingIndex = 0;
programInterfaceInfo->textureDescriptorSetIndex = 0;
programInterfaceInfo->currentTextureBindingIndex = 0;
programInterfaceInfo->driverUniformsDescriptorSetIndex = kDriverUniformsBindingIndex;
// NOTE(hqle): Unused for now, until we support ES 3.0
programInterfaceInfo->shaderResourceDescriptorSetIndex = -1;
programInterfaceInfo->xfbBindingIndexStart = -1;
programInterfaceInfo->locationsUsedForXfbExtension = 0;
programInterfaceInfo->shaderResourceDescriptorSetIndex = -1;
programInterfaceInfo->currentShaderResourceBindingIndex = 0;
programInterfaceInfo->locationsUsedForXfbExtension = 0;
static_assert(kDefaultUniformsBindingIndex != 0, "kDefaultUniformsBindingIndex must not be 0");
static_assert(kDriverUniformsBindingIndex != 0, "kDriverUniformsBindingIndex must not be 0");
......@@ -50,11 +52,12 @@ void GlslangGetShaderSource(const gl::ProgramState &programState,
gl::ShaderMap<std::string> *shaderSourcesOut,
ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
GlslangSourceOptions options = CreateSourceOptions();
GlslangProgramInterfaceInfo programInterfaceInfo;
ResetGlslangProgramInterfaceInfo(&programInterfaceInfo);
rx::GlslangGetShaderSource(CreateSourceOptions(), programState, resources,
&programInterfaceInfo, shaderSourcesOut, variableInfoMapOut);
rx::GlslangGetShaderSource(options, programState, resources, &programInterfaceInfo,
shaderSourcesOut, variableInfoMapOut);
}
angle::Result GlslangGetShaderSpirvCode(ErrorHandler *context,
......
......@@ -598,6 +598,7 @@ ContextVk::ContextVk(const gl::State &state, gl::ErrorSet *errorSet, RendererVk
mVertexArray(nullptr),
mDrawFramebuffer(nullptr),
mProgram(nullptr),
mExecutable(nullptr),
mLastIndexBufferOffset(0),
mCurrentDrawElementsType(gl::DrawElementsType::InvalidEnum),
mXfbBaseVertex(0),
......@@ -2792,7 +2793,8 @@ angle::Result ContextVk::syncState(const gl::Context *context,
case gl::State::DIRTY_BIT_DISPATCH_INDIRECT_BUFFER_BINDING:
break;
case gl::State::DIRTY_BIT_PROGRAM_BINDING:
mProgram = vk::GetImpl(glState.getProgram());
mProgram = vk::GetImpl(glState.getProgram());
mExecutable = &mProgram->getExecutable();
break;
case gl::State::DIRTY_BIT_PROGRAM_EXECUTABLE:
{
......
......@@ -26,6 +26,7 @@ struct FeaturesVk;
namespace rx
{
class ProgramExecutableVk;
class RendererVk;
class WindowSurfaceVk;
......@@ -657,6 +658,11 @@ class ContextVk : public ContextImpl, public vk::Context
size_t getVkIndexTypeSize(gl::DrawElementsType glIndexType) const;
bool shouldConvertUint8VkIndexType(gl::DrawElementsType glIndexType) const;
const ProgramExecutableVk *getExecutable() const { return mExecutable; }
ProgramExecutableVk *getExecutable() { return mExecutable; }
ProgramVk *getShaderProgram(const gl::State &glState, gl::ShaderType shaderType) const;
private:
// Dirty bits.
enum DirtyBitType : size_t
......@@ -950,6 +956,7 @@ class ContextVk : public ContextImpl, public vk::Context
VertexArrayVk *mVertexArray;
FramebufferVk *mDrawFramebuffer;
ProgramVk *mProgram;
ProgramExecutableVk *mExecutable;
// Graph resource used to record dispatch commands and hold resource dependencies.
vk::DispatchHelper mDispatcher;
......
......@@ -21,7 +21,10 @@ angle::Result ErrorHandler(vk::Context *context, GlslangError)
return angle::Result::Stop;
}
GlslangSourceOptions CreateSourceOptions(const angle::FeaturesVk &features)
} // namespace
// static
GlslangSourceOptions GlslangWrapperVk::CreateSourceOptions(const angle::FeaturesVk &features)
{
GlslangSourceOptions options;
......@@ -33,7 +36,6 @@ GlslangSourceOptions CreateSourceOptions(const angle::FeaturesVk &features)
return options;
}
} // namespace
// static
void GlslangWrapperVk::ResetGlslangProgramInterfaceInfo(
......@@ -41,12 +43,14 @@ void GlslangWrapperVk::ResetGlslangProgramInterfaceInfo(
{
glslangProgramInterfaceInfo->uniformsAndXfbDescriptorSetIndex =
kUniformsAndXfbDescriptorSetIndex;
glslangProgramInterfaceInfo->textureDescriptorSetIndex = kTextureDescriptorSetIndex;
glslangProgramInterfaceInfo->currentUniformBindingIndex = 0;
glslangProgramInterfaceInfo->textureDescriptorSetIndex = kTextureDescriptorSetIndex;
glslangProgramInterfaceInfo->currentTextureBindingIndex = 0;
glslangProgramInterfaceInfo->shaderResourceDescriptorSetIndex =
kShaderResourceDescriptorSetIndex;
glslangProgramInterfaceInfo->currentShaderResourceBindingIndex = 0;
glslangProgramInterfaceInfo->driverUniformsDescriptorSetIndex =
kDriverUniformsDescriptorSetIndex;
glslangProgramInterfaceInfo->xfbBindingIndexStart = kXfbBindingIndexStart;
glslangProgramInterfaceInfo->locationsUsedForXfbExtension = 0;
}
......@@ -59,8 +63,9 @@ void GlslangWrapperVk::GetShaderSource(const angle::FeaturesVk &features,
gl::ShaderMap<std::string> *shaderSourcesOut,
ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
GlslangGetShaderSource(CreateSourceOptions(features), programState, resources,
programInterfaceInfo, shaderSourcesOut, variableInfoMapOut);
GlslangSourceOptions options = CreateSourceOptions(features);
GlslangGetShaderSource(options, programState, resources, programInterfaceInfo, shaderSourcesOut,
variableInfoMapOut);
}
// static
......
......@@ -25,6 +25,8 @@ namespace rx
class GlslangWrapperVk
{
public:
static GlslangSourceOptions CreateSourceOptions(const angle::FeaturesVk &features);
static void ResetGlslangProgramInterfaceInfo(
GlslangProgramInterfaceInfo *glslangProgramInterfaceInfo);
......
......@@ -8,10 +8,13 @@
#include "libANGLE/renderer/vulkan/ProgramExecutableVk.h"
#include "libANGLE/renderer/glslang_wrapper_utils.h"
#include "libANGLE/renderer/vulkan/BufferVk.h"
#include "libANGLE/renderer/vulkan/GlslangWrapperVk.h"
#include "libANGLE/renderer/vulkan/TextureVk.h"
#include "libANGLE/renderer/vulkan/TransformFeedbackVk.h"
#include "libANGLE/renderer/vulkan/vk_helpers.h"
#include "libANGLE/renderer/vulkan/vk_utils.h"
namespace rx
{
......@@ -21,11 +24,7 @@ DefaultUniformBlock::DefaultUniformBlock() = default;
DefaultUniformBlock::~DefaultUniformBlock() = default;
ProgramExecutableVk::ProgramExecutableVk()
: mStorageBlockBindingsOffset(0),
mAtomicCounterBufferBindingsOffset(0),
mImageBindingsOffset(0),
mEmptyDescriptorSets{},
mDynamicBufferOffsets{}
: mEmptyDescriptorSets{}, mNumDefaultUniformDescriptors(0), mDynamicBufferOffsets{}
{}
ProgramExecutableVk::~ProgramExecutableVk() = default;
......@@ -34,8 +33,6 @@ void ProgramExecutableVk::reset(ContextVk *contextVk)
{
RendererVk *renderer = contextVk->getRenderer();
clearVariableInfoMap();
for (auto &descriptorSetLayout : mDescriptorSetLayouts)
{
descriptorSetLayout.reset();
......@@ -46,6 +43,7 @@ void ProgramExecutableVk::reset(ContextVk *contextVk)
mDescriptorSets.clear();
mEmptyDescriptorSets.fill(VK_NULL_HANDLE);
mNumDefaultUniformDescriptors = 0;
for (vk::RefCountedDescriptorPoolBinding &binding : mDescriptorPoolBindings)
{
......@@ -61,20 +59,62 @@ void ProgramExecutableVk::reset(ContextVk *contextVk)
mDescriptorBuffersCache.clear();
}
void ProgramExecutableVk::clearVariableInfoMap()
std::unique_ptr<rx::LinkEvent> ProgramExecutableVk::load(gl::BinaryInputStream *stream)
{
mVariableInfoMap.clear();
clearVariableInfoMap();
size_t variableInfoMapSize = stream->readInt<size_t>();
for (size_t i = 0; i < variableInfoMapSize; ++i)
{
const std::string variableName = stream->readString();
ShaderInterfaceVariableInfo *info = &mVariableInfoMap[variableName];
info->descriptorSet = stream->readInt<uint32_t>();
info->binding = stream->readInt<uint32_t>();
for (gl::ShaderType shaderType : gl::AllShaderTypes())
{
info->location[shaderType] = stream->readInt<uint32_t>();
info->component[shaderType] = stream->readInt<uint32_t>();
}
// PackedEnumBitSet uses uint8_t
info->activeStages = gl::ShaderBitSet(stream->readInt<uint8_t>());
info->xfbBuffer = stream->readInt<uint32_t>();
info->xfbOffset = stream->readInt<uint32_t>();
info->xfbStride = stream->readInt<uint32_t>();
}
return std::make_unique<LinkEventDone>(angle::Result::Continue);
}
void ProgramExecutableVk::updateBindingOffsets(const gl::ProgramState &programState)
void ProgramExecutableVk::save(gl::BinaryOutputStream *stream)
{
mStorageBlockBindingsOffset = static_cast<uint32_t>(programState.getUniqueUniformBlockCount());
stream->writeInt<size_t>(mVariableInfoMap.size());
for (const auto &it : mVariableInfoMap)
{
stream->writeString(it.first);
stream->writeInt<uint32_t>(it.second.descriptorSet);
stream->writeInt<uint32_t>(it.second.binding);
mAtomicCounterBufferBindingsOffset = static_cast<uint32_t>(
mStorageBlockBindingsOffset + programState.getUniqueStorageBlockCount());
for (gl::ShaderType shaderType : gl::AllShaderTypes())
{
stream->writeInt<uint32_t>(it.second.location[shaderType]);
stream->writeInt<uint32_t>(it.second.component[shaderType]);
}
uint32_t atomicCounterBindingCount = programState.getAtomicCounterBuffers().empty() ? 0 : 1;
mImageBindingsOffset = mAtomicCounterBufferBindingsOffset + atomicCounterBindingCount;
// PackedEnumBitSet uses uint8_t
stream->writeInt<uint8_t>(it.second.activeStages.bits());
stream->writeInt<uint32_t>(it.second.xfbBuffer);
stream->writeInt<uint32_t>(it.second.xfbOffset);
stream->writeInt<uint32_t>(it.second.xfbStride);
}
}
void ProgramExecutableVk::clearVariableInfoMap()
{
mVariableInfoMap.clear();
}
uint32_t GetInterfaceBlockArraySize(const std::vector<gl::InterfaceBlock> &blocks,
......@@ -138,28 +178,32 @@ angle::Result ProgramExecutableVk::allocateDescriptorSetAndGetInfo(ContextVk *co
return angle::Result::Continue;
}
void AddInterfaceBlockDescriptorSetDesc(const std::vector<gl::InterfaceBlock> &blocks,
uint32_t bindingStart,
VkDescriptorType descType,
vk::DescriptorSetLayoutDesc *descOut)
void ProgramExecutableVk::addInterfaceBlockDescriptorSetDesc(
const std::vector<gl::InterfaceBlock> &blocks,
VkDescriptorType descType,
vk::DescriptorSetLayoutDesc *descOut)
{
uint32_t bindingIndex = 0;
for (uint32_t bufferIndex = 0; bufferIndex < blocks.size();)
{
gl::InterfaceBlock block = blocks[bufferIndex];
const uint32_t arraySize = GetInterfaceBlockArraySize(blocks, bufferIndex);
VkShaderStageFlags activeStages =
gl_vk::GetShaderStageFlags(blocks[bufferIndex].activeShaders());
bufferIndex += arraySize;
descOut->update(bindingStart + bindingIndex, descType, arraySize, activeStages);
if (!block.activeShaders().any())
{
continue;
}
bufferIndex += arraySize;
++bindingIndex;
const std::string blockName = block.mappedName;
const ShaderInterfaceVariableInfo &info = mVariableInfoMap[blockName];
const VkShaderStageFlags &activeStages = gl_vk::GetShaderStageFlags(info.activeStages);
descOut->update(info.binding, descType, arraySize, activeStages);
}
}
void AddAtomicCounterBufferDescriptorSetDesc(
void ProgramExecutableVk::addAtomicCounterBufferDescriptorSetDesc(
const std::vector<gl::AtomicCounterBuffer> &atomicCounterBuffers,
uint32_t bindingStart,
vk::DescriptorSetLayoutDesc *descOut)
{
if (atomicCounterBuffers.empty())
......@@ -167,20 +211,17 @@ void AddAtomicCounterBufferDescriptorSetDesc(
return;
}
VkShaderStageFlags activeStages = 0;
for (const gl::AtomicCounterBuffer &buffer : atomicCounterBuffers)
{
activeStages |= gl_vk::GetShaderStageFlags(buffer.activeShaders());
}
std::string blockName(sh::vk::kAtomicCountersBlockName);
const ShaderInterfaceVariableInfo &info = mVariableInfoMap[blockName];
VkShaderStageFlags activeStages = rx::gl_vk::GetShaderStageFlags(info.activeStages);
// A single storage buffer array is used for all stages for simplicity.
descOut->update(bindingStart, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
descOut->update(info.binding, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
gl::IMPLEMENTATION_MAX_ATOMIC_COUNTER_BUFFERS, activeStages);
}
void AddImageDescriptorSetDesc(const gl::ProgramState &programState,
uint32_t bindingStart,
vk::DescriptorSetLayoutDesc *descOut)
void ProgramExecutableVk::addImageDescriptorSetDesc(const gl::ProgramState &programState,
vk::DescriptorSetLayoutDesc *descOut)
{
const std::vector<gl::ImageBinding> &imageBindings = programState.getImageBindings();
const std::vector<gl::LinkedUniform> &uniforms = programState.getUniforms();
......@@ -191,21 +232,22 @@ void AddImageDescriptorSetDesc(const gl::ProgramState &programState,
uint32_t uniformIndex = programState.getUniformIndexFromImageIndex(imageIndex);
const gl::LinkedUniform &imageUniform = uniforms[uniformIndex];
std::string name = imageUniform.mappedName;
GetImageNameWithoutIndices(&name);
ShaderInterfaceVariableInfo &info = mVariableInfoMap[name];
// The front-end always binds array image units sequentially.
uint32_t arraySize = static_cast<uint32_t>(imageBinding.boundImageUnits.size());
VkShaderStageFlags activeStages = gl_vk::GetShaderStageFlags(imageUniform.activeShaders());
VkShaderStageFlags activeStages = gl_vk::GetShaderStageFlags(info.activeStages);
uint32_t bindingIndex = bindingStart + imageIndex;
descOut->update(bindingIndex, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, arraySize, activeStages);
descOut->update(info.binding, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, arraySize, activeStages);
}
}
void AddTextureDescriptorSetDesc(const gl::ProgramState &programState,
bool useOldRewriteStructSamplers,
vk::DescriptorSetLayoutDesc *descOut)
void ProgramExecutableVk::addTextureDescriptorSetDesc(const gl::ProgramState &programState,
bool useOldRewriteStructSamplers,
vk::DescriptorSetLayoutDesc *descOut)
{
uint32_t bindingIndex = 0;
const std::vector<gl::SamplerBinding> &samplerBindings = programState.getSamplerBindings();
const std::vector<gl::LinkedUniform> &uniforms = programState.getUniforms();
......@@ -216,10 +258,14 @@ void AddTextureDescriptorSetDesc(const gl::ProgramState &programState,
uint32_t uniformIndex = programState.getUniformIndexFromSamplerIndex(textureIndex);
const gl::LinkedUniform &samplerUniform = uniforms[uniformIndex];
const std::string samplerName = useOldRewriteStructSamplers
? GetMappedSamplerNameOld(samplerUniform.name)
: GlslangGetMappedSamplerName(samplerUniform.name);
ShaderInterfaceVariableInfo &info = mVariableInfoMap[samplerName];
// The front-end always binds array sampler units sequentially.
uint32_t arraySize = static_cast<uint32_t>(samplerBinding.boundTextureUnits.size());
VkShaderStageFlags activeStages =
gl_vk::GetShaderStageFlags(samplerUniform.activeShaders());
VkShaderStageFlags activeStages = gl_vk::GetShaderStageFlags(info.activeStages);
if (!useOldRewriteStructSamplers)
{
......@@ -237,7 +283,7 @@ void AddTextureDescriptorSetDesc(const gl::ProgramState &programState,
}
}
descOut->update(bindingIndex++, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, arraySize,
descOut->update(info.binding, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, arraySize,
activeStages);
}
}
......@@ -316,19 +362,24 @@ angle::Result ProgramExecutableVk::createPipelineLayout(const gl::Context *glCon
const gl::ShaderBitSet &linkedShaderStages = glExecutable.getLinkedShaderStages();
reset(contextVk);
updateBindingOffsets(programState);
// Store a reference to the pipeline and descriptor set layouts. This will create them if they
// don't already exist in the cache.
// Default uniforms and transform feedback:
vk::DescriptorSetLayoutDesc uniformsAndXfbSetDesc;
uint32_t uniformBindingIndex = 0;
for (const gl::ShaderType shaderType : linkedShaderStages)
{
uniformsAndXfbSetDesc.update(uniformBindingIndex++,
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1,
const std::string uniformBlockName = kDefaultUniformNames[shaderType];
ShaderInterfaceVariableInfo &info = mVariableInfoMap[uniformBlockName];
if (!info.activeStages[shaderType])
{
continue;
}
uniformsAndXfbSetDesc.update(info.binding, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1,
gl_vk::kShaderStageMap[shaderType]);
mNumDefaultUniformDescriptors++;
}
bool hasVertexShader = glExecutable.hasLinkedShaderStage(gl::ShaderType::Vertex);
bool hasXfbVaryings = !programState.getLinkedTransformFeedbackVaryings().empty();
......@@ -336,7 +387,7 @@ angle::Result ProgramExecutableVk::createPipelineLayout(const gl::Context *glCon
{
size_t xfbBufferCount = programState.getTransformFeedbackBufferCount();
TransformFeedbackVk *transformFeedbackVk = vk::GetImpl(transformFeedback);
transformFeedbackVk->updateDescriptorSetLayout(contextVk, xfbBufferCount,
transformFeedbackVk->updateDescriptorSetLayout(contextVk, mVariableInfoMap, xfbBufferCount,
&uniformsAndXfbSetDesc);
}
......@@ -347,16 +398,13 @@ angle::Result ProgramExecutableVk::createPipelineLayout(const gl::Context *glCon
// Uniform and storage buffers, atomic counter buffers and images:
vk::DescriptorSetLayoutDesc resourcesSetDesc;
AddInterfaceBlockDescriptorSetDesc(programState.getUniformBlocks(),
getUniformBlockBindingsOffset(),
addInterfaceBlockDescriptorSetDesc(programState.getUniformBlocks(),
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &resourcesSetDesc);
AddInterfaceBlockDescriptorSetDesc(programState.getShaderStorageBlocks(),
getStorageBlockBindingsOffset(),
addInterfaceBlockDescriptorSetDesc(programState.getShaderStorageBlocks(),
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resourcesSetDesc);
AddAtomicCounterBufferDescriptorSetDesc(programState.getAtomicCounterBuffers(),
getAtomicCounterBufferBindingsOffset(),
addAtomicCounterBufferDescriptorSetDesc(programState.getAtomicCounterBuffers(),
&resourcesSetDesc);
AddImageDescriptorSetDesc(programState, getImageBindingsOffset(), &resourcesSetDesc);
addImageDescriptorSetDesc(programState, &resourcesSetDesc);
ANGLE_TRY(renderer->getDescriptorSetLayout(
contextVk, resourcesSetDesc, &mDescriptorSetLayouts[kShaderResourceDescriptorSetIndex]));
......@@ -364,16 +412,15 @@ angle::Result ProgramExecutableVk::createPipelineLayout(const gl::Context *glCon
// Textures:
vk::DescriptorSetLayoutDesc texturesSetDesc;
AddTextureDescriptorSetDesc(programState, contextVk->useOldRewriteStructSamplers(),
addTextureDescriptorSetDesc(programState, contextVk->useOldRewriteStructSamplers(),
&texturesSetDesc);
ANGLE_TRY(renderer->getDescriptorSetLayout(contextVk, texturesSetDesc,
&mDescriptorSetLayouts[kTextureDescriptorSetIndex]));
// Driver uniforms:
VkShaderStageFlags driverUniformsStages = programState.getProgramExecutable().isCompute()
? VK_SHADER_STAGE_COMPUTE_BIT
: VK_SHADER_STAGE_ALL_GRAPHICS;
VkShaderStageFlags driverUniformsStages =
glExecutable.isCompute() ? VK_SHADER_STAGE_COMPUTE_BIT : VK_SHADER_STAGE_ALL_GRAPHICS;
vk::DescriptorSetLayoutDesc driverUniformsSetDesc =
contextVk->getDriverUniformsDescriptorSetDesc(driverUniformsStages);
ANGLE_TRY(renderer->getDescriptorSetLayout(
......@@ -396,7 +443,7 @@ angle::Result ProgramExecutableVk::createPipelineLayout(const gl::Context *glCon
// Initialize descriptor pools.
std::array<VkDescriptorPoolSize, 2> uniformAndXfbSetSize = {
{{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
static_cast<uint32_t>(programState.getProgramExecutable().getLinkedShaderStageCount())},
static_cast<uint32_t>(mNumDefaultUniformDescriptors)},
{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS}}};
uint32_t uniformBlockCount = static_cast<uint32_t>(programState.getUniformBlocks().size());
......@@ -451,7 +498,7 @@ angle::Result ProgramExecutableVk::createPipelineLayout(const gl::Context *glCon
&textureSetSize, 1));
}
mDynamicBufferOffsets.resize(programState.getProgramExecutable().getLinkedShaderStageCount());
mDynamicBufferOffsets.resize(glExecutable.getLinkedShaderStageCount());
// Initialize an "empty" buffer for use with default uniform blocks where there are no uniforms,
// or atomic counter buffer array indices that are unused.
......@@ -482,17 +529,23 @@ void ProgramExecutableVk::updateDefaultUniformsDescriptorSet(
gl::ShaderVector<VkDescriptorBufferInfo> descriptorBufferInfo(shaderStageCount);
gl::ShaderVector<VkWriteDescriptorSet> writeDescriptorInfo(shaderStageCount);
uint32_t bindingIndex = 0;
mDescriptorBuffersCache.clear();
// Write default uniforms for each shader type.
uint32_t writeInfoCount = 0;
for (const gl::ShaderType shaderType :
programState.getProgramExecutable().getLinkedShaderStages())
{
const std::string uniformBlockName = kDefaultUniformNames[shaderType];
ShaderInterfaceVariableInfo &info = mVariableInfoMap[uniformBlockName];
if (!info.activeStages[shaderType])
{
return;
}
DefaultUniformBlock &uniformBlock = defaultUniformBlocks[shaderType];
VkDescriptorBufferInfo &bufferInfo = descriptorBufferInfo[bindingIndex];
VkWriteDescriptorSet &writeInfo = writeDescriptorInfo[bindingIndex];
VkDescriptorBufferInfo &bufferInfo = descriptorBufferInfo[writeInfoCount];
VkWriteDescriptorSet &writeInfo = writeDescriptorInfo[writeInfoCount];
if (!uniformBlock.uniformData.empty())
{
......@@ -513,7 +566,7 @@ void ProgramExecutableVk::updateDefaultUniformsDescriptorSet(
writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeInfo.pNext = nullptr;
writeInfo.dstSet = mDescriptorSets[kUniformsAndXfbDescriptorSetIndex];
writeInfo.dstBinding = bindingIndex;
writeInfo.dstBinding = info.binding;
writeInfo.dstArrayElement = 0;
writeInfo.descriptorCount = 1;
writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
......@@ -521,15 +574,14 @@ void ProgramExecutableVk::updateDefaultUniformsDescriptorSet(
writeInfo.pBufferInfo = &bufferInfo;
writeInfo.pTexelBufferView = nullptr;
++bindingIndex;
++writeInfoCount;
}
VkDevice device = contextVk->getDevice();
ASSERT(bindingIndex == shaderStageCount);
ASSERT(shaderStageCount <= kReservedDefaultUniformBindingCount);
ASSERT(writeInfoCount <= kReservedDefaultUniformBindingCount);
vkUpdateDescriptorSets(device, shaderStageCount, writeDescriptorInfo.data(), 0, nullptr);
vkUpdateDescriptorSets(device, writeInfoCount, writeDescriptorInfo.data(), 0, nullptr);
}
void ProgramExecutableVk::updateBuffersDescriptorSet(ContextVk *contextVk,
......@@ -548,8 +600,6 @@ void ProgramExecutableVk::updateBuffersDescriptorSet(ContextVk *contextVk,
ASSERT(descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ||
descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
const bool isStorageBuffer = descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
const uint32_t bindingStart =
isStorageBuffer ? getStorageBlockBindingsOffset() : getUniformBlockBindingsOffset();
static_assert(
gl::IMPLEMENTATION_MAX_SHADER_STORAGE_BUFFER_BINDINGS >=
......@@ -559,9 +609,6 @@ void ProgramExecutableVk::updateBuffersDescriptorSet(ContextVk *contextVk,
gl::StorageBuffersArray<VkDescriptorBufferInfo> descriptorBufferInfo;
gl::StorageBuffersArray<VkWriteDescriptorSet> writeDescriptorInfo;
uint32_t writeCount = 0;
// The binding is incremented every time arrayElement 0 is encountered, which means there will
// be an increment right at the start. Start from -1 to get 0 as the first binding.
int32_t currentBinding = -1;
// Write uniform or storage buffers.
const gl::State &glState = contextVk->getState();
......@@ -572,11 +619,9 @@ void ProgramExecutableVk::updateBuffersDescriptorSet(ContextVk *contextVk,
isStorageBuffer ? glState.getIndexedShaderStorageBuffer(block.binding)
: glState.getIndexedUniformBuffer(block.binding);
if (!block.isArray || block.arrayElement == 0)
if (!block.activeShaders().any())
{
// Array indices of the same buffer binding are placed sequentially in `blocks`.
// Thus, the block binding is updated only when array index 0 is encountered.
++currentBinding;
continue;
}
if (bufferBinding.get() == nullptr)
......@@ -584,9 +629,10 @@ void ProgramExecutableVk::updateBuffersDescriptorSet(ContextVk *contextVk,
continue;
}
uint32_t binding = bindingStart + currentBinding;
uint32_t arrayElement = block.isArray ? block.arrayElement : 0;
VkDeviceSize maxBlockSize = isStorageBuffer ? 0 : block.dataSize;
ShaderInterfaceVariableInfo info = mVariableInfoMap[block.mappedName];
uint32_t binding = info.binding;
uint32_t arrayElement = block.isArray ? block.arrayElement : 0;
VkDeviceSize maxBlockSize = isStorageBuffer ? 0 : block.dataSize;
VkDescriptorBufferInfo &bufferInfo = descriptorBufferInfo[writeCount];
VkWriteDescriptorSet &writeInfo = writeDescriptorInfo[writeCount];
......@@ -634,7 +680,8 @@ void ProgramExecutableVk::updateAtomicCounterBuffersDescriptorSet(
VkDescriptorSet descriptorSet = mDescriptorSets[kShaderResourceDescriptorSetIndex];
const uint32_t bindingStart = getAtomicCounterBufferBindingsOffset();
std::string blockName(sh::vk::kAtomicCountersBlockName);
const ShaderInterfaceVariableInfo &info = mVariableInfoMap[blockName];
gl::AtomicCounterBuffersArray<VkDescriptorBufferInfo> descriptorBufferInfo;
gl::AtomicCounterBuffersArray<VkWriteDescriptorSet> writeDescriptorInfo;
......@@ -661,7 +708,7 @@ void ProgramExecutableVk::updateAtomicCounterBuffersDescriptorSet(
VkWriteDescriptorSet &writeInfo = writeDescriptorInfo[binding];
WriteBufferDescriptorSetBinding(bufferBinding, 0, descriptorSet,
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, bindingStart, binding,
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, info.binding, binding,
requiredOffsetAlignment, &bufferInfo, &writeInfo);
BufferVk *bufferVk = vk::GetImpl(bufferBinding.get());
......@@ -688,7 +735,7 @@ void ProgramExecutableVk::updateAtomicCounterBuffersDescriptorSet(
writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeInfo.pNext = nullptr;
writeInfo.dstSet = descriptorSet;
writeInfo.dstBinding = bindingStart;
writeInfo.dstBinding = info.binding;
writeInfo.dstArrayElement = static_cast<uint32_t>(binding);
writeInfo.descriptorCount = 1;
writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
......@@ -708,6 +755,7 @@ angle::Result ProgramExecutableVk::updateImagesDescriptorSet(const gl::ProgramSt
{
const gl::State &glState = contextVk->getState();
const std::vector<gl::ImageBinding> &imageBindings = programState.getImageBindings();
const std::vector<gl::LinkedUniform> &uniforms = programState.getUniforms();
if (imageBindings.empty())
{
......@@ -718,8 +766,6 @@ angle::Result ProgramExecutableVk::updateImagesDescriptorSet(const gl::ProgramSt
const gl::ActiveTextureArray<TextureVk *> &activeImages = contextVk->getActiveImages();
const uint32_t bindingStart = getImageBindingsOffset();
gl::ImagesArray<VkDescriptorImageInfo> descriptorImageInfo;
gl::ImagesArray<VkWriteDescriptorSet> writeDescriptorInfo;
uint32_t writeCount = 0;
......@@ -728,6 +774,11 @@ angle::Result ProgramExecutableVk::updateImagesDescriptorSet(const gl::ProgramSt
for (uint32_t imageIndex = 0; imageIndex < imageBindings.size(); ++imageIndex)
{
const gl::ImageBinding &imageBinding = imageBindings[imageIndex];
uint32_t uniformIndex = programState.getUniformIndexFromImageIndex(imageIndex);
const gl::LinkedUniform &imageUniform = uniforms[uniformIndex];
std::string name = imageUniform.mappedName;
GetImageNameWithoutIndices(&name);
ShaderInterfaceVariableInfo &info = mVariableInfoMap[name];
ASSERT(!imageBinding.unreferenced);
......@@ -759,7 +810,7 @@ angle::Result ProgramExecutableVk::updateImagesDescriptorSet(const gl::ProgramSt
writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeInfo.pNext = nullptr;
writeInfo.dstSet = descriptorSet;
writeInfo.dstBinding = bindingStart + imageIndex;
writeInfo.dstBinding = info.binding;
writeInfo.dstArrayElement = arrayElement;
writeInfo.descriptorCount = 1;
writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
......@@ -882,85 +933,89 @@ angle::Result ProgramExecutableVk::updateTexturesDescriptorSet(const gl::Program
bool emulateSeamfulCubeMapSampling = contextVk->emulateSeamfulCubeMapSampling();
bool useOldRewriteStructSamplers = contextVk->useOldRewriteStructSamplers();
std::unordered_map<std::string, uint32_t> mappedSamplerNameToBindingIndex;
std::unordered_map<std::string, uint32_t> mappedSamplerNameToArrayOffset;
uint32_t currentBindingIndex = 0;
for (uint32_t textureIndex = 0; textureIndex < programState.getSamplerBindings().size();
++textureIndex)
for (const gl::ShaderType shaderType : executable.getLinkedShaderStages())
{
const gl::SamplerBinding &samplerBinding = programState.getSamplerBindings()[textureIndex];
ASSERT(!samplerBinding.unreferenced);
uint32_t uniformIndex = programState.getUniformIndexFromSamplerIndex(textureIndex);
const gl::LinkedUniform &samplerUniform = programState.getUniforms()[uniformIndex];
std::string mappedSamplerName = GlslangGetMappedSamplerName(samplerUniform.name);
if (useOldRewriteStructSamplers ||
mappedSamplerNameToBindingIndex.emplace(mappedSamplerName, currentBindingIndex).second)
{
currentBindingIndex++;
}
uint32_t bindingIndex = textureIndex;
uint32_t arrayOffset = 0;
uint32_t arraySize = static_cast<uint32_t>(samplerBinding.boundTextureUnits.size());
if (!useOldRewriteStructSamplers)
std::unordered_map<std::string, uint32_t> mappedSamplerNameToArrayOffset;
for (uint32_t textureIndex = 0; textureIndex < programState.getSamplerBindings().size();
++textureIndex)
{
bindingIndex = mappedSamplerNameToBindingIndex[mappedSamplerName];
arrayOffset = mappedSamplerNameToArrayOffset[mappedSamplerName];
// Front-end generates array elements in order, so we can just increment
// the offset each time we process a nested array.
mappedSamplerNameToArrayOffset[mappedSamplerName] += arraySize;
}
const gl::SamplerBinding &samplerBinding =
programState.getSamplerBindings()[textureIndex];
for (uint32_t arrayElement = 0; arrayElement < arraySize; ++arrayElement)
{
GLuint textureUnit = samplerBinding.boundTextureUnits[arrayElement];
TextureVk *textureVk = activeTextures[textureUnit].texture;
SamplerVk *samplerVk = activeTextures[textureUnit].sampler;
ASSERT(!samplerBinding.unreferenced);
vk::ImageHelper &image = textureVk->getImage();
uint32_t uniformIndex = programState.getUniformIndexFromSamplerIndex(textureIndex);
const gl::LinkedUniform &samplerUniform = programState.getUniforms()[uniformIndex];
std::string mappedSamplerName = GlslangGetMappedSamplerName(samplerUniform.name);
VkDescriptorImageInfo &imageInfo = descriptorImageInfo[writeCount];
// Use bound sampler object if one present, otherwise use texture's sampler
const vk::Sampler &sampler =
(samplerVk != nullptr) ? samplerVk->getSampler() : textureVk->getSampler();
if (!samplerUniform.isActive(shaderType))
{
continue;
}
imageInfo.sampler = sampler.getHandle();
imageInfo.imageLayout = image.getCurrentLayout();
uint32_t arrayOffset = 0;
uint32_t arraySize = static_cast<uint32_t>(samplerBinding.boundTextureUnits.size());
if (emulateSeamfulCubeMapSampling)
if (!useOldRewriteStructSamplers)
{
// If emulating seamful cubemapping, use the fetch image view. This is basically
// the same image view as read, except it's a 2DArray view for cube maps.
imageInfo.imageView =
textureVk->getFetchImageViewAndRecordUse(contextVk).getHandle();
arrayOffset = mappedSamplerNameToArrayOffset[mappedSamplerName];
// Front-end generates array elements in order, so we can just increment
// the offset each time we process a nested array.
mappedSamplerNameToArrayOffset[mappedSamplerName] += arraySize;
}
else
for (uint32_t arrayElement = 0; arrayElement < arraySize; ++arrayElement)
{
imageInfo.imageView =
textureVk->getReadImageViewAndRecordUse(contextVk).getHandle();
GLuint textureUnit = samplerBinding.boundTextureUnits[arrayElement];
TextureVk *textureVk = activeTextures[textureUnit].texture;
SamplerVk *samplerVk = activeTextures[textureUnit].sampler;
vk::ImageHelper &image = textureVk->getImage();
VkDescriptorImageInfo &imageInfo = descriptorImageInfo[writeCount];
// Use bound sampler object if one present, otherwise use texture's sampler
const vk::Sampler &sampler =
(samplerVk != nullptr) ? samplerVk->getSampler() : textureVk->getSampler();
imageInfo.sampler = sampler.getHandle();
imageInfo.imageLayout = image.getCurrentLayout();
if (emulateSeamfulCubeMapSampling)
{
// If emulating seamful cubemapping, use the fetch image view. This is
// basically the same image view as read, except it's a 2DArray view for
// cube maps.
imageInfo.imageView =
textureVk->getFetchImageViewAndRecordUse(contextVk).getHandle();
}
else
{
imageInfo.imageView =
textureVk->getReadImageViewAndRecordUse(contextVk).getHandle();
}
const std::string samplerName =
contextVk->getRenderer()->getFeatures().forceOldRewriteStructSamplers.enabled
? GetMappedSamplerNameOld(samplerUniform.name)
: GlslangGetMappedSamplerName(samplerUniform.name);
ShaderInterfaceVariableInfo &info = mVariableInfoMap[samplerName];
VkWriteDescriptorSet &writeInfo = writeDescriptorInfo[writeCount];
writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeInfo.pNext = nullptr;
writeInfo.dstSet = descriptorSet;
writeInfo.dstBinding = info.binding;
writeInfo.dstArrayElement = arrayOffset + arrayElement;
writeInfo.descriptorCount = 1;
writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
writeInfo.pImageInfo = &imageInfo;
writeInfo.pBufferInfo = nullptr;
writeInfo.pTexelBufferView = nullptr;
++writeCount;
}
VkWriteDescriptorSet &writeInfo = writeDescriptorInfo[writeCount];
writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeInfo.pNext = nullptr;
writeInfo.dstSet = descriptorSet;
writeInfo.dstBinding = bindingIndex;
writeInfo.dstArrayElement = arrayOffset + arrayElement;
writeInfo.descriptorCount = 1;
writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
writeInfo.pImageInfo = &imageInfo;
writeInfo.pBufferInfo = nullptr;
writeInfo.pTexelBufferView = nullptr;
++writeCount;
}
}
......@@ -1033,10 +1088,10 @@ angle::Result ProgramExecutableVk::updateDescriptorSets(ContextVk *contextVk,
// requires a dynamic offset.
const uint32_t uniformBlockOffsetCount =
descriptorSetIndex == kUniformsAndXfbDescriptorSetIndex
? static_cast<uint32_t>(mDynamicBufferOffsets.size())
? static_cast<uint32_t>(mNumDefaultUniformDescriptors)
: 0;
commandBuffer->bindDescriptorSets(mPipelineLayout.get(), pipelineBindPoint,
commandBuffer->bindDescriptorSets(getPipelineLayout(), pipelineBindPoint,
descriptorSetIndex, 1, &descSet, uniformBlockOffsetCount,
mDynamicBufferOffsets.data());
}
......
......@@ -44,9 +44,13 @@ class ProgramExecutableVk
void reset(ContextVk *contextVk);
void save(gl::BinaryOutputStream *stream);
std::unique_ptr<rx::LinkEvent> load(gl::BinaryInputStream *stream);
void clearVariableInfoMap();
ShaderInterfaceVariableInfoMap &getShaderInterfaceVariableInfoMap() { return mVariableInfoMap; }
const vk::PipelineLayout &getPipelineLayout() const { return mPipelineLayout.get(); }
angle::Result createPipelineLayout(const gl::Context *glContext,
const gl::ProgramExecutable &glExecutable,
const gl::ProgramState &programState);
......@@ -68,19 +72,21 @@ class ProgramExecutableVk
friend class ProgramVk;
friend class ProgramPipelineVk;
void updateBindingOffsets(const gl::ProgramState &programState);
uint32_t getUniformBlockBindingsOffset() const { return 0; }
uint32_t getStorageBlockBindingsOffset() const { return mStorageBlockBindingsOffset; }
uint32_t getAtomicCounterBufferBindingsOffset() const
{
return mAtomicCounterBufferBindingsOffset;
}
uint32_t getImageBindingsOffset() const { return mImageBindingsOffset; }
angle::Result allocateDescriptorSet(ContextVk *contextVk, uint32_t descriptorSetIndex);
angle::Result allocateDescriptorSetAndGetInfo(ContextVk *contextVk,
uint32_t descriptorSetIndex,
bool *newPoolAllocatedOut);
void addInterfaceBlockDescriptorSetDesc(const std::vector<gl::InterfaceBlock> &blocks,
VkDescriptorType descType,
vk::DescriptorSetLayoutDesc *descOut);
void addAtomicCounterBufferDescriptorSetDesc(
const std::vector<gl::AtomicCounterBuffer> &atomicCounterBuffers,
vk::DescriptorSetLayoutDesc *descOut);
void addImageDescriptorSetDesc(const gl::ProgramState &programState,
vk::DescriptorSetLayoutDesc *descOut);
void addTextureDescriptorSetDesc(const gl::ProgramState &programState,
bool useOldRewriteStructSamplers,
vk::DescriptorSetLayoutDesc *descOut);
void updateDefaultUniformsDescriptorSet(
const gl::ProgramState &programState,
......@@ -100,13 +106,6 @@ class ProgramExecutableVk
angle::Result updateImagesDescriptorSet(const gl::ProgramState &programState,
ContextVk *contextVk);
// In their descriptor set, uniform buffers are placed first, then storage buffers, then atomic
// counter buffers and then images. These cached values contain the offsets where storage
// buffer, atomic counter buffer and image bindings start.
uint32_t mStorageBlockBindingsOffset;
uint32_t mAtomicCounterBufferBindingsOffset;
uint32_t mImageBindingsOffset;
// This is a special "empty" placeholder buffer for when a shader has no uniforms or doesn't
// use all slots in the atomic counter buffer array.
//
......@@ -118,6 +117,7 @@ class ProgramExecutableVk
std::vector<VkDescriptorSet> mDescriptorSets;
vk::DescriptorSetLayoutArray<VkDescriptorSet> mEmptyDescriptorSets;
std::vector<vk::BufferHelper *> mDescriptorBuffersCache;
size_t mNumDefaultUniformDescriptors;
std::unordered_map<vk::TextureDescriptorDesc, VkDescriptorSet> mTextureDescriptorsCache;
......@@ -137,6 +137,8 @@ class ProgramExecutableVk
gl::ShaderVector<uint32_t> mDynamicBufferOffsets;
// TODO: http://anglebug.com/4524: Need a different hash key than a string,
// since that's slow to calculate.
ShaderInterfaceVariableInfoMap mVariableInfoMap;
};
......
......@@ -303,6 +303,7 @@ std::unique_ptr<rx::LinkEvent> ProgramVk::load(const gl::Context *context,
reset(contextVk);
mShaderInfo.load(stream);
mExecutable.load(stream);
// Deserializes the uniformLayout data of mDefaultUniformBlocks
for (gl::ShaderType shaderType : gl::AllShaderTypes())
......@@ -337,6 +338,7 @@ std::unique_ptr<rx::LinkEvent> ProgramVk::load(const gl::Context *context,
void ProgramVk::save(const gl::Context *context, gl::BinaryOutputStream *stream)
{
mShaderInfo.save(stream);
mExecutable.save(stream);
// Serializes the uniformLayout data of mDefaultUniformBlocks
for (gl::ShaderType shaderType : gl::AllShaderTypes())
......@@ -378,6 +380,7 @@ std::unique_ptr<LinkEvent> ProgramVk::link(const gl::Context *context,
linkResources(resources);
reset(contextVk);
mExecutable.clearVariableInfoMap();
// Gather variable info and transform sources.
gl::ShaderMap<std::string> shaderSources;
......
......@@ -11,6 +11,7 @@
#include "libANGLE/Context.h"
#include "libANGLE/Query.h"
#include "libANGLE/renderer/glslang_wrapper_utils.h"
#include "libANGLE/renderer/vulkan/BufferVk.h"
#include "libANGLE/renderer/vulkan/ContextVk.h"
#include "libANGLE/renderer/vulkan/FramebufferVk.h"
......@@ -161,6 +162,7 @@ angle::Result TransformFeedbackVk::bindIndexedBuffer(
void TransformFeedbackVk::updateDescriptorSetLayout(
ContextVk *contextVk,
ShaderInterfaceVariableInfoMap &vsVariableInfoMap,
size_t xfbBufferCount,
vk::DescriptorSetLayoutDesc *descSetLayoutOut) const
{
......@@ -169,8 +171,11 @@ void TransformFeedbackVk::updateDescriptorSetLayout(
for (uint32_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
{
descSetLayoutOut->update(kXfbBindingIndexStart + bufferIndex,
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT);
const std::string bufferName = GetXfbBufferName(bufferIndex);
const ShaderInterfaceVariableInfo &info = vsVariableInfoMap[bufferName];
descSetLayoutOut->update(info.binding, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1,
VK_SHADER_STAGE_VERTEX_BIT);
}
}
......@@ -296,12 +301,17 @@ void TransformFeedbackVk::writeDescriptorSet(ContextVk *contextVk,
VkDescriptorBufferInfo *pBufferInfo,
VkDescriptorSet descSet) const
{
VkDevice device = contextVk->getDevice();
VkDevice device = contextVk->getDevice();
ProgramExecutableVk *executableVk = contextVk->getExecutable();
ShaderInterfaceVariableInfoMap variableInfoMap =
executableVk->getShaderInterfaceVariableInfoMap();
const std::string bufferName = GetXfbBufferName(0);
ShaderInterfaceVariableInfo &info = variableInfoMap[bufferName];
VkWriteDescriptorSet writeDescriptorInfo = {};
writeDescriptorInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeDescriptorInfo.dstSet = descSet;
writeDescriptorInfo.dstBinding = kXfbBindingIndexStart;
writeDescriptorInfo.dstBinding = info.binding;
writeDescriptorInfo.dstArrayElement = 0;
writeDescriptorInfo.descriptorCount = static_cast<uint32_t>(xfbBufferCount);
writeDescriptorInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
......
......@@ -11,6 +11,8 @@
#define LIBANGLE_RENDERER_VULKAN_TRANSFORMFEEDBACKVK_H_
#include "libANGLE/renderer/TransformFeedbackImpl.h"
#include "libANGLE/renderer/glslang_wrapper_utils.h"
#include "libANGLE/renderer/vulkan/vk_helpers.h"
namespace gl
......@@ -54,6 +56,7 @@ class TransformFeedbackVk : public TransformFeedbackImpl
const gl::OffsetBindingPointer<gl::Buffer> &binding) override;
void updateDescriptorSetLayout(ContextVk *contextVk,
ShaderInterfaceVariableInfoMap &vsVariableInfoMap,
size_t xfbBufferCount,
vk::DescriptorSetLayoutDesc *descSetLayoutOut) const;
void initDescriptorSet(ContextVk *contextVk,
......
......@@ -977,8 +977,6 @@ constexpr uint32_t kReservedDriverUniformBindingCount = 1;
// supported.
constexpr uint32_t kReservedPerStageDefaultUniformBindingCount = 1;
constexpr uint32_t kReservedDefaultUniformBindingCount = 3;
// Binding index start for transform feedback buffers:
constexpr uint32_t kXfbBindingIndexStart = kReservedDefaultUniformBindingCount;
} // namespace rx
#endif // LIBANGLE_RENDERER_VULKAN_VK_CACHE_UTILS_H_
......@@ -3998,6 +3998,12 @@ TEST_P(TextureLimitsTest, MaxFragmentTextures)
// Test rendering with maximum combined texture units.
TEST_P(TextureLimitsTest, MaxCombinedTextures)
{
// TODO(timvp): http://anglebug.com/3570
// Currently only fails on SwiftShader but we don't have an IsSwiftShader().
// max per-stage sampled image bindings count (32) exceeds device
// maxPerStageDescriptorSampledImages limit (16)
ANGLE_SKIP_TEST_IF(IsVulkan());
GLint vertexTextures = mMaxVertexTextures;
if (vertexTextures + mMaxFragmentTextures > mMaxCombinedTextures)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment