tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ValidateVaryingLocations.cpp (12799B)


      1 //
      2 // Copyright 2002 The ANGLE Project Authors. All rights reserved.
      3 // Use of this source code is governed by a BSD-style license that can be
      4 // found in the LICENSE file.
      5 //
      6 // The ValidateVaryingLocations function checks if there exists location conflicts on shader
      7 // varyings.
      8 //
      9 
     10 #include "ValidateVaryingLocations.h"
     11 
     12 #include "compiler/translator/Diagnostics.h"
     13 #include "compiler/translator/SymbolTable.h"
     14 #include "compiler/translator/tree_util/IntermTraverse.h"
     15 #include "compiler/translator/util.h"
     16 
     17 namespace sh
     18 {
     19 
     20 namespace
     21 {
     22 
     23 void error(const TIntermSymbol &symbol, const char *reason, TDiagnostics *diagnostics)
     24 {
     25    diagnostics->error(symbol.getLine(), reason, symbol.getName().data());
     26 }
     27 
     28 int GetStructLocationCount(const TStructure *structure);
     29 
     30 int GetFieldLocationCount(const TField *field)
     31 {
     32    int field_size         = 0;
     33    const TType *fieldType = field->type();
     34 
     35    if (fieldType->getStruct() != nullptr)
     36    {
     37        field_size = GetStructLocationCount(fieldType->getStruct());
     38    }
     39    else if (fieldType->isMatrix())
     40    {
     41        field_size = fieldType->getNominalSize();
     42    }
     43    else
     44    {
     45        ASSERT(fieldType->getSecondarySize() == 1);
     46        field_size = 1;
     47    }
     48 
     49    if (fieldType->isArray())
     50    {
     51        field_size *= fieldType->getArraySizeProduct();
     52    }
     53 
     54    return field_size;
     55 }
     56 
     57 int GetStructLocationCount(const TStructure *structure)
     58 {
     59    int totalLocation = 0;
     60    for (const TField *field : structure->fields())
     61    {
     62        totalLocation += GetFieldLocationCount(field);
     63    }
     64    return totalLocation;
     65 }
     66 
     67 int GetInterfaceBlockLocationCount(const TType &varyingType, bool ignoreVaryingArraySize)
     68 {
     69    int totalLocation = 0;
     70    for (const TField *field : varyingType.getInterfaceBlock()->fields())
     71    {
     72        totalLocation += GetFieldLocationCount(field);
     73    }
     74 
     75    if (!ignoreVaryingArraySize && varyingType.isArray())
     76    {
     77        totalLocation *= varyingType.getArraySizeProduct();
     78    }
     79    return totalLocation;
     80 }
     81 
     82 int GetLocationCount(const TType &varyingType, bool ignoreVaryingArraySize)
     83 {
     84    ASSERT(!varyingType.isInterfaceBlock());
     85 
     86    if (varyingType.getStruct() != nullptr)
     87    {
     88        int totalLocation = 0;
     89        for (const TField *field : varyingType.getStruct()->fields())
     90        {
     91            const TType *fieldType = field->type();
     92            ASSERT(fieldType->getStruct() == nullptr && !fieldType->isArray());
     93 
     94            totalLocation += GetFieldLocationCount(field);
     95        }
     96        return totalLocation;
     97    }
     98 
     99    ASSERT(varyingType.isMatrix() || varyingType.getSecondarySize() == 1);
    100    int elementLocationCount = varyingType.isMatrix() ? varyingType.getNominalSize() : 1;
    101 
    102    // [GL_EXT_shader_io_blocks SPEC Chapter 4.4.1]
    103    // Geometry shader inputs, tessellation control shader inputs and outputs, and tessellation
    104    // evaluation inputs all have an additional level of arrayness relative to other shader inputs
    105    // and outputs. This outer array level is removed from the type before considering how many
    106    // locations the type consumes.
    107    if (ignoreVaryingArraySize)
    108    {
    109        // Array-of-arrays cannot be inputs or outputs of a geometry shader.
    110        // (GL_EXT_geometry_shader SPEC issues(5))
    111        ASSERT(!varyingType.isArrayOfArrays());
    112        return elementLocationCount;
    113    }
    114 
    115    return elementLocationCount * varyingType.getArraySizeProduct();
    116 }
    117 
    118 bool ShouldIgnoreVaryingArraySize(TQualifier qualifier, GLenum shaderType)
    119 {
    120    bool isVaryingIn = IsShaderIn(qualifier) && qualifier != EvqPatchIn;
    121 
    122    switch (shaderType)
    123    {
    124        case GL_GEOMETRY_SHADER:
    125        case GL_TESS_EVALUATION_SHADER:
    126            return isVaryingIn;
    127        case GL_TESS_CONTROL_SHADER:
    128            return (IsShaderOut(qualifier) && qualifier != EvqPatchOut) || isVaryingIn;
    129        default:
    130            return false;
    131    }
    132 }
    133 
    134 struct SymbolAndField
    135 {
    136    const TIntermSymbol *symbol;
    137    const TField *field;
    138 };
    139 using LocationMap = std::map<int, SymbolAndField>;
    140 
    141 void MarkVaryingLocations(TDiagnostics *diagnostics,
    142                          const TIntermSymbol *varying,
    143                          const TField *field,
    144                          int location,
    145                          int elementCount,
    146                          LocationMap *locationMap)
    147 {
    148    for (int elementIndex = 0; elementIndex < elementCount; ++elementIndex)
    149    {
    150        const int offsetLocation = location + elementIndex;
    151        auto conflict            = locationMap->find(offsetLocation);
    152        if (conflict != locationMap->end())
    153        {
    154            std::stringstream strstr = sh::InitializeStream<std::stringstream>();
    155            strstr << "'" << varying->getName();
    156            if (field)
    157            {
    158                strstr << "." << field->name();
    159            }
    160            strstr << "' conflicting location with '" << conflict->second.symbol->getName();
    161            if (conflict->second.field)
    162            {
    163                strstr << "." << conflict->second.field->name();
    164            }
    165            strstr << "'";
    166            error(*varying, strstr.str().c_str(), diagnostics);
    167        }
    168        else
    169        {
    170            (*locationMap)[offsetLocation] = {varying, field};
    171        }
    172    }
    173 }
    174 
    175 using VaryingVector = std::vector<const TIntermSymbol *>;
    176 
    177 void ValidateShaderInterfaceAndAssignLocations(TDiagnostics *diagnostics,
    178                                               const VaryingVector &varyingVector,
    179                                               GLenum shaderType)
    180 {
    181    // Location conflicts can only happen when there are two or more varyings in varyingVector.
    182    if (varyingVector.size() <= 1)
    183    {
    184        return;
    185    }
    186 
    187    LocationMap locationMap;
    188    for (const TIntermSymbol *varying : varyingVector)
    189    {
    190        const TType &varyingType = varying->getType();
    191        const int location       = varyingType.getLayoutQualifier().location;
    192        ASSERT(location >= 0);
    193 
    194        bool ignoreVaryingArraySize =
    195            ShouldIgnoreVaryingArraySize(varying->getQualifier(), shaderType);
    196 
    197        // A varying is either:
    198        //
    199        // - A vector or matrix, which can take a number of contiguous locations
    200        // - A struct, which also takes a number of contiguous locations
    201        // - An interface block.
    202        //
    203        // Interface blocks can assign arbitrary locations to their fields, for example:
    204        //
    205        //     layout(location = 4) in block {
    206        //         vec4 a;                         // gets location 4
    207        //         vec4 b;                         // gets location 5
    208        //         layout(location = 7) vec4 c;    // gets location 7
    209        //         vec4 d;                         // gets location 8
    210        //         layout (location = 1) vec4 e;   // gets location 1
    211        //         vec4 f;                         // gets location 2
    212        //     };
    213        //
    214        // The following code therefore takes two paths.  For non-interface-block types, the number
    215        // of locations for the varying is calculated (elementCount), and all locations in
    216        // [location, location + elementCount) are marked as occupied.
    217        //
    218        // For interface blocks, a similar algorithm is implemented except each field is
    219        // individually marked with the location either advancing automatically or taking its value
    220        // from the field's layout qualifier.
    221 
    222        if (varyingType.isInterfaceBlock())
    223        {
    224            int currentLocation       = location;
    225            bool anyFieldWithLocation = false;
    226 
    227            for (const TField *field : varyingType.getInterfaceBlock()->fields())
    228            {
    229                const int fieldLocation = field->type()->getLayoutQualifier().location;
    230                if (fieldLocation >= 0)
    231                {
    232                    currentLocation      = fieldLocation;
    233                    anyFieldWithLocation = true;
    234                }
    235 
    236                const int fieldLocationCount = GetFieldLocationCount(field);
    237                MarkVaryingLocations(diagnostics, varying, field, currentLocation,
    238                                     fieldLocationCount, &locationMap);
    239 
    240                currentLocation += fieldLocationCount;
    241            }
    242 
    243            // Array interface blocks can't have location qualifiers on fields.
    244            ASSERT(ignoreVaryingArraySize || !anyFieldWithLocation || !varyingType.isArray());
    245 
    246            if (!ignoreVaryingArraySize && varyingType.isArray())
    247            {
    248                // This is only reached if the varying is an array of interface blocks, with only a
    249                // layout qualifier on the block itself, for example:
    250                //
    251                //     layout(location = 4) in block {
    252                //         vec4 a;
    253                //         vec4 b;
    254                //         vec4 c;
    255                //         vec4 d;
    256                //     } instance[N];
    257                //
    258                // The locations for instance[0] are already marked by the above code, so we need to
    259                // further mark locations occupied by instances [1, N).  |currentLocation| is
    260                // already just past the end of instance[0], which is the beginning of instance[1].
    261                //
    262                int remainingLocations = currentLocation * (varyingType.getArraySizeProduct() - 1);
    263                MarkVaryingLocations(diagnostics, varying, nullptr, currentLocation,
    264                                     remainingLocations, &locationMap);
    265            }
    266        }
    267        else
    268        {
    269            const int elementCount = GetLocationCount(varying->getType(), ignoreVaryingArraySize);
    270            MarkVaryingLocations(diagnostics, varying, nullptr, location, elementCount,
    271                                 &locationMap);
    272        }
    273    }
    274 }
    275 
    276 class ValidateVaryingLocationsTraverser : public TIntermTraverser
    277 {
    278  public:
    279    ValidateVaryingLocationsTraverser(GLenum shaderType);
    280    void validate(TDiagnostics *diagnostics);
    281 
    282  private:
    283    bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
    284    bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override;
    285 
    286    VaryingVector mInputVaryingsWithLocation;
    287    VaryingVector mOutputVaryingsWithLocation;
    288    GLenum mShaderType;
    289 };
    290 
    291 ValidateVaryingLocationsTraverser::ValidateVaryingLocationsTraverser(GLenum shaderType)
    292    : TIntermTraverser(true, false, false), mShaderType(shaderType)
    293 {}
    294 
    295 bool ValidateVaryingLocationsTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node)
    296 {
    297    const TIntermSequence &sequence = *(node->getSequence());
    298    ASSERT(!sequence.empty());
    299 
    300    const TIntermSymbol *symbol = sequence.front()->getAsSymbolNode();
    301    if (symbol == nullptr)
    302    {
    303        return false;
    304    }
    305 
    306    if (symbol->variable().symbolType() == SymbolType::Empty)
    307    {
    308        return false;
    309    }
    310 
    311    // Collect varyings that have explicit 'location' qualifiers.
    312    const TQualifier qualifier = symbol->getQualifier();
    313    if (symbol->getType().getLayoutQualifier().location != -1)
    314    {
    315        if (IsVaryingIn(qualifier))
    316        {
    317            mInputVaryingsWithLocation.push_back(symbol);
    318        }
    319        else if (IsVaryingOut(qualifier))
    320        {
    321            mOutputVaryingsWithLocation.push_back(symbol);
    322        }
    323    }
    324 
    325    return false;
    326 }
    327 
    328 bool ValidateVaryingLocationsTraverser::visitFunctionDefinition(Visit visit,
    329                                                                TIntermFunctionDefinition *node)
    330 {
    331    // We stop traversing function definitions because varyings cannot be defined in a function.
    332    return false;
    333 }
    334 
    335 void ValidateVaryingLocationsTraverser::validate(TDiagnostics *diagnostics)
    336 {
    337    ASSERT(diagnostics);
    338 
    339    ValidateShaderInterfaceAndAssignLocations(diagnostics, mInputVaryingsWithLocation, mShaderType);
    340    ValidateShaderInterfaceAndAssignLocations(diagnostics, mOutputVaryingsWithLocation,
    341                                              mShaderType);
    342 }
    343 
    344 }  // anonymous namespace
    345 
    346 unsigned int CalculateVaryingLocationCount(const TType &varyingType, GLenum shaderType)
    347 {
    348    const TQualifier qualifier        = varyingType.getQualifier();
    349    const bool ignoreVaryingArraySize = ShouldIgnoreVaryingArraySize(qualifier, shaderType);
    350 
    351    if (varyingType.isInterfaceBlock())
    352    {
    353        return GetInterfaceBlockLocationCount(varyingType, ignoreVaryingArraySize);
    354    }
    355 
    356    return GetLocationCount(varyingType, ignoreVaryingArraySize);
    357 }
    358 
    359 bool ValidateVaryingLocations(TIntermBlock *root, TDiagnostics *diagnostics, GLenum shaderType)
    360 {
    361    ValidateVaryingLocationsTraverser varyingValidator(shaderType);
    362    root->traverse(&varyingValidator);
    363    int numErrorsBefore = diagnostics->numErrors();
    364    varyingValidator.validate(diagnostics);
    365    return (diagnostics->numErrors() == numErrorsBefore);
    366 }
    367 
    368 }  // namespace sh