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