// // Copyright 2013 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. // // blocklayout.cpp: // Implementation for block layout classes and methods. // #include "compiler/translator/blocklayout.h" #include "common/mathutil.h" #include "common/utilities.h" #include "compiler/translator/Common.h" namespace sh { namespace { class BlockLayoutMapVisitor : public BlockEncoderVisitor { public: BlockLayoutMapVisitor(BlockLayoutMap *blockInfoOut, const std::string &instanceName, BlockLayoutEncoder *encoder) : BlockEncoderVisitor(instanceName, instanceName, encoder), mInfoOut(blockInfoOut) {} void encodeVariable(const ShaderVariable &variable, const BlockMemberInfo &variableInfo, const std::string &name, const std::string &mappedName) override { ASSERT(!gl::IsSamplerType(variable.type)); if (!gl::IsOpaqueType(variable.type)) { (*mInfoOut)[name] = variableInfo; } } private: BlockLayoutMap *mInfoOut; }; template void GetInterfaceBlockInfo(const std::vector &fields, const std::string &prefix, BlockLayoutEncoder *encoder, bool inRowMajorLayout, bool onlyActiveVariables, BlockLayoutMap *blockInfoOut) { BlockLayoutMapVisitor visitor(blockInfoOut, prefix, encoder); if (onlyActiveVariables) { TraverseActiveShaderVariables(fields, inRowMajorLayout, &visitor); } else { TraverseShaderVariables(fields, inRowMajorLayout, &visitor); } } void TraverseStructVariable(const ShaderVariable &variable, bool isRowMajorLayout, ShaderVariableVisitor *visitor) { const std::vector &fields = variable.fields; visitor->enterStructAccess(variable, isRowMajorLayout); TraverseShaderVariables(fields, isRowMajorLayout, visitor); visitor->exitStructAccess(variable, isRowMajorLayout); } void TraverseStructArrayVariable(const ShaderVariable &variable, bool inRowMajorLayout, ShaderVariableVisitor *visitor) { visitor->enterArray(variable); // Nested arrays are processed starting from outermost (arrayNestingIndex 0u) and ending at the // innermost. We make a special case for unsized arrays. const unsigned int currentArraySize = variable.getNestedArraySize(0); for (unsigned int arrayElement = 0u; arrayElement < currentArraySize; ++arrayElement) { visitor->enterArrayElement(variable, arrayElement); ShaderVariable elementVar = variable; elementVar.indexIntoArray(arrayElement); if (variable.arraySizes.size() > 1u) { TraverseStructArrayVariable(elementVar, inRowMajorLayout, visitor); } else { TraverseStructVariable(elementVar, inRowMajorLayout, visitor); } visitor->exitArrayElement(variable, arrayElement); } visitor->exitArray(variable); } void TraverseArrayOfArraysVariable(const ShaderVariable &variable, unsigned int arrayNestingIndex, bool isRowMajorMatrix, ShaderVariableVisitor *visitor) { visitor->enterArray(variable); const unsigned int currentArraySize = variable.getNestedArraySize(arrayNestingIndex); unsigned int count = std::max(currentArraySize, 1u); for (unsigned int arrayElement = 0u; arrayElement < count; ++arrayElement) { visitor->enterArrayElement(variable, arrayElement); ShaderVariable elementVar = variable; elementVar.indexIntoArray(arrayElement); if (arrayNestingIndex + 2u < variable.arraySizes.size()) { TraverseArrayOfArraysVariable(elementVar, arrayNestingIndex, isRowMajorMatrix, visitor); } else { if (gl::IsSamplerType(variable.type) || gl::IsImageType(variable.type) || variable.isFragmentInOut) { visitor->visitOpaqueObject(elementVar); } else { visitor->visitVariable(elementVar, isRowMajorMatrix); } } visitor->exitArrayElement(variable, arrayElement); } visitor->exitArray(variable); } std::string CollapseNameStack(const std::vector &nameStack) { std::stringstream strstr = sh::InitializeStream(); for (const std::string &part : nameStack) { strstr << part; } return strstr.str(); } size_t GetStd430BaseAlignment(GLenum variableType, bool isRowMajor) { GLenum flippedType = isRowMajor ? variableType : gl::TransposeMatrixType(variableType); size_t numComponents = static_cast(gl::VariableColumnCount(flippedType)); return ComponentAlignment(numComponents); } class BaseAlignmentVisitor : public ShaderVariableVisitor { public: BaseAlignmentVisitor() = default; void visitVariable(const ShaderVariable &variable, bool isRowMajor) override { size_t baseAlignment = GetStd430BaseAlignment(variable.type, isRowMajor); mCurrentAlignment = std::max(mCurrentAlignment, baseAlignment); } // This is in components rather than bytes. size_t getBaseAlignment() const { return mCurrentAlignment; } private: size_t mCurrentAlignment = 0; }; } // anonymous namespace // BlockLayoutEncoder implementation. BlockLayoutEncoder::BlockLayoutEncoder() : mCurrentOffset(0) {} BlockMemberInfo BlockLayoutEncoder::encodeType(GLenum type, const std::vector &arraySizes, bool isRowMajorMatrix) { int arrayStride; int matrixStride; getBlockLayoutInfo(type, arraySizes, isRowMajorMatrix, &arrayStride, &matrixStride); const BlockMemberInfo memberInfo(static_cast(mCurrentOffset * kBytesPerComponent), static_cast(arrayStride * kBytesPerComponent), static_cast(matrixStride * kBytesPerComponent), isRowMajorMatrix); advanceOffset(type, arraySizes, isRowMajorMatrix, arrayStride, matrixStride); return memberInfo; } BlockMemberInfo BlockLayoutEncoder::encodeArrayOfPreEncodedStructs( size_t size, const std::vector &arraySizes) { const unsigned int innerArraySizeProduct = gl::InnerArraySizeProduct(arraySizes); const unsigned int outermostArraySize = gl::OutermostArraySize(arraySizes); // The size of struct is expected to be already aligned appropriately. const size_t arrayStride = size * innerArraySizeProduct; const BlockMemberInfo memberInfo(static_cast(mCurrentOffset * kBytesPerComponent), static_cast(arrayStride), -1, false); angle::base::CheckedNumeric checkedOffset(arrayStride); checkedOffset *= outermostArraySize; checkedOffset /= kBytesPerComponent; checkedOffset += mCurrentOffset; mCurrentOffset = checkedOffset.ValueOrDefault(std::numeric_limits::max()); return memberInfo; } size_t BlockLayoutEncoder::getCurrentOffset() const { angle::base::CheckedNumeric checkedOffset(mCurrentOffset); checkedOffset *= kBytesPerComponent; return checkedOffset.ValueOrDefault(std::numeric_limits::max()); } size_t BlockLayoutEncoder::getShaderVariableSize(const ShaderVariable &structVar, bool isRowMajor) { size_t currentOffset = mCurrentOffset; mCurrentOffset = 0; BlockEncoderVisitor visitor("", "", this); enterAggregateType(structVar); TraverseShaderVariables(structVar.fields, isRowMajor, &visitor); exitAggregateType(structVar); size_t structVarSize = getCurrentOffset(); mCurrentOffset = currentOffset; return structVarSize; } // static size_t BlockLayoutEncoder::GetBlockRegister(const BlockMemberInfo &info) { return (info.offset / kBytesPerComponent) / kComponentsPerRegister; } // static size_t BlockLayoutEncoder::GetBlockRegisterElement(const BlockMemberInfo &info) { return (info.offset / kBytesPerComponent) % kComponentsPerRegister; } void BlockLayoutEncoder::align(size_t baseAlignment) { angle::base::CheckedNumeric checkedOffset(mCurrentOffset); checkedOffset += baseAlignment; checkedOffset -= 1; angle::base::CheckedNumeric checkedAlignmentOffset = checkedOffset; checkedAlignmentOffset %= baseAlignment; checkedOffset -= checkedAlignmentOffset.ValueOrDefault(std::numeric_limits::max()); mCurrentOffset = checkedOffset.ValueOrDefault(std::numeric_limits::max()); } // StubBlockEncoder implementation. void StubBlockEncoder::getBlockLayoutInfo(GLenum type, const std::vector &arraySizes, bool isRowMajorMatrix, int *arrayStrideOut, int *matrixStrideOut) { *arrayStrideOut = 0; *matrixStrideOut = 0; } // Std140BlockEncoder implementation. Std140BlockEncoder::Std140BlockEncoder() {} void Std140BlockEncoder::enterAggregateType(const ShaderVariable &structVar) { align(getBaseAlignment(structVar)); } void Std140BlockEncoder::exitAggregateType(const ShaderVariable &structVar) { align(getBaseAlignment(structVar)); } void Std140BlockEncoder::getBlockLayoutInfo(GLenum type, const std::vector &arraySizes, bool isRowMajorMatrix, int *arrayStrideOut, int *matrixStrideOut) { // We assume we are only dealing with 4 byte components (no doubles or half-words currently) ASSERT(gl::VariableComponentSize(gl::VariableComponentType(type)) == kBytesPerComponent); size_t baseAlignment = 0; int matrixStride = 0; int arrayStride = 0; if (gl::IsMatrixType(type)) { baseAlignment = getTypeBaseAlignment(type, isRowMajorMatrix); matrixStride = static_cast(getTypeBaseAlignment(type, isRowMajorMatrix)); if (!arraySizes.empty()) { const int numRegisters = gl::MatrixRegisterCount(type, isRowMajorMatrix); arrayStride = static_cast(getTypeBaseAlignment(type, isRowMajorMatrix) * numRegisters); } } else if (!arraySizes.empty()) { baseAlignment = static_cast(getTypeBaseAlignment(type, false)); arrayStride = static_cast(getTypeBaseAlignment(type, false)); } else { const size_t numComponents = static_cast(gl::VariableComponentCount(type)); baseAlignment = ComponentAlignment(numComponents); } align(baseAlignment); *matrixStrideOut = matrixStride; *arrayStrideOut = arrayStride; } void Std140BlockEncoder::advanceOffset(GLenum type, const std::vector &arraySizes, bool isRowMajorMatrix, int arrayStride, int matrixStride) { if (!arraySizes.empty()) { angle::base::CheckedNumeric checkedOffset(arrayStride); checkedOffset *= gl::ArraySizeProduct(arraySizes); checkedOffset += mCurrentOffset; mCurrentOffset = checkedOffset.ValueOrDefault(std::numeric_limits::max()); } else if (gl::IsMatrixType(type)) { angle::base::CheckedNumeric checkedOffset(matrixStride); checkedOffset *= gl::MatrixRegisterCount(type, isRowMajorMatrix); checkedOffset += mCurrentOffset; mCurrentOffset = checkedOffset.ValueOrDefault(std::numeric_limits::max()); } else { angle::base::CheckedNumeric checkedOffset(mCurrentOffset); checkedOffset += gl::VariableComponentCount(type); mCurrentOffset = checkedOffset.ValueOrDefault(std::numeric_limits::max()); } } size_t Std140BlockEncoder::getBaseAlignment(const ShaderVariable &variable) const { return kComponentsPerRegister; } size_t Std140BlockEncoder::getTypeBaseAlignment(GLenum type, bool isRowMajorMatrix) const { return kComponentsPerRegister; } // Std430BlockEncoder implementation. Std430BlockEncoder::Std430BlockEncoder() {} size_t Std430BlockEncoder::getBaseAlignment(const ShaderVariable &shaderVar) const { if (shaderVar.isStruct()) { BaseAlignmentVisitor visitor; TraverseShaderVariables(shaderVar.fields, false, &visitor); return visitor.getBaseAlignment(); } return GetStd430BaseAlignment(shaderVar.type, shaderVar.isRowMajorLayout); } size_t Std430BlockEncoder::getTypeBaseAlignment(GLenum type, bool isRowMajorMatrix) const { return GetStd430BaseAlignment(type, isRowMajorMatrix); } void GetInterfaceBlockInfo(const std::vector &fields, const std::string &prefix, BlockLayoutEncoder *encoder, BlockLayoutMap *blockInfoOut) { // Matrix packing is always recorded in individual fields, so they'll set the row major layout // flag to true if needed. // Iterates over all variables. GetInterfaceBlockInfo(fields, prefix, encoder, false, false, blockInfoOut); } void GetActiveUniformBlockInfo(const std::vector &uniforms, const std::string &prefix, BlockLayoutEncoder *encoder, BlockLayoutMap *blockInfoOut) { // Matrix packing is always recorded in individual fields, so they'll set the row major layout // flag to true if needed. // Iterates only over the active variables. GetInterfaceBlockInfo(uniforms, prefix, encoder, false, true, blockInfoOut); } // VariableNameVisitor implementation. VariableNameVisitor::VariableNameVisitor(const std::string &namePrefix, const std::string &mappedNamePrefix) { if (!namePrefix.empty()) { mNameStack.push_back(namePrefix + "."); } if (!mappedNamePrefix.empty()) { mMappedNameStack.push_back(mappedNamePrefix + "."); } } VariableNameVisitor::~VariableNameVisitor() = default; void VariableNameVisitor::enterStruct(const ShaderVariable &structVar) { mNameStack.push_back(structVar.name); mMappedNameStack.push_back(structVar.mappedName); } void VariableNameVisitor::exitStruct(const ShaderVariable &structVar) { mNameStack.pop_back(); mMappedNameStack.pop_back(); } void VariableNameVisitor::enterStructAccess(const ShaderVariable &structVar, bool isRowMajor) { mNameStack.push_back("."); mMappedNameStack.push_back("."); } void VariableNameVisitor::exitStructAccess(const ShaderVariable &structVar, bool isRowMajor) { mNameStack.pop_back(); mMappedNameStack.pop_back(); } void VariableNameVisitor::enterArray(const ShaderVariable &arrayVar) { if (!arrayVar.hasParentArrayIndex() && !arrayVar.isStruct()) { mNameStack.push_back(arrayVar.name); mMappedNameStack.push_back(arrayVar.mappedName); } mArraySizeStack.push_back(arrayVar.getOutermostArraySize()); } void VariableNameVisitor::exitArray(const ShaderVariable &arrayVar) { if (!arrayVar.hasParentArrayIndex() && !arrayVar.isStruct()) { mNameStack.pop_back(); mMappedNameStack.pop_back(); } mArraySizeStack.pop_back(); } void VariableNameVisitor::enterArrayElement(const ShaderVariable &arrayVar, unsigned int arrayElement) { std::stringstream strstr = sh::InitializeStream(); strstr << "[" << arrayElement << "]"; std::string elementString = strstr.str(); mNameStack.push_back(elementString); mMappedNameStack.push_back(elementString); } void VariableNameVisitor::exitArrayElement(const ShaderVariable &arrayVar, unsigned int arrayElement) { mNameStack.pop_back(); mMappedNameStack.pop_back(); } std::string VariableNameVisitor::collapseNameStack() const { return CollapseNameStack(mNameStack); } std::string VariableNameVisitor::collapseMappedNameStack() const { return CollapseNameStack(mMappedNameStack); } void VariableNameVisitor::visitOpaqueObject(const sh::ShaderVariable &variable) { if (!variable.hasParentArrayIndex()) { mNameStack.push_back(variable.name); mMappedNameStack.push_back(variable.mappedName); } std::string name = collapseNameStack(); std::string mappedName = collapseMappedNameStack(); if (!variable.hasParentArrayIndex()) { mNameStack.pop_back(); mMappedNameStack.pop_back(); } visitNamedOpaqueObject(variable, name, mappedName, mArraySizeStack); } void VariableNameVisitor::visitVariable(const ShaderVariable &variable, bool isRowMajor) { if (!variable.hasParentArrayIndex()) { mNameStack.push_back(variable.name); mMappedNameStack.push_back(variable.mappedName); } std::string name = collapseNameStack(); std::string mappedName = collapseMappedNameStack(); if (!variable.hasParentArrayIndex()) { mNameStack.pop_back(); mMappedNameStack.pop_back(); } visitNamedVariable(variable, isRowMajor, name, mappedName, mArraySizeStack); } // BlockEncoderVisitor implementation. BlockEncoderVisitor::BlockEncoderVisitor(const std::string &namePrefix, const std::string &mappedNamePrefix, BlockLayoutEncoder *encoder) : VariableNameVisitor(namePrefix, mappedNamePrefix), mEncoder(encoder) {} BlockEncoderVisitor::~BlockEncoderVisitor() = default; void BlockEncoderVisitor::enterStructAccess(const ShaderVariable &structVar, bool isRowMajor) { mStructStackSize++; if (!mIsTopLevelArrayStrideReady) { size_t structSize = mEncoder->getShaderVariableSize(structVar, isRowMajor); mTopLevelArrayStride *= structSize; mIsTopLevelArrayStrideReady = true; } VariableNameVisitor::enterStructAccess(structVar, isRowMajor); mEncoder->enterAggregateType(structVar); } void BlockEncoderVisitor::exitStructAccess(const ShaderVariable &structVar, bool isRowMajor) { mStructStackSize--; mEncoder->exitAggregateType(structVar); VariableNameVisitor::exitStructAccess(structVar, isRowMajor); } void BlockEncoderVisitor::enterArrayElement(const sh::ShaderVariable &arrayVar, unsigned int arrayElement) { if (mStructStackSize == 0 && !arrayVar.hasParentArrayIndex()) { // From the ES 3.1 spec "7.3.1.1 Naming Active Resources": // For an active shader storage block member declared as an array of an aggregate type, // an entry will be generated only for the first array element, regardless of its type. // Such block members are referred to as top-level arrays. If the block member is an // aggregate type, the enumeration rules are then applied recursively. if (arrayElement == 0) { mTopLevelArraySize = arrayVar.getOutermostArraySize(); mTopLevelArrayStride = arrayVar.getInnerArraySizeProduct(); mIsTopLevelArrayStrideReady = false; } else { mSkipEnabled = true; } } VariableNameVisitor::enterArrayElement(arrayVar, arrayElement); } void BlockEncoderVisitor::exitArrayElement(const sh::ShaderVariable &arrayVar, unsigned int arrayElement) { if (mStructStackSize == 0 && !arrayVar.hasParentArrayIndex()) { mTopLevelArraySize = 1; mTopLevelArrayStride = 0; mIsTopLevelArrayStrideReady = true; mSkipEnabled = false; } VariableNameVisitor::exitArrayElement(arrayVar, arrayElement); } void BlockEncoderVisitor::visitNamedVariable(const ShaderVariable &variable, bool isRowMajor, const std::string &name, const std::string &mappedName, const std::vector &arraySizes) { std::vector innermostArraySize; if (variable.isArray()) { innermostArraySize.push_back(variable.getNestedArraySize(0)); } BlockMemberInfo variableInfo = mEncoder->encodeType(variable.type, innermostArraySize, isRowMajor); if (!mIsTopLevelArrayStrideReady) { ASSERT(mTopLevelArrayStride); mTopLevelArrayStride *= variableInfo.arrayStride; mIsTopLevelArrayStrideReady = true; } variableInfo.topLevelArrayStride = mTopLevelArrayStride; encodeVariable(variable, variableInfo, name, mappedName); } void TraverseShaderVariable(const ShaderVariable &variable, bool isRowMajorLayout, ShaderVariableVisitor *visitor) { bool rowMajorLayout = (isRowMajorLayout || variable.isRowMajorLayout); bool isRowMajor = rowMajorLayout && gl::IsMatrixType(variable.type); if (variable.isStruct()) { visitor->enterStruct(variable); if (variable.isArray()) { TraverseStructArrayVariable(variable, rowMajorLayout, visitor); } else { TraverseStructVariable(variable, rowMajorLayout, visitor); } visitor->exitStruct(variable); } else if (variable.isArrayOfArrays()) { TraverseArrayOfArraysVariable(variable, 0u, isRowMajor, visitor); } else if (gl::IsSamplerType(variable.type) || gl::IsImageType(variable.type) || variable.isFragmentInOut) { visitor->visitOpaqueObject(variable); } else { visitor->visitVariable(variable, isRowMajor); } } } // namespace sh