blocklayout.cpp (22792B)
1 // 2 // Copyright 2013 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 // blocklayout.cpp: 7 // Implementation for block layout classes and methods. 8 // 9 10 #include "compiler/translator/blocklayout.h" 11 12 #include "common/mathutil.h" 13 #include "common/utilities.h" 14 #include "compiler/translator/Common.h" 15 16 namespace sh 17 { 18 19 namespace 20 { 21 class BlockLayoutMapVisitor : public BlockEncoderVisitor 22 { 23 public: 24 BlockLayoutMapVisitor(BlockLayoutMap *blockInfoOut, 25 const std::string &instanceName, 26 BlockLayoutEncoder *encoder) 27 : BlockEncoderVisitor(instanceName, instanceName, encoder), mInfoOut(blockInfoOut) 28 {} 29 30 void encodeVariable(const ShaderVariable &variable, 31 const BlockMemberInfo &variableInfo, 32 const std::string &name, 33 const std::string &mappedName) override 34 { 35 ASSERT(!gl::IsSamplerType(variable.type)); 36 if (!gl::IsOpaqueType(variable.type)) 37 { 38 (*mInfoOut)[name] = variableInfo; 39 } 40 } 41 42 private: 43 BlockLayoutMap *mInfoOut; 44 }; 45 46 template <typename VarT> 47 void GetInterfaceBlockInfo(const std::vector<VarT> &fields, 48 const std::string &prefix, 49 BlockLayoutEncoder *encoder, 50 bool inRowMajorLayout, 51 bool onlyActiveVariables, 52 BlockLayoutMap *blockInfoOut) 53 { 54 BlockLayoutMapVisitor visitor(blockInfoOut, prefix, encoder); 55 if (onlyActiveVariables) 56 { 57 TraverseActiveShaderVariables(fields, inRowMajorLayout, &visitor); 58 } 59 else 60 { 61 TraverseShaderVariables(fields, inRowMajorLayout, &visitor); 62 } 63 } 64 65 void TraverseStructVariable(const ShaderVariable &variable, 66 bool isRowMajorLayout, 67 ShaderVariableVisitor *visitor) 68 { 69 const std::vector<ShaderVariable> &fields = variable.fields; 70 71 visitor->enterStructAccess(variable, isRowMajorLayout); 72 TraverseShaderVariables(fields, isRowMajorLayout, visitor); 73 visitor->exitStructAccess(variable, isRowMajorLayout); 74 } 75 76 void TraverseStructArrayVariable(const ShaderVariable &variable, 77 bool inRowMajorLayout, 78 ShaderVariableVisitor *visitor) 79 { 80 visitor->enterArray(variable); 81 82 // Nested arrays are processed starting from outermost (arrayNestingIndex 0u) and ending at the 83 // innermost. We make a special case for unsized arrays. 84 const unsigned int currentArraySize = variable.getNestedArraySize(0); 85 for (unsigned int arrayElement = 0u; arrayElement < currentArraySize; ++arrayElement) 86 { 87 visitor->enterArrayElement(variable, arrayElement); 88 ShaderVariable elementVar = variable; 89 elementVar.indexIntoArray(arrayElement); 90 91 if (variable.arraySizes.size() > 1u) 92 { 93 TraverseStructArrayVariable(elementVar, inRowMajorLayout, visitor); 94 } 95 else 96 { 97 TraverseStructVariable(elementVar, inRowMajorLayout, visitor); 98 } 99 100 visitor->exitArrayElement(variable, arrayElement); 101 } 102 103 visitor->exitArray(variable); 104 } 105 106 void TraverseArrayOfArraysVariable(const ShaderVariable &variable, 107 unsigned int arrayNestingIndex, 108 bool isRowMajorMatrix, 109 ShaderVariableVisitor *visitor) 110 { 111 visitor->enterArray(variable); 112 113 const unsigned int currentArraySize = variable.getNestedArraySize(arrayNestingIndex); 114 unsigned int count = std::max(currentArraySize, 1u); 115 for (unsigned int arrayElement = 0u; arrayElement < count; ++arrayElement) 116 { 117 visitor->enterArrayElement(variable, arrayElement); 118 119 ShaderVariable elementVar = variable; 120 elementVar.indexIntoArray(arrayElement); 121 122 if (arrayNestingIndex + 2u < variable.arraySizes.size()) 123 { 124 TraverseArrayOfArraysVariable(elementVar, arrayNestingIndex, isRowMajorMatrix, visitor); 125 } 126 else 127 { 128 if (gl::IsSamplerType(variable.type) || gl::IsImageType(variable.type) || 129 variable.isFragmentInOut) 130 { 131 visitor->visitOpaqueObject(elementVar); 132 } 133 else 134 { 135 visitor->visitVariable(elementVar, isRowMajorMatrix); 136 } 137 } 138 139 visitor->exitArrayElement(variable, arrayElement); 140 } 141 142 visitor->exitArray(variable); 143 } 144 145 std::string CollapseNameStack(const std::vector<std::string> &nameStack) 146 { 147 std::stringstream strstr = sh::InitializeStream<std::stringstream>(); 148 for (const std::string &part : nameStack) 149 { 150 strstr << part; 151 } 152 return strstr.str(); 153 } 154 155 size_t GetStd430BaseAlignment(GLenum variableType, bool isRowMajor) 156 { 157 GLenum flippedType = isRowMajor ? variableType : gl::TransposeMatrixType(variableType); 158 size_t numComponents = static_cast<size_t>(gl::VariableColumnCount(flippedType)); 159 return ComponentAlignment(numComponents); 160 } 161 162 class BaseAlignmentVisitor : public ShaderVariableVisitor 163 { 164 public: 165 BaseAlignmentVisitor() = default; 166 void visitVariable(const ShaderVariable &variable, bool isRowMajor) override 167 { 168 size_t baseAlignment = GetStd430BaseAlignment(variable.type, isRowMajor); 169 mCurrentAlignment = std::max(mCurrentAlignment, baseAlignment); 170 } 171 172 // This is in components rather than bytes. 173 size_t getBaseAlignment() const { return mCurrentAlignment; } 174 175 private: 176 size_t mCurrentAlignment = 0; 177 }; 178 } // anonymous namespace 179 180 // BlockLayoutEncoder implementation. 181 BlockLayoutEncoder::BlockLayoutEncoder() : mCurrentOffset(0) {} 182 183 BlockMemberInfo BlockLayoutEncoder::encodeType(GLenum type, 184 const std::vector<unsigned int> &arraySizes, 185 bool isRowMajorMatrix) 186 { 187 int arrayStride; 188 int matrixStride; 189 190 getBlockLayoutInfo(type, arraySizes, isRowMajorMatrix, &arrayStride, &matrixStride); 191 192 const BlockMemberInfo memberInfo(static_cast<int>(mCurrentOffset * kBytesPerComponent), 193 static_cast<int>(arrayStride * kBytesPerComponent), 194 static_cast<int>(matrixStride * kBytesPerComponent), 195 isRowMajorMatrix); 196 197 advanceOffset(type, arraySizes, isRowMajorMatrix, arrayStride, matrixStride); 198 199 return memberInfo; 200 } 201 202 BlockMemberInfo BlockLayoutEncoder::encodeArrayOfPreEncodedStructs( 203 size_t size, 204 const std::vector<unsigned int> &arraySizes) 205 { 206 const unsigned int innerArraySizeProduct = gl::InnerArraySizeProduct(arraySizes); 207 const unsigned int outermostArraySize = gl::OutermostArraySize(arraySizes); 208 209 // The size of struct is expected to be already aligned appropriately. 210 const size_t arrayStride = size * innerArraySizeProduct; 211 212 const BlockMemberInfo memberInfo(static_cast<int>(mCurrentOffset * kBytesPerComponent), 213 static_cast<int>(arrayStride), -1, false); 214 215 angle::base::CheckedNumeric<size_t> checkedOffset(arrayStride); 216 checkedOffset *= outermostArraySize; 217 checkedOffset /= kBytesPerComponent; 218 checkedOffset += mCurrentOffset; 219 mCurrentOffset = checkedOffset.ValueOrDefault(std::numeric_limits<size_t>::max()); 220 221 return memberInfo; 222 } 223 224 size_t BlockLayoutEncoder::getCurrentOffset() const 225 { 226 angle::base::CheckedNumeric<size_t> checkedOffset(mCurrentOffset); 227 checkedOffset *= kBytesPerComponent; 228 return checkedOffset.ValueOrDefault(std::numeric_limits<size_t>::max()); 229 } 230 231 size_t BlockLayoutEncoder::getShaderVariableSize(const ShaderVariable &structVar, bool isRowMajor) 232 { 233 size_t currentOffset = mCurrentOffset; 234 mCurrentOffset = 0; 235 BlockEncoderVisitor visitor("", "", this); 236 enterAggregateType(structVar); 237 TraverseShaderVariables(structVar.fields, isRowMajor, &visitor); 238 exitAggregateType(structVar); 239 size_t structVarSize = getCurrentOffset(); 240 mCurrentOffset = currentOffset; 241 return structVarSize; 242 } 243 244 // static 245 size_t BlockLayoutEncoder::GetBlockRegister(const BlockMemberInfo &info) 246 { 247 return (info.offset / kBytesPerComponent) / kComponentsPerRegister; 248 } 249 250 // static 251 size_t BlockLayoutEncoder::GetBlockRegisterElement(const BlockMemberInfo &info) 252 { 253 return (info.offset / kBytesPerComponent) % kComponentsPerRegister; 254 } 255 256 void BlockLayoutEncoder::align(size_t baseAlignment) 257 { 258 angle::base::CheckedNumeric<size_t> checkedOffset(mCurrentOffset); 259 checkedOffset += baseAlignment; 260 checkedOffset -= 1; 261 angle::base::CheckedNumeric<size_t> checkedAlignmentOffset = checkedOffset; 262 checkedAlignmentOffset %= baseAlignment; 263 checkedOffset -= checkedAlignmentOffset.ValueOrDefault(std::numeric_limits<size_t>::max()); 264 mCurrentOffset = checkedOffset.ValueOrDefault(std::numeric_limits<size_t>::max()); 265 } 266 267 // StubBlockEncoder implementation. 268 void StubBlockEncoder::getBlockLayoutInfo(GLenum type, 269 const std::vector<unsigned int> &arraySizes, 270 bool isRowMajorMatrix, 271 int *arrayStrideOut, 272 int *matrixStrideOut) 273 { 274 *arrayStrideOut = 0; 275 *matrixStrideOut = 0; 276 } 277 278 // Std140BlockEncoder implementation. 279 Std140BlockEncoder::Std140BlockEncoder() {} 280 281 void Std140BlockEncoder::enterAggregateType(const ShaderVariable &structVar) 282 { 283 align(getBaseAlignment(structVar)); 284 } 285 286 void Std140BlockEncoder::exitAggregateType(const ShaderVariable &structVar) 287 { 288 align(getBaseAlignment(structVar)); 289 } 290 291 void Std140BlockEncoder::getBlockLayoutInfo(GLenum type, 292 const std::vector<unsigned int> &arraySizes, 293 bool isRowMajorMatrix, 294 int *arrayStrideOut, 295 int *matrixStrideOut) 296 { 297 // We assume we are only dealing with 4 byte components (no doubles or half-words currently) 298 ASSERT(gl::VariableComponentSize(gl::VariableComponentType(type)) == kBytesPerComponent); 299 300 size_t baseAlignment = 0; 301 int matrixStride = 0; 302 int arrayStride = 0; 303 304 if (gl::IsMatrixType(type)) 305 { 306 baseAlignment = getTypeBaseAlignment(type, isRowMajorMatrix); 307 matrixStride = static_cast<int>(getTypeBaseAlignment(type, isRowMajorMatrix)); 308 309 if (!arraySizes.empty()) 310 { 311 const int numRegisters = gl::MatrixRegisterCount(type, isRowMajorMatrix); 312 arrayStride = 313 static_cast<int>(getTypeBaseAlignment(type, isRowMajorMatrix) * numRegisters); 314 } 315 } 316 else if (!arraySizes.empty()) 317 { 318 baseAlignment = static_cast<int>(getTypeBaseAlignment(type, false)); 319 arrayStride = static_cast<int>(getTypeBaseAlignment(type, false)); 320 } 321 else 322 { 323 const size_t numComponents = static_cast<size_t>(gl::VariableComponentCount(type)); 324 baseAlignment = ComponentAlignment(numComponents); 325 } 326 327 align(baseAlignment); 328 329 *matrixStrideOut = matrixStride; 330 *arrayStrideOut = arrayStride; 331 } 332 333 void Std140BlockEncoder::advanceOffset(GLenum type, 334 const std::vector<unsigned int> &arraySizes, 335 bool isRowMajorMatrix, 336 int arrayStride, 337 int matrixStride) 338 { 339 if (!arraySizes.empty()) 340 { 341 angle::base::CheckedNumeric<size_t> checkedOffset(arrayStride); 342 checkedOffset *= gl::ArraySizeProduct(arraySizes); 343 checkedOffset += mCurrentOffset; 344 mCurrentOffset = checkedOffset.ValueOrDefault(std::numeric_limits<size_t>::max()); 345 } 346 else if (gl::IsMatrixType(type)) 347 { 348 angle::base::CheckedNumeric<size_t> checkedOffset(matrixStride); 349 checkedOffset *= gl::MatrixRegisterCount(type, isRowMajorMatrix); 350 checkedOffset += mCurrentOffset; 351 mCurrentOffset = checkedOffset.ValueOrDefault(std::numeric_limits<size_t>::max()); 352 } 353 else 354 { 355 angle::base::CheckedNumeric<size_t> checkedOffset(mCurrentOffset); 356 checkedOffset += gl::VariableComponentCount(type); 357 mCurrentOffset = checkedOffset.ValueOrDefault(std::numeric_limits<size_t>::max()); 358 } 359 } 360 361 size_t Std140BlockEncoder::getBaseAlignment(const ShaderVariable &variable) const 362 { 363 return kComponentsPerRegister; 364 } 365 366 size_t Std140BlockEncoder::getTypeBaseAlignment(GLenum type, bool isRowMajorMatrix) const 367 { 368 return kComponentsPerRegister; 369 } 370 371 // Std430BlockEncoder implementation. 372 Std430BlockEncoder::Std430BlockEncoder() {} 373 374 size_t Std430BlockEncoder::getBaseAlignment(const ShaderVariable &shaderVar) const 375 { 376 if (shaderVar.isStruct()) 377 { 378 BaseAlignmentVisitor visitor; 379 TraverseShaderVariables(shaderVar.fields, false, &visitor); 380 return visitor.getBaseAlignment(); 381 } 382 383 return GetStd430BaseAlignment(shaderVar.type, shaderVar.isRowMajorLayout); 384 } 385 386 size_t Std430BlockEncoder::getTypeBaseAlignment(GLenum type, bool isRowMajorMatrix) const 387 { 388 return GetStd430BaseAlignment(type, isRowMajorMatrix); 389 } 390 391 void GetInterfaceBlockInfo(const std::vector<ShaderVariable> &fields, 392 const std::string &prefix, 393 BlockLayoutEncoder *encoder, 394 BlockLayoutMap *blockInfoOut) 395 { 396 // Matrix packing is always recorded in individual fields, so they'll set the row major layout 397 // flag to true if needed. 398 // Iterates over all variables. 399 GetInterfaceBlockInfo(fields, prefix, encoder, false, false, blockInfoOut); 400 } 401 402 void GetActiveUniformBlockInfo(const std::vector<ShaderVariable> &uniforms, 403 const std::string &prefix, 404 BlockLayoutEncoder *encoder, 405 BlockLayoutMap *blockInfoOut) 406 { 407 // Matrix packing is always recorded in individual fields, so they'll set the row major layout 408 // flag to true if needed. 409 // Iterates only over the active variables. 410 GetInterfaceBlockInfo(uniforms, prefix, encoder, false, true, blockInfoOut); 411 } 412 413 // VariableNameVisitor implementation. 414 VariableNameVisitor::VariableNameVisitor(const std::string &namePrefix, 415 const std::string &mappedNamePrefix) 416 { 417 if (!namePrefix.empty()) 418 { 419 mNameStack.push_back(namePrefix + "."); 420 } 421 422 if (!mappedNamePrefix.empty()) 423 { 424 mMappedNameStack.push_back(mappedNamePrefix + "."); 425 } 426 } 427 428 VariableNameVisitor::~VariableNameVisitor() = default; 429 430 void VariableNameVisitor::enterStruct(const ShaderVariable &structVar) 431 { 432 mNameStack.push_back(structVar.name); 433 mMappedNameStack.push_back(structVar.mappedName); 434 } 435 436 void VariableNameVisitor::exitStruct(const ShaderVariable &structVar) 437 { 438 mNameStack.pop_back(); 439 mMappedNameStack.pop_back(); 440 } 441 442 void VariableNameVisitor::enterStructAccess(const ShaderVariable &structVar, bool isRowMajor) 443 { 444 mNameStack.push_back("."); 445 mMappedNameStack.push_back("."); 446 } 447 448 void VariableNameVisitor::exitStructAccess(const ShaderVariable &structVar, bool isRowMajor) 449 { 450 mNameStack.pop_back(); 451 mMappedNameStack.pop_back(); 452 } 453 454 void VariableNameVisitor::enterArray(const ShaderVariable &arrayVar) 455 { 456 if (!arrayVar.hasParentArrayIndex() && !arrayVar.isStruct()) 457 { 458 mNameStack.push_back(arrayVar.name); 459 mMappedNameStack.push_back(arrayVar.mappedName); 460 } 461 mArraySizeStack.push_back(arrayVar.getOutermostArraySize()); 462 } 463 464 void VariableNameVisitor::exitArray(const ShaderVariable &arrayVar) 465 { 466 if (!arrayVar.hasParentArrayIndex() && !arrayVar.isStruct()) 467 { 468 mNameStack.pop_back(); 469 mMappedNameStack.pop_back(); 470 } 471 mArraySizeStack.pop_back(); 472 } 473 474 void VariableNameVisitor::enterArrayElement(const ShaderVariable &arrayVar, 475 unsigned int arrayElement) 476 { 477 std::stringstream strstr = sh::InitializeStream<std::stringstream>(); 478 strstr << "[" << arrayElement << "]"; 479 std::string elementString = strstr.str(); 480 mNameStack.push_back(elementString); 481 mMappedNameStack.push_back(elementString); 482 } 483 484 void VariableNameVisitor::exitArrayElement(const ShaderVariable &arrayVar, 485 unsigned int arrayElement) 486 { 487 mNameStack.pop_back(); 488 mMappedNameStack.pop_back(); 489 } 490 491 std::string VariableNameVisitor::collapseNameStack() const 492 { 493 return CollapseNameStack(mNameStack); 494 } 495 496 std::string VariableNameVisitor::collapseMappedNameStack() const 497 { 498 return CollapseNameStack(mMappedNameStack); 499 } 500 501 void VariableNameVisitor::visitOpaqueObject(const sh::ShaderVariable &variable) 502 { 503 if (!variable.hasParentArrayIndex()) 504 { 505 mNameStack.push_back(variable.name); 506 mMappedNameStack.push_back(variable.mappedName); 507 } 508 509 std::string name = collapseNameStack(); 510 std::string mappedName = collapseMappedNameStack(); 511 512 if (!variable.hasParentArrayIndex()) 513 { 514 mNameStack.pop_back(); 515 mMappedNameStack.pop_back(); 516 } 517 518 visitNamedOpaqueObject(variable, name, mappedName, mArraySizeStack); 519 } 520 521 void VariableNameVisitor::visitVariable(const ShaderVariable &variable, bool isRowMajor) 522 { 523 if (!variable.hasParentArrayIndex()) 524 { 525 mNameStack.push_back(variable.name); 526 mMappedNameStack.push_back(variable.mappedName); 527 } 528 529 std::string name = collapseNameStack(); 530 std::string mappedName = collapseMappedNameStack(); 531 532 if (!variable.hasParentArrayIndex()) 533 { 534 mNameStack.pop_back(); 535 mMappedNameStack.pop_back(); 536 } 537 538 visitNamedVariable(variable, isRowMajor, name, mappedName, mArraySizeStack); 539 } 540 541 // BlockEncoderVisitor implementation. 542 BlockEncoderVisitor::BlockEncoderVisitor(const std::string &namePrefix, 543 const std::string &mappedNamePrefix, 544 BlockLayoutEncoder *encoder) 545 : VariableNameVisitor(namePrefix, mappedNamePrefix), mEncoder(encoder) 546 {} 547 548 BlockEncoderVisitor::~BlockEncoderVisitor() = default; 549 550 void BlockEncoderVisitor::enterStructAccess(const ShaderVariable &structVar, bool isRowMajor) 551 { 552 mStructStackSize++; 553 if (!mIsTopLevelArrayStrideReady) 554 { 555 size_t structSize = mEncoder->getShaderVariableSize(structVar, isRowMajor); 556 mTopLevelArrayStride *= structSize; 557 mIsTopLevelArrayStrideReady = true; 558 } 559 560 VariableNameVisitor::enterStructAccess(structVar, isRowMajor); 561 mEncoder->enterAggregateType(structVar); 562 } 563 564 void BlockEncoderVisitor::exitStructAccess(const ShaderVariable &structVar, bool isRowMajor) 565 { 566 mStructStackSize--; 567 mEncoder->exitAggregateType(structVar); 568 VariableNameVisitor::exitStructAccess(structVar, isRowMajor); 569 } 570 571 void BlockEncoderVisitor::enterArrayElement(const sh::ShaderVariable &arrayVar, 572 unsigned int arrayElement) 573 { 574 if (mStructStackSize == 0 && !arrayVar.hasParentArrayIndex()) 575 { 576 // From the ES 3.1 spec "7.3.1.1 Naming Active Resources": 577 // For an active shader storage block member declared as an array of an aggregate type, 578 // an entry will be generated only for the first array element, regardless of its type. 579 // Such block members are referred to as top-level arrays. If the block member is an 580 // aggregate type, the enumeration rules are then applied recursively. 581 if (arrayElement == 0) 582 { 583 mTopLevelArraySize = arrayVar.getOutermostArraySize(); 584 mTopLevelArrayStride = arrayVar.getInnerArraySizeProduct(); 585 mIsTopLevelArrayStrideReady = false; 586 } 587 else 588 { 589 mSkipEnabled = true; 590 } 591 } 592 VariableNameVisitor::enterArrayElement(arrayVar, arrayElement); 593 } 594 595 void BlockEncoderVisitor::exitArrayElement(const sh::ShaderVariable &arrayVar, 596 unsigned int arrayElement) 597 { 598 if (mStructStackSize == 0 && !arrayVar.hasParentArrayIndex()) 599 { 600 mTopLevelArraySize = 1; 601 mTopLevelArrayStride = 0; 602 mIsTopLevelArrayStrideReady = true; 603 mSkipEnabled = false; 604 } 605 VariableNameVisitor::exitArrayElement(arrayVar, arrayElement); 606 } 607 608 void BlockEncoderVisitor::visitNamedVariable(const ShaderVariable &variable, 609 bool isRowMajor, 610 const std::string &name, 611 const std::string &mappedName, 612 const std::vector<unsigned int> &arraySizes) 613 { 614 std::vector<unsigned int> innermostArraySize; 615 616 if (variable.isArray()) 617 { 618 innermostArraySize.push_back(variable.getNestedArraySize(0)); 619 } 620 BlockMemberInfo variableInfo = 621 mEncoder->encodeType(variable.type, innermostArraySize, isRowMajor); 622 if (!mIsTopLevelArrayStrideReady) 623 { 624 ASSERT(mTopLevelArrayStride); 625 mTopLevelArrayStride *= variableInfo.arrayStride; 626 mIsTopLevelArrayStrideReady = true; 627 } 628 variableInfo.topLevelArrayStride = mTopLevelArrayStride; 629 encodeVariable(variable, variableInfo, name, mappedName); 630 } 631 632 void TraverseShaderVariable(const ShaderVariable &variable, 633 bool isRowMajorLayout, 634 ShaderVariableVisitor *visitor) 635 { 636 bool rowMajorLayout = (isRowMajorLayout || variable.isRowMajorLayout); 637 bool isRowMajor = rowMajorLayout && gl::IsMatrixType(variable.type); 638 639 if (variable.isStruct()) 640 { 641 visitor->enterStruct(variable); 642 if (variable.isArray()) 643 { 644 TraverseStructArrayVariable(variable, rowMajorLayout, visitor); 645 } 646 else 647 { 648 TraverseStructVariable(variable, rowMajorLayout, visitor); 649 } 650 visitor->exitStruct(variable); 651 } 652 else if (variable.isArrayOfArrays()) 653 { 654 TraverseArrayOfArraysVariable(variable, 0u, isRowMajor, visitor); 655 } 656 else if (gl::IsSamplerType(variable.type) || gl::IsImageType(variable.type) || 657 variable.isFragmentInOut) 658 { 659 visitor->visitOpaqueObject(variable); 660 } 661 else 662 { 663 visitor->visitVariable(variable, isRowMajor); 664 } 665 } 666 } // namespace sh