1 | // |
2 | // Copyright (c) 2002-2014 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 | |
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: |
59 | CommaSeparatedListItemPrefixGenerator() : mFirst(true) {} |
60 | |
61 | private: |
62 | bool mFirst; |
63 | |
64 | friend TInfoSinkBase &operator<<(TInfoSinkBase &out, |
65 | CommaSeparatedListItemPrefixGenerator &gen); |
66 | }; |
67 | |
68 | TInfoSinkBase &operator<<(TInfoSinkBase &out, CommaSeparatedListItemPrefixGenerator &gen) |
69 | { |
70 | if (gen.mFirst) |
71 | { |
72 | gen.mFirst = false; |
73 | } |
74 | else |
75 | { |
76 | out << ", " ; |
77 | } |
78 | return out; |
79 | } |
80 | |
81 | } // namespace |
82 | |
83 | TOutputGLSLBase::TOutputGLSLBase(TInfoSinkBase &objSink, |
84 | ShArrayIndexClampingStrategy clampingStrategy, |
85 | ShHashFunction64 hashFunction, |
86 | NameMap &nameMap, |
87 | TSymbolTable *symbolTable, |
88 | sh::GLenum shaderType, |
89 | int shaderVersion, |
90 | ShShaderOutput output, |
91 | ShCompileOptions compileOptions) |
92 | : TIntermTraverser(true, true, true, symbolTable), |
93 | mObjSink(objSink), |
94 | mDeclaringVariable(false), |
95 | mClampingStrategy(clampingStrategy), |
96 | mHashFunction(hashFunction), |
97 | mNameMap(nameMap), |
98 | mShaderType(shaderType), |
99 | mShaderVersion(shaderVersion), |
100 | mOutput(output), |
101 | mCompileOptions(compileOptions) |
102 | {} |
103 | |
104 | void TOutputGLSLBase::writeInvariantQualifier(const TType &type) |
105 | { |
106 | if (!sh::RemoveInvariant(mShaderType, mShaderVersion, mOutput, mCompileOptions)) |
107 | { |
108 | TInfoSinkBase &out = objSink(); |
109 | out << "invariant " ; |
110 | } |
111 | } |
112 | |
113 | void TOutputGLSLBase::writeFloat(TInfoSinkBase &out, float f) |
114 | { |
115 | if ((gl::isInf(f) || gl::isNaN(f)) && mShaderVersion >= 300) |
116 | { |
117 | out << "uintBitsToFloat(" << gl::bitCast<uint32_t>(f) << "u)" ; |
118 | } |
119 | else |
120 | { |
121 | out << std::min(FLT_MAX, std::max(-FLT_MAX, f)); |
122 | } |
123 | } |
124 | |
125 | void TOutputGLSLBase::writeTriplet(Visit visit, |
126 | const char *preStr, |
127 | const char *inStr, |
128 | const char *postStr) |
129 | { |
130 | TInfoSinkBase &out = objSink(); |
131 | if (visit == PreVisit && preStr) |
132 | out << preStr; |
133 | else if (visit == InVisit && inStr) |
134 | out << inStr; |
135 | else if (visit == PostVisit && postStr) |
136 | out << postStr; |
137 | } |
138 | |
139 | void TOutputGLSLBase::writeBuiltInFunctionTriplet(Visit visit, |
140 | TOperator op, |
141 | bool useEmulatedFunction) |
142 | { |
143 | TInfoSinkBase &out = objSink(); |
144 | if (visit == PreVisit) |
145 | { |
146 | const char *opStr(GetOperatorString(op)); |
147 | if (useEmulatedFunction) |
148 | { |
149 | BuiltInFunctionEmulator::WriteEmulatedFunctionName(out, opStr); |
150 | } |
151 | else |
152 | { |
153 | out << opStr; |
154 | } |
155 | out << "(" ; |
156 | } |
157 | else |
158 | { |
159 | writeTriplet(visit, nullptr, ", " , ")" ); |
160 | } |
161 | } |
162 | |
163 | void TOutputGLSLBase::writeLayoutQualifier(TIntermTyped *variable) |
164 | { |
165 | const TType &type = variable->getType(); |
166 | |
167 | if (!NeedsToWriteLayoutQualifier(type)) |
168 | { |
169 | return; |
170 | } |
171 | |
172 | if (type.getBasicType() == EbtInterfaceBlock) |
173 | { |
174 | const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock(); |
175 | declareInterfaceBlockLayout(interfaceBlock); |
176 | return; |
177 | } |
178 | |
179 | TInfoSinkBase &out = objSink(); |
180 | const TLayoutQualifier &layoutQualifier = type.getLayoutQualifier(); |
181 | out << "layout(" ; |
182 | |
183 | CommaSeparatedListItemPrefixGenerator listItemPrefix; |
184 | |
185 | if (type.getQualifier() == EvqFragmentOut || type.getQualifier() == EvqVertexIn || |
186 | IsVarying(type.getQualifier())) |
187 | { |
188 | if (layoutQualifier.location >= 0) |
189 | { |
190 | out << listItemPrefix << "location = " << layoutQualifier.location; |
191 | } |
192 | if (type.getQualifier() == EvqFragmentOut && layoutQualifier.index >= 0) |
193 | { |
194 | out << listItemPrefix << "index = " << layoutQualifier.index; |
195 | } |
196 | } |
197 | |
198 | if (type.getQualifier() == EvqFragmentOut) |
199 | { |
200 | if (layoutQualifier.yuv == true) |
201 | { |
202 | out << listItemPrefix << "yuv" ; |
203 | } |
204 | } |
205 | |
206 | if (IsOpaqueType(type.getBasicType())) |
207 | { |
208 | if (layoutQualifier.binding >= 0) |
209 | { |
210 | out << listItemPrefix << "binding = " << layoutQualifier.binding; |
211 | } |
212 | } |
213 | |
214 | if (IsImage(type.getBasicType())) |
215 | { |
216 | if (layoutQualifier.imageInternalFormat != EiifUnspecified) |
217 | { |
218 | ASSERT(type.getQualifier() == EvqTemporary || type.getQualifier() == EvqUniform); |
219 | out << listItemPrefix |
220 | << getImageInternalFormatString(layoutQualifier.imageInternalFormat); |
221 | } |
222 | } |
223 | |
224 | if (IsAtomicCounter(type.getBasicType())) |
225 | { |
226 | out << listItemPrefix << "offset = " << layoutQualifier.offset; |
227 | } |
228 | |
229 | out << ") " ; |
230 | } |
231 | |
232 | void TOutputGLSLBase::writeQualifier(TQualifier qualifier, const TSymbol *symbol) |
233 | { |
234 | const char *result = mapQualifierToString(qualifier); |
235 | if (result && result[0] != '\0') |
236 | { |
237 | objSink() << result << " " ; |
238 | } |
239 | } |
240 | |
241 | const char *TOutputGLSLBase::mapQualifierToString(TQualifier qualifier) |
242 | { |
243 | if (sh::IsGLSL410OrOlder(mOutput) && mShaderVersion >= 300 && |
244 | (mCompileOptions & SH_REMOVE_INVARIANT_AND_CENTROID_FOR_ESSL3) != 0) |
245 | { |
246 | switch (qualifier) |
247 | { |
248 | // The return string is consistent with sh::getQualifierString() from |
249 | // BaseTypes.h minus the "centroid" keyword. |
250 | case EvqCentroid: |
251 | return "" ; |
252 | case EvqCentroidIn: |
253 | return "smooth in" ; |
254 | case EvqCentroidOut: |
255 | return "smooth out" ; |
256 | default: |
257 | break; |
258 | } |
259 | } |
260 | if (sh::IsGLSL130OrNewer(mOutput)) |
261 | { |
262 | switch (qualifier) |
263 | { |
264 | case EvqAttribute: |
265 | return "in" ; |
266 | case EvqVaryingIn: |
267 | return "in" ; |
268 | case EvqVaryingOut: |
269 | return "out" ; |
270 | default: |
271 | break; |
272 | } |
273 | } |
274 | return sh::getQualifierString(qualifier); |
275 | } |
276 | |
277 | void TOutputGLSLBase::writeVariableType(const TType &type, const TSymbol *symbol) |
278 | { |
279 | TQualifier qualifier = type.getQualifier(); |
280 | TInfoSinkBase &out = objSink(); |
281 | if (type.isInvariant()) |
282 | { |
283 | writeInvariantQualifier(type); |
284 | } |
285 | if (qualifier != EvqTemporary && qualifier != EvqGlobal) |
286 | { |
287 | writeQualifier(qualifier, symbol); |
288 | } |
289 | |
290 | const TMemoryQualifier &memoryQualifier = type.getMemoryQualifier(); |
291 | if (memoryQualifier.readonly) |
292 | { |
293 | ASSERT(IsImage(type.getBasicType())); |
294 | out << "readonly " ; |
295 | } |
296 | |
297 | if (memoryQualifier.writeonly) |
298 | { |
299 | ASSERT(IsImage(type.getBasicType())); |
300 | out << "writeonly " ; |
301 | } |
302 | |
303 | if (memoryQualifier.coherent) |
304 | { |
305 | ASSERT(IsImage(type.getBasicType())); |
306 | out << "coherent " ; |
307 | } |
308 | |
309 | if (memoryQualifier.restrictQualifier) |
310 | { |
311 | ASSERT(IsImage(type.getBasicType())); |
312 | out << "restrict " ; |
313 | } |
314 | |
315 | if (memoryQualifier.volatileQualifier) |
316 | { |
317 | ASSERT(IsImage(type.getBasicType())); |
318 | out << "volatile " ; |
319 | } |
320 | |
321 | // Declare the struct if we have not done so already. |
322 | if (type.getBasicType() == EbtStruct && !structDeclared(type.getStruct())) |
323 | { |
324 | const TStructure *structure = type.getStruct(); |
325 | |
326 | declareStruct(structure); |
327 | } |
328 | else if (type.getBasicType() == EbtInterfaceBlock) |
329 | { |
330 | const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock(); |
331 | declareInterfaceBlock(interfaceBlock); |
332 | } |
333 | else |
334 | { |
335 | if (writeVariablePrecision(type.getPrecision())) |
336 | out << " " ; |
337 | out << getTypeName(type); |
338 | } |
339 | } |
340 | |
341 | void TOutputGLSLBase::writeFunctionParameters(const TFunction *func) |
342 | { |
343 | TInfoSinkBase &out = objSink(); |
344 | size_t paramCount = func->getParamCount(); |
345 | for (size_t i = 0; i < paramCount; ++i) |
346 | { |
347 | const TVariable *param = func->getParam(i); |
348 | const TType &type = param->getType(); |
349 | writeVariableType(type, param); |
350 | |
351 | if (param->symbolType() != SymbolType::Empty) |
352 | out << " " << hashName(param); |
353 | if (type.isArray()) |
354 | out << ArrayString(type); |
355 | |
356 | // Put a comma if this is not the last argument. |
357 | if (i != paramCount - 1) |
358 | out << ", " ; |
359 | } |
360 | } |
361 | |
362 | const TConstantUnion *TOutputGLSLBase::writeConstantUnion(const TType &type, |
363 | const TConstantUnion *pConstUnion) |
364 | { |
365 | TInfoSinkBase &out = objSink(); |
366 | |
367 | if (type.getBasicType() == EbtStruct) |
368 | { |
369 | const TStructure *structure = type.getStruct(); |
370 | out << hashName(structure) << "(" ; |
371 | |
372 | const TFieldList &fields = structure->fields(); |
373 | for (size_t i = 0; i < fields.size(); ++i) |
374 | { |
375 | const TType *fieldType = fields[i]->type(); |
376 | ASSERT(fieldType != nullptr); |
377 | pConstUnion = writeConstantUnion(*fieldType, pConstUnion); |
378 | if (i != fields.size() - 1) |
379 | out << ", " ; |
380 | } |
381 | out << ")" ; |
382 | } |
383 | else |
384 | { |
385 | size_t size = type.getObjectSize(); |
386 | bool writeType = size > 1; |
387 | if (writeType) |
388 | out << getTypeName(type) << "(" ; |
389 | for (size_t i = 0; i < size; ++i, ++pConstUnion) |
390 | { |
391 | switch (pConstUnion->getType()) |
392 | { |
393 | case EbtFloat: |
394 | writeFloat(out, pConstUnion->getFConst()); |
395 | break; |
396 | case EbtInt: |
397 | out << pConstUnion->getIConst(); |
398 | break; |
399 | case EbtUInt: |
400 | out << pConstUnion->getUConst() << "u" ; |
401 | break; |
402 | case EbtBool: |
403 | out << pConstUnion->getBConst(); |
404 | break; |
405 | case EbtYuvCscStandardEXT: |
406 | out << getYuvCscStandardEXTString(pConstUnion->getYuvCscStandardEXTConst()); |
407 | break; |
408 | default: |
409 | UNREACHABLE(); |
410 | } |
411 | if (i != size - 1) |
412 | out << ", " ; |
413 | } |
414 | if (writeType) |
415 | out << ")" ; |
416 | } |
417 | return pConstUnion; |
418 | } |
419 | |
420 | void TOutputGLSLBase::writeConstructorTriplet(Visit visit, const TType &type) |
421 | { |
422 | TInfoSinkBase &out = objSink(); |
423 | if (visit == PreVisit) |
424 | { |
425 | if (type.isArray()) |
426 | { |
427 | out << getTypeName(type); |
428 | out << ArrayString(type); |
429 | out << "(" ; |
430 | } |
431 | else |
432 | { |
433 | out << getTypeName(type) << "(" ; |
434 | } |
435 | } |
436 | else |
437 | { |
438 | writeTriplet(visit, nullptr, ", " , ")" ); |
439 | } |
440 | } |
441 | |
442 | void TOutputGLSLBase::visitSymbol(TIntermSymbol *node) |
443 | { |
444 | TInfoSinkBase &out = objSink(); |
445 | out << hashName(&node->variable()); |
446 | |
447 | if (mDeclaringVariable && node->getType().isArray()) |
448 | out << ArrayString(node->getType()); |
449 | } |
450 | |
451 | void TOutputGLSLBase::visitConstantUnion(TIntermConstantUnion *node) |
452 | { |
453 | writeConstantUnion(node->getType(), node->getConstantValue()); |
454 | } |
455 | |
456 | bool TOutputGLSLBase::visitSwizzle(Visit visit, TIntermSwizzle *node) |
457 | { |
458 | TInfoSinkBase &out = objSink(); |
459 | if (visit == PostVisit) |
460 | { |
461 | out << "." ; |
462 | node->writeOffsetsAsXYZW(&out); |
463 | } |
464 | return true; |
465 | } |
466 | |
467 | bool TOutputGLSLBase::visitBinary(Visit visit, TIntermBinary *node) |
468 | { |
469 | bool visitChildren = true; |
470 | TInfoSinkBase &out = objSink(); |
471 | switch (node->getOp()) |
472 | { |
473 | case EOpComma: |
474 | writeTriplet(visit, "(" , ", " , ")" ); |
475 | break; |
476 | case EOpInitialize: |
477 | if (visit == InVisit) |
478 | { |
479 | out << " = " ; |
480 | // RHS of initialize is not being declared. |
481 | mDeclaringVariable = false; |
482 | } |
483 | break; |
484 | case EOpAssign: |
485 | writeTriplet(visit, "(" , " = " , ")" ); |
486 | break; |
487 | case EOpAddAssign: |
488 | writeTriplet(visit, "(" , " += " , ")" ); |
489 | break; |
490 | case EOpSubAssign: |
491 | writeTriplet(visit, "(" , " -= " , ")" ); |
492 | break; |
493 | case EOpDivAssign: |
494 | writeTriplet(visit, "(" , " /= " , ")" ); |
495 | break; |
496 | case EOpIModAssign: |
497 | writeTriplet(visit, "(" , " %= " , ")" ); |
498 | break; |
499 | // Notice the fall-through. |
500 | case EOpMulAssign: |
501 | case EOpVectorTimesMatrixAssign: |
502 | case EOpVectorTimesScalarAssign: |
503 | case EOpMatrixTimesScalarAssign: |
504 | case EOpMatrixTimesMatrixAssign: |
505 | writeTriplet(visit, "(" , " *= " , ")" ); |
506 | break; |
507 | case EOpBitShiftLeftAssign: |
508 | writeTriplet(visit, "(" , " <<= " , ")" ); |
509 | break; |
510 | case EOpBitShiftRightAssign: |
511 | writeTriplet(visit, "(" , " >>= " , ")" ); |
512 | break; |
513 | case EOpBitwiseAndAssign: |
514 | writeTriplet(visit, "(" , " &= " , ")" ); |
515 | break; |
516 | case EOpBitwiseXorAssign: |
517 | writeTriplet(visit, "(" , " ^= " , ")" ); |
518 | break; |
519 | case EOpBitwiseOrAssign: |
520 | writeTriplet(visit, "(" , " |= " , ")" ); |
521 | break; |
522 | |
523 | case EOpIndexDirect: |
524 | writeTriplet(visit, nullptr, "[" , "]" ); |
525 | break; |
526 | case EOpIndexIndirect: |
527 | if (node->getAddIndexClamp()) |
528 | { |
529 | if (visit == InVisit) |
530 | { |
531 | if (mClampingStrategy == SH_CLAMP_WITH_CLAMP_INTRINSIC) |
532 | out << "[int(clamp(float(" ; |
533 | else |
534 | out << "[webgl_int_clamp(" ; |
535 | } |
536 | else if (visit == PostVisit) |
537 | { |
538 | TIntermTyped *left = node->getLeft(); |
539 | TType leftType = left->getType(); |
540 | |
541 | if (mClampingStrategy == SH_CLAMP_WITH_CLAMP_INTRINSIC) |
542 | out << "), 0.0, float(" ; |
543 | else |
544 | out << ", 0, " ; |
545 | |
546 | if (leftType.isUnsizedArray()) |
547 | { |
548 | // For runtime-sized arrays in ESSL 3.10 we need to call the length method |
549 | // to get the length to clamp against. See ESSL 3.10 section 4.1.9. Note |
550 | // that a runtime-sized array expression is guaranteed not to have side |
551 | // effects, so it's fine to add the expression to the output twice. |
552 | ASSERT(mShaderVersion >= 310); |
553 | ASSERT(!left->hasSideEffects()); |
554 | left->traverse(this); |
555 | out << ".length() - 1" ; |
556 | } |
557 | else |
558 | { |
559 | int maxSize; |
560 | if (leftType.isArray()) |
561 | { |
562 | maxSize = static_cast<int>(leftType.getOutermostArraySize()) - 1; |
563 | } |
564 | else |
565 | { |
566 | maxSize = leftType.getNominalSize() - 1; |
567 | } |
568 | out << maxSize; |
569 | } |
570 | if (mClampingStrategy == SH_CLAMP_WITH_CLAMP_INTRINSIC) |
571 | out << ")))]" ; |
572 | else |
573 | out << ")]" ; |
574 | } |
575 | } |
576 | else |
577 | { |
578 | writeTriplet(visit, nullptr, "[" , "]" ); |
579 | } |
580 | break; |
581 | case EOpIndexDirectStruct: |
582 | if (visit == InVisit) |
583 | { |
584 | // Here we are writing out "foo.bar", where "foo" is struct |
585 | // and "bar" is field. In AST, it is represented as a binary |
586 | // node, where left child represents "foo" and right child "bar". |
587 | // The node itself represents ".". The struct field "bar" is |
588 | // actually stored as an index into TStructure::fields. |
589 | out << "." ; |
590 | const TStructure *structure = node->getLeft()->getType().getStruct(); |
591 | const TIntermConstantUnion *index = node->getRight()->getAsConstantUnion(); |
592 | const TField *field = structure->fields()[index->getIConst(0)]; |
593 | |
594 | out << hashFieldName(field); |
595 | visitChildren = false; |
596 | } |
597 | break; |
598 | case EOpIndexDirectInterfaceBlock: |
599 | if (visit == InVisit) |
600 | { |
601 | out << "." ; |
602 | const TInterfaceBlock *interfaceBlock = |
603 | node->getLeft()->getType().getInterfaceBlock(); |
604 | const TIntermConstantUnion *index = node->getRight()->getAsConstantUnion(); |
605 | const TField *field = interfaceBlock->fields()[index->getIConst(0)]; |
606 | out << hashFieldName(field); |
607 | visitChildren = false; |
608 | } |
609 | break; |
610 | |
611 | case EOpAdd: |
612 | writeTriplet(visit, "(" , " + " , ")" ); |
613 | break; |
614 | case EOpSub: |
615 | writeTriplet(visit, "(" , " - " , ")" ); |
616 | break; |
617 | case EOpMul: |
618 | writeTriplet(visit, "(" , " * " , ")" ); |
619 | break; |
620 | case EOpDiv: |
621 | writeTriplet(visit, "(" , " / " , ")" ); |
622 | break; |
623 | case EOpIMod: |
624 | writeTriplet(visit, "(" , " % " , ")" ); |
625 | break; |
626 | case EOpBitShiftLeft: |
627 | writeTriplet(visit, "(" , " << " , ")" ); |
628 | break; |
629 | case EOpBitShiftRight: |
630 | writeTriplet(visit, "(" , " >> " , ")" ); |
631 | break; |
632 | case EOpBitwiseAnd: |
633 | writeTriplet(visit, "(" , " & " , ")" ); |
634 | break; |
635 | case EOpBitwiseXor: |
636 | writeTriplet(visit, "(" , " ^ " , ")" ); |
637 | break; |
638 | case EOpBitwiseOr: |
639 | writeTriplet(visit, "(" , " | " , ")" ); |
640 | break; |
641 | |
642 | case EOpEqual: |
643 | writeTriplet(visit, "(" , " == " , ")" ); |
644 | break; |
645 | case EOpNotEqual: |
646 | writeTriplet(visit, "(" , " != " , ")" ); |
647 | break; |
648 | case EOpLessThan: |
649 | writeTriplet(visit, "(" , " < " , ")" ); |
650 | break; |
651 | case EOpGreaterThan: |
652 | writeTriplet(visit, "(" , " > " , ")" ); |
653 | break; |
654 | case EOpLessThanEqual: |
655 | writeTriplet(visit, "(" , " <= " , ")" ); |
656 | break; |
657 | case EOpGreaterThanEqual: |
658 | writeTriplet(visit, "(" , " >= " , ")" ); |
659 | break; |
660 | |
661 | // Notice the fall-through. |
662 | case EOpVectorTimesScalar: |
663 | case EOpVectorTimesMatrix: |
664 | case EOpMatrixTimesVector: |
665 | case EOpMatrixTimesScalar: |
666 | case EOpMatrixTimesMatrix: |
667 | writeTriplet(visit, "(" , " * " , ")" ); |
668 | break; |
669 | |
670 | case EOpLogicalOr: |
671 | writeTriplet(visit, "(" , " || " , ")" ); |
672 | break; |
673 | case EOpLogicalXor: |
674 | writeTriplet(visit, "(" , " ^^ " , ")" ); |
675 | break; |
676 | case EOpLogicalAnd: |
677 | writeTriplet(visit, "(" , " && " , ")" ); |
678 | break; |
679 | default: |
680 | UNREACHABLE(); |
681 | } |
682 | |
683 | return visitChildren; |
684 | } |
685 | |
686 | bool TOutputGLSLBase::visitUnary(Visit visit, TIntermUnary *node) |
687 | { |
688 | const char *preString = "" ; |
689 | const char *postString = ")" ; |
690 | |
691 | switch (node->getOp()) |
692 | { |
693 | case EOpNegative: |
694 | preString = "(-" ; |
695 | break; |
696 | case EOpPositive: |
697 | preString = "(+" ; |
698 | break; |
699 | case EOpLogicalNot: |
700 | preString = "(!" ; |
701 | break; |
702 | case EOpBitwiseNot: |
703 | preString = "(~" ; |
704 | break; |
705 | |
706 | case EOpPostIncrement: |
707 | preString = "(" ; |
708 | postString = "++)" ; |
709 | break; |
710 | case EOpPostDecrement: |
711 | preString = "(" ; |
712 | postString = "--)" ; |
713 | break; |
714 | case EOpPreIncrement: |
715 | preString = "(++" ; |
716 | break; |
717 | case EOpPreDecrement: |
718 | preString = "(--" ; |
719 | break; |
720 | case EOpArrayLength: |
721 | preString = "((" ; |
722 | postString = ").length())" ; |
723 | break; |
724 | |
725 | case EOpRadians: |
726 | case EOpDegrees: |
727 | case EOpSin: |
728 | case EOpCos: |
729 | case EOpTan: |
730 | case EOpAsin: |
731 | case EOpAcos: |
732 | case EOpAtan: |
733 | case EOpSinh: |
734 | case EOpCosh: |
735 | case EOpTanh: |
736 | case EOpAsinh: |
737 | case EOpAcosh: |
738 | case EOpAtanh: |
739 | case EOpExp: |
740 | case EOpLog: |
741 | case EOpExp2: |
742 | case EOpLog2: |
743 | case EOpSqrt: |
744 | case EOpInversesqrt: |
745 | case EOpAbs: |
746 | case EOpSign: |
747 | case EOpFloor: |
748 | case EOpTrunc: |
749 | case EOpRound: |
750 | case EOpRoundEven: |
751 | case EOpCeil: |
752 | case EOpFract: |
753 | case EOpIsnan: |
754 | case EOpIsinf: |
755 | case EOpFloatBitsToInt: |
756 | case EOpFloatBitsToUint: |
757 | case EOpIntBitsToFloat: |
758 | case EOpUintBitsToFloat: |
759 | case EOpPackSnorm2x16: |
760 | case EOpPackUnorm2x16: |
761 | case EOpPackHalf2x16: |
762 | case EOpUnpackSnorm2x16: |
763 | case EOpUnpackUnorm2x16: |
764 | case EOpUnpackHalf2x16: |
765 | case EOpPackUnorm4x8: |
766 | case EOpPackSnorm4x8: |
767 | case EOpUnpackUnorm4x8: |
768 | case EOpUnpackSnorm4x8: |
769 | case EOpLength: |
770 | case EOpNormalize: |
771 | case EOpDFdx: |
772 | case EOpDFdy: |
773 | case EOpFwidth: |
774 | case EOpTranspose: |
775 | case EOpDeterminant: |
776 | case EOpInverse: |
777 | case EOpAny: |
778 | case EOpAll: |
779 | case EOpLogicalNotComponentWise: |
780 | case EOpBitfieldReverse: |
781 | case EOpBitCount: |
782 | case EOpFindLSB: |
783 | case EOpFindMSB: |
784 | writeBuiltInFunctionTriplet(visit, node->getOp(), node->getUseEmulatedFunction()); |
785 | return true; |
786 | default: |
787 | UNREACHABLE(); |
788 | } |
789 | |
790 | writeTriplet(visit, preString, nullptr, postString); |
791 | |
792 | return true; |
793 | } |
794 | |
795 | bool TOutputGLSLBase::visitTernary(Visit visit, TIntermTernary *node) |
796 | { |
797 | TInfoSinkBase &out = objSink(); |
798 | // Notice two brackets at the beginning and end. The outer ones |
799 | // encapsulate the whole ternary expression. This preserves the |
800 | // order of precedence when ternary expressions are used in a |
801 | // compound expression, i.e., c = 2 * (a < b ? 1 : 2). |
802 | out << "((" ; |
803 | node->getCondition()->traverse(this); |
804 | out << ") ? (" ; |
805 | node->getTrueExpression()->traverse(this); |
806 | out << ") : (" ; |
807 | node->getFalseExpression()->traverse(this); |
808 | out << "))" ; |
809 | return false; |
810 | } |
811 | |
812 | bool TOutputGLSLBase::visitIfElse(Visit visit, TIntermIfElse *node) |
813 | { |
814 | TInfoSinkBase &out = objSink(); |
815 | |
816 | out << "if (" ; |
817 | node->getCondition()->traverse(this); |
818 | out << ")\n" ; |
819 | |
820 | visitCodeBlock(node->getTrueBlock()); |
821 | |
822 | if (node->getFalseBlock()) |
823 | { |
824 | out << "else\n" ; |
825 | visitCodeBlock(node->getFalseBlock()); |
826 | } |
827 | return false; |
828 | } |
829 | |
830 | bool TOutputGLSLBase::visitSwitch(Visit visit, TIntermSwitch *node) |
831 | { |
832 | ASSERT(node->getStatementList()); |
833 | writeTriplet(visit, "switch (" , ") " , nullptr); |
834 | // The curly braces get written when visiting the statementList aggregate |
835 | return true; |
836 | } |
837 | |
838 | bool TOutputGLSLBase::visitCase(Visit visit, TIntermCase *node) |
839 | { |
840 | if (node->hasCondition()) |
841 | { |
842 | writeTriplet(visit, "case (" , nullptr, "):\n" ); |
843 | return true; |
844 | } |
845 | else |
846 | { |
847 | TInfoSinkBase &out = objSink(); |
848 | out << "default:\n" ; |
849 | return false; |
850 | } |
851 | } |
852 | |
853 | bool TOutputGLSLBase::visitBlock(Visit visit, TIntermBlock *node) |
854 | { |
855 | TInfoSinkBase &out = objSink(); |
856 | // Scope the blocks except when at the global scope. |
857 | if (getCurrentTraversalDepth() > 0) |
858 | { |
859 | out << "{\n" ; |
860 | } |
861 | |
862 | for (TIntermSequence::const_iterator iter = node->getSequence()->begin(); |
863 | iter != node->getSequence()->end(); ++iter) |
864 | { |
865 | TIntermNode *curNode = *iter; |
866 | ASSERT(curNode != nullptr); |
867 | curNode->traverse(this); |
868 | |
869 | if (isSingleStatement(curNode)) |
870 | out << ";\n" ; |
871 | } |
872 | |
873 | // Scope the blocks except when at the global scope. |
874 | if (getCurrentTraversalDepth() > 0) |
875 | { |
876 | out << "}\n" ; |
877 | } |
878 | return false; |
879 | } |
880 | |
881 | bool TOutputGLSLBase::visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) |
882 | { |
883 | TIntermFunctionPrototype *prototype = node->getFunctionPrototype(); |
884 | prototype->traverse(this); |
885 | visitCodeBlock(node->getBody()); |
886 | |
887 | // Fully processed; no need to visit children. |
888 | return false; |
889 | } |
890 | |
891 | bool TOutputGLSLBase::visitInvariantDeclaration(Visit visit, TIntermInvariantDeclaration *node) |
892 | { |
893 | TInfoSinkBase &out = objSink(); |
894 | ASSERT(visit == PreVisit); |
895 | const TIntermSymbol *symbol = node->getSymbol(); |
896 | out << "invariant " << hashName(&symbol->variable()); |
897 | return false; |
898 | } |
899 | |
900 | void TOutputGLSLBase::visitFunctionPrototype(TIntermFunctionPrototype *node) |
901 | { |
902 | TInfoSinkBase &out = objSink(); |
903 | |
904 | const TType &type = node->getType(); |
905 | writeVariableType(type, node->getFunction()); |
906 | if (type.isArray()) |
907 | out << ArrayString(type); |
908 | |
909 | out << " " << hashFunctionNameIfNeeded(node->getFunction()); |
910 | |
911 | out << "(" ; |
912 | writeFunctionParameters(node->getFunction()); |
913 | out << ")" ; |
914 | } |
915 | |
916 | bool TOutputGLSLBase::visitAggregate(Visit visit, TIntermAggregate *node) |
917 | { |
918 | bool visitChildren = true; |
919 | TInfoSinkBase &out = objSink(); |
920 | switch (node->getOp()) |
921 | { |
922 | case EOpCallFunctionInAST: |
923 | case EOpCallInternalRawFunction: |
924 | case EOpCallBuiltInFunction: |
925 | // Function call. |
926 | if (visit == PreVisit) |
927 | { |
928 | if (node->getOp() == EOpCallBuiltInFunction) |
929 | { |
930 | out << translateTextureFunction(node->getFunction()->name()); |
931 | } |
932 | else |
933 | { |
934 | out << hashFunctionNameIfNeeded(node->getFunction()); |
935 | } |
936 | out << "(" ; |
937 | } |
938 | else if (visit == InVisit) |
939 | out << ", " ; |
940 | else |
941 | out << ")" ; |
942 | break; |
943 | case EOpConstruct: |
944 | writeConstructorTriplet(visit, node->getType()); |
945 | break; |
946 | |
947 | case EOpEqualComponentWise: |
948 | case EOpNotEqualComponentWise: |
949 | case EOpLessThanComponentWise: |
950 | case EOpGreaterThanComponentWise: |
951 | case EOpLessThanEqualComponentWise: |
952 | case EOpGreaterThanEqualComponentWise: |
953 | case EOpMod: |
954 | case EOpModf: |
955 | case EOpPow: |
956 | case EOpAtan: |
957 | case EOpMin: |
958 | case EOpMax: |
959 | case EOpClamp: |
960 | case EOpMix: |
961 | case EOpStep: |
962 | case EOpSmoothstep: |
963 | case EOpFrexp: |
964 | case EOpLdexp: |
965 | case EOpDistance: |
966 | case EOpDot: |
967 | case EOpCross: |
968 | case EOpFaceforward: |
969 | case EOpReflect: |
970 | case EOpRefract: |
971 | case EOpMulMatrixComponentWise: |
972 | case EOpOuterProduct: |
973 | case EOpBitfieldExtract: |
974 | case EOpBitfieldInsert: |
975 | case EOpUaddCarry: |
976 | case EOpUsubBorrow: |
977 | case EOpUmulExtended: |
978 | case EOpImulExtended: |
979 | case EOpBarrier: |
980 | case EOpMemoryBarrier: |
981 | case EOpMemoryBarrierAtomicCounter: |
982 | case EOpMemoryBarrierBuffer: |
983 | case EOpMemoryBarrierImage: |
984 | case EOpMemoryBarrierShared: |
985 | case EOpGroupMemoryBarrier: |
986 | case EOpAtomicAdd: |
987 | case EOpAtomicMin: |
988 | case EOpAtomicMax: |
989 | case EOpAtomicAnd: |
990 | case EOpAtomicOr: |
991 | case EOpAtomicXor: |
992 | case EOpAtomicExchange: |
993 | case EOpAtomicCompSwap: |
994 | case EOpEmitVertex: |
995 | case EOpEndPrimitive: |
996 | writeBuiltInFunctionTriplet(visit, node->getOp(), node->getUseEmulatedFunction()); |
997 | break; |
998 | default: |
999 | UNREACHABLE(); |
1000 | } |
1001 | return visitChildren; |
1002 | } |
1003 | |
1004 | bool TOutputGLSLBase::visitDeclaration(Visit visit, TIntermDeclaration *node) |
1005 | { |
1006 | TInfoSinkBase &out = objSink(); |
1007 | |
1008 | // Variable declaration. |
1009 | if (visit == PreVisit) |
1010 | { |
1011 | const TIntermSequence &sequence = *(node->getSequence()); |
1012 | TIntermTyped *variable = sequence.front()->getAsTyped(); |
1013 | writeLayoutQualifier(variable); |
1014 | TIntermSymbol *symbolNode = variable->getAsSymbolNode(); |
1015 | writeVariableType(variable->getType(), symbolNode ? &symbolNode->variable() : nullptr); |
1016 | if (variable->getAsSymbolNode() == nullptr || |
1017 | variable->getAsSymbolNode()->variable().symbolType() != SymbolType::Empty) |
1018 | { |
1019 | out << " " ; |
1020 | } |
1021 | mDeclaringVariable = true; |
1022 | } |
1023 | else if (visit == InVisit) |
1024 | { |
1025 | UNREACHABLE(); |
1026 | } |
1027 | else |
1028 | { |
1029 | mDeclaringVariable = false; |
1030 | } |
1031 | return true; |
1032 | } |
1033 | |
1034 | bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop *node) |
1035 | { |
1036 | TInfoSinkBase &out = objSink(); |
1037 | |
1038 | TLoopType loopType = node->getType(); |
1039 | |
1040 | if (loopType == ELoopFor) // for loop |
1041 | { |
1042 | out << "for (" ; |
1043 | if (node->getInit()) |
1044 | node->getInit()->traverse(this); |
1045 | out << "; " ; |
1046 | |
1047 | if (node->getCondition()) |
1048 | node->getCondition()->traverse(this); |
1049 | out << "; " ; |
1050 | |
1051 | if (node->getExpression()) |
1052 | node->getExpression()->traverse(this); |
1053 | out << ")\n" ; |
1054 | |
1055 | visitCodeBlock(node->getBody()); |
1056 | } |
1057 | else if (loopType == ELoopWhile) // while loop |
1058 | { |
1059 | out << "while (" ; |
1060 | ASSERT(node->getCondition() != nullptr); |
1061 | node->getCondition()->traverse(this); |
1062 | out << ")\n" ; |
1063 | |
1064 | visitCodeBlock(node->getBody()); |
1065 | } |
1066 | else // do-while loop |
1067 | { |
1068 | ASSERT(loopType == ELoopDoWhile); |
1069 | out << "do\n" ; |
1070 | |
1071 | visitCodeBlock(node->getBody()); |
1072 | |
1073 | out << "while (" ; |
1074 | ASSERT(node->getCondition() != nullptr); |
1075 | node->getCondition()->traverse(this); |
1076 | out << ");\n" ; |
1077 | } |
1078 | |
1079 | // No need to visit children. They have been already processed in |
1080 | // this function. |
1081 | return false; |
1082 | } |
1083 | |
1084 | bool TOutputGLSLBase::visitBranch(Visit visit, TIntermBranch *node) |
1085 | { |
1086 | switch (node->getFlowOp()) |
1087 | { |
1088 | case EOpKill: |
1089 | writeTriplet(visit, "discard" , nullptr, nullptr); |
1090 | break; |
1091 | case EOpBreak: |
1092 | writeTriplet(visit, "break" , nullptr, nullptr); |
1093 | break; |
1094 | case EOpContinue: |
1095 | writeTriplet(visit, "continue" , nullptr, nullptr); |
1096 | break; |
1097 | case EOpReturn: |
1098 | writeTriplet(visit, "return " , nullptr, nullptr); |
1099 | break; |
1100 | default: |
1101 | UNREACHABLE(); |
1102 | } |
1103 | |
1104 | return true; |
1105 | } |
1106 | |
1107 | void TOutputGLSLBase::visitCodeBlock(TIntermBlock *node) |
1108 | { |
1109 | TInfoSinkBase &out = objSink(); |
1110 | if (node != nullptr) |
1111 | { |
1112 | node->traverse(this); |
1113 | // Single statements not part of a sequence need to be terminated |
1114 | // with semi-colon. |
1115 | if (isSingleStatement(node)) |
1116 | out << ";\n" ; |
1117 | } |
1118 | else |
1119 | { |
1120 | out << "{\n}\n" ; // Empty code block. |
1121 | } |
1122 | } |
1123 | |
1124 | void TOutputGLSLBase::visitPreprocessorDirective(TIntermPreprocessorDirective *node) |
1125 | { |
1126 | TInfoSinkBase &out = objSink(); |
1127 | |
1128 | out << "\n" ; |
1129 | |
1130 | switch (node->getDirective()) |
1131 | { |
1132 | case PreprocessorDirective::Define: |
1133 | out << "#define" ; |
1134 | break; |
1135 | case PreprocessorDirective::Endif: |
1136 | out << "#endif" ; |
1137 | break; |
1138 | case PreprocessorDirective::If: |
1139 | out << "#if" ; |
1140 | break; |
1141 | case PreprocessorDirective::Ifdef: |
1142 | out << "#ifdef" ; |
1143 | break; |
1144 | |
1145 | default: |
1146 | UNREACHABLE(); |
1147 | break; |
1148 | } |
1149 | |
1150 | if (!node->getCommand().empty()) |
1151 | { |
1152 | out << " " << node->getCommand(); |
1153 | } |
1154 | |
1155 | out << "\n" ; |
1156 | } |
1157 | |
1158 | ImmutableString TOutputGLSLBase::getTypeName(const TType &type) |
1159 | { |
1160 | return GetTypeName(type, mHashFunction, &mNameMap); |
1161 | } |
1162 | |
1163 | ImmutableString TOutputGLSLBase::hashName(const TSymbol *symbol) |
1164 | { |
1165 | return HashName(symbol, mHashFunction, &mNameMap); |
1166 | } |
1167 | |
1168 | ImmutableString TOutputGLSLBase::hashFieldName(const TField *field) |
1169 | { |
1170 | ASSERT(field->symbolType() != SymbolType::Empty); |
1171 | if (field->symbolType() == SymbolType::UserDefined) |
1172 | { |
1173 | return HashName(field->name(), mHashFunction, &mNameMap); |
1174 | } |
1175 | |
1176 | return field->name(); |
1177 | } |
1178 | |
1179 | ImmutableString TOutputGLSLBase::hashFunctionNameIfNeeded(const TFunction *func) |
1180 | { |
1181 | if (func->isMain()) |
1182 | { |
1183 | return func->name(); |
1184 | } |
1185 | else |
1186 | { |
1187 | return hashName(func); |
1188 | } |
1189 | } |
1190 | |
1191 | bool TOutputGLSLBase::structDeclared(const TStructure *structure) const |
1192 | { |
1193 | ASSERT(structure); |
1194 | if (structure->symbolType() == SymbolType::Empty) |
1195 | { |
1196 | return false; |
1197 | } |
1198 | |
1199 | return (mDeclaredStructs.count(structure->uniqueId().get()) > 0); |
1200 | } |
1201 | |
1202 | void TOutputGLSLBase::declareStruct(const TStructure *structure) |
1203 | { |
1204 | TInfoSinkBase &out = objSink(); |
1205 | |
1206 | out << "struct " ; |
1207 | |
1208 | if (structure->symbolType() != SymbolType::Empty) |
1209 | { |
1210 | out << hashName(structure) << " " ; |
1211 | } |
1212 | out << "{\n" ; |
1213 | const TFieldList &fields = structure->fields(); |
1214 | for (size_t i = 0; i < fields.size(); ++i) |
1215 | { |
1216 | const TField *field = fields[i]; |
1217 | if (writeVariablePrecision(field->type()->getPrecision())) |
1218 | out << " " ; |
1219 | out << getTypeName(*field->type()) << " " << hashFieldName(field); |
1220 | if (field->type()->isArray()) |
1221 | out << ArrayString(*field->type()); |
1222 | out << ";\n" ; |
1223 | } |
1224 | out << "}" ; |
1225 | |
1226 | if (structure->symbolType() != SymbolType::Empty) |
1227 | { |
1228 | mDeclaredStructs.insert(structure->uniqueId().get()); |
1229 | } |
1230 | } |
1231 | |
1232 | void TOutputGLSLBase::declareInterfaceBlockLayout(const TInterfaceBlock *interfaceBlock) |
1233 | { |
1234 | TInfoSinkBase &out = objSink(); |
1235 | |
1236 | out << "layout(" ; |
1237 | |
1238 | switch (interfaceBlock->blockStorage()) |
1239 | { |
1240 | case EbsUnspecified: |
1241 | case EbsShared: |
1242 | // Default block storage is shared. |
1243 | out << "shared" ; |
1244 | break; |
1245 | |
1246 | case EbsPacked: |
1247 | out << "packed" ; |
1248 | break; |
1249 | |
1250 | case EbsStd140: |
1251 | out << "std140" ; |
1252 | break; |
1253 | |
1254 | case EbsStd430: |
1255 | out << "std430" ; |
1256 | break; |
1257 | |
1258 | default: |
1259 | UNREACHABLE(); |
1260 | break; |
1261 | } |
1262 | |
1263 | if (interfaceBlock->blockBinding() >= 0) |
1264 | { |
1265 | out << ", " ; |
1266 | out << "binding = " << interfaceBlock->blockBinding(); |
1267 | } |
1268 | |
1269 | out << ") " ; |
1270 | } |
1271 | |
1272 | void TOutputGLSLBase::declareInterfaceBlock(const TInterfaceBlock *interfaceBlock) |
1273 | { |
1274 | TInfoSinkBase &out = objSink(); |
1275 | |
1276 | out << hashName(interfaceBlock) << "{\n" ; |
1277 | const TFieldList &fields = interfaceBlock->fields(); |
1278 | for (const TField *field : fields) |
1279 | { |
1280 | if (field->type()->isMatrix() || field->type()->isStructureContainingMatrices()) |
1281 | { |
1282 | out << "layout(" ; |
1283 | switch (field->type()->getLayoutQualifier().matrixPacking) |
1284 | { |
1285 | case EmpUnspecified: |
1286 | case EmpColumnMajor: |
1287 | // Default matrix packing is column major. |
1288 | out << "column_major" ; |
1289 | break; |
1290 | |
1291 | case EmpRowMajor: |
1292 | out << "row_major" ; |
1293 | break; |
1294 | |
1295 | default: |
1296 | UNREACHABLE(); |
1297 | break; |
1298 | } |
1299 | out << ") " ; |
1300 | } |
1301 | |
1302 | if (writeVariablePrecision(field->type()->getPrecision())) |
1303 | out << " " ; |
1304 | out << getTypeName(*field->type()) << " " << hashFieldName(field); |
1305 | if (field->type()->isArray()) |
1306 | out << ArrayString(*field->type()); |
1307 | out << ";\n" ; |
1308 | } |
1309 | out << "}" ; |
1310 | } |
1311 | |
1312 | void WriteGeometryShaderLayoutQualifiers(TInfoSinkBase &out, |
1313 | sh::TLayoutPrimitiveType inputPrimitive, |
1314 | int invocations, |
1315 | sh::TLayoutPrimitiveType outputPrimitive, |
1316 | int maxVertices) |
1317 | { |
1318 | // Omit 'invocations = 1' |
1319 | if (inputPrimitive != EptUndefined || invocations > 1) |
1320 | { |
1321 | out << "layout (" ; |
1322 | |
1323 | if (inputPrimitive != EptUndefined) |
1324 | { |
1325 | out << getGeometryShaderPrimitiveTypeString(inputPrimitive); |
1326 | } |
1327 | |
1328 | if (invocations > 1) |
1329 | { |
1330 | if (inputPrimitive != EptUndefined) |
1331 | { |
1332 | out << ", " ; |
1333 | } |
1334 | out << "invocations = " << invocations; |
1335 | } |
1336 | out << ") in;\n" ; |
1337 | } |
1338 | |
1339 | if (outputPrimitive != EptUndefined || maxVertices != -1) |
1340 | { |
1341 | out << "layout (" ; |
1342 | |
1343 | if (outputPrimitive != EptUndefined) |
1344 | { |
1345 | out << getGeometryShaderPrimitiveTypeString(outputPrimitive); |
1346 | } |
1347 | |
1348 | if (maxVertices != -1) |
1349 | { |
1350 | if (outputPrimitive != EptUndefined) |
1351 | { |
1352 | out << ", " ; |
1353 | } |
1354 | out << "max_vertices = " << maxVertices; |
1355 | } |
1356 | out << ") out;\n" ; |
1357 | } |
1358 | } |
1359 | |
1360 | // If SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS is enabled, layout qualifiers are spilled whenever |
1361 | // variables with specified layout qualifiers are copied. Additional checks are needed against the |
1362 | // type and storage qualifier of the variable to verify that layout qualifiers have to be outputted. |
1363 | // TODO (mradev): Fix layout qualifier spilling in ScalarizeVecAndMatConstructorArgs and remove |
1364 | // NeedsToWriteLayoutQualifier. |
1365 | bool NeedsToWriteLayoutQualifier(const TType &type) |
1366 | { |
1367 | if (type.getBasicType() == EbtInterfaceBlock) |
1368 | { |
1369 | return true; |
1370 | } |
1371 | |
1372 | const TLayoutQualifier &layoutQualifier = type.getLayoutQualifier(); |
1373 | |
1374 | if ((type.getQualifier() == EvqFragmentOut || type.getQualifier() == EvqVertexIn || |
1375 | IsVarying(type.getQualifier())) && |
1376 | layoutQualifier.location >= 0) |
1377 | { |
1378 | return true; |
1379 | } |
1380 | |
1381 | if (type.getQualifier() == EvqFragmentOut && layoutQualifier.yuv == true) |
1382 | { |
1383 | return true; |
1384 | } |
1385 | |
1386 | if (IsOpaqueType(type.getBasicType()) && layoutQualifier.binding != -1) |
1387 | { |
1388 | return true; |
1389 | } |
1390 | |
1391 | if (IsImage(type.getBasicType()) && layoutQualifier.imageInternalFormat != EiifUnspecified) |
1392 | { |
1393 | return true; |
1394 | } |
1395 | return false; |
1396 | } |
1397 | |
1398 | } // namespace sh |
1399 | |