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> ¶meters)
77 {
78 TString parameterList;
79 for (size_t parameter = 0u; parameter < parameters.size(); parameter++)
80 {
81 const TType ¶mType = 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 ¶mType = 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 ¶meter = 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 ¶meter = 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