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
20 namespace sh
21 {
22
23 namespace
24 {
25
26 class GLFragColorBroadcastTraverser : public TIntermTraverser
27 {
28 public:
GLFragColorBroadcastTraverser(int maxDrawBuffers,TSymbolTable * symbolTable,int shaderVersion)29 GLFragColorBroadcastTraverser(int maxDrawBuffers, TSymbolTable *symbolTable, int shaderVersion)
30 : TIntermTraverser(true, false, false, symbolTable),
31 mGLFragColorUsed(false),
32 mMaxDrawBuffers(maxDrawBuffers),
33 mShaderVersion(shaderVersion)
34 {
35 }
36
37 void broadcastGLFragColor(TIntermBlock *root);
38
isGLFragColorUsed() const39 bool isGLFragColorUsed() const { return mGLFragColorUsed; }
40
41 protected:
42 void visitSymbol(TIntermSymbol *node) override;
43
44 TIntermBinary *constructGLFragDataNode(int index) const;
45 TIntermBinary *constructGLFragDataAssignNode(int index) const;
46
47 private:
48 bool mGLFragColorUsed;
49 int mMaxDrawBuffers;
50 const int mShaderVersion;
51 };
52
constructGLFragDataNode(int index) const53 TIntermBinary *GLFragColorBroadcastTraverser::constructGLFragDataNode(int index) const
54 {
55 TIntermSymbol *symbol =
56 ReferenceBuiltInVariable(TString("gl_FragData"), *mSymbolTable, mShaderVersion);
57 TIntermTyped *indexNode = CreateIndexNode(index);
58
59 TIntermBinary *binary = new TIntermBinary(EOpIndexDirect, symbol, indexNode);
60 return binary;
61 }
62
constructGLFragDataAssignNode(int index) const63 TIntermBinary *GLFragColorBroadcastTraverser::constructGLFragDataAssignNode(int index) const
64 {
65 TIntermTyped *fragDataIndex = constructGLFragDataNode(index);
66 TIntermTyped *fragDataZero = constructGLFragDataNode(0);
67
68 return new TIntermBinary(EOpAssign, fragDataIndex, fragDataZero);
69 }
70
visitSymbol(TIntermSymbol * node)71 void GLFragColorBroadcastTraverser::visitSymbol(TIntermSymbol *node)
72 {
73 if (node->getSymbol() == "gl_FragColor")
74 {
75 queueReplacement(constructGLFragDataNode(0), OriginalNode::IS_DROPPED);
76 mGLFragColorUsed = true;
77 }
78 }
79
broadcastGLFragColor(TIntermBlock * root)80 void GLFragColorBroadcastTraverser::broadcastGLFragColor(TIntermBlock *root)
81 {
82 ASSERT(mMaxDrawBuffers > 1);
83 if (!mGLFragColorUsed)
84 {
85 return;
86 }
87
88 TIntermBlock *broadcastBlock = new TIntermBlock();
89 // Now insert statements
90 // gl_FragData[1] = gl_FragData[0];
91 // ...
92 // gl_FragData[maxDrawBuffers - 1] = gl_FragData[0];
93 for (int colorIndex = 1; colorIndex < mMaxDrawBuffers; ++colorIndex)
94 {
95 broadcastBlock->appendStatement(constructGLFragDataAssignNode(colorIndex));
96 }
97 RunAtTheEndOfShader(root, broadcastBlock, mSymbolTable);
98 }
99
100 } // namespace anonymous
101
EmulateGLFragColorBroadcast(TIntermBlock * root,int maxDrawBuffers,std::vector<sh::OutputVariable> * outputVariables,TSymbolTable * symbolTable,int shaderVersion)102 void EmulateGLFragColorBroadcast(TIntermBlock *root,
103 int maxDrawBuffers,
104 std::vector<sh::OutputVariable> *outputVariables,
105 TSymbolTable *symbolTable,
106 int shaderVersion)
107 {
108 ASSERT(maxDrawBuffers > 1);
109 GLFragColorBroadcastTraverser traverser(maxDrawBuffers, symbolTable, shaderVersion);
110 root->traverse(&traverser);
111 if (traverser.isGLFragColorUsed())
112 {
113 traverser.updateTree();
114 traverser.broadcastGLFragColor(root);
115 for (auto &var : *outputVariables)
116 {
117 if (var.name == "gl_FragColor")
118 {
119 // TODO(zmo): Find a way to keep the original variable information.
120 var.name = "gl_FragData";
121 var.mappedName = "gl_FragData";
122 var.arraySizes.push_back(maxDrawBuffers);
123 ASSERT(var.arraySizes.size() == 1u);
124 }
125 }
126 }
127 }
128
129 } // namespace sh
130