• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/utilities.h"
15 #include "libANGLE/Program.h"
16 #include "libANGLE/Shader.h"
17 
18 namespace gl
19 {
20 
21 namespace
22 {
23 
24 // true if varying x has a higher priority in packing than y
ComparePackedVarying(const PackedVarying & x,const PackedVarying & y)25 bool ComparePackedVarying(const PackedVarying &x, const PackedVarying &y)
26 {
27     // If the PackedVarying 'x' or 'y' to be compared is an array element, this clones an equivalent
28     // non-array shader variable 'vx' or 'vy' for actual comparison instead.
29     sh::ShaderVariable vx, vy;
30     const sh::ShaderVariable *px, *py;
31 
32     px = &x.varying();
33     py = &y.varying();
34 
35     if (x.isArrayElement())
36     {
37         vx = *px;
38         vx.arraySizes.clear();
39         px = &vx;
40     }
41 
42     if (y.isArrayElement())
43     {
44         vy = *py;
45         vy.arraySizes.clear();
46         py = &vy;
47     }
48 
49     return gl::CompareShaderVar(*px, *py);
50 }
51 
52 }  // anonymous namespace
53 
54 // Implementation of VaryingInShaderRef
VaryingInShaderRef(ShaderType stageIn,const sh::ShaderVariable * varyingIn)55 VaryingInShaderRef::VaryingInShaderRef(ShaderType stageIn, const sh::ShaderVariable *varyingIn)
56     : varying(varyingIn), stage(stageIn)
57 {}
58 
59 VaryingInShaderRef::~VaryingInShaderRef() = default;
60 
VaryingInShaderRef(VaryingInShaderRef && other)61 VaryingInShaderRef::VaryingInShaderRef(VaryingInShaderRef &&other)
62 {
63     *this = std::move(other);
64 }
65 
operator =(VaryingInShaderRef && other)66 VaryingInShaderRef &VaryingInShaderRef::operator=(VaryingInShaderRef &&other)
67 {
68     std::swap(varying, other.varying);
69     std::swap(stage, other.stage);
70     std::swap(parentStructName, other.parentStructName);
71     std::swap(parentStructMappedName, other.parentStructMappedName);
72 
73     return *this;
74 }
75 
76 // Implementation of PackedVarying
PackedVarying(VaryingInShaderRef && frontVaryingIn,VaryingInShaderRef && backVaryingIn,sh::InterpolationType interpolationIn)77 PackedVarying::PackedVarying(VaryingInShaderRef &&frontVaryingIn,
78                              VaryingInShaderRef &&backVaryingIn,
79                              sh::InterpolationType interpolationIn)
80     : PackedVarying(std::move(frontVaryingIn), std::move(backVaryingIn), interpolationIn, 0)
81 {}
82 
PackedVarying(VaryingInShaderRef && frontVaryingIn,VaryingInShaderRef && backVaryingIn,sh::InterpolationType interpolationIn,GLuint fieldIndexIn)83 PackedVarying::PackedVarying(VaryingInShaderRef &&frontVaryingIn,
84                              VaryingInShaderRef &&backVaryingIn,
85                              sh::InterpolationType interpolationIn,
86                              GLuint fieldIndexIn)
87     : frontVarying(std::move(frontVaryingIn)),
88       backVarying(std::move(backVaryingIn)),
89       interpolation(interpolationIn),
90       arrayIndex(GL_INVALID_INDEX),
91       fieldIndex(fieldIndexIn)
92 {}
93 
94 PackedVarying::~PackedVarying() = default;
95 
PackedVarying(PackedVarying && other)96 PackedVarying::PackedVarying(PackedVarying &&other)
97     : frontVarying(other.frontVarying.stage, other.frontVarying.varying),
98       backVarying(other.backVarying.stage, other.backVarying.varying)
99 {
100     *this = std::move(other);
101 }
102 
operator =(PackedVarying && other)103 PackedVarying &PackedVarying::operator=(PackedVarying &&other)
104 {
105     std::swap(frontVarying, other.frontVarying);
106     std::swap(backVarying, other.backVarying);
107     std::swap(interpolation, other.interpolation);
108     std::swap(arrayIndex, other.arrayIndex);
109     std::swap(fieldIndex, other.fieldIndex);
110 
111     return *this;
112 }
113 
114 // Implementation of VaryingPacking
VaryingPacking(GLuint maxVaryingVectors,PackMode packMode)115 VaryingPacking::VaryingPacking(GLuint maxVaryingVectors, PackMode packMode)
116     : mRegisterMap(maxVaryingVectors), mPackMode(packMode)
117 {}
118 
119 VaryingPacking::~VaryingPacking() = default;
120 
reset()121 void VaryingPacking::reset()
122 {
123     clearRegisterMap();
124     mRegisterList.clear();
125     mPackedVaryings.clear();
126 
127     for (std::vector<std::string> inactiveVaryingMappedNames : mInactiveVaryingMappedNames)
128     {
129         inactiveVaryingMappedNames.clear();
130     }
131 }
132 
clearRegisterMap()133 void VaryingPacking::clearRegisterMap()
134 {
135     std::fill(mRegisterMap.begin(), mRegisterMap.end(), Register());
136 }
137 
138 // Packs varyings into generic varying registers, using the algorithm from
139 // See [OpenGL ES Shading Language 1.00 rev. 17] appendix A section 7 page 111
140 // Also [OpenGL ES Shading Language 3.00 rev. 4] Section 11 page 119
141 // Returns false if unsuccessful.
packVarying(const PackedVarying & packedVarying)142 bool VaryingPacking::packVarying(const PackedVarying &packedVarying)
143 {
144     const sh::ShaderVariable &varying = packedVarying.varying();
145 
146     // "Non - square matrices of type matCxR consume the same space as a square matrix of type matN
147     // where N is the greater of C and R."
148     // Here we are a bit more conservative and allow packing non-square matrices more tightly.
149     // Make sure we use transposed matrix types to count registers correctly.
150     ASSERT(!varying.isStruct());
151     GLenum transposedType       = gl::TransposeMatrixType(varying.type);
152     unsigned int varyingRows    = gl::VariableRowCount(transposedType);
153     unsigned int varyingColumns = gl::VariableColumnCount(transposedType);
154 
155     // Special pack mode for D3D9. Each varying takes a full register, no sharing.
156     // TODO(jmadill): Implement more sophisticated component packing in D3D9.
157     if (mPackMode == PackMode::ANGLE_NON_CONFORMANT_D3D9)
158     {
159         varyingColumns = 4;
160     }
161 
162     // "Variables of type mat2 occupies 2 complete rows."
163     // For non-WebGL contexts, we allow mat2 to occupy only two columns per row.
164     else if (mPackMode == PackMode::WEBGL_STRICT && varying.type == GL_FLOAT_MAT2)
165     {
166         varyingColumns = 4;
167     }
168 
169     // "Arrays of size N are assumed to take N times the size of the base type"
170     // GLSL ES 3.10 section 4.3.6: Output variables cannot be arrays of arrays or arrays of
171     // structures, so we may use getBasicTypeElementCount().
172     const unsigned int elementCount = varying.getBasicTypeElementCount();
173     varyingRows *= (packedVarying.isArrayElement() ? 1 : elementCount);
174 
175     unsigned int maxVaryingVectors = static_cast<unsigned int>(mRegisterMap.size());
176 
177     // Fail if we are packing a single over-large varying.
178     if (varyingRows > maxVaryingVectors)
179     {
180         return false;
181     }
182 
183     // "For 2, 3 and 4 component variables packing is started using the 1st column of the 1st row.
184     // Variables are then allocated to successive rows, aligning them to the 1st column."
185     if (varyingColumns >= 2 && varyingColumns <= 4)
186     {
187         for (unsigned int row = 0; row <= maxVaryingVectors - varyingRows; ++row)
188         {
189             if (isFree(row, 0, varyingRows, varyingColumns))
190             {
191                 insert(row, 0, packedVarying);
192                 return true;
193             }
194         }
195 
196         // "For 2 component variables, when there are no spare rows, the strategy is switched to
197         // using the highest numbered row and the lowest numbered column where the variable will
198         // fit."
199         if (varyingColumns == 2)
200         {
201             for (unsigned int r = maxVaryingVectors - varyingRows + 1; r-- >= 1;)
202             {
203                 if (isFree(r, 2, varyingRows, 2))
204                 {
205                     insert(r, 2, packedVarying);
206                     return true;
207                 }
208             }
209         }
210 
211         return false;
212     }
213 
214     // "1 component variables have their own packing rule. They are packed in order of size, largest
215     // first. Each variable is placed in the column that leaves the least amount of space in the
216     // column and aligned to the lowest available rows within that column."
217     ASSERT(varyingColumns == 1);
218     unsigned int contiguousSpace[4]     = {0};
219     unsigned int bestContiguousSpace[4] = {0};
220     unsigned int totalSpace[4]          = {0};
221 
222     for (unsigned int row = 0; row < maxVaryingVectors; ++row)
223     {
224         for (unsigned int column = 0; column < 4; ++column)
225         {
226             if (mRegisterMap[row][column])
227             {
228                 contiguousSpace[column] = 0;
229             }
230             else
231             {
232                 contiguousSpace[column]++;
233                 totalSpace[column]++;
234 
235                 if (contiguousSpace[column] > bestContiguousSpace[column])
236                 {
237                     bestContiguousSpace[column] = contiguousSpace[column];
238                 }
239             }
240         }
241     }
242 
243     unsigned int bestColumn = 0;
244     for (unsigned int column = 1; column < 4; ++column)
245     {
246         if (bestContiguousSpace[column] >= varyingRows &&
247             (bestContiguousSpace[bestColumn] < varyingRows ||
248              totalSpace[column] < totalSpace[bestColumn]))
249         {
250             bestColumn = column;
251         }
252     }
253 
254     if (bestContiguousSpace[bestColumn] >= varyingRows)
255     {
256         for (unsigned int row = 0; row < maxVaryingVectors; row++)
257         {
258             if (isFree(row, bestColumn, varyingRows, 1))
259             {
260                 for (unsigned int arrayIndex = 0; arrayIndex < varyingRows; ++arrayIndex)
261                 {
262                     // If varyingRows > 1, it must be an array.
263                     PackedVaryingRegister registerInfo;
264                     registerInfo.packedVarying  = &packedVarying;
265                     registerInfo.registerRow    = row + arrayIndex;
266                     registerInfo.registerColumn = bestColumn;
267                     registerInfo.varyingArrayIndex =
268                         (packedVarying.isArrayElement() ? packedVarying.arrayIndex : arrayIndex);
269                     registerInfo.varyingRowIndex = 0;
270                     // Do not record register info for builtins.
271                     // TODO(jmadill): Clean this up.
272                     if (!varying.isBuiltIn())
273                     {
274                         mRegisterList.push_back(registerInfo);
275                     }
276                     mRegisterMap[row + arrayIndex][bestColumn] = true;
277                 }
278                 break;
279             }
280         }
281         return true;
282     }
283 
284     return false;
285 }
286 
isFree(unsigned int registerRow,unsigned int registerColumn,unsigned int varyingRows,unsigned int varyingColumns) const287 bool VaryingPacking::isFree(unsigned int registerRow,
288                             unsigned int registerColumn,
289                             unsigned int varyingRows,
290                             unsigned int varyingColumns) const
291 {
292     for (unsigned int row = 0; row < varyingRows; ++row)
293     {
294         ASSERT(registerRow + row < mRegisterMap.size());
295         for (unsigned int column = 0; column < varyingColumns; ++column)
296         {
297             ASSERT(registerColumn + column < 4);
298             if (mRegisterMap[registerRow + row][registerColumn + column])
299             {
300                 return false;
301             }
302         }
303     }
304 
305     return true;
306 }
307 
insert(unsigned int registerRow,unsigned int registerColumn,const PackedVarying & packedVarying)308 void VaryingPacking::insert(unsigned int registerRow,
309                             unsigned int registerColumn,
310                             const PackedVarying &packedVarying)
311 {
312     unsigned int varyingRows    = 0;
313     unsigned int varyingColumns = 0;
314 
315     const sh::ShaderVariable &varying = packedVarying.varying();
316     ASSERT(!varying.isStruct());
317     GLenum transposedType = gl::TransposeMatrixType(varying.type);
318     varyingRows           = gl::VariableRowCount(transposedType);
319     varyingColumns        = gl::VariableColumnCount(transposedType);
320 
321     PackedVaryingRegister registerInfo;
322     registerInfo.packedVarying  = &packedVarying;
323     registerInfo.registerColumn = registerColumn;
324 
325     // GLSL ES 3.10 section 4.3.6: Output variables cannot be arrays of arrays or arrays of
326     // structures, so we may use getBasicTypeElementCount().
327     const unsigned int arrayElementCount = varying.getBasicTypeElementCount();
328     for (unsigned int arrayElement = 0; arrayElement < arrayElementCount; ++arrayElement)
329     {
330         if (packedVarying.isArrayElement() && arrayElement != packedVarying.arrayIndex)
331         {
332             continue;
333         }
334         for (unsigned int varyingRow = 0; varyingRow < varyingRows; ++varyingRow)
335         {
336             registerInfo.registerRow     = registerRow + (arrayElement * varyingRows) + varyingRow;
337             registerInfo.varyingRowIndex = varyingRow;
338             registerInfo.varyingArrayIndex = arrayElement;
339             // Do not record register info for builtins.
340             // TODO(jmadill): Clean this up.
341             if (!varying.isBuiltIn())
342             {
343                 mRegisterList.push_back(registerInfo);
344             }
345 
346             for (unsigned int columnIndex = 0; columnIndex < varyingColumns; ++columnIndex)
347             {
348                 mRegisterMap[registerInfo.registerRow][registerColumn + columnIndex] = true;
349             }
350         }
351     }
352 }
353 
packUserVarying(const ProgramVaryingRef & ref,VaryingUniqueFullNames * uniqueFullNames)354 void VaryingPacking::packUserVarying(const ProgramVaryingRef &ref,
355                                      VaryingUniqueFullNames *uniqueFullNames)
356 {
357     const sh::ShaderVariable *input  = ref.frontShader;
358     const sh::ShaderVariable *output = ref.backShader;
359 
360     // Will get the vertex shader interpolation by default.
361     sh::InterpolationType interpolation = input ? input->interpolation : output->interpolation;
362 
363     VaryingInShaderRef frontVarying(ref.frontShaderStage, input);
364     VaryingInShaderRef backVarying(ref.backShaderStage, output);
365 
366     mPackedVaryings.emplace_back(std::move(frontVarying), std::move(backVarying), interpolation);
367     if (input)
368     {
369         (*uniqueFullNames)[ref.frontShaderStage].insert(
370             mPackedVaryings.back().fullName(ref.frontShaderStage));
371     }
372     if (output)
373     {
374         (*uniqueFullNames)[ref.backShaderStage].insert(
375             mPackedVaryings.back().fullName(ref.backShaderStage));
376     }
377 }
378 
packUserVaryingField(const ProgramVaryingRef & ref,GLuint fieldIndex,VaryingUniqueFullNames * uniqueFullNames)379 void VaryingPacking::packUserVaryingField(const ProgramVaryingRef &ref,
380                                           GLuint fieldIndex,
381                                           VaryingUniqueFullNames *uniqueFullNames)
382 {
383     const sh::ShaderVariable *input  = ref.frontShader;
384     const sh::ShaderVariable *output = ref.backShader;
385 
386     // Will get the vertex shader interpolation by default.
387     sh::InterpolationType interpolation = input ? input->interpolation : output->interpolation;
388 
389     const sh::ShaderVariable *frontField = input ? &input->fields[fieldIndex] : nullptr;
390     const sh::ShaderVariable *backField  = output ? &output->fields[fieldIndex] : nullptr;
391 
392     VaryingInShaderRef frontVarying(ref.frontShaderStage, frontField);
393     VaryingInShaderRef backVarying(ref.backShaderStage, backField);
394 
395     if (input)
396     {
397         ASSERT(!frontField->isStruct() && !frontField->isArray());
398         frontVarying.parentStructName       = input->name;
399         frontVarying.parentStructMappedName = input->mappedName;
400     }
401     if (output)
402     {
403         ASSERT(!backField->isStruct() && !backField->isArray());
404         backVarying.parentStructName       = output->name;
405         backVarying.parentStructMappedName = output->mappedName;
406     }
407 
408     mPackedVaryings.emplace_back(std::move(frontVarying), std::move(backVarying), interpolation,
409                                  fieldIndex);
410 
411     if (input)
412     {
413         (*uniqueFullNames)[ref.frontShaderStage].insert(
414             mPackedVaryings.back().fullName(ref.frontShaderStage));
415     }
416     if (output)
417     {
418         (*uniqueFullNames)[ref.backShaderStage].insert(
419             mPackedVaryings.back().fullName(ref.backShaderStage));
420     }
421 }
422 
packUserVaryingTF(const ProgramVaryingRef & ref,size_t subscript)423 void VaryingPacking::packUserVaryingTF(const ProgramVaryingRef &ref, size_t subscript)
424 {
425     const sh::ShaderVariable *input = ref.frontShader;
426 
427     VaryingInShaderRef frontVarying(ref.frontShaderStage, input);
428     VaryingInShaderRef backVarying(ref.backShaderStage, nullptr);
429 
430     mPackedVaryings.emplace_back(std::move(frontVarying), std::move(backVarying),
431                                  input->interpolation);
432     mPackedVaryings.back().arrayIndex = static_cast<GLuint>(subscript);
433 }
434 
packUserVaryingFieldTF(const ProgramVaryingRef & ref,const sh::ShaderVariable & field,GLuint fieldIndex)435 void VaryingPacking::packUserVaryingFieldTF(const ProgramVaryingRef &ref,
436                                             const sh::ShaderVariable &field,
437                                             GLuint fieldIndex)
438 {
439     const sh::ShaderVariable *input = ref.frontShader;
440 
441     VaryingInShaderRef frontVarying(ref.frontShaderStage, &field);
442     VaryingInShaderRef backVarying(ref.backShaderStage, nullptr);
443 
444     frontVarying.parentStructName       = input->name;
445     frontVarying.parentStructMappedName = input->mappedName;
446 
447     mPackedVaryings.emplace_back(std::move(frontVarying), std::move(backVarying),
448                                  input->interpolation, fieldIndex);
449 }
450 
collectAndPackUserVaryings(gl::InfoLog & infoLog,const ProgramMergedVaryings & mergedVaryings,const std::vector<std::string> & tfVaryings,const bool isSeparableProgram)451 bool VaryingPacking::collectAndPackUserVaryings(gl::InfoLog &infoLog,
452                                                 const ProgramMergedVaryings &mergedVaryings,
453                                                 const std::vector<std::string> &tfVaryings,
454                                                 const bool isSeparableProgram)
455 {
456     VaryingUniqueFullNames uniqueFullNames;
457     mPackedVaryings.clear();
458     clearRegisterMap();
459 
460     for (const ProgramVaryingRef &ref : mergedVaryings)
461     {
462         const sh::ShaderVariable *input  = ref.frontShader;
463         const sh::ShaderVariable *output = ref.backShader;
464 
465         // Only pack statically used varyings that have a matched input or output, plus special
466         // builtins. Note that we pack all statically used user-defined varyings even if they are
467         // not active. GLES specs are a bit vague on whether it's allowed to only pack active
468         // varyings, though GLES 3.1 spec section 11.1.2.1 says that "device-dependent
469         // optimizations" may be used to make vertex shader outputs fit.
470         if ((input && output && output->staticUse) ||
471             (input && input->isBuiltIn() && input->active) ||
472             (output && output->isBuiltIn() && output->active) ||
473             (isSeparableProgram && ((input && input->active) || (output && output->active))))
474         {
475             const sh::ShaderVariable *varying = output ? output : input;
476 
477             // Don't count gl_Position. Also don't count gl_PointSize for D3D9.
478             if (varying->name != "gl_Position" &&
479                 !(varying->name == "gl_PointSize" &&
480                   mPackMode == PackMode::ANGLE_NON_CONFORMANT_D3D9))
481             {
482                 if (varying->isStruct())
483                 {
484                     ASSERT(!(varying->isArray() && varying == input));
485 
486                     for (GLuint fieldIndex = 0; fieldIndex < varying->fields.size(); ++fieldIndex)
487                     {
488                         packUserVaryingField(ref, fieldIndex, &uniqueFullNames);
489                     }
490                     if (input)
491                     {
492                         uniqueFullNames[ref.frontShaderStage].insert(input->name);
493                     }
494                     if (output)
495                     {
496                         uniqueFullNames[ref.backShaderStage].insert(output->name);
497                     }
498                 }
499                 else
500                 {
501                     packUserVarying(ref, &uniqueFullNames);
502                 }
503                 continue;
504             }
505         }
506 
507         // If the varying is not used in the input, we know it is inactive, unless it's a separable
508         // program, in which case the input shader may not exist in this program.
509         if (!input && !isSeparableProgram)
510         {
511             mInactiveVaryingMappedNames[ref.backShaderStage].push_back(output->mappedName);
512             continue;
513         }
514 
515         // Keep Transform FB varyings in the merged list always.
516         for (const std::string &tfVarying : tfVaryings)
517         {
518             std::vector<unsigned int> subscripts;
519             std::string baseName = ParseResourceName(tfVarying, &subscripts);
520             size_t subscript     = GL_INVALID_INDEX;
521             if (!subscripts.empty())
522             {
523                 subscript = subscripts.back();
524             }
525             // Already packed for fragment shader.
526             if (uniqueFullNames[ref.frontShaderStage].count(tfVarying) > 0 ||
527                 uniqueFullNames[ref.frontShaderStage].count(baseName) > 0)
528             {
529                 continue;
530             }
531             if (input->isStruct())
532             {
533                 GLuint fieldIndex               = 0;
534                 const sh::ShaderVariable *field = input->findField(tfVarying, &fieldIndex);
535                 if (field != nullptr)
536                 {
537                     ASSERT(!field->isStruct() && !field->isArray());
538 
539                     packUserVaryingFieldTF(ref, *field, fieldIndex);
540                     uniqueFullNames[ref.frontShaderStage].insert(tfVarying);
541                 }
542                 uniqueFullNames[ref.frontShaderStage].insert(input->name);
543             }
544             // Array as a whole and array element conflict has already been checked in
545             // linkValidateTransformFeedback.
546             else if (baseName == input->name)
547             {
548                 // only pack varyings that are not builtins.
549                 if (tfVarying.compare(0, 3, "gl_") != 0)
550                 {
551                     packUserVaryingTF(ref, subscript);
552                     uniqueFullNames[ref.frontShaderStage].insert(tfVarying);
553                 }
554                 // Continue to match next array element for 'input' if the current match is array
555                 // element.
556                 if (subscript == GL_INVALID_INDEX)
557                 {
558                     break;
559                 }
560             }
561         }
562 
563         if (input && uniqueFullNames[ref.frontShaderStage].count(input->name) == 0)
564         {
565             mInactiveVaryingMappedNames[ref.frontShaderStage].push_back(input->mappedName);
566         }
567         if (output && uniqueFullNames[ref.backShaderStage].count(output->name) == 0)
568         {
569             mInactiveVaryingMappedNames[ref.backShaderStage].push_back(output->mappedName);
570         }
571     }
572 
573     std::sort(mPackedVaryings.begin(), mPackedVaryings.end(), ComparePackedVarying);
574 
575     return packUserVaryings(infoLog, mPackedVaryings);
576 }
577 
578 // See comment on packVarying.
packUserVaryings(gl::InfoLog & infoLog,const std::vector<PackedVarying> & packedVaryings)579 bool VaryingPacking::packUserVaryings(gl::InfoLog &infoLog,
580                                       const std::vector<PackedVarying> &packedVaryings)
581 {
582     // "Variables are packed into the registers one at a time so that they each occupy a contiguous
583     // subrectangle. No splitting of variables is permitted."
584     for (const PackedVarying &packedVarying : packedVaryings)
585     {
586         if (!packVarying(packedVarying))
587         {
588             ShaderType eitherStage = packedVarying.frontVarying.varying
589                                          ? packedVarying.frontVarying.stage
590                                          : packedVarying.backVarying.stage;
591             infoLog << "Could not pack varying " << packedVarying.fullName(eitherStage);
592 
593             // TODO(jmadill): Implement more sophisticated component packing in D3D9.
594             if (mPackMode == PackMode::ANGLE_NON_CONFORMANT_D3D9)
595             {
596                 infoLog << "Note: Additional non-conformant packing restrictions are enforced on "
597                            "D3D9.";
598             }
599 
600             return false;
601         }
602     }
603 
604     // Sort the packed register list
605     std::sort(mRegisterList.begin(), mRegisterList.end());
606 
607     return true;
608 }
609 }  // namespace gl
610