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> ¶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 += 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 ¶mType = 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 ¶meter = 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 ¶meter = 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