StructureHLSL.cpp (22089B)
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 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 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 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 103 Std140PaddingHelper::Std140PaddingHelper(const std::map<TString, int> &structElementIndexes, 104 unsigned *uniqueCounter) 105 : mPaddingCounter(uniqueCounter), mElementIndex(0), mStructElementIndexes(&structElementIndexes) 106 {} 107 108 Std140PaddingHelper::Std140PaddingHelper(const Std140PaddingHelper &other) 109 : mPaddingCounter(other.mPaddingCounter), 110 mElementIndex(other.mElementIndex), 111 mStructElementIndexes(other.mStructElementIndexes) 112 {} 113 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 122 TString Std140PaddingHelper::next() 123 { 124 unsigned value = (*mPaddingCounter)++; 125 return str(value); 126 } 127 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 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 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 281 StructureHLSL::StructureHLSL() : mUniquePaddingCounter(0) {} 282 283 Std140PaddingHelper StructureHLSL::getPaddingHelper() 284 { 285 return Std140PaddingHelper(mStd140StructElementIndexes, &mUniquePaddingCounter); 286 } 287 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 305 TString StructureHLSL::defineNameless(const TStructure &structure) 306 { 307 return Define(structure, false, false, false, nullptr); 308 } 309 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 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 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 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 628 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 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