1153 lines
44 KiB
C++
1153 lines
44 KiB
C++
//
|
|
// Copyright 2015 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.
|
|
//
|
|
// VaryingPacking:
|
|
// Class which describes a mapping from varyings to registers, according
|
|
// to the spec, or using custom packing algorithms. We also keep a register
|
|
// allocation list for the D3D renderer.
|
|
//
|
|
|
|
#include "libANGLE/VaryingPacking.h"
|
|
|
|
#include "common/utilities.h"
|
|
#include "libANGLE/Program.h"
|
|
#include "libANGLE/ProgramExecutable.h"
|
|
#include "libANGLE/Shader.h"
|
|
|
|
namespace gl
|
|
{
|
|
|
|
namespace
|
|
{
|
|
|
|
// true if varying x has a higher priority in packing than y
|
|
bool ComparePackedVarying(const PackedVarying &x, const PackedVarying &y)
|
|
{
|
|
// If the PackedVarying 'x' or 'y' to be compared is an array element for transform feedback,
|
|
// this clones an equivalent non-array shader variable 'vx' or 'vy' for actual comparison
|
|
// instead. For I/O block arrays, the array index is used in the comparison.
|
|
sh::ShaderVariable vx, vy;
|
|
const sh::ShaderVariable *px, *py;
|
|
|
|
px = &x.varying();
|
|
py = &y.varying();
|
|
|
|
if (x.isTransformFeedbackArrayElement())
|
|
{
|
|
vx = *px;
|
|
vx.arraySizes.clear();
|
|
px = &vx;
|
|
}
|
|
|
|
if (y.isTransformFeedbackArrayElement())
|
|
{
|
|
vy = *py;
|
|
vy.arraySizes.clear();
|
|
py = &vy;
|
|
}
|
|
|
|
// Make sure struct fields end up together.
|
|
if (x.isStructField() != y.isStructField())
|
|
{
|
|
return x.isStructField();
|
|
}
|
|
|
|
if (x.isStructField())
|
|
{
|
|
ASSERT(y.isStructField());
|
|
|
|
if (x.getParentStructName() != y.getParentStructName())
|
|
{
|
|
return x.getParentStructName() < y.getParentStructName();
|
|
}
|
|
}
|
|
|
|
// For I/O block fields, order first by array index:
|
|
if (!x.isTransformFeedbackArrayElement() && !y.isTransformFeedbackArrayElement())
|
|
{
|
|
if (x.arrayIndex != y.arrayIndex)
|
|
{
|
|
return x.arrayIndex < y.arrayIndex;
|
|
}
|
|
}
|
|
|
|
// Then order by field index
|
|
if (x.fieldIndex != y.fieldIndex)
|
|
{
|
|
return x.fieldIndex < y.fieldIndex;
|
|
}
|
|
|
|
// Then order by secondary field index
|
|
if (x.secondaryFieldIndex != y.secondaryFieldIndex)
|
|
{
|
|
return x.secondaryFieldIndex < y.secondaryFieldIndex;
|
|
}
|
|
|
|
// Otherwise order by variable
|
|
return gl::CompareShaderVar(*px, *py);
|
|
}
|
|
|
|
bool InterfaceVariablesMatch(const sh::ShaderVariable &front, const sh::ShaderVariable &back)
|
|
{
|
|
// Matching ruels from 7.4.1 Shader Interface Matching from the GLES 3.2 spec:
|
|
// - the two variables match in name, type, and qualification; or
|
|
// - the two variables are declared with the same location qualifier and match in type and
|
|
// qualification. Note that we use a more permissive check here thanks to front-end validation.
|
|
if (back.location != -1 && back.location == front.location)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (front.isShaderIOBlock != back.isShaderIOBlock)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Compare names, or if shader I/O blocks, block names.
|
|
const std::string &backName = back.isShaderIOBlock ? back.structOrBlockName : back.name;
|
|
const std::string &frontName = front.isShaderIOBlock ? front.structOrBlockName : front.name;
|
|
return backName == frontName;
|
|
}
|
|
|
|
GLint GetMaxShaderInputVectors(const Caps &caps, ShaderType shaderStage)
|
|
{
|
|
switch (shaderStage)
|
|
{
|
|
case ShaderType::TessControl:
|
|
return caps.maxTessControlInputComponents / 4;
|
|
case ShaderType::TessEvaluation:
|
|
return caps.maxTessEvaluationInputComponents / 4;
|
|
case ShaderType::Geometry:
|
|
return caps.maxGeometryInputComponents / 4;
|
|
case ShaderType::Fragment:
|
|
return caps.maxFragmentInputComponents / 4;
|
|
default:
|
|
return std::numeric_limits<GLint>::max();
|
|
}
|
|
}
|
|
|
|
GLint GetMaxShaderOutputVectors(const Caps &caps, ShaderType shaderStage)
|
|
{
|
|
switch (shaderStage)
|
|
{
|
|
case ShaderType::Vertex:
|
|
return caps.maxVertexOutputComponents / 4;
|
|
case ShaderType::TessControl:
|
|
return caps.maxTessControlOutputComponents / 4;
|
|
case ShaderType::TessEvaluation:
|
|
return caps.maxTessEvaluationOutputComponents / 4;
|
|
case ShaderType::Geometry:
|
|
return caps.maxGeometryOutputComponents / 4;
|
|
default:
|
|
return std::numeric_limits<GLint>::max();
|
|
}
|
|
}
|
|
|
|
bool ShouldSkipPackedVarying(const sh::ShaderVariable &varying, PackMode packMode)
|
|
{
|
|
// Don't pack gl_Position. Also don't count gl_PointSize for D3D9.
|
|
return varying.name == "gl_Position" ||
|
|
(varying.name == "gl_PointSize" && packMode == PackMode::ANGLE_NON_CONFORMANT_D3D9);
|
|
}
|
|
|
|
std::vector<unsigned int> StripVaryingArrayDimension(const sh::ShaderVariable *frontVarying,
|
|
ShaderType frontShaderStage,
|
|
const sh::ShaderVariable *backVarying,
|
|
ShaderType backShaderStage,
|
|
bool isStructField)
|
|
{
|
|
// "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 (backVarying && backVarying->isArray() && !backVarying->isPatch && !isStructField &&
|
|
(backShaderStage == ShaderType::Geometry || backShaderStage == ShaderType::TessEvaluation ||
|
|
backShaderStage == ShaderType::TessControl))
|
|
{
|
|
std::vector<unsigned int> arr = backVarying->arraySizes;
|
|
arr.pop_back();
|
|
return arr;
|
|
}
|
|
|
|
if (frontVarying && frontVarying->isArray() && !frontVarying->isPatch && !isStructField &&
|
|
frontShaderStage == ShaderType::TessControl)
|
|
{
|
|
std::vector<unsigned int> arr = frontVarying->arraySizes;
|
|
arr.pop_back();
|
|
return arr;
|
|
}
|
|
|
|
return frontVarying ? frontVarying->arraySizes : backVarying->arraySizes;
|
|
}
|
|
} // anonymous namespace
|
|
|
|
// Implementation of VaryingInShaderRef
|
|
VaryingInShaderRef::VaryingInShaderRef(ShaderType stageIn, const sh::ShaderVariable *varyingIn)
|
|
: varying(varyingIn), stage(stageIn)
|
|
{}
|
|
|
|
VaryingInShaderRef::~VaryingInShaderRef() = default;
|
|
|
|
VaryingInShaderRef::VaryingInShaderRef(VaryingInShaderRef &&other)
|
|
: varying(other.varying),
|
|
stage(other.stage),
|
|
parentStructName(std::move(other.parentStructName)),
|
|
parentStructMappedName(std::move(other.parentStructMappedName))
|
|
{}
|
|
|
|
VaryingInShaderRef &VaryingInShaderRef::operator=(VaryingInShaderRef &&other)
|
|
{
|
|
std::swap(varying, other.varying);
|
|
std::swap(stage, other.stage);
|
|
std::swap(parentStructName, other.parentStructName);
|
|
std::swap(parentStructMappedName, other.parentStructMappedName);
|
|
|
|
return *this;
|
|
}
|
|
|
|
// Implementation of PackedVarying
|
|
PackedVarying::PackedVarying(VaryingInShaderRef &&frontVaryingIn,
|
|
VaryingInShaderRef &&backVaryingIn,
|
|
sh::InterpolationType interpolationIn)
|
|
: PackedVarying(std::move(frontVaryingIn),
|
|
std::move(backVaryingIn),
|
|
interpolationIn,
|
|
GL_INVALID_INDEX,
|
|
0,
|
|
0)
|
|
{}
|
|
|
|
PackedVarying::PackedVarying(VaryingInShaderRef &&frontVaryingIn,
|
|
VaryingInShaderRef &&backVaryingIn,
|
|
sh::InterpolationType interpolationIn,
|
|
GLuint arrayIndexIn,
|
|
GLuint fieldIndexIn,
|
|
GLuint secondaryFieldIndexIn)
|
|
: frontVarying(std::move(frontVaryingIn)),
|
|
backVarying(std::move(backVaryingIn)),
|
|
interpolation(interpolationIn),
|
|
arrayIndex(arrayIndexIn),
|
|
isTransformFeedback(false),
|
|
fieldIndex(fieldIndexIn),
|
|
secondaryFieldIndex(secondaryFieldIndexIn)
|
|
{}
|
|
|
|
PackedVarying::~PackedVarying() = default;
|
|
|
|
PackedVarying::PackedVarying(PackedVarying &&other)
|
|
: frontVarying(std::move(other.frontVarying)),
|
|
backVarying(std::move(other.backVarying)),
|
|
interpolation(other.interpolation),
|
|
arrayIndex(other.arrayIndex),
|
|
isTransformFeedback(other.isTransformFeedback),
|
|
fieldIndex(other.fieldIndex),
|
|
secondaryFieldIndex(other.secondaryFieldIndex)
|
|
{}
|
|
|
|
PackedVarying &PackedVarying::operator=(PackedVarying &&other)
|
|
{
|
|
std::swap(frontVarying, other.frontVarying);
|
|
std::swap(backVarying, other.backVarying);
|
|
std::swap(interpolation, other.interpolation);
|
|
std::swap(arrayIndex, other.arrayIndex);
|
|
std::swap(isTransformFeedback, other.isTransformFeedback);
|
|
std::swap(fieldIndex, other.fieldIndex);
|
|
std::swap(secondaryFieldIndex, other.secondaryFieldIndex);
|
|
|
|
return *this;
|
|
}
|
|
|
|
unsigned int PackedVarying::getBasicTypeElementCount() const
|
|
{
|
|
// "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."
|
|
std::vector<unsigned int> arr =
|
|
StripVaryingArrayDimension(frontVarying.varying, frontVarying.stage, backVarying.varying,
|
|
backVarying.stage, isStructField());
|
|
return arr.empty() ? 1u : arr.back();
|
|
}
|
|
|
|
// Implementation of VaryingPacking
|
|
VaryingPacking::VaryingPacking() = default;
|
|
|
|
VaryingPacking::~VaryingPacking() = default;
|
|
|
|
void VaryingPacking::reset()
|
|
{
|
|
clearRegisterMap();
|
|
mRegisterList.clear();
|
|
mPackedVaryings.clear();
|
|
|
|
for (std::vector<std::string> &inactiveVaryingMappedNames : mInactiveVaryingMappedNames)
|
|
{
|
|
inactiveVaryingMappedNames.clear();
|
|
}
|
|
|
|
for (std::vector<std::string> &activeBuiltIns : mActiveOutputBuiltIns)
|
|
{
|
|
activeBuiltIns.clear();
|
|
}
|
|
}
|
|
|
|
void VaryingPacking::clearRegisterMap()
|
|
{
|
|
std::fill(mRegisterMap.begin(), mRegisterMap.end(), Register());
|
|
}
|
|
|
|
// Packs varyings into generic varying registers, using the algorithm from
|
|
// See [OpenGL ES Shading Language 1.00 rev. 17] appendix A section 7 page 111
|
|
// Also [OpenGL ES Shading Language 3.00 rev. 4] Section 11 page 119
|
|
// Returns false if unsuccessful.
|
|
bool VaryingPacking::packVaryingIntoRegisterMap(PackMode packMode,
|
|
const PackedVarying &packedVarying)
|
|
{
|
|
const sh::ShaderVariable &varying = packedVarying.varying();
|
|
|
|
// "Non - square matrices of type matCxR consume the same space as a square matrix of type matN
|
|
// where N is the greater of C and R."
|
|
// Here we are a bit more conservative and allow packing non-square matrices more tightly.
|
|
// Make sure we use transposed matrix types to count registers correctly.
|
|
ASSERT(!varying.isStruct());
|
|
GLenum transposedType = gl::TransposeMatrixType(varying.type);
|
|
unsigned int varyingRows = gl::VariableRowCount(transposedType);
|
|
unsigned int varyingColumns = gl::VariableColumnCount(transposedType);
|
|
|
|
// Special pack mode for D3D9. Each varying takes a full register, no sharing.
|
|
// TODO(jmadill): Implement more sophisticated component packing in D3D9.
|
|
if (packMode == PackMode::ANGLE_NON_CONFORMANT_D3D9)
|
|
{
|
|
varyingColumns = 4;
|
|
}
|
|
|
|
// "Variables of type mat2 occupies 2 complete rows."
|
|
// For non-WebGL contexts, we allow mat2 to occupy only two columns per row.
|
|
else if (packMode == PackMode::WEBGL_STRICT && varying.type == GL_FLOAT_MAT2)
|
|
{
|
|
varyingColumns = 4;
|
|
}
|
|
|
|
// "Arrays of size N are assumed to take N times the size of the base type"
|
|
// GLSL ES 3.10 section 4.3.6: Output variables cannot be arrays of arrays or arrays of
|
|
// structures, so we may use getBasicTypeElementCount().
|
|
const unsigned int elementCount = packedVarying.getBasicTypeElementCount();
|
|
varyingRows *= (packedVarying.isTransformFeedbackArrayElement() ? 1 : elementCount);
|
|
|
|
unsigned int maxVaryingVectors = static_cast<unsigned int>(mRegisterMap.size());
|
|
|
|
// Fail if we are packing a single over-large varying.
|
|
if (varyingRows > maxVaryingVectors)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// "For 2, 3 and 4 component variables packing is started using the 1st column of the 1st row.
|
|
// Variables are then allocated to successive rows, aligning them to the 1st column."
|
|
if (varyingColumns >= 2 && varyingColumns <= 4)
|
|
{
|
|
for (unsigned int row = 0; row <= maxVaryingVectors - varyingRows; ++row)
|
|
{
|
|
if (isRegisterRangeFree(row, 0, varyingRows, varyingColumns))
|
|
{
|
|
insertVaryingIntoRegisterMap(row, 0, varyingColumns, packedVarying);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// "For 2 component variables, when there are no spare rows, the strategy is switched to
|
|
// using the highest numbered row and the lowest numbered column where the variable will
|
|
// fit."
|
|
if (varyingColumns == 2)
|
|
{
|
|
for (unsigned int r = maxVaryingVectors - varyingRows + 1; r-- >= 1;)
|
|
{
|
|
if (isRegisterRangeFree(r, 2, varyingRows, 2))
|
|
{
|
|
insertVaryingIntoRegisterMap(r, 2, varyingColumns, packedVarying);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// "1 component variables have their own packing rule. They are packed in order of size, largest
|
|
// first. Each variable is placed in the column that leaves the least amount of space in the
|
|
// column and aligned to the lowest available rows within that column."
|
|
ASSERT(varyingColumns == 1);
|
|
unsigned int contiguousSpace[4] = {0};
|
|
unsigned int bestContiguousSpace[4] = {0};
|
|
unsigned int totalSpace[4] = {0};
|
|
|
|
for (unsigned int row = 0; row < maxVaryingVectors; ++row)
|
|
{
|
|
for (unsigned int column = 0; column < 4; ++column)
|
|
{
|
|
if (mRegisterMap[row][column])
|
|
{
|
|
contiguousSpace[column] = 0;
|
|
}
|
|
else
|
|
{
|
|
contiguousSpace[column]++;
|
|
totalSpace[column]++;
|
|
|
|
if (contiguousSpace[column] > bestContiguousSpace[column])
|
|
{
|
|
bestContiguousSpace[column] = contiguousSpace[column];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
unsigned int bestColumn = 0;
|
|
for (unsigned int column = 1; column < 4; ++column)
|
|
{
|
|
if (bestContiguousSpace[column] >= varyingRows &&
|
|
(bestContiguousSpace[bestColumn] < varyingRows ||
|
|
totalSpace[column] < totalSpace[bestColumn]))
|
|
{
|
|
bestColumn = column;
|
|
}
|
|
}
|
|
|
|
if (bestContiguousSpace[bestColumn] >= varyingRows)
|
|
{
|
|
for (unsigned int row = 0; row < maxVaryingVectors; row++)
|
|
{
|
|
if (isRegisterRangeFree(row, bestColumn, varyingRows, 1))
|
|
{
|
|
for (unsigned int arrayIndex = 0; arrayIndex < varyingRows; ++arrayIndex)
|
|
{
|
|
// If varyingRows > 1, it must be an array.
|
|
PackedVaryingRegister registerInfo;
|
|
registerInfo.packedVarying = &packedVarying;
|
|
registerInfo.registerRow = row + arrayIndex;
|
|
registerInfo.registerColumn = bestColumn;
|
|
registerInfo.varyingArrayIndex =
|
|
(packedVarying.isTransformFeedbackArrayElement() ? packedVarying.arrayIndex
|
|
: arrayIndex);
|
|
registerInfo.varyingRowIndex = 0;
|
|
// Do not record register info for builtins.
|
|
// TODO(jmadill): Clean this up.
|
|
if (!varying.isBuiltIn())
|
|
{
|
|
mRegisterList.push_back(registerInfo);
|
|
}
|
|
mRegisterMap[row + arrayIndex][bestColumn] = true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool VaryingPacking::isRegisterRangeFree(unsigned int registerRow,
|
|
unsigned int registerColumn,
|
|
unsigned int varyingRows,
|
|
unsigned int varyingColumns) const
|
|
{
|
|
for (unsigned int row = 0; row < varyingRows; ++row)
|
|
{
|
|
ASSERT(registerRow + row < mRegisterMap.size());
|
|
for (unsigned int column = 0; column < varyingColumns; ++column)
|
|
{
|
|
ASSERT(registerColumn + column < 4);
|
|
if (mRegisterMap[registerRow + row][registerColumn + column])
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void VaryingPacking::insertVaryingIntoRegisterMap(unsigned int registerRow,
|
|
unsigned int registerColumn,
|
|
unsigned int varyingColumns,
|
|
const PackedVarying &packedVarying)
|
|
{
|
|
unsigned int varyingRows = 0;
|
|
|
|
const sh::ShaderVariable &varying = packedVarying.varying();
|
|
ASSERT(!varying.isStruct());
|
|
GLenum transposedType = gl::TransposeMatrixType(varying.type);
|
|
varyingRows = gl::VariableRowCount(transposedType);
|
|
|
|
PackedVaryingRegister registerInfo;
|
|
registerInfo.packedVarying = &packedVarying;
|
|
registerInfo.registerColumn = registerColumn;
|
|
|
|
// GLSL ES 3.10 section 4.3.6: Output variables cannot be arrays of arrays or arrays of
|
|
// structures, so we may use getBasicTypeElementCount().
|
|
const unsigned int arrayElementCount = packedVarying.getBasicTypeElementCount();
|
|
for (unsigned int arrayElement = 0; arrayElement < arrayElementCount; ++arrayElement)
|
|
{
|
|
if (packedVarying.isTransformFeedbackArrayElement() &&
|
|
arrayElement != packedVarying.arrayIndex)
|
|
{
|
|
continue;
|
|
}
|
|
for (unsigned int varyingRow = 0; varyingRow < varyingRows; ++varyingRow)
|
|
{
|
|
registerInfo.registerRow = registerRow + (arrayElement * varyingRows) + varyingRow;
|
|
registerInfo.varyingRowIndex = varyingRow;
|
|
registerInfo.varyingArrayIndex = arrayElement;
|
|
// Do not record register info for builtins.
|
|
// TODO(jmadill): Clean this up.
|
|
if (!varying.isBuiltIn())
|
|
{
|
|
mRegisterList.push_back(registerInfo);
|
|
}
|
|
|
|
for (unsigned int columnIndex = 0; columnIndex < varyingColumns; ++columnIndex)
|
|
{
|
|
mRegisterMap[registerInfo.registerRow][registerColumn + columnIndex] = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void VaryingPacking::collectUserVarying(const ProgramVaryingRef &ref,
|
|
VaryingUniqueFullNames *uniqueFullNames)
|
|
{
|
|
const sh::ShaderVariable *input = ref.frontShader;
|
|
const sh::ShaderVariable *output = ref.backShader;
|
|
|
|
// Will get the vertex shader interpolation by default.
|
|
sh::InterpolationType interpolation = input ? input->interpolation : output->interpolation;
|
|
|
|
VaryingInShaderRef frontVarying(ref.frontShaderStage, input);
|
|
VaryingInShaderRef backVarying(ref.backShaderStage, output);
|
|
|
|
mPackedVaryings.emplace_back(std::move(frontVarying), std::move(backVarying), interpolation);
|
|
if (input && !input->isBuiltIn())
|
|
{
|
|
(*uniqueFullNames)[ref.frontShaderStage].insert(
|
|
mPackedVaryings.back().fullName(ref.frontShaderStage));
|
|
}
|
|
if (output && !output->isBuiltIn())
|
|
{
|
|
(*uniqueFullNames)[ref.backShaderStage].insert(
|
|
mPackedVaryings.back().fullName(ref.backShaderStage));
|
|
}
|
|
}
|
|
|
|
void VaryingPacking::collectUserVaryingField(const ProgramVaryingRef &ref,
|
|
GLuint arrayIndex,
|
|
GLuint fieldIndex,
|
|
GLuint secondaryFieldIndex,
|
|
VaryingUniqueFullNames *uniqueFullNames)
|
|
{
|
|
const sh::ShaderVariable *input = ref.frontShader;
|
|
const sh::ShaderVariable *output = ref.backShader;
|
|
|
|
// Will get the vertex shader interpolation by default.
|
|
sh::InterpolationType interpolation = input ? input->interpolation : output->interpolation;
|
|
|
|
const sh::ShaderVariable *frontField = input ? &input->fields[fieldIndex] : nullptr;
|
|
const sh::ShaderVariable *backField = output ? &output->fields[fieldIndex] : nullptr;
|
|
|
|
if (secondaryFieldIndex != GL_INVALID_INDEX)
|
|
{
|
|
frontField = frontField ? &frontField->fields[secondaryFieldIndex] : nullptr;
|
|
backField = backField ? &backField->fields[secondaryFieldIndex] : nullptr;
|
|
}
|
|
|
|
VaryingInShaderRef frontVarying(ref.frontShaderStage, frontField);
|
|
VaryingInShaderRef backVarying(ref.backShaderStage, backField);
|
|
|
|
if (input)
|
|
{
|
|
if (frontField->isShaderIOBlock)
|
|
{
|
|
frontVarying.parentStructName = input->structOrBlockName;
|
|
frontVarying.parentStructMappedName = input->mappedStructOrBlockName;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(!frontField->isStruct() && !frontField->isArray());
|
|
frontVarying.parentStructName = input->name;
|
|
frontVarying.parentStructMappedName = input->mappedName;
|
|
}
|
|
}
|
|
if (output)
|
|
{
|
|
if (backField->isShaderIOBlock)
|
|
{
|
|
backVarying.parentStructName = output->structOrBlockName;
|
|
backVarying.parentStructMappedName = output->mappedStructOrBlockName;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(!backField->isStruct() && !backField->isArray());
|
|
backVarying.parentStructName = output->name;
|
|
backVarying.parentStructMappedName = output->mappedName;
|
|
}
|
|
}
|
|
|
|
mPackedVaryings.emplace_back(std::move(frontVarying), std::move(backVarying), interpolation,
|
|
arrayIndex, fieldIndex,
|
|
secondaryFieldIndex == GL_INVALID_INDEX ? 0 : secondaryFieldIndex);
|
|
|
|
if (input)
|
|
{
|
|
(*uniqueFullNames)[ref.frontShaderStage].insert(
|
|
mPackedVaryings.back().fullName(ref.frontShaderStage));
|
|
}
|
|
if (output)
|
|
{
|
|
(*uniqueFullNames)[ref.backShaderStage].insert(
|
|
mPackedVaryings.back().fullName(ref.backShaderStage));
|
|
}
|
|
}
|
|
|
|
void VaryingPacking::collectUserVaryingTF(const ProgramVaryingRef &ref, size_t subscript)
|
|
{
|
|
const sh::ShaderVariable *input = ref.frontShader;
|
|
|
|
VaryingInShaderRef frontVarying(ref.frontShaderStage, input);
|
|
VaryingInShaderRef backVarying(ref.backShaderStage, nullptr);
|
|
|
|
mPackedVaryings.emplace_back(std::move(frontVarying), std::move(backVarying),
|
|
input->interpolation);
|
|
mPackedVaryings.back().arrayIndex = static_cast<GLuint>(subscript);
|
|
mPackedVaryings.back().isTransformFeedback = true;
|
|
}
|
|
|
|
void VaryingPacking::collectUserVaryingFieldTF(const ProgramVaryingRef &ref,
|
|
const sh::ShaderVariable &field,
|
|
GLuint fieldIndex,
|
|
GLuint secondaryFieldIndex)
|
|
{
|
|
const sh::ShaderVariable *input = ref.frontShader;
|
|
|
|
const sh::ShaderVariable *frontField = &field;
|
|
if (secondaryFieldIndex != GL_INVALID_INDEX)
|
|
{
|
|
frontField = &frontField->fields[secondaryFieldIndex];
|
|
}
|
|
|
|
VaryingInShaderRef frontVarying(ref.frontShaderStage, frontField);
|
|
VaryingInShaderRef backVarying(ref.backShaderStage, nullptr);
|
|
|
|
if (frontField->isShaderIOBlock)
|
|
{
|
|
frontVarying.parentStructName = input->structOrBlockName;
|
|
frontVarying.parentStructMappedName = input->mappedStructOrBlockName;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(!frontField->isStruct() && !frontField->isArray());
|
|
frontVarying.parentStructName = input->name;
|
|
frontVarying.parentStructMappedName = input->mappedName;
|
|
}
|
|
|
|
mPackedVaryings.emplace_back(std::move(frontVarying), std::move(backVarying),
|
|
input->interpolation, GL_INVALID_INDEX, fieldIndex,
|
|
secondaryFieldIndex == GL_INVALID_INDEX ? 0 : secondaryFieldIndex);
|
|
}
|
|
|
|
void VaryingPacking::collectVarying(const sh::ShaderVariable &varying,
|
|
const ProgramVaryingRef &ref,
|
|
PackMode packMode,
|
|
VaryingUniqueFullNames *uniqueFullNames)
|
|
{
|
|
const sh::ShaderVariable *input = ref.frontShader;
|
|
const sh::ShaderVariable *output = ref.backShader;
|
|
|
|
if (varying.isStruct())
|
|
{
|
|
std::vector<unsigned int> arraySizes = StripVaryingArrayDimension(
|
|
ref.frontShader, ref.frontShaderStage, ref.backShader, ref.backShaderStage, false);
|
|
const bool isArray = !arraySizes.empty();
|
|
const GLuint arraySize = isArray ? arraySizes[0] : 1;
|
|
|
|
for (GLuint arrayIndex = 0; arrayIndex < arraySize; ++arrayIndex)
|
|
{
|
|
const GLuint effectiveArrayIndex = isArray ? arrayIndex : GL_INVALID_INDEX;
|
|
for (GLuint fieldIndex = 0; fieldIndex < varying.fields.size(); ++fieldIndex)
|
|
{
|
|
const sh::ShaderVariable &fieldVarying = varying.fields[fieldIndex];
|
|
if (ShouldSkipPackedVarying(fieldVarying, packMode))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (fieldVarying.isStruct())
|
|
{
|
|
if (fieldVarying.isArray())
|
|
{
|
|
unsigned int structFieldArraySize = fieldVarying.arraySizes[0];
|
|
for (unsigned int fieldArrayIndex = 0;
|
|
fieldArrayIndex < structFieldArraySize; ++fieldArrayIndex)
|
|
{
|
|
for (GLuint nestedIndex = 0; nestedIndex < fieldVarying.fields.size();
|
|
nestedIndex++)
|
|
{
|
|
collectUserVaryingField(ref, effectiveArrayIndex, fieldIndex,
|
|
nestedIndex, uniqueFullNames);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (GLuint nestedIndex = 0; nestedIndex < fieldVarying.fields.size();
|
|
nestedIndex++)
|
|
{
|
|
collectUserVaryingField(ref, effectiveArrayIndex, fieldIndex,
|
|
nestedIndex, uniqueFullNames);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
collectUserVaryingField(ref, effectiveArrayIndex, fieldIndex, GL_INVALID_INDEX,
|
|
uniqueFullNames);
|
|
}
|
|
}
|
|
}
|
|
if (input)
|
|
{
|
|
(*uniqueFullNames)[ref.frontShaderStage].insert(input->name);
|
|
if (input->isShaderIOBlock)
|
|
{
|
|
(*uniqueFullNames)[ref.frontShaderStage].insert(input->structOrBlockName);
|
|
}
|
|
}
|
|
if (output)
|
|
{
|
|
(*uniqueFullNames)[ref.backShaderStage].insert(output->name);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
collectUserVarying(ref, uniqueFullNames);
|
|
}
|
|
}
|
|
|
|
void VaryingPacking::collectTFVarying(const std::string &tfVarying,
|
|
const ProgramVaryingRef &ref,
|
|
VaryingUniqueFullNames *uniqueFullNames)
|
|
{
|
|
const sh::ShaderVariable *input = ref.frontShader;
|
|
|
|
std::vector<unsigned int> subscripts;
|
|
std::string baseName = ParseResourceName(tfVarying, &subscripts);
|
|
|
|
// Already packed as active varying.
|
|
if ((*uniqueFullNames)[ref.frontShaderStage].count(tfVarying) > 0 ||
|
|
(*uniqueFullNames)[ref.frontShaderStage].count(baseName) > 0 ||
|
|
(input->isShaderIOBlock &&
|
|
(*uniqueFullNames)[ref.frontShaderStage].count(input->structOrBlockName) > 0))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (input->isStruct())
|
|
{
|
|
GLuint fieldIndex = 0;
|
|
const sh::ShaderVariable *field = input->findField(tfVarying, &fieldIndex);
|
|
if (field != nullptr)
|
|
{
|
|
ASSERT(input->isShaderIOBlock || (!field->isStruct() && !field->isArray()));
|
|
|
|
// If it's an I/O block whose member is being captured, pack every member of the
|
|
// block. Currently, we pack either all or none of an I/O block.
|
|
if (input->isShaderIOBlock)
|
|
{
|
|
for (fieldIndex = 0; fieldIndex < input->fields.size(); ++fieldIndex)
|
|
{
|
|
if (input->fields[fieldIndex].isStruct())
|
|
{
|
|
|
|
for (GLuint nestedIndex = 0;
|
|
nestedIndex < input->fields[fieldIndex].fields.size(); nestedIndex++)
|
|
{
|
|
collectUserVaryingFieldTF(ref, input->fields[fieldIndex], fieldIndex,
|
|
nestedIndex);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
collectUserVaryingFieldTF(ref, input->fields[fieldIndex], fieldIndex,
|
|
GL_INVALID_INDEX);
|
|
}
|
|
}
|
|
|
|
(*uniqueFullNames)[ref.frontShaderStage].insert(input->structOrBlockName);
|
|
}
|
|
else
|
|
{
|
|
collectUserVaryingFieldTF(ref, *field, fieldIndex, GL_INVALID_INDEX);
|
|
}
|
|
(*uniqueFullNames)[ref.frontShaderStage].insert(tfVarying);
|
|
(*uniqueFullNames)[ref.frontShaderStage].insert(input->name);
|
|
}
|
|
}
|
|
// Array as a whole and array element conflict has already been checked in
|
|
// linkValidateTransformFeedback.
|
|
else if (baseName == input->name)
|
|
{
|
|
size_t subscript = GL_INVALID_INDEX;
|
|
if (!subscripts.empty())
|
|
{
|
|
subscript = subscripts.back();
|
|
}
|
|
|
|
// only pack varyings that are not builtins.
|
|
if (tfVarying.compare(0, 3, "gl_") != 0)
|
|
{
|
|
collectUserVaryingTF(ref, subscript);
|
|
(*uniqueFullNames)[ref.frontShaderStage].insert(tfVarying);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool VaryingPacking::collectAndPackUserVaryings(gl::InfoLog &infoLog,
|
|
GLint maxVaryingVectors,
|
|
PackMode packMode,
|
|
ShaderType frontShaderStage,
|
|
ShaderType backShaderStage,
|
|
const ProgramMergedVaryings &mergedVaryings,
|
|
const std::vector<std::string> &tfVaryings,
|
|
const bool isSeparableProgram)
|
|
{
|
|
VaryingUniqueFullNames uniqueFullNames;
|
|
|
|
reset();
|
|
|
|
for (const ProgramVaryingRef &ref : mergedVaryings)
|
|
{
|
|
const sh::ShaderVariable *input = ref.frontShader;
|
|
const sh::ShaderVariable *output = ref.backShader;
|
|
|
|
if ((input && ref.frontShaderStage != frontShaderStage) ||
|
|
(output && ref.backShaderStage != backShaderStage))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const bool isActiveBuiltInInput = input && input->isBuiltIn() && input->active;
|
|
const bool isActiveBuiltInOutput = output && output->isBuiltIn() && output->active;
|
|
|
|
// Keep track of output builtins that are used by the shader, such as gl_Position,
|
|
// gl_PointSize etc.
|
|
if (isActiveBuiltInInput)
|
|
{
|
|
mActiveOutputBuiltIns[ref.frontShaderStage].push_back(input->name);
|
|
// Keep track of members of builtins, such as gl_out[].gl_Position, too.
|
|
for (sh::ShaderVariable field : input->fields)
|
|
{
|
|
mActiveOutputBuiltIns[ref.frontShaderStage].push_back(field.name);
|
|
}
|
|
}
|
|
|
|
// Only pack statically used varyings that have a matched input or output, plus special
|
|
// builtins. Note that we pack all statically used user-defined varyings even if they are
|
|
// not active. GLES specs are a bit vague on whether it's allowed to only pack active
|
|
// varyings, though GLES 3.1 spec section 11.1.2.1 says that "device-dependent
|
|
// optimizations" may be used to make vertex shader outputs fit.
|
|
//
|
|
// When separable programs are linked, varyings at the separable program's boundary are
|
|
// treated as active. See section 7.4.1 in
|
|
// https://www.khronos.org/registry/OpenGL/specs/es/3.2/es_spec_3.2.pdf
|
|
bool matchedInputOutputStaticUse = (input && output && output->staticUse);
|
|
bool activeBuiltIn = (isActiveBuiltInInput || isActiveBuiltInOutput);
|
|
|
|
// Output variable in TCS can be read as input in another invocation by barrier.
|
|
// See section 11.2.1.2.4 Tessellation Control Shader Execution Order in OpenGL ES 3.2.
|
|
bool staticUseInTCS =
|
|
(input && input->staticUse && ref.frontShaderStage == ShaderType::TessControl);
|
|
|
|
// Separable program requirements
|
|
bool separableActiveInput = (input && (input->active || !output));
|
|
bool separableActiveOutput = (output && (output->active || !input));
|
|
bool separableActiveVarying =
|
|
(isSeparableProgram && (separableActiveInput || separableActiveOutput));
|
|
|
|
if (matchedInputOutputStaticUse || activeBuiltIn || separableActiveVarying ||
|
|
staticUseInTCS)
|
|
{
|
|
const sh::ShaderVariable *varying = output ? output : input;
|
|
|
|
if (!ShouldSkipPackedVarying(*varying, packMode))
|
|
{
|
|
collectVarying(*varying, ref, packMode, &uniqueFullNames);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// If the varying is not used in the input, we know it is inactive, unless it's a separable
|
|
// program, in which case the input shader may not exist in this program.
|
|
if (!input && !isSeparableProgram)
|
|
{
|
|
if (!output->isBuiltIn())
|
|
{
|
|
mInactiveVaryingMappedNames[ref.backShaderStage].push_back(output->mappedName);
|
|
if (output->isShaderIOBlock)
|
|
{
|
|
mInactiveVaryingMappedNames[ref.backShaderStage].push_back(
|
|
output->mappedStructOrBlockName);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// Keep Transform FB varyings in the merged list always.
|
|
for (const std::string &tfVarying : tfVaryings)
|
|
{
|
|
collectTFVarying(tfVarying, ref, &uniqueFullNames);
|
|
}
|
|
|
|
if (input && !input->isBuiltIn() &&
|
|
uniqueFullNames[ref.frontShaderStage].count(input->name) == 0)
|
|
{
|
|
mInactiveVaryingMappedNames[ref.frontShaderStage].push_back(input->mappedName);
|
|
if (input->isShaderIOBlock)
|
|
{
|
|
mInactiveVaryingMappedNames[ref.frontShaderStage].push_back(
|
|
input->mappedStructOrBlockName);
|
|
}
|
|
}
|
|
if (output && !output->isBuiltIn() &&
|
|
uniqueFullNames[ref.backShaderStage].count(output->name) == 0)
|
|
{
|
|
mInactiveVaryingMappedNames[ref.backShaderStage].push_back(output->mappedName);
|
|
if (output->isShaderIOBlock)
|
|
{
|
|
mInactiveVaryingMappedNames[ref.backShaderStage].push_back(
|
|
output->mappedStructOrBlockName);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::sort(mPackedVaryings.begin(), mPackedVaryings.end(), ComparePackedVarying);
|
|
|
|
return packUserVaryings(infoLog, maxVaryingVectors, packMode, mPackedVaryings);
|
|
}
|
|
|
|
// See comment on packVarying.
|
|
bool VaryingPacking::packUserVaryings(gl::InfoLog &infoLog,
|
|
GLint maxVaryingVectors,
|
|
PackMode packMode,
|
|
const std::vector<PackedVarying> &packedVaryings)
|
|
{
|
|
clearRegisterMap();
|
|
mRegisterMap.resize(maxVaryingVectors);
|
|
|
|
// "Variables are packed into the registers one at a time so that they each occupy a contiguous
|
|
// subrectangle. No splitting of variables is permitted."
|
|
for (const PackedVarying &packedVarying : packedVaryings)
|
|
{
|
|
if (!packVaryingIntoRegisterMap(packMode, packedVarying))
|
|
{
|
|
ShaderType eitherStage = packedVarying.frontVarying.varying
|
|
? packedVarying.frontVarying.stage
|
|
: packedVarying.backVarying.stage;
|
|
infoLog << "Could not pack varying " << packedVarying.fullName(eitherStage);
|
|
|
|
// TODO(jmadill): Implement more sophisticated component packing in D3D9.
|
|
if (packMode == PackMode::ANGLE_NON_CONFORMANT_D3D9)
|
|
{
|
|
infoLog << "Note: Additional non-conformant packing restrictions are enforced on "
|
|
"D3D9.";
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Sort the packed register list
|
|
std::sort(mRegisterList.begin(), mRegisterList.end());
|
|
|
|
return true;
|
|
}
|
|
|
|
// ProgramVaryingPacking implementation.
|
|
ProgramVaryingPacking::ProgramVaryingPacking() = default;
|
|
|
|
ProgramVaryingPacking::~ProgramVaryingPacking() = default;
|
|
|
|
const VaryingPacking &ProgramVaryingPacking::getInputPacking(ShaderType backShaderStage) const
|
|
{
|
|
ShaderType frontShaderStage = mBackToFrontStageMap[backShaderStage];
|
|
|
|
// If there's a missing shader stage, return the compute shader packing which is always empty.
|
|
if (frontShaderStage == ShaderType::InvalidEnum)
|
|
{
|
|
ASSERT(mVaryingPackings[ShaderType::Compute].getMaxSemanticIndex() == 0);
|
|
return mVaryingPackings[ShaderType::Compute];
|
|
}
|
|
|
|
return mVaryingPackings[frontShaderStage];
|
|
}
|
|
|
|
const VaryingPacking &ProgramVaryingPacking::getOutputPacking(ShaderType frontShaderStage) const
|
|
{
|
|
return mVaryingPackings[frontShaderStage];
|
|
}
|
|
|
|
bool ProgramVaryingPacking::collectAndPackUserVaryings(InfoLog &infoLog,
|
|
const Caps &caps,
|
|
PackMode packMode,
|
|
const ShaderBitSet &activeShadersMask,
|
|
const ProgramMergedVaryings &mergedVaryings,
|
|
const std::vector<std::string> &tfVaryings,
|
|
bool isSeparableProgram)
|
|
{
|
|
mBackToFrontStageMap.fill(ShaderType::InvalidEnum);
|
|
|
|
ShaderBitSet activeShaders = activeShadersMask;
|
|
|
|
ASSERT(activeShaders.any());
|
|
ShaderType frontShaderStage = activeShaders.first();
|
|
activeShaders[frontShaderStage] = false;
|
|
|
|
// Special case for start-after-vertex.
|
|
if (frontShaderStage != ShaderType::Vertex)
|
|
{
|
|
ShaderType emulatedFrontShaderStage = ShaderType::Vertex;
|
|
ShaderType backShaderStage = frontShaderStage;
|
|
|
|
if (!mVaryingPackings[emulatedFrontShaderStage].collectAndPackUserVaryings(
|
|
infoLog, GetMaxShaderInputVectors(caps, backShaderStage), packMode,
|
|
ShaderType::InvalidEnum, backShaderStage, mergedVaryings, tfVaryings,
|
|
isSeparableProgram))
|
|
{
|
|
return false;
|
|
}
|
|
mBackToFrontStageMap[backShaderStage] = emulatedFrontShaderStage;
|
|
}
|
|
|
|
// Process input/output shader pairs.
|
|
for (ShaderType backShaderStage : activeShaders)
|
|
{
|
|
GLint maxVaryingVectors;
|
|
if (frontShaderStage == ShaderType::Vertex && backShaderStage == ShaderType::Fragment)
|
|
{
|
|
maxVaryingVectors = caps.maxVaryingVectors;
|
|
}
|
|
else
|
|
{
|
|
GLint outputVaryingsMax = GetMaxShaderOutputVectors(caps, frontShaderStage);
|
|
GLint inputVaryingsMax = GetMaxShaderInputVectors(caps, backShaderStage);
|
|
maxVaryingVectors = std::min(inputVaryingsMax, outputVaryingsMax);
|
|
}
|
|
|
|
ASSERT(maxVaryingVectors > 0 && maxVaryingVectors < std::numeric_limits<GLint>::max());
|
|
|
|
if (!mVaryingPackings[frontShaderStage].collectAndPackUserVaryings(
|
|
infoLog, maxVaryingVectors, packMode, frontShaderStage, backShaderStage,
|
|
mergedVaryings, tfVaryings, isSeparableProgram))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
mBackToFrontStageMap[backShaderStage] = frontShaderStage;
|
|
frontShaderStage = backShaderStage;
|
|
}
|
|
|
|
// Special case for stop-before-fragment.
|
|
if (frontShaderStage != ShaderType::Fragment)
|
|
{
|
|
if (!mVaryingPackings[frontShaderStage].collectAndPackUserVaryings(
|
|
infoLog, GetMaxShaderOutputVectors(caps, frontShaderStage), packMode,
|
|
frontShaderStage, ShaderType::InvalidEnum, mergedVaryings, tfVaryings,
|
|
isSeparableProgram))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ShaderType emulatedBackShaderStage = ShaderType::Fragment;
|
|
mBackToFrontStageMap[emulatedBackShaderStage] = frontShaderStage;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
ProgramMergedVaryings GetMergedVaryingsFromLinkingVariables(
|
|
const LinkingVariables &linkingVariables)
|
|
{
|
|
ShaderType frontShaderType = ShaderType::InvalidEnum;
|
|
ProgramMergedVaryings merged;
|
|
|
|
for (ShaderType backShaderType : kAllGraphicsShaderTypes)
|
|
{
|
|
if (!linkingVariables.isShaderStageUsedBitset[backShaderType])
|
|
{
|
|
continue;
|
|
}
|
|
const std::vector<sh::ShaderVariable> &backShaderOutputVaryings =
|
|
linkingVariables.outputVaryings[backShaderType];
|
|
const std::vector<sh::ShaderVariable> &backShaderInputVaryings =
|
|
linkingVariables.inputVaryings[backShaderType];
|
|
|
|
// Add outputs. These are always unmatched since we walk shader stages sequentially.
|
|
for (const sh::ShaderVariable &frontVarying : backShaderOutputVaryings)
|
|
{
|
|
ProgramVaryingRef ref;
|
|
ref.frontShader = &frontVarying;
|
|
ref.frontShaderStage = backShaderType;
|
|
merged.push_back(ref);
|
|
}
|
|
|
|
if (frontShaderType == ShaderType::InvalidEnum)
|
|
{
|
|
// If this is our first shader stage, and not a VS, we might have unmatched inputs.
|
|
for (const sh::ShaderVariable &backVarying : backShaderInputVaryings)
|
|
{
|
|
ProgramVaryingRef ref;
|
|
ref.backShader = &backVarying;
|
|
ref.backShaderStage = backShaderType;
|
|
merged.push_back(ref);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Match inputs with the prior shader stage outputs.
|
|
for (const sh::ShaderVariable &backVarying : backShaderInputVaryings)
|
|
{
|
|
bool found = false;
|
|
for (ProgramVaryingRef &ref : merged)
|
|
{
|
|
if (ref.frontShader && ref.frontShaderStage == frontShaderType &&
|
|
InterfaceVariablesMatch(*ref.frontShader, backVarying))
|
|
{
|
|
ASSERT(ref.backShader == nullptr);
|
|
|
|
ref.backShader = &backVarying;
|
|
ref.backShaderStage = backShaderType;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Some outputs are never matched, e.g. some builtin variables.
|
|
if (!found)
|
|
{
|
|
ProgramVaryingRef ref;
|
|
ref.backShader = &backVarying;
|
|
ref.backShaderStage = backShaderType;
|
|
merged.push_back(ref);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Save the current back shader to use as the next front shader.
|
|
frontShaderType = backShaderType;
|
|
}
|
|
|
|
return merged;
|
|
}
|
|
} // namespace gl
|