// // 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. //
// 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;
// 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)
{ returntrue;
}
if (front.isShaderIOBlock != back.isShaderIOBlock)
{ returnfalse;
}
std::vector<unsignedint> 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."
unsignedint 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<unsignedint> arr =
StripVaryingArrayDimension(frontVarying.varying, frontVarying.stage, backVarying.varying,
backVarying.stage, isStructField()); return arr.empty() ? 1u : arr.back();
}
// Implementation of VaryingPacking
VaryingPacking::VaryingPacking() = default;
// 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); unsignedint varyingRows = gl::VariableRowCount(transposedType); unsignedint 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. elseif (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(). constunsignedint elementCount = packedVarying.getBasicTypeElementCount();
varyingRows *= (packedVarying.isTransformFeedbackArrayElement() ? 1 : elementCount);
// Fail if we are packing a single over-large varying. if (varyingRows > maxVaryingVectors)
{ returnfalse;
}
// "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 (unsignedint row = 0; row <= maxVaryingVectors - varyingRows; ++row)
{ if (isRegisterRangeFree(row, 0, varyingRows, varyingColumns))
{
insertVaryingIntoRegisterMap(row, 0, varyingColumns, packedVarying); returntrue;
}
}
// "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 (unsignedint r = maxVaryingVectors - varyingRows + 1; r-- >= 1;)
{ if (isRegisterRangeFree(r, 2, varyingRows, 2))
{
insertVaryingIntoRegisterMap(r, 2, varyingColumns, packedVarying); returntrue;
}
}
}
returnfalse;
}
// "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); unsignedint contiguousSpace[4] = {0}; unsignedint bestContiguousSpace[4] = {0}; unsignedint totalSpace[4] = {0};
// GLSL ES 3.10 section 4.3.6: Output variables cannot be arrays of arrays or arrays of // structures, so we may use getBasicTypeElementCount(). constunsignedint arrayElementCount = packedVarying.getBasicTypeElementCount(); for (unsignedint arrayElement = 0; arrayElement < arrayElementCount; ++arrayElement)
{ if (packedVarying.isTransformFeedbackArrayElement() &&
arrayElement != packedVarying.arrayIndex)
{ continue;
} for (unsignedint 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);
}
// 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())
{
(*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. elseif (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);
}
}
}
// 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);
// 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);
}
}
}
// 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.";
}
returnfalse;
}
}
// Sort the packed register list
std::sort(mRegisterList.begin(), mRegisterList.end());
// 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];
}
// Special case for start-after-vertex. if (frontShaderStage != ShaderType::Vertex)
{
ShaderType emulatedFrontShaderStage = ShaderType::Vertex;
ShaderType backShaderStage = frontShaderStage;
// 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))
{ returnfalse;
}
// 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);
// 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
Messung V0.5
¤ Dauer der Verarbeitung: 0.17 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.