1 //
2 // Copyright 2019 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 // EmulateGLDrawID is an AST traverser to convert the gl_DrawID builtin
7 // to a uniform int
8 //
9 // EmulateGLBaseVertex is an AST traverser to convert the gl_BaseVertex builtin
10 // to a uniform int
11 //
12 // EmulateGLBaseInstance is an AST traverser to convert the gl_BaseInstance builtin
13 // to a uniform int
14 //
15
16 #include "compiler/translator/tree_ops/EmulateMultiDrawShaderBuiltins.h"
17
18 #include "angle_gl.h"
19 #include "compiler/translator/StaticType.h"
20 #include "compiler/translator/Symbol.h"
21 #include "compiler/translator/SymbolTable.h"
22 #include "compiler/translator/tree_util/BuiltIn.h"
23 #include "compiler/translator/tree_util/IntermTraverse.h"
24 #include "compiler/translator/tree_util/ReplaceVariable.h"
25 #include "compiler/translator/util.h"
26
27 namespace sh
28 {
29
30 namespace
31 {
32
33 constexpr const ImmutableString kEmulatedGLDrawIDName("angle_DrawID");
34
35 class FindGLDrawIDTraverser : public TIntermTraverser
36 {
37 public:
FindGLDrawIDTraverser()38 FindGLDrawIDTraverser() : TIntermTraverser(true, false, false), mVariable(nullptr) {}
39
getGLDrawIDBuiltinVariable()40 const TVariable *getGLDrawIDBuiltinVariable() { return mVariable; }
41
42 protected:
visitSymbol(TIntermSymbol * node)43 void visitSymbol(TIntermSymbol *node) override
44 {
45 if (&node->variable() == BuiltInVariable::gl_DrawID())
46 {
47 mVariable = &node->variable();
48 }
49 }
50
51 private:
52 const TVariable *mVariable;
53 };
54
55 class AddBaseVertexToGLVertexIDTraverser : public TIntermTraverser
56 {
57 public:
AddBaseVertexToGLVertexIDTraverser()58 AddBaseVertexToGLVertexIDTraverser() : TIntermTraverser(true, false, false) {}
59
60 protected:
visitSymbol(TIntermSymbol * node)61 void visitSymbol(TIntermSymbol *node) override
62 {
63 if (&node->variable() == BuiltInVariable::gl_VertexID())
64 {
65
66 TIntermSymbol *baseVertexRef = new TIntermSymbol(BuiltInVariable::gl_BaseVertex());
67
68 TIntermBinary *addBaseVertex = new TIntermBinary(EOpAdd, node, baseVertexRef);
69 queueReplacement(addBaseVertex, OriginalNode::BECOMES_CHILD);
70 }
71 }
72 };
73
74 constexpr const ImmutableString kEmulatedGLBaseVertexName("angle_BaseVertex");
75
76 class FindGLBaseVertexTraverser : public TIntermTraverser
77 {
78 public:
FindGLBaseVertexTraverser()79 FindGLBaseVertexTraverser() : TIntermTraverser(true, false, false), mVariable(nullptr) {}
80
getGLBaseVertexBuiltinVariable()81 const TVariable *getGLBaseVertexBuiltinVariable() { return mVariable; }
82
83 protected:
visitSymbol(TIntermSymbol * node)84 void visitSymbol(TIntermSymbol *node) override
85 {
86 if (&node->variable() == BuiltInVariable::gl_BaseVertex())
87 {
88 mVariable = &node->variable();
89 }
90 }
91
92 private:
93 const TVariable *mVariable;
94 };
95
96 constexpr const ImmutableString kEmulatedGLBaseInstanceName("angle_BaseInstance");
97
98 class FindGLBaseInstanceTraverser : public TIntermTraverser
99 {
100 public:
FindGLBaseInstanceTraverser()101 FindGLBaseInstanceTraverser() : TIntermTraverser(true, false, false), mVariable(nullptr) {}
102
getGLBaseInstanceBuiltinVariable()103 const TVariable *getGLBaseInstanceBuiltinVariable() { return mVariable; }
104
105 protected:
visitSymbol(TIntermSymbol * node)106 void visitSymbol(TIntermSymbol *node) override
107 {
108 if (&node->variable() == BuiltInVariable::gl_BaseInstance())
109 {
110 mVariable = &node->variable();
111 }
112 }
113
114 private:
115 const TVariable *mVariable;
116 };
117
118 } // namespace
119
EmulateGLDrawID(TCompiler * compiler,TIntermBlock * root,TSymbolTable * symbolTable,std::vector<sh::ShaderVariable> * uniforms,bool shouldCollect)120 bool EmulateGLDrawID(TCompiler *compiler,
121 TIntermBlock *root,
122 TSymbolTable *symbolTable,
123 std::vector<sh::ShaderVariable> *uniforms,
124 bool shouldCollect)
125 {
126 FindGLDrawIDTraverser traverser;
127 root->traverse(&traverser);
128 const TVariable *builtInVariable = traverser.getGLDrawIDBuiltinVariable();
129 if (builtInVariable)
130 {
131 const TType *type = StaticType::Get<EbtInt, EbpHigh, EvqUniform, 1, 1>();
132 const TVariable *drawID =
133 new TVariable(symbolTable, kEmulatedGLDrawIDName, type, SymbolType::AngleInternal);
134 const TIntermSymbol *drawIDSymbol = new TIntermSymbol(drawID);
135
136 // AngleInternal variables don't get collected
137 if (shouldCollect)
138 {
139 ShaderVariable uniform;
140 uniform.name = kEmulatedGLDrawIDName.data();
141 uniform.mappedName = kEmulatedGLDrawIDName.data();
142 uniform.type = GLVariableType(*type);
143 uniform.precision = GLVariablePrecision(*type);
144 uniform.staticUse = symbolTable->isStaticallyUsed(*builtInVariable);
145 uniform.active = true;
146 uniform.binding = type->getLayoutQualifier().binding;
147 uniform.location = type->getLayoutQualifier().location;
148 uniform.offset = type->getLayoutQualifier().offset;
149 uniform.readonly = type->getMemoryQualifier().readonly;
150 uniform.writeonly = type->getMemoryQualifier().writeonly;
151 uniforms->push_back(uniform);
152 }
153
154 DeclareGlobalVariable(root, drawID);
155 if (!ReplaceVariableWithTyped(compiler, root, builtInVariable, drawIDSymbol))
156 {
157 return false;
158 }
159 }
160
161 return true;
162 }
163
EmulateGLBaseVertexBaseInstance(TCompiler * compiler,TIntermBlock * root,TSymbolTable * symbolTable,std::vector<sh::ShaderVariable> * uniforms,bool shouldCollect,bool addBaseVertexToVertexID)164 bool EmulateGLBaseVertexBaseInstance(TCompiler *compiler,
165 TIntermBlock *root,
166 TSymbolTable *symbolTable,
167 std::vector<sh::ShaderVariable> *uniforms,
168 bool shouldCollect,
169 bool addBaseVertexToVertexID)
170 {
171 bool addBaseVertex = false, addBaseInstance = false;
172 ShaderVariable uniformBaseVertex, uniformBaseInstance;
173
174 if (addBaseVertexToVertexID)
175 {
176 // This is a workaround for Mac AMD GPU
177 // Replace gl_VertexID with (gl_VertexID + gl_BaseVertex)
178 AddBaseVertexToGLVertexIDTraverser traverserVertexID;
179 root->traverse(&traverserVertexID);
180 if (!traverserVertexID.updateTree(compiler, root))
181 {
182 return false;
183 }
184 }
185
186 FindGLBaseVertexTraverser traverserBaseVertex;
187 root->traverse(&traverserBaseVertex);
188 const TVariable *builtInVariableBaseVertex =
189 traverserBaseVertex.getGLBaseVertexBuiltinVariable();
190
191 if (builtInVariableBaseVertex)
192 {
193 const TType *type = StaticType::Get<EbtInt, EbpHigh, EvqUniform, 1, 1>();
194 const TVariable *baseVertex =
195 new TVariable(symbolTable, kEmulatedGLBaseVertexName, type, SymbolType::AngleInternal);
196 const TIntermSymbol *baseVertexSymbol = new TIntermSymbol(baseVertex);
197
198 // AngleInternal variables don't get collected
199 if (shouldCollect)
200 {
201 uniformBaseVertex.name = kEmulatedGLBaseVertexName.data();
202 uniformBaseVertex.mappedName = kEmulatedGLBaseVertexName.data();
203 uniformBaseVertex.type = GLVariableType(*type);
204 uniformBaseVertex.precision = GLVariablePrecision(*type);
205 uniformBaseVertex.staticUse = symbolTable->isStaticallyUsed(*builtInVariableBaseVertex);
206 uniformBaseVertex.active = true;
207 uniformBaseVertex.binding = type->getLayoutQualifier().binding;
208 uniformBaseVertex.location = type->getLayoutQualifier().location;
209 uniformBaseVertex.offset = type->getLayoutQualifier().offset;
210 uniformBaseVertex.readonly = type->getMemoryQualifier().readonly;
211 uniformBaseVertex.writeonly = type->getMemoryQualifier().writeonly;
212 addBaseVertex = true;
213 }
214
215 DeclareGlobalVariable(root, baseVertex);
216 if (!ReplaceVariableWithTyped(compiler, root, builtInVariableBaseVertex, baseVertexSymbol))
217 {
218 return false;
219 }
220 }
221
222 FindGLBaseInstanceTraverser traverserInstance;
223 root->traverse(&traverserInstance);
224 const TVariable *builtInVariableBaseInstance =
225 traverserInstance.getGLBaseInstanceBuiltinVariable();
226
227 if (builtInVariableBaseInstance)
228 {
229 const TType *type = StaticType::Get<EbtInt, EbpHigh, EvqUniform, 1, 1>();
230 const TVariable *baseInstance = new TVariable(symbolTable, kEmulatedGLBaseInstanceName,
231 type, SymbolType::AngleInternal);
232 const TIntermSymbol *baseInstanceSymbol = new TIntermSymbol(baseInstance);
233
234 // AngleInternal variables don't get collected
235 if (shouldCollect)
236 {
237 uniformBaseInstance.name = kEmulatedGLBaseInstanceName.data();
238 uniformBaseInstance.mappedName = kEmulatedGLBaseInstanceName.data();
239 uniformBaseInstance.type = GLVariableType(*type);
240 uniformBaseInstance.precision = GLVariablePrecision(*type);
241 uniformBaseInstance.staticUse =
242 symbolTable->isStaticallyUsed(*builtInVariableBaseInstance);
243 uniformBaseInstance.active = true;
244 uniformBaseInstance.binding = type->getLayoutQualifier().binding;
245 uniformBaseInstance.location = type->getLayoutQualifier().location;
246 uniformBaseInstance.offset = type->getLayoutQualifier().offset;
247 uniformBaseInstance.readonly = type->getMemoryQualifier().readonly;
248 uniformBaseInstance.writeonly = type->getMemoryQualifier().writeonly;
249 addBaseInstance = true;
250 }
251
252 DeclareGlobalVariable(root, baseInstance);
253 if (!ReplaceVariableWithTyped(compiler, root, builtInVariableBaseInstance,
254 baseInstanceSymbol))
255 {
256 return false;
257 }
258 }
259
260 // Make sure the order in uniforms is the same as the traverse order
261 if (addBaseInstance)
262 {
263 uniforms->push_back(uniformBaseInstance);
264 }
265 if (addBaseVertex)
266 {
267 uniforms->push_back(uniformBaseVertex);
268 }
269
270 return true;
271 }
272
273 } // namespace sh
274