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
14 #include "compiler/translator/tree_ops/EmulateGLFragColorBroadcast.h"
15
16 #include "compiler/translator/Compiler.h"
17 #include "compiler/translator/Symbol.h"
18 #include "compiler/translator/tree_util/IntermNode_util.h"
19 #include "compiler/translator/tree_util/IntermTraverse.h"
20 #include "compiler/translator/tree_util/RunAtTheEndOfShader.h"
21
22 namespace sh
23 {
24
25 namespace
26 {
27
28 constexpr const ImmutableString kGlFragDataString("gl_FragData");
29
30 class GLFragColorBroadcastTraverser : public TIntermTraverser
31 {
32 public:
GLFragColorBroadcastTraverser(int maxDrawBuffers,TSymbolTable * symbolTable,int shaderVersion)33 GLFragColorBroadcastTraverser(int maxDrawBuffers, TSymbolTable *symbolTable, int shaderVersion)
34 : TIntermTraverser(true, false, false, symbolTable),
35 mGLFragColorUsed(false),
36 mMaxDrawBuffers(maxDrawBuffers),
37 mShaderVersion(shaderVersion)
38 {}
39
40 ANGLE_NO_DISCARD bool broadcastGLFragColor(TCompiler *compiler, TIntermBlock *root);
41
isGLFragColorUsed() const42 bool isGLFragColorUsed() const { return mGLFragColorUsed; }
43
44 protected:
45 void visitSymbol(TIntermSymbol *node) override;
46
47 TIntermBinary *constructGLFragDataNode(int index) const;
48 TIntermBinary *constructGLFragDataAssignNode(int index) const;
49
50 private:
51 bool mGLFragColorUsed;
52 int mMaxDrawBuffers;
53 const int mShaderVersion;
54 };
55
constructGLFragDataNode(int index) const56 TIntermBinary *GLFragColorBroadcastTraverser::constructGLFragDataNode(int index) const
57 {
58 TIntermSymbol *symbol =
59 ReferenceBuiltInVariable(kGlFragDataString, *mSymbolTable, mShaderVersion);
60 TIntermTyped *indexNode = CreateIndexNode(index);
61
62 TIntermBinary *binary = new TIntermBinary(EOpIndexDirect, symbol, indexNode);
63 return binary;
64 }
65
constructGLFragDataAssignNode(int index) const66 TIntermBinary *GLFragColorBroadcastTraverser::constructGLFragDataAssignNode(int index) const
67 {
68 TIntermTyped *fragDataIndex = constructGLFragDataNode(index);
69 TIntermTyped *fragDataZero = constructGLFragDataNode(0);
70
71 return new TIntermBinary(EOpAssign, fragDataIndex, fragDataZero);
72 }
73
visitSymbol(TIntermSymbol * node)74 void GLFragColorBroadcastTraverser::visitSymbol(TIntermSymbol *node)
75 {
76 if (node->variable().symbolType() == SymbolType::BuiltIn && node->getName() == "gl_FragColor")
77 {
78 queueReplacement(constructGLFragDataNode(0), OriginalNode::IS_DROPPED);
79 mGLFragColorUsed = true;
80 }
81 }
82
broadcastGLFragColor(TCompiler * compiler,TIntermBlock * root)83 bool GLFragColorBroadcastTraverser::broadcastGLFragColor(TCompiler *compiler, TIntermBlock *root)
84 {
85 ASSERT(mMaxDrawBuffers > 1);
86 if (!mGLFragColorUsed)
87 {
88 return true;
89 }
90
91 TIntermBlock *broadcastBlock = new TIntermBlock();
92 // Now insert statements
93 // gl_FragData[1] = gl_FragData[0];
94 // ...
95 // gl_FragData[maxDrawBuffers - 1] = gl_FragData[0];
96 for (int colorIndex = 1; colorIndex < mMaxDrawBuffers; ++colorIndex)
97 {
98 broadcastBlock->appendStatement(constructGLFragDataAssignNode(colorIndex));
99 }
100 return RunAtTheEndOfShader(compiler, root, broadcastBlock, mSymbolTable);
101 }
102
103 } // namespace
104
EmulateGLFragColorBroadcast(TCompiler * compiler,TIntermBlock * root,int maxDrawBuffers,std::vector<sh::ShaderVariable> * outputVariables,TSymbolTable * symbolTable,int shaderVersion)105 bool EmulateGLFragColorBroadcast(TCompiler *compiler,
106 TIntermBlock *root,
107 int maxDrawBuffers,
108 std::vector<sh::ShaderVariable> *outputVariables,
109 TSymbolTable *symbolTable,
110 int shaderVersion)
111 {
112 ASSERT(maxDrawBuffers > 1);
113 GLFragColorBroadcastTraverser traverser(maxDrawBuffers, symbolTable, shaderVersion);
114 root->traverse(&traverser);
115 if (traverser.isGLFragColorUsed())
116 {
117 if (!traverser.updateTree(compiler, root))
118 {
119 return false;
120 }
121 if (!traverser.broadcastGLFragColor(compiler, root))
122 {
123 return false;
124 }
125
126 for (auto &var : *outputVariables)
127 {
128 if (var.name == "gl_FragColor")
129 {
130 // TODO(zmo): Find a way to keep the original variable information.
131 var.name = "gl_FragData";
132 var.mappedName = "gl_FragData";
133 var.arraySizes.push_back(maxDrawBuffers);
134 ASSERT(var.arraySizes.size() == 1u);
135 }
136 }
137 }
138
139 return true;
140 }
141
142 } // namespace sh
143