1 //
2 // Copyright 2002 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 // gl_FragColor needs to broadcast to all color buffers in ES2 if
7 // GL_EXT_draw_buffers is explicitly enabled in a fragment shader.
8 //
9 // We emulate this by replacing all gl_FragColor with gl_FragData[0], and in the end
10 // of main() function, assigning gl_FragData[1], ..., gl_FragData[maxDrawBuffers-1]
11 // with gl_FragData[0].
12 //
13 // Similar replacement applies to gl_SecondaryFragColorEXT if it is used.
14 //
15
16 #include "compiler/translator/tree_ops/EmulateGLFragColorBroadcast.h"
17
18 #include "compiler/translator/Compiler.h"
19 #include "compiler/translator/Symbol.h"
20 #include "compiler/translator/tree_util/IntermNode_util.h"
21 #include "compiler/translator/tree_util/IntermTraverse.h"
22 #include "compiler/translator/tree_util/RunAtTheEndOfShader.h"
23
24 namespace sh
25 {
26
27 namespace
28 {
29
30 constexpr const ImmutableString kGlFragDataString("gl_FragData");
31 constexpr const ImmutableString kGlSecondaryFragDataString("gl_SecondaryFragDataEXT");
32
33 class GLFragColorBroadcastTraverser : public TIntermTraverser
34 {
35 public:
GLFragColorBroadcastTraverser(int maxDrawBuffers,int maxDualSourceDrawBuffers,TSymbolTable * symbolTable,int shaderVersion)36 GLFragColorBroadcastTraverser(int maxDrawBuffers,
37 int maxDualSourceDrawBuffers,
38 TSymbolTable *symbolTable,
39 int shaderVersion)
40 : TIntermTraverser(true, false, false, symbolTable),
41 mGLFragColorUsed(false),
42 mGLSecondaryFragColorUsed(false),
43 mMaxDrawBuffers(maxDrawBuffers),
44 mMaxDualSourceDrawBuffers(maxDualSourceDrawBuffers),
45 mShaderVersion(shaderVersion)
46 {}
47
48 [[nodiscard]] bool broadcastGLFragColor(TCompiler *compiler, TIntermBlock *root);
49
isGLFragColorUsed() const50 bool isGLFragColorUsed() const { return mGLFragColorUsed; }
isGLSecondaryFragColorUsed() const51 bool isGLSecondaryFragColorUsed() const { return mGLSecondaryFragColorUsed; }
52
53 protected:
54 void visitSymbol(TIntermSymbol *node) override;
55
56 TIntermBinary *constructGLFragDataNode(int index, bool secondary) const;
57 TIntermBinary *constructGLFragDataAssignNode(int index, bool secondary) const;
58
59 private:
60 bool mGLFragColorUsed;
61 bool mGLSecondaryFragColorUsed;
62 int mMaxDrawBuffers;
63 int mMaxDualSourceDrawBuffers;
64 const int mShaderVersion;
65 };
66
constructGLFragDataNode(int index,bool secondary) const67 TIntermBinary *GLFragColorBroadcastTraverser::constructGLFragDataNode(int index,
68 bool secondary) const
69 {
70 TIntermSymbol *symbol = ReferenceBuiltInVariable(
71 secondary ? kGlSecondaryFragDataString : kGlFragDataString, *mSymbolTable, mShaderVersion);
72 TIntermTyped *indexNode = CreateIndexNode(index);
73
74 TIntermBinary *binary = new TIntermBinary(EOpIndexDirect, symbol, indexNode);
75 return binary;
76 }
77
constructGLFragDataAssignNode(int index,bool secondary) const78 TIntermBinary *GLFragColorBroadcastTraverser::constructGLFragDataAssignNode(int index,
79 bool secondary) const
80 {
81 TIntermTyped *fragDataIndex = constructGLFragDataNode(index, secondary);
82 TIntermTyped *fragDataZero = constructGLFragDataNode(0, secondary);
83
84 return new TIntermBinary(EOpAssign, fragDataIndex, fragDataZero);
85 }
86
visitSymbol(TIntermSymbol * node)87 void GLFragColorBroadcastTraverser::visitSymbol(TIntermSymbol *node)
88 {
89 if (node->variable().symbolType() == SymbolType::BuiltIn)
90 {
91 if (node->getName() == "gl_FragColor")
92 {
93 queueReplacement(constructGLFragDataNode(0, false), OriginalNode::IS_DROPPED);
94 mGLFragColorUsed = true;
95 }
96 else if (node->getName() == "gl_SecondaryFragColorEXT")
97 {
98 queueReplacement(constructGLFragDataNode(0, true), OriginalNode::IS_DROPPED);
99 mGLSecondaryFragColorUsed = true;
100 }
101 }
102 }
103
broadcastGLFragColor(TCompiler * compiler,TIntermBlock * root)104 bool GLFragColorBroadcastTraverser::broadcastGLFragColor(TCompiler *compiler, TIntermBlock *root)
105 {
106 ASSERT(mMaxDrawBuffers > 1);
107 ASSERT(mMaxDualSourceDrawBuffers > 0 || !mGLSecondaryFragColorUsed);
108 if (!mGLFragColorUsed && !mGLSecondaryFragColorUsed)
109 {
110 return true;
111 }
112
113 TIntermBlock *broadcastBlock = new TIntermBlock();
114 // Now insert statements
115 // maxDrawBuffers is replaced with maxDualSourceDrawBuffers
116 // if gl_SecondaryFragColorEXT was statically used.
117 // gl_FragData[1] = gl_FragData[0];
118 // ...
119 // gl_FragData[maxDrawBuffers - 1] = gl_FragData[0];
120 if (mGLFragColorUsed)
121 {
122 const int buffers = mGLSecondaryFragColorUsed ? mMaxDualSourceDrawBuffers : mMaxDrawBuffers;
123 for (int colorIndex = 1; colorIndex < buffers; ++colorIndex)
124 {
125 broadcastBlock->appendStatement(constructGLFragDataAssignNode(colorIndex, false));
126 }
127 }
128 if (mGLSecondaryFragColorUsed)
129 {
130 for (int colorIndex = 1; colorIndex < mMaxDualSourceDrawBuffers; ++colorIndex)
131 {
132 broadcastBlock->appendStatement(constructGLFragDataAssignNode(colorIndex, true));
133 }
134 }
135 if (broadcastBlock->getChildCount() == 0)
136 {
137 return true;
138 }
139 return RunAtTheEndOfShader(compiler, root, broadcastBlock, mSymbolTable);
140 }
141
142 } // namespace
143
EmulateGLFragColorBroadcast(TCompiler * compiler,TIntermBlock * root,int maxDrawBuffers,int maxDualSourceDrawBuffers,std::vector<sh::ShaderVariable> * outputVariables,TSymbolTable * symbolTable,int shaderVersion)144 bool EmulateGLFragColorBroadcast(TCompiler *compiler,
145 TIntermBlock *root,
146 int maxDrawBuffers,
147 int maxDualSourceDrawBuffers,
148 std::vector<sh::ShaderVariable> *outputVariables,
149 TSymbolTable *symbolTable,
150 int shaderVersion)
151 {
152 ASSERT(maxDrawBuffers > 1);
153 GLFragColorBroadcastTraverser traverser(maxDrawBuffers, maxDualSourceDrawBuffers, symbolTable,
154 shaderVersion);
155 root->traverse(&traverser);
156 if (traverser.isGLFragColorUsed() || traverser.isGLSecondaryFragColorUsed())
157 {
158 if (!traverser.updateTree(compiler, root))
159 {
160 return false;
161 }
162 if (!traverser.broadcastGLFragColor(compiler, root))
163 {
164 return false;
165 }
166
167 for (auto &var : *outputVariables)
168 {
169 if (var.name == "gl_FragColor")
170 {
171 // TODO(zmo): Find a way to keep the original variable information.
172 var.name = "gl_FragData";
173 var.mappedName = "gl_FragData";
174 var.arraySizes.push_back(traverser.isGLSecondaryFragColorUsed()
175 ? maxDualSourceDrawBuffers
176 : maxDrawBuffers);
177 ASSERT(var.arraySizes.size() == 1u);
178 }
179 else if (var.name == "gl_SecondaryFragColorEXT")
180 {
181 var.name = "gl_SecondaryFragDataEXT";
182 var.mappedName = "gl_SecondaryFragDataEXT";
183 var.arraySizes.push_back(maxDualSourceDrawBuffers);
184 ASSERT(var.arraySizes.size() == 1u);
185 }
186 }
187 }
188
189 return true;
190 }
191
192 } // namespace sh
193