1 //
2 // Copyright (c) 2002-2016 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/EmulateGLFragColorBroadcast.h"
15
16 #include "compiler/translator/IntermNode_util.h"
17 #include "compiler/translator/IntermTraverse.h"
18 #include "compiler/translator/RunAtTheEndOfShader.h"
19 #include "compiler/translator/Symbol.h"
20
21 namespace sh
22 {
23
24 namespace
25 {
26
27 constexpr const ImmutableString kGlFragDataString("gl_FragData");
28
29 class GLFragColorBroadcastTraverser : public TIntermTraverser
30 {
31 public:
GLFragColorBroadcastTraverser(int maxDrawBuffers,TSymbolTable * symbolTable,int shaderVersion)32 GLFragColorBroadcastTraverser(int maxDrawBuffers, TSymbolTable *symbolTable, int shaderVersion)
33 : TIntermTraverser(true, false, false, symbolTable),
34 mGLFragColorUsed(false),
35 mMaxDrawBuffers(maxDrawBuffers),
36 mShaderVersion(shaderVersion)
37 {
38 }
39
40 void broadcastGLFragColor(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(TIntermBlock * root)83 void GLFragColorBroadcastTraverser::broadcastGLFragColor(TIntermBlock *root)
84 {
85 ASSERT(mMaxDrawBuffers > 1);
86 if (!mGLFragColorUsed)
87 {
88 return;
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 RunAtTheEndOfShader(root, broadcastBlock, mSymbolTable);
101 }
102
103 } // namespace anonymous
104
EmulateGLFragColorBroadcast(TIntermBlock * root,int maxDrawBuffers,std::vector<sh::OutputVariable> * outputVariables,TSymbolTable * symbolTable,int shaderVersion)105 void EmulateGLFragColorBroadcast(TIntermBlock *root,
106 int maxDrawBuffers,
107 std::vector<sh::OutputVariable> *outputVariables,
108 TSymbolTable *symbolTable,
109 int shaderVersion)
110 {
111 ASSERT(maxDrawBuffers > 1);
112 GLFragColorBroadcastTraverser traverser(maxDrawBuffers, symbolTable, shaderVersion);
113 root->traverse(&traverser);
114 if (traverser.isGLFragColorUsed())
115 {
116 traverser.updateTree();
117 traverser.broadcastGLFragColor(root);
118 for (auto &var : *outputVariables)
119 {
120 if (var.name == "gl_FragColor")
121 {
122 // TODO(zmo): Find a way to keep the original variable information.
123 var.name = "gl_FragData";
124 var.mappedName = "gl_FragData";
125 var.arraySizes.push_back(maxDrawBuffers);
126 ASSERT(var.arraySizes.size() == 1u);
127 }
128 }
129 }
130 }
131
132 } // namespace sh
133