diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /gfx/angle/checkout/src/libANGLE/ProgramLinkedResources.cpp | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/angle/checkout/src/libANGLE/ProgramLinkedResources.cpp')
-rw-r--r-- | gfx/angle/checkout/src/libANGLE/ProgramLinkedResources.cpp | 1331 |
1 files changed, 1331 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..cd2086dc13 --- /dev/null +++ b/gfx/angle/checkout/src/libANGLE/ProgramLinkedResources.cpp @@ -0,0 +1,1331 @@ +// +// Copyright (c) 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::Uniform &uniform1, + const sh::Uniform &uniform2, + std::string *mismatchedStructFieldName) +{ +#if ANGLE_PROGRAM_LINK_VALIDATE_UNIFORM_PRECISION == ANGLE_ENABLED + const bool validatePrecision = true; +#else + const bool validatePrecision = false; +#endif + + LinkMismatchError linkError = Program::LinkValidateVariablesBase( + uniform1, uniform2, validatePrecision, true, 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; +} + +using ShaderUniform = std::pair<ShaderType, const sh::Uniform *>; + +bool ValidateGraphicsUniformsPerShader(Shader *shaderToLink, + bool extendLinkedUniforms, + std::map<std::string, ShaderUniform> *linkedUniforms, + InfoLog &infoLog) +{ + ASSERT(shaderToLink && linkedUniforms); + + for (const sh::Uniform &uniform : shaderToLink->getUniforms()) + { + const auto &entry = linkedUniforms->find(uniform.name); + if (entry != linkedUniforms->end()) + { + const sh::Uniform &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->getType()); + return false; + } + } + else if (extendLinkedUniforms) + { + (*linkedUniforms)[uniform.name] = std::make_pair(shaderToLink->getType(), &uniform); + } + } + + return true; +} + +GLuint GetMaximumShaderUniformVectors(ShaderType shaderType, const Caps &caps) +{ + switch (shaderType) + { + case ShaderType::Vertex: + return caps.maxVertexUniformVectors; + case ShaderType::Fragment: + return caps.maxFragmentUniformVectors; + + case ShaderType::Compute: + case ShaderType::Geometry: + return 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) 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, &mDummyEncoder), + mGetMemberInfo(getMemberInfo), + mBufferVariablesOut(bufferVariablesOut), + mShaderType(shaderType), + mBlockIndex(blockIndex) + {} + + void visitNamedVariable(const sh::ShaderVariable &variable, + bool isRowMajor, + const std::string &name, + const std::string &mappedName) 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::DummyBlockEncoder mDummyEncoder; +}; + +struct ShaderUniformCount +{ + unsigned int vectorCount = 0; + unsigned int samplerCount = 0; + unsigned int imageCount = 0; + unsigned int atomicCounterCount = 0; +}; + +ShaderUniformCount &operator+=(ShaderUniformCount &lhs, const ShaderUniformCount &rhs) +{ + lhs.vectorCount += rhs.vectorCount; + lhs.samplerCount += rhs.samplerCount; + lhs.imageCount += rhs.imageCount; + lhs.atomicCounterCount += rhs.atomicCounterCount; + 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::Uniform &uniform, + std::vector<LinkedUniform> *uniforms, + std::vector<LinkedUniform> *samplerUniforms, + std::vector<LinkedUniform> *imageUniforms, + std::vector<LinkedUniform> *atomicCounterUniforms, + 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), + mUnusedUniforms(unusedUniforms) + {} + + void visitNamedSampler(const sh::ShaderVariable &sampler, + const std::string &name, + const std::string &mappedName) override + { + visitNamedVariable(sampler, false, name, mappedName); + } + + void visitNamedVariable(const sh::ShaderVariable &variable, + bool isRowMajor, + const std::string &name, + const std::string &mappedName) override + { + bool isSampler = IsSamplerType(variable.type); + bool isImage = IsImageType(variable.type); + bool isAtomicCounter = IsAtomicCounterType(variable.type); + std::vector<LinkedUniform> *uniformList = mUniforms; + if (isSampler) + { + uniformList = mSamplerUniforms; + } + else if (isImage) + { + uniformList = mImageUniforms; + } + else if (isAtomicCounter) + { + uniformList = mAtomicCounterUniforms; + } + + 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; + if (variable.hasParentArrayIndex()) + { + linkedUniform.setParentArrayIndex(variable.parentArrayIndex()); + } + if (mMarkActive) + { + linkedUniform.setActive(mShaderType, true); + } + else + { + mUnusedUniforms->emplace_back(linkedUniform.name, linkedUniform.isSampler()); + } + + 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)) + { + mUniformCount.vectorCount += VariableRegisterCount(variable.type) * elementCount; + } + + mUniformCount.samplerCount += (isSampler ? elementCount : 0); + mUniformCount.imageCount += (isImage ? elementCount : 0); + mUniformCount.atomicCounterCount += (isAtomicCounter ? 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); + } + + 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<UnusedUniform> *mUnusedUniforms; + 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; +} +} // anonymous namespace + +UniformLinker::UniformLinker(const ProgramState &state) : mState(state) {} + +UniformLinker::~UniformLinker() = default; + +void UniformLinker::getResults(std::vector<LinkedUniform> *uniforms, + std::vector<UnusedUniform> *unusedUniforms, + std::vector<VariableLocation> *uniformLocations) +{ + uniforms->swap(mUniforms); + unusedUniforms->swap(mUnusedUniforms); + uniformLocations->swap(mUniformLocations); +} + +bool UniformLinker::link(const Caps &caps, + InfoLog &infoLog, + const ProgramBindings &uniformLocationBindings) +{ + if (mState.getAttachedShader(ShaderType::Vertex) && + mState.getAttachedShader(ShaderType::Fragment)) + { + ASSERT(mState.getAttachedShader(ShaderType::Compute) == nullptr); + 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 : kAllGraphicsShaderTypes) + { + Shader *currentShader = mState.getAttachedShader(shaderType); + if (currentShader) + { + if (shaderType == ShaderType::Vertex) + { + for (const sh::Uniform &vertexUniform : currentShader->getUniforms()) + { + linkedUniforms[vertexUniform.name] = + std::make_pair(ShaderType::Vertex, &vertexUniform); + } + } + else + { + bool isLastShader = (shaderType == ShaderType::Fragment); + if (!ValidateGraphicsUniformsPerShader(currentShader, !isLastShader, + &linkedUniforms, infoLog)) + { + return false; + } + } + } + } + + return true; +} + +bool UniformLinker::indexUniforms(InfoLog &infoLog, const ProgramBindings &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 bindUniformLocation 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)) + { + 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 ProgramBindings &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()) + { + 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 = mUniforms.erase(uniformIter); + } + } +} + +bool UniformLinker::flattenUniformsAndCheckCapsForShader( + Shader *shader, + const Caps &caps, + std::vector<LinkedUniform> &samplerUniforms, + std::vector<LinkedUniform> &imageUniforms, + std::vector<LinkedUniform> &atomicCounterUniforms, + std::vector<UnusedUniform> &unusedUniforms, + InfoLog &infoLog) +{ + ShaderUniformCount shaderUniformCount; + for (const sh::Uniform &uniform : shader->getUniforms()) + { + FlattenUniformVisitor flattener(shader->getType(), uniform, &mUniforms, &samplerUniforms, + &imageUniforms, &atomicCounterUniforms, &unusedUniforms); + sh::TraverseShaderVariable(uniform, false, &flattener); + + if (uniform.active) + { + shaderUniformCount += flattener.getCounts(); + } + else + { + unusedUniforms.emplace_back(uniform.name, IsSamplerType(uniform.type)); + } + } + + ShaderType shaderType = shader->getType(); + + // TODO (jiawei.shao@intel.com): check whether we need finer-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 > caps.maxShaderTextureImageUnits[shaderType]) + { + LogUniformsExceedLimit(shaderType, UniformType::Sampler, + caps.maxShaderTextureImageUnits[shaderType], infoLog); + return false; + } + + if (shaderUniformCount.imageCount > caps.maxShaderImageUniforms[shaderType]) + { + LogUniformsExceedLimit(shaderType, UniformType::Image, + caps.maxShaderImageUniforms[shaderType], infoLog); + return false; + } + + if (shaderUniformCount.atomicCounterCount > 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<UnusedUniform> unusedUniforms; + + for (const ShaderType shaderType : AllShaderTypes()) + { + Shader *shader = mState.getAttachedShader(shaderType); + if (!shader) + { + continue; + } + + if (!flattenUniformsAndCheckCapsForShader(shader, caps, samplerUniforms, imageUniforms, + atomicCounterUniforms, 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()); + 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 > caps.maxCombinedAtomicCounters) + { + infoLog << "atomic counter count exceeds MAX_COMBINED_ATOMIC_COUNTERS" + << caps.maxCombinedAtomicCounters << ")."; + return false; + } + } + } + return true; +} + +// InterfaceBlockLinker implementation. +InterfaceBlockLinker::InterfaceBlockLinker(std::vector<InterfaceBlock> *blocksOut, + std::vector<std::string> *unusedInterfaceBlocksOut) + : mShaderBlocks({}), mBlocksOut(blocksOut), mUnusedInterfaceBlocksOut(unusedInterfaceBlocksOut) +{} + +InterfaceBlockLinker::~InterfaceBlockLinker() {} + +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)); + } + + 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, 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(std::vector<InterfaceBlock> *blocksOut, + std::vector<LinkedUniform> *uniformsOut, + std::vector<std::string> *unusedInterfaceBlocksOut) + : InterfaceBlockLinker(blocksOut, unusedInterfaceBlocksOut), mUniformsOut(uniformsOut) +{} + +UniformBlockLinker::~UniformBlockLinker() {} + +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( + std::vector<InterfaceBlock> *blocksOut, + std::vector<BufferVariable> *bufferVariablesOut, + std::vector<std::string> *unusedInterfaceBlocksOut) + : InterfaceBlockLinker(blocksOut, unusedInterfaceBlocksOut), + mBufferVariablesOut(bufferVariablesOut) +{} + +ShaderStorageBlockLinker::~ShaderStorageBlockLinker() {} + +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( + std::vector<AtomicCounterBuffer> *atomicCounterBuffersOut) + : mAtomicCounterBuffersOut(atomicCounterBuffersOut) +{} + +AtomicCounterBufferLinker::~AtomicCounterBufferLinker() {} + +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( + GLuint maxVaryingVectors, + PackMode packMode, + std::vector<InterfaceBlock> *uniformBlocksOut, + std::vector<LinkedUniform> *uniformsOut, + std::vector<InterfaceBlock> *shaderStorageBlocksOut, + std::vector<BufferVariable> *bufferVariablesOut, + std::vector<AtomicCounterBuffer> *atomicCounterBuffersOut) + : varyingPacking(maxVaryingVectors, packMode), + uniformBlockLinker(uniformBlocksOut, uniformsOut, &unusedInterfaceBlocks), + shaderStorageBlockLinker(shaderStorageBlocksOut, bufferVariablesOut, &unusedInterfaceBlocks), + atomicCounterBufferLinker(atomicCounterBuffersOut) +{} + +ProgramLinkedResources::~ProgramLinkedResources() = default; + +void ProgramLinkedResourcesLinker::linkResources(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()); + } + } + + 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 bufer interface block info. + InterfaceBlockInfo shaderStorageBlockInfo(mCustomEncoderFactory); + for (const ShaderType shaderType : AllShaderTypes()) + { + Shader *shader = programState.getAttachedShader(shaderType); + if (shader) + { + shaderStorageBlockInfo.getShaderBlockInfo(shader->getShaderStorageBlocks()); + } + } + 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 + (glUniform.getBasicTypeElementCount() * glUniform.getElementSize()); + if (dataOffset > bufferDataSize) + { + bufferDataSize = dataOffset; + } + } +} + +} // namespace gl |