diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /gfx/angle/checkout/src/compiler/translator/ValidateVaryingLocations.cpp | |
parent | Initial commit. (diff) | |
download | thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/angle/checkout/src/compiler/translator/ValidateVaryingLocations.cpp')
-rw-r--r-- | gfx/angle/checkout/src/compiler/translator/ValidateVaryingLocations.cpp | 368 |
1 files changed, 368 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..3c2eeeb5bc --- /dev/null +++ b/gfx/angle/checkout/src/compiler/translator/ValidateVaryingLocations.cpp @@ -0,0 +1,368 @@ +// +// Copyright 2002 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 GetStructLocationCount(const TStructure *structure); + +int GetFieldLocationCount(const TField *field) +{ + int field_size = 0; + const TType *fieldType = field->type(); + + if (fieldType->getStruct() != nullptr) + { + field_size = GetStructLocationCount(fieldType->getStruct()); + } + else if (fieldType->isMatrix()) + { + field_size = fieldType->getNominalSize(); + } + else + { + ASSERT(fieldType->getSecondarySize() == 1); + field_size = 1; + } + + if (fieldType->isArray()) + { + field_size *= fieldType->getArraySizeProduct(); + } + + return field_size; +} + +int GetStructLocationCount(const TStructure *structure) +{ + int totalLocation = 0; + for (const TField *field : structure->fields()) + { + totalLocation += GetFieldLocationCount(field); + } + return totalLocation; +} + +int GetInterfaceBlockLocationCount(const TType &varyingType, bool ignoreVaryingArraySize) +{ + int totalLocation = 0; + for (const TField *field : varyingType.getInterfaceBlock()->fields()) + { + totalLocation += GetFieldLocationCount(field); + } + + if (!ignoreVaryingArraySize && varyingType.isArray()) + { + totalLocation *= varyingType.getArraySizeProduct(); + } + return totalLocation; +} + +int GetLocationCount(const TType &varyingType, bool ignoreVaryingArraySize) +{ + ASSERT(!varyingType.isInterfaceBlock()); + + if (varyingType.getStruct() != nullptr) + { + int totalLocation = 0; + for (const TField *field : varyingType.getStruct()->fields()) + { + const TType *fieldType = field->type(); + ASSERT(fieldType->getStruct() == nullptr && !fieldType->isArray()); + + totalLocation += GetFieldLocationCount(field); + } + return totalLocation; + } + + ASSERT(varyingType.isMatrix() || varyingType.getSecondarySize() == 1); + int elementLocationCount = varyingType.isMatrix() ? varyingType.getNominalSize() : 1; + + // [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. + 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 elementLocationCount; + } + + return elementLocationCount * varyingType.getArraySizeProduct(); +} + +bool ShouldIgnoreVaryingArraySize(TQualifier qualifier, GLenum shaderType) +{ + bool isVaryingIn = IsShaderIn(qualifier) && qualifier != EvqPatchIn; + + switch (shaderType) + { + case GL_GEOMETRY_SHADER: + case GL_TESS_EVALUATION_SHADER: + return isVaryingIn; + case GL_TESS_CONTROL_SHADER: + return (IsShaderOut(qualifier) && qualifier != EvqPatchOut) || isVaryingIn; + default: + return false; + } +} + +struct SymbolAndField +{ + const TIntermSymbol *symbol; + const TField *field; +}; +using LocationMap = std::map<int, SymbolAndField>; + +void MarkVaryingLocations(TDiagnostics *diagnostics, + const TIntermSymbol *varying, + const TField *field, + int location, + int elementCount, + LocationMap *locationMap) +{ + for (int elementIndex = 0; elementIndex < elementCount; ++elementIndex) + { + const int offsetLocation = location + elementIndex; + auto conflict = locationMap->find(offsetLocation); + if (conflict != locationMap->end()) + { + std::stringstream strstr = sh::InitializeStream<std::stringstream>(); + strstr << "'" << varying->getName(); + if (field) + { + strstr << "." << field->name(); + } + strstr << "' conflicting location with '" << conflict->second.symbol->getName(); + if (conflict->second.field) + { + strstr << "." << conflict->second.field->name(); + } + strstr << "'"; + error(*varying, strstr.str().c_str(), diagnostics); + } + else + { + (*locationMap)[offsetLocation] = {varying, field}; + } + } +} + +using VaryingVector = std::vector<const TIntermSymbol *>; + +void ValidateShaderInterfaceAndAssignLocations(TDiagnostics *diagnostics, + const VaryingVector &varyingVector, + GLenum shaderType) +{ + // Location conflicts can only happen when there are two or more varyings in varyingVector. + if (varyingVector.size() <= 1) + { + return; + } + + LocationMap locationMap; + for (const TIntermSymbol *varying : varyingVector) + { + const TType &varyingType = varying->getType(); + const int location = varyingType.getLayoutQualifier().location; + ASSERT(location >= 0); + + bool ignoreVaryingArraySize = + ShouldIgnoreVaryingArraySize(varying->getQualifier(), shaderType); + + // A varying is either: + // + // - A vector or matrix, which can take a number of contiguous locations + // - A struct, which also takes a number of contiguous locations + // - An interface block. + // + // Interface blocks can assign arbitrary locations to their fields, for example: + // + // layout(location = 4) in block { + // vec4 a; // gets location 4 + // vec4 b; // gets location 5 + // layout(location = 7) vec4 c; // gets location 7 + // vec4 d; // gets location 8 + // layout (location = 1) vec4 e; // gets location 1 + // vec4 f; // gets location 2 + // }; + // + // The following code therefore takes two paths. For non-interface-block types, the number + // of locations for the varying is calculated (elementCount), and all locations in + // [location, location + elementCount) are marked as occupied. + // + // For interface blocks, a similar algorithm is implemented except each field is + // individually marked with the location either advancing automatically or taking its value + // from the field's layout qualifier. + + if (varyingType.isInterfaceBlock()) + { + int currentLocation = location; + bool anyFieldWithLocation = false; + + for (const TField *field : varyingType.getInterfaceBlock()->fields()) + { + const int fieldLocation = field->type()->getLayoutQualifier().location; + if (fieldLocation >= 0) + { + currentLocation = fieldLocation; + anyFieldWithLocation = true; + } + + const int fieldLocationCount = GetFieldLocationCount(field); + MarkVaryingLocations(diagnostics, varying, field, currentLocation, + fieldLocationCount, &locationMap); + + currentLocation += fieldLocationCount; + } + + // Array interface blocks can't have location qualifiers on fields. + ASSERT(ignoreVaryingArraySize || !anyFieldWithLocation || !varyingType.isArray()); + + if (!ignoreVaryingArraySize && varyingType.isArray()) + { + // This is only reached if the varying is an array of interface blocks, with only a + // layout qualifier on the block itself, for example: + // + // layout(location = 4) in block { + // vec4 a; + // vec4 b; + // vec4 c; + // vec4 d; + // } instance[N]; + // + // The locations for instance[0] are already marked by the above code, so we need to + // further mark locations occupied by instances [1, N). |currentLocation| is + // already just past the end of instance[0], which is the beginning of instance[1]. + // + int remainingLocations = currentLocation * (varyingType.getArraySizeProduct() - 1); + MarkVaryingLocations(diagnostics, varying, nullptr, currentLocation, + remainingLocations, &locationMap); + } + } + else + { + const int elementCount = GetLocationCount(varying->getType(), ignoreVaryingArraySize); + MarkVaryingLocations(diagnostics, varying, nullptr, location, elementCount, + &locationMap); + } + } +} + +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); + + ValidateShaderInterfaceAndAssignLocations(diagnostics, mInputVaryingsWithLocation, mShaderType); + ValidateShaderInterfaceAndAssignLocations(diagnostics, mOutputVaryingsWithLocation, + mShaderType); +} + +} // anonymous namespace + +unsigned int CalculateVaryingLocationCount(const TType &varyingType, GLenum shaderType) +{ + const TQualifier qualifier = varyingType.getQualifier(); + const bool ignoreVaryingArraySize = ShouldIgnoreVaryingArraySize(qualifier, shaderType); + + if (varyingType.isInterfaceBlock()) + { + return GetInterfaceBlockLocationCount(varyingType, ignoreVaryingArraySize); + } + + return GetLocationCount(varyingType, ignoreVaryingArraySize); +} + +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 |