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