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