1 //
2 // Copyright 2023 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 // EmulateFramebufferFetch.h: Replace inout, gl_LastFragData, gl_LastFragColorARM,
7 // gl_LastFragDepthARM and gl_LastFragStencilARM with usages of input attachments.
8 //
9
10 #include "compiler/translator/tree_ops/spirv/EmulateFramebufferFetch.h"
11
12 #include "common/bitset_utils.h"
13 #include "compiler/translator/Compiler.h"
14 #include "compiler/translator/ImmutableStringBuilder.h"
15 #include "compiler/translator/SymbolTable.h"
16 #include "compiler/translator/tree_util/BuiltIn.h"
17 #include "compiler/translator/tree_util/IntermNode_util.h"
18 #include "compiler/translator/tree_util/IntermTraverse.h"
19 #include "compiler/translator/tree_util/ReplaceVariable.h"
20 #include "compiler/translator/tree_util/RunAtTheBeginningOfShader.h"
21 #include "compiler/translator/util.h"
22
23 namespace sh
24 {
25 namespace
26 {
27 using InputAttachmentIndexUsage = angle::BitSet<32>;
28
29 struct AttachmentTypes
30 {
31 TVector<const TType *> color;
32 const TType *depth = nullptr;
33 const TType *stencil = nullptr;
34 };
35
36 // A traverser that looks at which inout variables exist, which gl_LastFragData indices have been
37 // used and whether gl_LastFragColorARM, gl_LastFragDepthARM or gl_LastFragStencilARM are
38 // referenced. It builds a set of indices correspondingly; these are input attachment indices the
39 // shader may read from.
40 class InputAttachmentUsageTraverser : public TIntermTraverser
41 {
42 public:
InputAttachmentUsageTraverser(uint32_t maxDrawBuffers)43 InputAttachmentUsageTraverser(uint32_t maxDrawBuffers)
44 : TIntermTraverser(true, false, false),
45 mMaxDrawBuffers(maxDrawBuffers),
46 mUsesLastFragColorARM(false),
47 mUsesLastFragDepthARM(false),
48 mUsesLastFragStencilARM(false)
49 {
50 mAttachmentTypes.color.resize(maxDrawBuffers, nullptr);
51 }
52
53 bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
54 bool visitBinary(Visit visit, TIntermBinary *node) override;
55 void visitSymbol(TIntermSymbol *node) override;
56
getIndexUsage() const57 InputAttachmentIndexUsage getIndexUsage() const { return mIndexUsage; }
usesLastFragColorARM() const58 bool usesLastFragColorARM() const { return mUsesLastFragColorARM; }
usesLastFragDepthARM() const59 bool usesLastFragDepthARM() const { return mUsesLastFragDepthARM; }
usesLastFragStencilARM() const60 bool usesLastFragStencilARM() const { return mUsesLastFragStencilARM; }
61
getAttachmentTypes() const62 const AttachmentTypes &getAttachmentTypes() const { return mAttachmentTypes; }
63
64 private:
65 void setInputAttachmentIndex(uint32_t index, const TType *type);
66
67 uint32_t mMaxDrawBuffers;
68 InputAttachmentIndexUsage mIndexUsage;
69 AttachmentTypes mAttachmentTypes;
70 bool mUsesLastFragColorARM;
71 bool mUsesLastFragDepthARM;
72 bool mUsesLastFragStencilARM;
73 };
74
setInputAttachmentIndex(uint32_t index,const TType * type)75 void InputAttachmentUsageTraverser::setInputAttachmentIndex(uint32_t index, const TType *type)
76 {
77 ASSERT(index < mMaxDrawBuffers);
78 mIndexUsage.set(index);
79 mAttachmentTypes.color[index] = type;
80 }
81
visitDeclaration(Visit visit,TIntermDeclaration * node)82 bool InputAttachmentUsageTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node)
83 {
84 const TIntermSequence &sequence = *node->getSequence();
85 ASSERT(sequence.size() == 1);
86
87 TIntermSymbol *symbol = sequence.front()->getAsSymbolNode();
88 if (symbol == nullptr)
89 {
90 return true;
91 }
92
93 if (symbol->getQualifier() == EvqFragmentInOut)
94 {
95 ASSERT(symbol->getType().getLayoutQualifier().index <= 0);
96
97 // The input attachment index is identical to the location qualifier. If there's only one
98 // output, GLSL is allowed to not specify the location qualifier, in which case it would
99 // implicitly be at location 0.
100 const TType &type = symbol->getType();
101 const unsigned int baseInputAttachmentIndex =
102 std::max(0, type.getLayoutQualifier().location);
103
104 uint32_t arraySize = type.isArray() ? type.getOutermostArraySize() : 1;
105 for (unsigned int index = 0; index < arraySize; index++)
106 {
107 setInputAttachmentIndex(baseInputAttachmentIndex + index, &type);
108 }
109 }
110
111 return false;
112 }
113
visitBinary(Visit visit,TIntermBinary * node)114 bool InputAttachmentUsageTraverser::visitBinary(Visit visit, TIntermBinary *node)
115 {
116 TOperator op = node->getOp();
117 if (op != EOpIndexDirect && op != EOpIndexIndirect)
118 {
119 return true;
120 }
121
122 TIntermSymbol *left = node->getLeft()->getAsSymbolNode();
123 if (left == nullptr || left->getQualifier() != EvqLastFragData)
124 {
125 return true;
126 }
127
128 ASSERT(left->getName() == "gl_LastFragData");
129 const TType &type = left->getType();
130
131 const TConstantUnion *constIndex = node->getRight()->getConstantValue();
132 // Non-const indices on gl_LastFragData are not allowed.
133 ASSERT(constIndex != nullptr);
134
135 uint32_t index = 0;
136 switch (constIndex->getType())
137 {
138 case EbtInt:
139 index = constIndex->getIConst();
140 break;
141 case EbtUInt:
142 index = constIndex->getUConst();
143 break;
144 case EbtFloat:
145 index = static_cast<uint32_t>(constIndex->getFConst());
146 break;
147 case EbtBool:
148 index = constIndex->getBConst() ? 1 : 0;
149 break;
150 default:
151 UNREACHABLE();
152 break;
153 }
154 setInputAttachmentIndex(index, &type);
155
156 return true;
157 }
158
visitSymbol(TIntermSymbol * symbol)159 void InputAttachmentUsageTraverser::visitSymbol(TIntermSymbol *symbol)
160 {
161 switch (symbol->getQualifier())
162 {
163 case EvqLastFragColor:
164 ASSERT(symbol->getName() == "gl_LastFragColorARM");
165
166 // gl_LastFragColorARM always reads back from location 0.
167 setInputAttachmentIndex(0, &symbol->getType());
168 mUsesLastFragColorARM = true;
169 break;
170
171 case EvqLastFragDepth:
172 ASSERT(symbol->getName() == "gl_LastFragDepthARM");
173
174 // gl_LastFragDepthARM doesn't need an explicit input attachment index (with
175 // VK_KHR_dynamic_rendering_local_read)
176 mUsesLastFragDepthARM = true;
177 mAttachmentTypes.depth = &symbol->getType();
178 break;
179
180 case EvqLastFragStencil:
181 ASSERT(symbol->getName() == "gl_LastFragStencilARM");
182
183 // gl_LastFragStencilARM doesn't need an explicit input attachment index (with
184 // VK_KHR_dynamic_rendering_local_read)
185 mUsesLastFragStencilARM = true;
186 mAttachmentTypes.stencil = &symbol->getType();
187 break;
188
189 default:
190 break;
191 }
192 }
193
GetInputAttachmentName(size_t index)194 ImmutableString GetInputAttachmentName(size_t index)
195 {
196 std::stringstream nameStream = sh::InitializeStream<std::stringstream>();
197 nameStream << "ANGLEInputAttachment" << index;
198 return ImmutableString(nameStream.str());
199 }
200
GetBasicTypeForSubpassInput(TBasicType inputType)201 TBasicType GetBasicTypeForSubpassInput(TBasicType inputType)
202 {
203 switch (inputType)
204 {
205 case EbtFloat:
206 return EbtSubpassInput;
207 case EbtInt:
208 return EbtISubpassInput;
209 case EbtUInt:
210 return EbtUSubpassInput;
211 default:
212 UNREACHABLE();
213 return EbtVoid;
214 }
215 }
216
DeclareInputAttachmentVariable(TSymbolTable * symbolTable,const TType * type,const ImmutableString & name,TIntermSequence * declarationsOut)217 const TVariable *DeclareInputAttachmentVariable(TSymbolTable *symbolTable,
218 const TType *type,
219 const ImmutableString &name,
220 TIntermSequence *declarationsOut)
221 {
222 const TVariable *inputAttachmentVar =
223 new TVariable(symbolTable, name, type, SymbolType::AngleInternal);
224
225 TIntermDeclaration *decl = new TIntermDeclaration;
226 decl->appendDeclarator(new TIntermSymbol(inputAttachmentVar));
227 declarationsOut->push_back(decl);
228
229 return inputAttachmentVar;
230 }
231
232 // Declare a color input attachment variable at a given index.
DeclareColorInputAttachmentVariable(TSymbolTable * symbolTable,const TType & outputType,size_t index,InputAttachmentMap * inputAttachmentMapOut,TIntermSequence * declarationsOut)233 void DeclareColorInputAttachmentVariable(TSymbolTable *symbolTable,
234 const TType &outputType,
235 size_t index,
236 InputAttachmentMap *inputAttachmentMapOut,
237 TIntermSequence *declarationsOut)
238 {
239 const TBasicType subpassInputType = GetBasicTypeForSubpassInput(outputType.getBasicType());
240
241 TType *inputAttachmentType =
242 new TType(subpassInputType, outputType.getPrecision(), EvqUniform, 1);
243 TLayoutQualifier inputAttachmentQualifier = inputAttachmentType->getLayoutQualifier();
244 inputAttachmentQualifier.inputAttachmentIndex = static_cast<int>(index);
245 inputAttachmentType->setLayoutQualifier(inputAttachmentQualifier);
246
247 const TVariable *inputAttachmentVar = DeclareInputAttachmentVariable(
248 symbolTable, inputAttachmentType, GetInputAttachmentName(index), declarationsOut);
249 inputAttachmentMapOut->color[static_cast<uint32_t>(index)] = inputAttachmentVar;
250 }
251
252 // Helper to declare a depth or stencil input attachment variable.
DeclareDepthStencilInputAttachmentVariable(TSymbolTable * symbolTable,const TType * type,const char * variableName,TIntermSequence * declarationsOut)253 const TVariable *DeclareDepthStencilInputAttachmentVariable(TSymbolTable *symbolTable,
254 const TType *type,
255 const char *variableName,
256 TIntermSequence *declarationsOut)
257 {
258 return DeclareInputAttachmentVariable(symbolTable, type, ImmutableString(variableName),
259 declarationsOut);
260 }
261
DeclareDepthInputAttachmentVariable(TSymbolTable * symbolTable,const TType * type,InputAttachmentMap * inputAttachmentMapOut,TIntermSequence * declarationsOut)262 void DeclareDepthInputAttachmentVariable(TSymbolTable *symbolTable,
263 const TType *type,
264 InputAttachmentMap *inputAttachmentMapOut,
265 TIntermSequence *declarationsOut)
266 {
267 const TType *inputAttachmentType =
268 new TType(EbtSubpassInput, type->getPrecision(), EvqUniform, 1);
269
270 inputAttachmentMapOut->depth = DeclareDepthStencilInputAttachmentVariable(
271 symbolTable, inputAttachmentType, "ANGLEDepthInputAttachment", declarationsOut);
272 }
273
DeclareStencilInputAttachmentVariable(TSymbolTable * symbolTable,const TType * type,InputAttachmentMap * inputAttachmentMapOut,TIntermSequence * declarationsOut)274 void DeclareStencilInputAttachmentVariable(TSymbolTable *symbolTable,
275 const TType *type,
276 InputAttachmentMap *inputAttachmentMapOut,
277 TIntermSequence *declarationsOut)
278 {
279 const TType *inputAttachmentType =
280 new TType(EbtISubpassInput, type->getPrecision(), EvqUniform, 1);
281
282 inputAttachmentMapOut->stencil = DeclareDepthStencilInputAttachmentVariable(
283 symbolTable, inputAttachmentType, "ANGLEStencilInputAttachment", declarationsOut);
284 }
285
286 // Declare a global variable to hold gl_LastFragData/gl_LastFragColorARM
DeclareLastFragDataGlobalVariable(TCompiler * compiler,TIntermBlock * root,const AttachmentTypes & attachmentTypes,TIntermSequence * declarationsOut)287 const TVariable *DeclareLastFragDataGlobalVariable(TCompiler *compiler,
288 TIntermBlock *root,
289 const AttachmentTypes &attachmentTypes,
290 TIntermSequence *declarationsOut)
291 {
292 // Find the first input attachment that is used. If gl_LastFragColorARM was used, this will be
293 // index 0. Otherwise if this is ES100, any index of gl_LastFragData may be used. Either way,
294 // the global variable is declared the same as gl_LastFragData would have been if used.
295 const TType *attachmentType = nullptr;
296 for (const TType *type : attachmentTypes.color)
297 {
298 if (type != nullptr)
299 {
300 attachmentType = type;
301 break;
302 }
303 }
304 ASSERT(attachmentType != nullptr);
305 TType *globalType = new TType(*attachmentType);
306 globalType->setQualifier(EvqGlobal);
307
308 // If the type of gl_LastFragColorARM is found, convert it to an array to match gl_LastFragData.
309 // This is necessary if the shader uses both gl_LastFragData and gl_LastFragColorARM
310 // simultaneously.
311 if (!globalType->isArray())
312 {
313 globalType->makeArray(compiler->getBuiltInResources().MaxDrawBuffers);
314 }
315
316 // Declare the global
317 const TVariable *global =
318 new TVariable(&compiler->getSymbolTable(), ImmutableString("ANGLELastFragData"), globalType,
319 SymbolType::AngleInternal);
320
321 TIntermDeclaration *decl = new TIntermDeclaration;
322 decl->appendDeclarator(new TIntermSymbol(global));
323 declarationsOut->push_back(decl);
324
325 return global;
326 }
327
328 struct InputUsage
329 {
330 InputAttachmentIndexUsage indices;
331 bool usesLastFragData;
332 bool usesLastFragDepth;
333 bool usesLastFragStencil;
334 };
335
336 // Declare an input attachment for each used index. Additionally, create a global variable for
337 // gl_LastFragData, gl_LastFragColorARM, gl_LastFragDepthARM and gl_LastFragStencilARM if needed.
DeclareVariables(TCompiler * compiler,TIntermBlock * root,const InputUsage & inputUsage,const AttachmentTypes & attachmentTypes,InputAttachmentMap * inputAttachmentMapOut,const TVariable ** lastFragDataOut)338 [[nodiscard]] bool DeclareVariables(TCompiler *compiler,
339 TIntermBlock *root,
340 const InputUsage &inputUsage,
341 const AttachmentTypes &attachmentTypes,
342 InputAttachmentMap *inputAttachmentMapOut,
343 const TVariable **lastFragDataOut)
344 {
345 TSymbolTable *symbolTable = &compiler->getSymbolTable();
346
347 TIntermSequence declarations;
348
349 // For every detected index, declare an input attachment variable.
350 for (size_t index : inputUsage.indices)
351 {
352 ASSERT(attachmentTypes.color[index] != nullptr);
353 DeclareColorInputAttachmentVariable(symbolTable, *attachmentTypes.color[index], index,
354 inputAttachmentMapOut, &declarations);
355 }
356 // Depth and stencil attachments don't need input attachment indices with
357 // VK_KHR_dynamic_rendering_local_read, so they are not covered by the above loop.
358 if (inputUsage.usesLastFragDepth)
359 {
360 DeclareDepthInputAttachmentVariable(symbolTable, attachmentTypes.depth,
361 inputAttachmentMapOut, &declarations);
362 }
363 if (inputUsage.usesLastFragStencil)
364 {
365 DeclareStencilInputAttachmentVariable(symbolTable, attachmentTypes.stencil,
366 inputAttachmentMapOut, &declarations);
367 }
368
369 // If gl_LastFragData or gl_LastFragColorARM is used, declare a global variable to retain that.
370 // The difference between ES300+ inout variables and gl_LastFrag* is that if the inout variable
371 // is read back after being written to, it should contain the latest value written to it, while
372 // gl_LastFrag* should contain the value before the fragment shader's invocation.
373 //
374 // As such, it is enough to initialize inout variables with the values from input attachments,
375 // but gl_LastFrag* needs to be stored in a global variable to retain its value even after
376 // gl_Frag* has been overwritten.
377 *lastFragDataOut = nullptr;
378 if (inputUsage.usesLastFragData)
379 {
380 *lastFragDataOut =
381 DeclareLastFragDataGlobalVariable(compiler, root, attachmentTypes, &declarations);
382 }
383
384 // Add the declarations to the beginning of the shader.
385 TIntermSequence &topLevel = *root->getSequence();
386 declarations.insert(declarations.end(), topLevel.begin(), topLevel.end());
387 topLevel = std::move(declarations);
388
389 return compiler->validateAST(root);
390 }
391
CreateSubpassLoadFuncCall(TSymbolTable * symbolTable,const TVariable * inputVariable)392 TIntermTyped *CreateSubpassLoadFuncCall(TSymbolTable *symbolTable, const TVariable *inputVariable)
393 {
394 TIntermSequence args = {new TIntermSymbol(inputVariable)};
395 return CreateBuiltInFunctionCallNode("subpassLoad", &args, *symbolTable,
396 kESSLInternalBackendBuiltIns);
397 }
398
GatherInoutVariables(TIntermBlock * root,TVector<const TVariable * > * inoutVariablesOut)399 void GatherInoutVariables(TIntermBlock *root, TVector<const TVariable *> *inoutVariablesOut)
400 {
401 TIntermSequence &topLevel = *root->getSequence();
402
403 for (TIntermNode *node : topLevel)
404 {
405 TIntermDeclaration *decl = node->getAsDeclarationNode();
406 if (decl != nullptr)
407 {
408 ASSERT(decl->getSequence()->size() == 1);
409
410 TIntermSymbol *symbol = decl->getSequence()->front()->getAsSymbolNode();
411 if (symbol != nullptr && symbol->getQualifier() == EvqFragmentInOut)
412 {
413 ASSERT(symbol->getType().getLayoutQualifier().index <= 0);
414 inoutVariablesOut->push_back(&symbol->variable());
415 }
416 }
417 }
418 }
419
InitializeFromInputAttachment(TSymbolTable * symbolTable,TIntermBlock * block,const TVariable * inputVariable,const TVariable * assignVariable,uint32_t assignVariableArrayIndex)420 void InitializeFromInputAttachment(TSymbolTable *symbolTable,
421 TIntermBlock *block,
422 const TVariable *inputVariable,
423 const TVariable *assignVariable,
424 uint32_t assignVariableArrayIndex)
425 {
426 ASSERT(inputVariable != nullptr);
427
428 TIntermTyped *var = new TIntermSymbol(assignVariable);
429 if (var->getType().isArray())
430 {
431 var = new TIntermBinary(EOpIndexDirect, var, CreateIndexNode(assignVariableArrayIndex));
432 }
433
434 TIntermTyped *input = CreateSubpassLoadFuncCall(symbolTable, inputVariable);
435
436 const int vecSize = assignVariable->getType().getNominalSize();
437 if (vecSize < 4)
438 {
439 TVector<int> swizzle = {0, 1, 2, 3};
440 swizzle.resize(vecSize);
441 input = new TIntermSwizzle(input, swizzle);
442 }
443
444 TIntermTyped *assignment = new TIntermBinary(EOpAssign, var, input);
445
446 block->appendStatement(assignment);
447 }
448
InitializeFromInputAttachments(TCompiler * compiler,TIntermBlock * root,const InputAttachmentMap & inputAttachmentMap,const TVector<const TVariable * > & inoutVariables,const TVariable * lastFragData)449 [[nodiscard]] bool InitializeFromInputAttachments(TCompiler *compiler,
450 TIntermBlock *root,
451 const InputAttachmentMap &inputAttachmentMap,
452 const TVector<const TVariable *> &inoutVariables,
453 const TVariable *lastFragData)
454 {
455 TSymbolTable *symbolTable = &compiler->getSymbolTable();
456 TIntermBlock *init = new TIntermBlock;
457
458 // Initialize inout variables
459 for (const TVariable *inoutVar : inoutVariables)
460 {
461 const TType &type = inoutVar->getType();
462 const unsigned int baseInputAttachmentIndex =
463 std::max(0, type.getLayoutQualifier().location);
464
465 uint32_t arraySize = type.isArray() ? type.getOutermostArraySize() : 1;
466 for (unsigned int index = 0; index < arraySize; index++)
467 {
468 ASSERT(inputAttachmentMap.color.find(baseInputAttachmentIndex + index) !=
469 inputAttachmentMap.color.end());
470
471 InitializeFromInputAttachment(
472 symbolTable, init, inputAttachmentMap.color.at(baseInputAttachmentIndex + index),
473 inoutVar, index);
474 }
475 }
476
477 // Initialize lastFragData, if present
478 if (lastFragData != nullptr)
479 {
480 for (auto &iter : inputAttachmentMap.color)
481 {
482 const uint32_t index = iter.first;
483 const TVariable *inputAttachmentVar = iter.second;
484
485 InitializeFromInputAttachment(symbolTable, init, inputAttachmentVar, lastFragData,
486 index);
487 }
488 }
489
490 return RunAtTheBeginningOfShader(compiler, root, init);
491 }
492
ReplaceVariables(TCompiler * compiler,TIntermBlock * root,const InputAttachmentMap & inputAttachmentMap,const TVariable * lastFragData)493 [[nodiscard]] bool ReplaceVariables(TCompiler *compiler,
494 TIntermBlock *root,
495 const InputAttachmentMap &inputAttachmentMap,
496 const TVariable *lastFragData)
497 {
498 TSymbolTable *symbolTable = &compiler->getSymbolTable();
499
500 TVector<const TVariable *> inoutVariables;
501 GatherInoutVariables(root, &inoutVariables);
502
503 // Generate code that initializes the global variable and the inout variables with corresponding
504 // input attachments. In particular, this is needed because if the shader writes to the inout
505 // color variables, reading the variable should produce said written values (using |subpassLoad|
506 // on every read would not have made that possible).
507 //
508 // Note that this is not done for depth/stencil. The extensions recommendation is to read from
509 // these values as late as possible, so preloading them can hurt performance. Instead, a
510 // |subpassLoad| is issued directly where the values are read from. Note that unlike color
511 // attachments, the application cannot write to the depth/stencil variables and expect to read
512 // back the shader-written values.
513 if (!InitializeFromInputAttachments(compiler, root, inputAttachmentMap, inoutVariables,
514 lastFragData))
515 {
516 return false;
517 }
518
519 // Build a map from:
520 //
521 // - inout to out variables
522 // - gl_LastFragData to lastFragData
523 // - gl_LastFragColorARM to lastFragData[0]
524 // - gl_LastFragDepthARM to subpassLoad(ANGLEDepthInputAttachment)
525 // - gl_LastFragStencilARM to subpassLoad(ANGLEStencilInputAttachment)
526
527 VariableReplacementMap replacementMap;
528 for (const TVariable *var : inoutVariables)
529 {
530 TType *outType = new TType(var->getType());
531 outType->setQualifier(EvqFragmentOut);
532 const TVariable *replacement =
533 new TVariable(symbolTable, var->name(), outType, var->symbolType());
534 replacementMap[var] = new TIntermSymbol(replacement);
535 }
536
537 if (lastFragData != nullptr || inputAttachmentMap.depth != nullptr ||
538 inputAttachmentMap.stencil != nullptr)
539 {
540 // Use the user-defined variables if found (and remove their declaration), or the built-in
541 // otherwise.
542 TIntermSequence &topLevel = *root->getSequence();
543 TIntermSequence newTopLevel;
544
545 const TVariable *glLastFragData = nullptr;
546 const TVariable *glLastFragColor = nullptr;
547 const TVariable *glLastFragDepth = nullptr;
548 const TVariable *glLastFragStencil = nullptr;
549
550 for (TIntermNode *node : topLevel)
551 {
552 TIntermDeclaration *decl = node->getAsDeclarationNode();
553 if (decl != nullptr)
554 {
555 ASSERT(decl->getSequence()->size() == 1);
556
557 TIntermSymbol *symbol = decl->getSequence()->front()->getAsSymbolNode();
558 if (symbol != nullptr)
559 {
560 switch (symbol->getQualifier())
561 {
562 case EvqLastFragData:
563 glLastFragData = &symbol->variable();
564 continue;
565 case EvqLastFragColor:
566 glLastFragColor = &symbol->variable();
567 continue;
568 case EvqLastFragDepth:
569 glLastFragDepth = &symbol->variable();
570 continue;
571 case EvqLastFragStencil:
572 glLastFragStencil = &symbol->variable();
573 continue;
574 default:
575 break;
576 }
577 }
578 }
579 newTopLevel.push_back(node);
580 }
581
582 topLevel = std::move(newTopLevel);
583
584 if (glLastFragData == nullptr)
585 {
586 glLastFragData = static_cast<const TVariable *>(
587 symbolTable->findBuiltIn(ImmutableString("gl_LastFragData"), 100));
588 }
589 if (glLastFragColor == nullptr)
590 {
591 glLastFragColor = static_cast<const TVariable *>(symbolTable->findBuiltIn(
592 ImmutableString("gl_LastFragColorARM"), compiler->getShaderVersion()));
593 }
594 if (glLastFragDepth == nullptr)
595 {
596 glLastFragDepth = static_cast<const TVariable *>(symbolTable->findBuiltIn(
597 ImmutableString("gl_LastFragDepthARM"), compiler->getShaderVersion()));
598 }
599 if (glLastFragStencil == nullptr)
600 {
601 glLastFragStencil = static_cast<const TVariable *>(symbolTable->findBuiltIn(
602 ImmutableString("gl_LastFragStencilARM"), compiler->getShaderVersion()));
603 }
604
605 if (lastFragData != nullptr)
606 {
607 replacementMap[glLastFragData] = new TIntermSymbol(lastFragData);
608 replacementMap[glLastFragColor] = new TIntermBinary(
609 EOpIndexDirect, new TIntermSymbol(lastFragData), CreateIndexNode(0));
610 }
611 if (inputAttachmentMap.depth != nullptr)
612 {
613 replacementMap[glLastFragDepth] = new TIntermSwizzle(
614 CreateSubpassLoadFuncCall(symbolTable, inputAttachmentMap.depth), {0});
615 }
616 if (inputAttachmentMap.stencil != nullptr)
617 {
618 replacementMap[glLastFragStencil] = new TIntermSwizzle(
619 CreateSubpassLoadFuncCall(symbolTable, inputAttachmentMap.stencil), {0});
620 }
621 }
622
623 // Replace the variables accordingly.
624 return ReplaceVariables(compiler, root, replacementMap);
625 }
626 } // anonymous namespace
627
EmulateFramebufferFetch(TCompiler * compiler,TIntermBlock * root,InputAttachmentMap * inputAttachmentMapOut)628 [[nodiscard]] bool EmulateFramebufferFetch(TCompiler *compiler,
629 TIntermBlock *root,
630 InputAttachmentMap *inputAttachmentMapOut)
631 {
632 // First, check if input attachments are necessary at all.
633 InputAttachmentUsageTraverser usageTraverser(compiler->getBuiltInResources().MaxDrawBuffers);
634 root->traverse(&usageTraverser);
635
636 InputUsage inputUsage = {};
637 inputUsage.indices = usageTraverser.getIndexUsage();
638 inputUsage.usesLastFragData =
639 (compiler->getShaderVersion() == 100 && inputUsage.indices.any()) ||
640 usageTraverser.usesLastFragColorARM();
641 inputUsage.usesLastFragDepth = usageTraverser.usesLastFragDepthARM();
642 inputUsage.usesLastFragStencil = usageTraverser.usesLastFragStencilARM();
643
644 if (!inputUsage.indices.any() && !inputUsage.usesLastFragDepth &&
645 !inputUsage.usesLastFragStencil)
646 {
647 return true;
648 }
649
650 // Declare the necessary variables for emulation; input attachments to read from and global
651 // variables to hold last frag data.
652 const TVariable *lastFragData = nullptr;
653 if (!DeclareVariables(compiler, root, inputUsage, usageTraverser.getAttachmentTypes(),
654 inputAttachmentMapOut, &lastFragData))
655 {
656 return false;
657 }
658
659 // Then replace references to gl_LastFragData with the global, gl_LastFragColorARM with
660 // global[0], gl_LastFragDepth/StencilARM with the appropriate subpassLoad opreration, replace
661 // inout variables with out equivalents and make sure color input attachments initialize the
662 // appropriate variables at the beginning of the shader.
663 if (!ReplaceVariables(compiler, root, *inputAttachmentMapOut, lastFragData))
664 {
665 return false;
666 }
667
668 return true;
669 }
670
671 } // namespace sh
672