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