// // 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/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.getSymbol().c_str()); } 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_OES_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_OES_geometry_shader SPEC issues(5)) ASSERT(!varyingType.isArrayOfArrays()); return varyingType.getSecondarySize(); } else { return varyingType.getSecondarySize() * static_cast(varyingType.getArraySizeProduct()); } } using VaryingVector = std::vector; 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 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; strstr << "'" << varying->getSymbol() << "' conflicting location with previously defined '" << locationMap[offsetLocation]->getSymbol() << "'"; 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; } // 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_OES); 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