summaryrefslogtreecommitdiffstats
path: root/gfx/angle/checkout/src/libANGLE/ProgramLinkedResources.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/angle/checkout/src/libANGLE/ProgramLinkedResources.cpp')
-rw-r--r--gfx/angle/checkout/src/libANGLE/ProgramLinkedResources.cpp2377
1 files changed, 2377 insertions, 0 deletions
diff --git a/gfx/angle/checkout/src/libANGLE/ProgramLinkedResources.cpp b/gfx/angle/checkout/src/libANGLE/ProgramLinkedResources.cpp
new file mode 100644
index 0000000000..11000fdabc
--- /dev/null
+++ b/gfx/angle/checkout/src/libANGLE/ProgramLinkedResources.cpp
@@ -0,0 +1,2377 @@
+//
+// Copyright 2017 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.
+//
+
+// ProgramLinkedResources.cpp: implements link-time checks for default block uniforms, and generates
+// uniform locations. Populates data structures related to uniforms so that they can be stored in
+// program state.
+
+#include "libANGLE/ProgramLinkedResources.h"
+
+#include "common/string_utils.h"
+#include "common/utilities.h"
+#include "libANGLE/Caps.h"
+#include "libANGLE/Context.h"
+#include "libANGLE/Shader.h"
+#include "libANGLE/features.h"
+
+namespace gl
+{
+namespace
+{
+LinkedUniform *FindUniform(std::vector<LinkedUniform> &list, const std::string &name)
+{
+ for (LinkedUniform &uniform : list)
+ {
+ if (uniform.name == name)
+ return &uniform;
+ }
+
+ return nullptr;
+}
+
+template <typename VarT>
+void SetActive(std::vector<VarT> *list, const std::string &name, ShaderType shaderType, bool active)
+{
+ for (auto &variable : *list)
+ {
+ if (variable.name == name)
+ {
+ variable.setActive(shaderType, active);
+ return;
+ }
+ }
+}
+
+// GLSL ES Spec 3.00.3, section 4.3.5.
+LinkMismatchError LinkValidateUniforms(const sh::ShaderVariable &uniform1,
+ const sh::ShaderVariable &uniform2,
+ std::string *mismatchedStructFieldName)
+{
+#if ANGLE_PROGRAM_LINK_VALIDATE_UNIFORM_PRECISION == ANGLE_ENABLED
+ const bool validatePrecisionFeature = true;
+#else
+ const bool validatePrecisionFeature = false;
+#endif
+
+ // Validate precision match of uniforms iff they are statically used
+ bool validatePrecision = uniform1.staticUse && uniform2.staticUse && validatePrecisionFeature;
+ LinkMismatchError linkError = LinkValidateProgramVariables(
+ uniform1, uniform2, validatePrecision, false, false, mismatchedStructFieldName);
+ if (linkError != LinkMismatchError::NO_MISMATCH)
+ {
+ return linkError;
+ }
+
+ // GLSL ES Spec 3.10.4, section 4.4.5.
+ if (uniform1.binding != -1 && uniform2.binding != -1 && uniform1.binding != uniform2.binding)
+ {
+ return LinkMismatchError::BINDING_MISMATCH;
+ }
+
+ // GLSL ES Spec 3.10.4, section 9.2.1.
+ if (uniform1.location != -1 && uniform2.location != -1 &&
+ uniform1.location != uniform2.location)
+ {
+ return LinkMismatchError::LOCATION_MISMATCH;
+ }
+ if (uniform1.offset != uniform2.offset)
+ {
+ return LinkMismatchError::OFFSET_MISMATCH;
+ }
+
+ return LinkMismatchError::NO_MISMATCH;
+}
+
+GLuint GetMaximumShaderUniformVectors(ShaderType shaderType, const Caps &caps)
+{
+ switch (shaderType)
+ {
+ case ShaderType::Vertex:
+ return static_cast<GLuint>(caps.maxVertexUniformVectors);
+ case ShaderType::Fragment:
+ return static_cast<GLuint>(caps.maxFragmentUniformVectors);
+
+ case ShaderType::Compute:
+ case ShaderType::Geometry:
+ case ShaderType::TessControl:
+ case ShaderType::TessEvaluation:
+ return static_cast<GLuint>(caps.maxShaderUniformComponents[shaderType]) / 4;
+
+ default:
+ UNREACHABLE();
+ return 0u;
+ }
+}
+
+enum class UniformType : uint8_t
+{
+ Variable = 0,
+ Sampler = 1,
+ Image = 2,
+ AtomicCounter = 3,
+
+ InvalidEnum = 4,
+ EnumCount = 4,
+};
+
+const char *GetUniformResourceNameString(UniformType uniformType)
+{
+ switch (uniformType)
+ {
+ case UniformType::Variable:
+ return "uniform";
+ case UniformType::Sampler:
+ return "texture image unit";
+ case UniformType::Image:
+ return "image uniform";
+ case UniformType::AtomicCounter:
+ return "atomic counter";
+ default:
+ UNREACHABLE();
+ return "";
+ }
+}
+
+std::string GetUniformResourceLimitName(ShaderType shaderType, UniformType uniformType)
+{
+ // Special case: MAX_TEXTURE_IMAGE_UNITS (no "MAX_FRAGMENT_TEXTURE_IMAGE_UNITS")
+ if (shaderType == ShaderType::Fragment && uniformType == UniformType::Sampler)
+ {
+ return "MAX_TEXTURE_IMAGE_UNITS";
+ }
+
+ std::ostringstream ostream;
+ ostream << "MAX_" << GetShaderTypeString(shaderType) << "_";
+
+ switch (uniformType)
+ {
+ case UniformType::Variable:
+ // For vertex and fragment shaders, ES 2.0 only defines MAX_VERTEX_UNIFORM_VECTORS and
+ // MAX_FRAGMENT_UNIFORM_VECTORS ([OpenGL ES 2.0] Table 6.20).
+ if (shaderType == ShaderType::Vertex || shaderType == ShaderType::Fragment)
+ {
+ ostream << "UNIFORM_VECTORS";
+ break;
+ }
+ // For compute and geometry shaders, there are no definitions on
+ // "MAX_COMPUTE_UNIFORM_VECTORS" or "MAX_GEOMETRY_UNIFORM_VECTORS_EXT"
+ // ([OpenGL ES 3.1] Table 20.45, [EXT_geometry_shader] Table 20.43gs).
+ else
+ {
+ ostream << "UNIFORM_COMPONENTS";
+ }
+ break;
+ case UniformType::Sampler:
+ ostream << "TEXTURE_IMAGE_UNITS";
+ break;
+ case UniformType::Image:
+ ostream << "IMAGE_UNIFORMS";
+ break;
+ case UniformType::AtomicCounter:
+ ostream << "ATOMIC_COUNTERS";
+ break;
+ default:
+ UNREACHABLE();
+ return "";
+ }
+
+ if (shaderType == ShaderType::Geometry)
+ {
+ ostream << "_EXT";
+ }
+
+ return ostream.str();
+}
+
+void LogUniformsExceedLimit(ShaderType shaderType,
+ UniformType uniformType,
+ GLuint limit,
+ InfoLog &infoLog)
+{
+ infoLog << GetShaderTypeString(shaderType) << " shader "
+ << GetUniformResourceNameString(uniformType) << "s count exceeds "
+ << GetUniformResourceLimitName(shaderType, uniformType) << "(" << limit << ")";
+}
+
+// The purpose of this visitor is to capture the uniforms in a uniform block. Each new uniform is
+// added to "uniformsOut".
+class UniformBlockEncodingVisitor : public sh::VariableNameVisitor
+{
+ public:
+ UniformBlockEncodingVisitor(const GetBlockMemberInfoFunc &getMemberInfo,
+ const std::string &namePrefix,
+ const std::string &mappedNamePrefix,
+ std::vector<LinkedUniform> *uniformsOut,
+ ShaderType shaderType,
+ int blockIndex)
+ : sh::VariableNameVisitor(namePrefix, mappedNamePrefix),
+ mGetMemberInfo(getMemberInfo),
+ mUniformsOut(uniformsOut),
+ mShaderType(shaderType),
+ mBlockIndex(blockIndex)
+ {}
+
+ void visitNamedVariable(const sh::ShaderVariable &variable,
+ bool isRowMajor,
+ const std::string &name,
+ const std::string &mappedName,
+ const std::vector<unsigned int> &arraySizes) override
+ {
+ // If getBlockMemberInfo returns false, the variable is optimized out.
+ sh::BlockMemberInfo variableInfo;
+ if (!mGetMemberInfo(name, mappedName, &variableInfo))
+ return;
+
+ std::string nameWithArrayIndex = name;
+ std::string mappedNameWithArrayIndex = mappedName;
+
+ if (variable.isArray())
+ {
+ nameWithArrayIndex += "[0]";
+ mappedNameWithArrayIndex += "[0]";
+ }
+
+ if (mBlockIndex == -1)
+ {
+ SetActive(mUniformsOut, nameWithArrayIndex, mShaderType, variable.active);
+ return;
+ }
+
+ LinkedUniform newUniform(variable.type, variable.precision, nameWithArrayIndex,
+ variable.arraySizes, -1, -1, -1, mBlockIndex, variableInfo);
+ newUniform.mappedName = mappedNameWithArrayIndex;
+ newUniform.setActive(mShaderType, variable.active);
+
+ // Since block uniforms have no location, we don't need to store them in the uniform
+ // locations list.
+ mUniformsOut->push_back(newUniform);
+ }
+
+ private:
+ const GetBlockMemberInfoFunc &mGetMemberInfo;
+ std::vector<LinkedUniform> *mUniformsOut;
+ const ShaderType mShaderType;
+ const int mBlockIndex;
+};
+
+// The purpose of this visitor is to capture the buffer variables in a shader storage block. Each
+// new buffer variable is stored in "bufferVariablesOut".
+class ShaderStorageBlockVisitor : public sh::BlockEncoderVisitor
+{
+ public:
+ ShaderStorageBlockVisitor(const GetBlockMemberInfoFunc &getMemberInfo,
+ const std::string &namePrefix,
+ const std::string &mappedNamePrefix,
+ std::vector<BufferVariable> *bufferVariablesOut,
+ ShaderType shaderType,
+ int blockIndex)
+ : sh::BlockEncoderVisitor(namePrefix, mappedNamePrefix, &mStubEncoder),
+ mGetMemberInfo(getMemberInfo),
+ mBufferVariablesOut(bufferVariablesOut),
+ mShaderType(shaderType),
+ mBlockIndex(blockIndex)
+ {}
+
+ void visitNamedVariable(const sh::ShaderVariable &variable,
+ bool isRowMajor,
+ const std::string &name,
+ const std::string &mappedName,
+ const std::vector<unsigned int> &arraySizes) override
+ {
+ if (mSkipEnabled)
+ return;
+
+ // If getBlockMemberInfo returns false, the variable is optimized out.
+ sh::BlockMemberInfo variableInfo;
+ if (!mGetMemberInfo(name, mappedName, &variableInfo))
+ return;
+
+ std::string nameWithArrayIndex = name;
+ std::string mappedNameWithArrayIndex = mappedName;
+
+ if (variable.isArray())
+ {
+ nameWithArrayIndex += "[0]";
+ mappedNameWithArrayIndex += "[0]";
+ }
+
+ if (mBlockIndex == -1)
+ {
+ SetActive(mBufferVariablesOut, nameWithArrayIndex, mShaderType, variable.active);
+ return;
+ }
+
+ BufferVariable newBufferVariable(variable.type, variable.precision, nameWithArrayIndex,
+ variable.arraySizes, mBlockIndex, variableInfo);
+ newBufferVariable.mappedName = mappedNameWithArrayIndex;
+ newBufferVariable.setActive(mShaderType, variable.active);
+
+ newBufferVariable.topLevelArraySize = mTopLevelArraySize;
+
+ mBufferVariablesOut->push_back(newBufferVariable);
+ }
+
+ private:
+ const GetBlockMemberInfoFunc &mGetMemberInfo;
+ std::vector<BufferVariable> *mBufferVariablesOut;
+ const ShaderType mShaderType;
+ const int mBlockIndex;
+ sh::StubBlockEncoder mStubEncoder;
+};
+
+struct ShaderUniformCount
+{
+ unsigned int vectorCount = 0;
+ unsigned int samplerCount = 0;
+ unsigned int imageCount = 0;
+ unsigned int atomicCounterCount = 0;
+ unsigned int fragmentInOutCount = 0;
+};
+
+ShaderUniformCount &operator+=(ShaderUniformCount &lhs, const ShaderUniformCount &rhs)
+{
+ lhs.vectorCount += rhs.vectorCount;
+ lhs.samplerCount += rhs.samplerCount;
+ lhs.imageCount += rhs.imageCount;
+ lhs.atomicCounterCount += rhs.atomicCounterCount;
+ lhs.fragmentInOutCount += rhs.fragmentInOutCount;
+ return lhs;
+}
+
+// The purpose of this visitor is to flatten struct and array uniforms into a list of singleton
+// uniforms. They are stored in separate lists by uniform type so they can be sorted in order.
+// Counts for each uniform category are stored and can be queried with "getCounts".
+class FlattenUniformVisitor : public sh::VariableNameVisitor
+{
+ public:
+ FlattenUniformVisitor(ShaderType shaderType,
+ const sh::ShaderVariable &uniform,
+ std::vector<LinkedUniform> *uniforms,
+ std::vector<LinkedUniform> *samplerUniforms,
+ std::vector<LinkedUniform> *imageUniforms,
+ std::vector<LinkedUniform> *atomicCounterUniforms,
+ std::vector<LinkedUniform> *inputAttachmentUniforms,
+ std::vector<UnusedUniform> *unusedUniforms)
+ : sh::VariableNameVisitor("", ""),
+ mShaderType(shaderType),
+ mMarkActive(uniform.active),
+ mMarkStaticUse(uniform.staticUse),
+ mBinding(uniform.binding),
+ mOffset(uniform.offset),
+ mLocation(uniform.location),
+ mUniforms(uniforms),
+ mSamplerUniforms(samplerUniforms),
+ mImageUniforms(imageUniforms),
+ mAtomicCounterUniforms(atomicCounterUniforms),
+ mInputAttachmentUniforms(inputAttachmentUniforms),
+ mUnusedUniforms(unusedUniforms)
+ {}
+
+ void visitNamedOpaqueObject(const sh::ShaderVariable &variable,
+ const std::string &name,
+ const std::string &mappedName,
+ const std::vector<unsigned int> &arraySizes) override
+ {
+ visitNamedVariable(variable, false, name, mappedName, arraySizes);
+ }
+
+ void visitNamedVariable(const sh::ShaderVariable &variable,
+ bool isRowMajor,
+ const std::string &name,
+ const std::string &mappedName,
+ const std::vector<unsigned int> &arraySizes) override
+ {
+ bool isSampler = IsSamplerType(variable.type);
+ bool isImage = IsImageType(variable.type);
+ bool isAtomicCounter = IsAtomicCounterType(variable.type);
+ bool isFragmentInOut = variable.isFragmentInOut;
+ std::vector<LinkedUniform> *uniformList = mUniforms;
+ if (isSampler)
+ {
+ uniformList = mSamplerUniforms;
+ }
+ else if (isImage)
+ {
+ uniformList = mImageUniforms;
+ }
+ else if (isAtomicCounter)
+ {
+ uniformList = mAtomicCounterUniforms;
+ }
+ else if (isFragmentInOut)
+ {
+ uniformList = mInputAttachmentUniforms;
+ }
+
+ std::string fullNameWithArrayIndex(name);
+ std::string fullMappedNameWithArrayIndex(mappedName);
+
+ if (variable.isArray())
+ {
+ // We're following the GLES 3.1 November 2016 spec section 7.3.1.1 Naming Active
+ // Resources and including [0] at the end of array variable names.
+ fullNameWithArrayIndex += "[0]";
+ fullMappedNameWithArrayIndex += "[0]";
+ }
+
+ LinkedUniform *existingUniform = FindUniform(*uniformList, fullNameWithArrayIndex);
+ if (existingUniform)
+ {
+ if (getBinding() != -1)
+ {
+ existingUniform->binding = getBinding();
+ }
+ if (getOffset() != -1)
+ {
+ existingUniform->offset = getOffset();
+ }
+ if (mLocation != -1)
+ {
+ existingUniform->location = mLocation;
+ }
+ if (mMarkActive)
+ {
+ existingUniform->active = true;
+ existingUniform->setActive(mShaderType, true);
+ }
+ if (mMarkStaticUse)
+ {
+ existingUniform->staticUse = true;
+ }
+ }
+ else
+ {
+ LinkedUniform linkedUniform(variable.type, variable.precision, fullNameWithArrayIndex,
+ variable.arraySizes, getBinding(), getOffset(), mLocation,
+ -1, sh::kDefaultBlockMemberInfo);
+ linkedUniform.mappedName = fullMappedNameWithArrayIndex;
+ linkedUniform.active = mMarkActive;
+ linkedUniform.staticUse = mMarkStaticUse;
+ linkedUniform.outerArraySizes = arraySizes;
+ linkedUniform.texelFetchStaticUse = variable.texelFetchStaticUse;
+ linkedUniform.imageUnitFormat = variable.imageUnitFormat;
+ linkedUniform.isFragmentInOut = variable.isFragmentInOut;
+ if (variable.hasParentArrayIndex())
+ {
+ linkedUniform.setParentArrayIndex(variable.parentArrayIndex());
+ }
+
+ std::vector<unsigned int> arrayDims = arraySizes;
+ ASSERT(variable.arraySizes.size() == 1 || variable.arraySizes.size() == 0);
+ arrayDims.push_back(variable.arraySizes.empty() ? 1 : variable.arraySizes[0]);
+
+ size_t numDimensions = arraySizes.size();
+ uint32_t arrayStride = 1;
+ for (size_t dimension = numDimensions; dimension > 0;)
+ {
+ --dimension;
+ arrayStride *= arrayDims[dimension + 1];
+ linkedUniform.outerArrayOffset += arrayStride * mArrayElementStack[dimension];
+ }
+
+ if (mMarkActive)
+ {
+ linkedUniform.setActive(mShaderType, true);
+ }
+ else
+ {
+ mUnusedUniforms->emplace_back(
+ linkedUniform.name, linkedUniform.isSampler(), linkedUniform.isImage(),
+ linkedUniform.isAtomicCounter(), linkedUniform.isFragmentInOut);
+ }
+
+ uniformList->push_back(linkedUniform);
+ }
+
+ unsigned int elementCount = variable.getBasicTypeElementCount();
+
+ // Samplers and images aren't "real" uniforms, so they don't count towards register usage.
+ // Likewise, don't count "real" uniforms towards opaque count.
+
+ if (!IsOpaqueType(variable.type) && !isFragmentInOut)
+ {
+ mUniformCount.vectorCount += VariableRegisterCount(variable.type) * elementCount;
+ }
+
+ mUniformCount.samplerCount += (isSampler ? elementCount : 0);
+ mUniformCount.imageCount += (isImage ? elementCount : 0);
+ mUniformCount.atomicCounterCount += (isAtomicCounter ? elementCount : 0);
+ mUniformCount.fragmentInOutCount += (isFragmentInOut ? elementCount : 0);
+
+ if (mLocation != -1)
+ {
+ mLocation += elementCount;
+ }
+ }
+
+ void enterStructAccess(const sh::ShaderVariable &structVar, bool isRowMajor) override
+ {
+ mStructStackSize++;
+ sh::VariableNameVisitor::enterStructAccess(structVar, isRowMajor);
+ }
+
+ void exitStructAccess(const sh::ShaderVariable &structVar, bool isRowMajor) override
+ {
+ mStructStackSize--;
+ sh::VariableNameVisitor::exitStructAccess(structVar, isRowMajor);
+ }
+
+ void enterArrayElement(const sh::ShaderVariable &arrayVar, unsigned int arrayElement) override
+ {
+ mArrayElementStack.push_back(arrayElement);
+ sh::VariableNameVisitor::enterArrayElement(arrayVar, arrayElement);
+ }
+
+ void exitArrayElement(const sh::ShaderVariable &arrayVar, unsigned int arrayElement) override
+ {
+ mArrayElementStack.pop_back();
+ sh::VariableNameVisitor::exitArrayElement(arrayVar, arrayElement);
+ }
+
+ ShaderUniformCount getCounts() const { return mUniformCount; }
+
+ private:
+ int getBinding() const { return mStructStackSize == 0 ? mBinding : -1; }
+ int getOffset() const { return mStructStackSize == 0 ? mOffset : -1; }
+
+ ShaderType mShaderType;
+
+ // Active and StaticUse are given separately because they are tracked at struct granularity.
+ bool mMarkActive;
+ bool mMarkStaticUse;
+ int mBinding;
+ int mOffset;
+ int mLocation;
+ std::vector<LinkedUniform> *mUniforms;
+ std::vector<LinkedUniform> *mSamplerUniforms;
+ std::vector<LinkedUniform> *mImageUniforms;
+ std::vector<LinkedUniform> *mAtomicCounterUniforms;
+ std::vector<LinkedUniform> *mInputAttachmentUniforms;
+ std::vector<UnusedUniform> *mUnusedUniforms;
+ std::vector<unsigned int> mArrayElementStack;
+ ShaderUniformCount mUniformCount;
+ unsigned int mStructStackSize = 0;
+};
+
+class InterfaceBlockInfo final : angle::NonCopyable
+{
+ public:
+ InterfaceBlockInfo(CustomBlockLayoutEncoderFactory *customEncoderFactory)
+ : mCustomEncoderFactory(customEncoderFactory)
+ {}
+
+ void getShaderBlockInfo(const std::vector<sh::InterfaceBlock> &interfaceBlocks);
+
+ bool getBlockSize(const std::string &name, const std::string &mappedName, size_t *sizeOut);
+ bool getBlockMemberInfo(const std::string &name,
+ const std::string &mappedName,
+ sh::BlockMemberInfo *infoOut);
+
+ private:
+ size_t getBlockInfo(const sh::InterfaceBlock &interfaceBlock);
+
+ std::map<std::string, size_t> mBlockSizes;
+ sh::BlockLayoutMap mBlockLayout;
+ // Based on the interface block layout, the std140 or std430 encoders are used. On some
+ // platforms (currently only D3D), there could be another non-standard encoder used.
+ CustomBlockLayoutEncoderFactory *mCustomEncoderFactory;
+};
+
+void InterfaceBlockInfo::getShaderBlockInfo(const std::vector<sh::InterfaceBlock> &interfaceBlocks)
+{
+ for (const sh::InterfaceBlock &interfaceBlock : interfaceBlocks)
+ {
+ if (!IsActiveInterfaceBlock(interfaceBlock))
+ continue;
+
+ if (mBlockSizes.count(interfaceBlock.name) > 0)
+ continue;
+
+ size_t dataSize = getBlockInfo(interfaceBlock);
+ mBlockSizes[interfaceBlock.name] = dataSize;
+ }
+}
+
+size_t InterfaceBlockInfo::getBlockInfo(const sh::InterfaceBlock &interfaceBlock)
+{
+ ASSERT(IsActiveInterfaceBlock(interfaceBlock));
+
+ // define member uniforms
+ sh::Std140BlockEncoder std140Encoder;
+ sh::Std430BlockEncoder std430Encoder;
+ sh::BlockLayoutEncoder *customEncoder = nullptr;
+ sh::BlockLayoutEncoder *encoder = nullptr;
+
+ if (interfaceBlock.layout == sh::BLOCKLAYOUT_STD140)
+ {
+ encoder = &std140Encoder;
+ }
+ else if (interfaceBlock.layout == sh::BLOCKLAYOUT_STD430)
+ {
+ encoder = &std430Encoder;
+ }
+ else if (mCustomEncoderFactory)
+ {
+ encoder = customEncoder = mCustomEncoderFactory->makeEncoder();
+ }
+ else
+ {
+ UNREACHABLE();
+ return 0;
+ }
+
+ sh::GetInterfaceBlockInfo(interfaceBlock.fields, interfaceBlock.fieldPrefix(), encoder,
+ &mBlockLayout);
+
+ size_t offset = encoder->getCurrentOffset();
+
+ SafeDelete(customEncoder);
+
+ return offset;
+}
+
+bool InterfaceBlockInfo::getBlockSize(const std::string &name,
+ const std::string &mappedName,
+ size_t *sizeOut)
+{
+ size_t nameLengthWithoutArrayIndex;
+ ParseArrayIndex(name, &nameLengthWithoutArrayIndex);
+ std::string baseName = name.substr(0u, nameLengthWithoutArrayIndex);
+ auto sizeIter = mBlockSizes.find(baseName);
+ if (sizeIter == mBlockSizes.end())
+ {
+ *sizeOut = 0;
+ return false;
+ }
+
+ *sizeOut = sizeIter->second;
+ return true;
+}
+
+bool InterfaceBlockInfo::getBlockMemberInfo(const std::string &name,
+ const std::string &mappedName,
+ sh::BlockMemberInfo *infoOut)
+{
+ auto infoIter = mBlockLayout.find(name);
+ if (infoIter == mBlockLayout.end())
+ {
+ *infoOut = sh::kDefaultBlockMemberInfo;
+ return false;
+ }
+
+ *infoOut = infoIter->second;
+ return true;
+}
+
+void GetFilteredVaryings(const std::vector<sh::ShaderVariable> &varyings,
+ std::vector<const sh::ShaderVariable *> *filteredVaryingsOut)
+{
+ for (const sh::ShaderVariable &varying : varyings)
+ {
+ // Built-in varyings obey special rules
+ if (varying.isBuiltIn())
+ {
+ continue;
+ }
+
+ filteredVaryingsOut->push_back(&varying);
+ }
+}
+
+LinkMismatchError LinkValidateVaryings(const sh::ShaderVariable &outputVarying,
+ const sh::ShaderVariable &inputVarying,
+ int shaderVersion,
+ ShaderType frontShaderType,
+ ShaderType backShaderType,
+ bool isSeparable,
+ std::string *mismatchedStructFieldName)
+{
+ // [ES 3.2 spec] 7.4.1 Shader Interface Matching:
+ // Tessellation control shader per-vertex output variables and blocks and tessellation control,
+ // tessellation evaluation, and geometry shader per-vertex input variables and blocks are
+ // required to be declared as arrays, with each element representing input or output values for
+ // a single vertex of a multi-vertex primitive. For the purposes of interface matching, such
+ // variables and blocks are treated as though they were not declared as arrays.
+ bool treatOutputAsNonArray =
+ (frontShaderType == ShaderType::TessControl && !outputVarying.isPatch);
+ bool treatInputAsNonArray =
+ ((backShaderType == ShaderType::TessControl ||
+ backShaderType == ShaderType::TessEvaluation || backShaderType == ShaderType::Geometry) &&
+ !inputVarying.isPatch);
+
+ // Skip the validation on the array sizes between a vertex output varying and a geometry input
+ // varying as it has been done before.
+ bool validatePrecision = isSeparable && (shaderVersion > 100);
+ LinkMismatchError linkError = LinkValidateProgramVariables(
+ outputVarying, inputVarying, validatePrecision, treatOutputAsNonArray, treatInputAsNonArray,
+ mismatchedStructFieldName);
+ if (linkError != LinkMismatchError::NO_MISMATCH)
+ {
+ return linkError;
+ }
+
+ // Explicit locations must match if the names match.
+ if (outputVarying.isSameNameAtLinkTime(inputVarying) &&
+ outputVarying.location != inputVarying.location)
+ {
+ return LinkMismatchError::LOCATION_MISMATCH;
+ }
+
+ if (!sh::InterpolationTypesMatch(outputVarying.interpolation, inputVarying.interpolation))
+ {
+ return LinkMismatchError::INTERPOLATION_TYPE_MISMATCH;
+ }
+
+ if (shaderVersion == 100 && outputVarying.isInvariant != inputVarying.isInvariant)
+ {
+ return LinkMismatchError::INVARIANCE_MISMATCH;
+ }
+
+ return LinkMismatchError::NO_MISMATCH;
+}
+
+bool DoShaderVariablesMatch(int frontShaderVersion,
+ ShaderType frontShaderType,
+ ShaderType backShaderType,
+ const sh::ShaderVariable &input,
+ const sh::ShaderVariable &output,
+ bool isSeparable,
+ gl::InfoLog &infoLog)
+{
+ bool namesMatch = input.isSameNameAtLinkTime(output);
+ bool locationsMatch = input.location != -1 && input.location == output.location;
+
+ // An output block is considered to match an input block in the subsequent
+ // shader if the two blocks have the same block name, and the members of the
+ // block match exactly in name, type, qualification, and declaration order.
+ //
+ // - For the purposes of shader interface matching, the gl_PointSize
+ // member of the intrinsically declared gl_PerVertex shader interface
+ // block is ignored.
+ // - Output blocks that do not match in name, but have a location and match
+ // in every other way listed above may be considered to match by some
+ // implementations, but not all - so this behaviour should not be relied
+ // upon.
+
+ // An output variable is considered to match an input variable in the subsequent
+ // shader if:
+ //
+ // - the two variables match in name, type, and qualification; or
+ // - the two variables are declared with the same location qualifier and
+ // match in type and qualification.
+
+ if (namesMatch || locationsMatch)
+ {
+ std::string mismatchedStructFieldName;
+ LinkMismatchError linkError =
+ LinkValidateVaryings(output, input, frontShaderVersion, frontShaderType, backShaderType,
+ isSeparable, &mismatchedStructFieldName);
+ if (linkError != LinkMismatchError::NO_MISMATCH)
+ {
+ LogLinkMismatch(infoLog, input.name, "varying", linkError, mismatchedStructFieldName,
+ frontShaderType, backShaderType);
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+const char *GetInterfaceBlockTypeString(sh::BlockType blockType)
+{
+ switch (blockType)
+ {
+ case sh::BlockType::BLOCK_UNIFORM:
+ return "uniform block";
+ case sh::BlockType::BLOCK_BUFFER:
+ return "shader storage block";
+ default:
+ UNREACHABLE();
+ return "";
+ }
+}
+
+std::string GetInterfaceBlockLimitName(ShaderType shaderType, sh::BlockType blockType)
+{
+ std::ostringstream stream;
+ stream << "GL_MAX_" << GetShaderTypeString(shaderType) << "_";
+
+ switch (blockType)
+ {
+ case sh::BlockType::BLOCK_UNIFORM:
+ stream << "UNIFORM_BUFFERS";
+ break;
+ case sh::BlockType::BLOCK_BUFFER:
+ stream << "SHADER_STORAGE_BLOCKS";
+ break;
+ default:
+ UNREACHABLE();
+ return "";
+ }
+
+ if (shaderType == ShaderType::Geometry)
+ {
+ stream << "_EXT";
+ }
+
+ return stream.str();
+}
+
+void LogInterfaceBlocksExceedLimit(InfoLog &infoLog,
+ ShaderType shaderType,
+ sh::BlockType blockType,
+ GLuint limit)
+{
+ infoLog << GetShaderTypeString(shaderType) << " shader "
+ << GetInterfaceBlockTypeString(blockType) << " count exceeds "
+ << GetInterfaceBlockLimitName(shaderType, blockType) << " (" << limit << ")";
+}
+
+bool ValidateInterfaceBlocksCount(GLuint maxInterfaceBlocks,
+ const std::vector<sh::InterfaceBlock> &interfaceBlocks,
+ ShaderType shaderType,
+ sh::BlockType blockType,
+ GLuint *combinedInterfaceBlocksCount,
+ InfoLog &infoLog)
+{
+ GLuint blockCount = 0;
+ for (const sh::InterfaceBlock &block : interfaceBlocks)
+ {
+ if (IsActiveInterfaceBlock(block))
+ {
+ blockCount += std::max(block.arraySize, 1u);
+ if (blockCount > maxInterfaceBlocks)
+ {
+ LogInterfaceBlocksExceedLimit(infoLog, shaderType, blockType, maxInterfaceBlocks);
+ return false;
+ }
+ }
+ }
+
+ // [OpenGL ES 3.1] Chapter 7.6.2 Page 105:
+ // If a uniform block is used by multiple shader stages, each such use counts separately
+ // against this combined limit.
+ // [OpenGL ES 3.1] Chapter 7.8 Page 111:
+ // If a shader storage block in a program is referenced by multiple shaders, each such
+ // reference counts separately against this combined limit.
+ if (combinedInterfaceBlocksCount)
+ {
+ *combinedInterfaceBlocksCount += blockCount;
+ }
+
+ return true;
+}
+} // anonymous namespace
+
+UniformLinker::UniformLinker(const ShaderBitSet &activeShaderStages,
+ const ShaderMap<std::vector<sh::ShaderVariable>> &shaderUniforms)
+ : mActiveShaderStages(activeShaderStages), mShaderUniforms(shaderUniforms)
+{}
+
+UniformLinker::~UniformLinker() = default;
+
+void UniformLinker::getResults(std::vector<LinkedUniform> *uniforms,
+ std::vector<UnusedUniform> *unusedUniformsOutOrNull,
+ std::vector<VariableLocation> *uniformLocationsOutOrNull)
+{
+ uniforms->swap(mUniforms);
+
+ if (unusedUniformsOutOrNull)
+ {
+ unusedUniformsOutOrNull->swap(mUnusedUniforms);
+ }
+
+ if (uniformLocationsOutOrNull)
+ {
+ uniformLocationsOutOrNull->swap(mUniformLocations);
+ }
+}
+
+bool UniformLinker::link(const Caps &caps,
+ InfoLog &infoLog,
+ const ProgramAliasedBindings &uniformLocationBindings)
+{
+ if (mActiveShaderStages[ShaderType::Vertex] && mActiveShaderStages[ShaderType::Fragment])
+ {
+ if (!validateGraphicsUniforms(infoLog))
+ {
+ return false;
+ }
+ }
+
+ // Flatten the uniforms list (nested fields) into a simple list (no nesting).
+ // Also check the maximum uniform vector and sampler counts.
+ if (!flattenUniformsAndCheckCaps(caps, infoLog))
+ {
+ return false;
+ }
+
+ if (!checkMaxCombinedAtomicCounters(caps, infoLog))
+ {
+ return false;
+ }
+
+ if (!indexUniforms(infoLog, uniformLocationBindings))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool UniformLinker::validateGraphicsUniforms(InfoLog &infoLog) const
+{
+ // Check that uniforms defined in the graphics shaders are identical
+ std::map<std::string, ShaderUniform> linkedUniforms;
+
+ for (const ShaderType shaderType : mActiveShaderStages)
+ {
+ if (shaderType == ShaderType::Vertex)
+ {
+ for (const sh::ShaderVariable &vertexUniform : mShaderUniforms[ShaderType::Vertex])
+ {
+ linkedUniforms[vertexUniform.name] =
+ std::make_pair(ShaderType::Vertex, &vertexUniform);
+ }
+ }
+ else
+ {
+ bool isLastShader = (shaderType == ShaderType::Fragment);
+ if (!validateGraphicsUniformsPerShader(shaderType, !isLastShader, &linkedUniforms,
+ infoLog))
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool UniformLinker::validateGraphicsUniformsPerShader(
+ ShaderType shaderToLink,
+ bool extendLinkedUniforms,
+ std::map<std::string, ShaderUniform> *linkedUniforms,
+ InfoLog &infoLog) const
+{
+ ASSERT(mActiveShaderStages[shaderToLink] && linkedUniforms);
+
+ for (const sh::ShaderVariable &uniform : mShaderUniforms[shaderToLink])
+ {
+ const auto &entry = linkedUniforms->find(uniform.name);
+ if (entry != linkedUniforms->end())
+ {
+ const sh::ShaderVariable &linkedUniform = *(entry->second.second);
+ std::string mismatchedStructFieldName;
+ LinkMismatchError linkError =
+ LinkValidateUniforms(uniform, linkedUniform, &mismatchedStructFieldName);
+ if (linkError != LinkMismatchError::NO_MISMATCH)
+ {
+ LogLinkMismatch(infoLog, uniform.name, "uniform", linkError,
+ mismatchedStructFieldName, entry->second.first, shaderToLink);
+ return false;
+ }
+ }
+ else if (extendLinkedUniforms)
+ {
+ (*linkedUniforms)[uniform.name] = std::make_pair(shaderToLink, &uniform);
+ }
+ }
+
+ return true;
+}
+
+bool UniformLinker::indexUniforms(InfoLog &infoLog,
+ const ProgramAliasedBindings &uniformLocationBindings)
+{
+ // Locations which have been allocated for an unused uniform.
+ std::set<GLuint> ignoredLocations;
+
+ int maxUniformLocation = -1;
+
+ // Gather uniform locations that have been set either using the bindUniformLocationCHROMIUM API
+ // or by using a location layout qualifier and check conflicts between them.
+ if (!gatherUniformLocationsAndCheckConflicts(infoLog, uniformLocationBindings,
+ &ignoredLocations, &maxUniformLocation))
+ {
+ return false;
+ }
+
+ // Conflicts have been checked, now we can prune non-statically used uniforms. Code further down
+ // the line relies on only having statically used uniforms in mUniforms.
+ pruneUnusedUniforms();
+
+ // Gather uniforms that have their location pre-set and uniforms that don't yet have a location.
+ std::vector<VariableLocation> unlocatedUniforms;
+ std::map<GLuint, VariableLocation> preLocatedUniforms;
+
+ for (size_t uniformIndex = 0; uniformIndex < mUniforms.size(); uniformIndex++)
+ {
+ const LinkedUniform &uniform = mUniforms[uniformIndex];
+
+ if ((uniform.isBuiltIn() && !uniform.isEmulatedBuiltIn()) ||
+ IsAtomicCounterType(uniform.type) || uniform.isFragmentInOut)
+ {
+ continue;
+ }
+
+ int preSetLocation = uniformLocationBindings.getBinding(uniform);
+ int shaderLocation = uniform.location;
+
+ if (shaderLocation != -1)
+ {
+ preSetLocation = shaderLocation;
+ }
+
+ unsigned int elementCount = uniform.getBasicTypeElementCount();
+ for (unsigned int arrayIndex = 0; arrayIndex < elementCount; arrayIndex++)
+ {
+ VariableLocation location(arrayIndex, static_cast<unsigned int>(uniformIndex));
+
+ if ((arrayIndex == 0 && preSetLocation != -1) || shaderLocation != -1)
+ {
+ int elementLocation = preSetLocation + arrayIndex;
+ preLocatedUniforms[elementLocation] = location;
+ }
+ else
+ {
+ unlocatedUniforms.push_back(location);
+ }
+ }
+ }
+
+ // Make enough space for all uniforms, with pre-set locations or not.
+ mUniformLocations.resize(
+ std::max(unlocatedUniforms.size() + preLocatedUniforms.size() + ignoredLocations.size(),
+ static_cast<size_t>(maxUniformLocation + 1)));
+
+ // Assign uniforms with pre-set locations
+ for (const auto &uniform : preLocatedUniforms)
+ {
+ mUniformLocations[uniform.first] = uniform.second;
+ }
+
+ // Assign ignored uniforms
+ for (const auto &ignoredLocation : ignoredLocations)
+ {
+ mUniformLocations[ignoredLocation].markIgnored();
+ }
+
+ // Automatically assign locations for the rest of the uniforms
+ size_t nextUniformLocation = 0;
+ for (const auto &unlocatedUniform : unlocatedUniforms)
+ {
+ while (mUniformLocations[nextUniformLocation].used() ||
+ mUniformLocations[nextUniformLocation].ignored)
+ {
+ nextUniformLocation++;
+ }
+
+ ASSERT(nextUniformLocation < mUniformLocations.size());
+ mUniformLocations[nextUniformLocation] = unlocatedUniform;
+ nextUniformLocation++;
+ }
+
+ return true;
+}
+
+bool UniformLinker::gatherUniformLocationsAndCheckConflicts(
+ InfoLog &infoLog,
+ const ProgramAliasedBindings &uniformLocationBindings,
+ std::set<GLuint> *ignoredLocations,
+ int *maxUniformLocation)
+{
+ // All the locations where another uniform can't be located.
+ std::set<GLuint> reservedLocations;
+
+ for (const LinkedUniform &uniform : mUniforms)
+ {
+ if ((uniform.isBuiltIn() && !uniform.isEmulatedBuiltIn()) || uniform.isFragmentInOut)
+ {
+ // The uniform of the fragment inout is not a normal uniform type. So, in the case of
+ // the fragment inout, this routine should be skipped.
+ continue;
+ }
+
+ int apiBoundLocation = uniformLocationBindings.getBinding(uniform);
+ int shaderLocation = uniform.location;
+
+ if (shaderLocation != -1)
+ {
+ unsigned int elementCount = uniform.getBasicTypeElementCount();
+
+ for (unsigned int arrayIndex = 0; arrayIndex < elementCount; arrayIndex++)
+ {
+ // GLSL ES 3.10 section 4.4.3
+ int elementLocation = shaderLocation + arrayIndex;
+ *maxUniformLocation = std::max(*maxUniformLocation, elementLocation);
+ if (reservedLocations.find(elementLocation) != reservedLocations.end())
+ {
+ infoLog << "Multiple uniforms bound to location " << elementLocation << ".";
+ return false;
+ }
+ reservedLocations.insert(elementLocation);
+ if (!uniform.active)
+ {
+ ignoredLocations->insert(elementLocation);
+ }
+ }
+ }
+ else if (apiBoundLocation != -1 && uniform.staticUse)
+ {
+ // Only the first location is reserved even if the uniform is an array.
+ *maxUniformLocation = std::max(*maxUniformLocation, apiBoundLocation);
+ if (reservedLocations.find(apiBoundLocation) != reservedLocations.end())
+ {
+ infoLog << "Multiple uniforms bound to location " << apiBoundLocation << ".";
+ return false;
+ }
+ reservedLocations.insert(apiBoundLocation);
+ if (!uniform.active)
+ {
+ ignoredLocations->insert(apiBoundLocation);
+ }
+ }
+ }
+
+ // Record the uniform locations that were bound using the API for uniforms that were not found
+ // from the shader. Other uniforms should not be assigned to those locations.
+ for (const auto &locationBinding : uniformLocationBindings)
+ {
+ GLuint location = locationBinding.second.location;
+ if (reservedLocations.find(location) == reservedLocations.end())
+ {
+ ignoredLocations->insert(location);
+ *maxUniformLocation = std::max(*maxUniformLocation, static_cast<int>(location));
+ }
+ }
+
+ return true;
+}
+
+void UniformLinker::pruneUnusedUniforms()
+{
+ auto uniformIter = mUniforms.begin();
+ while (uniformIter != mUniforms.end())
+ {
+ if (uniformIter->active)
+ {
+ ++uniformIter;
+ }
+ else
+ {
+ mUnusedUniforms.emplace_back(uniformIter->name, uniformIter->isSampler(),
+ uniformIter->isImage(), uniformIter->isAtomicCounter(),
+ uniformIter->isFragmentInOut);
+ uniformIter = mUniforms.erase(uniformIter);
+ }
+ }
+}
+
+bool UniformLinker::flattenUniformsAndCheckCapsForShader(
+ ShaderType shaderType,
+ const Caps &caps,
+ std::vector<LinkedUniform> &samplerUniforms,
+ std::vector<LinkedUniform> &imageUniforms,
+ std::vector<LinkedUniform> &atomicCounterUniforms,
+ std::vector<LinkedUniform> &inputAttachmentUniforms,
+ std::vector<UnusedUniform> &unusedUniforms,
+ InfoLog &infoLog)
+{
+ ShaderUniformCount shaderUniformCount;
+ for (const sh::ShaderVariable &uniform : mShaderUniforms[shaderType])
+ {
+ FlattenUniformVisitor flattener(shaderType, uniform, &mUniforms, &samplerUniforms,
+ &imageUniforms, &atomicCounterUniforms,
+ &inputAttachmentUniforms, &unusedUniforms);
+ sh::TraverseShaderVariable(uniform, false, &flattener);
+
+ if (uniform.active)
+ {
+ shaderUniformCount += flattener.getCounts();
+ }
+ else
+ {
+ unusedUniforms.emplace_back(uniform.name, IsSamplerType(uniform.type),
+ IsImageType(uniform.type),
+ IsAtomicCounterType(uniform.type), uniform.isFragmentInOut);
+ }
+ }
+
+ // This code does not do fine-grained component counting.
+ GLuint maxUniformVectorsCount = GetMaximumShaderUniformVectors(shaderType, caps);
+ if (shaderUniformCount.vectorCount > maxUniformVectorsCount)
+ {
+ GLuint maxUniforms = 0u;
+
+ // See comments in GetUniformResourceLimitName()
+ if (shaderType == ShaderType::Vertex || shaderType == ShaderType::Fragment)
+ {
+ maxUniforms = maxUniformVectorsCount;
+ }
+ else
+ {
+ maxUniforms = maxUniformVectorsCount * 4;
+ }
+
+ LogUniformsExceedLimit(shaderType, UniformType::Variable, maxUniforms, infoLog);
+ return false;
+ }
+
+ if (shaderUniformCount.samplerCount >
+ static_cast<GLuint>(caps.maxShaderTextureImageUnits[shaderType]))
+ {
+ LogUniformsExceedLimit(shaderType, UniformType::Sampler,
+ caps.maxShaderTextureImageUnits[shaderType], infoLog);
+ return false;
+ }
+
+ if (shaderUniformCount.imageCount >
+ static_cast<GLuint>(caps.maxShaderImageUniforms[shaderType]))
+ {
+ LogUniformsExceedLimit(shaderType, UniformType::Image,
+ caps.maxShaderImageUniforms[shaderType], infoLog);
+ return false;
+ }
+
+ if (shaderUniformCount.atomicCounterCount >
+ static_cast<GLuint>(caps.maxShaderAtomicCounters[shaderType]))
+ {
+ LogUniformsExceedLimit(shaderType, UniformType::AtomicCounter,
+ caps.maxShaderAtomicCounters[shaderType], infoLog);
+ return false;
+ }
+
+ return true;
+}
+
+bool UniformLinker::flattenUniformsAndCheckCaps(const Caps &caps, InfoLog &infoLog)
+{
+ std::vector<LinkedUniform> samplerUniforms;
+ std::vector<LinkedUniform> imageUniforms;
+ std::vector<LinkedUniform> atomicCounterUniforms;
+ std::vector<LinkedUniform> inputAttachmentUniforms;
+ std::vector<UnusedUniform> unusedUniforms;
+
+ for (const ShaderType shaderType : mActiveShaderStages)
+ {
+ if (!flattenUniformsAndCheckCapsForShader(shaderType, caps, samplerUniforms, imageUniforms,
+ atomicCounterUniforms, inputAttachmentUniforms,
+ unusedUniforms, infoLog))
+ {
+ return false;
+ }
+ }
+
+ mUniforms.insert(mUniforms.end(), samplerUniforms.begin(), samplerUniforms.end());
+ mUniforms.insert(mUniforms.end(), imageUniforms.begin(), imageUniforms.end());
+ mUniforms.insert(mUniforms.end(), atomicCounterUniforms.begin(), atomicCounterUniforms.end());
+ mUniforms.insert(mUniforms.end(), inputAttachmentUniforms.begin(),
+ inputAttachmentUniforms.end());
+ mUnusedUniforms.insert(mUnusedUniforms.end(), unusedUniforms.begin(), unusedUniforms.end());
+ return true;
+}
+
+bool UniformLinker::checkMaxCombinedAtomicCounters(const Caps &caps, InfoLog &infoLog)
+{
+ unsigned int atomicCounterCount = 0;
+ for (const auto &uniform : mUniforms)
+ {
+ if (IsAtomicCounterType(uniform.type) && uniform.active)
+ {
+ atomicCounterCount += uniform.getBasicTypeElementCount();
+ if (atomicCounterCount > static_cast<GLuint>(caps.maxCombinedAtomicCounters))
+ {
+ infoLog << "atomic counter count exceeds MAX_COMBINED_ATOMIC_COUNTERS"
+ << caps.maxCombinedAtomicCounters << ").";
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+// InterfaceBlockLinker implementation.
+InterfaceBlockLinker::InterfaceBlockLinker() = default;
+
+InterfaceBlockLinker::~InterfaceBlockLinker() = default;
+
+void InterfaceBlockLinker::init(std::vector<InterfaceBlock> *blocksOut,
+ std::vector<std::string> *unusedInterfaceBlocksOut)
+{
+ mBlocksOut = blocksOut;
+ mUnusedInterfaceBlocksOut = unusedInterfaceBlocksOut;
+}
+
+void InterfaceBlockLinker::addShaderBlocks(ShaderType shaderType,
+ const std::vector<sh::InterfaceBlock> *blocks)
+{
+ mShaderBlocks[shaderType] = blocks;
+}
+
+void InterfaceBlockLinker::linkBlocks(const GetBlockSizeFunc &getBlockSize,
+ const GetBlockMemberInfoFunc &getMemberInfo) const
+{
+ ASSERT(mBlocksOut->empty());
+
+ std::set<std::string> visitedList;
+
+ for (const ShaderType shaderType : AllShaderTypes())
+ {
+ if (!mShaderBlocks[shaderType])
+ {
+ continue;
+ }
+
+ for (const sh::InterfaceBlock &block : *mShaderBlocks[shaderType])
+ {
+ if (!IsActiveInterfaceBlock(block))
+ {
+ mUnusedInterfaceBlocksOut->push_back(block.name);
+ continue;
+ }
+
+ if (visitedList.count(block.name) == 0)
+ {
+ defineInterfaceBlock(getBlockSize, getMemberInfo, block, shaderType);
+ visitedList.insert(block.name);
+ continue;
+ }
+
+ if (!block.active)
+ {
+ mUnusedInterfaceBlocksOut->push_back(block.name);
+ continue;
+ }
+
+ for (InterfaceBlock &priorBlock : *mBlocksOut)
+ {
+ if (block.name == priorBlock.name)
+ {
+ priorBlock.setActive(shaderType, true);
+
+ std::unique_ptr<sh::ShaderVariableVisitor> visitor(
+ getVisitor(getMemberInfo, block.fieldPrefix(), block.fieldMappedPrefix(),
+ shaderType, -1));
+
+ sh::TraverseShaderVariables(block.fields, false, visitor.get());
+ }
+ }
+ }
+ }
+}
+
+void InterfaceBlockLinker::defineInterfaceBlock(const GetBlockSizeFunc &getBlockSize,
+ const GetBlockMemberInfoFunc &getMemberInfo,
+ const sh::InterfaceBlock &interfaceBlock,
+ ShaderType shaderType) const
+{
+ size_t blockSize = 0;
+ std::vector<unsigned int> blockIndexes;
+
+ int blockIndex = static_cast<int>(mBlocksOut->size());
+ // Track the first and last block member index to determine the range of active block members in
+ // the block.
+ size_t firstBlockMemberIndex = getCurrentBlockMemberIndex();
+
+ std::unique_ptr<sh::ShaderVariableVisitor> visitor(
+ getVisitor(getMemberInfo, interfaceBlock.fieldPrefix(), interfaceBlock.fieldMappedPrefix(),
+ shaderType, blockIndex));
+ sh::TraverseShaderVariables(interfaceBlock.fields, false, visitor.get());
+
+ size_t lastBlockMemberIndex = getCurrentBlockMemberIndex();
+
+ for (size_t blockMemberIndex = firstBlockMemberIndex; blockMemberIndex < lastBlockMemberIndex;
+ ++blockMemberIndex)
+ {
+ blockIndexes.push_back(static_cast<unsigned int>(blockMemberIndex));
+ }
+
+ unsigned int firstFieldArraySize = interfaceBlock.fields[0].getArraySizeProduct();
+
+ for (unsigned int arrayElement = 0; arrayElement < interfaceBlock.elementCount();
+ ++arrayElement)
+ {
+ std::string blockArrayName = interfaceBlock.name;
+ std::string blockMappedArrayName = interfaceBlock.mappedName;
+ if (interfaceBlock.isArray())
+ {
+ blockArrayName += ArrayString(arrayElement);
+ blockMappedArrayName += ArrayString(arrayElement);
+ }
+
+ // Don't define this block at all if it's not active in the implementation.
+ if (!getBlockSize(blockArrayName, blockMappedArrayName, &blockSize))
+ {
+ continue;
+ }
+
+ // ESSL 3.10 section 4.4.4 page 58:
+ // Any uniform or shader storage block declared without a binding qualifier is initially
+ // assigned to block binding point zero.
+ int blockBinding =
+ (interfaceBlock.binding == -1 ? 0 : interfaceBlock.binding + arrayElement);
+ InterfaceBlock block(interfaceBlock.name, interfaceBlock.mappedName,
+ interfaceBlock.isArray(), arrayElement, firstFieldArraySize,
+ blockBinding);
+ block.memberIndexes = blockIndexes;
+ block.setActive(shaderType, interfaceBlock.active);
+
+ // Since all block elements in an array share the same active interface blocks, they
+ // will all be active once any block member is used. So, since interfaceBlock.name[0]
+ // was active, here we will add every block element in the array.
+ block.dataSize = static_cast<unsigned int>(blockSize);
+ mBlocksOut->push_back(block);
+ }
+}
+
+// UniformBlockLinker implementation.
+UniformBlockLinker::UniformBlockLinker() = default;
+
+UniformBlockLinker::~UniformBlockLinker() {}
+
+void UniformBlockLinker::init(std::vector<InterfaceBlock> *blocksOut,
+ std::vector<LinkedUniform> *uniformsOut,
+ std::vector<std::string> *unusedInterfaceBlocksOut)
+{
+ InterfaceBlockLinker::init(blocksOut, unusedInterfaceBlocksOut);
+ mUniformsOut = uniformsOut;
+}
+
+size_t UniformBlockLinker::getCurrentBlockMemberIndex() const
+{
+ return mUniformsOut->size();
+}
+
+sh::ShaderVariableVisitor *UniformBlockLinker::getVisitor(
+ const GetBlockMemberInfoFunc &getMemberInfo,
+ const std::string &namePrefix,
+ const std::string &mappedNamePrefix,
+ ShaderType shaderType,
+ int blockIndex) const
+{
+ return new UniformBlockEncodingVisitor(getMemberInfo, namePrefix, mappedNamePrefix,
+ mUniformsOut, shaderType, blockIndex);
+}
+
+// ShaderStorageBlockLinker implementation.
+ShaderStorageBlockLinker::ShaderStorageBlockLinker() = default;
+
+ShaderStorageBlockLinker::~ShaderStorageBlockLinker() = default;
+
+void ShaderStorageBlockLinker::init(std::vector<InterfaceBlock> *blocksOut,
+ std::vector<BufferVariable> *bufferVariablesOut,
+ std::vector<std::string> *unusedInterfaceBlocksOut)
+{
+ InterfaceBlockLinker::init(blocksOut, unusedInterfaceBlocksOut);
+ mBufferVariablesOut = bufferVariablesOut;
+}
+
+size_t ShaderStorageBlockLinker::getCurrentBlockMemberIndex() const
+{
+ return mBufferVariablesOut->size();
+}
+
+sh::ShaderVariableVisitor *ShaderStorageBlockLinker::getVisitor(
+ const GetBlockMemberInfoFunc &getMemberInfo,
+ const std::string &namePrefix,
+ const std::string &mappedNamePrefix,
+ ShaderType shaderType,
+ int blockIndex) const
+{
+ return new ShaderStorageBlockVisitor(getMemberInfo, namePrefix, mappedNamePrefix,
+ mBufferVariablesOut, shaderType, blockIndex);
+}
+
+// AtomicCounterBufferLinker implementation.
+AtomicCounterBufferLinker::AtomicCounterBufferLinker() = default;
+
+AtomicCounterBufferLinker::~AtomicCounterBufferLinker() = default;
+
+void AtomicCounterBufferLinker::init(std::vector<AtomicCounterBuffer> *atomicCounterBuffersOut)
+{
+ mAtomicCounterBuffersOut = atomicCounterBuffersOut;
+}
+
+void AtomicCounterBufferLinker::link(const std::map<int, unsigned int> &sizeMap) const
+{
+ for (auto &atomicCounterBuffer : *mAtomicCounterBuffersOut)
+ {
+ auto bufferSize = sizeMap.find(atomicCounterBuffer.binding);
+ ASSERT(bufferSize != sizeMap.end());
+ atomicCounterBuffer.dataSize = bufferSize->second;
+ }
+}
+
+ProgramLinkedResources::ProgramLinkedResources() = default;
+
+ProgramLinkedResources::~ProgramLinkedResources() = default;
+
+LinkingVariables::LinkingVariables(const Context *context, const ProgramState &state)
+{
+ for (ShaderType shaderType : kAllGraphicsShaderTypes)
+ {
+ Shader *shader = state.getAttachedShader(shaderType);
+ if (shader)
+ {
+ outputVaryings[shaderType] = shader->getOutputVaryings(context);
+ inputVaryings[shaderType] = shader->getInputVaryings(context);
+ uniforms[shaderType] = shader->getUniforms(context);
+ uniformBlocks[shaderType] = shader->getUniformBlocks(context);
+ isShaderStageUsedBitset.set(shaderType);
+ }
+ }
+}
+
+LinkingVariables::LinkingVariables(const ProgramPipelineState &state)
+{
+ for (ShaderType shaderType : state.getExecutable().getLinkedShaderStages())
+ {
+ const Program *program = state.getShaderProgram(shaderType);
+ ASSERT(program);
+ outputVaryings[shaderType] = program->getExecutable().getLinkedOutputVaryings(shaderType);
+ inputVaryings[shaderType] = program->getExecutable().getLinkedInputVaryings(shaderType);
+ uniforms[shaderType] = program->getState().getExecutable().getLinkedUniforms(shaderType);
+ uniformBlocks[shaderType] =
+ program->getState().getExecutable().getLinkedUniformBlocks(shaderType);
+ isShaderStageUsedBitset.set(shaderType);
+ }
+}
+
+LinkingVariables::~LinkingVariables() = default;
+
+void ProgramLinkedResources::init(std::vector<InterfaceBlock> *uniformBlocksOut,
+ std::vector<LinkedUniform> *uniformsOut,
+ std::vector<InterfaceBlock> *shaderStorageBlocksOut,
+ std::vector<BufferVariable> *bufferVariablesOut,
+ std::vector<AtomicCounterBuffer> *atomicCounterBuffersOut)
+{
+ uniformBlockLinker.init(uniformBlocksOut, uniformsOut, &unusedInterfaceBlocks);
+ shaderStorageBlockLinker.init(shaderStorageBlocksOut, bufferVariablesOut,
+ &unusedInterfaceBlocks);
+ atomicCounterBufferLinker.init(atomicCounterBuffersOut);
+}
+
+void ProgramLinkedResourcesLinker::linkResources(const Context *context,
+ const ProgramState &programState,
+ const ProgramLinkedResources &resources) const
+{
+ // Gather uniform interface block info.
+ InterfaceBlockInfo uniformBlockInfo(mCustomEncoderFactory);
+ for (const ShaderType shaderType : AllShaderTypes())
+ {
+ Shader *shader = programState.getAttachedShader(shaderType);
+ if (shader)
+ {
+ uniformBlockInfo.getShaderBlockInfo(shader->getUniformBlocks(context));
+ }
+ }
+
+ auto getUniformBlockSize = [&uniformBlockInfo](const std::string &name,
+ const std::string &mappedName, size_t *sizeOut) {
+ return uniformBlockInfo.getBlockSize(name, mappedName, sizeOut);
+ };
+
+ auto getUniformBlockMemberInfo = [&uniformBlockInfo](const std::string &name,
+ const std::string &mappedName,
+ sh::BlockMemberInfo *infoOut) {
+ return uniformBlockInfo.getBlockMemberInfo(name, mappedName, infoOut);
+ };
+
+ // Link uniform interface blocks.
+ resources.uniformBlockLinker.linkBlocks(getUniformBlockSize, getUniformBlockMemberInfo);
+
+ // Gather storage buffer interface block info.
+ InterfaceBlockInfo shaderStorageBlockInfo(mCustomEncoderFactory);
+ for (const ShaderType shaderType : AllShaderTypes())
+ {
+ Shader *shader = programState.getAttachedShader(shaderType);
+ if (shader)
+ {
+ shaderStorageBlockInfo.getShaderBlockInfo(shader->getShaderStorageBlocks(context));
+ }
+ }
+ auto getShaderStorageBlockSize = [&shaderStorageBlockInfo](const std::string &name,
+ const std::string &mappedName,
+ size_t *sizeOut) {
+ return shaderStorageBlockInfo.getBlockSize(name, mappedName, sizeOut);
+ };
+
+ auto getShaderStorageBlockMemberInfo = [&shaderStorageBlockInfo](const std::string &name,
+ const std::string &mappedName,
+ sh::BlockMemberInfo *infoOut) {
+ return shaderStorageBlockInfo.getBlockMemberInfo(name, mappedName, infoOut);
+ };
+
+ // Link storage buffer interface blocks.
+ resources.shaderStorageBlockLinker.linkBlocks(getShaderStorageBlockSize,
+ getShaderStorageBlockMemberInfo);
+
+ // Gather and link atomic counter buffer interface blocks.
+ std::map<int, unsigned int> sizeMap;
+ getAtomicCounterBufferSizeMap(programState, sizeMap);
+ resources.atomicCounterBufferLinker.link(sizeMap);
+}
+
+void ProgramLinkedResourcesLinker::getAtomicCounterBufferSizeMap(
+ const ProgramState &programState,
+ std::map<int, unsigned int> &sizeMapOut) const
+{
+ for (unsigned int index : programState.getAtomicCounterUniformRange())
+ {
+ const LinkedUniform &glUniform = programState.getUniforms()[index];
+
+ auto &bufferDataSize = sizeMapOut[glUniform.binding];
+
+ // Calculate the size of the buffer by finding the end of the last uniform with the same
+ // binding. The end of the uniform is calculated by finding the initial offset of the
+ // uniform and adding size of the uniform. For arrays, the size is the number of elements
+ // times the element size (should always by 4 for atomic_units).
+ unsigned dataOffset =
+ glUniform.offset + static_cast<unsigned int>(glUniform.getBasicTypeElementCount() *
+ glUniform.getElementSize());
+ if (dataOffset > bufferDataSize)
+ {
+ bufferDataSize = dataOffset;
+ }
+ }
+}
+
+bool LinkValidateProgramGlobalNames(InfoLog &infoLog,
+ const ProgramExecutable &executable,
+ const LinkingVariables &linkingVariables)
+{
+ angle::HashMap<std::string, const sh::ShaderVariable *> uniformMap;
+ using BlockAndFieldPair = std::pair<const sh::InterfaceBlock *, const sh::ShaderVariable *>;
+ angle::HashMap<std::string, std::vector<BlockAndFieldPair>> uniformBlockFieldMap;
+
+ for (ShaderType shaderType : kAllGraphicsShaderTypes)
+ {
+ if (!linkingVariables.isShaderStageUsedBitset[shaderType])
+ {
+ continue;
+ }
+
+ // Build a map of Uniforms
+ const std::vector<sh::ShaderVariable> &uniforms = linkingVariables.uniforms[shaderType];
+ for (const auto &uniform : uniforms)
+ {
+ uniformMap[uniform.name] = &uniform;
+ }
+
+ // Build a map of Uniform Blocks
+ // This will also detect any field name conflicts between Uniform Blocks without instance
+ // names
+ const std::vector<sh::InterfaceBlock> &uniformBlocks =
+ linkingVariables.uniformBlocks[shaderType];
+
+ for (const auto &uniformBlock : uniformBlocks)
+ {
+ // Only uniform blocks without an instance name can create a conflict with their field
+ // names
+ if (!uniformBlock.instanceName.empty())
+ {
+ continue;
+ }
+
+ for (const auto &field : uniformBlock.fields)
+ {
+ if (!uniformBlockFieldMap.count(field.name))
+ {
+ // First time we've seen this uniform block field name, so add the
+ // (Uniform Block, Field) pair immediately since there can't be a conflict yet
+ BlockAndFieldPair blockAndFieldPair(&uniformBlock, &field);
+ std::vector<BlockAndFieldPair> newUniformBlockList;
+ newUniformBlockList.push_back(blockAndFieldPair);
+ uniformBlockFieldMap[field.name] = newUniformBlockList;
+ continue;
+ }
+
+ // We've seen this name before.
+ // We need to check each of the uniform blocks that contain a field with this name
+ // to see if there's a conflict or not.
+ std::vector<BlockAndFieldPair> prevBlockFieldPairs =
+ uniformBlockFieldMap[field.name];
+ for (const auto &prevBlockFieldPair : prevBlockFieldPairs)
+ {
+ const sh::InterfaceBlock *prevUniformBlock = prevBlockFieldPair.first;
+ const sh::ShaderVariable *prevUniformBlockField = prevBlockFieldPair.second;
+
+ if (uniformBlock.isSameInterfaceBlockAtLinkTime(*prevUniformBlock))
+ {
+ // The same uniform block should, by definition, contain the same field name
+ continue;
+ }
+
+ // The uniform blocks don't match, so check if the necessary field properties
+ // also match
+ if ((field.name == prevUniformBlockField->name) &&
+ (field.type == prevUniformBlockField->type) &&
+ (field.precision == prevUniformBlockField->precision))
+ {
+ infoLog << "Name conflicts between uniform block field names: "
+ << field.name;
+ return false;
+ }
+ }
+
+ // No conflict, so record this pair
+ BlockAndFieldPair blockAndFieldPair(&uniformBlock, &field);
+ uniformBlockFieldMap[field.name].push_back(blockAndFieldPair);
+ }
+ }
+ }
+
+ // Validate no uniform names conflict with attribute names
+ if (linkingVariables.isShaderStageUsedBitset[ShaderType::Vertex])
+ {
+ // ESSL 3.00.6 section 4.3.5:
+ // If a uniform variable name is declared in one stage (e.g., a vertex shader)
+ // but not in another (e.g., a fragment shader), then that name is still
+ // available in the other stage for a different use.
+ std::unordered_set<std::string> uniforms;
+ for (const sh::ShaderVariable &uniform : linkingVariables.uniforms[ShaderType::Vertex])
+ {
+ uniforms.insert(uniform.name);
+ }
+ for (const auto &attrib : executable.getProgramInputs())
+ {
+ if (uniforms.count(attrib.name))
+ {
+ infoLog << "Name conflicts between a uniform and an attribute: " << attrib.name;
+ return false;
+ }
+ }
+ }
+
+ // Validate no Uniform Block fields conflict with other Uniforms
+ for (const auto &uniformBlockField : uniformBlockFieldMap)
+ {
+ const std::string &fieldName = uniformBlockField.first;
+ if (uniformMap.count(fieldName))
+ {
+ infoLog << "Name conflicts between a uniform and a uniform block field: " << fieldName;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// [OpenGL ES 3.2] Chapter 7.4.1 "Shader Interface Matching"
+bool LinkValidateShaderInterfaceMatching(const std::vector<sh::ShaderVariable> &outputVaryings,
+ const std::vector<sh::ShaderVariable> &inputVaryings,
+ ShaderType frontShaderType,
+ ShaderType backShaderType,
+ int frontShaderVersion,
+ int backShaderVersion,
+ bool isSeparable,
+ gl::InfoLog &infoLog)
+{
+ ASSERT(frontShaderVersion == backShaderVersion);
+
+ std::vector<const sh::ShaderVariable *> filteredInputVaryings;
+ std::vector<const sh::ShaderVariable *> filteredOutputVaryings;
+
+ GetFilteredVaryings(inputVaryings, &filteredInputVaryings);
+ GetFilteredVaryings(outputVaryings, &filteredOutputVaryings);
+
+ // Separable programs require the number of inputs and outputs match
+ if (isSeparable && filteredInputVaryings.size() < filteredOutputVaryings.size())
+ {
+ infoLog << GetShaderTypeString(backShaderType)
+ << " does not consume all varyings generated by "
+ << GetShaderTypeString(frontShaderType);
+ return false;
+ }
+ if (isSeparable && filteredInputVaryings.size() > filteredOutputVaryings.size())
+ {
+ infoLog << GetShaderTypeString(frontShaderType)
+ << " does not generate all varyings consumed by "
+ << GetShaderTypeString(backShaderType);
+ return false;
+ }
+
+ // All inputs must match all outputs
+ for (const sh::ShaderVariable *input : filteredInputVaryings)
+ {
+ bool match = false;
+ for (const sh::ShaderVariable *output : filteredOutputVaryings)
+ {
+ if (DoShaderVariablesMatch(frontShaderVersion, frontShaderType, backShaderType, *input,
+ *output, isSeparable, infoLog))
+ {
+ match = true;
+ break;
+ }
+ }
+
+ // We permit unmatched, unreferenced varyings. Note that this specifically depends on
+ // whether the input is statically used - a statically used input should fail this test even
+ // if it is not active. GLSL ES 3.00.6 section 4.3.10.
+ if (!match && input->staticUse)
+ {
+ const std::string &name =
+ input->isShaderIOBlock ? input->structOrBlockName : input->name;
+ infoLog << GetShaderTypeString(backShaderType) << " varying " << name
+ << " does not match any " << GetShaderTypeString(frontShaderType) << " varying";
+ return false;
+ }
+ }
+
+ return true;
+}
+
+LinkMismatchError LinkValidateProgramVariables(const sh::ShaderVariable &variable1,
+ const sh::ShaderVariable &variable2,
+ bool validatePrecision,
+ bool treatVariable1AsNonArray,
+ bool treatVariable2AsNonArray,
+ std::string *mismatchedStructOrBlockMemberName)
+{
+ if (variable1.type != variable2.type)
+ {
+ return LinkMismatchError::TYPE_MISMATCH;
+ }
+
+ bool variable1IsArray = variable1.isArray();
+ bool variable2IsArray = variable2.isArray();
+ if (treatVariable1AsNonArray)
+ {
+ ASSERT(variable1IsArray);
+ variable1IsArray = false;
+ }
+ if (treatVariable2AsNonArray)
+ {
+ ASSERT(variable2IsArray);
+ variable2IsArray = false;
+ }
+ // TODO(anglebug.com/5557): Investigate interactions with arrays-of-arrays.
+ if (variable1IsArray != variable2IsArray)
+ {
+ return LinkMismatchError::ARRAYNESS_MISMATCH;
+ }
+ if (!treatVariable1AsNonArray && !treatVariable2AsNonArray &&
+ variable1.arraySizes != variable2.arraySizes)
+ {
+ return LinkMismatchError::ARRAY_SIZE_MISMATCH;
+ }
+ if (validatePrecision && variable1.precision != variable2.precision)
+ {
+ return LinkMismatchError::PRECISION_MISMATCH;
+ }
+ if (!variable1.isShaderIOBlock && !variable2.isShaderIOBlock &&
+ variable1.structOrBlockName != variable2.structOrBlockName)
+ {
+ return LinkMismatchError::STRUCT_NAME_MISMATCH;
+ }
+ if (variable1.imageUnitFormat != variable2.imageUnitFormat)
+ {
+ return LinkMismatchError::FORMAT_MISMATCH;
+ }
+
+ if (variable1.fields.size() != variable2.fields.size())
+ {
+ return LinkMismatchError::FIELD_NUMBER_MISMATCH;
+ }
+ const unsigned int numMembers = static_cast<unsigned int>(variable1.fields.size());
+ for (unsigned int memberIndex = 0; memberIndex < numMembers; memberIndex++)
+ {
+ const sh::ShaderVariable &member1 = variable1.fields[memberIndex];
+ const sh::ShaderVariable &member2 = variable2.fields[memberIndex];
+
+ if (member1.name != member2.name)
+ {
+ return LinkMismatchError::FIELD_NAME_MISMATCH;
+ }
+
+ if (member1.interpolation != member2.interpolation)
+ {
+ return LinkMismatchError::INTERPOLATION_TYPE_MISMATCH;
+ }
+
+ if (variable1.isShaderIOBlock && variable2.isShaderIOBlock)
+ {
+ if (member1.location != member2.location)
+ {
+ return LinkMismatchError::FIELD_LOCATION_MISMATCH;
+ }
+
+ if (member1.structOrBlockName != member2.structOrBlockName)
+ {
+ return LinkMismatchError::FIELD_STRUCT_NAME_MISMATCH;
+ }
+ }
+
+ LinkMismatchError linkErrorOnField = LinkValidateProgramVariables(
+ member1, member2, validatePrecision, false, false, mismatchedStructOrBlockMemberName);
+ if (linkErrorOnField != LinkMismatchError::NO_MISMATCH)
+ {
+ AddProgramVariableParentPrefix(member1.name, mismatchedStructOrBlockMemberName);
+ return linkErrorOnField;
+ }
+ }
+
+ return LinkMismatchError::NO_MISMATCH;
+}
+
+void AddProgramVariableParentPrefix(const std::string &parentName, std::string *mismatchedFieldName)
+{
+ ASSERT(mismatchedFieldName);
+ if (mismatchedFieldName->empty())
+ {
+ *mismatchedFieldName = parentName;
+ }
+ else
+ {
+ std::ostringstream stream;
+ stream << parentName << "." << *mismatchedFieldName;
+ *mismatchedFieldName = stream.str();
+ }
+}
+
+bool LinkValidateBuiltInVaryingsInvariant(const std::vector<sh::ShaderVariable> &vertexVaryings,
+ const std::vector<sh::ShaderVariable> &fragmentVaryings,
+ int vertexShaderVersion,
+ InfoLog &infoLog)
+{
+ bool glPositionIsInvariant = false;
+ bool glPointSizeIsInvariant = false;
+ bool glFragCoordIsInvariant = false;
+ bool glPointCoordIsInvariant = false;
+
+ for (const sh::ShaderVariable &varying : vertexVaryings)
+ {
+ if (!varying.isBuiltIn())
+ {
+ continue;
+ }
+ if (varying.name.compare("gl_Position") == 0)
+ {
+ glPositionIsInvariant = varying.isInvariant;
+ }
+ else if (varying.name.compare("gl_PointSize") == 0)
+ {
+ glPointSizeIsInvariant = varying.isInvariant;
+ }
+ }
+
+ for (const sh::ShaderVariable &varying : fragmentVaryings)
+ {
+ if (!varying.isBuiltIn())
+ {
+ continue;
+ }
+ if (varying.name.compare("gl_FragCoord") == 0)
+ {
+ glFragCoordIsInvariant = varying.isInvariant;
+ }
+ else if (varying.name.compare("gl_PointCoord") == 0)
+ {
+ glPointCoordIsInvariant = varying.isInvariant;
+ }
+ }
+
+ // There is some ambiguity in ESSL 1.00.17 paragraph 4.6.4 interpretation,
+ // for example, https://cvs.khronos.org/bugzilla/show_bug.cgi?id=13842.
+ // Not requiring invariance to match is supported by:
+ // dEQP, WebGL CTS, Nexus 5X GLES
+ if (glFragCoordIsInvariant && !glPositionIsInvariant)
+ {
+ infoLog << "gl_FragCoord can only be declared invariant if and only if gl_Position is "
+ "declared invariant.";
+ return false;
+ }
+ if (glPointCoordIsInvariant && !glPointSizeIsInvariant)
+ {
+ infoLog << "gl_PointCoord can only be declared invariant if and only if gl_PointSize is "
+ "declared invariant.";
+ return false;
+ }
+
+ return true;
+}
+
+bool LinkValidateBuiltInVaryings(const std::vector<sh::ShaderVariable> &outputVaryings,
+ const std::vector<sh::ShaderVariable> &inputVaryings,
+ ShaderType outputShaderType,
+ ShaderType inputShaderType,
+ int outputShaderVersion,
+ int inputShaderVersion,
+ InfoLog &infoLog)
+{
+ ASSERT(outputShaderVersion == inputShaderVersion);
+
+ // Only ESSL 1.0 has restrictions on matching input and output invariance
+ if (inputShaderVersion == 100 && outputShaderType == ShaderType::Vertex &&
+ inputShaderType == ShaderType::Fragment)
+ {
+ return LinkValidateBuiltInVaryingsInvariant(outputVaryings, inputVaryings,
+ outputShaderVersion, infoLog);
+ }
+
+ uint32_t sizeClipDistance = 0;
+ uint32_t sizeCullDistance = 0;
+
+ for (const sh::ShaderVariable &varying : outputVaryings)
+ {
+ if (!varying.isBuiltIn())
+ {
+ continue;
+ }
+ if (varying.name.compare("gl_ClipDistance") == 0)
+ {
+ sizeClipDistance = varying.getOutermostArraySize();
+ }
+ else if (varying.name.compare("gl_CullDistance") == 0)
+ {
+ sizeCullDistance = varying.getOutermostArraySize();
+ }
+ }
+
+ for (const sh::ShaderVariable &varying : inputVaryings)
+ {
+ if (!varying.isBuiltIn())
+ {
+ continue;
+ }
+ if (varying.name.compare("gl_ClipDistance") == 0)
+ {
+ if (sizeClipDistance != varying.getOutermostArraySize())
+ {
+ infoLog << "If either shader redeclares the built-in arrays gl_ClipDistance[] the "
+ "array must have the same size in both shaders.";
+ return false;
+ }
+ }
+ else if (varying.name.compare("gl_CullDistance") == 0)
+ {
+ if (sizeCullDistance != varying.getOutermostArraySize())
+ {
+ infoLog << "If either shader redeclares the built-in arrays gl_CullDistance[] the "
+ "array must have the same size in both shaders.";
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+void LogAmbiguousFieldLinkMismatch(InfoLog &infoLog,
+ const std::string &blockName1,
+ const std::string &blockName2,
+ const std::string &fieldName,
+ ShaderType shaderType1,
+ ShaderType shaderType2)
+{
+ infoLog << "Ambiguous field '" << fieldName << "' in blocks '" << blockName1 << "' ("
+ << GetShaderTypeString(shaderType1) << " shader) and '" << blockName2 << "' ("
+ << GetShaderTypeString(shaderType2) << " shader) which don't have instance names.";
+}
+
+bool ValidateInstancelessGraphicsInterfaceBlocksPerShader(
+ const std::vector<sh::InterfaceBlock> &interfaceBlocks,
+ ShaderType shaderType,
+ InterfaceBlockMap *instancelessBlocksFields,
+ InfoLog &infoLog)
+{
+ ASSERT(instancelessBlocksFields);
+
+ for (const sh::InterfaceBlock &block : interfaceBlocks)
+ {
+ if (!block.instanceName.empty())
+ {
+ continue;
+ }
+
+ for (const sh::ShaderVariable &field : block.fields)
+ {
+ const auto &entry = instancelessBlocksFields->find(field.name);
+ if (entry != instancelessBlocksFields->end())
+ {
+ const sh::InterfaceBlock &linkedBlock = *(entry->second.second);
+ if (block.name != linkedBlock.name)
+ {
+ LogAmbiguousFieldLinkMismatch(infoLog, block.name, linkedBlock.name, field.name,
+ entry->second.first, shaderType);
+ return false;
+ }
+ }
+ else
+ {
+ (*instancelessBlocksFields)[field.name] = std::make_pair(shaderType, &block);
+ }
+ }
+ }
+
+ return true;
+}
+
+LinkMismatchError LinkValidateInterfaceBlockFields(const sh::ShaderVariable &blockField1,
+ const sh::ShaderVariable &blockField2,
+ bool webglCompatibility,
+ std::string *mismatchedBlockFieldName)
+{
+ if (blockField1.name != blockField2.name)
+ {
+ return LinkMismatchError::FIELD_NAME_MISMATCH;
+ }
+
+ // If webgl, validate precision of UBO fields, otherwise don't. See Khronos bug 10287.
+ LinkMismatchError linkError = LinkValidateProgramVariables(
+ blockField1, blockField2, webglCompatibility, false, false, mismatchedBlockFieldName);
+ if (linkError != LinkMismatchError::NO_MISMATCH)
+ {
+ AddProgramVariableParentPrefix(blockField1.name, mismatchedBlockFieldName);
+ return linkError;
+ }
+
+ if (blockField1.isRowMajorLayout != blockField2.isRowMajorLayout)
+ {
+ AddProgramVariableParentPrefix(blockField1.name, mismatchedBlockFieldName);
+ return LinkMismatchError::MATRIX_PACKING_MISMATCH;
+ }
+
+ return LinkMismatchError::NO_MISMATCH;
+}
+
+LinkMismatchError AreMatchingInterfaceBlocks(const sh::InterfaceBlock &interfaceBlock1,
+ const sh::InterfaceBlock &interfaceBlock2,
+ bool webglCompatibility,
+ std::string *mismatchedBlockFieldName)
+{
+ // validate blocks for the same member types
+ if (interfaceBlock1.fields.size() != interfaceBlock2.fields.size())
+ {
+ return LinkMismatchError::FIELD_NUMBER_MISMATCH;
+ }
+ if (interfaceBlock1.arraySize != interfaceBlock2.arraySize)
+ {
+ return LinkMismatchError::ARRAY_SIZE_MISMATCH;
+ }
+ if (interfaceBlock1.layout != interfaceBlock2.layout ||
+ interfaceBlock1.binding != interfaceBlock2.binding)
+ {
+ return LinkMismatchError::LAYOUT_QUALIFIER_MISMATCH;
+ }
+ if (interfaceBlock1.instanceName.empty() != interfaceBlock2.instanceName.empty())
+ {
+ return LinkMismatchError::INSTANCE_NAME_MISMATCH;
+ }
+ const unsigned int numBlockMembers = static_cast<unsigned int>(interfaceBlock1.fields.size());
+ for (unsigned int blockMemberIndex = 0; blockMemberIndex < numBlockMembers; blockMemberIndex++)
+ {
+ const sh::ShaderVariable &member1 = interfaceBlock1.fields[blockMemberIndex];
+ const sh::ShaderVariable &member2 = interfaceBlock2.fields[blockMemberIndex];
+
+ LinkMismatchError linkError = LinkValidateInterfaceBlockFields(
+ member1, member2, webglCompatibility, mismatchedBlockFieldName);
+ if (linkError != LinkMismatchError::NO_MISMATCH)
+ {
+ return linkError;
+ }
+ }
+ return LinkMismatchError::NO_MISMATCH;
+}
+
+void InitializeInterfaceBlockMap(const std::vector<sh::InterfaceBlock> &interfaceBlocks,
+ ShaderType shaderType,
+ InterfaceBlockMap *linkedInterfaceBlocks)
+{
+ ASSERT(linkedInterfaceBlocks);
+
+ for (const sh::InterfaceBlock &interfaceBlock : interfaceBlocks)
+ {
+ (*linkedInterfaceBlocks)[interfaceBlock.name] = std::make_pair(shaderType, &interfaceBlock);
+ }
+}
+
+bool ValidateGraphicsInterfaceBlocksPerShader(
+ const std::vector<sh::InterfaceBlock> &interfaceBlocksToLink,
+ ShaderType shaderType,
+ bool webglCompatibility,
+ InterfaceBlockMap *linkedBlocks,
+ InfoLog &infoLog)
+{
+ ASSERT(linkedBlocks);
+
+ for (const sh::InterfaceBlock &block : interfaceBlocksToLink)
+ {
+ const auto &entry = linkedBlocks->find(block.name);
+ if (entry != linkedBlocks->end())
+ {
+ const sh::InterfaceBlock &linkedBlock = *(entry->second.second);
+ std::string mismatchedStructFieldName;
+ LinkMismatchError linkError = AreMatchingInterfaceBlocks(
+ block, linkedBlock, webglCompatibility, &mismatchedStructFieldName);
+ if (linkError != LinkMismatchError::NO_MISMATCH)
+ {
+ LogLinkMismatch(infoLog, block.name, GetInterfaceBlockTypeString(block.blockType),
+ linkError, mismatchedStructFieldName, entry->second.first,
+ shaderType);
+ return false;
+ }
+ }
+ else
+ {
+ (*linkedBlocks)[block.name] = std::make_pair(shaderType, &block);
+ }
+ }
+
+ return true;
+}
+
+bool ValidateInterfaceBlocksMatch(
+ GLuint numShadersHasInterfaceBlocks,
+ const ShaderMap<const std::vector<sh::InterfaceBlock> *> &shaderInterfaceBlocks,
+ InfoLog &infoLog,
+ bool webglCompatibility,
+ InterfaceBlockMap *instancelessInterfaceBlocksFields)
+{
+ for (ShaderType shaderType : kAllGraphicsShaderTypes)
+ {
+ // Validate that instanceless blocks of different names don't have fields of the same name.
+ if (shaderInterfaceBlocks[shaderType] &&
+ !ValidateInstancelessGraphicsInterfaceBlocksPerShader(
+ *shaderInterfaceBlocks[shaderType], shaderType, instancelessInterfaceBlocksFields,
+ infoLog))
+ {
+ return false;
+ }
+ }
+
+ if (numShadersHasInterfaceBlocks < 2u)
+ {
+ return true;
+ }
+
+ ASSERT(!shaderInterfaceBlocks[ShaderType::Compute]);
+
+ // Check that interface blocks defined in the graphics shaders are identical
+
+ InterfaceBlockMap linkedInterfaceBlocks;
+
+ bool interfaceBlockMapInitialized = false;
+ for (ShaderType shaderType : kAllGraphicsShaderTypes)
+ {
+ if (!shaderInterfaceBlocks[shaderType])
+ {
+ continue;
+ }
+
+ if (!interfaceBlockMapInitialized)
+ {
+ InitializeInterfaceBlockMap(*shaderInterfaceBlocks[shaderType], shaderType,
+ &linkedInterfaceBlocks);
+ interfaceBlockMapInitialized = true;
+ }
+ else if (!ValidateGraphicsInterfaceBlocksPerShader(*shaderInterfaceBlocks[shaderType],
+ shaderType, webglCompatibility,
+ &linkedInterfaceBlocks, infoLog))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool LinkValidateProgramInterfaceBlocks(const Context *context,
+ ShaderBitSet activeProgramStages,
+ const ProgramLinkedResources &resources,
+ InfoLog &infoLog,
+ GLuint *combinedShaderStorageBlocksCountOut)
+{
+ ASSERT(combinedShaderStorageBlocksCountOut);
+
+ const Caps &caps = context->getCaps();
+ const bool webglCompatibility = context->isWebGL();
+ const Version &version = context->getClientVersion();
+
+ GLuint combinedUniformBlocksCount = 0u;
+ GLuint numShadersHasUniformBlocks = 0u;
+ ShaderMap<const std::vector<sh::InterfaceBlock> *> allShaderUniformBlocks = {};
+ InterfaceBlockMap instancelessInterfaceBlocksFields;
+
+ for (ShaderType shaderType : activeProgramStages)
+ {
+ const std::vector<sh::InterfaceBlock> &uniformBlocks =
+ resources.uniformBlockLinker.getShaderBlocks(shaderType);
+ if (!uniformBlocks.empty())
+ {
+ if (!ValidateInterfaceBlocksCount(
+ static_cast<GLuint>(caps.maxShaderUniformBlocks[shaderType]), uniformBlocks,
+ shaderType, sh::BlockType::BLOCK_UNIFORM, &combinedUniformBlocksCount, infoLog))
+ {
+ return false;
+ }
+
+ allShaderUniformBlocks[shaderType] = &uniformBlocks;
+ ++numShadersHasUniformBlocks;
+ }
+ }
+
+ if (combinedUniformBlocksCount > static_cast<GLuint>(caps.maxCombinedUniformBlocks))
+ {
+ infoLog << "The sum of the number of active uniform blocks exceeds "
+ "MAX_COMBINED_UNIFORM_BLOCKS ("
+ << caps.maxCombinedUniformBlocks << ").";
+ return false;
+ }
+
+ if (!ValidateInterfaceBlocksMatch(numShadersHasUniformBlocks, allShaderUniformBlocks, infoLog,
+ webglCompatibility, &instancelessInterfaceBlocksFields))
+ {
+ return false;
+ }
+
+ if (version >= Version(3, 1))
+ {
+ *combinedShaderStorageBlocksCountOut = 0u;
+ GLuint numShadersHasShaderStorageBlocks = 0u;
+ ShaderMap<const std::vector<sh::InterfaceBlock> *> allShaderStorageBlocks = {};
+ for (ShaderType shaderType : activeProgramStages)
+ {
+ const std::vector<sh::InterfaceBlock> &shaderStorageBlocks =
+ resources.shaderStorageBlockLinker.getShaderBlocks(shaderType);
+ if (!shaderStorageBlocks.empty())
+ {
+ if (!ValidateInterfaceBlocksCount(
+ static_cast<GLuint>(caps.maxShaderStorageBlocks[shaderType]),
+ shaderStorageBlocks, shaderType, sh::BlockType::BLOCK_BUFFER,
+ combinedShaderStorageBlocksCountOut, infoLog))
+ {
+ return false;
+ }
+
+ allShaderStorageBlocks[shaderType] = &shaderStorageBlocks;
+ ++numShadersHasShaderStorageBlocks;
+ }
+ }
+
+ if (*combinedShaderStorageBlocksCountOut >
+ static_cast<GLuint>(caps.maxCombinedShaderStorageBlocks))
+ {
+ infoLog << "The sum of the number of active shader storage blocks exceeds "
+ "MAX_COMBINED_SHADER_STORAGE_BLOCKS ("
+ << caps.maxCombinedShaderStorageBlocks << ").";
+ return false;
+ }
+
+ if (!ValidateInterfaceBlocksMatch(numShadersHasShaderStorageBlocks, allShaderStorageBlocks,
+ infoLog, webglCompatibility,
+ &instancelessInterfaceBlocksFields))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace gl