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