xref: /aosp_15_r20/external/angle/src/compiler/translator/tree_ops/spirv/EmulateFragColorData.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1 //
2 // Copyright 2021 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 // EmulateFragColorData: Emulate gl_FragColor and gl_FragData.
7 //
8 
9 #include "compiler/translator/tree_ops/spirv/EmulateFragColorData.h"
10 
11 #include "compiler/translator/Compiler.h"
12 #include "compiler/translator/ImmutableStringBuilder.h"
13 #include "compiler/translator/SymbolTable.h"
14 #include "compiler/translator/tree_util/IntermNode_util.h"
15 #include "compiler/translator/tree_util/IntermTraverse.h"
16 #include "compiler/translator/tree_util/ReplaceVariable.h"
17 
18 namespace sh
19 {
20 namespace
21 {
22 // Traverser that:
23 //
24 // 1. Declares outputs corresponding to gl_FragColor and gl_FragData (and their corresponding
25 //    Secondary versions for framebuffer fetch).
26 // 2. Replaces built-in references with these variables.
27 class EmulateFragColorDataTraverser : public TIntermTraverser
28 {
29   public:
EmulateFragColorDataTraverser(TCompiler * compiler,TSymbolTable * symbolTable,bool hasGLSecondaryFragData)30     EmulateFragColorDataTraverser(TCompiler *compiler,
31                                   TSymbolTable *symbolTable,
32                                   bool hasGLSecondaryFragData)
33         : TIntermTraverser(true, false, false, symbolTable),
34           mResources(compiler->getResources()),
35           mHasGLSecondaryFragData(hasGLSecondaryFragData)
36     {}
37 
visitSymbol(TIntermSymbol * symbol)38     void visitSymbol(TIntermSymbol *symbol) override
39     {
40         const TVariable *variable = &symbol->variable();
41         const TType &type         = variable->getType();
42 
43         // If this built-in was already visited, reuse the variable defined for it.
44         auto replacement = mVariableMap.find(variable);
45         if (replacement != mVariableMap.end())
46         {
47             queueReplacement(replacement->second->deepCopy(), OriginalNode::IS_DROPPED);
48             return;
49         }
50 
51         int index        = 0;
52         const char *name = "";
53 
54         // Replace the built-ins being emulated with a variable of the appropriate type.
55         switch (type.getQualifier())
56         {
57             case EvqFragColor:
58                 name = "webgl_FragColor";
59                 break;
60             case EvqFragData:
61                 name = "webgl_FragData";
62                 break;
63             case EvqSecondaryFragColorEXT:
64                 name  = "webgl_SecondaryFragColorEXT";
65                 index = 1;
66                 break;
67             case EvqSecondaryFragDataEXT:
68                 name  = "webgl_SecondaryFragDataEXT";
69                 index = 1;
70                 break;
71             default:
72                 // Not the built-in we are looking for.
73                 return;
74         }
75 
76         TType *outputType = new TType(type);
77         // The total number of color attachments is limited to MaxDualSourceDrawBuffers when
78         // dual-source blending is used in the shader. As such resize gl_FragData appropriately when
79         // it is redeclared in a shader that uses gl_SecondaryFragData.
80         if (type.getQualifier() == EvqFragData && mHasGLSecondaryFragData)
81         {
82             outputType->setArraySize(0, mResources.MaxDualSourceDrawBuffers);
83         }
84 
85         outputType->setQualifier(EvqFragmentOut);
86         if (index > 0)
87         {
88             TLayoutQualifier layoutQualifier = outputType->getLayoutQualifier();
89             layoutQualifier.index            = index;
90             outputType->setLayoutQualifier(layoutQualifier);
91         }
92 
93         TVariable *replacementVar = new TVariable(mSymbolTable, ImmutableString(name), outputType,
94                                                   SymbolType::AngleInternal);
95 
96         TIntermSymbol *newSymbol = new TIntermSymbol(replacementVar);
97         mVariableMap[variable]   = newSymbol;
98 
99         queueReplacement(newSymbol, OriginalNode::IS_DROPPED);
100     }
101 
addDeclarations(TIntermBlock * root)102     void addDeclarations(TIntermBlock *root)
103     {
104         // Insert the declaration before the first function.
105         size_t firstFunctionIndex = FindFirstFunctionDefinitionIndex(root);
106         TIntermSequence declarations;
107 
108         for (auto &replaced : mVariableMap)
109         {
110             TIntermDeclaration *decl = new TIntermDeclaration;
111             TIntermSymbol *symbol    = replaced.second->deepCopy()->getAsSymbolNode();
112             decl->appendDeclarator(symbol);
113             declarations.push_back(decl);
114         }
115 
116         root->insertChildNodes(firstFunctionIndex, declarations);
117     }
118 
119   private:
120     const ShBuiltInResources &mResources;
121     bool mHasGLSecondaryFragData;
122 
123     // A map of already replaced built-in variables.
124     VariableReplacementMap mVariableMap;
125 };
126 
EmulateFragColorDataImpl(TCompiler * compiler,TIntermBlock * root,TSymbolTable * symbolTable,bool hasGLSecondaryFragData)127 bool EmulateFragColorDataImpl(TCompiler *compiler,
128                               TIntermBlock *root,
129                               TSymbolTable *symbolTable,
130                               bool hasGLSecondaryFragData)
131 {
132     EmulateFragColorDataTraverser traverser(compiler, symbolTable, hasGLSecondaryFragData);
133     root->traverse(&traverser);
134     if (!traverser.updateTree(compiler, root))
135     {
136         return false;
137     }
138 
139     traverser.addDeclarations(root);
140     return true;
141 }
142 }  // anonymous namespace
143 
EmulateFragColorData(TCompiler * compiler,TIntermBlock * root,TSymbolTable * symbolTable,bool hasGLSecondaryFragData)144 bool EmulateFragColorData(TCompiler *compiler,
145                           TIntermBlock *root,
146                           TSymbolTable *symbolTable,
147                           bool hasGLSecondaryFragData)
148 {
149     if (compiler->getShaderType() != GL_FRAGMENT_SHADER)
150     {
151         return true;
152     }
153 
154     // This transformation adds variable declarations after the fact and so some validation is
155     // momentarily disabled.
156     bool enableValidateVariableReferences = compiler->disableValidateVariableReferences();
157 
158     bool result = EmulateFragColorDataImpl(compiler, root, symbolTable, hasGLSecondaryFragData);
159 
160     compiler->restoreValidateVariableReferences(enableValidateVariableReferences);
161     return result && compiler->validateAST(root);
162 }
163 }  // namespace sh
164