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