xref: /aosp_15_r20/external/angle/src/libANGLE/VaryingPacking.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1 //
2 // Copyright 2015 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // VaryingPacking:
7 //   Class which describes a mapping from varyings to registers, according
8 //   to the spec, or using custom packing algorithms. We also keep a register
9 //   allocation list for the D3D renderer.
10 //
11 
12 #include "libANGLE/VaryingPacking.h"
13 
14 #include "common/CompiledShaderState.h"
15 #include "common/utilities.h"
16 #include "libANGLE/Program.h"
17 #include "libANGLE/ProgramExecutable.h"
18 #include "libANGLE/Shader.h"
19 
20 namespace gl
21 {
22 
23 namespace
24 {
25 // true if varying x has a higher priority in packing than y
ComparePackedVarying(const PackedVarying & x,const PackedVarying & y)26 bool ComparePackedVarying(const PackedVarying &x, const PackedVarying &y)
27 {
28     // If the PackedVarying 'x' or 'y' to be compared is an array element for transform feedback,
29     // this clones an equivalent non-array shader variable 'vx' or 'vy' for actual comparison
30     // instead.  For I/O block arrays, the array index is used in the comparison.
31     sh::ShaderVariable vx, vy;
32     const sh::ShaderVariable *px, *py;
33 
34     px = &x.varying();
35     py = &y.varying();
36 
37     if (x.isTransformFeedbackArrayElement())
38     {
39         vx = *px;
40         vx.arraySizes.clear();
41         px = &vx;
42     }
43 
44     if (y.isTransformFeedbackArrayElement())
45     {
46         vy = *py;
47         vy.arraySizes.clear();
48         py = &vy;
49     }
50 
51     // Make sure struct fields end up together.
52     if (x.isStructField() != y.isStructField())
53     {
54         return x.isStructField();
55     }
56 
57     if (x.isStructField())
58     {
59         ASSERT(y.isStructField());
60 
61         if (x.getParentStructName() != y.getParentStructName())
62         {
63             return x.getParentStructName() < y.getParentStructName();
64         }
65     }
66 
67     // For I/O block fields, order first by array index:
68     if (!x.isTransformFeedbackArrayElement() && !y.isTransformFeedbackArrayElement())
69     {
70         if (x.arrayIndex != y.arrayIndex)
71         {
72             return x.arrayIndex < y.arrayIndex;
73         }
74     }
75 
76     // Then order by field index
77     if (x.fieldIndex != y.fieldIndex)
78     {
79         return x.fieldIndex < y.fieldIndex;
80     }
81 
82     // Then order by secondary field index
83     if (x.secondaryFieldIndex != y.secondaryFieldIndex)
84     {
85         return x.secondaryFieldIndex < y.secondaryFieldIndex;
86     }
87 
88     // Otherwise order by variable
89     return gl::CompareShaderVar(*px, *py);
90 }
91 
InterfaceVariablesMatch(const sh::ShaderVariable & front,const sh::ShaderVariable & back)92 bool InterfaceVariablesMatch(const sh::ShaderVariable &front, const sh::ShaderVariable &back)
93 {
94     // Matching ruels from 7.4.1 Shader Interface Matching from the GLES 3.2 spec:
95     // - the two variables match in name, type, and qualification; or
96     // - the two variables are declared with the same location qualifier and match in type and
97     // qualification. Note that we use a more permissive check here thanks to front-end validation.
98     if (back.location != -1 && back.location == front.location)
99     {
100         return true;
101     }
102 
103     if (front.isShaderIOBlock != back.isShaderIOBlock)
104     {
105         return false;
106     }
107 
108     // Compare names, or if shader I/O blocks, block names.
109     const std::string &backName  = back.isShaderIOBlock ? back.structOrBlockName : back.name;
110     const std::string &frontName = front.isShaderIOBlock ? front.structOrBlockName : front.name;
111     return backName == frontName;
112 }
113 
GetMaxShaderInputVectors(const Caps & caps,ShaderType shaderStage)114 GLint GetMaxShaderInputVectors(const Caps &caps, ShaderType shaderStage)
115 {
116     switch (shaderStage)
117     {
118         case ShaderType::TessControl:
119             return caps.maxTessControlInputComponents / 4;
120         case ShaderType::TessEvaluation:
121             return caps.maxTessEvaluationInputComponents / 4;
122         case ShaderType::Geometry:
123             return caps.maxGeometryInputComponents / 4;
124         case ShaderType::Fragment:
125             return caps.maxFragmentInputComponents / 4;
126         default:
127             return std::numeric_limits<GLint>::max();
128     }
129 }
130 
GetMaxShaderOutputVectors(const Caps & caps,ShaderType shaderStage)131 GLint GetMaxShaderOutputVectors(const Caps &caps, ShaderType shaderStage)
132 {
133     switch (shaderStage)
134     {
135         case ShaderType::Vertex:
136             return caps.maxVertexOutputComponents / 4;
137         case ShaderType::TessControl:
138             return caps.maxTessControlOutputComponents / 4;
139         case ShaderType::TessEvaluation:
140             return caps.maxTessEvaluationOutputComponents / 4;
141         case ShaderType::Geometry:
142             return caps.maxGeometryOutputComponents / 4;
143         default:
144             return std::numeric_limits<GLint>::max();
145     }
146 }
147 
ShouldSkipPackedVarying(const sh::ShaderVariable & varying,PackMode packMode)148 bool ShouldSkipPackedVarying(const sh::ShaderVariable &varying, PackMode packMode)
149 {
150     // Don't pack gl_Position. Also don't count gl_PointSize for D3D9.
151     // Additionally, gl_TessLevelInner and gl_TessLevelOuter should not be packed.
152     return varying.name == "gl_Position" ||
153            (varying.name == "gl_PointSize" && packMode == PackMode::ANGLE_NON_CONFORMANT_D3D9) ||
154            varying.name == "gl_TessLevelInner" || varying.name == "gl_TessLevelOuter";
155 }
156 
StripVaryingArrayDimension(const sh::ShaderVariable * frontVarying,ShaderType frontShaderStage,const sh::ShaderVariable * backVarying,ShaderType backShaderStage,bool isStructField)157 std::vector<unsigned int> StripVaryingArrayDimension(const sh::ShaderVariable *frontVarying,
158                                                      ShaderType frontShaderStage,
159                                                      const sh::ShaderVariable *backVarying,
160                                                      ShaderType backShaderStage,
161                                                      bool isStructField)
162 {
163     // "Geometry shader inputs, tessellation control shader inputs and outputs, and tessellation
164     // evaluation inputs all have an additional level of arrayness relative to other shader inputs
165     // and outputs. This outer array level is removed from the type before considering how many
166     // locations the type consumes."
167 
168     if (backVarying && backVarying->isArray() && !backVarying->isPatch && !isStructField &&
169         (backShaderStage == ShaderType::Geometry || backShaderStage == ShaderType::TessEvaluation ||
170          backShaderStage == ShaderType::TessControl))
171     {
172         std::vector<unsigned int> arr = backVarying->arraySizes;
173         arr.pop_back();
174         return arr;
175     }
176 
177     if (frontVarying && frontVarying->isArray() && !frontVarying->isPatch && !isStructField &&
178         frontShaderStage == ShaderType::TessControl)
179     {
180         std::vector<unsigned int> arr = frontVarying->arraySizes;
181         arr.pop_back();
182         return arr;
183     }
184 
185     return frontVarying ? frontVarying->arraySizes : backVarying->arraySizes;
186 }
187 
GetPerVertexMember(const std::string & name)188 PerVertexMember GetPerVertexMember(const std::string &name)
189 {
190     if (name == "gl_Position")
191     {
192         return PerVertexMember::Position;
193     }
194     if (name == "gl_PointSize")
195     {
196         return PerVertexMember::PointSize;
197     }
198     if (name == "gl_ClipDistance")
199     {
200         return PerVertexMember::ClipDistance;
201     }
202     if (name == "gl_CullDistance")
203     {
204         return PerVertexMember::CullDistance;
205     }
206     return PerVertexMember::InvalidEnum;
207 }
208 
SetActivePerVertexMembers(const sh::ShaderVariable * var,PerVertexMemberBitSet * bitset)209 void SetActivePerVertexMembers(const sh::ShaderVariable *var, PerVertexMemberBitSet *bitset)
210 {
211     ASSERT(var->isBuiltIn() && var->active);
212 
213     // Only process gl_Position, gl_PointSize, gl_ClipDistance, gl_CullDistance and the fields of
214     // gl_in/out.
215     if (var->fields.empty())
216     {
217         PerVertexMember member = GetPerVertexMember(var->name);
218         // Skip gl_TessLevelInner/Outer etc.
219         if (member != PerVertexMember::InvalidEnum)
220         {
221             bitset->set(member);
222         }
223         return;
224     }
225 
226     // This must be gl_out.  Note that only `out gl_PerVertex` is processed; the input of the
227     // next stage is implicitly identically active.
228     ASSERT(var->name == "gl_out");
229     for (const sh::ShaderVariable &field : var->fields)
230     {
231         bitset->set(GetPerVertexMember(field.name));
232     }
233 }
234 }  // anonymous namespace
235 
236 // Implementation of VaryingInShaderRef
VaryingInShaderRef(ShaderType stageIn,const sh::ShaderVariable * varyingIn)237 VaryingInShaderRef::VaryingInShaderRef(ShaderType stageIn, const sh::ShaderVariable *varyingIn)
238     : varying(varyingIn), stage(stageIn)
239 {}
240 
241 VaryingInShaderRef::~VaryingInShaderRef() = default;
242 
VaryingInShaderRef(VaryingInShaderRef && other)243 VaryingInShaderRef::VaryingInShaderRef(VaryingInShaderRef &&other)
244     : varying(other.varying),
245       stage(other.stage),
246       parentStructName(std::move(other.parentStructName))
247 {}
248 
operator =(VaryingInShaderRef && other)249 VaryingInShaderRef &VaryingInShaderRef::operator=(VaryingInShaderRef &&other)
250 {
251     std::swap(varying, other.varying);
252     std::swap(stage, other.stage);
253     std::swap(parentStructName, other.parentStructName);
254 
255     return *this;
256 }
257 
258 // Implementation of PackedVarying
PackedVarying(VaryingInShaderRef && frontVaryingIn,VaryingInShaderRef && backVaryingIn,sh::InterpolationType interpolationIn)259 PackedVarying::PackedVarying(VaryingInShaderRef &&frontVaryingIn,
260                              VaryingInShaderRef &&backVaryingIn,
261                              sh::InterpolationType interpolationIn)
262     : PackedVarying(std::move(frontVaryingIn),
263                     std::move(backVaryingIn),
264                     interpolationIn,
265                     GL_INVALID_INDEX,
266                     0,
267                     0)
268 {}
269 
PackedVarying(VaryingInShaderRef && frontVaryingIn,VaryingInShaderRef && backVaryingIn,sh::InterpolationType interpolationIn,GLuint arrayIndexIn,GLuint fieldIndexIn,GLuint secondaryFieldIndexIn)270 PackedVarying::PackedVarying(VaryingInShaderRef &&frontVaryingIn,
271                              VaryingInShaderRef &&backVaryingIn,
272                              sh::InterpolationType interpolationIn,
273                              GLuint arrayIndexIn,
274                              GLuint fieldIndexIn,
275                              GLuint secondaryFieldIndexIn)
276     : frontVarying(std::move(frontVaryingIn)),
277       backVarying(std::move(backVaryingIn)),
278       interpolation(interpolationIn),
279       arrayIndex(arrayIndexIn),
280       isTransformFeedback(false),
281       fieldIndex(fieldIndexIn),
282       secondaryFieldIndex(secondaryFieldIndexIn)
283 {}
284 
285 PackedVarying::~PackedVarying() = default;
286 
PackedVarying(PackedVarying && other)287 PackedVarying::PackedVarying(PackedVarying &&other)
288     : frontVarying(std::move(other.frontVarying)),
289       backVarying(std::move(other.backVarying)),
290       interpolation(other.interpolation),
291       arrayIndex(other.arrayIndex),
292       isTransformFeedback(other.isTransformFeedback),
293       fieldIndex(other.fieldIndex),
294       secondaryFieldIndex(other.secondaryFieldIndex)
295 {}
296 
operator =(PackedVarying && other)297 PackedVarying &PackedVarying::operator=(PackedVarying &&other)
298 {
299     std::swap(frontVarying, other.frontVarying);
300     std::swap(backVarying, other.backVarying);
301     std::swap(interpolation, other.interpolation);
302     std::swap(arrayIndex, other.arrayIndex);
303     std::swap(isTransformFeedback, other.isTransformFeedback);
304     std::swap(fieldIndex, other.fieldIndex);
305     std::swap(secondaryFieldIndex, other.secondaryFieldIndex);
306 
307     return *this;
308 }
309 
getBasicTypeElementCount() const310 unsigned int PackedVarying::getBasicTypeElementCount() const
311 {
312     // "Geometry shader inputs, tessellation control shader inputs and outputs, and tessellation
313     // evaluation inputs all have an additional level of arrayness relative to other shader inputs
314     // and outputs. This outer array level is removed from the type before considering how many
315     // locations the type consumes."
316     std::vector<unsigned int> arr =
317         StripVaryingArrayDimension(frontVarying.varying, frontVarying.stage, backVarying.varying,
318                                    backVarying.stage, isStructField());
319     return arr.empty() ? 1u : arr.back();
320 }
321 
322 // Implementation of VaryingPacking
323 VaryingPacking::VaryingPacking() = default;
324 
325 VaryingPacking::~VaryingPacking() = default;
326 
reset()327 void VaryingPacking::reset()
328 {
329     clearRegisterMap();
330     mRegisterList.clear();
331     mPackedVaryings.clear();
332 
333     for (std::vector<uint32_t> &inactiveVaryingIds : mInactiveVaryingIds)
334     {
335         inactiveVaryingIds.clear();
336     }
337 
338     std::fill(mOutputPerVertexActiveMembers.begin(), mOutputPerVertexActiveMembers.end(),
339               gl::PerVertexMemberBitSet{});
340 }
341 
clearRegisterMap()342 void VaryingPacking::clearRegisterMap()
343 {
344     std::fill(mRegisterMap.begin(), mRegisterMap.end(), Register());
345 }
346 
347 // Packs varyings into generic varying registers, using the algorithm from
348 // See [OpenGL ES Shading Language 1.00 rev. 17] appendix A section 7 page 111
349 // Also [OpenGL ES Shading Language 3.00 rev. 4] Section 11 page 119
350 // Returns false if unsuccessful.
packVaryingIntoRegisterMap(PackMode packMode,const PackedVarying & packedVarying)351 bool VaryingPacking::packVaryingIntoRegisterMap(PackMode packMode,
352                                                 const PackedVarying &packedVarying)
353 {
354     const sh::ShaderVariable &varying = packedVarying.varying();
355 
356     // "Non - square matrices of type matCxR consume the same space as a square matrix of type matN
357     // where N is the greater of C and R."
358     // Here we are a bit more conservative and allow packing non-square matrices more tightly.
359     // Make sure we use transposed matrix types to count registers correctly.
360     ASSERT(!varying.isStruct());
361     GLenum transposedType       = gl::TransposeMatrixType(varying.type);
362     unsigned int varyingRows    = gl::VariableRowCount(transposedType);
363     unsigned int varyingColumns = gl::VariableColumnCount(transposedType);
364 
365     // Special pack mode for D3D9. Each varying takes a full register, no sharing.
366     // TODO(jmadill): Implement more sophisticated component packing in D3D9.
367     if (packMode == PackMode::ANGLE_NON_CONFORMANT_D3D9)
368     {
369         varyingColumns = 4;
370     }
371 
372     // "Variables of type mat2 occupies 2 complete rows."
373     // For non-WebGL contexts, we allow mat2 to occupy only two columns per row.
374     else if (packMode == PackMode::WEBGL_STRICT && varying.type == GL_FLOAT_MAT2)
375     {
376         varyingColumns = 4;
377     }
378 
379     // "Arrays of size N are assumed to take N times the size of the base type"
380     // GLSL ES 3.10 section 4.3.6: Output variables cannot be arrays of arrays or arrays of
381     // structures, so we may use getBasicTypeElementCount().
382     const unsigned int elementCount = packedVarying.getBasicTypeElementCount();
383     varyingRows *= (packedVarying.isTransformFeedbackArrayElement() ? 1 : elementCount);
384 
385     unsigned int maxVaryingVectors = static_cast<unsigned int>(mRegisterMap.size());
386 
387     // Fail if we are packing a single over-large varying.
388     if (varyingRows > maxVaryingVectors)
389     {
390         return false;
391     }
392 
393     // "For 2, 3 and 4 component variables packing is started using the 1st column of the 1st row.
394     // Variables are then allocated to successive rows, aligning them to the 1st column."
395     if (varyingColumns >= 2 && varyingColumns <= 4)
396     {
397         for (unsigned int row = 0; row <= maxVaryingVectors - varyingRows; ++row)
398         {
399             if (isRegisterRangeFree(row, 0, varyingRows, varyingColumns))
400             {
401                 insertVaryingIntoRegisterMap(row, 0, varyingColumns, packedVarying);
402                 return true;
403             }
404         }
405 
406         // "For 2 component variables, when there are no spare rows, the strategy is switched to
407         // using the highest numbered row and the lowest numbered column where the variable will
408         // fit."
409         if (varyingColumns == 2)
410         {
411             for (unsigned int r = maxVaryingVectors - varyingRows + 1; r-- >= 1;)
412             {
413                 if (isRegisterRangeFree(r, 2, varyingRows, 2))
414                 {
415                     insertVaryingIntoRegisterMap(r, 2, varyingColumns, packedVarying);
416                     return true;
417                 }
418             }
419         }
420 
421         return false;
422     }
423 
424     // "1 component variables have their own packing rule. They are packed in order of size, largest
425     // first. Each variable is placed in the column that leaves the least amount of space in the
426     // column and aligned to the lowest available rows within that column."
427     ASSERT(varyingColumns == 1);
428     unsigned int contiguousSpace[4]     = {0};
429     unsigned int bestContiguousSpace[4] = {0};
430     unsigned int totalSpace[4]          = {0};
431 
432     for (unsigned int row = 0; row < maxVaryingVectors; ++row)
433     {
434         for (unsigned int column = 0; column < 4; ++column)
435         {
436             if (mRegisterMap[row][column])
437             {
438                 contiguousSpace[column] = 0;
439             }
440             else
441             {
442                 contiguousSpace[column]++;
443                 totalSpace[column]++;
444 
445                 if (contiguousSpace[column] > bestContiguousSpace[column])
446                 {
447                     bestContiguousSpace[column] = contiguousSpace[column];
448                 }
449             }
450         }
451     }
452 
453     unsigned int bestColumn = 0;
454     for (unsigned int column = 1; column < 4; ++column)
455     {
456         if (bestContiguousSpace[column] >= varyingRows &&
457             (bestContiguousSpace[bestColumn] < varyingRows ||
458              totalSpace[column] < totalSpace[bestColumn]))
459         {
460             bestColumn = column;
461         }
462     }
463 
464     if (bestContiguousSpace[bestColumn] >= varyingRows)
465     {
466         for (unsigned int row = 0; row < maxVaryingVectors; row++)
467         {
468             if (isRegisterRangeFree(row, bestColumn, varyingRows, 1))
469             {
470                 for (unsigned int arrayIndex = 0; arrayIndex < varyingRows; ++arrayIndex)
471                 {
472                     // If varyingRows > 1, it must be an array.
473                     PackedVaryingRegister registerInfo;
474                     registerInfo.packedVarying  = &packedVarying;
475                     registerInfo.registerRow    = row + arrayIndex;
476                     registerInfo.registerColumn = bestColumn;
477                     registerInfo.varyingArrayIndex =
478                         (packedVarying.isTransformFeedbackArrayElement() ? packedVarying.arrayIndex
479                                                                          : arrayIndex);
480                     registerInfo.varyingRowIndex = 0;
481                     // Do not record register info for builtins.
482                     // TODO(jmadill): Clean this up.
483                     if (!varying.isBuiltIn())
484                     {
485                         mRegisterList.push_back(registerInfo);
486                     }
487                     mRegisterMap[row + arrayIndex][bestColumn] = true;
488                 }
489                 break;
490             }
491         }
492         return true;
493     }
494 
495     return false;
496 }
497 
isRegisterRangeFree(unsigned int registerRow,unsigned int registerColumn,unsigned int varyingRows,unsigned int varyingColumns) const498 bool VaryingPacking::isRegisterRangeFree(unsigned int registerRow,
499                                          unsigned int registerColumn,
500                                          unsigned int varyingRows,
501                                          unsigned int varyingColumns) const
502 {
503     for (unsigned int row = 0; row < varyingRows; ++row)
504     {
505         ASSERT(registerRow + row < mRegisterMap.size());
506         for (unsigned int column = 0; column < varyingColumns; ++column)
507         {
508             ASSERT(registerColumn + column < 4);
509             if (mRegisterMap[registerRow + row][registerColumn + column])
510             {
511                 return false;
512             }
513         }
514     }
515 
516     return true;
517 }
518 
insertVaryingIntoRegisterMap(unsigned int registerRow,unsigned int registerColumn,unsigned int varyingColumns,const PackedVarying & packedVarying)519 void VaryingPacking::insertVaryingIntoRegisterMap(unsigned int registerRow,
520                                                   unsigned int registerColumn,
521                                                   unsigned int varyingColumns,
522                                                   const PackedVarying &packedVarying)
523 {
524     unsigned int varyingRows = 0;
525 
526     const sh::ShaderVariable &varying = packedVarying.varying();
527     ASSERT(!varying.isStruct());
528     GLenum transposedType = gl::TransposeMatrixType(varying.type);
529     varyingRows           = gl::VariableRowCount(transposedType);
530 
531     PackedVaryingRegister registerInfo;
532     registerInfo.packedVarying  = &packedVarying;
533     registerInfo.registerColumn = registerColumn;
534 
535     // GLSL ES 3.10 section 4.3.6: Output variables cannot be arrays of arrays or arrays of
536     // structures, so we may use getBasicTypeElementCount().
537     const unsigned int arrayElementCount = packedVarying.getBasicTypeElementCount();
538     for (unsigned int arrayElement = 0; arrayElement < arrayElementCount; ++arrayElement)
539     {
540         if (packedVarying.isTransformFeedbackArrayElement() &&
541             arrayElement != packedVarying.arrayIndex)
542         {
543             continue;
544         }
545         for (unsigned int varyingRow = 0; varyingRow < varyingRows; ++varyingRow)
546         {
547             registerInfo.registerRow     = registerRow + (arrayElement * varyingRows) + varyingRow;
548             registerInfo.varyingRowIndex = varyingRow;
549             registerInfo.varyingArrayIndex = arrayElement;
550             // Do not record register info for builtins.
551             // TODO(jmadill): Clean this up.
552             if (!varying.isBuiltIn())
553             {
554                 mRegisterList.push_back(registerInfo);
555             }
556 
557             for (unsigned int columnIndex = 0; columnIndex < varyingColumns; ++columnIndex)
558             {
559                 mRegisterMap[registerInfo.registerRow][registerColumn + columnIndex] = true;
560             }
561         }
562     }
563 }
564 
collectUserVarying(const ProgramVaryingRef & ref,VaryingUniqueFullNames * uniqueFullNames)565 void VaryingPacking::collectUserVarying(const ProgramVaryingRef &ref,
566                                         VaryingUniqueFullNames *uniqueFullNames)
567 {
568     const sh::ShaderVariable *input  = ref.frontShader;
569     const sh::ShaderVariable *output = ref.backShader;
570 
571     // Will get the vertex shader interpolation by default.
572     sh::InterpolationType interpolation = input ? input->interpolation : output->interpolation;
573 
574     VaryingInShaderRef frontVarying(ref.frontShaderStage, input);
575     VaryingInShaderRef backVarying(ref.backShaderStage, output);
576 
577     mPackedVaryings.emplace_back(std::move(frontVarying), std::move(backVarying), interpolation);
578     if (input && !input->isBuiltIn())
579     {
580         (*uniqueFullNames)[ref.frontShaderStage].insert(
581             mPackedVaryings.back().fullName(ref.frontShaderStage));
582     }
583     if (output && !output->isBuiltIn())
584     {
585         (*uniqueFullNames)[ref.backShaderStage].insert(
586             mPackedVaryings.back().fullName(ref.backShaderStage));
587     }
588 }
589 
collectUserVaryingField(const ProgramVaryingRef & ref,GLuint arrayIndex,GLuint fieldIndex,GLuint secondaryFieldIndex,VaryingUniqueFullNames * uniqueFullNames)590 void VaryingPacking::collectUserVaryingField(const ProgramVaryingRef &ref,
591                                              GLuint arrayIndex,
592                                              GLuint fieldIndex,
593                                              GLuint secondaryFieldIndex,
594                                              VaryingUniqueFullNames *uniqueFullNames)
595 {
596     const sh::ShaderVariable *input  = ref.frontShader;
597     const sh::ShaderVariable *output = ref.backShader;
598 
599     // Will get the vertex shader interpolation by default.
600     sh::InterpolationType interpolation = input ? input->interpolation : output->interpolation;
601 
602     const sh::ShaderVariable *frontField = input ? &input->fields[fieldIndex] : nullptr;
603     const sh::ShaderVariable *backField  = output ? &output->fields[fieldIndex] : nullptr;
604 
605     if (secondaryFieldIndex != GL_INVALID_INDEX)
606     {
607         frontField = frontField ? &frontField->fields[secondaryFieldIndex] : nullptr;
608         backField  = backField ? &backField->fields[secondaryFieldIndex] : nullptr;
609     }
610 
611     VaryingInShaderRef frontVarying(ref.frontShaderStage, frontField);
612     VaryingInShaderRef backVarying(ref.backShaderStage, backField);
613 
614     if (input)
615     {
616         if (frontField->isShaderIOBlock)
617         {
618             frontVarying.parentStructName = input->structOrBlockName;
619         }
620         else
621         {
622             ASSERT(!frontField->isStruct() && !frontField->isArray());
623             frontVarying.parentStructName = input->name;
624         }
625     }
626     if (output)
627     {
628         if (backField->isShaderIOBlock)
629         {
630             backVarying.parentStructName = output->structOrBlockName;
631         }
632         else
633         {
634             ASSERT(!backField->isStruct() && !backField->isArray());
635             backVarying.parentStructName = output->name;
636         }
637     }
638 
639     mPackedVaryings.emplace_back(std::move(frontVarying), std::move(backVarying), interpolation,
640                                  arrayIndex, fieldIndex,
641                                  secondaryFieldIndex == GL_INVALID_INDEX ? 0 : secondaryFieldIndex);
642 
643     if (input)
644     {
645         (*uniqueFullNames)[ref.frontShaderStage].insert(
646             mPackedVaryings.back().fullName(ref.frontShaderStage));
647     }
648     if (output)
649     {
650         (*uniqueFullNames)[ref.backShaderStage].insert(
651             mPackedVaryings.back().fullName(ref.backShaderStage));
652     }
653 }
654 
collectUserVaryingTF(const ProgramVaryingRef & ref,size_t subscript)655 void VaryingPacking::collectUserVaryingTF(const ProgramVaryingRef &ref, size_t subscript)
656 {
657     const sh::ShaderVariable *input = ref.frontShader;
658 
659     VaryingInShaderRef frontVarying(ref.frontShaderStage, input);
660     VaryingInShaderRef backVarying(ref.backShaderStage, nullptr);
661 
662     mPackedVaryings.emplace_back(std::move(frontVarying), std::move(backVarying),
663                                  input->interpolation);
664     mPackedVaryings.back().arrayIndex          = static_cast<GLuint>(subscript);
665     mPackedVaryings.back().isTransformFeedback = true;
666 }
667 
collectUserVaryingFieldTF(const ProgramVaryingRef & ref,const sh::ShaderVariable & field,GLuint fieldIndex,GLuint secondaryFieldIndex)668 void VaryingPacking::collectUserVaryingFieldTF(const ProgramVaryingRef &ref,
669                                                const sh::ShaderVariable &field,
670                                                GLuint fieldIndex,
671                                                GLuint secondaryFieldIndex)
672 {
673     const sh::ShaderVariable *input = ref.frontShader;
674 
675     const sh::ShaderVariable *frontField = &field;
676     if (secondaryFieldIndex != GL_INVALID_INDEX)
677     {
678         frontField = &frontField->fields[secondaryFieldIndex];
679     }
680 
681     VaryingInShaderRef frontVarying(ref.frontShaderStage, frontField);
682     VaryingInShaderRef backVarying(ref.backShaderStage, nullptr);
683 
684     if (frontField->isShaderIOBlock)
685     {
686         frontVarying.parentStructName = input->structOrBlockName;
687     }
688     else
689     {
690         ASSERT(!frontField->isStruct() && !frontField->isArray());
691         frontVarying.parentStructName = input->name;
692     }
693 
694     mPackedVaryings.emplace_back(std::move(frontVarying), std::move(backVarying),
695                                  input->interpolation, GL_INVALID_INDEX, fieldIndex,
696                                  secondaryFieldIndex == GL_INVALID_INDEX ? 0 : secondaryFieldIndex);
697 }
698 
collectVarying(const sh::ShaderVariable & varying,const ProgramVaryingRef & ref,PackMode packMode,VaryingUniqueFullNames * uniqueFullNames)699 void VaryingPacking::collectVarying(const sh::ShaderVariable &varying,
700                                     const ProgramVaryingRef &ref,
701                                     PackMode packMode,
702                                     VaryingUniqueFullNames *uniqueFullNames)
703 {
704     const sh::ShaderVariable *input  = ref.frontShader;
705     const sh::ShaderVariable *output = ref.backShader;
706 
707     if (varying.isStruct())
708     {
709         std::vector<unsigned int> arraySizes = StripVaryingArrayDimension(
710             ref.frontShader, ref.frontShaderStage, ref.backShader, ref.backShaderStage, false);
711         const bool isArray     = !arraySizes.empty();
712         const GLuint arraySize = isArray ? arraySizes[0] : 1;
713 
714         for (GLuint arrayIndex = 0; arrayIndex < arraySize; ++arrayIndex)
715         {
716             const GLuint effectiveArrayIndex = isArray ? arrayIndex : GL_INVALID_INDEX;
717             for (GLuint fieldIndex = 0; fieldIndex < varying.fields.size(); ++fieldIndex)
718             {
719                 const sh::ShaderVariable &fieldVarying = varying.fields[fieldIndex];
720                 if (ShouldSkipPackedVarying(fieldVarying, packMode))
721                 {
722                     continue;
723                 }
724 
725                 if (fieldVarying.isStruct())
726                 {
727                     if (fieldVarying.isArray())
728                     {
729                         unsigned int structFieldArraySize = fieldVarying.arraySizes[0];
730                         for (unsigned int fieldArrayIndex = 0;
731                              fieldArrayIndex < structFieldArraySize; ++fieldArrayIndex)
732                         {
733                             for (GLuint nestedIndex = 0; nestedIndex < fieldVarying.fields.size();
734                                  nestedIndex++)
735                             {
736                                 collectUserVaryingField(ref, effectiveArrayIndex, fieldIndex,
737                                                         nestedIndex, uniqueFullNames);
738                             }
739                         }
740                     }
741                     else
742                     {
743                         for (GLuint nestedIndex = 0; nestedIndex < fieldVarying.fields.size();
744                              nestedIndex++)
745                         {
746                             collectUserVaryingField(ref, effectiveArrayIndex, fieldIndex,
747                                                     nestedIndex, uniqueFullNames);
748                         }
749                     }
750                 }
751                 else
752                 {
753                     collectUserVaryingField(ref, effectiveArrayIndex, fieldIndex, GL_INVALID_INDEX,
754                                             uniqueFullNames);
755                 }
756             }
757         }
758         if (input)
759         {
760             (*uniqueFullNames)[ref.frontShaderStage].insert(input->name);
761             if (input->isShaderIOBlock)
762             {
763                 (*uniqueFullNames)[ref.frontShaderStage].insert(input->structOrBlockName);
764             }
765         }
766         if (output)
767         {
768             (*uniqueFullNames)[ref.backShaderStage].insert(output->name);
769         }
770     }
771     else
772     {
773         collectUserVarying(ref, uniqueFullNames);
774     }
775 }
776 
collectTFVarying(const std::string & tfVarying,const ProgramVaryingRef & ref,VaryingUniqueFullNames * uniqueFullNames)777 void VaryingPacking::collectTFVarying(const std::string &tfVarying,
778                                       const ProgramVaryingRef &ref,
779                                       VaryingUniqueFullNames *uniqueFullNames)
780 {
781     const sh::ShaderVariable *input = ref.frontShader;
782 
783     std::vector<unsigned int> subscripts;
784     std::string baseName = ParseResourceName(tfVarying, &subscripts);
785 
786     // Already packed as active varying.
787     if ((*uniqueFullNames)[ref.frontShaderStage].count(tfVarying) > 0 ||
788         (*uniqueFullNames)[ref.frontShaderStage].count(baseName) > 0 ||
789         (input->isShaderIOBlock &&
790          (*uniqueFullNames)[ref.frontShaderStage].count(input->structOrBlockName) > 0))
791     {
792         return;
793     }
794 
795     if (input->isStruct())
796     {
797         GLuint fieldIndex               = 0;
798         const sh::ShaderVariable *field = input->findField(tfVarying, &fieldIndex);
799         if (field != nullptr)
800         {
801             ASSERT(input->isShaderIOBlock || (!field->isStruct() && !field->isArray()));
802 
803             // If it's an I/O block whose member is being captured, pack every member of the
804             // block.  Currently, we pack either all or none of an I/O block.
805             if (input->isShaderIOBlock)
806             {
807                 for (fieldIndex = 0; fieldIndex < input->fields.size(); ++fieldIndex)
808                 {
809                     if (input->fields[fieldIndex].isStruct())
810                     {
811 
812                         for (GLuint nestedIndex = 0;
813                              nestedIndex < input->fields[fieldIndex].fields.size(); nestedIndex++)
814                         {
815                             collectUserVaryingFieldTF(ref, input->fields[fieldIndex], fieldIndex,
816                                                       nestedIndex);
817                         }
818                     }
819                     else
820                     {
821                         collectUserVaryingFieldTF(ref, input->fields[fieldIndex], fieldIndex,
822                                                   GL_INVALID_INDEX);
823                     }
824                 }
825 
826                 (*uniqueFullNames)[ref.frontShaderStage].insert(input->structOrBlockName);
827             }
828             else
829             {
830                 collectUserVaryingFieldTF(ref, *field, fieldIndex, GL_INVALID_INDEX);
831             }
832             (*uniqueFullNames)[ref.frontShaderStage].insert(tfVarying);
833             (*uniqueFullNames)[ref.frontShaderStage].insert(input->name);
834         }
835     }
836     // Array as a whole and array element conflict has already been checked in
837     // linkValidateTransformFeedback.
838     else if (baseName == input->name)
839     {
840         size_t subscript = GL_INVALID_INDEX;
841         if (!subscripts.empty())
842         {
843             subscript = subscripts.back();
844         }
845 
846         // only pack varyings that are not builtins.
847         if (tfVarying.compare(0, 3, "gl_") != 0)
848         {
849             collectUserVaryingTF(ref, subscript);
850             (*uniqueFullNames)[ref.frontShaderStage].insert(tfVarying);
851         }
852     }
853 }
854 
collectAndPackUserVaryings(gl::InfoLog & infoLog,GLint maxVaryingVectors,PackMode packMode,ShaderType frontShaderStage,ShaderType backShaderStage,const ProgramMergedVaryings & mergedVaryings,const std::vector<std::string> & tfVaryings,const bool isSeparableProgram)855 bool VaryingPacking::collectAndPackUserVaryings(gl::InfoLog &infoLog,
856                                                 GLint maxVaryingVectors,
857                                                 PackMode packMode,
858                                                 ShaderType frontShaderStage,
859                                                 ShaderType backShaderStage,
860                                                 const ProgramMergedVaryings &mergedVaryings,
861                                                 const std::vector<std::string> &tfVaryings,
862                                                 const bool isSeparableProgram)
863 {
864     VaryingUniqueFullNames uniqueFullNames;
865 
866     reset();
867 
868     for (const ProgramVaryingRef &ref : mergedVaryings)
869     {
870         const sh::ShaderVariable *input  = ref.frontShader;
871         const sh::ShaderVariable *output = ref.backShader;
872 
873         if ((input && ref.frontShaderStage != frontShaderStage) ||
874             (output && ref.backShaderStage != backShaderStage))
875         {
876             continue;
877         }
878 
879         const bool isActiveBuiltInInput  = input && input->isBuiltIn() && input->active;
880         const bool isActiveBuiltInOutput = output && output->isBuiltIn() && output->active;
881 
882         if (isActiveBuiltInInput)
883         {
884             SetActivePerVertexMembers(input, &mOutputPerVertexActiveMembers[frontShaderStage]);
885         }
886 
887         // Only pack statically used varyings that have a matched input or output, plus special
888         // builtins. Note that we pack all statically used user-defined varyings even if they are
889         // not active. GLES specs are a bit vague on whether it's allowed to only pack active
890         // varyings, though GLES 3.1 spec section 11.1.2.1 says that "device-dependent
891         // optimizations" may be used to make vertex shader outputs fit.
892         //
893         // When separable programs are linked, varyings at the separable program's boundary are
894         // treated as active. See section 7.4.1 in
895         // https://www.khronos.org/registry/OpenGL/specs/es/3.2/es_spec_3.2.pdf
896         bool matchedInputOutputStaticUse = (input && output && output->staticUse);
897         bool activeBuiltIn               = (isActiveBuiltInInput || isActiveBuiltInOutput);
898 
899         // Output variable in TCS can be read as input in another invocation by barrier.
900         // See section 11.2.1.2.4 Tessellation Control Shader Execution Order in OpenGL ES 3.2.
901         bool staticUseInTCS =
902             (input && input->staticUse && ref.frontShaderStage == ShaderType::TessControl);
903 
904         // Separable program requirements
905         bool separableActiveInput  = (input && (input->active || !output));
906         bool separableActiveOutput = (output && (output->active || !input));
907         bool separableActiveVarying =
908             (isSeparableProgram && (separableActiveInput || separableActiveOutput));
909 
910         if (matchedInputOutputStaticUse || activeBuiltIn || separableActiveVarying ||
911             staticUseInTCS)
912         {
913             const sh::ShaderVariable *varying = output ? output : input;
914 
915             if (!ShouldSkipPackedVarying(*varying, packMode))
916             {
917                 collectVarying(*varying, ref, packMode, &uniqueFullNames);
918                 continue;
919             }
920         }
921 
922         // If the varying is not used in the input, we know it is inactive, unless it's a separable
923         // program, in which case the input shader may not exist in this program.
924         if (!input && !isSeparableProgram)
925         {
926             if (!output->isBuiltIn() && output->id != 0)
927             {
928                 mInactiveVaryingIds[ref.backShaderStage].push_back(output->id);
929             }
930             continue;
931         }
932 
933         // Keep Transform FB varyings in the merged list always.
934         for (const std::string &tfVarying : tfVaryings)
935         {
936             collectTFVarying(tfVarying, ref, &uniqueFullNames);
937         }
938 
939         if (input && !input->isBuiltIn())
940         {
941             const std::string &name =
942                 input->isShaderIOBlock ? input->structOrBlockName : input->name;
943             if (uniqueFullNames[ref.frontShaderStage].count(name) == 0 && input->id != 0)
944             {
945                 mInactiveVaryingIds[ref.frontShaderStage].push_back(input->id);
946             }
947         }
948         if (output && !output->isBuiltIn())
949         {
950             const std::string &name =
951                 output->isShaderIOBlock ? output->structOrBlockName : output->name;
952             if (uniqueFullNames[ref.backShaderStage].count(name) == 0 && output->id != 0)
953             {
954                 mInactiveVaryingIds[ref.backShaderStage].push_back(output->id);
955             }
956         }
957     }
958 
959     std::sort(mPackedVaryings.begin(), mPackedVaryings.end(), ComparePackedVarying);
960 
961     return packUserVaryings(infoLog, maxVaryingVectors, packMode, mPackedVaryings);
962 }
963 
964 // See comment on packVarying.
packUserVaryings(gl::InfoLog & infoLog,GLint maxVaryingVectors,PackMode packMode,const std::vector<PackedVarying> & packedVaryings)965 bool VaryingPacking::packUserVaryings(gl::InfoLog &infoLog,
966                                       GLint maxVaryingVectors,
967                                       PackMode packMode,
968                                       const std::vector<PackedVarying> &packedVaryings)
969 {
970     clearRegisterMap();
971     mRegisterMap.resize(maxVaryingVectors);
972 
973     // "Variables are packed into the registers one at a time so that they each occupy a contiguous
974     // subrectangle. No splitting of variables is permitted."
975     for (const PackedVarying &packedVarying : packedVaryings)
976     {
977         if (!packVaryingIntoRegisterMap(packMode, packedVarying))
978         {
979             ShaderType eitherStage = packedVarying.frontVarying.varying
980                                          ? packedVarying.frontVarying.stage
981                                          : packedVarying.backVarying.stage;
982             infoLog << "Could not pack varying " << packedVarying.fullName(eitherStage);
983 
984             // TODO(jmadill): Implement more sophisticated component packing in D3D9.
985             if (packMode == PackMode::ANGLE_NON_CONFORMANT_D3D9)
986             {
987                 infoLog << "Note: Additional non-conformant packing restrictions are enforced on "
988                            "D3D9.";
989             }
990 
991             return false;
992         }
993     }
994 
995     // Sort the packed register list
996     std::sort(mRegisterList.begin(), mRegisterList.end());
997 
998     return true;
999 }
1000 
1001 // ProgramVaryingPacking implementation.
1002 ProgramVaryingPacking::ProgramVaryingPacking() = default;
1003 
1004 ProgramVaryingPacking::~ProgramVaryingPacking() = default;
1005 
getInputPacking(ShaderType backShaderStage) const1006 const VaryingPacking &ProgramVaryingPacking::getInputPacking(ShaderType backShaderStage) const
1007 {
1008     ShaderType frontShaderStage = mBackToFrontStageMap[backShaderStage];
1009 
1010     // If there's a missing shader stage, return the compute shader packing which is always empty.
1011     if (frontShaderStage == ShaderType::InvalidEnum)
1012     {
1013         ASSERT(mVaryingPackings[ShaderType::Compute].getMaxSemanticIndex() == 0);
1014         return mVaryingPackings[ShaderType::Compute];
1015     }
1016 
1017     return mVaryingPackings[frontShaderStage];
1018 }
1019 
getOutputPacking(ShaderType frontShaderStage) const1020 const VaryingPacking &ProgramVaryingPacking::getOutputPacking(ShaderType frontShaderStage) const
1021 {
1022     return mVaryingPackings[frontShaderStage];
1023 }
1024 
collectAndPackUserVaryings(InfoLog & infoLog,const Caps & caps,PackMode packMode,const ShaderBitSet & activeShadersMask,const ProgramMergedVaryings & mergedVaryings,const std::vector<std::string> & tfVaryings,bool isSeparableProgram)1025 bool ProgramVaryingPacking::collectAndPackUserVaryings(InfoLog &infoLog,
1026                                                        const Caps &caps,
1027                                                        PackMode packMode,
1028                                                        const ShaderBitSet &activeShadersMask,
1029                                                        const ProgramMergedVaryings &mergedVaryings,
1030                                                        const std::vector<std::string> &tfVaryings,
1031                                                        bool isSeparableProgram)
1032 {
1033     mBackToFrontStageMap.fill(ShaderType::InvalidEnum);
1034 
1035     ShaderBitSet activeShaders = activeShadersMask;
1036 
1037     ASSERT(activeShaders.any());
1038     ShaderType frontShaderStage     = activeShaders.first();
1039     activeShaders[frontShaderStage] = false;
1040 
1041     // Special case for start-after-vertex.
1042     if (frontShaderStage != ShaderType::Vertex)
1043     {
1044         ShaderType emulatedFrontShaderStage = ShaderType::Vertex;
1045         ShaderType backShaderStage          = frontShaderStage;
1046 
1047         if (!mVaryingPackings[emulatedFrontShaderStage].collectAndPackUserVaryings(
1048                 infoLog, GetMaxShaderInputVectors(caps, backShaderStage), packMode,
1049                 ShaderType::InvalidEnum, backShaderStage, mergedVaryings, tfVaryings,
1050                 isSeparableProgram))
1051         {
1052             return false;
1053         }
1054         mBackToFrontStageMap[backShaderStage] = emulatedFrontShaderStage;
1055     }
1056 
1057     // Process input/output shader pairs.
1058     for (ShaderType backShaderStage : activeShaders)
1059     {
1060         GLint maxVaryingVectors;
1061         if (frontShaderStage == ShaderType::Vertex && backShaderStage == ShaderType::Fragment)
1062         {
1063             maxVaryingVectors = caps.maxVaryingVectors;
1064         }
1065         else
1066         {
1067             GLint outputVaryingsMax = GetMaxShaderOutputVectors(caps, frontShaderStage);
1068             GLint inputVaryingsMax  = GetMaxShaderInputVectors(caps, backShaderStage);
1069             maxVaryingVectors       = std::min(inputVaryingsMax, outputVaryingsMax);
1070         }
1071 
1072         ASSERT(maxVaryingVectors > 0 && maxVaryingVectors < std::numeric_limits<GLint>::max());
1073 
1074         if (!mVaryingPackings[frontShaderStage].collectAndPackUserVaryings(
1075                 infoLog, maxVaryingVectors, packMode, frontShaderStage, backShaderStage,
1076                 mergedVaryings, tfVaryings, isSeparableProgram))
1077         {
1078             return false;
1079         }
1080 
1081         mBackToFrontStageMap[backShaderStage] = frontShaderStage;
1082         frontShaderStage                      = backShaderStage;
1083     }
1084 
1085     // Special case for stop-before-fragment.
1086     if (frontShaderStage != ShaderType::Fragment)
1087     {
1088         if (!mVaryingPackings[frontShaderStage].collectAndPackUserVaryings(
1089                 infoLog, GetMaxShaderOutputVectors(caps, frontShaderStage), packMode,
1090                 frontShaderStage, ShaderType::InvalidEnum, mergedVaryings, tfVaryings,
1091                 isSeparableProgram))
1092         {
1093             return false;
1094         }
1095 
1096         ShaderType emulatedBackShaderStage            = ShaderType::Fragment;
1097         mBackToFrontStageMap[emulatedBackShaderStage] = frontShaderStage;
1098     }
1099 
1100     return true;
1101 }
1102 
GetMergedVaryingsFromLinkingVariables(const LinkingVariables & linkingVariables)1103 ProgramMergedVaryings GetMergedVaryingsFromLinkingVariables(
1104     const LinkingVariables &linkingVariables)
1105 {
1106     ShaderType frontShaderType = ShaderType::InvalidEnum;
1107     ProgramMergedVaryings merged;
1108 
1109     for (ShaderType backShaderType : kAllGraphicsShaderTypes)
1110     {
1111         if (!linkingVariables.isShaderStageUsedBitset[backShaderType])
1112         {
1113             continue;
1114         }
1115         const std::vector<sh::ShaderVariable> &backShaderOutputVaryings =
1116             linkingVariables.outputVaryings[backShaderType];
1117         const std::vector<sh::ShaderVariable> &backShaderInputVaryings =
1118             linkingVariables.inputVaryings[backShaderType];
1119 
1120         // Add outputs. These are always unmatched since we walk shader stages sequentially.
1121         for (const sh::ShaderVariable &frontVarying : backShaderOutputVaryings)
1122         {
1123             ProgramVaryingRef ref;
1124             ref.frontShader      = &frontVarying;
1125             ref.frontShaderStage = backShaderType;
1126             merged.push_back(ref);
1127         }
1128 
1129         if (frontShaderType == ShaderType::InvalidEnum)
1130         {
1131             // If this is our first shader stage, and not a VS, we might have unmatched inputs.
1132             for (const sh::ShaderVariable &backVarying : backShaderInputVaryings)
1133             {
1134                 ProgramVaryingRef ref;
1135                 ref.backShader      = &backVarying;
1136                 ref.backShaderStage = backShaderType;
1137                 merged.push_back(ref);
1138             }
1139         }
1140         else
1141         {
1142             // Match inputs with the prior shader stage outputs.
1143             for (const sh::ShaderVariable &backVarying : backShaderInputVaryings)
1144             {
1145                 bool found = false;
1146                 for (ProgramVaryingRef &ref : merged)
1147                 {
1148                     if (ref.frontShader && ref.frontShaderStage == frontShaderType &&
1149                         InterfaceVariablesMatch(*ref.frontShader, backVarying))
1150                     {
1151                         ASSERT(ref.backShader == nullptr);
1152 
1153                         ref.backShader      = &backVarying;
1154                         ref.backShaderStage = backShaderType;
1155                         found               = true;
1156                         break;
1157                     }
1158                 }
1159 
1160                 // Some outputs are never matched, e.g. some builtin variables.
1161                 if (!found)
1162                 {
1163                     ProgramVaryingRef ref;
1164                     ref.backShader      = &backVarying;
1165                     ref.backShaderStage = backShaderType;
1166                     merged.push_back(ref);
1167                 }
1168             }
1169         }
1170 
1171         // Save the current back shader to use as the next front shader.
1172         frontShaderType = backShaderType;
1173     }
1174 
1175     return merged;
1176 }
1177 }  // namespace gl
1178