1 // 2 // Copyright (C) 2013 LunarG, Inc. 3 // 4 // All rights reserved. 5 // 6 // Redistribution and use in source and binary forms, with or without 7 // modification, are permitted provided that the following conditions 8 // are met: 9 // 10 // Redistributions of source code must retain the above copyright 11 // notice, this list of conditions and the following disclaimer. 12 // 13 // Redistributions in binary form must reproduce the above 14 // copyright notice, this list of conditions and the following 15 // disclaimer in the documentation and/or other materials provided 16 // with the distribution. 17 // 18 // Neither the name of 3Dlabs Inc. Ltd. nor the names of its 19 // contributors may be used to endorse or promote products derived 20 // from this software without specific prior written permission. 21 // 22 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26 // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 30 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 32 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 // POSSIBILITY OF SUCH DAMAGE. 34 // 35 36 // 37 // Do link-time merging and validation of intermediate representations. 38 // 39 // Basic model is that during compilation, each compilation unit (shader) is 40 // compiled into one TIntermediate instance. Then, at link time, multiple 41 // units for the same stage can be merged together, which can generate errors. 42 // Then, after all merging, a single instance of TIntermediate represents 43 // the whole stage. A final error check can be done on the resulting stage, 44 // even if no merging was done (i.e., the stage was only one compilation unit). 45 // 46 47 #include "localintermediate.h" 48 #include "../Include/InfoSink.h" 49 50 namespace glslang { 51 52 // 53 // Link-time error emitter. 54 // 55 void TIntermediate::error(TInfoSink& infoSink, const char* message) 56 { 57 infoSink.info.prefix(EPrefixError); 58 infoSink.info << "Linking " << StageName(language) << " stage: " << message << "\n"; 59 60 ++numErrors; 61 } 62 63 // Link-time warning. 64 void TIntermediate::warn(TInfoSink& infoSink, const char* message) 65 { 66 infoSink.info.prefix(EPrefixWarning); 67 infoSink.info << "Linking " << StageName(language) << " stage: " << message << "\n"; 68 } 69 70 // TODO: 4.4 offset/align: "Two blocks linked together in the same program with the same block 71 // name must have the exact same set of members qualified with offset and their integral-constant 72 // expression values must be the same, or a link-time error results." 73 74 // 75 // Merge the information from 'unit' into 'this' 76 // 77 void TIntermediate::merge(TInfoSink& infoSink, TIntermediate& unit) 78 { 79 if (source == EShSourceNone) 80 source = unit.source; 81 82 if (source != unit.source) 83 error(infoSink, "can't link compilation units from different source languages"); 84 85 if (unit.getNumEntryPoints() > 0) { 86 if (getNumEntryPoints() > 0) 87 error(infoSink, "can't handle multiple entry points per stage"); 88 else { 89 entryPointName = unit.getEntryPointName(); 90 entryPointMangledName = unit.getEntryPointMangledName(); 91 } 92 } 93 numEntryPoints += unit.getNumEntryPoints(); 94 numErrors += unit.getNumErrors(); 95 numPushConstants += unit.numPushConstants; 96 callGraph.insert(callGraph.end(), unit.callGraph.begin(), unit.callGraph.end()); 97 98 if (originUpperLeft != unit.originUpperLeft || pixelCenterInteger != unit.pixelCenterInteger) 99 error(infoSink, "gl_FragCoord redeclarations must match across shaders"); 100 101 if (! earlyFragmentTests) 102 earlyFragmentTests = unit.earlyFragmentTests; 103 104 if (!postDepthCoverage) 105 postDepthCoverage = unit.postDepthCoverage; 106 107 if (depthLayout == EldNone) 108 depthLayout = unit.depthLayout; 109 else if (depthLayout != unit.depthLayout) 110 error(infoSink, "Contradictory depth layouts"); 111 112 blendEquations |= unit.blendEquations; 113 114 if (inputPrimitive == ElgNone) 115 inputPrimitive = unit.inputPrimitive; 116 else if (inputPrimitive != unit.inputPrimitive) 117 error(infoSink, "Contradictory input layout primitives"); 118 119 if (outputPrimitive == ElgNone) 120 outputPrimitive = unit.outputPrimitive; 121 else if (outputPrimitive != unit.outputPrimitive) 122 error(infoSink, "Contradictory output layout primitives"); 123 124 if (vertices == TQualifier::layoutNotSet) 125 vertices = unit.vertices; 126 else if (vertices != unit.vertices) { 127 if (language == EShLangGeometry) 128 error(infoSink, "Contradictory layout max_vertices values"); 129 else if (language == EShLangTessControl) 130 error(infoSink, "Contradictory layout vertices values"); 131 else 132 assert(0); 133 } 134 135 if (vertexSpacing == EvsNone) 136 vertexSpacing = unit.vertexSpacing; 137 else if (vertexSpacing != unit.vertexSpacing) 138 error(infoSink, "Contradictory input vertex spacing"); 139 140 if (vertexOrder == EvoNone) 141 vertexOrder = unit.vertexOrder; 142 else if (vertexOrder != unit.vertexOrder) 143 error(infoSink, "Contradictory triangle ordering"); 144 145 if (unit.pointMode) 146 pointMode = true; 147 148 for (int i = 0; i < 3; ++i) { 149 if (localSize[i] > 1) 150 localSize[i] = unit.localSize[i]; 151 else if (localSize[i] != unit.localSize[i]) 152 error(infoSink, "Contradictory local size"); 153 154 if (localSizeSpecId[i] != TQualifier::layoutNotSet) 155 localSizeSpecId[i] = unit.localSizeSpecId[i]; 156 else if (localSizeSpecId[i] != unit.localSizeSpecId[i]) 157 error(infoSink, "Contradictory local size specialization ids"); 158 } 159 160 if (unit.xfbMode) 161 xfbMode = true; 162 for (size_t b = 0; b < xfbBuffers.size(); ++b) { 163 if (xfbBuffers[b].stride == TQualifier::layoutXfbStrideEnd) 164 xfbBuffers[b].stride = unit.xfbBuffers[b].stride; 165 else if (xfbBuffers[b].stride != unit.xfbBuffers[b].stride) 166 error(infoSink, "Contradictory xfb_stride"); 167 xfbBuffers[b].implicitStride = std::max(xfbBuffers[b].implicitStride, unit.xfbBuffers[b].implicitStride); 168 if (unit.xfbBuffers[b].containsDouble) 169 xfbBuffers[b].containsDouble = true; 170 // TODO: 4.4 link: enhanced layouts: compare ranges 171 } 172 173 if (unit.treeRoot == 0) 174 return; 175 176 if (treeRoot == 0) { 177 treeRoot = unit.treeRoot; 178 version = unit.version; 179 requestedExtensions = unit.requestedExtensions; 180 return; 181 } 182 183 // Getting this far means we have two existing trees to merge... 184 185 version = std::max(version, unit.version); 186 requestedExtensions.insert(unit.requestedExtensions.begin(), unit.requestedExtensions.end()); 187 188 // Get the top-level globals of each unit 189 TIntermSequence& globals = treeRoot->getAsAggregate()->getSequence(); 190 TIntermSequence& unitGlobals = unit.treeRoot->getAsAggregate()->getSequence(); 191 192 // Get the linker-object lists 193 TIntermSequence& linkerObjects = findLinkerObjects(); 194 TIntermSequence& unitLinkerObjects = unit.findLinkerObjects(); 195 196 mergeBodies(infoSink, globals, unitGlobals); 197 mergeLinkerObjects(infoSink, linkerObjects, unitLinkerObjects); 198 199 ioAccessed.insert(unit.ioAccessed.begin(), unit.ioAccessed.end()); 200 } 201 202 // 203 // Merge the function bodies and global-level initializers from unitGlobals into globals. 204 // Will error check duplication of function bodies for the same signature. 205 // 206 void TIntermediate::mergeBodies(TInfoSink& infoSink, TIntermSequence& globals, const TIntermSequence& unitGlobals) 207 { 208 // TODO: link-time performance: Processing in alphabetical order will be faster 209 210 // Error check the global objects, not including the linker objects 211 for (unsigned int child = 0; child < globals.size() - 1; ++child) { 212 for (unsigned int unitChild = 0; unitChild < unitGlobals.size() - 1; ++unitChild) { 213 TIntermAggregate* body = globals[child]->getAsAggregate(); 214 TIntermAggregate* unitBody = unitGlobals[unitChild]->getAsAggregate(); 215 if (body && unitBody && body->getOp() == EOpFunction && unitBody->getOp() == EOpFunction && body->getName() == unitBody->getName()) { 216 error(infoSink, "Multiple function bodies in multiple compilation units for the same signature in the same stage:"); 217 infoSink.info << " " << globals[child]->getAsAggregate()->getName() << "\n"; 218 } 219 } 220 } 221 222 // Merge the global objects, just in front of the linker objects 223 globals.insert(globals.end() - 1, unitGlobals.begin(), unitGlobals.end() - 1); 224 } 225 226 // 227 // Merge the linker objects from unitLinkerObjects into linkerObjects. 228 // Duplication is expected and filtered out, but contradictions are an error. 229 // 230 void TIntermediate::mergeLinkerObjects(TInfoSink& infoSink, TIntermSequence& linkerObjects, const TIntermSequence& unitLinkerObjects) 231 { 232 // Error check and merge the linker objects (duplicates should not be created) 233 std::size_t initialNumLinkerObjects = linkerObjects.size(); 234 for (unsigned int unitLinkObj = 0; unitLinkObj < unitLinkerObjects.size(); ++unitLinkObj) { 235 bool merge = true; 236 for (std::size_t linkObj = 0; linkObj < initialNumLinkerObjects; ++linkObj) { 237 TIntermSymbol* symbol = linkerObjects[linkObj]->getAsSymbolNode(); 238 TIntermSymbol* unitSymbol = unitLinkerObjects[unitLinkObj]->getAsSymbolNode(); 239 assert(symbol && unitSymbol); 240 if (symbol->getName() == unitSymbol->getName()) { 241 // filter out copy 242 merge = false; 243 244 // but if one has an initializer and the other does not, update 245 // the initializer 246 if (symbol->getConstArray().empty() && ! unitSymbol->getConstArray().empty()) 247 symbol->setConstArray(unitSymbol->getConstArray()); 248 249 // Similarly for binding 250 if (! symbol->getQualifier().hasBinding() && unitSymbol->getQualifier().hasBinding()) 251 symbol->getQualifier().layoutBinding = unitSymbol->getQualifier().layoutBinding; 252 253 // Update implicit array sizes 254 mergeImplicitArraySizes(symbol->getWritableType(), unitSymbol->getType()); 255 256 // Check for consistent types/qualification/initializers etc. 257 mergeErrorCheck(infoSink, *symbol, *unitSymbol, false); 258 } 259 } 260 if (merge) 261 linkerObjects.push_back(unitLinkerObjects[unitLinkObj]); 262 } 263 } 264 265 // TODO 4.5 link functionality: cull distance array size checking 266 267 // Recursively merge the implicit array sizes through the objects' respective type trees. 268 void TIntermediate::mergeImplicitArraySizes(TType& type, const TType& unitType) 269 { 270 if (type.isImplicitlySizedArray() && unitType.isArray()) { 271 int newImplicitArraySize = unitType.isImplicitlySizedArray() ? unitType.getImplicitArraySize() : unitType.getOuterArraySize(); 272 if (newImplicitArraySize > type.getImplicitArraySize ()) 273 type.setImplicitArraySize(newImplicitArraySize); 274 } 275 276 // Type mismatches are caught and reported after this, just be careful for now. 277 if (! type.isStruct() || ! unitType.isStruct() || type.getStruct()->size() != unitType.getStruct()->size()) 278 return; 279 280 for (int i = 0; i < (int)type.getStruct()->size(); ++i) 281 mergeImplicitArraySizes(*(*type.getStruct())[i].type, *(*unitType.getStruct())[i].type); 282 } 283 284 // 285 // Compare two global objects from two compilation units and see if they match 286 // well enough. Rules can be different for intra- vs. cross-stage matching. 287 // 288 // This function only does one of intra- or cross-stage matching per call. 289 // 290 void TIntermediate::mergeErrorCheck(TInfoSink& infoSink, const TIntermSymbol& symbol, const TIntermSymbol& unitSymbol, bool crossStage) 291 { 292 bool writeTypeComparison = false; 293 294 // Types have to match 295 if (symbol.getType() != unitSymbol.getType()) { 296 error(infoSink, "Types must match:"); 297 writeTypeComparison = true; 298 } 299 300 // Qualifiers have to (almost) match 301 302 // Storage... 303 if (symbol.getQualifier().storage != unitSymbol.getQualifier().storage) { 304 error(infoSink, "Storage qualifiers must match:"); 305 writeTypeComparison = true; 306 } 307 308 // Precision... 309 if (symbol.getQualifier().precision != unitSymbol.getQualifier().precision) { 310 error(infoSink, "Precision qualifiers must match:"); 311 writeTypeComparison = true; 312 } 313 314 // Invariance... 315 if (! crossStage && symbol.getQualifier().invariant != unitSymbol.getQualifier().invariant) { 316 error(infoSink, "Presence of invariant qualifier must match:"); 317 writeTypeComparison = true; 318 } 319 320 // Precise... 321 if (! crossStage && symbol.getQualifier().noContraction != unitSymbol.getQualifier().noContraction) { 322 error(infoSink, "Presence of precise qualifier must match:"); 323 writeTypeComparison = true; 324 } 325 326 // Auxiliary and interpolation... 327 if (symbol.getQualifier().centroid != unitSymbol.getQualifier().centroid || 328 symbol.getQualifier().smooth != unitSymbol.getQualifier().smooth || 329 symbol.getQualifier().flat != unitSymbol.getQualifier().flat || 330 symbol.getQualifier().sample != unitSymbol.getQualifier().sample || 331 symbol.getQualifier().patch != unitSymbol.getQualifier().patch || 332 symbol.getQualifier().nopersp != unitSymbol.getQualifier().nopersp) { 333 error(infoSink, "Interpolation and auxiliary storage qualifiers must match:"); 334 writeTypeComparison = true; 335 } 336 337 // Memory... 338 if (symbol.getQualifier().coherent != unitSymbol.getQualifier().coherent || 339 symbol.getQualifier().volatil != unitSymbol.getQualifier().volatil || 340 symbol.getQualifier().restrict != unitSymbol.getQualifier().restrict || 341 symbol.getQualifier().readonly != unitSymbol.getQualifier().readonly || 342 symbol.getQualifier().writeonly != unitSymbol.getQualifier().writeonly) { 343 error(infoSink, "Memory qualifiers must match:"); 344 writeTypeComparison = true; 345 } 346 347 // Layouts... 348 // TODO: 4.4 enhanced layouts: Generalize to include offset/align: current spec 349 // requires separate user-supplied offset from actual computed offset, but 350 // current implementation only has one offset. 351 if (symbol.getQualifier().layoutMatrix != unitSymbol.getQualifier().layoutMatrix || 352 symbol.getQualifier().layoutPacking != unitSymbol.getQualifier().layoutPacking || 353 symbol.getQualifier().layoutLocation != unitSymbol.getQualifier().layoutLocation || 354 symbol.getQualifier().layoutComponent != unitSymbol.getQualifier().layoutComponent || 355 symbol.getQualifier().layoutIndex != unitSymbol.getQualifier().layoutIndex || 356 symbol.getQualifier().layoutBinding != unitSymbol.getQualifier().layoutBinding || 357 (symbol.getQualifier().hasBinding() && (symbol.getQualifier().layoutOffset != unitSymbol.getQualifier().layoutOffset))) { 358 error(infoSink, "Layout qualification must match:"); 359 writeTypeComparison = true; 360 } 361 362 // Initializers have to match, if both are present, and if we don't already know the types don't match 363 if (! writeTypeComparison) { 364 if (! symbol.getConstArray().empty() && ! unitSymbol.getConstArray().empty()) { 365 if (symbol.getConstArray() != unitSymbol.getConstArray()) { 366 error(infoSink, "Initializers must match:"); 367 infoSink.info << " " << symbol.getName() << "\n"; 368 } 369 } 370 } 371 372 if (writeTypeComparison) 373 infoSink.info << " " << symbol.getName() << ": \"" << symbol.getType().getCompleteString() << "\" versus \"" << 374 unitSymbol.getType().getCompleteString() << "\"\n"; 375 } 376 377 // 378 // Do final link-time error checking of a complete (merged) intermediate representation. 379 // (Much error checking was done during merging). 380 // 381 // Also, lock in defaults of things not set, including array sizes. 382 // 383 void TIntermediate::finalCheck(TInfoSink& infoSink, bool keepUncalled) 384 { 385 if (getTreeRoot() == nullptr) 386 return; 387 388 if (numEntryPoints < 1) { 389 if (source == EShSourceGlsl) 390 error(infoSink, "Missing entry point: Each stage requires one entry point"); 391 else 392 warn(infoSink, "Entry point not found"); 393 } 394 395 if (numPushConstants > 1) 396 error(infoSink, "Only one push_constant block is allowed per stage"); 397 398 // recursion and missing body checking 399 checkCallGraphCycles(infoSink); 400 checkCallGraphBodies(infoSink, keepUncalled); 401 402 // overlap/alias/missing I/O, etc. 403 inOutLocationCheck(infoSink); 404 405 // invocations 406 if (invocations == TQualifier::layoutNotSet) 407 invocations = 1; 408 409 if (inIoAccessed("gl_ClipDistance") && inIoAccessed("gl_ClipVertex")) 410 error(infoSink, "Can only use one of gl_ClipDistance or gl_ClipVertex (gl_ClipDistance is preferred)"); 411 if (inIoAccessed("gl_CullDistance") && inIoAccessed("gl_ClipVertex")) 412 error(infoSink, "Can only use one of gl_CullDistance or gl_ClipVertex (gl_ClipDistance is preferred)"); 413 414 if (userOutputUsed() && (inIoAccessed("gl_FragColor") || inIoAccessed("gl_FragData"))) 415 error(infoSink, "Cannot use gl_FragColor or gl_FragData when using user-defined outputs"); 416 if (inIoAccessed("gl_FragColor") && inIoAccessed("gl_FragData")) 417 error(infoSink, "Cannot use both gl_FragColor and gl_FragData"); 418 419 for (size_t b = 0; b < xfbBuffers.size(); ++b) { 420 if (xfbBuffers[b].containsDouble) 421 RoundToPow2(xfbBuffers[b].implicitStride, 8); 422 423 // "It is a compile-time or link-time error to have 424 // any xfb_offset that overflows xfb_stride, whether stated on declarations before or after the xfb_stride, or 425 // in different compilation units. While xfb_stride can be declared multiple times for the same buffer, it is a 426 // compile-time or link-time error to have different values specified for the stride for the same buffer." 427 if (xfbBuffers[b].stride != TQualifier::layoutXfbStrideEnd && xfbBuffers[b].implicitStride > xfbBuffers[b].stride) { 428 error(infoSink, "xfb_stride is too small to hold all buffer entries:"); 429 infoSink.info.prefix(EPrefixError); 430 infoSink.info << " xfb_buffer " << (unsigned int)b << ", xfb_stride " << xfbBuffers[b].stride << ", minimum stride needed: " << xfbBuffers[b].implicitStride << "\n"; 431 } 432 if (xfbBuffers[b].stride == TQualifier::layoutXfbStrideEnd) 433 xfbBuffers[b].stride = xfbBuffers[b].implicitStride; 434 435 // "If the buffer is capturing any 436 // outputs with double-precision components, the stride must be a multiple of 8, otherwise it must be a 437 // multiple of 4, or a compile-time or link-time error results." 438 if (xfbBuffers[b].containsDouble && ! IsMultipleOfPow2(xfbBuffers[b].stride, 8)) { 439 error(infoSink, "xfb_stride must be multiple of 8 for buffer holding a double:"); 440 infoSink.info.prefix(EPrefixError); 441 infoSink.info << " xfb_buffer " << (unsigned int)b << ", xfb_stride " << xfbBuffers[b].stride << "\n"; 442 } else if (! IsMultipleOfPow2(xfbBuffers[b].stride, 4)) { 443 error(infoSink, "xfb_stride must be multiple of 4:"); 444 infoSink.info.prefix(EPrefixError); 445 infoSink.info << " xfb_buffer " << (unsigned int)b << ", xfb_stride " << xfbBuffers[b].stride << "\n"; 446 } 447 448 // "The resulting stride (implicit or explicit), when divided by 4, must be less than or equal to the 449 // implementation-dependent constant gl_MaxTransformFeedbackInterleavedComponents." 450 if (xfbBuffers[b].stride > (unsigned int)(4 * resources.maxTransformFeedbackInterleavedComponents)) { 451 error(infoSink, "xfb_stride is too large:"); 452 infoSink.info.prefix(EPrefixError); 453 infoSink.info << " xfb_buffer " << (unsigned int)b << ", components (1/4 stride) needed are " << xfbBuffers[b].stride/4 << ", gl_MaxTransformFeedbackInterleavedComponents is " << resources.maxTransformFeedbackInterleavedComponents << "\n"; 454 } 455 } 456 457 switch (language) { 458 case EShLangVertex: 459 break; 460 case EShLangTessControl: 461 if (vertices == TQualifier::layoutNotSet) 462 error(infoSink, "At least one shader must specify an output layout(vertices=...)"); 463 break; 464 case EShLangTessEvaluation: 465 if (source == EShSourceGlsl) { 466 if (inputPrimitive == ElgNone) 467 error(infoSink, "At least one shader must specify an input layout primitive"); 468 if (vertexSpacing == EvsNone) 469 vertexSpacing = EvsEqual; 470 if (vertexOrder == EvoNone) 471 vertexOrder = EvoCcw; 472 } 473 break; 474 case EShLangGeometry: 475 if (inputPrimitive == ElgNone) 476 error(infoSink, "At least one shader must specify an input layout primitive"); 477 if (outputPrimitive == ElgNone 478 #ifdef NV_EXTENSIONS 479 && !getGeoPassthroughEXT() 480 #endif 481 ) 482 error(infoSink, "At least one shader must specify an output layout primitive"); 483 if (vertices == TQualifier::layoutNotSet 484 #ifdef NV_EXTENSIONS 485 && !getGeoPassthroughEXT() 486 #endif 487 ) 488 error(infoSink, "At least one shader must specify a layout(max_vertices = value)"); 489 break; 490 case EShLangFragment: 491 // for GL_ARB_post_depth_coverage, EarlyFragmentTest is set automatically in 492 // ParseHelper.cpp. So if we reach here, this must be GL_EXT_post_depth_coverage 493 // requiring explicit early_fragment_tests 494 if (getPostDepthCoverage() && !getEarlyFragmentTests()) 495 error(infoSink, "post_depth_coverage requires early_fragment_tests"); 496 break; 497 case EShLangCompute: 498 break; 499 default: 500 error(infoSink, "Unknown Stage."); 501 break; 502 } 503 504 // Process the tree for any node-specific work. 505 class TFinalLinkTraverser : public TIntermTraverser { 506 public: 507 TFinalLinkTraverser() { } 508 virtual ~TFinalLinkTraverser() { } 509 510 virtual void visitSymbol(TIntermSymbol* symbol) 511 { 512 // Implicitly size arrays. 513 symbol->getWritableType().adoptImplicitArraySizes(); 514 } 515 } finalLinkTraverser; 516 517 treeRoot->traverse(&finalLinkTraverser); 518 } 519 520 // 521 // See if the call graph contains any static recursion, which is disallowed 522 // by the specification. 523 // 524 void TIntermediate::checkCallGraphCycles(TInfoSink& infoSink) 525 { 526 // Clear fields we'll use for this. 527 for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) { 528 call->visited = false; 529 call->currentPath = false; 530 call->errorGiven = false; 531 } 532 533 // 534 // Loop, looking for a new connected subgraph. One subgraph is handled per loop iteration. 535 // 536 537 TCall* newRoot; 538 do { 539 // See if we have unvisited parts of the graph. 540 newRoot = 0; 541 for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) { 542 if (! call->visited) { 543 newRoot = &(*call); 544 break; 545 } 546 } 547 548 // If not, we are done. 549 if (! newRoot) 550 break; 551 552 // Otherwise, we found a new subgraph, process it: 553 // See what all can be reached by this new root, and if any of 554 // that is recursive. This is done by depth-first traversals, seeing 555 // if a new call is found that was already in the currentPath (a back edge), 556 // thereby detecting recursion. 557 std::list<TCall*> stack; 558 newRoot->currentPath = true; // currentPath will be true iff it is on the stack 559 stack.push_back(newRoot); 560 while (! stack.empty()) { 561 // get a caller 562 TCall* call = stack.back(); 563 564 // Add to the stack just one callee. 565 // This algorithm always terminates, because only !visited and !currentPath causes a push 566 // and all pushes change currentPath to true, and all pops change visited to true. 567 TGraph::iterator child = callGraph.begin(); 568 for (; child != callGraph.end(); ++child) { 569 570 // If we already visited this node, its whole subgraph has already been processed, so skip it. 571 if (child->visited) 572 continue; 573 574 if (call->callee == child->caller) { 575 if (child->currentPath) { 576 // Then, we found a back edge 577 if (! child->errorGiven) { 578 error(infoSink, "Recursion detected:"); 579 infoSink.info << " " << call->callee << " calling " << child->callee << "\n"; 580 child->errorGiven = true; 581 recursive = true; 582 } 583 } else { 584 child->currentPath = true; 585 stack.push_back(&(*child)); 586 break; 587 } 588 } 589 } 590 if (child == callGraph.end()) { 591 // no more callees, we bottomed out, never look at this node again 592 stack.back()->currentPath = false; 593 stack.back()->visited = true; 594 stack.pop_back(); 595 } 596 } // end while, meaning nothing left to process in this subtree 597 598 } while (newRoot); // redundant loop check; should always exit via the 'break' above 599 } 600 601 // 602 // See which functions are reachable from the entry point and which have bodies. 603 // Reachable ones with missing bodies are errors. 604 // Unreachable bodies are dead code. 605 // 606 void TIntermediate::checkCallGraphBodies(TInfoSink& infoSink, bool keepUncalled) 607 { 608 // Clear fields we'll use for this. 609 for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) { 610 call->visited = false; 611 call->calleeBodyPosition = -1; 612 } 613 614 // The top level of the AST includes function definitions (bodies). 615 // Compare these to function calls in the call graph. 616 // We'll end up knowing which have bodies, and if so, 617 // how to map the call-graph node to the location in the AST. 618 TIntermSequence &functionSequence = getTreeRoot()->getAsAggregate()->getSequence(); 619 std::vector<bool> reachable(functionSequence.size(), true); // so that non-functions are reachable 620 for (int f = 0; f < (int)functionSequence.size(); ++f) { 621 glslang::TIntermAggregate* node = functionSequence[f]->getAsAggregate(); 622 if (node && (node->getOp() == glslang::EOpFunction)) { 623 if (node->getName().compare(getEntryPointMangledName().c_str()) != 0) 624 reachable[f] = false; // so that function bodies are unreachable, until proven otherwise 625 for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) { 626 if (call->callee == node->getName()) 627 call->calleeBodyPosition = f; 628 } 629 } 630 } 631 632 // Start call-graph traversal by visiting the entry point nodes. 633 for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) { 634 if (call->caller.compare(getEntryPointMangledName().c_str()) == 0) 635 call->visited = true; 636 } 637 638 // Propagate 'visited' through the call-graph to every part of the graph it 639 // can reach (seeded with the entry-point setting above). 640 bool changed; 641 do { 642 changed = false; 643 for (auto call1 = callGraph.begin(); call1 != callGraph.end(); ++call1) { 644 if (call1->visited) { 645 for (TGraph::iterator call2 = callGraph.begin(); call2 != callGraph.end(); ++call2) { 646 if (! call2->visited) { 647 if (call1->callee == call2->caller) { 648 changed = true; 649 call2->visited = true; 650 } 651 } 652 } 653 } 654 } 655 } while (changed); 656 657 // Any call-graph node set to visited but without a callee body is an error. 658 for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) { 659 if (call->visited) { 660 if (call->calleeBodyPosition == -1) { 661 error(infoSink, "No function definition (body) found: "); 662 infoSink.info << " " << call->callee << "\n"; 663 } else 664 reachable[call->calleeBodyPosition] = true; 665 } 666 } 667 668 // Bodies in the AST not reached by the call graph are dead; 669 // clear them out, since they can't be reached and also can't 670 // be translated further due to possibility of being ill defined. 671 if (! keepUncalled) { 672 for (int f = 0; f < (int)functionSequence.size(); ++f) { 673 if (! reachable[f]) 674 functionSequence[f] = nullptr; 675 } 676 functionSequence.erase(std::remove(functionSequence.begin(), functionSequence.end(), nullptr), functionSequence.end()); 677 } 678 } 679 680 // 681 // Satisfy rules for location qualifiers on inputs and outputs 682 // 683 void TIntermediate::inOutLocationCheck(TInfoSink& infoSink) 684 { 685 // ES 3.0 requires all outputs to have location qualifiers if there is more than one output 686 bool fragOutWithNoLocation = false; 687 int numFragOut = 0; 688 689 // TODO: linker functionality: location collision checking 690 691 TIntermSequence& linkObjects = findLinkerObjects(); 692 for (size_t i = 0; i < linkObjects.size(); ++i) { 693 const TType& type = linkObjects[i]->getAsTyped()->getType(); 694 const TQualifier& qualifier = type.getQualifier(); 695 if (language == EShLangFragment) { 696 if (qualifier.storage == EvqVaryingOut && qualifier.builtIn == EbvNone) { 697 ++numFragOut; 698 if (!qualifier.hasAnyLocation()) 699 fragOutWithNoLocation = true; 700 } 701 } 702 } 703 704 if (profile == EEsProfile) { 705 if (numFragOut > 1 && fragOutWithNoLocation) 706 error(infoSink, "when more than one fragment shader output, all must have location qualifiers"); 707 } 708 } 709 710 TIntermSequence& TIntermediate::findLinkerObjects() const 711 { 712 // Get the top-level globals 713 TIntermSequence& globals = treeRoot->getAsAggregate()->getSequence(); 714 715 // Get the last member of the sequences, expected to be the linker-object lists 716 assert(globals.back()->getAsAggregate()->getOp() == EOpLinkerObjects); 717 718 return globals.back()->getAsAggregate()->getSequence(); 719 } 720 721 // See if a variable was both a user-declared output and used. 722 // Note: the spec discusses writing to one, but this looks at read or write, which 723 // is more useful, and perhaps the spec should be changed to reflect that. 724 bool TIntermediate::userOutputUsed() const 725 { 726 const TIntermSequence& linkerObjects = findLinkerObjects(); 727 728 bool found = false; 729 for (size_t i = 0; i < linkerObjects.size(); ++i) { 730 const TIntermSymbol& symbolNode = *linkerObjects[i]->getAsSymbolNode(); 731 if (symbolNode.getQualifier().storage == EvqVaryingOut && 732 symbolNode.getName().compare(0, 3, "gl_") != 0 && 733 inIoAccessed(symbolNode.getName())) { 734 found = true; 735 break; 736 } 737 } 738 739 return found; 740 } 741 742 // Accumulate locations used for inputs, outputs, and uniforms, and check for collisions 743 // as the accumulation is done. 744 // 745 // Returns < 0 if no collision, >= 0 if collision and the value returned is a colliding value. 746 // 747 // typeCollision is set to true if there is no direct collision, but the types in the same location 748 // are different. 749 // 750 int TIntermediate::addUsedLocation(const TQualifier& qualifier, const TType& type, bool& typeCollision) 751 { 752 typeCollision = false; 753 754 int set; 755 if (qualifier.isPipeInput()) 756 set = 0; 757 else if (qualifier.isPipeOutput()) 758 set = 1; 759 else if (qualifier.storage == EvqUniform) 760 set = 2; 761 else if (qualifier.storage == EvqBuffer) 762 set = 3; 763 else 764 return -1; 765 766 int size; 767 if (qualifier.isUniformOrBuffer()) { 768 if (type.isExplicitlySizedArray()) 769 size = type.getCumulativeArraySize(); 770 else 771 size = 1; 772 } else { 773 // Strip off the outer array dimension for those having an extra one. 774 if (type.isArray() && qualifier.isArrayedIo(language)) { 775 TType elementType(type, 0); 776 size = computeTypeLocationSize(elementType); 777 } else 778 size = computeTypeLocationSize(type); 779 } 780 781 // Locations, and components within locations. 782 // 783 // Almost always, dealing with components means a single location is involved. 784 // The exception is a dvec3. From the spec: 785 // 786 // "A dvec3 will consume all four components of the first location and components 0 and 1 of 787 // the second location. This leaves components 2 and 3 available for other component-qualified 788 // declarations." 789 // 790 // That means, without ever mentioning a component, a component range 791 // for a different location gets specified, if it's not a vertex shader input. (!) 792 // (A vertex shader input will show using only one location, even for a dvec3/4.) 793 // 794 // So, for the case of dvec3, we need two independent ioRanges. 795 796 int collision = -1; // no collision 797 if (size == 2 && type.getBasicType() == EbtDouble && type.getVectorSize() == 3 && 798 (qualifier.isPipeInput() || qualifier.isPipeOutput())) { 799 // Dealing with dvec3 in/out split across two locations. 800 // Need two io-ranges. 801 // The case where the dvec3 doesn't start at component 0 was previously caught as overflow. 802 803 // First range: 804 TRange locationRange(qualifier.layoutLocation, qualifier.layoutLocation); 805 TRange componentRange(0, 3); 806 TIoRange range(locationRange, componentRange, type.getBasicType(), 0); 807 808 // check for collisions 809 collision = checkLocationRange(set, range, type, typeCollision); 810 if (collision < 0) { 811 usedIo[set].push_back(range); 812 813 // Second range: 814 TRange locationRange2(qualifier.layoutLocation + 1, qualifier.layoutLocation + 1); 815 TRange componentRange2(0, 1); 816 TIoRange range2(locationRange2, componentRange2, type.getBasicType(), 0); 817 818 // check for collisions 819 collision = checkLocationRange(set, range2, type, typeCollision); 820 if (collision < 0) 821 usedIo[set].push_back(range2); 822 } 823 } else { 824 // Not a dvec3 in/out split across two locations, generic path. 825 // Need a single IO-range block. 826 827 TRange locationRange(qualifier.layoutLocation, qualifier.layoutLocation + size - 1); 828 TRange componentRange(0, 3); 829 if (qualifier.hasComponent() || type.getVectorSize() > 0) { 830 int consumedComponents = type.getVectorSize() * (type.getBasicType() == EbtDouble ? 2 : 1); 831 if (qualifier.hasComponent()) 832 componentRange.start = qualifier.layoutComponent; 833 componentRange.last = componentRange.start + consumedComponents - 1; 834 } 835 836 // combine location and component ranges 837 TIoRange range(locationRange, componentRange, type.getBasicType(), qualifier.hasIndex() ? qualifier.layoutIndex : 0); 838 839 // check for collisions, except for vertex inputs on desktop 840 if (! (profile != EEsProfile && language == EShLangVertex && qualifier.isPipeInput())) 841 collision = checkLocationRange(set, range, type, typeCollision); 842 843 if (collision < 0) 844 usedIo[set].push_back(range); 845 } 846 847 return collision; 848 } 849 850 // Compare a new (the passed in) 'range' against the existing set, and see 851 // if there are any collisions. 852 // 853 // Returns < 0 if no collision, >= 0 if collision and the value returned is a colliding value. 854 // 855 int TIntermediate::checkLocationRange(int set, const TIoRange& range, const TType& type, bool& typeCollision) 856 { 857 for (size_t r = 0; r < usedIo[set].size(); ++r) { 858 if (range.overlap(usedIo[set][r])) { 859 // there is a collision; pick one 860 return std::max(range.location.start, usedIo[set][r].location.start); 861 } else if (range.location.overlap(usedIo[set][r].location) && type.getBasicType() != usedIo[set][r].basicType) { 862 // aliased-type mismatch 863 typeCollision = true; 864 return std::max(range.location.start, usedIo[set][r].location.start); 865 } 866 } 867 868 return -1; // no collision 869 } 870 871 // Accumulate bindings and offsets, and check for collisions 872 // as the accumulation is done. 873 // 874 // Returns < 0 if no collision, >= 0 if collision and the value returned is a colliding value. 875 // 876 int TIntermediate::addUsedOffsets(int binding, int offset, int numOffsets) 877 { 878 TRange bindingRange(binding, binding); 879 TRange offsetRange(offset, offset + numOffsets - 1); 880 TOffsetRange range(bindingRange, offsetRange); 881 882 // check for collisions, except for vertex inputs on desktop 883 for (size_t r = 0; r < usedAtomics.size(); ++r) { 884 if (range.overlap(usedAtomics[r])) { 885 // there is a collision; pick one 886 return std::max(offset, usedAtomics[r].offset.start); 887 } 888 } 889 890 usedAtomics.push_back(range); 891 892 return -1; // no collision 893 } 894 895 // Accumulate used constant_id values. 896 // 897 // Return false is one was already used. 898 bool TIntermediate::addUsedConstantId(int id) 899 { 900 if (usedConstantId.find(id) != usedConstantId.end()) 901 return false; 902 903 usedConstantId.insert(id); 904 905 return true; 906 } 907 908 // Recursively figure out how many locations are used up by an input or output type. 909 // Return the size of type, as measured by "locations". 910 int TIntermediate::computeTypeLocationSize(const TType& type) const 911 { 912 // "If the declared input is an array of size n and each element takes m locations, it will be assigned m * n 913 // consecutive locations..." 914 if (type.isArray()) { 915 // TODO: perf: this can be flattened by using getCumulativeArraySize(), and a deref that discards all arrayness 916 TType elementType(type, 0); 917 if (type.isImplicitlySizedArray()) { 918 // TODO: are there valid cases of having an implicitly-sized array with a location? If so, running this code too early. 919 return computeTypeLocationSize(elementType); 920 } else 921 return type.getOuterArraySize() * computeTypeLocationSize(elementType); 922 } 923 924 // "The locations consumed by block and structure members are determined by applying the rules above 925 // recursively..." 926 if (type.isStruct()) { 927 int size = 0; 928 for (int member = 0; member < (int)type.getStruct()->size(); ++member) { 929 TType memberType(type, member); 930 size += computeTypeLocationSize(memberType); 931 } 932 return size; 933 } 934 935 // ES: "If a shader input is any scalar or vector type, it will consume a single location." 936 937 // Desktop: "If a vertex shader input is any scalar or vector type, it will consume a single location. If a non-vertex 938 // shader input is a scalar or vector type other than dvec3 or dvec4, it will consume a single location, while 939 // types dvec3 or dvec4 will consume two consecutive locations. Inputs of type double and dvec2 will 940 // consume only a single location, in all stages." 941 if (type.isScalar()) 942 return 1; 943 if (type.isVector()) { 944 if (language == EShLangVertex && type.getQualifier().isPipeInput()) 945 return 1; 946 if (type.getBasicType() == EbtDouble && type.getVectorSize() > 2) 947 return 2; 948 else 949 return 1; 950 } 951 952 // "If the declared input is an n x m single- or double-precision matrix, ... 953 // The number of locations assigned for each matrix will be the same as 954 // for an n-element array of m-component vectors..." 955 if (type.isMatrix()) { 956 TType columnType(type, 0); 957 return type.getMatrixCols() * computeTypeLocationSize(columnType); 958 } 959 960 assert(0); 961 return 1; 962 } 963 964 // Accumulate xfb buffer ranges and check for collisions as the accumulation is done. 965 // 966 // Returns < 0 if no collision, >= 0 if collision and the value returned is a colliding value. 967 // 968 int TIntermediate::addXfbBufferOffset(const TType& type) 969 { 970 const TQualifier& qualifier = type.getQualifier(); 971 972 assert(qualifier.hasXfbOffset() && qualifier.hasXfbBuffer()); 973 TXfbBuffer& buffer = xfbBuffers[qualifier.layoutXfbBuffer]; 974 975 // compute the range 976 unsigned int size = computeTypeXfbSize(type, buffer.containsDouble); 977 buffer.implicitStride = std::max(buffer.implicitStride, qualifier.layoutXfbOffset + size); 978 TRange range(qualifier.layoutXfbOffset, qualifier.layoutXfbOffset + size - 1); 979 980 // check for collisions 981 for (size_t r = 0; r < buffer.ranges.size(); ++r) { 982 if (range.overlap(buffer.ranges[r])) { 983 // there is a collision; pick an example to return 984 return std::max(range.start, buffer.ranges[r].start); 985 } 986 } 987 988 buffer.ranges.push_back(range); 989 990 return -1; // no collision 991 } 992 993 // Recursively figure out how many bytes of xfb buffer are used by the given type. 994 // Return the size of type, in bytes. 995 // Sets containsDouble to true if the type contains a double. 996 // N.B. Caller must set containsDouble to false before calling. 997 unsigned int TIntermediate::computeTypeXfbSize(const TType& type, bool& containsDouble) const 998 { 999 // "...if applied to an aggregate containing a double, the offset must also be a multiple of 8, 1000 // and the space taken in the buffer will be a multiple of 8. 1001 // ...within the qualified entity, subsequent components are each 1002 // assigned, in order, to the next available offset aligned to a multiple of 1003 // that component's size. Aggregate types are flattened down to the component 1004 // level to get this sequence of components." 1005 1006 if (type.isArray()) { 1007 // TODO: perf: this can be flattened by using getCumulativeArraySize(), and a deref that discards all arrayness 1008 assert(type.isExplicitlySizedArray()); 1009 TType elementType(type, 0); 1010 return type.getOuterArraySize() * computeTypeXfbSize(elementType, containsDouble); 1011 } 1012 1013 if (type.isStruct()) { 1014 unsigned int size = 0; 1015 bool structContainsDouble = false; 1016 for (int member = 0; member < (int)type.getStruct()->size(); ++member) { 1017 TType memberType(type, member); 1018 // "... if applied to 1019 // an aggregate containing a double, the offset must also be a multiple of 8, 1020 // and the space taken in the buffer will be a multiple of 8." 1021 bool memberContainsDouble = false; 1022 int memberSize = computeTypeXfbSize(memberType, memberContainsDouble); 1023 if (memberContainsDouble) { 1024 structContainsDouble = true; 1025 RoundToPow2(size, 8); 1026 } 1027 size += memberSize; 1028 } 1029 1030 if (structContainsDouble) { 1031 containsDouble = true; 1032 RoundToPow2(size, 8); 1033 } 1034 return size; 1035 } 1036 1037 int numComponents; 1038 if (type.isScalar()) 1039 numComponents = 1; 1040 else if (type.isVector()) 1041 numComponents = type.getVectorSize(); 1042 else if (type.isMatrix()) 1043 numComponents = type.getMatrixCols() * type.getMatrixRows(); 1044 else { 1045 assert(0); 1046 numComponents = 1; 1047 } 1048 1049 if (type.getBasicType() == EbtDouble) { 1050 containsDouble = true; 1051 return 8 * numComponents; 1052 } else 1053 return 4 * numComponents; 1054 } 1055 1056 const int baseAlignmentVec4Std140 = 16; 1057 1058 // Return the size and alignment of a component of the given type. 1059 // The size is returned in the 'size' parameter 1060 // Return value is the alignment.. 1061 int TIntermediate::getBaseAlignmentScalar(const TType& type, int& size) 1062 { 1063 switch (type.getBasicType()) { 1064 case EbtInt64: 1065 case EbtUint64: 1066 case EbtDouble: size = 8; return 8; 1067 #ifdef AMD_EXTENSIONS 1068 case EbtInt16: 1069 case EbtUint16: 1070 case EbtFloat16: size = 2; return 2; 1071 #endif 1072 default: size = 4; return 4; 1073 } 1074 } 1075 1076 // Implement base-alignment and size rules from section 7.6.2.2 Standard Uniform Block Layout 1077 // Operates recursively. 1078 // 1079 // If std140 is true, it does the rounding up to vec4 size required by std140, 1080 // otherwise it does not, yielding std430 rules. 1081 // 1082 // The size is returned in the 'size' parameter 1083 // 1084 // The stride is only non-0 for arrays or matrices, and is the stride of the 1085 // top-level object nested within the type. E.g., for an array of matrices, 1086 // it is the distances needed between matrices, despite the rules saying the 1087 // stride comes from the flattening down to vectors. 1088 // 1089 // Return value is the alignment of the type. 1090 int TIntermediate::getBaseAlignment(const TType& type, int& size, int& stride, bool std140, bool rowMajor) 1091 { 1092 int alignment; 1093 1094 // When using the std140 storage layout, structures will be laid out in buffer 1095 // storage with its members stored in monotonically increasing order based on their 1096 // location in the declaration. A structure and each structure member have a base 1097 // offset and a base alignment, from which an aligned offset is computed by rounding 1098 // the base offset up to a multiple of the base alignment. The base offset of the first 1099 // member of a structure is taken from the aligned offset of the structure itself. The 1100 // base offset of all other structure members is derived by taking the offset of the 1101 // last basic machine unit consumed by the previous member and adding one. Each 1102 // structure member is stored in memory at its aligned offset. The members of a top- 1103 // level uniform block are laid out in buffer storage by treating the uniform block as 1104 // a structure with a base offset of zero. 1105 // 1106 // 1. If the member is a scalar consuming N basic machine units, the base alignment is N. 1107 // 1108 // 2. If the member is a two- or four-component vector with components consuming N basic 1109 // machine units, the base alignment is 2N or 4N, respectively. 1110 // 1111 // 3. If the member is a three-component vector with components consuming N 1112 // basic machine units, the base alignment is 4N. 1113 // 1114 // 4. If the member is an array of scalars or vectors, the base alignment and array 1115 // stride are set to match the base alignment of a single array element, according 1116 // to rules (1), (2), and (3), and rounded up to the base alignment of a vec4. The 1117 // array may have padding at the end; the base offset of the member following 1118 // the array is rounded up to the next multiple of the base alignment. 1119 // 1120 // 5. If the member is a column-major matrix with C columns and R rows, the 1121 // matrix is stored identically to an array of C column vectors with R 1122 // components each, according to rule (4). 1123 // 1124 // 6. If the member is an array of S column-major matrices with C columns and 1125 // R rows, the matrix is stored identically to a row of S X C column vectors 1126 // with R components each, according to rule (4). 1127 // 1128 // 7. If the member is a row-major matrix with C columns and R rows, the matrix 1129 // is stored identically to an array of R row vectors with C components each, 1130 // according to rule (4). 1131 // 1132 // 8. If the member is an array of S row-major matrices with C columns and R 1133 // rows, the matrix is stored identically to a row of S X R row vectors with C 1134 // components each, according to rule (4). 1135 // 1136 // 9. If the member is a structure, the base alignment of the structure is N , where 1137 // N is the largest base alignment value of any of its members, and rounded 1138 // up to the base alignment of a vec4. The individual members of this substructure 1139 // are then assigned offsets by applying this set of rules recursively, 1140 // where the base offset of the first member of the sub-structure is equal to the 1141 // aligned offset of the structure. The structure may have padding at the end; 1142 // the base offset of the member following the sub-structure is rounded up to 1143 // the next multiple of the base alignment of the structure. 1144 // 1145 // 10. If the member is an array of S structures, the S elements of the array are laid 1146 // out in order, according to rule (9). 1147 // 1148 // Assuming, for rule 10: The stride is the same as the size of an element. 1149 1150 stride = 0; 1151 int dummyStride; 1152 1153 // rules 4, 6, 8, and 10 1154 if (type.isArray()) { 1155 // TODO: perf: this might be flattened by using getCumulativeArraySize(), and a deref that discards all arrayness 1156 TType derefType(type, 0); 1157 alignment = getBaseAlignment(derefType, size, dummyStride, std140, rowMajor); 1158 if (std140) 1159 alignment = std::max(baseAlignmentVec4Std140, alignment); 1160 RoundToPow2(size, alignment); 1161 stride = size; // uses full matrix size for stride of an array of matrices (not quite what rule 6/8, but what's expected) 1162 // uses the assumption for rule 10 in the comment above 1163 size = stride * type.getOuterArraySize(); 1164 return alignment; 1165 } 1166 1167 // rule 9 1168 if (type.getBasicType() == EbtStruct) { 1169 const TTypeList& memberList = *type.getStruct(); 1170 1171 size = 0; 1172 int maxAlignment = std140 ? baseAlignmentVec4Std140 : 0; 1173 for (size_t m = 0; m < memberList.size(); ++m) { 1174 int memberSize; 1175 // modify just the children's view of matrix layout, if there is one for this member 1176 TLayoutMatrix subMatrixLayout = memberList[m].type->getQualifier().layoutMatrix; 1177 int memberAlignment = getBaseAlignment(*memberList[m].type, memberSize, dummyStride, std140, 1178 (subMatrixLayout != ElmNone) ? (subMatrixLayout == ElmRowMajor) : rowMajor); 1179 maxAlignment = std::max(maxAlignment, memberAlignment); 1180 RoundToPow2(size, memberAlignment); 1181 size += memberSize; 1182 } 1183 1184 // The structure may have padding at the end; the base offset of 1185 // the member following the sub-structure is rounded up to the next 1186 // multiple of the base alignment of the structure. 1187 RoundToPow2(size, maxAlignment); 1188 1189 return maxAlignment; 1190 } 1191 1192 // rule 1 1193 if (type.isScalar()) 1194 return getBaseAlignmentScalar(type, size); 1195 1196 // rules 2 and 3 1197 if (type.isVector()) { 1198 int scalarAlign = getBaseAlignmentScalar(type, size); 1199 switch (type.getVectorSize()) { 1200 case 2: 1201 size *= 2; 1202 return 2 * scalarAlign; 1203 default: 1204 size *= type.getVectorSize(); 1205 return 4 * scalarAlign; 1206 } 1207 } 1208 1209 // rules 5 and 7 1210 if (type.isMatrix()) { 1211 // rule 5: deref to row, not to column, meaning the size of vector is num columns instead of num rows 1212 TType derefType(type, 0, rowMajor); 1213 1214 alignment = getBaseAlignment(derefType, size, dummyStride, std140, rowMajor); 1215 if (std140) 1216 alignment = std::max(baseAlignmentVec4Std140, alignment); 1217 RoundToPow2(size, alignment); 1218 stride = size; // use intra-matrix stride for stride of a just a matrix 1219 if (rowMajor) 1220 size = stride * type.getMatrixRows(); 1221 else 1222 size = stride * type.getMatrixCols(); 1223 1224 return alignment; 1225 } 1226 1227 assert(0); // all cases should be covered above 1228 size = baseAlignmentVec4Std140; 1229 return baseAlignmentVec4Std140; 1230 } 1231 1232 // To aid the basic HLSL rule about crossing vec4 boundaries. 1233 bool TIntermediate::improperStraddle(const TType& type, int size, int offset) 1234 { 1235 if (! type.isVector() || type.isArray()) 1236 return false; 1237 1238 return size <= 16 ? offset / 16 != (offset + size - 1) / 16 1239 : offset % 16 != 0; 1240 } 1241 1242 } // end namespace glslang 1243