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