summaryrefslogtreecommitdiffstats
path: root/gfx/angle/checkout/src/libANGLE/VaryingPacking.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /gfx/angle/checkout/src/libANGLE/VaryingPacking.cpp
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/angle/checkout/src/libANGLE/VaryingPacking.cpp')
-rw-r--r--gfx/angle/checkout/src/libANGLE/VaryingPacking.cpp1152
1 files changed, 1152 insertions, 0 deletions
diff --git a/gfx/angle/checkout/src/libANGLE/VaryingPacking.cpp b/gfx/angle/checkout/src/libANGLE/VaryingPacking.cpp
new file mode 100644
index 0000000000..7dbb0cc2b8
--- /dev/null
+++ b/gfx/angle/checkout/src/libANGLE/VaryingPacking.cpp
@@ -0,0 +1,1152 @@
+//
+// 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