xref: /aosp_15_r20/external/angle/src/compiler/translator/tree_ops/spirv/EmulateFramebufferFetch.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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