| // | |
| // 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 |