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/OutputGLSLBase.h"
8
9 #include "angle_gl.h"
10 #include "common/debug.h"
11 #include "common/mathutil.h"
12 #include "compiler/translator/Compiler.h"
13 #include "compiler/translator/util.h"
14
15 #include <cfloat>
16
17 namespace sh
18 {
19
20 namespace
21 {
22
isSingleStatement(TIntermNode * node)23 bool isSingleStatement(TIntermNode *node)
24 {
25 if (node->getAsFunctionDefinition())
26 {
27 return false;
28 }
29 else if (node->getAsBlock())
30 {
31 return false;
32 }
33 else if (node->getAsIfElseNode())
34 {
35 return false;
36 }
37 else if (node->getAsLoopNode())
38 {
39 return false;
40 }
41 else if (node->getAsSwitchNode())
42 {
43 return false;
44 }
45 else if (node->getAsCaseNode())
46 {
47 return false;
48 }
49 else if (node->getAsPreprocessorDirective())
50 {
51 return false;
52 }
53 return true;
54 }
55
56 class CommaSeparatedListItemPrefixGenerator
57 {
58 public:
CommaSeparatedListItemPrefixGenerator()59 CommaSeparatedListItemPrefixGenerator() : mFirst(true) {}
60
61 private:
62 bool mFirst;
63
64 template <typename Stream>
65 friend Stream &operator<<(Stream &out, CommaSeparatedListItemPrefixGenerator &gen);
66 };
67
68 template <typename Stream>
operator <<(Stream & out,CommaSeparatedListItemPrefixGenerator & gen)69 Stream &operator<<(Stream &out, CommaSeparatedListItemPrefixGenerator &gen)
70 {
71 if (gen.mFirst)
72 {
73 gen.mFirst = false;
74 }
75 else
76 {
77 out << ", ";
78 }
79 return out;
80 }
81
82 } // namespace
83
TOutputGLSLBase(TCompiler * compiler,TInfoSinkBase & objSink,ShCompileOptions compileOptions)84 TOutputGLSLBase::TOutputGLSLBase(TCompiler *compiler,
85 TInfoSinkBase &objSink,
86 ShCompileOptions compileOptions)
87 : TIntermTraverser(true, true, true, &compiler->getSymbolTable()),
88 mObjSink(objSink),
89 mDeclaringVariable(false),
90 mHashFunction(compiler->getHashFunction()),
91 mNameMap(compiler->getNameMap()),
92 mShaderType(compiler->getShaderType()),
93 mShaderVersion(compiler->getShaderVersion()),
94 mOutput(compiler->getOutputType()),
95 mHighPrecisionSupported(compiler->isHighPrecisionSupported()),
96 mCompileOptions(compileOptions)
97 {}
98
writeInvariantQualifier(const TType & type)99 void TOutputGLSLBase::writeInvariantQualifier(const TType &type)
100 {
101 if (!sh::RemoveInvariant(mShaderType, mShaderVersion, mOutput, mCompileOptions))
102 {
103 TInfoSinkBase &out = objSink();
104 out << "invariant ";
105 }
106 }
107
writePreciseQualifier(const TType & type)108 void TOutputGLSLBase::writePreciseQualifier(const TType &type)
109 {
110 TInfoSinkBase &out = objSink();
111 out << "precise ";
112 }
113
writeFloat(TInfoSinkBase & out,float f)114 void TOutputGLSLBase::writeFloat(TInfoSinkBase &out, float f)
115 {
116 if ((gl::isInf(f) || gl::isNaN(f)) && mShaderVersion >= 300)
117 {
118 out << "uintBitsToFloat(" << gl::bitCast<uint32_t>(f) << "u)";
119 }
120 else
121 {
122 out << std::min(FLT_MAX, std::max(-FLT_MAX, f));
123 }
124 }
125
writeTriplet(Visit visit,const char * preStr,const char * inStr,const char * postStr)126 void TOutputGLSLBase::writeTriplet(Visit visit,
127 const char *preStr,
128 const char *inStr,
129 const char *postStr)
130 {
131 TInfoSinkBase &out = objSink();
132 if (visit == PreVisit && preStr)
133 out << preStr;
134 else if (visit == InVisit && inStr)
135 out << inStr;
136 else if (visit == PostVisit && postStr)
137 out << postStr;
138 }
139
writeFunctionTriplet(Visit visit,const ImmutableString & functionName,bool useEmulatedFunction)140 void TOutputGLSLBase::writeFunctionTriplet(Visit visit,
141 const ImmutableString &functionName,
142 bool useEmulatedFunction)
143 {
144 TInfoSinkBase &out = objSink();
145 if (visit == PreVisit)
146 {
147 if (useEmulatedFunction)
148 {
149 BuiltInFunctionEmulator::WriteEmulatedFunctionName(out, functionName.data());
150 }
151 else
152 {
153 out << functionName;
154 }
155 out << "(";
156 }
157 else
158 {
159 writeTriplet(visit, nullptr, ", ", ")");
160 }
161 }
162
163 // Outputs what goes inside layout(), except for location and binding qualifiers, as they are
164 // handled differently between GL GLSL and Vulkan GLSL.
getCommonLayoutQualifiers(TIntermSymbol * variable)165 std::string TOutputGLSLBase::getCommonLayoutQualifiers(TIntermSymbol *variable)
166 {
167 std::ostringstream out;
168 CommaSeparatedListItemPrefixGenerator listItemPrefix;
169
170 const TType &type = variable->getType();
171 const TLayoutQualifier &layoutQualifier = type.getLayoutQualifier();
172
173 if (type.getQualifier() == EvqFragmentOut || type.getQualifier() == EvqVertexIn ||
174 IsVarying(type.getQualifier()))
175 {
176 if (type.getQualifier() == EvqFragmentOut && layoutQualifier.index >= 0)
177 {
178 out << listItemPrefix << "index = " << layoutQualifier.index;
179 }
180 }
181
182 if (type.getQualifier() == EvqFragmentOut)
183 {
184 if (layoutQualifier.yuv == true)
185 {
186 out << listItemPrefix << "yuv";
187 }
188 }
189
190 if (IsImage(type.getBasicType()))
191 {
192 if (layoutQualifier.imageInternalFormat != EiifUnspecified)
193 {
194 ASSERT(type.getQualifier() == EvqTemporary || type.getQualifier() == EvqUniform);
195 out << listItemPrefix
196 << getImageInternalFormatString(layoutQualifier.imageInternalFormat);
197 }
198 }
199
200 if (IsAtomicCounter(type.getBasicType()))
201 {
202 out << listItemPrefix << "offset = " << layoutQualifier.offset;
203 }
204
205 return out.str();
206 }
207
208 // Outputs memory qualifiers applied to images, buffers and its fields, as well as image function
209 // arguments.
getMemoryQualifiers(const TType & type)210 std::string TOutputGLSLBase::getMemoryQualifiers(const TType &type)
211 {
212 std::ostringstream out;
213
214 const TMemoryQualifier &memoryQualifier = type.getMemoryQualifier();
215 if (memoryQualifier.readonly)
216 {
217 out << "readonly ";
218 }
219
220 if (memoryQualifier.writeonly)
221 {
222 out << "writeonly ";
223 }
224
225 if (memoryQualifier.coherent)
226 {
227 out << "coherent ";
228 }
229
230 if (memoryQualifier.restrictQualifier)
231 {
232 out << "restrict ";
233 }
234
235 if (memoryQualifier.volatileQualifier)
236 {
237 out << "volatile ";
238 }
239
240 return out.str();
241 }
242
writeLayoutQualifier(TIntermSymbol * variable)243 void TOutputGLSLBase::writeLayoutQualifier(TIntermSymbol *variable)
244 {
245 const TType &type = variable->getType();
246
247 if (!NeedsToWriteLayoutQualifier(type))
248 {
249 return;
250 }
251
252 if (type.getBasicType() == EbtInterfaceBlock)
253 {
254 declareInterfaceBlockLayout(type);
255 return;
256 }
257
258 TInfoSinkBase &out = objSink();
259 const TLayoutQualifier &layoutQualifier = type.getLayoutQualifier();
260 out << "layout(";
261
262 CommaSeparatedListItemPrefixGenerator listItemPrefix;
263
264 if (type.getQualifier() == EvqFragmentOut || type.getQualifier() == EvqVertexIn ||
265 IsVarying(type.getQualifier()))
266 {
267 if (layoutQualifier.location >= 0)
268 {
269 out << listItemPrefix << "location = " << layoutQualifier.location;
270 }
271 }
272
273 if (IsOpaqueType(type.getBasicType()))
274 {
275 if (layoutQualifier.binding >= 0)
276 {
277 out << listItemPrefix << "binding = " << layoutQualifier.binding;
278 }
279 }
280
281 std::string otherQualifiers = getCommonLayoutQualifiers(variable);
282 if (!otherQualifiers.empty())
283 {
284 out << listItemPrefix << otherQualifiers;
285 }
286
287 out << ") ";
288 }
289
writeFieldLayoutQualifier(const TField * field)290 void TOutputGLSLBase::writeFieldLayoutQualifier(const TField *field)
291 {
292 if (!field->type()->isMatrix() && !field->type()->isStructureContainingMatrices())
293 {
294 return;
295 }
296
297 TInfoSinkBase &out = objSink();
298
299 out << "layout(";
300 switch (field->type()->getLayoutQualifier().matrixPacking)
301 {
302 case EmpUnspecified:
303 case EmpColumnMajor:
304 // Default matrix packing is column major.
305 out << "column_major";
306 break;
307
308 case EmpRowMajor:
309 out << "row_major";
310 break;
311
312 default:
313 UNREACHABLE();
314 break;
315 }
316 out << ") ";
317 }
318
writeQualifier(TQualifier qualifier,const TType & type,const TSymbol * symbol)319 void TOutputGLSLBase::writeQualifier(TQualifier qualifier, const TType &type, const TSymbol *symbol)
320 {
321 const char *result = mapQualifierToString(qualifier);
322 if (result && result[0] != '\0')
323 {
324 objSink() << result << " ";
325 }
326
327 objSink() << getMemoryQualifiers(type);
328 }
329
mapQualifierToString(TQualifier qualifier)330 const char *TOutputGLSLBase::mapQualifierToString(TQualifier qualifier)
331 {
332 if (sh::IsGLSL410OrOlder(mOutput) && mShaderVersion >= 300 &&
333 (mCompileOptions & SH_REMOVE_INVARIANT_AND_CENTROID_FOR_ESSL3) != 0)
334 {
335 switch (qualifier)
336 {
337 // The return string is consistent with sh::getQualifierString() from
338 // BaseTypes.h minus the "centroid" keyword.
339 case EvqCentroid:
340 return "";
341 case EvqCentroidIn:
342 return "smooth in";
343 case EvqCentroidOut:
344 return "smooth out";
345 default:
346 break;
347 }
348 }
349 if (sh::IsGLSL130OrNewer(mOutput))
350 {
351 switch (qualifier)
352 {
353 case EvqAttribute:
354 return "in";
355 case EvqVaryingIn:
356 return "in";
357 case EvqVaryingOut:
358 return "out";
359 default:
360 break;
361 }
362 }
363
364 // Handle qualifiers that produce different output based on shader type.
365 switch (qualifier)
366 {
367 case EvqClipDistance:
368 case EvqCullDistance:
369 return mShaderType == GL_FRAGMENT_SHADER ? "in" : "out";
370 default:
371 break;
372 }
373
374 return sh::getQualifierString(qualifier);
375 }
376
377 namespace
378 {
379
380 constexpr char kIndent[] = " "; // 10x2 spaces
381 constexpr int kIndentWidth = 2;
382 constexpr int kMaxIndentLevel = sizeof(kIndent) / kIndentWidth;
383
384 } // namespace
385
getIndentPrefix(int extraIndentation)386 const char *TOutputGLSLBase::getIndentPrefix(int extraIndentation)
387 {
388 int indentDepth = std::min(kMaxIndentLevel, getCurrentBlockDepth() + extraIndentation);
389 ASSERT(indentDepth >= 0);
390 return kIndent + (kMaxIndentLevel - indentDepth) * kIndentWidth;
391 }
392
writeVariableType(const TType & type,const TSymbol * symbol,bool isFunctionArgument)393 void TOutputGLSLBase::writeVariableType(const TType &type,
394 const TSymbol *symbol,
395 bool isFunctionArgument)
396 {
397 TQualifier qualifier = type.getQualifier();
398 TInfoSinkBase &out = objSink();
399 if (type.isInvariant())
400 {
401 writeInvariantQualifier(type);
402 }
403 if (type.isPrecise())
404 {
405 writePreciseQualifier(type);
406 }
407 if (qualifier != EvqTemporary && qualifier != EvqGlobal)
408 {
409 writeQualifier(qualifier, type, symbol);
410 }
411 if (isFunctionArgument)
412 {
413 // Function arguments are the only place (other than image/SSBO/field declaration) where
414 // memory qualifiers can appear.
415 out << getMemoryQualifiers(type);
416 }
417
418 // Declare the struct.
419 if (type.isStructSpecifier())
420 {
421 const TStructure *structure = type.getStruct();
422
423 declareStruct(structure);
424 }
425 else if (type.getBasicType() == EbtInterfaceBlock)
426 {
427 declareInterfaceBlock(type);
428 }
429 else
430 {
431 if (writeVariablePrecision(type.getPrecision()))
432 out << " ";
433 out << getTypeName(type);
434 }
435 }
436
writeFunctionParameters(const TFunction * func)437 void TOutputGLSLBase::writeFunctionParameters(const TFunction *func)
438 {
439 TInfoSinkBase &out = objSink();
440 size_t paramCount = func->getParamCount();
441 for (size_t i = 0; i < paramCount; ++i)
442 {
443 const TVariable *param = func->getParam(i);
444 const TType &type = param->getType();
445 writeVariableType(type, param, true);
446
447 if (param->symbolType() != SymbolType::Empty)
448 {
449 out << " " << hashName(param);
450 }
451 if (type.isArray())
452 {
453 out << ArrayString(type);
454 }
455
456 // Put a comma if this is not the last argument.
457 if (i != paramCount - 1)
458 out << ", ";
459 }
460 }
461
writeConstantUnion(const TType & type,const TConstantUnion * pConstUnion)462 const TConstantUnion *TOutputGLSLBase::writeConstantUnion(const TType &type,
463 const TConstantUnion *pConstUnion)
464 {
465 TInfoSinkBase &out = objSink();
466
467 if (type.getBasicType() == EbtStruct)
468 {
469 const TStructure *structure = type.getStruct();
470 out << hashName(structure) << "(";
471
472 const TFieldList &fields = structure->fields();
473 for (size_t i = 0; i < fields.size(); ++i)
474 {
475 const TType *fieldType = fields[i]->type();
476 ASSERT(fieldType != nullptr);
477 pConstUnion = writeConstantUnion(*fieldType, pConstUnion);
478 if (i != fields.size() - 1)
479 out << ", ";
480 }
481 out << ")";
482 }
483 else
484 {
485 size_t size = type.getObjectSize();
486 bool writeType = size > 1;
487 if (writeType)
488 out << getTypeName(type) << "(";
489 for (size_t i = 0; i < size; ++i, ++pConstUnion)
490 {
491 switch (pConstUnion->getType())
492 {
493 case EbtFloat:
494 writeFloat(out, pConstUnion->getFConst());
495 break;
496 case EbtInt:
497 out << pConstUnion->getIConst();
498 break;
499 case EbtUInt:
500 out << pConstUnion->getUConst() << "u";
501 break;
502 case EbtBool:
503 out << pConstUnion->getBConst();
504 break;
505 case EbtYuvCscStandardEXT:
506 out << getYuvCscStandardEXTString(pConstUnion->getYuvCscStandardEXTConst());
507 break;
508 default:
509 UNREACHABLE();
510 }
511 if (i != size - 1)
512 out << ", ";
513 }
514 if (writeType)
515 out << ")";
516 }
517 return pConstUnion;
518 }
519
writeConstructorTriplet(Visit visit,const TType & type)520 void TOutputGLSLBase::writeConstructorTriplet(Visit visit, const TType &type)
521 {
522 TInfoSinkBase &out = objSink();
523 if (visit == PreVisit)
524 {
525 if (type.isArray())
526 {
527 out << getTypeName(type);
528 out << ArrayString(type);
529 out << "(";
530 }
531 else
532 {
533 out << getTypeName(type) << "(";
534 }
535 }
536 else
537 {
538 writeTriplet(visit, nullptr, ", ", ")");
539 }
540 }
541
visitSymbol(TIntermSymbol * node)542 void TOutputGLSLBase::visitSymbol(TIntermSymbol *node)
543 {
544 TInfoSinkBase &out = objSink();
545 out << hashName(&node->variable());
546
547 if (mDeclaringVariable && node->getType().isArray())
548 out << ArrayString(node->getType());
549 }
550
visitConstantUnion(TIntermConstantUnion * node)551 void TOutputGLSLBase::visitConstantUnion(TIntermConstantUnion *node)
552 {
553 writeConstantUnion(node->getType(), node->getConstantValue());
554 }
555
visitSwizzle(Visit visit,TIntermSwizzle * node)556 bool TOutputGLSLBase::visitSwizzle(Visit visit, TIntermSwizzle *node)
557 {
558 TInfoSinkBase &out = objSink();
559 if (visit == PostVisit)
560 {
561 out << ".";
562 node->writeOffsetsAsXYZW(&out);
563 }
564 return true;
565 }
566
visitBinary(Visit visit,TIntermBinary * node)567 bool TOutputGLSLBase::visitBinary(Visit visit, TIntermBinary *node)
568 {
569 bool visitChildren = true;
570 TInfoSinkBase &out = objSink();
571 switch (node->getOp())
572 {
573 case EOpComma:
574 writeTriplet(visit, "(", ", ", ")");
575 break;
576 case EOpInitialize:
577 if (visit == InVisit)
578 {
579 out << " = ";
580 // RHS of initialize is not being declared.
581 mDeclaringVariable = false;
582 }
583 break;
584 case EOpAssign:
585 writeTriplet(visit, "(", " = ", ")");
586 break;
587 case EOpAddAssign:
588 writeTriplet(visit, "(", " += ", ")");
589 break;
590 case EOpSubAssign:
591 writeTriplet(visit, "(", " -= ", ")");
592 break;
593 case EOpDivAssign:
594 writeTriplet(visit, "(", " /= ", ")");
595 break;
596 case EOpIModAssign:
597 writeTriplet(visit, "(", " %= ", ")");
598 break;
599 // Notice the fall-through.
600 case EOpMulAssign:
601 case EOpVectorTimesMatrixAssign:
602 case EOpVectorTimesScalarAssign:
603 case EOpMatrixTimesScalarAssign:
604 case EOpMatrixTimesMatrixAssign:
605 writeTriplet(visit, "(", " *= ", ")");
606 break;
607 case EOpBitShiftLeftAssign:
608 writeTriplet(visit, "(", " <<= ", ")");
609 break;
610 case EOpBitShiftRightAssign:
611 writeTriplet(visit, "(", " >>= ", ")");
612 break;
613 case EOpBitwiseAndAssign:
614 writeTriplet(visit, "(", " &= ", ")");
615 break;
616 case EOpBitwiseXorAssign:
617 writeTriplet(visit, "(", " ^= ", ")");
618 break;
619 case EOpBitwiseOrAssign:
620 writeTriplet(visit, "(", " |= ", ")");
621 break;
622
623 case EOpIndexDirect:
624 case EOpIndexIndirect:
625 writeTriplet(visit, nullptr, "[", "]");
626 break;
627 case EOpIndexDirectStruct:
628 if (visit == InVisit)
629 {
630 // Here we are writing out "foo.bar", where "foo" is struct
631 // and "bar" is field. In AST, it is represented as a binary
632 // node, where left child represents "foo" and right child "bar".
633 // The node itself represents ".". The struct field "bar" is
634 // actually stored as an index into TStructure::fields.
635 out << ".";
636 const TStructure *structure = node->getLeft()->getType().getStruct();
637 const TIntermConstantUnion *index = node->getRight()->getAsConstantUnion();
638 const TField *field = structure->fields()[index->getIConst(0)];
639
640 out << hashFieldName(field);
641 visitChildren = false;
642 }
643 break;
644 case EOpIndexDirectInterfaceBlock:
645 if (visit == InVisit)
646 {
647 out << ".";
648 const TInterfaceBlock *interfaceBlock =
649 node->getLeft()->getType().getInterfaceBlock();
650 const TIntermConstantUnion *index = node->getRight()->getAsConstantUnion();
651 const TField *field = interfaceBlock->fields()[index->getIConst(0)];
652 out << hashFieldName(field);
653 visitChildren = false;
654 }
655 break;
656
657 case EOpAdd:
658 writeTriplet(visit, "(", " + ", ")");
659 break;
660 case EOpSub:
661 writeTriplet(visit, "(", " - ", ")");
662 break;
663 case EOpMul:
664 writeTriplet(visit, "(", " * ", ")");
665 break;
666 case EOpDiv:
667 writeTriplet(visit, "(", " / ", ")");
668 break;
669 case EOpIMod:
670 writeTriplet(visit, "(", " % ", ")");
671 break;
672 case EOpBitShiftLeft:
673 writeTriplet(visit, "(", " << ", ")");
674 break;
675 case EOpBitShiftRight:
676 writeTriplet(visit, "(", " >> ", ")");
677 break;
678 case EOpBitwiseAnd:
679 writeTriplet(visit, "(", " & ", ")");
680 break;
681 case EOpBitwiseXor:
682 writeTriplet(visit, "(", " ^ ", ")");
683 break;
684 case EOpBitwiseOr:
685 writeTriplet(visit, "(", " | ", ")");
686 break;
687
688 case EOpEqual:
689 writeTriplet(visit, "(", " == ", ")");
690 break;
691 case EOpNotEqual:
692 writeTriplet(visit, "(", " != ", ")");
693 break;
694 case EOpLessThan:
695 writeTriplet(visit, "(", " < ", ")");
696 break;
697 case EOpGreaterThan:
698 writeTriplet(visit, "(", " > ", ")");
699 break;
700 case EOpLessThanEqual:
701 writeTriplet(visit, "(", " <= ", ")");
702 break;
703 case EOpGreaterThanEqual:
704 writeTriplet(visit, "(", " >= ", ")");
705 break;
706
707 // Notice the fall-through.
708 case EOpVectorTimesScalar:
709 case EOpVectorTimesMatrix:
710 case EOpMatrixTimesVector:
711 case EOpMatrixTimesScalar:
712 case EOpMatrixTimesMatrix:
713 writeTriplet(visit, "(", " * ", ")");
714 break;
715
716 case EOpLogicalOr:
717 writeTriplet(visit, "(", " || ", ")");
718 break;
719 case EOpLogicalXor:
720 writeTriplet(visit, "(", " ^^ ", ")");
721 break;
722 case EOpLogicalAnd:
723 writeTriplet(visit, "(", " && ", ")");
724 break;
725 default:
726 UNREACHABLE();
727 }
728
729 return visitChildren;
730 }
731
visitUnary(Visit visit,TIntermUnary * node)732 bool TOutputGLSLBase::visitUnary(Visit visit, TIntermUnary *node)
733 {
734 const char *preString = "";
735 const char *postString = ")";
736
737 switch (node->getOp())
738 {
739 case EOpNegative:
740 preString = "(-";
741 break;
742 case EOpPositive:
743 preString = "(+";
744 break;
745 case EOpLogicalNot:
746 preString = "(!";
747 break;
748 case EOpBitwiseNot:
749 preString = "(~";
750 break;
751
752 case EOpPostIncrement:
753 preString = "(";
754 postString = "++)";
755 break;
756 case EOpPostDecrement:
757 preString = "(";
758 postString = "--)";
759 break;
760 case EOpPreIncrement:
761 preString = "(++";
762 break;
763 case EOpPreDecrement:
764 preString = "(--";
765 break;
766 case EOpArrayLength:
767 preString = "((";
768 postString = ").length())";
769 break;
770
771 default:
772 writeFunctionTriplet(visit, node->getFunction()->name(),
773 node->getUseEmulatedFunction());
774 return true;
775 }
776
777 writeTriplet(visit, preString, nullptr, postString);
778
779 return true;
780 }
781
visitTernary(Visit visit,TIntermTernary * node)782 bool TOutputGLSLBase::visitTernary(Visit visit, TIntermTernary *node)
783 {
784 TInfoSinkBase &out = objSink();
785 // Notice two brackets at the beginning and end. The outer ones
786 // encapsulate the whole ternary expression. This preserves the
787 // order of precedence when ternary expressions are used in a
788 // compound expression, i.e., c = 2 * (a < b ? 1 : 2).
789 out << "((";
790 node->getCondition()->traverse(this);
791 out << ") ? (";
792 node->getTrueExpression()->traverse(this);
793 out << ") : (";
794 node->getFalseExpression()->traverse(this);
795 out << "))";
796 return false;
797 }
798
visitIfElse(Visit visit,TIntermIfElse * node)799 bool TOutputGLSLBase::visitIfElse(Visit visit, TIntermIfElse *node)
800 {
801 TInfoSinkBase &out = objSink();
802
803 out << "if (";
804 node->getCondition()->traverse(this);
805 out << ")\n";
806
807 visitCodeBlock(node->getTrueBlock());
808
809 if (node->getFalseBlock())
810 {
811 out << getIndentPrefix() << "else\n";
812 visitCodeBlock(node->getFalseBlock());
813 }
814 return false;
815 }
816
visitSwitch(Visit visit,TIntermSwitch * node)817 bool TOutputGLSLBase::visitSwitch(Visit visit, TIntermSwitch *node)
818 {
819 ASSERT(node->getStatementList());
820 writeTriplet(visit, "switch (", ") ", nullptr);
821 // The curly braces get written when visiting the statementList aggregate
822 return true;
823 }
824
visitCase(Visit visit,TIntermCase * node)825 bool TOutputGLSLBase::visitCase(Visit visit, TIntermCase *node)
826 {
827 if (node->hasCondition())
828 {
829 writeTriplet(visit, "case (", nullptr, "):\n");
830 return true;
831 }
832 else
833 {
834 TInfoSinkBase &out = objSink();
835 out << "default:\n";
836 return false;
837 }
838 }
839
visitBlock(Visit visit,TIntermBlock * node)840 bool TOutputGLSLBase::visitBlock(Visit visit, TIntermBlock *node)
841 {
842 TInfoSinkBase &out = objSink();
843 // Scope the blocks except when at the global scope.
844 if (getCurrentTraversalDepth() > 0)
845 {
846 out << "{\n";
847 }
848
849 for (TIntermSequence::const_iterator iter = node->getSequence()->begin();
850 iter != node->getSequence()->end(); ++iter)
851 {
852 TIntermNode *curNode = *iter;
853 ASSERT(curNode != nullptr);
854
855 out << getIndentPrefix(curNode->getAsCaseNode() ? -1 : 0);
856
857 curNode->traverse(this);
858
859 if (isSingleStatement(curNode))
860 out << ";\n";
861 }
862
863 // Scope the blocks except when at the global scope.
864 if (getCurrentTraversalDepth() > 0)
865 {
866 out << getIndentPrefix(-1) << "}\n";
867 }
868 return false;
869 }
870
visitFunctionDefinition(Visit visit,TIntermFunctionDefinition * node)871 bool TOutputGLSLBase::visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node)
872 {
873 TIntermFunctionPrototype *prototype = node->getFunctionPrototype();
874 prototype->traverse(this);
875 visitCodeBlock(node->getBody());
876
877 // Fully processed; no need to visit children.
878 return false;
879 }
880
visitGlobalQualifierDeclaration(Visit visit,TIntermGlobalQualifierDeclaration * node)881 bool TOutputGLSLBase::visitGlobalQualifierDeclaration(Visit visit,
882 TIntermGlobalQualifierDeclaration *node)
883 {
884 TInfoSinkBase &out = objSink();
885 ASSERT(visit == PreVisit);
886 const TIntermSymbol *symbol = node->getSymbol();
887 out << (node->isPrecise() ? "precise " : "invariant ") << hashName(&symbol->variable());
888 return false;
889 }
890
visitFunctionPrototype(TIntermFunctionPrototype * node)891 void TOutputGLSLBase::visitFunctionPrototype(TIntermFunctionPrototype *node)
892 {
893 TInfoSinkBase &out = objSink();
894
895 const TType &type = node->getType();
896 writeVariableType(type, node->getFunction(), false);
897 if (type.isArray())
898 out << ArrayString(type);
899
900 out << " " << hashFunctionNameIfNeeded(node->getFunction());
901
902 out << "(";
903 writeFunctionParameters(node->getFunction());
904 out << ")";
905 }
906
visitAggregate(Visit visit,TIntermAggregate * node)907 bool TOutputGLSLBase::visitAggregate(Visit visit, TIntermAggregate *node)
908 {
909 bool visitChildren = true;
910 if (node->getOp() == EOpConstruct)
911 {
912 writeConstructorTriplet(visit, node->getType());
913 }
914 else
915 {
916 // Function call.
917 ImmutableString functionName = node->getFunction()->name();
918 if (visit == PreVisit)
919 {
920 // No raw function is expected.
921 ASSERT(node->getOp() != EOpCallInternalRawFunction);
922
923 if (node->getOp() == EOpCallFunctionInAST)
924 {
925 functionName = hashFunctionNameIfNeeded(node->getFunction());
926 }
927 else
928 {
929 functionName =
930 translateTextureFunction(node->getFunction()->name(), mCompileOptions);
931 }
932 }
933 writeFunctionTriplet(visit, functionName, node->getUseEmulatedFunction());
934 }
935 return visitChildren;
936 }
937
visitDeclaration(Visit visit,TIntermDeclaration * node)938 bool TOutputGLSLBase::visitDeclaration(Visit visit, TIntermDeclaration *node)
939 {
940 TInfoSinkBase &out = objSink();
941
942 // Variable declaration.
943 if (visit == PreVisit)
944 {
945 const TIntermSequence &sequence = *(node->getSequence());
946 TIntermTyped *decl = sequence.front()->getAsTyped();
947 TIntermSymbol *symbolNode = decl->getAsSymbolNode();
948 if (symbolNode == nullptr)
949 {
950 ASSERT(decl->getAsBinaryNode() && decl->getAsBinaryNode()->getOp() == EOpInitialize);
951 symbolNode = decl->getAsBinaryNode()->getLeft()->getAsSymbolNode();
952 }
953 ASSERT(symbolNode);
954
955 if (symbolNode->getName() != "gl_ClipDistance" &&
956 symbolNode->getName() != "gl_CullDistance")
957 {
958 // gl_Clip/CullDistance re-declaration doesn't need layout.
959 writeLayoutQualifier(symbolNode);
960 }
961
962 writeVariableType(symbolNode->getType(), &symbolNode->variable(), false);
963 if (symbolNode->variable().symbolType() != SymbolType::Empty)
964 {
965 out << " ";
966 }
967 mDeclaringVariable = true;
968 }
969 else if (visit == InVisit)
970 {
971 UNREACHABLE();
972 }
973 else
974 {
975 mDeclaringVariable = false;
976 }
977 return true;
978 }
979
visitLoop(Visit visit,TIntermLoop * node)980 bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop *node)
981 {
982 TInfoSinkBase &out = objSink();
983
984 TLoopType loopType = node->getType();
985
986 if (loopType == ELoopFor) // for loop
987 {
988 out << "for (";
989 if (node->getInit())
990 node->getInit()->traverse(this);
991 out << "; ";
992
993 if (node->getCondition())
994 node->getCondition()->traverse(this);
995 out << "; ";
996
997 if (node->getExpression())
998 node->getExpression()->traverse(this);
999 out << ")\n";
1000
1001 visitCodeBlock(node->getBody());
1002 }
1003 else if (loopType == ELoopWhile) // while loop
1004 {
1005 out << "while (";
1006 ASSERT(node->getCondition() != nullptr);
1007 node->getCondition()->traverse(this);
1008 out << ")\n";
1009
1010 visitCodeBlock(node->getBody());
1011 }
1012 else // do-while loop
1013 {
1014 ASSERT(loopType == ELoopDoWhile);
1015 out << "do\n";
1016
1017 visitCodeBlock(node->getBody());
1018
1019 out << "while (";
1020 ASSERT(node->getCondition() != nullptr);
1021 node->getCondition()->traverse(this);
1022 out << ");\n";
1023 }
1024
1025 // No need to visit children. They have been already processed in
1026 // this function.
1027 return false;
1028 }
1029
visitBranch(Visit visit,TIntermBranch * node)1030 bool TOutputGLSLBase::visitBranch(Visit visit, TIntermBranch *node)
1031 {
1032 switch (node->getFlowOp())
1033 {
1034 case EOpKill:
1035 writeTriplet(visit, "discard", nullptr, nullptr);
1036 break;
1037 case EOpBreak:
1038 writeTriplet(visit, "break", nullptr, nullptr);
1039 break;
1040 case EOpContinue:
1041 writeTriplet(visit, "continue", nullptr, nullptr);
1042 break;
1043 case EOpReturn:
1044 writeTriplet(visit, "return ", nullptr, nullptr);
1045 break;
1046 default:
1047 UNREACHABLE();
1048 }
1049
1050 return true;
1051 }
1052
visitCodeBlock(TIntermBlock * node)1053 void TOutputGLSLBase::visitCodeBlock(TIntermBlock *node)
1054 {
1055 TInfoSinkBase &out = objSink();
1056 if (node != nullptr)
1057 {
1058 out << getIndentPrefix();
1059 node->traverse(this);
1060 // Single statements not part of a sequence need to be terminated
1061 // with semi-colon.
1062 if (isSingleStatement(node))
1063 out << ";\n";
1064 }
1065 else
1066 {
1067 out << "{\n}\n"; // Empty code block.
1068 }
1069 }
1070
visitPreprocessorDirective(TIntermPreprocessorDirective * node)1071 void TOutputGLSLBase::visitPreprocessorDirective(TIntermPreprocessorDirective *node)
1072 {
1073 TInfoSinkBase &out = objSink();
1074
1075 out << "\n";
1076
1077 switch (node->getDirective())
1078 {
1079 case PreprocessorDirective::Define:
1080 out << "#define";
1081 break;
1082 case PreprocessorDirective::Endif:
1083 out << "#endif";
1084 break;
1085 case PreprocessorDirective::If:
1086 out << "#if";
1087 break;
1088 case PreprocessorDirective::Ifdef:
1089 out << "#ifdef";
1090 break;
1091
1092 default:
1093 UNREACHABLE();
1094 break;
1095 }
1096
1097 if (!node->getCommand().empty())
1098 {
1099 out << " " << node->getCommand();
1100 }
1101
1102 out << "\n";
1103 }
1104
getTypeName(const TType & type)1105 ImmutableString TOutputGLSLBase::getTypeName(const TType &type)
1106 {
1107 if (type.getBasicType() == EbtSamplerVideoWEBGL)
1108 {
1109 // TODO(http://anglebug.com/3889): translate SamplerVideoWEBGL into different token
1110 // when necessary (e.g. on Android devices)
1111 return ImmutableString("sampler2D");
1112 }
1113
1114 return GetTypeName(type, mHashFunction, &mNameMap);
1115 }
1116
hashName(const TSymbol * symbol)1117 ImmutableString TOutputGLSLBase::hashName(const TSymbol *symbol)
1118 {
1119 return HashName(symbol, mHashFunction, &mNameMap);
1120 }
1121
hashFieldName(const TField * field)1122 ImmutableString TOutputGLSLBase::hashFieldName(const TField *field)
1123 {
1124 ASSERT(field->symbolType() != SymbolType::Empty);
1125 if (field->symbolType() == SymbolType::UserDefined)
1126 {
1127 return HashName(field->name(), mHashFunction, &mNameMap);
1128 }
1129
1130 return field->name();
1131 }
1132
hashFunctionNameIfNeeded(const TFunction * func)1133 ImmutableString TOutputGLSLBase::hashFunctionNameIfNeeded(const TFunction *func)
1134 {
1135 if (func->isMain())
1136 {
1137 return func->name();
1138 }
1139 else
1140 {
1141 return hashName(func);
1142 }
1143 }
1144
declareStruct(const TStructure * structure)1145 void TOutputGLSLBase::declareStruct(const TStructure *structure)
1146 {
1147 TInfoSinkBase &out = objSink();
1148
1149 out << "struct ";
1150
1151 if (structure->symbolType() != SymbolType::Empty)
1152 {
1153 out << hashName(structure) << " ";
1154 }
1155 out << "{\n";
1156 const TFieldList &fields = structure->fields();
1157 for (size_t i = 0; i < fields.size(); ++i)
1158 {
1159 out << getIndentPrefix(1);
1160 const TField *field = fields[i];
1161 const TType &fieldType = *field->type();
1162 if (writeVariablePrecision(fieldType.getPrecision()))
1163 {
1164 out << " ";
1165 }
1166 if (fieldType.isPrecise())
1167 {
1168 writePreciseQualifier(fieldType);
1169 }
1170 out << getTypeName(fieldType) << " " << hashFieldName(field);
1171 if (fieldType.isArray())
1172 {
1173 out << ArrayString(fieldType);
1174 }
1175 out << ";\n";
1176 }
1177 out << getIndentPrefix() << "}";
1178 }
1179
declareInterfaceBlockLayout(const TType & type)1180 void TOutputGLSLBase::declareInterfaceBlockLayout(const TType &type)
1181 {
1182 // 4.4.5 Uniform and Shader Storage Block Layout Qualifiers in GLSL 4.5 spec.
1183 // Layout qualifiers can be used for uniform and shader storage blocks,
1184 // but not for non-block uniform declarations.
1185 if (IsShaderIoBlock(type.getQualifier()))
1186 {
1187 return;
1188 }
1189
1190 const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock();
1191 TInfoSinkBase &out = objSink();
1192
1193 out << "layout(";
1194
1195 switch (interfaceBlock->blockStorage())
1196 {
1197 case EbsUnspecified:
1198 case EbsShared:
1199 // Default block storage is shared.
1200 out << "shared";
1201 break;
1202
1203 case EbsPacked:
1204 out << "packed";
1205 break;
1206
1207 case EbsStd140:
1208 out << "std140";
1209 break;
1210
1211 case EbsStd430:
1212 out << "std430";
1213 break;
1214
1215 default:
1216 UNREACHABLE();
1217 break;
1218 }
1219
1220 if (interfaceBlock->blockBinding() >= 0)
1221 {
1222 out << ", ";
1223 out << "binding = " << interfaceBlock->blockBinding();
1224 }
1225
1226 out << ") ";
1227 }
1228
getVariableInterpolation(TQualifier qualifier)1229 const char *getVariableInterpolation(TQualifier qualifier)
1230 {
1231 switch (qualifier)
1232 {
1233 case EvqSmoothOut:
1234 return "smooth out ";
1235 case EvqFlatOut:
1236 return "flat out ";
1237 case EvqNoPerspectiveOut:
1238 return "noperspective out ";
1239 case EvqCentroidOut:
1240 return "centroid out ";
1241 case EvqSmoothIn:
1242 return "smooth in ";
1243 case EvqFlatIn:
1244 return "flat in ";
1245 case EvqNoPerspectiveIn:
1246 return "noperspective in ";
1247 case EvqCentroidIn:
1248 return "centroid in ";
1249 default:
1250 break;
1251 }
1252 return nullptr;
1253 }
1254
declareInterfaceBlock(const TType & type)1255 void TOutputGLSLBase::declareInterfaceBlock(const TType &type)
1256 {
1257 const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock();
1258 TInfoSinkBase &out = objSink();
1259
1260 out << hashName(interfaceBlock) << "{\n";
1261 const TFieldList &fields = interfaceBlock->fields();
1262 for (const TField *field : fields)
1263 {
1264 out << getIndentPrefix(1);
1265 if (!IsShaderIoBlock(type.getQualifier()) && type.getQualifier() != EvqPatchIn &&
1266 type.getQualifier() != EvqPatchOut)
1267 {
1268 writeFieldLayoutQualifier(field);
1269 }
1270
1271 const TType &fieldType = *field->type();
1272
1273 out << getMemoryQualifiers(fieldType);
1274 if (writeVariablePrecision(fieldType.getPrecision()))
1275 out << " ";
1276 if (fieldType.isInvariant())
1277 {
1278 writeInvariantQualifier(fieldType);
1279 }
1280 if (fieldType.isPrecise())
1281 {
1282 writePreciseQualifier(fieldType);
1283 }
1284
1285 const char *qualifier = getVariableInterpolation(fieldType.getQualifier());
1286 if (qualifier != nullptr)
1287 out << qualifier;
1288
1289 out << getTypeName(fieldType) << " " << hashFieldName(field);
1290
1291 if (fieldType.isArray())
1292 out << ArrayString(fieldType);
1293 out << ";\n";
1294 }
1295 out << "}";
1296 }
1297
WritePragma(TInfoSinkBase & out,ShCompileOptions compileOptions,const TPragma & pragma)1298 void WritePragma(TInfoSinkBase &out, ShCompileOptions compileOptions, const TPragma &pragma)
1299 {
1300 if ((compileOptions & SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL) == 0)
1301 {
1302 if (pragma.stdgl.invariantAll)
1303 out << "#pragma STDGL invariant(all)\n";
1304 }
1305 }
1306
WriteGeometryShaderLayoutQualifiers(TInfoSinkBase & out,sh::TLayoutPrimitiveType inputPrimitive,int invocations,sh::TLayoutPrimitiveType outputPrimitive,int maxVertices)1307 void WriteGeometryShaderLayoutQualifiers(TInfoSinkBase &out,
1308 sh::TLayoutPrimitiveType inputPrimitive,
1309 int invocations,
1310 sh::TLayoutPrimitiveType outputPrimitive,
1311 int maxVertices)
1312 {
1313 // Omit 'invocations = 1'
1314 if (inputPrimitive != EptUndefined || invocations > 1)
1315 {
1316 out << "layout (";
1317
1318 if (inputPrimitive != EptUndefined)
1319 {
1320 out << getGeometryShaderPrimitiveTypeString(inputPrimitive);
1321 }
1322
1323 if (invocations > 1)
1324 {
1325 if (inputPrimitive != EptUndefined)
1326 {
1327 out << ", ";
1328 }
1329 out << "invocations = " << invocations;
1330 }
1331 out << ") in;\n";
1332 }
1333
1334 if (outputPrimitive != EptUndefined || maxVertices != -1)
1335 {
1336 out << "layout (";
1337
1338 if (outputPrimitive != EptUndefined)
1339 {
1340 out << getGeometryShaderPrimitiveTypeString(outputPrimitive);
1341 }
1342
1343 if (maxVertices != -1)
1344 {
1345 if (outputPrimitive != EptUndefined)
1346 {
1347 out << ", ";
1348 }
1349 out << "max_vertices = " << maxVertices;
1350 }
1351 out << ") out;\n";
1352 }
1353 }
1354
WriteTessControlShaderLayoutQualifiers(TInfoSinkBase & out,int inputVertices)1355 void WriteTessControlShaderLayoutQualifiers(TInfoSinkBase &out, int inputVertices)
1356 {
1357 if (inputVertices != 0)
1358 {
1359 out << "layout (vertices = " << inputVertices << ") out;\n";
1360 }
1361 }
1362
WriteTessEvaluationShaderLayoutQualifiers(TInfoSinkBase & out,sh::TLayoutTessEvaluationType inputPrimitive,sh::TLayoutTessEvaluationType inputVertexSpacing,sh::TLayoutTessEvaluationType inputOrdering,sh::TLayoutTessEvaluationType inputPoint)1363 void WriteTessEvaluationShaderLayoutQualifiers(TInfoSinkBase &out,
1364 sh::TLayoutTessEvaluationType inputPrimitive,
1365 sh::TLayoutTessEvaluationType inputVertexSpacing,
1366 sh::TLayoutTessEvaluationType inputOrdering,
1367 sh::TLayoutTessEvaluationType inputPoint)
1368 {
1369 if (inputPrimitive != EtetUndefined)
1370 {
1371 out << "layout (";
1372 out << getTessEvaluationShaderTypeString(inputPrimitive);
1373 if (inputVertexSpacing != EtetUndefined)
1374 {
1375 out << ", " << getTessEvaluationShaderTypeString(inputVertexSpacing);
1376 }
1377 if (inputOrdering != EtetUndefined)
1378 {
1379 out << ", " << getTessEvaluationShaderTypeString(inputOrdering);
1380 }
1381 if (inputPoint != EtetUndefined)
1382 {
1383 out << ", " << getTessEvaluationShaderTypeString(inputPoint);
1384 }
1385 out << ") in;\n";
1386 }
1387 }
1388
1389 // If SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS is enabled, layout qualifiers are spilled whenever
1390 // variables with specified layout qualifiers are copied. Additional checks are needed against the
1391 // type and storage qualifier of the variable to verify that layout qualifiers have to be outputted.
1392 // TODO (mradev): Fix layout qualifier spilling in ScalarizeVecAndMatConstructorArgs and remove
1393 // NeedsToWriteLayoutQualifier.
NeedsToWriteLayoutQualifier(const TType & type)1394 bool NeedsToWriteLayoutQualifier(const TType &type)
1395 {
1396 if (type.getBasicType() == EbtInterfaceBlock)
1397 {
1398 return true;
1399 }
1400
1401 const TLayoutQualifier &layoutQualifier = type.getLayoutQualifier();
1402
1403 if ((type.getQualifier() == EvqFragmentOut || type.getQualifier() == EvqVertexIn ||
1404 IsVarying(type.getQualifier())) &&
1405 layoutQualifier.location >= 0)
1406 {
1407 return true;
1408 }
1409
1410 if (type.getQualifier() == EvqFragmentOut && layoutQualifier.yuv == true)
1411 {
1412 return true;
1413 }
1414
1415 if (IsOpaqueType(type.getBasicType()) && layoutQualifier.binding != -1)
1416 {
1417 return true;
1418 }
1419
1420 if (IsImage(type.getBasicType()) && layoutQualifier.imageInternalFormat != EiifUnspecified)
1421 {
1422 return true;
1423 }
1424 return false;
1425 }
1426
EmitEarlyFragmentTestsGLSL(const TCompiler & compiler,TInfoSinkBase & sink)1427 void EmitEarlyFragmentTestsGLSL(const TCompiler &compiler, TInfoSinkBase &sink)
1428 {
1429 if (compiler.isEarlyFragmentTestsSpecified() || compiler.isEarlyFragmentTestsOptimized())
1430 {
1431 sink << "layout (early_fragment_tests) in;\n";
1432 }
1433 }
1434
EmitWorkGroupSizeGLSL(const TCompiler & compiler,TInfoSinkBase & sink)1435 void EmitWorkGroupSizeGLSL(const TCompiler &compiler, TInfoSinkBase &sink)
1436 {
1437 if (compiler.isComputeShaderLocalSizeDeclared())
1438 {
1439 const sh::WorkGroupSize &localSize = compiler.getComputeShaderLocalSize();
1440 sink << "layout (local_size_x=" << localSize[0] << ", local_size_y=" << localSize[1]
1441 << ", local_size_z=" << localSize[2] << ") in;\n";
1442 }
1443 }
1444
EmitMultiviewGLSL(const TCompiler & compiler,const ShCompileOptions & compileOptions,const TExtension extension,const TBehavior behavior,TInfoSinkBase & sink)1445 void EmitMultiviewGLSL(const TCompiler &compiler,
1446 const ShCompileOptions &compileOptions,
1447 const TExtension extension,
1448 const TBehavior behavior,
1449 TInfoSinkBase &sink)
1450 {
1451 ASSERT(behavior != EBhUndefined);
1452 if (behavior == EBhDisable)
1453 return;
1454
1455 const bool isVertexShader = (compiler.getShaderType() == GL_VERTEX_SHADER);
1456 if ((compileOptions & SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW) != 0)
1457 {
1458 // Emit ARB_shader_viewport_layer_array/NV_viewport_array2 in a vertex shader if the
1459 // SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER option is set and the
1460 // OVR_multiview(2) extension is requested.
1461 if (isVertexShader && (compileOptions & SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER) != 0)
1462 {
1463 sink << "#if defined(GL_ARB_shader_viewport_layer_array)\n"
1464 << "#extension GL_ARB_shader_viewport_layer_array : require\n"
1465 << "#elif defined(GL_NV_viewport_array2)\n"
1466 << "#extension GL_NV_viewport_array2 : require\n"
1467 << "#endif\n";
1468 }
1469 }
1470 else
1471 {
1472 sink << "#extension GL_OVR_multiview";
1473 if (extension == TExtension::OVR_multiview2)
1474 {
1475 sink << "2";
1476 }
1477 sink << " : " << GetBehaviorString(behavior) << "\n";
1478
1479 const auto &numViews = compiler.getNumViews();
1480 if (isVertexShader && numViews != -1)
1481 {
1482 sink << "layout(num_views=" << numViews << ") in;\n";
1483 }
1484 }
1485 }
1486
1487 } // namespace sh
1488