Edit

kc3-lang/angle/src/compiler/translator/ValidateVaryingLocations.cpp

Branch :

  • Show log

    Commit

  • Author : Olli Etuaho
    Date : 2018-03-16 10:43:11
    Hash : c26214de
    Message : Move AST utilities to a subdirectory Move AST related utilities to compiler/translator/tree_util. BUG=angleproject:2409 TEST=angle_unittests Change-Id: I7567c2f6f2710292029263257c7ac26e2a144ac8 Reviewed-on: https://chromium-review.googlesource.com/966032 Reviewed-by: Jamie Madill <jmadill@chromium.org> Reviewed-by: Corentin Wallez <cwallez@chromium.org> Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>

  • src/compiler/translator/ValidateVaryingLocations.cpp
  • //
    // 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;
                    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