summaryrefslogtreecommitdiffstats
path: root/gfx/angle/checkout/src/compiler/translator/blocklayout.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/angle/checkout/src/compiler/translator/blocklayout.cpp')
-rw-r--r--gfx/angle/checkout/src/compiler/translator/blocklayout.cpp666
1 files changed, 666 insertions, 0 deletions
diff --git a/gfx/angle/checkout/src/compiler/translator/blocklayout.cpp b/gfx/angle/checkout/src/compiler/translator/blocklayout.cpp
new file mode 100644
index 0000000000..24a2640dca
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/blocklayout.cpp
@@ -0,0 +1,666 @@
+//
+// 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 <typename VarT>
+void GetInterfaceBlockInfo(const std::vector<VarT> &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<ShaderVariable> &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<std::string> &nameStack)
+{
+ std::stringstream strstr = sh::InitializeStream<std::stringstream>();
+ 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<size_t>(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<unsigned int> &arraySizes,
+ bool isRowMajorMatrix)
+{
+ int arrayStride;
+ int matrixStride;
+
+ getBlockLayoutInfo(type, arraySizes, isRowMajorMatrix, &arrayStride, &matrixStride);
+
+ const BlockMemberInfo memberInfo(static_cast<int>(mCurrentOffset * kBytesPerComponent),
+ static_cast<int>(arrayStride * kBytesPerComponent),
+ static_cast<int>(matrixStride * kBytesPerComponent),
+ isRowMajorMatrix);
+
+ advanceOffset(type, arraySizes, isRowMajorMatrix, arrayStride, matrixStride);
+
+ return memberInfo;
+}
+
+BlockMemberInfo BlockLayoutEncoder::encodeArrayOfPreEncodedStructs(
+ size_t size,
+ const std::vector<unsigned int> &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<int>(mCurrentOffset * kBytesPerComponent),
+ static_cast<int>(arrayStride), -1, false);
+
+ angle::base::CheckedNumeric<size_t> checkedOffset(arrayStride);
+ checkedOffset *= outermostArraySize;
+ checkedOffset /= kBytesPerComponent;
+ checkedOffset += mCurrentOffset;
+ mCurrentOffset = checkedOffset.ValueOrDefault(std::numeric_limits<size_t>::max());
+
+ return memberInfo;
+}
+
+size_t BlockLayoutEncoder::getCurrentOffset() const
+{
+ angle::base::CheckedNumeric<size_t> checkedOffset(mCurrentOffset);
+ checkedOffset *= kBytesPerComponent;
+ return checkedOffset.ValueOrDefault(std::numeric_limits<size_t>::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<size_t> checkedOffset(mCurrentOffset);
+ checkedOffset += baseAlignment;
+ checkedOffset -= 1;
+ angle::base::CheckedNumeric<size_t> checkedAlignmentOffset = checkedOffset;
+ checkedAlignmentOffset %= baseAlignment;
+ checkedOffset -= checkedAlignmentOffset.ValueOrDefault(std::numeric_limits<size_t>::max());
+ mCurrentOffset = checkedOffset.ValueOrDefault(std::numeric_limits<size_t>::max());
+}
+
+// StubBlockEncoder implementation.
+void StubBlockEncoder::getBlockLayoutInfo(GLenum type,
+ const std::vector<unsigned int> &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<unsigned int> &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<int>(getTypeBaseAlignment(type, isRowMajorMatrix));
+
+ if (!arraySizes.empty())
+ {
+ const int numRegisters = gl::MatrixRegisterCount(type, isRowMajorMatrix);
+ arrayStride =
+ static_cast<int>(getTypeBaseAlignment(type, isRowMajorMatrix) * numRegisters);
+ }
+ }
+ else if (!arraySizes.empty())
+ {
+ baseAlignment = static_cast<int>(getTypeBaseAlignment(type, false));
+ arrayStride = static_cast<int>(getTypeBaseAlignment(type, false));
+ }
+ else
+ {
+ const size_t numComponents = static_cast<size_t>(gl::VariableComponentCount(type));
+ baseAlignment = ComponentAlignment(numComponents);
+ }
+
+ align(baseAlignment);
+
+ *matrixStrideOut = matrixStride;
+ *arrayStrideOut = arrayStride;
+}
+
+void Std140BlockEncoder::advanceOffset(GLenum type,
+ const std::vector<unsigned int> &arraySizes,
+ bool isRowMajorMatrix,
+ int arrayStride,
+ int matrixStride)
+{
+ if (!arraySizes.empty())
+ {
+ angle::base::CheckedNumeric<size_t> checkedOffset(arrayStride);
+ checkedOffset *= gl::ArraySizeProduct(arraySizes);
+ checkedOffset += mCurrentOffset;
+ mCurrentOffset = checkedOffset.ValueOrDefault(std::numeric_limits<size_t>::max());
+ }
+ else if (gl::IsMatrixType(type))
+ {
+ angle::base::CheckedNumeric<size_t> checkedOffset(matrixStride);
+ checkedOffset *= gl::MatrixRegisterCount(type, isRowMajorMatrix);
+ checkedOffset += mCurrentOffset;
+ mCurrentOffset = checkedOffset.ValueOrDefault(std::numeric_limits<size_t>::max());
+ }
+ else
+ {
+ angle::base::CheckedNumeric<size_t> checkedOffset(mCurrentOffset);
+ checkedOffset += gl::VariableComponentCount(type);
+ mCurrentOffset = checkedOffset.ValueOrDefault(std::numeric_limits<size_t>::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<ShaderVariable> &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<ShaderVariable> &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<std::stringstream>();
+ 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<unsigned int> &arraySizes)
+{
+ std::vector<unsigned int> 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