diff options
Diffstat (limited to 'gfx/angle/checkout/src/libANGLE/VaryingPacking.cpp')
-rw-r--r-- | gfx/angle/checkout/src/libANGLE/VaryingPacking.cpp | 1161 |
1 files changed, 1161 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..827b8739b2 --- /dev/null +++ b/gfx/angle/checkout/src/libANGLE/VaryingPacking.cpp @@ -0,0 +1,1161 @@ +// +// 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) + { + ASSERT(isSeparableProgram); + + 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) + { + ASSERT(isSeparableProgram); + + 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 GetMergedVaryingsFromShaders(const HasAttachedShaders &programOrPipeline, + const ProgramExecutable &programExecutable) +{ + ShaderType frontShaderType = ShaderType::InvalidEnum; + ProgramMergedVaryings merged; + + for (ShaderType backShaderType : kAllGraphicsShaderTypes) + { + Shader *backShader = programOrPipeline.getAttachedShader(backShaderType); + + if (!backShader && !programExecutable.hasLinkedShaderStage(backShaderType)) + { + continue; + } + + const std::vector<sh::ShaderVariable> &backShaderOutputVaryings = + backShader ? backShader->getOutputVaryings() + : programExecutable.getLinkedOutputVaryings(backShaderType); + const std::vector<sh::ShaderVariable> &backShaderInputVaryings = + backShader ? backShader->getInputVaryings() + : programExecutable.getLinkedInputVaryings(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 |