1 //
2 // Copyright 2014 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 // StructureHLSL.cpp:
7 //   HLSL translation of GLSL constructors and structures.
8 //
9 
10 #include "compiler/translator/StructureHLSL.h"
11 #include "common/utilities.h"
12 #include "compiler/translator/OutputHLSL.h"
13 #include "compiler/translator/Types.h"
14 #include "compiler/translator/UtilsHLSL.h"
15 #include "compiler/translator/util.h"
16 
17 namespace sh
18 {
19 
20 namespace
21 {
22 
Define(const TStructure & structure,bool useHLSLRowMajorPacking,bool useStd140Packing,bool forcePadding,Std140PaddingHelper * padHelper)23 TString Define(const TStructure &structure,
24                bool useHLSLRowMajorPacking,
25                bool useStd140Packing,
26                bool forcePadding,
27                Std140PaddingHelper *padHelper)
28 {
29     const TFieldList &fields    = structure.fields();
30     const bool isNameless       = (structure.symbolType() == SymbolType::Empty);
31     const TString &structName   = QualifiedStructNameString(structure, useHLSLRowMajorPacking,
32                                                           useStd140Packing, forcePadding);
33     const TString declareString = (isNameless ? "struct" : "struct " + structName);
34 
35     TString string;
36     string += declareString +
37               "\n"
38               "{\n";
39 
40     size_t memberSize = fields.size();
41     for (const TField *field : fields)
42     {
43         memberSize--;
44         const TType &fieldType = *field->type();
45         if (!IsSampler(fieldType.getBasicType()))
46         {
47             const TStructure *fieldStruct = fieldType.getStruct();
48             const TString &fieldTypeString =
49                 fieldStruct ? QualifiedStructNameString(*fieldStruct, useHLSLRowMajorPacking,
50                                                         useStd140Packing, false)
51                             : TypeString(fieldType);
52 
53             if (padHelper)
54             {
55                 string += padHelper->prePaddingString(
56                     fieldType, (memberSize != fields.size() - 1) && forcePadding);
57             }
58 
59             string += "    " + fieldTypeString + " " + DecorateField(field->name(), structure) +
60                       ArrayString(fieldType).data() + ";\n";
61 
62             if (padHelper)
63             {
64                 string += padHelper->postPaddingString(fieldType, useHLSLRowMajorPacking,
65                                                        memberSize == 0, forcePadding);
66             }
67         }
68     }
69 
70     // Nameless structs do not finish with a semicolon and newline, to leave room for an instance
71     // variable
72     string += (isNameless ? "} " : "};\n");
73 
74     return string;
75 }
76 
WriteParameterList(const std::vector<TType> & parameters)77 TString WriteParameterList(const std::vector<TType> &parameters)
78 {
79     TString parameterList;
80     for (size_t parameter = 0u; parameter < parameters.size(); parameter++)
81     {
82         const TType &paramType = parameters[parameter];
83 
84         parameterList +=
85             TypeString(paramType) + " x" + str(parameter) + ArrayString(paramType).data();
86 
87         if (parameter < parameters.size() - 1u)
88         {
89             parameterList += ", ";
90         }
91     }
92     return parameterList;
93 }
94 
GetElementPadding(int elementIndex,int alignment)95 int GetElementPadding(int elementIndex, int alignment)
96 {
97     const int paddingOffset = elementIndex % alignment;
98     return paddingOffset != 0 ? (alignment - paddingOffset) : 0;
99 }
100 
101 }  // anonymous namespace
102 
Std140PaddingHelper(const std::map<TString,int> & structElementIndexes,unsigned * uniqueCounter)103 Std140PaddingHelper::Std140PaddingHelper(const std::map<TString, int> &structElementIndexes,
104                                          unsigned *uniqueCounter)
105     : mPaddingCounter(uniqueCounter), mElementIndex(0), mStructElementIndexes(&structElementIndexes)
106 {}
107 
Std140PaddingHelper(const Std140PaddingHelper & other)108 Std140PaddingHelper::Std140PaddingHelper(const Std140PaddingHelper &other)
109     : mPaddingCounter(other.mPaddingCounter),
110       mElementIndex(other.mElementIndex),
111       mStructElementIndexes(other.mStructElementIndexes)
112 {}
113 
operator =(const Std140PaddingHelper & other)114 Std140PaddingHelper &Std140PaddingHelper::operator=(const Std140PaddingHelper &other)
115 {
116     mPaddingCounter       = other.mPaddingCounter;
117     mElementIndex         = other.mElementIndex;
118     mStructElementIndexes = other.mStructElementIndexes;
119     return *this;
120 }
121 
next()122 TString Std140PaddingHelper::next()
123 {
124     unsigned value = (*mPaddingCounter)++;
125     return str(value);
126 }
127 
prePadding(const TType & type,bool forcePadding)128 int Std140PaddingHelper::prePadding(const TType &type, bool forcePadding)
129 {
130     if (type.getBasicType() == EbtStruct || type.isMatrix() || type.isArray())
131     {
132         if (forcePadding)
133         {
134             // Add padding between the structure's members to follow the std140 rules manually.
135             const int forcePaddingCount = GetElementPadding(mElementIndex, 4);
136             mElementIndex               = 0;
137             return forcePaddingCount;
138         }
139         else
140         {
141             // no padding needed, HLSL will align the field to a new register
142             mElementIndex = 0;
143             return 0;
144         }
145     }
146 
147     const GLenum glType     = GLVariableType(type);
148     const int numComponents = gl::VariableComponentCount(glType);
149 
150     if (numComponents >= 4)
151     {
152         if (forcePadding)
153         {
154             // Add padding between the structure's members to follow the std140 rules manually.
155             const int forcePaddingCount = GetElementPadding(mElementIndex, 4);
156             mElementIndex               = numComponents % 4;
157             return forcePaddingCount;
158         }
159         else
160         {
161             // no padding needed, HLSL will align the field to a new register
162             mElementIndex = 0;
163             return 0;
164         }
165     }
166 
167     if (mElementIndex + numComponents > 4)
168     {
169         if (forcePadding)
170         {
171             // Add padding between the structure's members to follow the std140 rules manually.
172             const int forcePaddingCount = GetElementPadding(mElementIndex, 4);
173             mElementIndex               = numComponents;
174             return forcePaddingCount;
175         }
176         else
177         {
178             // no padding needed, HLSL will align the field to a new register
179             mElementIndex = numComponents;
180             return 0;
181         }
182     }
183 
184     const int alignment    = numComponents == 3 ? 4 : numComponents;
185     const int paddingCount = GetElementPadding(mElementIndex, alignment);
186 
187     mElementIndex += paddingCount;
188     mElementIndex += numComponents;
189     mElementIndex %= 4;
190 
191     return paddingCount;
192 }
193 
prePaddingString(const TType & type,bool forcePadding)194 TString Std140PaddingHelper::prePaddingString(const TType &type, bool forcePadding)
195 {
196     int paddingCount = prePadding(type, forcePadding);
197 
198     TString padding;
199 
200     for (int paddingIndex = 0; paddingIndex < paddingCount; paddingIndex++)
201     {
202         padding += "    float pad_" + next() + ";\n";
203     }
204 
205     return padding;
206 }
207 
postPaddingString(const TType & type,bool useHLSLRowMajorPacking,bool isLastElement,bool forcePadding)208 TString Std140PaddingHelper::postPaddingString(const TType &type,
209                                                bool useHLSLRowMajorPacking,
210                                                bool isLastElement,
211                                                bool forcePadding)
212 {
213     if (!type.isMatrix() && !type.isArray() && type.getBasicType() != EbtStruct)
214     {
215         if (forcePadding)
216         {
217             const GLenum glType     = GLVariableType(type);
218             const int numComponents = gl::VariableComponentCount(glType);
219             if (isLastElement || (numComponents >= 4))
220             {
221                 // If this structure will be used as HLSL StructuredBuffer member's type, in
222                 // order to follow the std140 rules, add padding at the end of the structure
223                 // if necessary. Or if the current element straddles a vec4 boundary, add
224                 // padding to round up the base offset of the next element to the base
225                 // alignment of a vec4.
226                 TString forcePaddingStr;
227                 const int paddingCount = GetElementPadding(mElementIndex, 4);
228                 for (int paddingIndex = 0; paddingIndex < paddingCount; paddingIndex++)
229                 {
230                     forcePaddingStr += "    float pad_" + next() + ";\n";
231                 }
232                 mElementIndex = 0;
233                 return forcePaddingStr;
234             }
235         }
236 
237         return "";
238     }
239 
240     int numComponents           = 0;
241     const TStructure *structure = type.getStruct();
242 
243     if (type.isMatrix())
244     {
245         // This method can also be called from structureString, which does not use layout
246         // qualifiers.
247         // Thus, use the method parameter for determining the matrix packing.
248         //
249         // Note HLSL row major packing corresponds to GL API column-major, and vice-versa, since we
250         // wish to always transpose GL matrices to play well with HLSL's matrix array indexing.
251         //
252         const bool isRowMajorMatrix = !useHLSLRowMajorPacking;
253         const GLenum glType         = GLVariableType(type);
254         numComponents               = gl::MatrixComponentCount(glType, isRowMajorMatrix);
255     }
256     else if (structure)
257     {
258         const TString &structName =
259             QualifiedStructNameString(*structure, useHLSLRowMajorPacking, true, false);
260         numComponents = mStructElementIndexes->find(structName)->second;
261 
262         if (numComponents == 0)
263         {
264             return "";
265         }
266     }
267     else
268     {
269         const GLenum glType = GLVariableType(type);
270         numComponents       = gl::VariableComponentCount(glType);
271     }
272 
273     TString padding;
274     for (int paddingOffset = numComponents; paddingOffset < 4; paddingOffset++)
275     {
276         padding += "    float pad_" + next() + ";\n";
277     }
278     return padding;
279 }
280 
StructureHLSL()281 StructureHLSL::StructureHLSL() : mUniquePaddingCounter(0) {}
282 
getPaddingHelper()283 Std140PaddingHelper StructureHLSL::getPaddingHelper()
284 {
285     return Std140PaddingHelper(mStd140StructElementIndexes, &mUniquePaddingCounter);
286 }
287 
defineQualified(const TStructure & structure,bool useHLSLRowMajorPacking,bool useStd140Packing,bool forcePadding)288 TString StructureHLSL::defineQualified(const TStructure &structure,
289                                        bool useHLSLRowMajorPacking,
290                                        bool useStd140Packing,
291                                        bool forcePadding)
292 {
293     if (useStd140Packing)
294     {
295         Std140PaddingHelper padHelper = getPaddingHelper();
296         return Define(structure, useHLSLRowMajorPacking, useStd140Packing, forcePadding,
297                       &padHelper);
298     }
299     else
300     {
301         return Define(structure, useHLSLRowMajorPacking, useStd140Packing, false, nullptr);
302     }
303 }
304 
defineNameless(const TStructure & structure)305 TString StructureHLSL::defineNameless(const TStructure &structure)
306 {
307     return Define(structure, false, false, false, nullptr);
308 }
309 
defineVariants(const TStructure & structure,const TString & name)310 StructureHLSL::DefinedStructs::iterator StructureHLSL::defineVariants(const TStructure &structure,
311                                                                       const TString &name)
312 {
313     ASSERT(mDefinedStructs.find(name) == mDefinedStructs.end());
314 
315     for (const TField *field : structure.fields())
316     {
317         const TType *fieldType = field->type();
318         if (fieldType->getBasicType() == EbtStruct)
319         {
320             ensureStructDefined(*fieldType->getStruct());
321         }
322     }
323 
324     DefinedStructs::iterator addedStruct =
325         mDefinedStructs.insert(std::make_pair(name, new TStructProperties())).first;
326     // Add element index
327     storeStd140ElementIndex(structure, false);
328     storeStd140ElementIndex(structure, true);
329 
330     const TString &structString = defineQualified(structure, false, false, false);
331 
332     ASSERT(std::find(mStructDeclarations.begin(), mStructDeclarations.end(), structString) ==
333            mStructDeclarations.end());
334     // Add row-major packed struct for interface blocks
335     TString rowMajorString = "#pragma pack_matrix(row_major)\n" +
336                              defineQualified(structure, true, false, false) +
337                              "#pragma pack_matrix(column_major)\n";
338 
339     TString std140String         = defineQualified(structure, false, true, false);
340     TString std140RowMajorString = "#pragma pack_matrix(row_major)\n" +
341                                    defineQualified(structure, true, true, false) +
342                                    "#pragma pack_matrix(column_major)\n";
343 
344     // Must force to pad the structure's elements for StructuredBuffer's element type, if qualifier
345     // of structure is std140.
346     TString std140ForcePaddingString         = defineQualified(structure, false, true, true);
347     TString std140RowMajorForcePaddingString = "#pragma pack_matrix(row_major)\n" +
348                                                defineQualified(structure, true, true, true) +
349                                                "#pragma pack_matrix(column_major)\n";
350 
351     mStructDeclarations.push_back(structString);
352     mStructDeclarations.push_back(rowMajorString);
353     mStructDeclarations.push_back(std140String);
354     mStructDeclarations.push_back(std140RowMajorString);
355     mStructDeclarations.push_back(std140ForcePaddingString);
356     mStructDeclarations.push_back(std140RowMajorForcePaddingString);
357     return addedStruct;
358 }
359 
ensureStructDefined(const TStructure & structure)360 void StructureHLSL::ensureStructDefined(const TStructure &structure)
361 {
362     const TString name = StructNameString(structure);
363     if (name == "")
364     {
365         return;  // Nameless structures are not defined
366     }
367     if (mDefinedStructs.find(name) == mDefinedStructs.end())
368     {
369         defineVariants(structure, name);
370     }
371 }
372 
addStructConstructor(const TStructure & structure)373 TString StructureHLSL::addStructConstructor(const TStructure &structure)
374 {
375     const TString name = StructNameString(structure);
376 
377     if (name == "")
378     {
379         return TString();  // Nameless structures don't have constructors
380     }
381 
382     auto definedStruct = mDefinedStructs.find(name);
383     if (definedStruct == mDefinedStructs.end())
384     {
385         definedStruct = defineVariants(structure, name);
386     }
387     const TString constructorFunctionName = TString(name) + "_ctor";
388     TString *constructor                  = &definedStruct->second->constructor;
389     if (!constructor->empty())
390     {
391         return constructorFunctionName;  // Already added
392     }
393     *constructor += name + " " + constructorFunctionName + "(";
394 
395     std::vector<TType> ctorParameters;
396     const TFieldList &fields = structure.fields();
397     for (const TField *field : fields)
398     {
399         const TType *fieldType = field->type();
400         if (!IsSampler(fieldType->getBasicType()))
401         {
402             ctorParameters.push_back(*fieldType);
403         }
404     }
405     // Structs that have sampler members should not have constructor calls, and otherwise structs
406     // are guaranteed to be non-empty by the grammar. Structs can't contain empty declarations
407     // either.
408     ASSERT(!ctorParameters.empty());
409 
410     *constructor += WriteParameterList(ctorParameters);
411 
412     *constructor +=
413         ")\n"
414         "{\n"
415         "    " +
416         name + " structure = { ";
417 
418     for (size_t parameterIndex = 0u; parameterIndex < ctorParameters.size(); ++parameterIndex)
419     {
420         *constructor += "x" + str(parameterIndex);
421         if (parameterIndex < ctorParameters.size() - 1u)
422         {
423             *constructor += ", ";
424         }
425     }
426     *constructor +=
427         "};\n"
428         "    return structure;\n"
429         "}\n";
430 
431     return constructorFunctionName;
432 }
433 
addBuiltInConstructor(const TType & type,const TIntermSequence * parameters)434 TString StructureHLSL::addBuiltInConstructor(const TType &type, const TIntermSequence *parameters)
435 {
436     ASSERT(!type.isArray());
437     ASSERT(type.getStruct() == nullptr);
438     ASSERT(parameters);
439 
440     TType ctorType = type;
441     ctorType.setPrecision(EbpHigh);
442     ctorType.setQualifier(EvqTemporary);
443 
444     const TString constructorFunctionName =
445         TString(type.getBuiltInTypeNameString()) + "_ctor" + DisambiguateFunctionName(parameters);
446     TString constructor = TypeString(ctorType) + " " + constructorFunctionName + "(";
447 
448     std::vector<TType> ctorParameters;
449     for (auto parameter : *parameters)
450     {
451         const TType &paramType = parameter->getAsTyped()->getType();
452         ASSERT(!paramType.isArray());
453         ctorParameters.push_back(paramType);
454     }
455     constructor += WriteParameterList(ctorParameters);
456 
457     constructor +=
458         ")\n"
459         "{\n"
460         "    return " +
461         TypeString(ctorType) + "(";
462 
463     if (ctorType.isMatrix() && ctorParameters.size() == 1)
464     {
465         int rows               = ctorType.getRows();
466         int cols               = ctorType.getCols();
467         const TType &parameter = ctorParameters[0];
468 
469         if (parameter.isScalar())
470         {
471             for (int col = 0; col < cols; col++)
472             {
473                 for (int row = 0; row < rows; row++)
474                 {
475                     constructor += TString((row == col) ? "x0" : "0.0");
476 
477                     if (row < rows - 1 || col < cols - 1)
478                     {
479                         constructor += ", ";
480                     }
481                 }
482             }
483         }
484         else if (parameter.isMatrix())
485         {
486             for (int col = 0; col < cols; col++)
487             {
488                 for (int row = 0; row < rows; row++)
489                 {
490                     if (row < parameter.getRows() && col < parameter.getCols())
491                     {
492                         constructor += TString("x0") + "[" + str(col) + "][" + str(row) + "]";
493                     }
494                     else
495                     {
496                         constructor += TString((row == col) ? "1.0" : "0.0");
497                     }
498 
499                     if (row < rows - 1 || col < cols - 1)
500                     {
501                         constructor += ", ";
502                     }
503                 }
504             }
505         }
506         else
507         {
508             ASSERT(rows == 2 && cols == 2 && parameter.isVector() &&
509                    parameter.getNominalSize() == 4);
510 
511             constructor += "x0";
512         }
513     }
514     else
515     {
516         size_t remainingComponents = ctorType.getObjectSize();
517         size_t parameterIndex      = 0;
518 
519         while (remainingComponents > 0)
520         {
521             const TType &parameter     = ctorParameters[parameterIndex];
522             const size_t parameterSize = parameter.getObjectSize();
523             bool moreParameters        = parameterIndex + 1 < ctorParameters.size();
524 
525             constructor += "x" + str(parameterIndex);
526 
527             if (parameter.isScalar())
528             {
529                 remainingComponents -= parameter.getObjectSize();
530             }
531             else if (parameter.isVector())
532             {
533                 if (remainingComponents == parameterSize || moreParameters)
534                 {
535                     ASSERT(parameterSize <= remainingComponents);
536                     remainingComponents -= parameterSize;
537                 }
538                 else if (remainingComponents < static_cast<size_t>(parameter.getNominalSize()))
539                 {
540                     switch (remainingComponents)
541                     {
542                         case 1:
543                             constructor += ".x";
544                             break;
545                         case 2:
546                             constructor += ".xy";
547                             break;
548                         case 3:
549                             constructor += ".xyz";
550                             break;
551                         case 4:
552                             constructor += ".xyzw";
553                             break;
554                         default:
555                             UNREACHABLE();
556                     }
557 
558                     remainingComponents = 0;
559                 }
560                 else
561                     UNREACHABLE();
562             }
563             else if (parameter.isMatrix())
564             {
565                 int column = 0;
566                 while (remainingComponents > 0 && column < parameter.getCols())
567                 {
568                     constructor += "[" + str(column) + "]";
569 
570                     if (remainingComponents < static_cast<size_t>(parameter.getRows()))
571                     {
572                         switch (remainingComponents)
573                         {
574                             case 1:
575                                 constructor += ".x";
576                                 break;
577                             case 2:
578                                 constructor += ".xy";
579                                 break;
580                             case 3:
581                                 constructor += ".xyz";
582                                 break;
583                             default:
584                                 UNREACHABLE();
585                         }
586 
587                         remainingComponents = 0;
588                     }
589                     else
590                     {
591                         remainingComponents -= parameter.getRows();
592 
593                         if (remainingComponents > 0)
594                         {
595                             constructor += ", x" + str(parameterIndex);
596                         }
597                     }
598 
599                     column++;
600                 }
601             }
602             else
603             {
604                 UNREACHABLE();
605             }
606 
607             if (moreParameters)
608             {
609                 parameterIndex++;
610             }
611 
612             if (remainingComponents)
613             {
614                 constructor += ", ";
615             }
616         }
617     }
618 
619     constructor +=
620         ");\n"
621         "}\n";
622 
623     mBuiltInConstructors.insert(constructor);
624 
625     return constructorFunctionName;
626 }
627 
structsHeader() const628 std::string StructureHLSL::structsHeader() const
629 {
630     TInfoSinkBase out;
631 
632     for (auto &declaration : mStructDeclarations)
633     {
634         out << declaration;
635     }
636 
637     for (auto &structure : mDefinedStructs)
638     {
639         out << structure.second->constructor;
640     }
641 
642     for (auto &constructor : mBuiltInConstructors)
643     {
644         out << constructor;
645     }
646 
647     return out.str();
648 }
649 
storeStd140ElementIndex(const TStructure & structure,bool useHLSLRowMajorPacking)650 void StructureHLSL::storeStd140ElementIndex(const TStructure &structure,
651                                             bool useHLSLRowMajorPacking)
652 {
653     Std140PaddingHelper padHelper = getPaddingHelper();
654     const TFieldList &fields      = structure.fields();
655 
656     for (const TField *field : fields)
657     {
658         padHelper.prePadding(*field->type(), false);
659     }
660 
661     // Add remaining element index to the global map, for use with nested structs in standard
662     // layouts
663     const TString &structName =
664         QualifiedStructNameString(structure, useHLSLRowMajorPacking, true, false);
665     mStd140StructElementIndexes[structName] = padHelper.elementIndex();
666 }
667 
668 }  // namespace sh
669