OutputHLSL.cpp (122598B)
1 // 2 // Copyright 2002 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 7 #include "compiler/translator/OutputHLSL.h" 8 9 #include <stdio.h> 10 #include <algorithm> 11 #include <cfloat> 12 13 #include "common/angleutils.h" 14 #include "common/debug.h" 15 #include "common/utilities.h" 16 #include "compiler/translator/AtomicCounterFunctionHLSL.h" 17 #include "compiler/translator/BuiltInFunctionEmulator.h" 18 #include "compiler/translator/BuiltInFunctionEmulatorHLSL.h" 19 #include "compiler/translator/ImageFunctionHLSL.h" 20 #include "compiler/translator/InfoSink.h" 21 #include "compiler/translator/ResourcesHLSL.h" 22 #include "compiler/translator/StructureHLSL.h" 23 #include "compiler/translator/TextureFunctionHLSL.h" 24 #include "compiler/translator/TranslatorHLSL.h" 25 #include "compiler/translator/UtilsHLSL.h" 26 #include "compiler/translator/blocklayout.h" 27 #include "compiler/translator/tree_ops/d3d/RemoveSwitchFallThrough.h" 28 #include "compiler/translator/tree_util/FindSymbolNode.h" 29 #include "compiler/translator/tree_util/NodeSearch.h" 30 #include "compiler/translator/util.h" 31 32 namespace sh 33 { 34 35 namespace 36 { 37 38 constexpr const char kImage2DFunctionString[] = "// @@ IMAGE2D DECLARATION FUNCTION STRING @@"; 39 40 TString ArrayHelperFunctionName(const char *prefix, const TType &type) 41 { 42 TStringStream fnName = sh::InitializeStream<TStringStream>(); 43 fnName << prefix << "_"; 44 if (type.isArray()) 45 { 46 for (unsigned int arraySize : type.getArraySizes()) 47 { 48 fnName << arraySize << "_"; 49 } 50 } 51 fnName << TypeString(type); 52 return fnName.str(); 53 } 54 55 bool IsDeclarationWrittenOut(TIntermDeclaration *node) 56 { 57 TIntermSequence *sequence = node->getSequence(); 58 TIntermTyped *variable = (*sequence)[0]->getAsTyped(); 59 ASSERT(sequence->size() == 1); 60 ASSERT(variable); 61 return (variable->getQualifier() == EvqTemporary || variable->getQualifier() == EvqGlobal || 62 variable->getQualifier() == EvqConst || variable->getQualifier() == EvqShared); 63 } 64 65 bool IsInStd140UniformBlock(TIntermTyped *node) 66 { 67 TIntermBinary *binaryNode = node->getAsBinaryNode(); 68 69 if (binaryNode) 70 { 71 return IsInStd140UniformBlock(binaryNode->getLeft()); 72 } 73 74 const TType &type = node->getType(); 75 76 if (type.getQualifier() == EvqUniform) 77 { 78 // determine if we are in the standard layout 79 const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock(); 80 if (interfaceBlock) 81 { 82 return (interfaceBlock->blockStorage() == EbsStd140); 83 } 84 } 85 86 return false; 87 } 88 89 const TInterfaceBlock *GetInterfaceBlockOfUniformBlockNearestIndexOperator(TIntermTyped *node) 90 { 91 const TIntermBinary *binaryNode = node->getAsBinaryNode(); 92 if (binaryNode) 93 { 94 if (binaryNode->getOp() == EOpIndexDirectInterfaceBlock) 95 { 96 return binaryNode->getLeft()->getType().getInterfaceBlock(); 97 } 98 } 99 100 const TIntermSymbol *symbolNode = node->getAsSymbolNode(); 101 if (symbolNode) 102 { 103 const TVariable &variable = symbolNode->variable(); 104 const TType &variableType = variable.getType(); 105 106 if (variableType.getQualifier() == EvqUniform && 107 variable.symbolType() == SymbolType::UserDefined) 108 { 109 return variableType.getInterfaceBlock(); 110 } 111 } 112 113 return nullptr; 114 } 115 116 const char *GetHLSLAtomicFunctionStringAndLeftParenthesis(TOperator op) 117 { 118 switch (op) 119 { 120 case EOpAtomicAdd: 121 return "InterlockedAdd("; 122 case EOpAtomicMin: 123 return "InterlockedMin("; 124 case EOpAtomicMax: 125 return "InterlockedMax("; 126 case EOpAtomicAnd: 127 return "InterlockedAnd("; 128 case EOpAtomicOr: 129 return "InterlockedOr("; 130 case EOpAtomicXor: 131 return "InterlockedXor("; 132 case EOpAtomicExchange: 133 return "InterlockedExchange("; 134 case EOpAtomicCompSwap: 135 return "InterlockedCompareExchange("; 136 default: 137 UNREACHABLE(); 138 return ""; 139 } 140 } 141 142 bool IsAtomicFunctionForSharedVariableDirectAssign(const TIntermBinary &node) 143 { 144 TIntermAggregate *aggregateNode = node.getRight()->getAsAggregate(); 145 if (aggregateNode == nullptr) 146 { 147 return false; 148 } 149 150 if (node.getOp() == EOpAssign && BuiltInGroup::IsAtomicMemory(aggregateNode->getOp())) 151 { 152 return !IsInShaderStorageBlock((*aggregateNode->getSequence())[0]->getAsTyped()); 153 } 154 155 return false; 156 } 157 158 const char *kZeros = "_ANGLE_ZEROS_"; 159 constexpr int kZeroCount = 256; 160 std::string DefineZeroArray() 161 { 162 std::stringstream ss = sh::InitializeStream<std::stringstream>(); 163 // For 'static', if the declaration does not include an initializer, the value is set to zero. 164 // https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/dx-graphics-hlsl-variable-syntax 165 ss << "static uint " << kZeros << "[" << kZeroCount << "];\n"; 166 return ss.str(); 167 } 168 169 std::string GetZeroInitializer(size_t size) 170 { 171 std::stringstream ss = sh::InitializeStream<std::stringstream>(); 172 size_t quotient = size / kZeroCount; 173 size_t reminder = size % kZeroCount; 174 175 for (size_t i = 0; i < quotient; ++i) 176 { 177 if (i != 0) 178 { 179 ss << ", "; 180 } 181 ss << kZeros; 182 } 183 184 for (size_t i = 0; i < reminder; ++i) 185 { 186 if (quotient != 0 || i != 0) 187 { 188 ss << ", "; 189 } 190 ss << "0"; 191 } 192 193 return ss.str(); 194 } 195 196 } // anonymous namespace 197 198 TReferencedBlock::TReferencedBlock(const TInterfaceBlock *aBlock, 199 const TVariable *aInstanceVariable) 200 : block(aBlock), instanceVariable(aInstanceVariable) 201 {} 202 203 bool OutputHLSL::needStructMapping(TIntermTyped *node) 204 { 205 ASSERT(node->getBasicType() == EbtStruct); 206 for (unsigned int n = 0u; getAncestorNode(n) != nullptr; ++n) 207 { 208 TIntermNode *ancestor = getAncestorNode(n); 209 const TIntermBinary *ancestorBinary = ancestor->getAsBinaryNode(); 210 if (ancestorBinary) 211 { 212 switch (ancestorBinary->getOp()) 213 { 214 case EOpIndexDirectStruct: 215 { 216 const TStructure *structure = ancestorBinary->getLeft()->getType().getStruct(); 217 const TIntermConstantUnion *index = 218 ancestorBinary->getRight()->getAsConstantUnion(); 219 const TField *field = structure->fields()[index->getIConst(0)]; 220 if (field->type()->getStruct() == nullptr) 221 { 222 return false; 223 } 224 break; 225 } 226 case EOpIndexDirect: 227 case EOpIndexIndirect: 228 break; 229 default: 230 return true; 231 } 232 } 233 else 234 { 235 const TIntermAggregate *ancestorAggregate = ancestor->getAsAggregate(); 236 if (ancestorAggregate) 237 { 238 return true; 239 } 240 return false; 241 } 242 } 243 return true; 244 } 245 246 void OutputHLSL::writeFloat(TInfoSinkBase &out, float f) 247 { 248 // This is known not to work for NaN on all drivers but make the best effort to output NaNs 249 // regardless. 250 if ((gl::isInf(f) || gl::isNaN(f)) && mShaderVersion >= 300 && 251 mOutputType == SH_HLSL_4_1_OUTPUT) 252 { 253 out << "asfloat(" << gl::bitCast<uint32_t>(f) << "u)"; 254 } 255 else 256 { 257 out << std::min(FLT_MAX, std::max(-FLT_MAX, f)); 258 } 259 } 260 261 void OutputHLSL::writeSingleConstant(TInfoSinkBase &out, const TConstantUnion *const constUnion) 262 { 263 ASSERT(constUnion != nullptr); 264 switch (constUnion->getType()) 265 { 266 case EbtFloat: 267 writeFloat(out, constUnion->getFConst()); 268 break; 269 case EbtInt: 270 out << constUnion->getIConst(); 271 break; 272 case EbtUInt: 273 out << constUnion->getUConst(); 274 break; 275 case EbtBool: 276 out << constUnion->getBConst(); 277 break; 278 default: 279 UNREACHABLE(); 280 } 281 } 282 283 const TConstantUnion *OutputHLSL::writeConstantUnionArray(TInfoSinkBase &out, 284 const TConstantUnion *const constUnion, 285 const size_t size) 286 { 287 const TConstantUnion *constUnionIterated = constUnion; 288 for (size_t i = 0; i < size; i++, constUnionIterated++) 289 { 290 writeSingleConstant(out, constUnionIterated); 291 292 if (i != size - 1) 293 { 294 out << ", "; 295 } 296 } 297 return constUnionIterated; 298 } 299 300 OutputHLSL::OutputHLSL(sh::GLenum shaderType, 301 ShShaderSpec shaderSpec, 302 int shaderVersion, 303 const TExtensionBehavior &extensionBehavior, 304 const char *sourcePath, 305 ShShaderOutput outputType, 306 int numRenderTargets, 307 int maxDualSourceDrawBuffers, 308 const std::vector<ShaderVariable> &uniforms, 309 const ShCompileOptions &compileOptions, 310 sh::WorkGroupSize workGroupSize, 311 TSymbolTable *symbolTable, 312 PerformanceDiagnostics *perfDiagnostics, 313 const std::map<int, const TInterfaceBlock *> &uniformBlockOptimizedMap, 314 const std::vector<InterfaceBlock> &shaderStorageBlocks, 315 bool isEarlyFragmentTestsSpecified) 316 : TIntermTraverser(true, true, true, symbolTable), 317 mShaderType(shaderType), 318 mShaderSpec(shaderSpec), 319 mShaderVersion(shaderVersion), 320 mExtensionBehavior(extensionBehavior), 321 mSourcePath(sourcePath), 322 mOutputType(outputType), 323 mCompileOptions(compileOptions), 324 mInsideFunction(false), 325 mInsideMain(false), 326 mUniformBlockOptimizedMap(uniformBlockOptimizedMap), 327 mNumRenderTargets(numRenderTargets), 328 mMaxDualSourceDrawBuffers(maxDualSourceDrawBuffers), 329 mCurrentFunctionMetadata(nullptr), 330 mWorkGroupSize(workGroupSize), 331 mPerfDiagnostics(perfDiagnostics), 332 mIsEarlyFragmentTestsSpecified(isEarlyFragmentTestsSpecified), 333 mNeedStructMapping(false) 334 { 335 mUsesFragColor = false; 336 mUsesFragData = false; 337 mUsesDepthRange = false; 338 mUsesFragCoord = false; 339 mUsesPointCoord = false; 340 mUsesFrontFacing = false; 341 mUsesHelperInvocation = false; 342 mUsesPointSize = false; 343 mUsesInstanceID = false; 344 mHasMultiviewExtensionEnabled = 345 IsExtensionEnabled(mExtensionBehavior, TExtension::OVR_multiview) || 346 IsExtensionEnabled(mExtensionBehavior, TExtension::OVR_multiview2); 347 mUsesViewID = false; 348 mUsesVertexID = false; 349 mUsesFragDepth = false; 350 mUsesNumWorkGroups = false; 351 mUsesWorkGroupID = false; 352 mUsesLocalInvocationID = false; 353 mUsesGlobalInvocationID = false; 354 mUsesLocalInvocationIndex = false; 355 mUsesXor = false; 356 mUsesDiscardRewriting = false; 357 mUsesNestedBreak = false; 358 mRequiresIEEEStrictCompiling = false; 359 mUseZeroArray = false; 360 mUsesSecondaryColor = false; 361 362 mUniqueIndex = 0; 363 364 mOutputLod0Function = false; 365 mInsideDiscontinuousLoop = false; 366 mNestedLoopDepth = 0; 367 368 mExcessiveLoopIndex = nullptr; 369 370 mStructureHLSL = new StructureHLSL; 371 mTextureFunctionHLSL = new TextureFunctionHLSL; 372 mImageFunctionHLSL = new ImageFunctionHLSL; 373 mAtomicCounterFunctionHLSL = 374 new AtomicCounterFunctionHLSL(compileOptions.forceAtomicValueResolution); 375 376 unsigned int firstUniformRegister = compileOptions.skipD3DConstantRegisterZero ? 1u : 0u; 377 mResourcesHLSL = new ResourcesHLSL(mStructureHLSL, outputType, uniforms, firstUniformRegister); 378 379 if (mOutputType == SH_HLSL_3_0_OUTPUT) 380 { 381 // Fragment shaders need dx_DepthRange, dx_ViewCoords, dx_DepthFront, 382 // and dx_FragCoordOffset. 383 // Vertex shaders need a slightly different set: dx_DepthRange, dx_ViewCoords and 384 // dx_ViewAdjust. 385 if (mShaderType == GL_VERTEX_SHADER) 386 { 387 mResourcesHLSL->reserveUniformRegisters(3); 388 } 389 else 390 { 391 mResourcesHLSL->reserveUniformRegisters(4); 392 } 393 } 394 395 // Reserve registers for the default uniform block and driver constants 396 mResourcesHLSL->reserveUniformBlockRegisters(2); 397 398 mSSBOOutputHLSL = new ShaderStorageBlockOutputHLSL(this, mResourcesHLSL, shaderStorageBlocks); 399 } 400 401 OutputHLSL::~OutputHLSL() 402 { 403 SafeDelete(mSSBOOutputHLSL); 404 SafeDelete(mStructureHLSL); 405 SafeDelete(mResourcesHLSL); 406 SafeDelete(mTextureFunctionHLSL); 407 SafeDelete(mImageFunctionHLSL); 408 SafeDelete(mAtomicCounterFunctionHLSL); 409 for (auto &eqFunction : mStructEqualityFunctions) 410 { 411 SafeDelete(eqFunction); 412 } 413 for (auto &eqFunction : mArrayEqualityFunctions) 414 { 415 SafeDelete(eqFunction); 416 } 417 } 418 419 void OutputHLSL::output(TIntermNode *treeRoot, TInfoSinkBase &objSink) 420 { 421 BuiltInFunctionEmulator builtInFunctionEmulator; 422 InitBuiltInFunctionEmulatorForHLSL(&builtInFunctionEmulator); 423 if (mCompileOptions.emulateIsnanFloatFunction) 424 { 425 InitBuiltInIsnanFunctionEmulatorForHLSLWorkarounds(&builtInFunctionEmulator, 426 mShaderVersion); 427 } 428 429 builtInFunctionEmulator.markBuiltInFunctionsForEmulation(treeRoot); 430 431 // Now that we are done changing the AST, do the analyses need for HLSL generation 432 CallDAG::InitResult success = mCallDag.init(treeRoot, nullptr); 433 ASSERT(success == CallDAG::INITDAG_SUCCESS); 434 mASTMetadataList = CreateASTMetadataHLSL(treeRoot, mCallDag); 435 436 const std::vector<MappedStruct> std140Structs = FlagStd140Structs(treeRoot); 437 // TODO(oetuaho): The std140Structs could be filtered based on which ones actually get used in 438 // the shader code. When we add shader storage blocks we might also consider an alternative 439 // solution, since the struct mapping won't work very well for shader storage blocks. 440 441 // Output the body and footer first to determine what has to go in the header 442 mInfoSinkStack.push(&mBody); 443 treeRoot->traverse(this); 444 mInfoSinkStack.pop(); 445 446 mInfoSinkStack.push(&mFooter); 447 mInfoSinkStack.pop(); 448 449 mInfoSinkStack.push(&mHeader); 450 header(mHeader, std140Structs, &builtInFunctionEmulator); 451 mInfoSinkStack.pop(); 452 453 objSink << mHeader.c_str(); 454 objSink << mBody.c_str(); 455 objSink << mFooter.c_str(); 456 457 builtInFunctionEmulator.cleanup(); 458 } 459 460 const std::map<std::string, unsigned int> &OutputHLSL::getShaderStorageBlockRegisterMap() const 461 { 462 return mResourcesHLSL->getShaderStorageBlockRegisterMap(); 463 } 464 465 const std::map<std::string, unsigned int> &OutputHLSL::getUniformBlockRegisterMap() const 466 { 467 return mResourcesHLSL->getUniformBlockRegisterMap(); 468 } 469 470 const std::map<std::string, bool> &OutputHLSL::getUniformBlockUseStructuredBufferMap() const 471 { 472 return mResourcesHLSL->getUniformBlockUseStructuredBufferMap(); 473 } 474 475 const std::map<std::string, unsigned int> &OutputHLSL::getUniformRegisterMap() const 476 { 477 return mResourcesHLSL->getUniformRegisterMap(); 478 } 479 480 unsigned int OutputHLSL::getReadonlyImage2DRegisterIndex() const 481 { 482 return mResourcesHLSL->getReadonlyImage2DRegisterIndex(); 483 } 484 485 unsigned int OutputHLSL::getImage2DRegisterIndex() const 486 { 487 return mResourcesHLSL->getImage2DRegisterIndex(); 488 } 489 490 const std::set<std::string> &OutputHLSL::getUsedImage2DFunctionNames() const 491 { 492 return mImageFunctionHLSL->getUsedImage2DFunctionNames(); 493 } 494 495 TString OutputHLSL::structInitializerString(int indent, 496 const TType &type, 497 const TString &name) const 498 { 499 TString init; 500 501 TString indentString; 502 for (int spaces = 0; spaces < indent; spaces++) 503 { 504 indentString += " "; 505 } 506 507 if (type.isArray()) 508 { 509 init += indentString + "{\n"; 510 for (unsigned int arrayIndex = 0u; arrayIndex < type.getOutermostArraySize(); ++arrayIndex) 511 { 512 TStringStream indexedString = sh::InitializeStream<TStringStream>(); 513 indexedString << name << "[" << arrayIndex << "]"; 514 TType elementType = type; 515 elementType.toArrayElementType(); 516 init += structInitializerString(indent + 1, elementType, indexedString.str()); 517 if (arrayIndex < type.getOutermostArraySize() - 1) 518 { 519 init += ","; 520 } 521 init += "\n"; 522 } 523 init += indentString + "}"; 524 } 525 else if (type.getBasicType() == EbtStruct) 526 { 527 init += indentString + "{\n"; 528 const TStructure &structure = *type.getStruct(); 529 const TFieldList &fields = structure.fields(); 530 for (unsigned int fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++) 531 { 532 const TField &field = *fields[fieldIndex]; 533 const TString &fieldName = name + "." + Decorate(field.name()); 534 const TType &fieldType = *field.type(); 535 536 init += structInitializerString(indent + 1, fieldType, fieldName); 537 if (fieldIndex < fields.size() - 1) 538 { 539 init += ","; 540 } 541 init += "\n"; 542 } 543 init += indentString + "}"; 544 } 545 else 546 { 547 init += indentString + name; 548 } 549 550 return init; 551 } 552 553 TString OutputHLSL::generateStructMapping(const std::vector<MappedStruct> &std140Structs) const 554 { 555 TString mappedStructs; 556 557 for (auto &mappedStruct : std140Structs) 558 { 559 const TInterfaceBlock *interfaceBlock = 560 mappedStruct.blockDeclarator->getType().getInterfaceBlock(); 561 TQualifier qualifier = mappedStruct.blockDeclarator->getType().getQualifier(); 562 switch (qualifier) 563 { 564 case EvqUniform: 565 if (mReferencedUniformBlocks.count(interfaceBlock->uniqueId().get()) == 0) 566 { 567 continue; 568 } 569 break; 570 case EvqBuffer: 571 continue; 572 default: 573 UNREACHABLE(); 574 return mappedStructs; 575 } 576 577 unsigned int instanceCount = 1u; 578 bool isInstanceArray = mappedStruct.blockDeclarator->isArray(); 579 if (isInstanceArray) 580 { 581 instanceCount = mappedStruct.blockDeclarator->getOutermostArraySize(); 582 } 583 584 for (unsigned int instanceArrayIndex = 0; instanceArrayIndex < instanceCount; 585 ++instanceArrayIndex) 586 { 587 TString originalName; 588 TString mappedName("map"); 589 590 if (mappedStruct.blockDeclarator->variable().symbolType() != SymbolType::Empty) 591 { 592 const ImmutableString &instanceName = 593 mappedStruct.blockDeclarator->variable().name(); 594 unsigned int instanceStringArrayIndex = GL_INVALID_INDEX; 595 if (isInstanceArray) 596 instanceStringArrayIndex = instanceArrayIndex; 597 TString instanceString = mResourcesHLSL->InterfaceBlockInstanceString( 598 instanceName, instanceStringArrayIndex); 599 originalName += instanceString; 600 mappedName += instanceString; 601 originalName += "."; 602 mappedName += "_"; 603 } 604 605 TString fieldName = Decorate(mappedStruct.field->name()); 606 originalName += fieldName; 607 mappedName += fieldName; 608 609 TType *structType = mappedStruct.field->type(); 610 mappedStructs += 611 "static " + Decorate(structType->getStruct()->name()) + " " + mappedName; 612 613 if (structType->isArray()) 614 { 615 mappedStructs += ArrayString(*mappedStruct.field->type()).data(); 616 } 617 618 mappedStructs += " =\n"; 619 mappedStructs += structInitializerString(0, *structType, originalName); 620 mappedStructs += ";\n"; 621 } 622 } 623 return mappedStructs; 624 } 625 626 void OutputHLSL::writeReferencedAttributes(TInfoSinkBase &out) const 627 { 628 for (const auto &attribute : mReferencedAttributes) 629 { 630 const TType &type = attribute.second->getType(); 631 const ImmutableString &name = attribute.second->name(); 632 633 out << "static " << TypeString(type) << " " << Decorate(name) << ArrayString(type) << " = " 634 << zeroInitializer(type) << ";\n"; 635 } 636 } 637 638 void OutputHLSL::writeReferencedVaryings(TInfoSinkBase &out) const 639 { 640 for (const auto &varying : mReferencedVaryings) 641 { 642 const TType &type = varying.second->getType(); 643 644 // Program linking depends on this exact format 645 out << "static " << InterpolationString(type.getQualifier()) << " " << TypeString(type) 646 << " " << DecorateVariableIfNeeded(*varying.second) << ArrayString(type) << " = " 647 << zeroInitializer(type) << ";\n"; 648 } 649 } 650 651 void OutputHLSL::header(TInfoSinkBase &out, 652 const std::vector<MappedStruct> &std140Structs, 653 const BuiltInFunctionEmulator *builtInFunctionEmulator) const 654 { 655 TString mappedStructs; 656 if (mNeedStructMapping) 657 { 658 mappedStructs = generateStructMapping(std140Structs); 659 } 660 661 // Suppress some common warnings: 662 // 3556 : Integer divides might be much slower, try using uints if possible. 663 // 3571 : The pow(f, e) intrinsic function won't work for negative f, use abs(f) or 664 // conditionally handle negative values if you expect them. 665 out << "#pragma warning( disable: 3556 3571 )\n"; 666 667 out << mStructureHLSL->structsHeader(); 668 669 mResourcesHLSL->uniformsHeader(out, mOutputType, mReferencedUniforms, mSymbolTable); 670 out << mResourcesHLSL->uniformBlocksHeader(mReferencedUniformBlocks, mUniformBlockOptimizedMap); 671 mSSBOOutputHLSL->writeShaderStorageBlocksHeader(mShaderType, out); 672 673 if (!mEqualityFunctions.empty()) 674 { 675 out << "\n// Equality functions\n\n"; 676 for (const auto &eqFunction : mEqualityFunctions) 677 { 678 out << eqFunction->functionDefinition << "\n"; 679 } 680 } 681 if (!mArrayAssignmentFunctions.empty()) 682 { 683 out << "\n// Assignment functions\n\n"; 684 for (const auto &assignmentFunction : mArrayAssignmentFunctions) 685 { 686 out << assignmentFunction.functionDefinition << "\n"; 687 } 688 } 689 if (!mArrayConstructIntoFunctions.empty()) 690 { 691 out << "\n// Array constructor functions\n\n"; 692 for (const auto &constructIntoFunction : mArrayConstructIntoFunctions) 693 { 694 out << constructIntoFunction.functionDefinition << "\n"; 695 } 696 } 697 698 if (mUsesDiscardRewriting) 699 { 700 out << "#define ANGLE_USES_DISCARD_REWRITING\n"; 701 } 702 703 if (mUsesNestedBreak) 704 { 705 out << "#define ANGLE_USES_NESTED_BREAK\n"; 706 } 707 708 if (mRequiresIEEEStrictCompiling) 709 { 710 out << "#define ANGLE_REQUIRES_IEEE_STRICT_COMPILING\n"; 711 } 712 713 out << "#ifdef ANGLE_ENABLE_LOOP_FLATTEN\n" 714 "#define LOOP [loop]\n" 715 "#define FLATTEN [flatten]\n" 716 "#else\n" 717 "#define LOOP\n" 718 "#define FLATTEN\n" 719 "#endif\n"; 720 721 // array stride for atomic counter buffers is always 4 per original extension 722 // ARB_shader_atomic_counters and discussion on 723 // https://github.com/KhronosGroup/OpenGL-API/issues/5 724 out << "\n#define ATOMIC_COUNTER_ARRAY_STRIDE 4\n\n"; 725 726 if (mUseZeroArray) 727 { 728 out << DefineZeroArray() << "\n"; 729 } 730 731 if (mShaderType == GL_FRAGMENT_SHADER) 732 { 733 const bool usingMRTExtension = 734 IsExtensionEnabled(mExtensionBehavior, TExtension::EXT_draw_buffers); 735 const bool usingBFEExtension = 736 IsExtensionEnabled(mExtensionBehavior, TExtension::EXT_blend_func_extended); 737 738 out << "// Varyings\n"; 739 writeReferencedVaryings(out); 740 out << "\n"; 741 742 if ((IsDesktopGLSpec(mShaderSpec) && mShaderVersion >= 130) || 743 (!IsDesktopGLSpec(mShaderSpec) && mShaderVersion >= 300)) 744 { 745 for (const auto &outputVariable : mReferencedOutputVariables) 746 { 747 const ImmutableString &variableName = outputVariable.second->name(); 748 const TType &variableType = outputVariable.second->getType(); 749 750 out << "static " << TypeString(variableType) << " out_" << variableName 751 << ArrayString(variableType) << " = " << zeroInitializer(variableType) << ";\n"; 752 } 753 } 754 else 755 { 756 const unsigned int numColorValues = usingMRTExtension ? mNumRenderTargets : 1; 757 758 out << "static float4 gl_Color[" << numColorValues 759 << "] =\n" 760 "{\n"; 761 for (unsigned int i = 0; i < numColorValues; i++) 762 { 763 out << " float4(0, 0, 0, 0)"; 764 if (i + 1 != numColorValues) 765 { 766 out << ","; 767 } 768 out << "\n"; 769 } 770 771 out << "};\n"; 772 773 if (usingBFEExtension && mUsesSecondaryColor) 774 { 775 out << "static float4 gl_SecondaryColor[" << mMaxDualSourceDrawBuffers 776 << "] = \n" 777 "{\n"; 778 for (int i = 0; i < mMaxDualSourceDrawBuffers; i++) 779 { 780 out << " float4(0, 0, 0, 0)"; 781 if (i + 1 != mMaxDualSourceDrawBuffers) 782 { 783 out << ","; 784 } 785 out << "\n"; 786 } 787 out << "};\n"; 788 } 789 } 790 791 if (mUsesFragDepth) 792 { 793 out << "static float gl_Depth = 0.0;\n"; 794 } 795 796 if (mUsesFragCoord) 797 { 798 out << "static float4 gl_FragCoord = float4(0, 0, 0, 0);\n"; 799 } 800 801 if (mUsesPointCoord) 802 { 803 out << "static float2 gl_PointCoord = float2(0.5, 0.5);\n"; 804 } 805 806 if (mUsesFrontFacing) 807 { 808 out << "static bool gl_FrontFacing = false;\n"; 809 } 810 811 if (mUsesHelperInvocation) 812 { 813 out << "static bool gl_HelperInvocation = false;\n"; 814 } 815 816 out << "\n"; 817 818 if (mUsesDepthRange) 819 { 820 out << "struct gl_DepthRangeParameters\n" 821 "{\n" 822 " float near;\n" 823 " float far;\n" 824 " float diff;\n" 825 "};\n" 826 "\n"; 827 } 828 829 if (mOutputType == SH_HLSL_4_1_OUTPUT || mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT) 830 { 831 out << "cbuffer DriverConstants : register(b1)\n" 832 "{\n"; 833 834 if (mUsesDepthRange) 835 { 836 out << " float3 dx_DepthRange : packoffset(c0);\n"; 837 } 838 839 if (mUsesFragCoord) 840 { 841 out << " float4 dx_ViewCoords : packoffset(c1);\n"; 842 out << " float2 dx_FragCoordOffset : packoffset(c3);\n"; 843 } 844 845 if (mUsesFragCoord || mUsesFrontFacing) 846 { 847 out << " float3 dx_DepthFront : packoffset(c2);\n"; 848 } 849 850 if (mUsesFragCoord) 851 { 852 // dx_ViewScale is only used in the fragment shader to correct 853 // the value for glFragCoord if necessary 854 out << " float2 dx_ViewScale : packoffset(c3.z);\n"; 855 } 856 857 if (mHasMultiviewExtensionEnabled) 858 { 859 // We have to add a value which we can use to keep track of which multi-view code 860 // path is to be selected in the GS. 861 out << " float multiviewSelectViewportIndex : packoffset(c4.x);\n"; 862 } 863 864 if (mOutputType == SH_HLSL_4_1_OUTPUT) 865 { 866 unsigned int registerIndex = 5; 867 mResourcesHLSL->samplerMetadataUniforms(out, registerIndex); 868 // Sampler metadata struct must be two 4-vec, 32 bytes. 869 registerIndex += mResourcesHLSL->getSamplerCount() * 2; 870 mResourcesHLSL->imageMetadataUniforms(out, registerIndex); 871 } 872 873 out << "};\n"; 874 875 if (mOutputType == SH_HLSL_4_1_OUTPUT && mResourcesHLSL->hasImages()) 876 { 877 out << kImage2DFunctionString << "\n"; 878 } 879 } 880 else 881 { 882 if (mUsesDepthRange) 883 { 884 out << "uniform float3 dx_DepthRange : register(c0);"; 885 } 886 887 if (mUsesFragCoord) 888 { 889 out << "uniform float4 dx_ViewCoords : register(c1);\n"; 890 } 891 892 if (mUsesFragCoord || mUsesFrontFacing) 893 { 894 out << "uniform float3 dx_DepthFront : register(c2);\n"; 895 out << "uniform float2 dx_FragCoordOffset : register(c3);\n"; 896 } 897 } 898 899 out << "\n"; 900 901 if (mUsesDepthRange) 902 { 903 out << "static gl_DepthRangeParameters gl_DepthRange = {dx_DepthRange.x, " 904 "dx_DepthRange.y, dx_DepthRange.z};\n" 905 "\n"; 906 } 907 908 if (usingMRTExtension && mNumRenderTargets > 1) 909 { 910 out << "#define GL_USES_MRT\n"; 911 } 912 913 if (mUsesFragColor) 914 { 915 out << "#define GL_USES_FRAG_COLOR\n"; 916 } 917 918 if (mUsesFragData) 919 { 920 out << "#define GL_USES_FRAG_DATA\n"; 921 } 922 923 if (mShaderVersion < 300 && usingBFEExtension && mUsesSecondaryColor) 924 { 925 out << "#define GL_USES_SECONDARY_COLOR\n"; 926 } 927 } 928 else if (mShaderType == GL_VERTEX_SHADER) 929 { 930 out << "// Attributes\n"; 931 writeReferencedAttributes(out); 932 out << "\n" 933 "static float4 gl_Position = float4(0, 0, 0, 0);\n"; 934 935 if (mUsesPointSize) 936 { 937 out << "static float gl_PointSize = float(1);\n"; 938 } 939 940 if (mUsesInstanceID) 941 { 942 out << "static int gl_InstanceID;"; 943 } 944 945 if (mUsesVertexID) 946 { 947 out << "static int gl_VertexID;"; 948 } 949 950 out << "\n" 951 "// Varyings\n"; 952 writeReferencedVaryings(out); 953 out << "\n"; 954 955 if (mUsesDepthRange) 956 { 957 out << "struct gl_DepthRangeParameters\n" 958 "{\n" 959 " float near;\n" 960 " float far;\n" 961 " float diff;\n" 962 "};\n" 963 "\n"; 964 } 965 966 if (mOutputType == SH_HLSL_4_1_OUTPUT || mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT) 967 { 968 out << "cbuffer DriverConstants : register(b1)\n" 969 "{\n"; 970 971 if (mUsesDepthRange) 972 { 973 out << " float3 dx_DepthRange : packoffset(c0);\n"; 974 } 975 976 // dx_ViewAdjust and dx_ViewCoords will only be used in Feature Level 9 977 // shaders. However, we declare it for all shaders (including Feature Level 10+). 978 // The bytecode is the same whether we declare it or not, since D3DCompiler removes it 979 // if it's unused. 980 out << " float4 dx_ViewAdjust : packoffset(c1);\n"; 981 out << " float2 dx_ViewCoords : packoffset(c2);\n"; 982 out << " float2 dx_ViewScale : packoffset(c3);\n"; 983 984 if (mHasMultiviewExtensionEnabled) 985 { 986 // We have to add a value which we can use to keep track of which multi-view code 987 // path is to be selected in the GS. 988 out << " float multiviewSelectViewportIndex : packoffset(c3.z);\n"; 989 } 990 991 out << " float clipControlOrigin : packoffset(c3.w);\n"; 992 out << " float clipControlZeroToOne : packoffset(c4);\n"; 993 994 if (mOutputType == SH_HLSL_4_1_OUTPUT) 995 { 996 mResourcesHLSL->samplerMetadataUniforms(out, 5); 997 } 998 999 if (mUsesVertexID) 1000 { 1001 out << " uint dx_VertexID : packoffset(c4.y);\n"; 1002 } 1003 1004 out << "};\n" 1005 "\n"; 1006 } 1007 else 1008 { 1009 if (mUsesDepthRange) 1010 { 1011 out << "uniform float3 dx_DepthRange : register(c0);\n"; 1012 } 1013 1014 out << "uniform float4 dx_ViewAdjust : register(c1);\n"; 1015 out << "uniform float2 dx_ViewCoords : register(c2);\n"; 1016 1017 out << "static const float clipControlOrigin = -1.0f;\n"; 1018 out << "static const float clipControlZeroToOne = 0.0f;\n"; 1019 1020 out << "\n"; 1021 } 1022 1023 if (mUsesDepthRange) 1024 { 1025 out << "static gl_DepthRangeParameters gl_DepthRange = {dx_DepthRange.x, " 1026 "dx_DepthRange.y, dx_DepthRange.z};\n" 1027 "\n"; 1028 } 1029 } 1030 else // Compute shader 1031 { 1032 ASSERT(mShaderType == GL_COMPUTE_SHADER); 1033 1034 out << "cbuffer DriverConstants : register(b1)\n" 1035 "{\n"; 1036 if (mUsesNumWorkGroups) 1037 { 1038 out << " uint3 gl_NumWorkGroups : packoffset(c0);\n"; 1039 } 1040 ASSERT(mOutputType == SH_HLSL_4_1_OUTPUT); 1041 unsigned int registerIndex = 1; 1042 mResourcesHLSL->samplerMetadataUniforms(out, registerIndex); 1043 // Sampler metadata struct must be two 4-vec, 32 bytes. 1044 registerIndex += mResourcesHLSL->getSamplerCount() * 2; 1045 mResourcesHLSL->imageMetadataUniforms(out, registerIndex); 1046 out << "};\n"; 1047 1048 out << kImage2DFunctionString << "\n"; 1049 1050 std::ostringstream systemValueDeclaration = sh::InitializeStream<std::ostringstream>(); 1051 std::ostringstream glBuiltinInitialization = sh::InitializeStream<std::ostringstream>(); 1052 1053 systemValueDeclaration << "\nstruct CS_INPUT\n{\n"; 1054 glBuiltinInitialization << "\nvoid initGLBuiltins(CS_INPUT input)\n" 1055 << "{\n"; 1056 1057 if (mUsesWorkGroupID) 1058 { 1059 out << "static uint3 gl_WorkGroupID = uint3(0, 0, 0);\n"; 1060 systemValueDeclaration << " uint3 dx_WorkGroupID : " 1061 << "SV_GroupID;\n"; 1062 glBuiltinInitialization << " gl_WorkGroupID = input.dx_WorkGroupID;\n"; 1063 } 1064 1065 if (mUsesLocalInvocationID) 1066 { 1067 out << "static uint3 gl_LocalInvocationID = uint3(0, 0, 0);\n"; 1068 systemValueDeclaration << " uint3 dx_LocalInvocationID : " 1069 << "SV_GroupThreadID;\n"; 1070 glBuiltinInitialization << " gl_LocalInvocationID = input.dx_LocalInvocationID;\n"; 1071 } 1072 1073 if (mUsesGlobalInvocationID) 1074 { 1075 out << "static uint3 gl_GlobalInvocationID = uint3(0, 0, 0);\n"; 1076 systemValueDeclaration << " uint3 dx_GlobalInvocationID : " 1077 << "SV_DispatchThreadID;\n"; 1078 glBuiltinInitialization << " gl_GlobalInvocationID = input.dx_GlobalInvocationID;\n"; 1079 } 1080 1081 if (mUsesLocalInvocationIndex) 1082 { 1083 out << "static uint gl_LocalInvocationIndex = uint(0);\n"; 1084 systemValueDeclaration << " uint dx_LocalInvocationIndex : " 1085 << "SV_GroupIndex;\n"; 1086 glBuiltinInitialization 1087 << " gl_LocalInvocationIndex = input.dx_LocalInvocationIndex;\n"; 1088 } 1089 1090 systemValueDeclaration << "};\n\n"; 1091 glBuiltinInitialization << "};\n\n"; 1092 1093 out << systemValueDeclaration.str(); 1094 out << glBuiltinInitialization.str(); 1095 } 1096 1097 if (!mappedStructs.empty()) 1098 { 1099 out << "// Structures from std140 blocks with padding removed\n"; 1100 out << "\n"; 1101 out << mappedStructs; 1102 out << "\n"; 1103 } 1104 1105 bool getDimensionsIgnoresBaseLevel = mCompileOptions.HLSLGetDimensionsIgnoresBaseLevel; 1106 mTextureFunctionHLSL->textureFunctionHeader(out, mOutputType, getDimensionsIgnoresBaseLevel); 1107 mImageFunctionHLSL->imageFunctionHeader(out); 1108 mAtomicCounterFunctionHLSL->atomicCounterFunctionHeader(out); 1109 1110 if (mUsesFragCoord) 1111 { 1112 out << "#define GL_USES_FRAG_COORD\n"; 1113 } 1114 1115 if (mUsesPointCoord) 1116 { 1117 out << "#define GL_USES_POINT_COORD\n"; 1118 } 1119 1120 if (mUsesFrontFacing) 1121 { 1122 out << "#define GL_USES_FRONT_FACING\n"; 1123 } 1124 1125 if (mUsesHelperInvocation) 1126 { 1127 out << "#define GL_USES_HELPER_INVOCATION\n"; 1128 } 1129 1130 if (mUsesPointSize) 1131 { 1132 out << "#define GL_USES_POINT_SIZE\n"; 1133 } 1134 1135 if (mHasMultiviewExtensionEnabled) 1136 { 1137 out << "#define GL_ANGLE_MULTIVIEW_ENABLED\n"; 1138 } 1139 1140 if (mUsesVertexID) 1141 { 1142 out << "#define GL_USES_VERTEX_ID\n"; 1143 } 1144 1145 if (mUsesViewID) 1146 { 1147 out << "#define GL_USES_VIEW_ID\n"; 1148 } 1149 1150 if (mUsesFragDepth) 1151 { 1152 out << "#define GL_USES_FRAG_DEPTH\n"; 1153 } 1154 1155 if (mUsesDepthRange) 1156 { 1157 out << "#define GL_USES_DEPTH_RANGE\n"; 1158 } 1159 1160 if (mUsesXor) 1161 { 1162 out << "bool xor(bool p, bool q)\n" 1163 "{\n" 1164 " return (p || q) && !(p && q);\n" 1165 "}\n" 1166 "\n"; 1167 } 1168 1169 builtInFunctionEmulator->outputEmulatedFunctions(out); 1170 } 1171 1172 void OutputHLSL::visitSymbol(TIntermSymbol *node) 1173 { 1174 const TVariable &variable = node->variable(); 1175 1176 // Empty symbols can only appear in declarations and function arguments, and in either of those 1177 // cases the symbol nodes are not visited. 1178 ASSERT(variable.symbolType() != SymbolType::Empty); 1179 1180 TInfoSinkBase &out = getInfoSink(); 1181 1182 // Handle accessing std140 structs by value 1183 if (IsInStd140UniformBlock(node) && node->getBasicType() == EbtStruct && 1184 needStructMapping(node)) 1185 { 1186 mNeedStructMapping = true; 1187 out << "map"; 1188 } 1189 1190 const ImmutableString &name = variable.name(); 1191 const TSymbolUniqueId &uniqueId = variable.uniqueId(); 1192 1193 if (name == "gl_DepthRange") 1194 { 1195 mUsesDepthRange = true; 1196 out << name; 1197 } 1198 else if (IsAtomicCounter(variable.getType().getBasicType())) 1199 { 1200 const TType &variableType = variable.getType(); 1201 if (variableType.getQualifier() == EvqUniform) 1202 { 1203 TLayoutQualifier layout = variableType.getLayoutQualifier(); 1204 mReferencedUniforms[uniqueId.get()] = &variable; 1205 out << getAtomicCounterNameForBinding(layout.binding) << ", " << layout.offset; 1206 } 1207 else 1208 { 1209 TString varName = DecorateVariableIfNeeded(variable); 1210 out << varName << ", " << varName << "_offset"; 1211 } 1212 } 1213 else 1214 { 1215 const TType &variableType = variable.getType(); 1216 TQualifier qualifier = variable.getType().getQualifier(); 1217 1218 ensureStructDefined(variableType); 1219 1220 if (qualifier == EvqUniform) 1221 { 1222 const TInterfaceBlock *interfaceBlock = variableType.getInterfaceBlock(); 1223 1224 if (interfaceBlock) 1225 { 1226 if (mReferencedUniformBlocks.count(interfaceBlock->uniqueId().get()) == 0) 1227 { 1228 const TVariable *instanceVariable = nullptr; 1229 if (variableType.isInterfaceBlock()) 1230 { 1231 instanceVariable = &variable; 1232 } 1233 mReferencedUniformBlocks[interfaceBlock->uniqueId().get()] = 1234 new TReferencedBlock(interfaceBlock, instanceVariable); 1235 } 1236 } 1237 else 1238 { 1239 mReferencedUniforms[uniqueId.get()] = &variable; 1240 } 1241 1242 out << DecorateVariableIfNeeded(variable); 1243 } 1244 else if (qualifier == EvqBuffer) 1245 { 1246 UNREACHABLE(); 1247 } 1248 else if (qualifier == EvqAttribute || qualifier == EvqVertexIn) 1249 { 1250 mReferencedAttributes[uniqueId.get()] = &variable; 1251 out << Decorate(name); 1252 } 1253 else if (IsVarying(qualifier)) 1254 { 1255 mReferencedVaryings[uniqueId.get()] = &variable; 1256 out << DecorateVariableIfNeeded(variable); 1257 if (variable.symbolType() == SymbolType::AngleInternal && name == "ViewID_OVR") 1258 { 1259 mUsesViewID = true; 1260 } 1261 } 1262 else if (qualifier == EvqFragmentOut) 1263 { 1264 mReferencedOutputVariables[uniqueId.get()] = &variable; 1265 out << "out_" << name; 1266 } 1267 else if (qualifier == EvqFragColor) 1268 { 1269 out << "gl_Color[0]"; 1270 mUsesFragColor = true; 1271 } 1272 else if (qualifier == EvqFragData) 1273 { 1274 out << "gl_Color"; 1275 mUsesFragData = true; 1276 } 1277 else if (qualifier == EvqSecondaryFragColorEXT) 1278 { 1279 out << "gl_SecondaryColor[0]"; 1280 mUsesSecondaryColor = true; 1281 } 1282 else if (qualifier == EvqSecondaryFragDataEXT) 1283 { 1284 out << "gl_SecondaryColor"; 1285 mUsesSecondaryColor = true; 1286 } 1287 else if (qualifier == EvqFragCoord) 1288 { 1289 mUsesFragCoord = true; 1290 out << name; 1291 } 1292 else if (qualifier == EvqPointCoord) 1293 { 1294 mUsesPointCoord = true; 1295 out << name; 1296 } 1297 else if (qualifier == EvqFrontFacing) 1298 { 1299 mUsesFrontFacing = true; 1300 out << name; 1301 } 1302 else if (qualifier == EvqHelperInvocation) 1303 { 1304 mUsesHelperInvocation = true; 1305 out << name; 1306 } 1307 else if (qualifier == EvqPointSize) 1308 { 1309 mUsesPointSize = true; 1310 out << name; 1311 } 1312 else if (qualifier == EvqInstanceID) 1313 { 1314 mUsesInstanceID = true; 1315 out << name; 1316 } 1317 else if (qualifier == EvqVertexID) 1318 { 1319 mUsesVertexID = true; 1320 out << name; 1321 } 1322 else if (name == "gl_FragDepthEXT" || name == "gl_FragDepth") 1323 { 1324 mUsesFragDepth = true; 1325 out << "gl_Depth"; 1326 } 1327 else if (qualifier == EvqNumWorkGroups) 1328 { 1329 mUsesNumWorkGroups = true; 1330 out << name; 1331 } 1332 else if (qualifier == EvqWorkGroupID) 1333 { 1334 mUsesWorkGroupID = true; 1335 out << name; 1336 } 1337 else if (qualifier == EvqLocalInvocationID) 1338 { 1339 mUsesLocalInvocationID = true; 1340 out << name; 1341 } 1342 else if (qualifier == EvqGlobalInvocationID) 1343 { 1344 mUsesGlobalInvocationID = true; 1345 out << name; 1346 } 1347 else if (qualifier == EvqLocalInvocationIndex) 1348 { 1349 mUsesLocalInvocationIndex = true; 1350 out << name; 1351 } 1352 else 1353 { 1354 out << DecorateVariableIfNeeded(variable); 1355 } 1356 } 1357 } 1358 1359 void OutputHLSL::outputEqual(Visit visit, const TType &type, TOperator op, TInfoSinkBase &out) 1360 { 1361 if (type.isScalar() && !type.isArray()) 1362 { 1363 if (op == EOpEqual) 1364 { 1365 outputTriplet(out, visit, "(", " == ", ")"); 1366 } 1367 else 1368 { 1369 outputTriplet(out, visit, "(", " != ", ")"); 1370 } 1371 } 1372 else 1373 { 1374 if (visit == PreVisit && op == EOpNotEqual) 1375 { 1376 out << "!"; 1377 } 1378 1379 if (type.isArray()) 1380 { 1381 const TString &functionName = addArrayEqualityFunction(type); 1382 outputTriplet(out, visit, (functionName + "(").c_str(), ", ", ")"); 1383 } 1384 else if (type.getBasicType() == EbtStruct) 1385 { 1386 const TStructure &structure = *type.getStruct(); 1387 const TString &functionName = addStructEqualityFunction(structure); 1388 outputTriplet(out, visit, (functionName + "(").c_str(), ", ", ")"); 1389 } 1390 else 1391 { 1392 ASSERT(type.isMatrix() || type.isVector()); 1393 outputTriplet(out, visit, "all(", " == ", ")"); 1394 } 1395 } 1396 } 1397 1398 void OutputHLSL::outputAssign(Visit visit, const TType &type, TInfoSinkBase &out) 1399 { 1400 if (type.isArray()) 1401 { 1402 const TString &functionName = addArrayAssignmentFunction(type); 1403 outputTriplet(out, visit, (functionName + "(").c_str(), ", ", ")"); 1404 } 1405 else 1406 { 1407 outputTriplet(out, visit, "(", " = ", ")"); 1408 } 1409 } 1410 1411 bool OutputHLSL::ancestorEvaluatesToSamplerInStruct() 1412 { 1413 for (unsigned int n = 0u; getAncestorNode(n) != nullptr; ++n) 1414 { 1415 TIntermNode *ancestor = getAncestorNode(n); 1416 const TIntermBinary *ancestorBinary = ancestor->getAsBinaryNode(); 1417 if (ancestorBinary == nullptr) 1418 { 1419 return false; 1420 } 1421 switch (ancestorBinary->getOp()) 1422 { 1423 case EOpIndexDirectStruct: 1424 { 1425 const TStructure *structure = ancestorBinary->getLeft()->getType().getStruct(); 1426 const TIntermConstantUnion *index = 1427 ancestorBinary->getRight()->getAsConstantUnion(); 1428 const TField *field = structure->fields()[index->getIConst(0)]; 1429 if (IsSampler(field->type()->getBasicType())) 1430 { 1431 return true; 1432 } 1433 break; 1434 } 1435 case EOpIndexDirect: 1436 break; 1437 default: 1438 // Returning a sampler from indirect indexing is not supported. 1439 return false; 1440 } 1441 } 1442 return false; 1443 } 1444 1445 bool OutputHLSL::visitSwizzle(Visit visit, TIntermSwizzle *node) 1446 { 1447 TInfoSinkBase &out = getInfoSink(); 1448 if (visit == PostVisit) 1449 { 1450 out << "."; 1451 node->writeOffsetsAsXYZW(&out); 1452 } 1453 return true; 1454 } 1455 1456 bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node) 1457 { 1458 TInfoSinkBase &out = getInfoSink(); 1459 1460 switch (node->getOp()) 1461 { 1462 case EOpComma: 1463 outputTriplet(out, visit, "(", ", ", ")"); 1464 break; 1465 case EOpAssign: 1466 if (node->isArray()) 1467 { 1468 TIntermAggregate *rightAgg = node->getRight()->getAsAggregate(); 1469 if (rightAgg != nullptr && rightAgg->isConstructor()) 1470 { 1471 const TString &functionName = addArrayConstructIntoFunction(node->getType()); 1472 out << functionName << "("; 1473 node->getLeft()->traverse(this); 1474 TIntermSequence *seq = rightAgg->getSequence(); 1475 for (auto &arrayElement : *seq) 1476 { 1477 out << ", "; 1478 arrayElement->traverse(this); 1479 } 1480 out << ")"; 1481 return false; 1482 } 1483 // ArrayReturnValueToOutParameter should have eliminated expressions where a 1484 // function call is assigned. 1485 ASSERT(rightAgg == nullptr); 1486 } 1487 // Assignment expressions with atomic functions should be transformed into atomic 1488 // function calls in HLSL. 1489 // e.g. original_value = atomicAdd(dest, value) should be translated into 1490 // InterlockedAdd(dest, value, original_value); 1491 else if (IsAtomicFunctionForSharedVariableDirectAssign(*node)) 1492 { 1493 TIntermAggregate *atomicFunctionNode = node->getRight()->getAsAggregate(); 1494 TOperator atomicFunctionOp = atomicFunctionNode->getOp(); 1495 out << GetHLSLAtomicFunctionStringAndLeftParenthesis(atomicFunctionOp); 1496 TIntermSequence *argumentSeq = atomicFunctionNode->getSequence(); 1497 ASSERT(argumentSeq->size() >= 2u); 1498 for (auto &argument : *argumentSeq) 1499 { 1500 argument->traverse(this); 1501 out << ", "; 1502 } 1503 node->getLeft()->traverse(this); 1504 out << ")"; 1505 return false; 1506 } 1507 else if (IsInShaderStorageBlock(node->getLeft())) 1508 { 1509 mSSBOOutputHLSL->outputStoreFunctionCallPrefix(node->getLeft()); 1510 out << ", "; 1511 if (IsInShaderStorageBlock(node->getRight())) 1512 { 1513 mSSBOOutputHLSL->outputLoadFunctionCall(node->getRight()); 1514 } 1515 else 1516 { 1517 node->getRight()->traverse(this); 1518 } 1519 1520 out << ")"; 1521 return false; 1522 } 1523 else if (IsInShaderStorageBlock(node->getRight())) 1524 { 1525 node->getLeft()->traverse(this); 1526 out << " = "; 1527 mSSBOOutputHLSL->outputLoadFunctionCall(node->getRight()); 1528 return false; 1529 } 1530 1531 outputAssign(visit, node->getType(), out); 1532 break; 1533 case EOpInitialize: 1534 if (visit == PreVisit) 1535 { 1536 TIntermSymbol *symbolNode = node->getLeft()->getAsSymbolNode(); 1537 ASSERT(symbolNode); 1538 TIntermTyped *initializer = node->getRight(); 1539 1540 // Global initializers must be constant at this point. 1541 ASSERT(symbolNode->getQualifier() != EvqGlobal || initializer->hasConstantValue()); 1542 1543 // GLSL allows to write things like "float x = x;" where a new variable x is defined 1544 // and the value of an existing variable x is assigned. HLSL uses C semantics (the 1545 // new variable is created before the assignment is evaluated), so we need to 1546 // convert 1547 // this to "float t = x, x = t;". 1548 if (writeSameSymbolInitializer(out, symbolNode, initializer)) 1549 { 1550 // Skip initializing the rest of the expression 1551 return false; 1552 } 1553 else if (writeConstantInitialization(out, symbolNode, initializer)) 1554 { 1555 return false; 1556 } 1557 } 1558 else if (visit == InVisit) 1559 { 1560 out << " = "; 1561 if (IsInShaderStorageBlock(node->getRight())) 1562 { 1563 mSSBOOutputHLSL->outputLoadFunctionCall(node->getRight()); 1564 return false; 1565 } 1566 } 1567 break; 1568 case EOpAddAssign: 1569 outputTriplet(out, visit, "(", " += ", ")"); 1570 break; 1571 case EOpSubAssign: 1572 outputTriplet(out, visit, "(", " -= ", ")"); 1573 break; 1574 case EOpMulAssign: 1575 outputTriplet(out, visit, "(", " *= ", ")"); 1576 break; 1577 case EOpVectorTimesScalarAssign: 1578 outputTriplet(out, visit, "(", " *= ", ")"); 1579 break; 1580 case EOpMatrixTimesScalarAssign: 1581 outputTriplet(out, visit, "(", " *= ", ")"); 1582 break; 1583 case EOpVectorTimesMatrixAssign: 1584 if (visit == PreVisit) 1585 { 1586 out << "("; 1587 } 1588 else if (visit == InVisit) 1589 { 1590 out << " = mul("; 1591 node->getLeft()->traverse(this); 1592 out << ", transpose("; 1593 } 1594 else 1595 { 1596 out << ")))"; 1597 } 1598 break; 1599 case EOpMatrixTimesMatrixAssign: 1600 if (visit == PreVisit) 1601 { 1602 out << "("; 1603 } 1604 else if (visit == InVisit) 1605 { 1606 out << " = transpose(mul(transpose("; 1607 node->getLeft()->traverse(this); 1608 out << "), transpose("; 1609 } 1610 else 1611 { 1612 out << "))))"; 1613 } 1614 break; 1615 case EOpDivAssign: 1616 outputTriplet(out, visit, "(", " /= ", ")"); 1617 break; 1618 case EOpIModAssign: 1619 outputTriplet(out, visit, "(", " %= ", ")"); 1620 break; 1621 case EOpBitShiftLeftAssign: 1622 outputTriplet(out, visit, "(", " <<= ", ")"); 1623 break; 1624 case EOpBitShiftRightAssign: 1625 outputTriplet(out, visit, "(", " >>= ", ")"); 1626 break; 1627 case EOpBitwiseAndAssign: 1628 outputTriplet(out, visit, "(", " &= ", ")"); 1629 break; 1630 case EOpBitwiseXorAssign: 1631 outputTriplet(out, visit, "(", " ^= ", ")"); 1632 break; 1633 case EOpBitwiseOrAssign: 1634 outputTriplet(out, visit, "(", " |= ", ")"); 1635 break; 1636 case EOpIndexDirect: 1637 { 1638 const TType &leftType = node->getLeft()->getType(); 1639 if (leftType.isInterfaceBlock()) 1640 { 1641 if (visit == PreVisit) 1642 { 1643 TIntermSymbol *instanceArraySymbol = node->getLeft()->getAsSymbolNode(); 1644 const TInterfaceBlock *interfaceBlock = leftType.getInterfaceBlock(); 1645 1646 ASSERT(leftType.getQualifier() == EvqUniform); 1647 if (mReferencedUniformBlocks.count(interfaceBlock->uniqueId().get()) == 0) 1648 { 1649 mReferencedUniformBlocks[interfaceBlock->uniqueId().get()] = 1650 new TReferencedBlock(interfaceBlock, &instanceArraySymbol->variable()); 1651 } 1652 const int arrayIndex = node->getRight()->getAsConstantUnion()->getIConst(0); 1653 out << mResourcesHLSL->InterfaceBlockInstanceString( 1654 instanceArraySymbol->getName(), arrayIndex); 1655 return false; 1656 } 1657 } 1658 else if (ancestorEvaluatesToSamplerInStruct()) 1659 { 1660 // All parts of an expression that access a sampler in a struct need to use _ as 1661 // separator to access the sampler variable that has been moved out of the struct. 1662 outputTriplet(out, visit, "", "_", ""); 1663 } 1664 else if (IsAtomicCounter(leftType.getBasicType())) 1665 { 1666 outputTriplet(out, visit, "", " + (", ") * ATOMIC_COUNTER_ARRAY_STRIDE"); 1667 } 1668 else 1669 { 1670 outputTriplet(out, visit, "", "[", "]"); 1671 if (visit == PostVisit) 1672 { 1673 const TInterfaceBlock *interfaceBlock = 1674 GetInterfaceBlockOfUniformBlockNearestIndexOperator(node->getLeft()); 1675 if (interfaceBlock && 1676 mUniformBlockOptimizedMap.count(interfaceBlock->uniqueId().get()) != 0) 1677 { 1678 // If the uniform block member's type is not structure, we had explicitly 1679 // packed the member into a structure, so need to add an operator of field 1680 // slection. 1681 const TField *field = interfaceBlock->fields()[0]; 1682 const TType *fieldType = field->type(); 1683 if (fieldType->isMatrix() || fieldType->isVectorArray() || 1684 fieldType->isScalarArray()) 1685 { 1686 out << "." << Decorate(field->name()); 1687 } 1688 } 1689 } 1690 } 1691 } 1692 break; 1693 case EOpIndexIndirect: 1694 { 1695 // We do not currently support indirect references to interface blocks 1696 ASSERT(node->getLeft()->getBasicType() != EbtInterfaceBlock); 1697 1698 const TType &leftType = node->getLeft()->getType(); 1699 if (IsAtomicCounter(leftType.getBasicType())) 1700 { 1701 outputTriplet(out, visit, "", " + (", ") * ATOMIC_COUNTER_ARRAY_STRIDE"); 1702 } 1703 else 1704 { 1705 outputTriplet(out, visit, "", "[", "]"); 1706 if (visit == PostVisit) 1707 { 1708 const TInterfaceBlock *interfaceBlock = 1709 GetInterfaceBlockOfUniformBlockNearestIndexOperator(node->getLeft()); 1710 if (interfaceBlock && 1711 mUniformBlockOptimizedMap.count(interfaceBlock->uniqueId().get()) != 0) 1712 { 1713 // If the uniform block member's type is not structure, we had explicitly 1714 // packed the member into a structure, so need to add an operator of field 1715 // slection. 1716 const TField *field = interfaceBlock->fields()[0]; 1717 const TType *fieldType = field->type(); 1718 if (fieldType->isMatrix() || fieldType->isVectorArray() || 1719 fieldType->isScalarArray()) 1720 { 1721 out << "." << Decorate(field->name()); 1722 } 1723 } 1724 } 1725 } 1726 break; 1727 } 1728 case EOpIndexDirectStruct: 1729 { 1730 const TStructure *structure = node->getLeft()->getType().getStruct(); 1731 const TIntermConstantUnion *index = node->getRight()->getAsConstantUnion(); 1732 const TField *field = structure->fields()[index->getIConst(0)]; 1733 1734 // In cases where indexing returns a sampler, we need to access the sampler variable 1735 // that has been moved out of the struct. 1736 bool indexingReturnsSampler = IsSampler(field->type()->getBasicType()); 1737 if (visit == PreVisit && indexingReturnsSampler) 1738 { 1739 // Samplers extracted from structs have "angle" prefix to avoid name conflicts. 1740 // This prefix is only output at the beginning of the indexing expression, which 1741 // may have multiple parts. 1742 out << "angle"; 1743 } 1744 if (!indexingReturnsSampler) 1745 { 1746 // All parts of an expression that access a sampler in a struct need to use _ as 1747 // separator to access the sampler variable that has been moved out of the struct. 1748 indexingReturnsSampler = ancestorEvaluatesToSamplerInStruct(); 1749 } 1750 if (visit == InVisit) 1751 { 1752 if (indexingReturnsSampler) 1753 { 1754 out << "_" << field->name(); 1755 } 1756 else 1757 { 1758 out << "." << DecorateField(field->name(), *structure); 1759 } 1760 1761 return false; 1762 } 1763 } 1764 break; 1765 case EOpIndexDirectInterfaceBlock: 1766 { 1767 ASSERT(!IsInShaderStorageBlock(node->getLeft())); 1768 bool structInStd140UniformBlock = node->getBasicType() == EbtStruct && 1769 IsInStd140UniformBlock(node->getLeft()) && 1770 needStructMapping(node); 1771 if (visit == PreVisit && structInStd140UniformBlock) 1772 { 1773 mNeedStructMapping = true; 1774 out << "map"; 1775 } 1776 if (visit == InVisit) 1777 { 1778 const TInterfaceBlock *interfaceBlock = 1779 node->getLeft()->getType().getInterfaceBlock(); 1780 const TIntermConstantUnion *index = node->getRight()->getAsConstantUnion(); 1781 const TField *field = interfaceBlock->fields()[index->getIConst(0)]; 1782 if (structInStd140UniformBlock || 1783 mUniformBlockOptimizedMap.count(interfaceBlock->uniqueId().get()) != 0) 1784 { 1785 out << "_"; 1786 } 1787 else 1788 { 1789 out << "."; 1790 } 1791 out << Decorate(field->name()); 1792 1793 return false; 1794 } 1795 break; 1796 } 1797 case EOpAdd: 1798 outputTriplet(out, visit, "(", " + ", ")"); 1799 break; 1800 case EOpSub: 1801 outputTriplet(out, visit, "(", " - ", ")"); 1802 break; 1803 case EOpMul: 1804 outputTriplet(out, visit, "(", " * ", ")"); 1805 break; 1806 case EOpDiv: 1807 outputTriplet(out, visit, "(", " / ", ")"); 1808 break; 1809 case EOpIMod: 1810 outputTriplet(out, visit, "(", " % ", ")"); 1811 break; 1812 case EOpBitShiftLeft: 1813 outputTriplet(out, visit, "(", " << ", ")"); 1814 break; 1815 case EOpBitShiftRight: 1816 outputTriplet(out, visit, "(", " >> ", ")"); 1817 break; 1818 case EOpBitwiseAnd: 1819 outputTriplet(out, visit, "(", " & ", ")"); 1820 break; 1821 case EOpBitwiseXor: 1822 outputTriplet(out, visit, "(", " ^ ", ")"); 1823 break; 1824 case EOpBitwiseOr: 1825 outputTriplet(out, visit, "(", " | ", ")"); 1826 break; 1827 case EOpEqual: 1828 case EOpNotEqual: 1829 outputEqual(visit, node->getLeft()->getType(), node->getOp(), out); 1830 break; 1831 case EOpLessThan: 1832 outputTriplet(out, visit, "(", " < ", ")"); 1833 break; 1834 case EOpGreaterThan: 1835 outputTriplet(out, visit, "(", " > ", ")"); 1836 break; 1837 case EOpLessThanEqual: 1838 outputTriplet(out, visit, "(", " <= ", ")"); 1839 break; 1840 case EOpGreaterThanEqual: 1841 outputTriplet(out, visit, "(", " >= ", ")"); 1842 break; 1843 case EOpVectorTimesScalar: 1844 outputTriplet(out, visit, "(", " * ", ")"); 1845 break; 1846 case EOpMatrixTimesScalar: 1847 outputTriplet(out, visit, "(", " * ", ")"); 1848 break; 1849 case EOpVectorTimesMatrix: 1850 outputTriplet(out, visit, "mul(", ", transpose(", "))"); 1851 break; 1852 case EOpMatrixTimesVector: 1853 outputTriplet(out, visit, "mul(transpose(", "), ", ")"); 1854 break; 1855 case EOpMatrixTimesMatrix: 1856 outputTriplet(out, visit, "transpose(mul(transpose(", "), transpose(", ")))"); 1857 break; 1858 case EOpLogicalOr: 1859 // HLSL doesn't short-circuit ||, so we assume that || affected by short-circuiting have 1860 // been unfolded. 1861 ASSERT(!node->getRight()->hasSideEffects()); 1862 outputTriplet(out, visit, "(", " || ", ")"); 1863 return true; 1864 case EOpLogicalXor: 1865 mUsesXor = true; 1866 outputTriplet(out, visit, "xor(", ", ", ")"); 1867 break; 1868 case EOpLogicalAnd: 1869 // HLSL doesn't short-circuit &&, so we assume that && affected by short-circuiting have 1870 // been unfolded. 1871 ASSERT(!node->getRight()->hasSideEffects()); 1872 outputTriplet(out, visit, "(", " && ", ")"); 1873 return true; 1874 default: 1875 UNREACHABLE(); 1876 } 1877 1878 return true; 1879 } 1880 1881 bool OutputHLSL::visitUnary(Visit visit, TIntermUnary *node) 1882 { 1883 TInfoSinkBase &out = getInfoSink(); 1884 1885 switch (node->getOp()) 1886 { 1887 case EOpNegative: 1888 outputTriplet(out, visit, "(-", "", ")"); 1889 break; 1890 case EOpPositive: 1891 outputTriplet(out, visit, "(+", "", ")"); 1892 break; 1893 case EOpLogicalNot: 1894 outputTriplet(out, visit, "(!", "", ")"); 1895 break; 1896 case EOpBitwiseNot: 1897 outputTriplet(out, visit, "(~", "", ")"); 1898 break; 1899 case EOpPostIncrement: 1900 outputTriplet(out, visit, "(", "", "++)"); 1901 break; 1902 case EOpPostDecrement: 1903 outputTriplet(out, visit, "(", "", "--)"); 1904 break; 1905 case EOpPreIncrement: 1906 outputTriplet(out, visit, "(++", "", ")"); 1907 break; 1908 case EOpPreDecrement: 1909 outputTriplet(out, visit, "(--", "", ")"); 1910 break; 1911 case EOpRadians: 1912 outputTriplet(out, visit, "radians(", "", ")"); 1913 break; 1914 case EOpDegrees: 1915 outputTriplet(out, visit, "degrees(", "", ")"); 1916 break; 1917 case EOpSin: 1918 outputTriplet(out, visit, "sin(", "", ")"); 1919 break; 1920 case EOpCos: 1921 outputTriplet(out, visit, "cos(", "", ")"); 1922 break; 1923 case EOpTan: 1924 outputTriplet(out, visit, "tan(", "", ")"); 1925 break; 1926 case EOpAsin: 1927 outputTriplet(out, visit, "asin(", "", ")"); 1928 break; 1929 case EOpAcos: 1930 outputTriplet(out, visit, "acos(", "", ")"); 1931 break; 1932 case EOpAtan: 1933 outputTriplet(out, visit, "atan(", "", ")"); 1934 break; 1935 case EOpSinh: 1936 outputTriplet(out, visit, "sinh(", "", ")"); 1937 break; 1938 case EOpCosh: 1939 outputTriplet(out, visit, "cosh(", "", ")"); 1940 break; 1941 case EOpTanh: 1942 case EOpAsinh: 1943 case EOpAcosh: 1944 case EOpAtanh: 1945 ASSERT(node->getUseEmulatedFunction()); 1946 writeEmulatedFunctionTriplet(out, visit, node->getFunction()); 1947 break; 1948 case EOpExp: 1949 outputTriplet(out, visit, "exp(", "", ")"); 1950 break; 1951 case EOpLog: 1952 outputTriplet(out, visit, "log(", "", ")"); 1953 break; 1954 case EOpExp2: 1955 outputTriplet(out, visit, "exp2(", "", ")"); 1956 break; 1957 case EOpLog2: 1958 outputTriplet(out, visit, "log2(", "", ")"); 1959 break; 1960 case EOpSqrt: 1961 outputTriplet(out, visit, "sqrt(", "", ")"); 1962 break; 1963 case EOpInversesqrt: 1964 outputTriplet(out, visit, "rsqrt(", "", ")"); 1965 break; 1966 case EOpAbs: 1967 outputTriplet(out, visit, "abs(", "", ")"); 1968 break; 1969 case EOpSign: 1970 outputTriplet(out, visit, "sign(", "", ")"); 1971 break; 1972 case EOpFloor: 1973 outputTriplet(out, visit, "floor(", "", ")"); 1974 break; 1975 case EOpTrunc: 1976 outputTriplet(out, visit, "trunc(", "", ")"); 1977 break; 1978 case EOpRound: 1979 outputTriplet(out, visit, "round(", "", ")"); 1980 break; 1981 case EOpRoundEven: 1982 ASSERT(node->getUseEmulatedFunction()); 1983 writeEmulatedFunctionTriplet(out, visit, node->getFunction()); 1984 break; 1985 case EOpCeil: 1986 outputTriplet(out, visit, "ceil(", "", ")"); 1987 break; 1988 case EOpFract: 1989 outputTriplet(out, visit, "frac(", "", ")"); 1990 break; 1991 case EOpIsnan: 1992 if (node->getUseEmulatedFunction()) 1993 writeEmulatedFunctionTriplet(out, visit, node->getFunction()); 1994 else 1995 outputTriplet(out, visit, "isnan(", "", ")"); 1996 mRequiresIEEEStrictCompiling = true; 1997 break; 1998 case EOpIsinf: 1999 outputTriplet(out, visit, "isinf(", "", ")"); 2000 break; 2001 case EOpFloatBitsToInt: 2002 outputTriplet(out, visit, "asint(", "", ")"); 2003 break; 2004 case EOpFloatBitsToUint: 2005 outputTriplet(out, visit, "asuint(", "", ")"); 2006 break; 2007 case EOpIntBitsToFloat: 2008 outputTriplet(out, visit, "asfloat(", "", ")"); 2009 break; 2010 case EOpUintBitsToFloat: 2011 outputTriplet(out, visit, "asfloat(", "", ")"); 2012 break; 2013 case EOpPackSnorm2x16: 2014 case EOpPackUnorm2x16: 2015 case EOpPackHalf2x16: 2016 case EOpUnpackSnorm2x16: 2017 case EOpUnpackUnorm2x16: 2018 case EOpUnpackHalf2x16: 2019 case EOpPackUnorm4x8: 2020 case EOpPackSnorm4x8: 2021 case EOpUnpackUnorm4x8: 2022 case EOpUnpackSnorm4x8: 2023 ASSERT(node->getUseEmulatedFunction()); 2024 writeEmulatedFunctionTriplet(out, visit, node->getFunction()); 2025 break; 2026 case EOpLength: 2027 outputTriplet(out, visit, "length(", "", ")"); 2028 break; 2029 case EOpNormalize: 2030 outputTriplet(out, visit, "normalize(", "", ")"); 2031 break; 2032 case EOpTranspose: 2033 outputTriplet(out, visit, "transpose(", "", ")"); 2034 break; 2035 case EOpDeterminant: 2036 outputTriplet(out, visit, "determinant(transpose(", "", "))"); 2037 break; 2038 case EOpInverse: 2039 ASSERT(node->getUseEmulatedFunction()); 2040 writeEmulatedFunctionTriplet(out, visit, node->getFunction()); 2041 break; 2042 2043 case EOpAny: 2044 outputTriplet(out, visit, "any(", "", ")"); 2045 break; 2046 case EOpAll: 2047 outputTriplet(out, visit, "all(", "", ")"); 2048 break; 2049 case EOpNotComponentWise: 2050 outputTriplet(out, visit, "(!", "", ")"); 2051 break; 2052 case EOpBitfieldReverse: 2053 outputTriplet(out, visit, "reversebits(", "", ")"); 2054 break; 2055 case EOpBitCount: 2056 outputTriplet(out, visit, "countbits(", "", ")"); 2057 break; 2058 case EOpFindLSB: 2059 // Note that it's unclear from the HLSL docs what this returns for 0, but this is tested 2060 // in GLSLTest and results are consistent with GL. 2061 outputTriplet(out, visit, "firstbitlow(", "", ")"); 2062 break; 2063 case EOpFindMSB: 2064 // Note that it's unclear from the HLSL docs what this returns for 0 or -1, but this is 2065 // tested in GLSLTest and results are consistent with GL. 2066 outputTriplet(out, visit, "firstbithigh(", "", ")"); 2067 break; 2068 case EOpArrayLength: 2069 { 2070 TIntermTyped *operand = node->getOperand(); 2071 ASSERT(IsInShaderStorageBlock(operand)); 2072 mSSBOOutputHLSL->outputLengthFunctionCall(operand); 2073 return false; 2074 } 2075 default: 2076 UNREACHABLE(); 2077 } 2078 2079 return true; 2080 } 2081 2082 ImmutableString OutputHLSL::samplerNamePrefixFromStruct(TIntermTyped *node) 2083 { 2084 if (node->getAsSymbolNode()) 2085 { 2086 ASSERT(node->getAsSymbolNode()->variable().symbolType() != SymbolType::Empty); 2087 return node->getAsSymbolNode()->getName(); 2088 } 2089 TIntermBinary *nodeBinary = node->getAsBinaryNode(); 2090 switch (nodeBinary->getOp()) 2091 { 2092 case EOpIndexDirect: 2093 { 2094 int index = nodeBinary->getRight()->getAsConstantUnion()->getIConst(0); 2095 2096 std::stringstream prefixSink = sh::InitializeStream<std::stringstream>(); 2097 prefixSink << samplerNamePrefixFromStruct(nodeBinary->getLeft()) << "_" << index; 2098 return ImmutableString(prefixSink.str()); 2099 } 2100 case EOpIndexDirectStruct: 2101 { 2102 const TStructure *s = nodeBinary->getLeft()->getAsTyped()->getType().getStruct(); 2103 int index = nodeBinary->getRight()->getAsConstantUnion()->getIConst(0); 2104 const TField *field = s->fields()[index]; 2105 2106 std::stringstream prefixSink = sh::InitializeStream<std::stringstream>(); 2107 prefixSink << samplerNamePrefixFromStruct(nodeBinary->getLeft()) << "_" 2108 << field->name(); 2109 return ImmutableString(prefixSink.str()); 2110 } 2111 default: 2112 UNREACHABLE(); 2113 return kEmptyImmutableString; 2114 } 2115 } 2116 2117 bool OutputHLSL::visitBlock(Visit visit, TIntermBlock *node) 2118 { 2119 TInfoSinkBase &out = getInfoSink(); 2120 2121 bool isMainBlock = mInsideMain && getParentNode()->getAsFunctionDefinition(); 2122 2123 if (mInsideFunction) 2124 { 2125 outputLineDirective(out, node->getLine().first_line); 2126 out << "{\n"; 2127 if (isMainBlock) 2128 { 2129 if (mShaderType == GL_COMPUTE_SHADER) 2130 { 2131 out << "initGLBuiltins(input);\n"; 2132 } 2133 else 2134 { 2135 out << "@@ MAIN PROLOGUE @@\n"; 2136 } 2137 } 2138 } 2139 2140 for (TIntermNode *statement : *node->getSequence()) 2141 { 2142 outputLineDirective(out, statement->getLine().first_line); 2143 2144 statement->traverse(this); 2145 2146 // Don't output ; after case labels, they're terminated by : 2147 // This is needed especially since outputting a ; after a case statement would turn empty 2148 // case statements into non-empty case statements, disallowing fall-through from them. 2149 // Also the output code is clearer if we don't output ; after statements where it is not 2150 // needed: 2151 // * if statements 2152 // * switch statements 2153 // * blocks 2154 // * function definitions 2155 // * loops (do-while loops output the semicolon in VisitLoop) 2156 // * declarations that don't generate output. 2157 if (statement->getAsCaseNode() == nullptr && statement->getAsIfElseNode() == nullptr && 2158 statement->getAsBlock() == nullptr && statement->getAsLoopNode() == nullptr && 2159 statement->getAsSwitchNode() == nullptr && 2160 statement->getAsFunctionDefinition() == nullptr && 2161 (statement->getAsDeclarationNode() == nullptr || 2162 IsDeclarationWrittenOut(statement->getAsDeclarationNode())) && 2163 statement->getAsGlobalQualifierDeclarationNode() == nullptr) 2164 { 2165 out << ";\n"; 2166 } 2167 } 2168 2169 if (mInsideFunction) 2170 { 2171 outputLineDirective(out, node->getLine().last_line); 2172 if (isMainBlock && shaderNeedsGenerateOutput()) 2173 { 2174 // We could have an empty main, a main function without a branch at the end, or a main 2175 // function with a discard statement at the end. In these cases we need to add a return 2176 // statement. 2177 bool needReturnStatement = 2178 node->getSequence()->empty() || !node->getSequence()->back()->getAsBranchNode() || 2179 node->getSequence()->back()->getAsBranchNode()->getFlowOp() != EOpReturn; 2180 if (needReturnStatement) 2181 { 2182 out << "return " << generateOutputCall() << ";\n"; 2183 } 2184 } 2185 out << "}\n"; 2186 } 2187 2188 return false; 2189 } 2190 2191 bool OutputHLSL::visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) 2192 { 2193 TInfoSinkBase &out = getInfoSink(); 2194 2195 ASSERT(mCurrentFunctionMetadata == nullptr); 2196 2197 size_t index = mCallDag.findIndex(node->getFunction()->uniqueId()); 2198 ASSERT(index != CallDAG::InvalidIndex); 2199 mCurrentFunctionMetadata = &mASTMetadataList[index]; 2200 2201 const TFunction *func = node->getFunction(); 2202 2203 if (func->isMain()) 2204 { 2205 // The stub strings below are replaced when shader is dynamically defined by its layout: 2206 switch (mShaderType) 2207 { 2208 case GL_VERTEX_SHADER: 2209 out << "@@ VERTEX ATTRIBUTES @@\n\n" 2210 << "@@ VERTEX OUTPUT @@\n\n" 2211 << "VS_OUTPUT main(VS_INPUT input)"; 2212 break; 2213 case GL_FRAGMENT_SHADER: 2214 out << "@@ PIXEL OUTPUT @@\n\n"; 2215 if (mIsEarlyFragmentTestsSpecified) 2216 { 2217 out << "[earlydepthstencil]\n"; 2218 } 2219 out << "PS_OUTPUT main(@@ PIXEL MAIN PARAMETERS @@)"; 2220 break; 2221 case GL_COMPUTE_SHADER: 2222 out << "[numthreads(" << mWorkGroupSize[0] << ", " << mWorkGroupSize[1] << ", " 2223 << mWorkGroupSize[2] << ")]\n"; 2224 out << "void main(CS_INPUT input)"; 2225 break; 2226 default: 2227 UNREACHABLE(); 2228 break; 2229 } 2230 } 2231 else 2232 { 2233 out << TypeString(node->getFunctionPrototype()->getType()) << " "; 2234 out << DecorateFunctionIfNeeded(func) << DisambiguateFunctionName(func) 2235 << (mOutputLod0Function ? "Lod0(" : "("); 2236 2237 size_t paramCount = func->getParamCount(); 2238 for (unsigned int i = 0; i < paramCount; i++) 2239 { 2240 const TVariable *param = func->getParam(i); 2241 ensureStructDefined(param->getType()); 2242 2243 writeParameter(param, out); 2244 2245 if (i < paramCount - 1) 2246 { 2247 out << ", "; 2248 } 2249 } 2250 2251 out << ")\n"; 2252 } 2253 2254 mInsideFunction = true; 2255 if (func->isMain()) 2256 { 2257 mInsideMain = true; 2258 } 2259 // The function body node will output braces. 2260 node->getBody()->traverse(this); 2261 mInsideFunction = false; 2262 mInsideMain = false; 2263 2264 mCurrentFunctionMetadata = nullptr; 2265 2266 bool needsLod0 = mASTMetadataList[index].mNeedsLod0; 2267 if (needsLod0 && !mOutputLod0Function && mShaderType == GL_FRAGMENT_SHADER) 2268 { 2269 ASSERT(!node->getFunction()->isMain()); 2270 mOutputLod0Function = true; 2271 node->traverse(this); 2272 mOutputLod0Function = false; 2273 } 2274 2275 return false; 2276 } 2277 2278 bool OutputHLSL::visitDeclaration(Visit visit, TIntermDeclaration *node) 2279 { 2280 if (visit == PreVisit) 2281 { 2282 TIntermSequence *sequence = node->getSequence(); 2283 TIntermTyped *declarator = (*sequence)[0]->getAsTyped(); 2284 ASSERT(sequence->size() == 1); 2285 ASSERT(declarator); 2286 2287 if (IsDeclarationWrittenOut(node)) 2288 { 2289 TInfoSinkBase &out = getInfoSink(); 2290 ensureStructDefined(declarator->getType()); 2291 2292 if (!declarator->getAsSymbolNode() || 2293 declarator->getAsSymbolNode()->variable().symbolType() != 2294 SymbolType::Empty) // Variable declaration 2295 { 2296 if (declarator->getQualifier() == EvqShared) 2297 { 2298 out << "groupshared "; 2299 } 2300 else if (!mInsideFunction) 2301 { 2302 out << "static "; 2303 } 2304 2305 out << TypeString(declarator->getType()) + " "; 2306 2307 TIntermSymbol *symbol = declarator->getAsSymbolNode(); 2308 2309 if (symbol) 2310 { 2311 symbol->traverse(this); 2312 out << ArrayString(symbol->getType()); 2313 // Temporarily disable shadred memory initialization. It is very slow for D3D11 2314 // drivers to compile a compute shader if we add code to initialize a 2315 // groupshared array variable with a large array size. And maybe produce 2316 // incorrect result. See http://anglebug.com/3226. 2317 if (declarator->getQualifier() != EvqShared) 2318 { 2319 out << " = " + zeroInitializer(symbol->getType()); 2320 } 2321 } 2322 else 2323 { 2324 declarator->traverse(this); 2325 } 2326 } 2327 } 2328 else if (IsVaryingOut(declarator->getQualifier())) 2329 { 2330 TIntermSymbol *symbol = declarator->getAsSymbolNode(); 2331 ASSERT(symbol); // Varying declarations can't have initializers. 2332 2333 const TVariable &variable = symbol->variable(); 2334 2335 if (variable.symbolType() != SymbolType::Empty) 2336 { 2337 // Vertex outputs which are declared but not written to should still be declared to 2338 // allow successful linking. 2339 mReferencedVaryings[symbol->uniqueId().get()] = &variable; 2340 } 2341 } 2342 } 2343 return false; 2344 } 2345 2346 bool OutputHLSL::visitGlobalQualifierDeclaration(Visit visit, 2347 TIntermGlobalQualifierDeclaration *node) 2348 { 2349 // Do not do any translation 2350 return false; 2351 } 2352 2353 void OutputHLSL::visitFunctionPrototype(TIntermFunctionPrototype *node) 2354 { 2355 TInfoSinkBase &out = getInfoSink(); 2356 2357 size_t index = mCallDag.findIndex(node->getFunction()->uniqueId()); 2358 // Skip the prototype if it is not implemented (and thus not used) 2359 if (index == CallDAG::InvalidIndex) 2360 { 2361 return; 2362 } 2363 2364 const TFunction *func = node->getFunction(); 2365 2366 TString name = DecorateFunctionIfNeeded(func); 2367 out << TypeString(node->getType()) << " " << name << DisambiguateFunctionName(func) 2368 << (mOutputLod0Function ? "Lod0(" : "("); 2369 2370 size_t paramCount = func->getParamCount(); 2371 for (unsigned int i = 0; i < paramCount; i++) 2372 { 2373 writeParameter(func->getParam(i), out); 2374 2375 if (i < paramCount - 1) 2376 { 2377 out << ", "; 2378 } 2379 } 2380 2381 out << ");\n"; 2382 2383 // Also prototype the Lod0 variant if needed 2384 bool needsLod0 = mASTMetadataList[index].mNeedsLod0; 2385 if (needsLod0 && !mOutputLod0Function && mShaderType == GL_FRAGMENT_SHADER) 2386 { 2387 mOutputLod0Function = true; 2388 node->traverse(this); 2389 mOutputLod0Function = false; 2390 } 2391 } 2392 2393 bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) 2394 { 2395 TInfoSinkBase &out = getInfoSink(); 2396 2397 switch (node->getOp()) 2398 { 2399 case EOpCallFunctionInAST: 2400 case EOpCallInternalRawFunction: 2401 default: 2402 { 2403 TIntermSequence *arguments = node->getSequence(); 2404 2405 bool lod0 = (mInsideDiscontinuousLoop || mOutputLod0Function) && 2406 mShaderType == GL_FRAGMENT_SHADER; 2407 2408 // No raw function is expected. 2409 ASSERT(node->getOp() != EOpCallInternalRawFunction); 2410 2411 if (node->getOp() == EOpCallFunctionInAST) 2412 { 2413 if (node->isArray()) 2414 { 2415 UNIMPLEMENTED(); 2416 } 2417 size_t index = mCallDag.findIndex(node->getFunction()->uniqueId()); 2418 ASSERT(index != CallDAG::InvalidIndex); 2419 lod0 &= mASTMetadataList[index].mNeedsLod0; 2420 2421 out << DecorateFunctionIfNeeded(node->getFunction()); 2422 out << DisambiguateFunctionName(node->getSequence()); 2423 out << (lod0 ? "Lod0(" : "("); 2424 } 2425 else if (node->getFunction()->isImageFunction()) 2426 { 2427 const ImmutableString &name = node->getFunction()->name(); 2428 TType type = (*arguments)[0]->getAsTyped()->getType(); 2429 const ImmutableString &imageFunctionName = mImageFunctionHLSL->useImageFunction( 2430 name, type.getBasicType(), type.getLayoutQualifier().imageInternalFormat, 2431 type.getMemoryQualifier().readonly); 2432 out << imageFunctionName << "("; 2433 } 2434 else if (node->getFunction()->isAtomicCounterFunction()) 2435 { 2436 const ImmutableString &name = node->getFunction()->name(); 2437 ImmutableString atomicFunctionName = 2438 mAtomicCounterFunctionHLSL->useAtomicCounterFunction(name); 2439 out << atomicFunctionName << "("; 2440 } 2441 else 2442 { 2443 const ImmutableString &name = node->getFunction()->name(); 2444 TBasicType samplerType = (*arguments)[0]->getAsTyped()->getType().getBasicType(); 2445 int coords = 0; // textureSize(gsampler2DMS) doesn't have a second argument. 2446 if (arguments->size() > 1) 2447 { 2448 coords = (*arguments)[1]->getAsTyped()->getNominalSize(); 2449 } 2450 const ImmutableString &textureFunctionName = 2451 mTextureFunctionHLSL->useTextureFunction(name, samplerType, coords, 2452 arguments->size(), lod0, mShaderType); 2453 out << textureFunctionName << "("; 2454 } 2455 2456 for (TIntermSequence::iterator arg = arguments->begin(); arg != arguments->end(); arg++) 2457 { 2458 TIntermTyped *typedArg = (*arg)->getAsTyped(); 2459 if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT && IsSampler(typedArg->getBasicType())) 2460 { 2461 out << "texture_"; 2462 (*arg)->traverse(this); 2463 out << ", sampler_"; 2464 } 2465 2466 (*arg)->traverse(this); 2467 2468 if (typedArg->getType().isStructureContainingSamplers()) 2469 { 2470 const TType &argType = typedArg->getType(); 2471 TVector<const TVariable *> samplerSymbols; 2472 ImmutableString structName = samplerNamePrefixFromStruct(typedArg); 2473 std::string namePrefix = "angle_"; 2474 namePrefix += structName.data(); 2475 argType.createSamplerSymbols(ImmutableString(namePrefix), "", &samplerSymbols, 2476 nullptr, mSymbolTable); 2477 for (const TVariable *sampler : samplerSymbols) 2478 { 2479 if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT) 2480 { 2481 out << ", texture_" << sampler->name(); 2482 out << ", sampler_" << sampler->name(); 2483 } 2484 else 2485 { 2486 // In case of HLSL 4.1+, this symbol is the sampler index, and in case 2487 // of D3D9, it's the sampler variable. 2488 out << ", " << sampler->name(); 2489 } 2490 } 2491 } 2492 2493 if (arg < arguments->end() - 1) 2494 { 2495 out << ", "; 2496 } 2497 } 2498 2499 out << ")"; 2500 2501 return false; 2502 } 2503 case EOpConstruct: 2504 outputConstructor(out, visit, node); 2505 break; 2506 case EOpEqualComponentWise: 2507 outputTriplet(out, visit, "(", " == ", ")"); 2508 break; 2509 case EOpNotEqualComponentWise: 2510 outputTriplet(out, visit, "(", " != ", ")"); 2511 break; 2512 case EOpLessThanComponentWise: 2513 outputTriplet(out, visit, "(", " < ", ")"); 2514 break; 2515 case EOpGreaterThanComponentWise: 2516 outputTriplet(out, visit, "(", " > ", ")"); 2517 break; 2518 case EOpLessThanEqualComponentWise: 2519 outputTriplet(out, visit, "(", " <= ", ")"); 2520 break; 2521 case EOpGreaterThanEqualComponentWise: 2522 outputTriplet(out, visit, "(", " >= ", ")"); 2523 break; 2524 case EOpMod: 2525 ASSERT(node->getUseEmulatedFunction()); 2526 writeEmulatedFunctionTriplet(out, visit, node->getFunction()); 2527 break; 2528 case EOpModf: 2529 outputTriplet(out, visit, "modf(", ", ", ")"); 2530 break; 2531 case EOpPow: 2532 outputTriplet(out, visit, "pow(", ", ", ")"); 2533 break; 2534 case EOpAtan: 2535 ASSERT(node->getSequence()->size() == 2); // atan(x) is a unary operator 2536 ASSERT(node->getUseEmulatedFunction()); 2537 writeEmulatedFunctionTriplet(out, visit, node->getFunction()); 2538 break; 2539 case EOpMin: 2540 outputTriplet(out, visit, "min(", ", ", ")"); 2541 break; 2542 case EOpMax: 2543 outputTriplet(out, visit, "max(", ", ", ")"); 2544 break; 2545 case EOpClamp: 2546 outputTriplet(out, visit, "clamp(", ", ", ")"); 2547 break; 2548 case EOpMix: 2549 { 2550 TIntermTyped *lastParamNode = (*(node->getSequence()))[2]->getAsTyped(); 2551 if (lastParamNode->getType().getBasicType() == EbtBool) 2552 { 2553 // There is no HLSL equivalent for ESSL3 built-in "genType mix (genType x, genType 2554 // y, genBType a)", 2555 // so use emulated version. 2556 ASSERT(node->getUseEmulatedFunction()); 2557 writeEmulatedFunctionTriplet(out, visit, node->getFunction()); 2558 } 2559 else 2560 { 2561 outputTriplet(out, visit, "lerp(", ", ", ")"); 2562 } 2563 break; 2564 } 2565 case EOpStep: 2566 outputTriplet(out, visit, "step(", ", ", ")"); 2567 break; 2568 case EOpSmoothstep: 2569 outputTriplet(out, visit, "smoothstep(", ", ", ")"); 2570 break; 2571 case EOpFma: 2572 outputTriplet(out, visit, "mad(", ", ", ")"); 2573 break; 2574 case EOpFrexp: 2575 case EOpLdexp: 2576 ASSERT(node->getUseEmulatedFunction()); 2577 writeEmulatedFunctionTriplet(out, visit, node->getFunction()); 2578 break; 2579 case EOpDistance: 2580 outputTriplet(out, visit, "distance(", ", ", ")"); 2581 break; 2582 case EOpDot: 2583 outputTriplet(out, visit, "dot(", ", ", ")"); 2584 break; 2585 case EOpCross: 2586 outputTriplet(out, visit, "cross(", ", ", ")"); 2587 break; 2588 case EOpFaceforward: 2589 ASSERT(node->getUseEmulatedFunction()); 2590 writeEmulatedFunctionTriplet(out, visit, node->getFunction()); 2591 break; 2592 case EOpReflect: 2593 outputTriplet(out, visit, "reflect(", ", ", ")"); 2594 break; 2595 case EOpRefract: 2596 outputTriplet(out, visit, "refract(", ", ", ")"); 2597 break; 2598 case EOpOuterProduct: 2599 ASSERT(node->getUseEmulatedFunction()); 2600 writeEmulatedFunctionTriplet(out, visit, node->getFunction()); 2601 break; 2602 case EOpMatrixCompMult: 2603 outputTriplet(out, visit, "(", " * ", ")"); 2604 break; 2605 case EOpBitfieldExtract: 2606 case EOpBitfieldInsert: 2607 case EOpUaddCarry: 2608 case EOpUsubBorrow: 2609 case EOpUmulExtended: 2610 case EOpImulExtended: 2611 ASSERT(node->getUseEmulatedFunction()); 2612 writeEmulatedFunctionTriplet(out, visit, node->getFunction()); 2613 break; 2614 case EOpDFdx: 2615 if (mInsideDiscontinuousLoop || mOutputLod0Function) 2616 { 2617 outputTriplet(out, visit, "(", "", ", 0.0)"); 2618 } 2619 else 2620 { 2621 outputTriplet(out, visit, "ddx(", "", ")"); 2622 } 2623 break; 2624 case EOpDFdy: 2625 if (mInsideDiscontinuousLoop || mOutputLod0Function) 2626 { 2627 outputTriplet(out, visit, "(", "", ", 0.0)"); 2628 } 2629 else 2630 { 2631 outputTriplet(out, visit, "ddy(", "", ")"); 2632 } 2633 break; 2634 case EOpFwidth: 2635 if (mInsideDiscontinuousLoop || mOutputLod0Function) 2636 { 2637 outputTriplet(out, visit, "(", "", ", 0.0)"); 2638 } 2639 else 2640 { 2641 outputTriplet(out, visit, "fwidth(", "", ")"); 2642 } 2643 break; 2644 case EOpBarrier: 2645 // barrier() is translated to GroupMemoryBarrierWithGroupSync(), which is the 2646 // cheapest *WithGroupSync() function, without any functionality loss, but 2647 // with the potential for severe performance loss. 2648 outputTriplet(out, visit, "GroupMemoryBarrierWithGroupSync(", "", ")"); 2649 break; 2650 case EOpMemoryBarrierShared: 2651 outputTriplet(out, visit, "GroupMemoryBarrier(", "", ")"); 2652 break; 2653 case EOpMemoryBarrierAtomicCounter: 2654 case EOpMemoryBarrierBuffer: 2655 case EOpMemoryBarrierImage: 2656 outputTriplet(out, visit, "DeviceMemoryBarrier(", "", ")"); 2657 break; 2658 case EOpGroupMemoryBarrier: 2659 case EOpMemoryBarrier: 2660 outputTriplet(out, visit, "AllMemoryBarrier(", "", ")"); 2661 break; 2662 2663 // Single atomic function calls without return value. 2664 // e.g. atomicAdd(dest, value) should be translated into InterlockedAdd(dest, value). 2665 case EOpAtomicAdd: 2666 case EOpAtomicMin: 2667 case EOpAtomicMax: 2668 case EOpAtomicAnd: 2669 case EOpAtomicOr: 2670 case EOpAtomicXor: 2671 // The parameter 'original_value' of InterlockedExchange(dest, value, original_value) 2672 // and InterlockedCompareExchange(dest, compare_value, value, original_value) is not 2673 // optional. 2674 // https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/interlockedexchange 2675 // https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/interlockedcompareexchange 2676 // So all the call of atomicExchange(dest, value) and atomicCompSwap(dest, 2677 // compare_value, value) should all be modified into the form of "int temp; temp = 2678 // atomicExchange(dest, value);" and "int temp; temp = atomicCompSwap(dest, 2679 // compare_value, value);" in the intermediate tree before traversing outputHLSL. 2680 case EOpAtomicExchange: 2681 case EOpAtomicCompSwap: 2682 { 2683 ASSERT(node->getChildCount() > 1); 2684 TIntermTyped *memNode = (*node->getSequence())[0]->getAsTyped(); 2685 if (IsInShaderStorageBlock(memNode)) 2686 { 2687 // Atomic memory functions for SSBO. 2688 // "_ssbo_atomicXXX_TYPE(RWByteAddressBuffer buffer, uint loc" is written to |out|. 2689 mSSBOOutputHLSL->outputAtomicMemoryFunctionCallPrefix(memNode, node->getOp()); 2690 // Write the rest argument list to |out|. 2691 for (size_t i = 1; i < node->getChildCount(); i++) 2692 { 2693 out << ", "; 2694 TIntermTyped *argument = (*node->getSequence())[i]->getAsTyped(); 2695 if (IsInShaderStorageBlock(argument)) 2696 { 2697 mSSBOOutputHLSL->outputLoadFunctionCall(argument); 2698 } 2699 else 2700 { 2701 argument->traverse(this); 2702 } 2703 } 2704 2705 out << ")"; 2706 return false; 2707 } 2708 else 2709 { 2710 // Atomic memory functions for shared variable. 2711 if (node->getOp() != EOpAtomicExchange && node->getOp() != EOpAtomicCompSwap) 2712 { 2713 outputTriplet(out, visit, 2714 GetHLSLAtomicFunctionStringAndLeftParenthesis(node->getOp()), ",", 2715 ")"); 2716 } 2717 else 2718 { 2719 UNREACHABLE(); 2720 } 2721 } 2722 2723 break; 2724 } 2725 } 2726 2727 return true; 2728 } 2729 2730 void OutputHLSL::writeIfElse(TInfoSinkBase &out, TIntermIfElse *node) 2731 { 2732 out << "if ("; 2733 2734 node->getCondition()->traverse(this); 2735 2736 out << ")\n"; 2737 2738 outputLineDirective(out, node->getLine().first_line); 2739 2740 bool discard = false; 2741 2742 if (node->getTrueBlock()) 2743 { 2744 // The trueBlock child node will output braces. 2745 node->getTrueBlock()->traverse(this); 2746 2747 // Detect true discard 2748 discard = (discard || FindDiscard::search(node->getTrueBlock())); 2749 } 2750 else 2751 { 2752 // TODO(oetuaho): Check if the semicolon inside is necessary. 2753 // It's there as a result of conservative refactoring of the output. 2754 out << "{;}\n"; 2755 } 2756 2757 outputLineDirective(out, node->getLine().first_line); 2758 2759 if (node->getFalseBlock()) 2760 { 2761 out << "else\n"; 2762 2763 outputLineDirective(out, node->getFalseBlock()->getLine().first_line); 2764 2765 // The falseBlock child node will output braces. 2766 node->getFalseBlock()->traverse(this); 2767 2768 outputLineDirective(out, node->getFalseBlock()->getLine().first_line); 2769 2770 // Detect false discard 2771 discard = (discard || FindDiscard::search(node->getFalseBlock())); 2772 } 2773 2774 // ANGLE issue 486: Detect problematic conditional discard 2775 if (discard) 2776 { 2777 mUsesDiscardRewriting = true; 2778 } 2779 } 2780 2781 bool OutputHLSL::visitTernary(Visit, TIntermTernary *) 2782 { 2783 // Ternary ops should have been already converted to something else in the AST. HLSL ternary 2784 // operator doesn't short-circuit, so it's not the same as the GLSL ternary operator. 2785 UNREACHABLE(); 2786 return false; 2787 } 2788 2789 bool OutputHLSL::visitIfElse(Visit visit, TIntermIfElse *node) 2790 { 2791 TInfoSinkBase &out = getInfoSink(); 2792 2793 ASSERT(mInsideFunction); 2794 2795 // D3D errors when there is a gradient operation in a loop in an unflattened if. 2796 if (mShaderType == GL_FRAGMENT_SHADER && mCurrentFunctionMetadata->hasGradientLoop(node)) 2797 { 2798 out << "FLATTEN "; 2799 } 2800 2801 writeIfElse(out, node); 2802 2803 return false; 2804 } 2805 2806 bool OutputHLSL::visitSwitch(Visit visit, TIntermSwitch *node) 2807 { 2808 TInfoSinkBase &out = getInfoSink(); 2809 2810 ASSERT(node->getStatementList()); 2811 if (visit == PreVisit) 2812 { 2813 node->setStatementList(RemoveSwitchFallThrough(node->getStatementList(), mPerfDiagnostics)); 2814 } 2815 outputTriplet(out, visit, "switch (", ") ", ""); 2816 // The curly braces get written when visiting the statementList block. 2817 return true; 2818 } 2819 2820 bool OutputHLSL::visitCase(Visit visit, TIntermCase *node) 2821 { 2822 TInfoSinkBase &out = getInfoSink(); 2823 2824 if (node->hasCondition()) 2825 { 2826 outputTriplet(out, visit, "case (", "", "):\n"); 2827 return true; 2828 } 2829 else 2830 { 2831 out << "default:\n"; 2832 return false; 2833 } 2834 } 2835 2836 void OutputHLSL::visitConstantUnion(TIntermConstantUnion *node) 2837 { 2838 TInfoSinkBase &out = getInfoSink(); 2839 writeConstantUnion(out, node->getType(), node->getConstantValue()); 2840 } 2841 2842 bool OutputHLSL::visitLoop(Visit visit, TIntermLoop *node) 2843 { 2844 mNestedLoopDepth++; 2845 2846 bool wasDiscontinuous = mInsideDiscontinuousLoop; 2847 mInsideDiscontinuousLoop = 2848 mInsideDiscontinuousLoop || mCurrentFunctionMetadata->mDiscontinuousLoops.count(node) > 0; 2849 2850 TInfoSinkBase &out = getInfoSink(); 2851 2852 if (mOutputType == SH_HLSL_3_0_OUTPUT) 2853 { 2854 if (handleExcessiveLoop(out, node)) 2855 { 2856 mInsideDiscontinuousLoop = wasDiscontinuous; 2857 mNestedLoopDepth--; 2858 2859 return false; 2860 } 2861 } 2862 2863 const char *unroll = mCurrentFunctionMetadata->hasGradientInCallGraph(node) ? "LOOP" : ""; 2864 if (node->getType() == ELoopDoWhile) 2865 { 2866 out << "{" << unroll << " do\n"; 2867 2868 outputLineDirective(out, node->getLine().first_line); 2869 } 2870 else 2871 { 2872 out << "{" << unroll << " for("; 2873 2874 if (node->getInit()) 2875 { 2876 node->getInit()->traverse(this); 2877 } 2878 2879 out << "; "; 2880 2881 if (node->getCondition()) 2882 { 2883 node->getCondition()->traverse(this); 2884 } 2885 2886 out << "; "; 2887 2888 if (node->getExpression()) 2889 { 2890 node->getExpression()->traverse(this); 2891 } 2892 2893 out << ")\n"; 2894 2895 outputLineDirective(out, node->getLine().first_line); 2896 } 2897 2898 if (node->getBody()) 2899 { 2900 // The loop body node will output braces. 2901 node->getBody()->traverse(this); 2902 } 2903 else 2904 { 2905 // TODO(oetuaho): Check if the semicolon inside is necessary. 2906 // It's there as a result of conservative refactoring of the output. 2907 out << "{;}\n"; 2908 } 2909 2910 outputLineDirective(out, node->getLine().first_line); 2911 2912 if (node->getType() == ELoopDoWhile) 2913 { 2914 outputLineDirective(out, node->getCondition()->getLine().first_line); 2915 out << "while ("; 2916 2917 node->getCondition()->traverse(this); 2918 2919 out << ");\n"; 2920 } 2921 2922 out << "}\n"; 2923 2924 mInsideDiscontinuousLoop = wasDiscontinuous; 2925 mNestedLoopDepth--; 2926 2927 return false; 2928 } 2929 2930 bool OutputHLSL::visitBranch(Visit visit, TIntermBranch *node) 2931 { 2932 if (visit == PreVisit) 2933 { 2934 TInfoSinkBase &out = getInfoSink(); 2935 2936 switch (node->getFlowOp()) 2937 { 2938 case EOpKill: 2939 out << "discard"; 2940 break; 2941 case EOpBreak: 2942 if (mNestedLoopDepth > 1) 2943 { 2944 mUsesNestedBreak = true; 2945 } 2946 2947 if (mExcessiveLoopIndex) 2948 { 2949 out << "{Break"; 2950 mExcessiveLoopIndex->traverse(this); 2951 out << " = true; break;}\n"; 2952 } 2953 else 2954 { 2955 out << "break"; 2956 } 2957 break; 2958 case EOpContinue: 2959 out << "continue"; 2960 break; 2961 case EOpReturn: 2962 if (node->getExpression()) 2963 { 2964 ASSERT(!mInsideMain); 2965 out << "return "; 2966 if (IsInShaderStorageBlock(node->getExpression())) 2967 { 2968 mSSBOOutputHLSL->outputLoadFunctionCall(node->getExpression()); 2969 return false; 2970 } 2971 } 2972 else 2973 { 2974 if (mInsideMain && shaderNeedsGenerateOutput()) 2975 { 2976 out << "return " << generateOutputCall(); 2977 } 2978 else 2979 { 2980 out << "return"; 2981 } 2982 } 2983 break; 2984 default: 2985 UNREACHABLE(); 2986 } 2987 } 2988 2989 return true; 2990 } 2991 2992 // Handle loops with more than 254 iterations (unsupported by D3D9) by splitting them 2993 // (The D3D documentation says 255 iterations, but the compiler complains at anything more than 2994 // 254). 2995 bool OutputHLSL::handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node) 2996 { 2997 const int MAX_LOOP_ITERATIONS = 254; 2998 2999 // Parse loops of the form: 3000 // for(int index = initial; index [comparator] limit; index += increment) 3001 TIntermSymbol *index = nullptr; 3002 TOperator comparator = EOpNull; 3003 int initial = 0; 3004 int limit = 0; 3005 int increment = 0; 3006 3007 // Parse index name and intial value 3008 if (node->getInit()) 3009 { 3010 TIntermDeclaration *init = node->getInit()->getAsDeclarationNode(); 3011 3012 if (init) 3013 { 3014 TIntermSequence *sequence = init->getSequence(); 3015 TIntermTyped *variable = (*sequence)[0]->getAsTyped(); 3016 3017 if (variable && variable->getQualifier() == EvqTemporary) 3018 { 3019 TIntermBinary *assign = variable->getAsBinaryNode(); 3020 3021 if (assign != nullptr && assign->getOp() == EOpInitialize) 3022 { 3023 TIntermSymbol *symbol = assign->getLeft()->getAsSymbolNode(); 3024 TIntermConstantUnion *constant = assign->getRight()->getAsConstantUnion(); 3025 3026 if (symbol && constant) 3027 { 3028 if (constant->getBasicType() == EbtInt && constant->isScalar()) 3029 { 3030 index = symbol; 3031 initial = constant->getIConst(0); 3032 } 3033 } 3034 } 3035 } 3036 } 3037 } 3038 3039 // Parse comparator and limit value 3040 if (index != nullptr && node->getCondition()) 3041 { 3042 TIntermBinary *test = node->getCondition()->getAsBinaryNode(); 3043 3044 if (test && test->getLeft()->getAsSymbolNode()->uniqueId() == index->uniqueId()) 3045 { 3046 TIntermConstantUnion *constant = test->getRight()->getAsConstantUnion(); 3047 3048 if (constant) 3049 { 3050 if (constant->getBasicType() == EbtInt && constant->isScalar()) 3051 { 3052 comparator = test->getOp(); 3053 limit = constant->getIConst(0); 3054 } 3055 } 3056 } 3057 } 3058 3059 // Parse increment 3060 if (index != nullptr && comparator != EOpNull && node->getExpression()) 3061 { 3062 TIntermBinary *binaryTerminal = node->getExpression()->getAsBinaryNode(); 3063 TIntermUnary *unaryTerminal = node->getExpression()->getAsUnaryNode(); 3064 3065 if (binaryTerminal) 3066 { 3067 TOperator op = binaryTerminal->getOp(); 3068 TIntermConstantUnion *constant = binaryTerminal->getRight()->getAsConstantUnion(); 3069 3070 if (constant) 3071 { 3072 if (constant->getBasicType() == EbtInt && constant->isScalar()) 3073 { 3074 int value = constant->getIConst(0); 3075 3076 switch (op) 3077 { 3078 case EOpAddAssign: 3079 increment = value; 3080 break; 3081 case EOpSubAssign: 3082 increment = -value; 3083 break; 3084 default: 3085 UNIMPLEMENTED(); 3086 } 3087 } 3088 } 3089 } 3090 else if (unaryTerminal) 3091 { 3092 TOperator op = unaryTerminal->getOp(); 3093 3094 switch (op) 3095 { 3096 case EOpPostIncrement: 3097 increment = 1; 3098 break; 3099 case EOpPostDecrement: 3100 increment = -1; 3101 break; 3102 case EOpPreIncrement: 3103 increment = 1; 3104 break; 3105 case EOpPreDecrement: 3106 increment = -1; 3107 break; 3108 default: 3109 UNIMPLEMENTED(); 3110 } 3111 } 3112 } 3113 3114 if (index != nullptr && comparator != EOpNull && increment != 0) 3115 { 3116 if (comparator == EOpLessThanEqual) 3117 { 3118 comparator = EOpLessThan; 3119 limit += 1; 3120 } 3121 3122 if (comparator == EOpLessThan) 3123 { 3124 int iterations = (limit - initial) / increment; 3125 3126 if (iterations <= MAX_LOOP_ITERATIONS) 3127 { 3128 return false; // Not an excessive loop 3129 } 3130 3131 TIntermSymbol *restoreIndex = mExcessiveLoopIndex; 3132 mExcessiveLoopIndex = index; 3133 3134 out << "{int "; 3135 index->traverse(this); 3136 out << ";\n" 3137 "bool Break"; 3138 index->traverse(this); 3139 out << " = false;\n"; 3140 3141 bool firstLoopFragment = true; 3142 3143 while (iterations > 0) 3144 { 3145 int clampedLimit = initial + increment * std::min(MAX_LOOP_ITERATIONS, iterations); 3146 3147 if (!firstLoopFragment) 3148 { 3149 out << "if (!Break"; 3150 index->traverse(this); 3151 out << ") {\n"; 3152 } 3153 3154 if (iterations <= MAX_LOOP_ITERATIONS) // Last loop fragment 3155 { 3156 mExcessiveLoopIndex = nullptr; // Stops setting the Break flag 3157 } 3158 3159 // for(int index = initial; index < clampedLimit; index += increment) 3160 const char *unroll = 3161 mCurrentFunctionMetadata->hasGradientInCallGraph(node) ? "LOOP" : ""; 3162 3163 out << unroll << " for("; 3164 index->traverse(this); 3165 out << " = "; 3166 out << initial; 3167 3168 out << "; "; 3169 index->traverse(this); 3170 out << " < "; 3171 out << clampedLimit; 3172 3173 out << "; "; 3174 index->traverse(this); 3175 out << " += "; 3176 out << increment; 3177 out << ")\n"; 3178 3179 outputLineDirective(out, node->getLine().first_line); 3180 out << "{\n"; 3181 3182 if (node->getBody()) 3183 { 3184 node->getBody()->traverse(this); 3185 } 3186 3187 outputLineDirective(out, node->getLine().first_line); 3188 out << ";}\n"; 3189 3190 if (!firstLoopFragment) 3191 { 3192 out << "}\n"; 3193 } 3194 3195 firstLoopFragment = false; 3196 3197 initial += MAX_LOOP_ITERATIONS * increment; 3198 iterations -= MAX_LOOP_ITERATIONS; 3199 } 3200 3201 out << "}"; 3202 3203 mExcessiveLoopIndex = restoreIndex; 3204 3205 return true; 3206 } 3207 else 3208 UNIMPLEMENTED(); 3209 } 3210 3211 return false; // Not handled as an excessive loop 3212 } 3213 3214 void OutputHLSL::outputTriplet(TInfoSinkBase &out, 3215 Visit visit, 3216 const char *preString, 3217 const char *inString, 3218 const char *postString) 3219 { 3220 if (visit == PreVisit) 3221 { 3222 out << preString; 3223 } 3224 else if (visit == InVisit) 3225 { 3226 out << inString; 3227 } 3228 else if (visit == PostVisit) 3229 { 3230 out << postString; 3231 } 3232 } 3233 3234 void OutputHLSL::outputLineDirective(TInfoSinkBase &out, int line) 3235 { 3236 if (mCompileOptions.lineDirectives && line > 0) 3237 { 3238 out << "\n"; 3239 out << "#line " << line; 3240 3241 if (mSourcePath) 3242 { 3243 out << " \"" << mSourcePath << "\""; 3244 } 3245 3246 out << "\n"; 3247 } 3248 } 3249 3250 void OutputHLSL::writeParameter(const TVariable *param, TInfoSinkBase &out) 3251 { 3252 const TType &type = param->getType(); 3253 TQualifier qualifier = type.getQualifier(); 3254 3255 TString nameStr = DecorateVariableIfNeeded(*param); 3256 ASSERT(nameStr != ""); // HLSL demands named arguments, also for prototypes 3257 3258 if (IsSampler(type.getBasicType())) 3259 { 3260 if (mOutputType == SH_HLSL_4_1_OUTPUT) 3261 { 3262 // Samplers are passed as indices to the sampler array. 3263 ASSERT(qualifier != EvqParamOut && qualifier != EvqParamInOut); 3264 out << "const uint " << nameStr << ArrayString(type); 3265 return; 3266 } 3267 if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT) 3268 { 3269 out << QualifierString(qualifier) << " " << TextureString(type.getBasicType()) 3270 << " texture_" << nameStr << ArrayString(type) << ", " << QualifierString(qualifier) 3271 << " " << SamplerString(type.getBasicType()) << " sampler_" << nameStr 3272 << ArrayString(type); 3273 return; 3274 } 3275 } 3276 3277 // If the parameter is an atomic counter, we need to add an extra parameter to keep track of the 3278 // buffer offset. 3279 if (IsAtomicCounter(type.getBasicType())) 3280 { 3281 out << QualifierString(qualifier) << " " << TypeString(type) << " " << nameStr << ", int " 3282 << nameStr << "_offset"; 3283 } 3284 else 3285 { 3286 out << QualifierString(qualifier) << " " << TypeString(type) << " " << nameStr 3287 << ArrayString(type); 3288 } 3289 3290 // If the structure parameter contains samplers, they need to be passed into the function as 3291 // separate parameters. HLSL doesn't natively support samplers in structs. 3292 if (type.isStructureContainingSamplers()) 3293 { 3294 ASSERT(qualifier != EvqParamOut && qualifier != EvqParamInOut); 3295 TVector<const TVariable *> samplerSymbols; 3296 std::string namePrefix = "angle"; 3297 namePrefix += nameStr.c_str(); 3298 type.createSamplerSymbols(ImmutableString(namePrefix), "", &samplerSymbols, nullptr, 3299 mSymbolTable); 3300 for (const TVariable *sampler : samplerSymbols) 3301 { 3302 const TType &samplerType = sampler->getType(); 3303 if (mOutputType == SH_HLSL_4_1_OUTPUT) 3304 { 3305 out << ", const uint " << sampler->name() << ArrayString(samplerType); 3306 } 3307 else if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT) 3308 { 3309 ASSERT(IsSampler(samplerType.getBasicType())); 3310 out << ", " << QualifierString(qualifier) << " " 3311 << TextureString(samplerType.getBasicType()) << " texture_" << sampler->name() 3312 << ArrayString(samplerType) << ", " << QualifierString(qualifier) << " " 3313 << SamplerString(samplerType.getBasicType()) << " sampler_" << sampler->name() 3314 << ArrayString(samplerType); 3315 } 3316 else 3317 { 3318 ASSERT(IsSampler(samplerType.getBasicType())); 3319 out << ", " << QualifierString(qualifier) << " " << TypeString(samplerType) << " " 3320 << sampler->name() << ArrayString(samplerType); 3321 } 3322 } 3323 } 3324 } 3325 3326 TString OutputHLSL::zeroInitializer(const TType &type) const 3327 { 3328 TString string; 3329 3330 size_t size = type.getObjectSize(); 3331 if (size >= kZeroCount) 3332 { 3333 mUseZeroArray = true; 3334 } 3335 string = GetZeroInitializer(size).c_str(); 3336 3337 return "{" + string + "}"; 3338 } 3339 3340 void OutputHLSL::outputConstructor(TInfoSinkBase &out, Visit visit, TIntermAggregate *node) 3341 { 3342 // Array constructors should have been already pruned from the code. 3343 ASSERT(!node->getType().isArray()); 3344 3345 if (visit == PreVisit) 3346 { 3347 TString constructorName; 3348 if (node->getBasicType() == EbtStruct) 3349 { 3350 constructorName = mStructureHLSL->addStructConstructor(*node->getType().getStruct()); 3351 } 3352 else 3353 { 3354 constructorName = 3355 mStructureHLSL->addBuiltInConstructor(node->getType(), node->getSequence()); 3356 } 3357 out << constructorName << "("; 3358 } 3359 else if (visit == InVisit) 3360 { 3361 out << ", "; 3362 } 3363 else if (visit == PostVisit) 3364 { 3365 out << ")"; 3366 } 3367 } 3368 3369 const TConstantUnion *OutputHLSL::writeConstantUnion(TInfoSinkBase &out, 3370 const TType &type, 3371 const TConstantUnion *const constUnion) 3372 { 3373 ASSERT(!type.isArray()); 3374 3375 const TConstantUnion *constUnionIterated = constUnion; 3376 3377 const TStructure *structure = type.getStruct(); 3378 if (structure) 3379 { 3380 out << mStructureHLSL->addStructConstructor(*structure) << "("; 3381 3382 const TFieldList &fields = structure->fields(); 3383 3384 for (size_t i = 0; i < fields.size(); i++) 3385 { 3386 const TType *fieldType = fields[i]->type(); 3387 constUnionIterated = writeConstantUnion(out, *fieldType, constUnionIterated); 3388 3389 if (i != fields.size() - 1) 3390 { 3391 out << ", "; 3392 } 3393 } 3394 3395 out << ")"; 3396 } 3397 else 3398 { 3399 size_t size = type.getObjectSize(); 3400 bool writeType = size > 1; 3401 3402 if (writeType) 3403 { 3404 out << TypeString(type) << "("; 3405 } 3406 constUnionIterated = writeConstantUnionArray(out, constUnionIterated, size); 3407 if (writeType) 3408 { 3409 out << ")"; 3410 } 3411 } 3412 3413 return constUnionIterated; 3414 } 3415 3416 void OutputHLSL::writeEmulatedFunctionTriplet(TInfoSinkBase &out, 3417 Visit visit, 3418 const TFunction *function) 3419 { 3420 if (visit == PreVisit) 3421 { 3422 ASSERT(function != nullptr); 3423 BuiltInFunctionEmulator::WriteEmulatedFunctionName(out, function->name().data()); 3424 out << "("; 3425 } 3426 else 3427 { 3428 outputTriplet(out, visit, nullptr, ", ", ")"); 3429 } 3430 } 3431 3432 bool OutputHLSL::writeSameSymbolInitializer(TInfoSinkBase &out, 3433 TIntermSymbol *symbolNode, 3434 TIntermTyped *expression) 3435 { 3436 ASSERT(symbolNode->variable().symbolType() != SymbolType::Empty); 3437 const TIntermSymbol *symbolInInitializer = FindSymbolNode(expression, symbolNode->getName()); 3438 3439 if (symbolInInitializer) 3440 { 3441 // Type already printed 3442 out << "t" + str(mUniqueIndex) + " = "; 3443 expression->traverse(this); 3444 out << ", "; 3445 symbolNode->traverse(this); 3446 out << " = t" + str(mUniqueIndex); 3447 3448 mUniqueIndex++; 3449 return true; 3450 } 3451 3452 return false; 3453 } 3454 3455 bool OutputHLSL::writeConstantInitialization(TInfoSinkBase &out, 3456 TIntermSymbol *symbolNode, 3457 TIntermTyped *initializer) 3458 { 3459 if (initializer->hasConstantValue()) 3460 { 3461 symbolNode->traverse(this); 3462 out << ArrayString(symbolNode->getType()); 3463 out << " = {"; 3464 writeConstantUnionArray(out, initializer->getConstantValue(), 3465 initializer->getType().getObjectSize()); 3466 out << "}"; 3467 return true; 3468 } 3469 return false; 3470 } 3471 3472 TString OutputHLSL::addStructEqualityFunction(const TStructure &structure) 3473 { 3474 const TFieldList &fields = structure.fields(); 3475 3476 for (const auto &eqFunction : mStructEqualityFunctions) 3477 { 3478 if (eqFunction->structure == &structure) 3479 { 3480 return eqFunction->functionName; 3481 } 3482 } 3483 3484 const TString &structNameString = StructNameString(structure); 3485 3486 StructEqualityFunction *function = new StructEqualityFunction(); 3487 function->structure = &structure; 3488 function->functionName = "angle_eq_" + structNameString; 3489 3490 TInfoSinkBase fnOut; 3491 3492 fnOut << "bool " << function->functionName << "(" << structNameString << " a, " 3493 << structNameString + " b)\n" 3494 << "{\n" 3495 " return "; 3496 3497 for (size_t i = 0; i < fields.size(); i++) 3498 { 3499 const TField *field = fields[i]; 3500 const TType *fieldType = field->type(); 3501 3502 const TString &fieldNameA = "a." + Decorate(field->name()); 3503 const TString &fieldNameB = "b." + Decorate(field->name()); 3504 3505 if (i > 0) 3506 { 3507 fnOut << " && "; 3508 } 3509 3510 fnOut << "("; 3511 outputEqual(PreVisit, *fieldType, EOpEqual, fnOut); 3512 fnOut << fieldNameA; 3513 outputEqual(InVisit, *fieldType, EOpEqual, fnOut); 3514 fnOut << fieldNameB; 3515 outputEqual(PostVisit, *fieldType, EOpEqual, fnOut); 3516 fnOut << ")"; 3517 } 3518 3519 fnOut << ";\n" 3520 << "}\n"; 3521 3522 function->functionDefinition = fnOut.c_str(); 3523 3524 mStructEqualityFunctions.push_back(function); 3525 mEqualityFunctions.push_back(function); 3526 3527 return function->functionName; 3528 } 3529 3530 TString OutputHLSL::addArrayEqualityFunction(const TType &type) 3531 { 3532 for (const auto &eqFunction : mArrayEqualityFunctions) 3533 { 3534 if (eqFunction->type == type) 3535 { 3536 return eqFunction->functionName; 3537 } 3538 } 3539 3540 TType elementType(type); 3541 elementType.toArrayElementType(); 3542 3543 ArrayHelperFunction *function = new ArrayHelperFunction(); 3544 function->type = type; 3545 3546 function->functionName = ArrayHelperFunctionName("angle_eq", type); 3547 3548 TInfoSinkBase fnOut; 3549 3550 const TString &typeName = TypeString(type); 3551 fnOut << "bool " << function->functionName << "(" << typeName << " a" << ArrayString(type) 3552 << ", " << typeName << " b" << ArrayString(type) << ")\n" 3553 << "{\n" 3554 " for (int i = 0; i < " 3555 << type.getOutermostArraySize() 3556 << "; ++i)\n" 3557 " {\n" 3558 " if ("; 3559 3560 outputEqual(PreVisit, elementType, EOpNotEqual, fnOut); 3561 fnOut << "a[i]"; 3562 outputEqual(InVisit, elementType, EOpNotEqual, fnOut); 3563 fnOut << "b[i]"; 3564 outputEqual(PostVisit, elementType, EOpNotEqual, fnOut); 3565 3566 fnOut << ") { return false; }\n" 3567 " }\n" 3568 " return true;\n" 3569 "}\n"; 3570 3571 function->functionDefinition = fnOut.c_str(); 3572 3573 mArrayEqualityFunctions.push_back(function); 3574 mEqualityFunctions.push_back(function); 3575 3576 return function->functionName; 3577 } 3578 3579 TString OutputHLSL::addArrayAssignmentFunction(const TType &type) 3580 { 3581 for (const auto &assignFunction : mArrayAssignmentFunctions) 3582 { 3583 if (assignFunction.type == type) 3584 { 3585 return assignFunction.functionName; 3586 } 3587 } 3588 3589 TType elementType(type); 3590 elementType.toArrayElementType(); 3591 3592 ArrayHelperFunction function; 3593 function.type = type; 3594 3595 function.functionName = ArrayHelperFunctionName("angle_assign", type); 3596 3597 TInfoSinkBase fnOut; 3598 3599 const TString &typeName = TypeString(type); 3600 fnOut << "void " << function.functionName << "(out " << typeName << " a" << ArrayString(type) 3601 << ", " << typeName << " b" << ArrayString(type) << ")\n" 3602 << "{\n" 3603 " for (int i = 0; i < " 3604 << type.getOutermostArraySize() 3605 << "; ++i)\n" 3606 " {\n" 3607 " "; 3608 3609 outputAssign(PreVisit, elementType, fnOut); 3610 fnOut << "a[i]"; 3611 outputAssign(InVisit, elementType, fnOut); 3612 fnOut << "b[i]"; 3613 outputAssign(PostVisit, elementType, fnOut); 3614 3615 fnOut << ";\n" 3616 " }\n" 3617 "}\n"; 3618 3619 function.functionDefinition = fnOut.c_str(); 3620 3621 mArrayAssignmentFunctions.push_back(function); 3622 3623 return function.functionName; 3624 } 3625 3626 TString OutputHLSL::addArrayConstructIntoFunction(const TType &type) 3627 { 3628 for (const auto &constructIntoFunction : mArrayConstructIntoFunctions) 3629 { 3630 if (constructIntoFunction.type == type) 3631 { 3632 return constructIntoFunction.functionName; 3633 } 3634 } 3635 3636 TType elementType(type); 3637 elementType.toArrayElementType(); 3638 3639 ArrayHelperFunction function; 3640 function.type = type; 3641 3642 function.functionName = ArrayHelperFunctionName("angle_construct_into", type); 3643 3644 TInfoSinkBase fnOut; 3645 3646 const TString &typeName = TypeString(type); 3647 fnOut << "void " << function.functionName << "(out " << typeName << " a" << ArrayString(type); 3648 for (unsigned int i = 0u; i < type.getOutermostArraySize(); ++i) 3649 { 3650 fnOut << ", " << typeName << " b" << i << ArrayString(elementType); 3651 } 3652 fnOut << ")\n" 3653 "{\n"; 3654 3655 for (unsigned int i = 0u; i < type.getOutermostArraySize(); ++i) 3656 { 3657 fnOut << " "; 3658 outputAssign(PreVisit, elementType, fnOut); 3659 fnOut << "a[" << i << "]"; 3660 outputAssign(InVisit, elementType, fnOut); 3661 fnOut << "b" << i; 3662 outputAssign(PostVisit, elementType, fnOut); 3663 fnOut << ";\n"; 3664 } 3665 fnOut << "}\n"; 3666 3667 function.functionDefinition = fnOut.c_str(); 3668 3669 mArrayConstructIntoFunctions.push_back(function); 3670 3671 return function.functionName; 3672 } 3673 3674 void OutputHLSL::ensureStructDefined(const TType &type) 3675 { 3676 const TStructure *structure = type.getStruct(); 3677 if (structure) 3678 { 3679 ASSERT(type.getBasicType() == EbtStruct); 3680 mStructureHLSL->ensureStructDefined(*structure); 3681 } 3682 } 3683 3684 bool OutputHLSL::shaderNeedsGenerateOutput() const 3685 { 3686 return mShaderType == GL_VERTEX_SHADER || mShaderType == GL_FRAGMENT_SHADER; 3687 } 3688 3689 const char *OutputHLSL::generateOutputCall() const 3690 { 3691 if (mShaderType == GL_VERTEX_SHADER) 3692 { 3693 return "generateOutput(input)"; 3694 } 3695 else 3696 { 3697 return "generateOutput()"; 3698 } 3699 } 3700 } // namespace sh