ReplaceClipCullDistanceVariable.cpp (22288B)
1 // 2 // Copyright 2020 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 // ReplaceClipCullDistanceVariable.cpp: Find any references to gl_ClipDistance or gl_CullDistance 7 // and replace it with ANGLEClipDistance or ANGLECullDistance. 8 // 9 10 #include "compiler/translator/tree_util/ReplaceClipCullDistanceVariable.h" 11 12 #include "common/bitset_utils.h" 13 #include "common/debug.h" 14 #include "common/utilities.h" 15 #include "compiler/translator/Compiler.h" 16 #include "compiler/translator/SymbolTable.h" 17 #include "compiler/translator/tree_util/BuiltIn.h" 18 #include "compiler/translator/tree_util/IntermNode_util.h" 19 #include "compiler/translator/tree_util/IntermTraverse.h" 20 #include "compiler/translator/tree_util/ReplaceVariable.h" 21 #include "compiler/translator/tree_util/RunAtTheBeginningOfShader.h" 22 #include "compiler/translator/tree_util/RunAtTheEndOfShader.h" 23 24 namespace sh 25 { 26 namespace 27 { 28 29 using ClipCullDistanceIdxSet = angle::BitSet<32>; 30 31 typedef TIntermNode *AssignFunc(const unsigned int index, 32 TIntermSymbol *left, 33 TIntermSymbol *right, 34 const TIntermTyped *enableFlags); 35 36 template <typename Variable> 37 const Variable *FindVariable(const std::vector<Variable> &mVars, const ImmutableString &name) 38 { 39 for (const Variable &var : mVars) 40 { 41 if (name == var.instanceName) 42 { 43 return &var; 44 } 45 } 46 47 return nullptr; 48 } 49 50 // Traverse the tree and collect the redeclaration and all constant index references of 51 // gl_ClipDistance/gl_CullDistance 52 class GLClipCullDistanceReferenceTraverser : public TIntermTraverser 53 { 54 public: 55 GLClipCullDistanceReferenceTraverser(const TIntermSymbol **redeclaredSymOut, 56 bool *nonConstIdxUsedOut, 57 unsigned int *maxConstIdxOut, 58 ClipCullDistanceIdxSet *constIndicesOut, 59 TQualifier targetQualifier) 60 : TIntermTraverser(true, false, false), 61 mRedeclaredSym(redeclaredSymOut), 62 mUseNonConstClipCullDistanceIndex(nonConstIdxUsedOut), 63 mMaxConstClipCullDistanceIndex(maxConstIdxOut), 64 mConstClipCullDistanceIndices(constIndicesOut), 65 mTargetQualifier(targetQualifier) 66 { 67 *mRedeclaredSym = nullptr; 68 *mUseNonConstClipCullDistanceIndex = false; 69 *mMaxConstClipCullDistanceIndex = 0; 70 mConstClipCullDistanceIndices->reset(); 71 } 72 73 bool visitDeclaration(Visit visit, TIntermDeclaration *node) override 74 { 75 // If gl_ClipDistance/gl_CullDistance is redeclared, we need to collect its information 76 const TIntermSequence &sequence = *(node->getSequence()); 77 78 if (sequence.size() != 1) 79 { 80 return true; 81 } 82 83 TIntermSymbol *variable = sequence.front()->getAsSymbolNode(); 84 if (variable == nullptr || variable->getType().getQualifier() != mTargetQualifier) 85 { 86 return true; 87 } 88 89 *mRedeclaredSym = variable->getAsSymbolNode(); 90 91 return true; 92 } 93 94 bool visitBinary(Visit visit, TIntermBinary *node) override 95 { 96 TOperator op = node->getOp(); 97 if (op != EOpIndexDirect && op != EOpIndexIndirect) 98 { 99 return true; 100 } 101 102 // gl_ClipDistance / gl_CullDistance 103 TIntermTyped *left = node->getLeft()->getAsTyped(); 104 if (!left) 105 { 106 return true; 107 } 108 109 ASSERT(op == EOpIndexDirect || op == EOpIndexIndirect); 110 111 TIntermSymbol *clipCullDistance = left->getAsSymbolNode(); 112 if (!clipCullDistance) 113 { 114 return true; 115 } 116 if (clipCullDistance->getType().getQualifier() != mTargetQualifier) 117 { 118 return true; 119 } 120 121 const TConstantUnion *constIdx = node->getRight()->getConstantValue(); 122 if (!constIdx) 123 { 124 *mUseNonConstClipCullDistanceIndex = true; 125 } 126 else 127 { 128 unsigned int idx = 0; 129 switch (constIdx->getType()) 130 { 131 case EbtInt: 132 idx = constIdx->getIConst(); 133 break; 134 case EbtUInt: 135 idx = constIdx->getUConst(); 136 break; 137 case EbtFloat: 138 idx = static_cast<unsigned int>(constIdx->getFConst()); 139 break; 140 case EbtBool: 141 idx = constIdx->getBConst() ? 1 : 0; 142 break; 143 default: 144 UNREACHABLE(); 145 break; 146 } 147 ASSERT(idx < mConstClipCullDistanceIndices->size()); 148 mConstClipCullDistanceIndices->set(idx); 149 150 *mMaxConstClipCullDistanceIndex = std::max(*mMaxConstClipCullDistanceIndex, idx); 151 } 152 153 return true; 154 } 155 156 private: 157 const TIntermSymbol **mRedeclaredSym; 158 // Flag indicating whether there is at least one reference of gl_ClipDistance with non-constant 159 // index 160 bool *mUseNonConstClipCullDistanceIndex; 161 // Max constant index that is used to reference gl_ClipDistance 162 unsigned int *mMaxConstClipCullDistanceIndex; 163 // List of constant index reference of gl_ClipDistance 164 ClipCullDistanceIdxSet *mConstClipCullDistanceIndices; 165 // Qualifier for gl_ClipDistance/gl_CullDistance 166 const TQualifier mTargetQualifier; 167 }; 168 169 // Replace all symbolic occurrences of given variables except one symbol. 170 class ReplaceVariableExceptOneTraverser : public TIntermTraverser 171 { 172 public: 173 ReplaceVariableExceptOneTraverser(const TVariable *toBeReplaced, 174 const TIntermTyped *replacement, 175 const TIntermSymbol *exception) 176 : TIntermTraverser(true, false, false), 177 mToBeReplaced(toBeReplaced), 178 mException(exception), 179 mReplacement(replacement) 180 {} 181 182 void visitSymbol(TIntermSymbol *node) override 183 { 184 if (&node->variable() == mToBeReplaced && node != mException) 185 { 186 queueReplacement(mReplacement->deepCopy(), OriginalNode::IS_DROPPED); 187 } 188 } 189 190 private: 191 const TVariable *const mToBeReplaced; 192 const TIntermSymbol *const mException; 193 const TIntermTyped *const mReplacement; 194 }; 195 196 TIntermNode *simpleAssignFunc(const unsigned int index, 197 TIntermSymbol *leftSymbol, 198 TIntermSymbol *rightSymbol, 199 const TIntermTyped * /*enableFlags*/) 200 { 201 // leftSymbol[index] = rightSymbol[index] 202 // E.g., ANGLEClipDistance[index] = gl_ClipDistance[index] 203 TIntermBinary *left = 204 new TIntermBinary(EOpIndexDirect, leftSymbol->deepCopy(), CreateIndexNode(index)); 205 TIntermBinary *right = 206 new TIntermBinary(EOpIndexDirect, rightSymbol->deepCopy(), CreateIndexNode(index)); 207 208 return new TIntermBinary(EOpAssign, left, right); 209 } 210 211 // This is only used for gl_ClipDistance 212 TIntermNode *assignFuncWithEnableFlags(const unsigned int index, 213 TIntermSymbol *leftSymbol, 214 TIntermSymbol *rightSymbol, 215 const TIntermTyped *enableFlags) 216 { 217 // if (ANGLEUniforms.clipDistancesEnabled & (0x1 << index)) 218 // gl_ClipDistance[index] = ANGLEClipDistance[index]; 219 // else 220 // gl_ClipDistance[index] = 0; 221 TIntermConstantUnion *bitMask = CreateUIntNode(0x1 << index); 222 TIntermBinary *bitwiseAnd = new TIntermBinary(EOpBitwiseAnd, enableFlags->deepCopy(), bitMask); 223 TIntermBinary *nonZero = new TIntermBinary(EOpNotEqual, bitwiseAnd, CreateUIntNode(0)); 224 225 TIntermBinary *left = 226 new TIntermBinary(EOpIndexDirect, leftSymbol->deepCopy(), CreateIndexNode(index)); 227 TIntermBinary *right = 228 new TIntermBinary(EOpIndexDirect, rightSymbol->deepCopy(), CreateIndexNode(index)); 229 TIntermBinary *assignment = new TIntermBinary(EOpAssign, left, right); 230 TIntermBlock *trueBlock = new TIntermBlock(); 231 trueBlock->appendStatement(assignment); 232 233 TIntermBinary *zeroAssignment = 234 new TIntermBinary(EOpAssign, left->deepCopy(), CreateFloatNode(0, EbpMedium)); 235 TIntermBlock *falseBlock = new TIntermBlock(); 236 falseBlock->appendStatement(zeroAssignment); 237 238 return new TIntermIfElse(nonZero, trueBlock, falseBlock); 239 } 240 241 class ReplaceClipCullDistanceAssignments : angle::NonCopyable 242 { 243 public: 244 ReplaceClipCullDistanceAssignments(TCompiler *compiler, 245 TIntermBlock *root, 246 TSymbolTable *symbolTable, 247 const TVariable *glClipCullDistanceVar, 248 const TIntermSymbol *redeclaredGlClipDistance, 249 const ImmutableString &angleVarName) 250 : mCompiler(compiler), 251 mRoot(root), 252 mSymbolTable(symbolTable), 253 mGlVar(glClipCullDistanceVar), 254 mRedeclaredGLVar(redeclaredGlClipDistance), 255 mANGLEVarName(angleVarName) 256 { 257 mEnabledDistances = 0; 258 } 259 260 unsigned int getEnabledClipCullDistance(const bool useNonConstIndex, 261 const unsigned int maxConstIndex); 262 const TVariable *declareANGLEVariable(const TVariable *originalVariable); 263 bool assignOriginalValueToANGLEVariable(const GLenum shaderType); 264 bool assignANGLEValueToOriginalVariable(const GLenum shaderType, 265 const bool isRedeclared, 266 const TIntermTyped *enableFlags, 267 const ClipCullDistanceIdxSet *constIndices); 268 269 private: 270 bool assignOriginalValueToANGLEVariableImpl(); 271 bool assignANGLEValueToOriginalVariableImpl(const bool isRedeclared, 272 const TIntermTyped *enableFlags, 273 const ClipCullDistanceIdxSet *constIndices, 274 AssignFunc assignFunc); 275 276 // Common variables for replacing gl_Clip/CullDistances with ANGLEClip/CullDistances 277 TCompiler *mCompiler; 278 TIntermBlock *mRoot; 279 TSymbolTable *mSymbolTable; 280 281 const TVariable *mGlVar; 282 const TIntermSymbol *mRedeclaredGLVar; 283 const ImmutableString mANGLEVarName; 284 285 unsigned int mEnabledDistances; 286 const TVariable *mANGLEVar; 287 }; 288 289 unsigned int ReplaceClipCullDistanceAssignments::getEnabledClipCullDistance( 290 const bool useNonConstIndex, 291 const unsigned int maxConstIndex) 292 { 293 if (mRedeclaredGLVar) 294 { 295 // If array is redeclared by user, use that redeclared size. 296 mEnabledDistances = mRedeclaredGLVar->getType().getOutermostArraySize(); 297 } 298 else if (!useNonConstIndex) 299 { 300 ASSERT(maxConstIndex < mGlVar->getType().getOutermostArraySize()); 301 // Only use constant index, then use max array index used. 302 mEnabledDistances = maxConstIndex + 1; 303 } 304 305 return mEnabledDistances; 306 } 307 308 const TVariable *ReplaceClipCullDistanceAssignments::declareANGLEVariable( 309 const TVariable *originalVariable) 310 { 311 ASSERT(mEnabledDistances > 0); 312 313 TType *clipCullDistanceType = new TType(originalVariable->getType()); 314 clipCullDistanceType->setQualifier(EvqGlobal); 315 clipCullDistanceType->toArrayBaseType(); 316 clipCullDistanceType->makeArray(mEnabledDistances); 317 318 mANGLEVar = 319 new TVariable(mSymbolTable, mANGLEVarName, clipCullDistanceType, SymbolType::AngleInternal); 320 321 TIntermSymbol *clipCullDistanceDeclarator = new TIntermSymbol(mANGLEVar); 322 TIntermDeclaration *clipCullDistanceDecl = new TIntermDeclaration; 323 clipCullDistanceDecl->appendDeclarator(clipCullDistanceDeclarator); 324 325 // Must declare ANGLEClipdistance/ANGLECullDistance before any function, since 326 // gl_ClipDistance/gl_CullDistance might be accessed within a function declared before main. 327 mRoot->insertStatement(0, clipCullDistanceDecl); 328 329 return mANGLEVar; 330 } 331 332 bool ReplaceClipCullDistanceAssignments::assignOriginalValueToANGLEVariableImpl() 333 { 334 ASSERT(mEnabledDistances > 0); 335 336 TIntermBlock *readBlock = new TIntermBlock; 337 TIntermSymbol *glClipCullDistanceSymbol = new TIntermSymbol(mGlVar); 338 TIntermSymbol *clipCullDistanceSymbol = new TIntermSymbol(mANGLEVar); 339 340 for (unsigned int i = 0; i < mEnabledDistances; i++) 341 { 342 readBlock->appendStatement( 343 simpleAssignFunc(i, clipCullDistanceSymbol, glClipCullDistanceSymbol, nullptr)); 344 } 345 346 return RunAtTheBeginningOfShader(mCompiler, mRoot, readBlock); 347 } 348 349 bool ReplaceClipCullDistanceAssignments::assignANGLEValueToOriginalVariableImpl( 350 const bool isRedeclared, 351 const TIntermTyped *enableFlags, 352 const ClipCullDistanceIdxSet *constIndices, 353 AssignFunc assignFunc) 354 { 355 ASSERT(mEnabledDistances > 0); 356 357 TIntermBlock *assignBlock = new TIntermBlock; 358 TIntermSymbol *glClipCullDistanceSymbol = new TIntermSymbol(mGlVar); 359 TIntermSymbol *clipCullDistanceSymbol = new TIntermSymbol(mANGLEVar); 360 361 // The array size is decided by either redeclaring the variable or accessing the variable with a 362 // integral constant index. And this size is the count of the enabled value. So, if the index 363 // which is greater than the array size, is used to access the variable, this access will be 364 // ignored. 365 if (isRedeclared || !constIndices) 366 { 367 for (unsigned int i = 0; i < mEnabledDistances; ++i) 368 { 369 assignBlock->appendStatement( 370 assignFunc(i, glClipCullDistanceSymbol, clipCullDistanceSymbol, enableFlags)); 371 } 372 } 373 else 374 { 375 // Assign ANGLEClip/CullDistance[i]'s value to gl_Clip/CullDistance[i] if i is in the 376 // constant indices list. Those elements whose index is not in the constant index list will 377 // be zeroise for initialization. 378 for (unsigned int i = 0; i < mEnabledDistances; ++i) 379 { 380 if (constIndices->test(i)) 381 { 382 assignBlock->appendStatement( 383 assignFunc(i, glClipCullDistanceSymbol, clipCullDistanceSymbol, enableFlags)); 384 } 385 else 386 { 387 // gl_Clip/CullDistance[i] = 0; 388 TIntermBinary *left = new TIntermBinary( 389 EOpIndexDirect, glClipCullDistanceSymbol->deepCopy(), CreateIndexNode(i)); 390 TIntermBinary *zeroAssignment = 391 new TIntermBinary(EOpAssign, left, CreateFloatNode(0, EbpMedium)); 392 assignBlock->appendStatement(zeroAssignment); 393 } 394 } 395 } 396 397 return RunAtTheEndOfShader(mCompiler, mRoot, assignBlock, mSymbolTable); 398 } 399 400 [[nodiscard]] bool ReplaceClipCullDistanceAssignments::assignOriginalValueToANGLEVariable( 401 const GLenum shaderType) 402 { 403 switch (shaderType) 404 { 405 case GL_VERTEX_SHADER: 406 // Vertex shader can use gl_Clip/CullDistance as a output only 407 break; 408 case GL_FRAGMENT_SHADER: 409 { 410 // These shader types can use gl_Clip/CullDistance as input 411 if (!assignOriginalValueToANGLEVariableImpl()) 412 { 413 return false; 414 } 415 break; 416 } 417 default: 418 { 419 UNREACHABLE(); 420 return false; 421 } 422 } 423 424 return true; 425 } 426 427 [[nodiscard]] bool ReplaceClipCullDistanceAssignments::assignANGLEValueToOriginalVariable( 428 const GLenum shaderType, 429 const bool isRedeclared, 430 const TIntermTyped *enableFlags, 431 const ClipCullDistanceIdxSet *constIndices) 432 { 433 switch (shaderType) 434 { 435 case GL_VERTEX_SHADER: 436 { 437 // Vertex shader can use gl_Clip/CullDistance as output. 438 // If the enabled gl_Clip/CullDistances are not initialized, results are undefined. 439 // EXT_clip_cull_distance spec : 440 // The shader must also set all values in gl_ClipDistance that have been enabled via the 441 // OpenGL ES API, or results are undefined. Values written into gl_ClipDistance for 442 // planes that are not enabled have no effect. 443 // ... 444 // Shaders writing gl_CullDistance must write all enabled distances, or culling results 445 // are undefined. 446 if (!assignANGLEValueToOriginalVariableImpl( 447 isRedeclared, enableFlags, constIndices, 448 enableFlags ? assignFuncWithEnableFlags : simpleAssignFunc)) 449 { 450 return false; 451 } 452 break; 453 } 454 case GL_FRAGMENT_SHADER: 455 // Fragment shader can use gl_Clip/CullDistance as input only 456 break; 457 default: 458 { 459 UNREACHABLE(); 460 return false; 461 } 462 } 463 464 return true; 465 } 466 467 // Common code to transform gl_ClipDistance and gl_CullDistance. Comments reference 468 // gl_ClipDistance, but are also applicable to gl_CullDistance. 469 [[nodiscard]] bool ReplaceClipCullDistanceAssignmentsImpl( 470 TCompiler *compiler, 471 TIntermBlock *root, 472 TSymbolTable *symbolTable, 473 const GLenum shaderType, 474 const TIntermTyped *clipDistanceEnableFlags, 475 const char *builtInName, 476 const char *replacementName, 477 TQualifier builtInQualifier) 478 { 479 // Collect all constant index references of gl_ClipDistance 480 ImmutableString name(builtInName); 481 ClipCullDistanceIdxSet constIndices; 482 bool useNonConstIndex = false; 483 const TIntermSymbol *redeclaredBuiltIn = nullptr; 484 unsigned int maxConstIndex = 0; 485 GLClipCullDistanceReferenceTraverser indexTraverser( 486 &redeclaredBuiltIn, &useNonConstIndex, &maxConstIndex, &constIndices, builtInQualifier); 487 root->traverse(&indexTraverser); 488 if (!useNonConstIndex && constIndices.none()) 489 { 490 // No references of gl_ClipDistance 491 return true; 492 } 493 494 // Retrieve gl_ClipDistance variable reference 495 // Search user redeclared gl_ClipDistance first 496 const TVariable *builtInVar = nullptr; 497 if (redeclaredBuiltIn) 498 { 499 builtInVar = &redeclaredBuiltIn->variable(); 500 } 501 else 502 { 503 // User defined not found, find in built-in table 504 builtInVar = static_cast<const TVariable *>( 505 symbolTable->findBuiltIn(name, compiler->getShaderVersion())); 506 } 507 if (!builtInVar) 508 { 509 return false; 510 } 511 512 ReplaceClipCullDistanceAssignments replacementUtils(compiler, root, symbolTable, builtInVar, 513 redeclaredBuiltIn, 514 ImmutableString(replacementName)); 515 516 // Declare a global variable substituting gl_ClipDistance 517 unsigned int enabledClipDistances = 518 replacementUtils.getEnabledClipCullDistance(useNonConstIndex, maxConstIndex); 519 if (!enabledClipDistances) 520 { 521 // Spec : 522 // The gl_ClipDistance array is predeclared as unsized and must be explicitly sized by the 523 // shader either redeclaring it with a size or implicitly sized by indexing it only with 524 // integral constant expressions. 525 return false; 526 } 527 528 const TVariable *replacementVar = replacementUtils.declareANGLEVariable(builtInVar); 529 530 // Replace gl_ClipDistance reference with ANGLEClipDistance, except the declaration 531 ReplaceVariableExceptOneTraverser replaceTraverser(builtInVar, 532 new TIntermSymbol(replacementVar), 533 /** exception */ redeclaredBuiltIn); 534 root->traverse(&replaceTraverser); 535 if (!replaceTraverser.updateTree(compiler, root)) 536 { 537 return false; 538 } 539 540 // Read gl_ClipDistance to ANGLEClipDistance for getting a original data 541 if (!replacementUtils.assignOriginalValueToANGLEVariable(shaderType)) 542 { 543 return false; 544 } 545 546 // Reassign ANGLEClipDistance to gl_ClipDistance but ignore those that are disabled 547 const bool isRedeclared = redeclaredBuiltIn != nullptr; 548 if (!replacementUtils.assignANGLEValueToOriginalVariable( 549 shaderType, isRedeclared, clipDistanceEnableFlags, &constIndices)) 550 { 551 return false; 552 } 553 554 // If not redeclared, replace the built-in with one that is appropriately sized 555 if (!isRedeclared) 556 { 557 TType *resizedType = new TType(builtInVar->getType()); 558 resizedType->setArraySize(0, enabledClipDistances); 559 560 TVariable *resizedVar = new TVariable(symbolTable, name, resizedType, SymbolType::BuiltIn); 561 562 return ReplaceVariable(compiler, root, builtInVar, resizedVar); 563 } 564 565 return true; 566 } 567 568 } // anonymous namespace 569 570 [[nodiscard]] bool ReplaceClipDistanceAssignments(TCompiler *compiler, 571 TIntermBlock *root, 572 TSymbolTable *symbolTable, 573 const GLenum shaderType, 574 const TIntermTyped *clipDistanceEnableFlags) 575 { 576 return ReplaceClipCullDistanceAssignmentsImpl(compiler, root, symbolTable, shaderType, 577 clipDistanceEnableFlags, "gl_ClipDistance", 578 "ANGLEClipDistance", EvqClipDistance); 579 } 580 581 [[nodiscard]] bool ReplaceCullDistanceAssignments(TCompiler *compiler, 582 TIntermBlock *root, 583 TSymbolTable *symbolTable, 584 const GLenum shaderType) 585 { 586 return ReplaceClipCullDistanceAssignmentsImpl(compiler, root, symbolTable, shaderType, nullptr, 587 "gl_CullDistance", "ANGLECullDistance", 588 EvqCullDistance); 589 } 590 591 } // namespace sh