diff options
Diffstat (limited to 'gfx/angle/checkout/src/compiler/translator/ValidateVaryingLocations.cpp')
-rw-r--r-- | gfx/angle/checkout/src/compiler/translator/ValidateVaryingLocations.cpp | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/gfx/angle/checkout/src/compiler/translator/ValidateVaryingLocations.cpp b/gfx/angle/checkout/src/compiler/translator/ValidateVaryingLocations.cpp new file mode 100644 index 0000000000..394e35c8d8 --- /dev/null +++ b/gfx/angle/checkout/src/compiler/translator/ValidateVaryingLocations.cpp @@ -0,0 +1,179 @@ +// +// Copyright (c) 2002-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. +// +// The ValidateVaryingLocations function checks if there exists location conflicts on shader +// varyings. +// + +#include "ValidateVaryingLocations.h" + +#include "compiler/translator/Diagnostics.h" +#include "compiler/translator/SymbolTable.h" +#include "compiler/translator/tree_util/IntermTraverse.h" +#include "compiler/translator/util.h" + +namespace sh +{ + +namespace +{ + +void error(const TIntermSymbol &symbol, const char *reason, TDiagnostics *diagnostics) +{ + diagnostics->error(symbol.getLine(), reason, symbol.getName().data()); +} + +int GetLocationCount(const TIntermSymbol *varying, bool ignoreVaryingArraySize) +{ + const auto &varyingType = varying->getType(); + if (varyingType.getStruct() != nullptr) + { + ASSERT(!varyingType.isArray()); + int totalLocation = 0; + for (const auto *field : varyingType.getStruct()->fields()) + { + const auto *fieldType = field->type(); + ASSERT(fieldType->getStruct() == nullptr && !fieldType->isArray()); + + totalLocation += fieldType->getSecondarySize(); + } + return totalLocation; + } + // [GL_EXT_shader_io_blocks SPEC Chapter 4.4.1] + // Geometry shader inputs, tessellation control shader inputs and outputs, and tessellation + // evaluation inputs all have an additional level of arrayness relative to other shader inputs + // and outputs. This outer array level is removed from the type before considering how many + // locations the type consumes. + else if (ignoreVaryingArraySize) + { + // Array-of-arrays cannot be inputs or outputs of a geometry shader. + // (GL_EXT_geometry_shader SPEC issues(5)) + ASSERT(!varyingType.isArrayOfArrays()); + return varyingType.getSecondarySize(); + } + else + { + return varyingType.getSecondarySize() * static_cast<int>(varyingType.getArraySizeProduct()); + } +} + +using VaryingVector = std::vector<const TIntermSymbol *>; + +void ValidateShaderInterface(TDiagnostics *diagnostics, + VaryingVector &varyingVector, + bool ignoreVaryingArraySize) +{ + // Location conflicts can only happen when there are two or more varyings in varyingVector. + if (varyingVector.size() <= 1) + { + return; + } + + std::map<int, const TIntermSymbol *> locationMap; + for (const TIntermSymbol *varying : varyingVector) + { + const int location = varying->getType().getLayoutQualifier().location; + ASSERT(location >= 0); + + const int elementCount = GetLocationCount(varying, ignoreVaryingArraySize); + for (int elementIndex = 0; elementIndex < elementCount; ++elementIndex) + { + const int offsetLocation = location + elementIndex; + if (locationMap.find(offsetLocation) != locationMap.end()) + { + std::stringstream strstr = sh::InitializeStream<std::stringstream>(); + strstr << "'" << varying->getName() + << "' conflicting location with previously defined '" + << locationMap[offsetLocation]->getName() << "'"; + error(*varying, strstr.str().c_str(), diagnostics); + } + else + { + locationMap[offsetLocation] = varying; + } + } + } +} + +class ValidateVaryingLocationsTraverser : public TIntermTraverser +{ + public: + ValidateVaryingLocationsTraverser(GLenum shaderType); + void validate(TDiagnostics *diagnostics); + + private: + bool visitDeclaration(Visit visit, TIntermDeclaration *node) override; + bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override; + + VaryingVector mInputVaryingsWithLocation; + VaryingVector mOutputVaryingsWithLocation; + GLenum mShaderType; +}; + +ValidateVaryingLocationsTraverser::ValidateVaryingLocationsTraverser(GLenum shaderType) + : TIntermTraverser(true, false, false), mShaderType(shaderType) +{} + +bool ValidateVaryingLocationsTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node) +{ + const TIntermSequence &sequence = *(node->getSequence()); + ASSERT(!sequence.empty()); + + const TIntermSymbol *symbol = sequence.front()->getAsSymbolNode(); + if (symbol == nullptr) + { + return false; + } + + if (symbol->variable().symbolType() == SymbolType::Empty) + { + return false; + } + + // Collect varyings that have explicit 'location' qualifiers. + const TQualifier qualifier = symbol->getQualifier(); + if (symbol->getType().getLayoutQualifier().location != -1) + { + if (IsVaryingIn(qualifier)) + { + mInputVaryingsWithLocation.push_back(symbol); + } + else if (IsVaryingOut(qualifier)) + { + mOutputVaryingsWithLocation.push_back(symbol); + } + } + + return false; +} + +bool ValidateVaryingLocationsTraverser::visitFunctionDefinition(Visit visit, + TIntermFunctionDefinition *node) +{ + // We stop traversing function definitions because varyings cannot be defined in a function. + return false; +} + +void ValidateVaryingLocationsTraverser::validate(TDiagnostics *diagnostics) +{ + ASSERT(diagnostics); + + ValidateShaderInterface(diagnostics, mInputVaryingsWithLocation, + mShaderType == GL_GEOMETRY_SHADER_EXT); + ValidateShaderInterface(diagnostics, mOutputVaryingsWithLocation, false); +} + +} // anonymous namespace + +bool ValidateVaryingLocations(TIntermBlock *root, TDiagnostics *diagnostics, GLenum shaderType) +{ + ValidateVaryingLocationsTraverser varyingValidator(shaderType); + root->traverse(&varyingValidator); + int numErrorsBefore = diagnostics->numErrors(); + varyingValidator.validate(diagnostics); + return (diagnostics->numErrors() == numErrorsBefore); +} + +} // namespace sh
\ No newline at end of file |