blob: 5547f2a6686cee32240d539c3ff7e9570f47c886 [file] [log] [blame]
//
// 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
{
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;
}
}
bool MarkVaryingLocations(const TVariable *variable,
const TField *field,
int location,
int elementCount,
LocationValidationMap *locationMap,
VariableAndField *conflictingSymbolOut,
const TField **conflictingFieldInNewSymbolOut)
{
for (int elementIndex = 0; elementIndex < elementCount; ++elementIndex)
{
const int offsetLocation = location + elementIndex;
auto conflict = locationMap->find(offsetLocation);
if (conflict != locationMap->end())
{
*conflictingSymbolOut = conflict->second;
*conflictingFieldInNewSymbolOut = field;
return false;
}
else
{
(*locationMap)[offsetLocation] = {variable, field};
}
}
return true;
}
} // 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 ValidateVaryingLocation(const TVariable *newVariable,
LocationValidationMap *locationMap,
GLenum shaderType,
VariableAndField *conflictingSymbolOut,
const TField **conflictingFieldInNewSymbolOut)
{
const TType &type = newVariable->getType();
const int location = type.getLayoutQualifier().location;
ASSERT(location >= 0);
bool ignoreVaryingArraySize = ShouldIgnoreVaryingArraySize(type.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 (type.isInterfaceBlock())
{
const int startLocation = location;
int currentLocation = location;
bool anyFieldWithLocation = false;
for (const TField *field : type.getInterfaceBlock()->fields())
{
const int fieldLocation = field->type()->getLayoutQualifier().location;
if (fieldLocation >= 0)
{
currentLocation = fieldLocation;
anyFieldWithLocation = true;
}
const int fieldLocationCount = GetFieldLocationCount(field);
if (!MarkVaryingLocations(newVariable, field, currentLocation, fieldLocationCount,
locationMap, conflictingSymbolOut,
conflictingFieldInNewSymbolOut))
{
return false;
}
currentLocation += fieldLocationCount;
}
// Array interface blocks can't have location qualifiers on fields.
ASSERT(ignoreVaryingArraySize || !anyFieldWithLocation || !type.isArray());
if (!ignoreVaryingArraySize && type.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 - startLocation) * (type.getArraySizeProduct() - 1);
if (!MarkVaryingLocations(newVariable, nullptr, currentLocation, remainingLocations,
locationMap, conflictingSymbolOut,
conflictingFieldInNewSymbolOut))
{
return false;
}
}
}
else
{
const int elementCount = GetLocationCount(type, ignoreVaryingArraySize);
if (!MarkVaryingLocations(newVariable, nullptr, location, elementCount, locationMap,
conflictingSymbolOut, conflictingFieldInNewSymbolOut))
{
return false;
}
}
return true;
}
} // namespace sh