xref: /aosp_15_r20/external/angle/src/compiler/translator/tree_ops/EmulateGLFragColorBroadcast.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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 // Similar replacement applies to gl_SecondaryFragColorEXT if it is used.
14 //
15 
16 #include "compiler/translator/tree_ops/EmulateGLFragColorBroadcast.h"
17 
18 #include "compiler/translator/Compiler.h"
19 #include "compiler/translator/Symbol.h"
20 #include "compiler/translator/tree_util/IntermNode_util.h"
21 #include "compiler/translator/tree_util/IntermTraverse.h"
22 #include "compiler/translator/tree_util/RunAtTheEndOfShader.h"
23 
24 namespace sh
25 {
26 
27 namespace
28 {
29 
30 constexpr const ImmutableString kGlFragDataString("gl_FragData");
31 constexpr const ImmutableString kGlSecondaryFragDataString("gl_SecondaryFragDataEXT");
32 
33 class GLFragColorBroadcastTraverser : public TIntermTraverser
34 {
35   public:
GLFragColorBroadcastTraverser(int maxDrawBuffers,int maxDualSourceDrawBuffers,TSymbolTable * symbolTable,int shaderVersion)36     GLFragColorBroadcastTraverser(int maxDrawBuffers,
37                                   int maxDualSourceDrawBuffers,
38                                   TSymbolTable *symbolTable,
39                                   int shaderVersion)
40         : TIntermTraverser(true, false, false, symbolTable),
41           mGLFragColorUsed(false),
42           mGLSecondaryFragColorUsed(false),
43           mMaxDrawBuffers(maxDrawBuffers),
44           mMaxDualSourceDrawBuffers(maxDualSourceDrawBuffers),
45           mShaderVersion(shaderVersion)
46     {}
47 
48     [[nodiscard]] bool broadcastGLFragColor(TCompiler *compiler, TIntermBlock *root);
49 
isGLFragColorUsed() const50     bool isGLFragColorUsed() const { return mGLFragColorUsed; }
isGLSecondaryFragColorUsed() const51     bool isGLSecondaryFragColorUsed() const { return mGLSecondaryFragColorUsed; }
52 
53   protected:
54     void visitSymbol(TIntermSymbol *node) override;
55 
56     TIntermBinary *constructGLFragDataNode(int index, bool secondary) const;
57     TIntermBinary *constructGLFragDataAssignNode(int index, bool secondary) const;
58 
59   private:
60     bool mGLFragColorUsed;
61     bool mGLSecondaryFragColorUsed;
62     int mMaxDrawBuffers;
63     int mMaxDualSourceDrawBuffers;
64     const int mShaderVersion;
65 };
66 
constructGLFragDataNode(int index,bool secondary) const67 TIntermBinary *GLFragColorBroadcastTraverser::constructGLFragDataNode(int index,
68                                                                       bool secondary) const
69 {
70     TIntermSymbol *symbol = ReferenceBuiltInVariable(
71         secondary ? kGlSecondaryFragDataString : kGlFragDataString, *mSymbolTable, mShaderVersion);
72     TIntermTyped *indexNode = CreateIndexNode(index);
73 
74     TIntermBinary *binary = new TIntermBinary(EOpIndexDirect, symbol, indexNode);
75     return binary;
76 }
77 
constructGLFragDataAssignNode(int index,bool secondary) const78 TIntermBinary *GLFragColorBroadcastTraverser::constructGLFragDataAssignNode(int index,
79                                                                             bool secondary) const
80 {
81     TIntermTyped *fragDataIndex = constructGLFragDataNode(index, secondary);
82     TIntermTyped *fragDataZero  = constructGLFragDataNode(0, secondary);
83 
84     return new TIntermBinary(EOpAssign, fragDataIndex, fragDataZero);
85 }
86 
visitSymbol(TIntermSymbol * node)87 void GLFragColorBroadcastTraverser::visitSymbol(TIntermSymbol *node)
88 {
89     if (node->variable().symbolType() == SymbolType::BuiltIn)
90     {
91         if (node->getName() == "gl_FragColor")
92         {
93             queueReplacement(constructGLFragDataNode(0, false), OriginalNode::IS_DROPPED);
94             mGLFragColorUsed = true;
95         }
96         else if (node->getName() == "gl_SecondaryFragColorEXT")
97         {
98             queueReplacement(constructGLFragDataNode(0, true), OriginalNode::IS_DROPPED);
99             mGLSecondaryFragColorUsed = true;
100         }
101     }
102 }
103 
broadcastGLFragColor(TCompiler * compiler,TIntermBlock * root)104 bool GLFragColorBroadcastTraverser::broadcastGLFragColor(TCompiler *compiler, TIntermBlock *root)
105 {
106     ASSERT(mMaxDrawBuffers > 1);
107     ASSERT(mMaxDualSourceDrawBuffers > 0 || !mGLSecondaryFragColorUsed);
108     if (!mGLFragColorUsed && !mGLSecondaryFragColorUsed)
109     {
110         return true;
111     }
112 
113     TIntermBlock *broadcastBlock = new TIntermBlock();
114     // Now insert statements
115     // maxDrawBuffers is replaced with maxDualSourceDrawBuffers
116     // if gl_SecondaryFragColorEXT was statically used.
117     //   gl_FragData[1] = gl_FragData[0];
118     //   ...
119     //   gl_FragData[maxDrawBuffers - 1] = gl_FragData[0];
120     if (mGLFragColorUsed)
121     {
122         const int buffers = mGLSecondaryFragColorUsed ? mMaxDualSourceDrawBuffers : mMaxDrawBuffers;
123         for (int colorIndex = 1; colorIndex < buffers; ++colorIndex)
124         {
125             broadcastBlock->appendStatement(constructGLFragDataAssignNode(colorIndex, false));
126         }
127     }
128     if (mGLSecondaryFragColorUsed)
129     {
130         for (int colorIndex = 1; colorIndex < mMaxDualSourceDrawBuffers; ++colorIndex)
131         {
132             broadcastBlock->appendStatement(constructGLFragDataAssignNode(colorIndex, true));
133         }
134     }
135     if (broadcastBlock->getChildCount() == 0)
136     {
137         return true;
138     }
139     return RunAtTheEndOfShader(compiler, root, broadcastBlock, mSymbolTable);
140 }
141 
142 }  // namespace
143 
EmulateGLFragColorBroadcast(TCompiler * compiler,TIntermBlock * root,int maxDrawBuffers,int maxDualSourceDrawBuffers,std::vector<sh::ShaderVariable> * outputVariables,TSymbolTable * symbolTable,int shaderVersion)144 bool EmulateGLFragColorBroadcast(TCompiler *compiler,
145                                  TIntermBlock *root,
146                                  int maxDrawBuffers,
147                                  int maxDualSourceDrawBuffers,
148                                  std::vector<sh::ShaderVariable> *outputVariables,
149                                  TSymbolTable *symbolTable,
150                                  int shaderVersion)
151 {
152     ASSERT(maxDrawBuffers > 1);
153     GLFragColorBroadcastTraverser traverser(maxDrawBuffers, maxDualSourceDrawBuffers, symbolTable,
154                                             shaderVersion);
155     root->traverse(&traverser);
156     if (traverser.isGLFragColorUsed() || traverser.isGLSecondaryFragColorUsed())
157     {
158         if (!traverser.updateTree(compiler, root))
159         {
160             return false;
161         }
162         if (!traverser.broadcastGLFragColor(compiler, root))
163         {
164             return false;
165         }
166 
167         for (auto &var : *outputVariables)
168         {
169             if (var.name == "gl_FragColor")
170             {
171                 // TODO(zmo): Find a way to keep the original variable information.
172                 var.name       = "gl_FragData";
173                 var.mappedName = "gl_FragData";
174                 var.arraySizes.push_back(traverser.isGLSecondaryFragColorUsed()
175                                              ? maxDualSourceDrawBuffers
176                                              : maxDrawBuffers);
177                 ASSERT(var.arraySizes.size() == 1u);
178             }
179             else if (var.name == "gl_SecondaryFragColorEXT")
180             {
181                 var.name       = "gl_SecondaryFragDataEXT";
182                 var.mappedName = "gl_SecondaryFragDataEXT";
183                 var.arraySizes.push_back(maxDualSourceDrawBuffers);
184                 ASSERT(var.arraySizes.size() == 1u);
185             }
186         }
187     }
188 
189     return true;
190 }
191 
192 }  // namespace sh
193