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/hlsl/StructureHLSL.h"
11 #include "common/utilities.h"
12 #include "compiler/translator/Types.h"
13 #include "compiler/translator/hlsl/OutputHLSL.h"
14 #include "compiler/translator/hlsl/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> ¶meters)
78 {
79 TString parameterList;
80 for (size_t parameter = 0u; parameter < parameters.size(); parameter++)
81 {
82 const TType ¶mType = 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 ¶mType = 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 uint8_t rows = ctorType.getRows();
466 uint8_t cols = ctorType.getCols();
467 const TType ¶meter = ctorParameters[0];
468
469 if (parameter.isScalar())
470 {
471 for (uint8_t col = 0; col < cols; col++)
472 {
473 for (uint8_t 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 (uint8_t col = 0; col < cols; col++)
487 {
488 for (uint8_t 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 ¶meter = 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 uint8_t 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