Line data Source code
1 : //
2 : // Copyright (c) 2002-2014 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 : // Scalarize vector and matrix constructor args, so that vectors built from components don't have
7 : // matrix arguments, and matrices built from components don't have vector arguments. This avoids
8 : // driver bugs around vector and matrix constructors.
9 : //
10 :
11 : #include "common/debug.h"
12 : #include "compiler/translator/ScalarizeVecAndMatConstructorArgs.h"
13 :
14 : #include <algorithm>
15 :
16 : #include "angle_gl.h"
17 : #include "common/angleutils.h"
18 : #include "compiler/translator/IntermNode.h"
19 :
20 : namespace sh
21 : {
22 :
23 : namespace
24 : {
25 :
26 0 : bool ContainsMatrixNode(const TIntermSequence &sequence)
27 : {
28 0 : for (size_t ii = 0; ii < sequence.size(); ++ii)
29 : {
30 0 : TIntermTyped *node = sequence[ii]->getAsTyped();
31 0 : if (node && node->isMatrix())
32 0 : return true;
33 : }
34 0 : return false;
35 : }
36 :
37 0 : bool ContainsVectorNode(const TIntermSequence &sequence)
38 : {
39 0 : for (size_t ii = 0; ii < sequence.size(); ++ii)
40 : {
41 0 : TIntermTyped *node = sequence[ii]->getAsTyped();
42 0 : if (node && node->isVector())
43 0 : return true;
44 : }
45 0 : return false;
46 : }
47 :
48 0 : TIntermBinary *ConstructVectorIndexBinaryNode(TIntermSymbol *symbolNode, int index)
49 : {
50 0 : return new TIntermBinary(EOpIndexDirect, symbolNode, TIntermTyped::CreateIndexNode(index));
51 : }
52 :
53 0 : TIntermBinary *ConstructMatrixIndexBinaryNode(
54 : TIntermSymbol *symbolNode, int colIndex, int rowIndex)
55 : {
56 : TIntermBinary *colVectorNode =
57 0 : ConstructVectorIndexBinaryNode(symbolNode, colIndex);
58 :
59 : return new TIntermBinary(EOpIndexDirect, colVectorNode,
60 0 : TIntermTyped::CreateIndexNode(rowIndex));
61 : }
62 :
63 0 : class ScalarizeArgsTraverser : public TIntermTraverser
64 : {
65 : public:
66 0 : ScalarizeArgsTraverser(sh::GLenum shaderType,
67 : bool fragmentPrecisionHigh,
68 : unsigned int *temporaryIndex)
69 0 : : TIntermTraverser(true, false, false),
70 : mShaderType(shaderType),
71 0 : mFragmentPrecisionHigh(fragmentPrecisionHigh)
72 : {
73 0 : useTemporaryIndex(temporaryIndex);
74 0 : }
75 :
76 : protected:
77 : bool visitAggregate(Visit visit, TIntermAggregate *node) override;
78 : bool visitBlock(Visit visit, TIntermBlock *node) override;
79 :
80 : private:
81 : void scalarizeArgs(TIntermAggregate *aggregate, bool scalarizeVector, bool scalarizeMatrix);
82 :
83 : // If we have the following code:
84 : // mat4 m(0);
85 : // vec4 v(1, m);
86 : // We will rewrite to:
87 : // mat4 m(0);
88 : // mat4 s0 = m;
89 : // vec4 v(1, s0[0][0], s0[0][1], s0[0][2]);
90 : // This function is to create nodes for "mat4 s0 = m;" and insert it to the code sequence. This
91 : // way the possible side effects of the constructor argument will only be evaluated once.
92 : void createTempVariable(TIntermTyped *original);
93 :
94 : std::vector<TIntermSequence> mBlockStack;
95 :
96 : sh::GLenum mShaderType;
97 : bool mFragmentPrecisionHigh;
98 : };
99 :
100 0 : bool ScalarizeArgsTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
101 : {
102 0 : if (visit == PreVisit)
103 : {
104 0 : switch (node->getOp())
105 : {
106 : case EOpConstructVec2:
107 : case EOpConstructVec3:
108 : case EOpConstructVec4:
109 : case EOpConstructBVec2:
110 : case EOpConstructBVec3:
111 : case EOpConstructBVec4:
112 : case EOpConstructIVec2:
113 : case EOpConstructIVec3:
114 : case EOpConstructIVec4:
115 0 : if (ContainsMatrixNode(*(node->getSequence())))
116 0 : scalarizeArgs(node, false, true);
117 0 : break;
118 : case EOpConstructMat2:
119 : case EOpConstructMat2x3:
120 : case EOpConstructMat2x4:
121 : case EOpConstructMat3x2:
122 : case EOpConstructMat3:
123 : case EOpConstructMat3x4:
124 : case EOpConstructMat4x2:
125 : case EOpConstructMat4x3:
126 : case EOpConstructMat4:
127 0 : if (ContainsVectorNode(*(node->getSequence())))
128 0 : scalarizeArgs(node, true, false);
129 0 : break;
130 : default:
131 0 : break;
132 : }
133 : }
134 0 : return true;
135 : }
136 :
137 0 : bool ScalarizeArgsTraverser::visitBlock(Visit visit, TIntermBlock *node)
138 : {
139 0 : mBlockStack.push_back(TIntermSequence());
140 : {
141 0 : for (TIntermNode *child : *node->getSequence())
142 : {
143 0 : ASSERT(child != nullptr);
144 0 : child->traverse(this);
145 0 : mBlockStack.back().push_back(child);
146 : }
147 : }
148 0 : if (mBlockStack.back().size() > node->getSequence()->size())
149 : {
150 0 : node->getSequence()->clear();
151 0 : *(node->getSequence()) = mBlockStack.back();
152 : }
153 0 : mBlockStack.pop_back();
154 0 : return false;
155 : }
156 :
157 0 : void ScalarizeArgsTraverser::scalarizeArgs(TIntermAggregate *aggregate,
158 : bool scalarizeVector,
159 : bool scalarizeMatrix)
160 : {
161 0 : ASSERT(aggregate);
162 0 : int size = 0;
163 0 : switch (aggregate->getOp())
164 : {
165 : case EOpConstructVec2:
166 : case EOpConstructBVec2:
167 : case EOpConstructIVec2:
168 0 : size = 2;
169 0 : break;
170 : case EOpConstructVec3:
171 : case EOpConstructBVec3:
172 : case EOpConstructIVec3:
173 0 : size = 3;
174 0 : break;
175 : case EOpConstructVec4:
176 : case EOpConstructBVec4:
177 : case EOpConstructIVec4:
178 : case EOpConstructMat2:
179 0 : size = 4;
180 0 : break;
181 : case EOpConstructMat2x3:
182 : case EOpConstructMat3x2:
183 0 : size = 6;
184 0 : break;
185 : case EOpConstructMat2x4:
186 : case EOpConstructMat4x2:
187 0 : size = 8;
188 0 : break;
189 : case EOpConstructMat3:
190 0 : size = 9;
191 0 : break;
192 : case EOpConstructMat3x4:
193 : case EOpConstructMat4x3:
194 0 : size = 12;
195 0 : break;
196 : case EOpConstructMat4:
197 0 : size = 16;
198 0 : break;
199 : default:
200 0 : break;
201 : }
202 0 : TIntermSequence *sequence = aggregate->getSequence();
203 0 : TIntermSequence original(*sequence);
204 0 : sequence->clear();
205 0 : for (size_t ii = 0; ii < original.size(); ++ii)
206 : {
207 0 : ASSERT(size > 0);
208 0 : TIntermTyped *node = original[ii]->getAsTyped();
209 0 : ASSERT(node);
210 0 : createTempVariable(node);
211 0 : if (node->isScalar())
212 : {
213 0 : sequence->push_back(createTempSymbol(node->getType()));
214 0 : size--;
215 : }
216 0 : else if (node->isVector())
217 : {
218 0 : if (scalarizeVector)
219 : {
220 0 : int repeat = std::min(size, node->getNominalSize());
221 0 : size -= repeat;
222 0 : for (int index = 0; index < repeat; ++index)
223 : {
224 0 : TIntermSymbol *symbolNode = createTempSymbol(node->getType());
225 : TIntermBinary *newNode = ConstructVectorIndexBinaryNode(
226 0 : symbolNode, index);
227 0 : sequence->push_back(newNode);
228 : }
229 : }
230 : else
231 : {
232 0 : TIntermSymbol *symbolNode = createTempSymbol(node->getType());
233 0 : sequence->push_back(symbolNode);
234 0 : size -= node->getNominalSize();
235 : }
236 : }
237 : else
238 : {
239 0 : ASSERT(node->isMatrix());
240 0 : if (scalarizeMatrix)
241 : {
242 0 : int colIndex = 0, rowIndex = 0;
243 0 : int repeat = std::min(size, node->getCols() * node->getRows());
244 0 : size -= repeat;
245 0 : while (repeat > 0)
246 : {
247 0 : TIntermSymbol *symbolNode = createTempSymbol(node->getType());
248 : TIntermBinary *newNode = ConstructMatrixIndexBinaryNode(
249 0 : symbolNode, colIndex, rowIndex);
250 0 : sequence->push_back(newNode);
251 0 : rowIndex++;
252 0 : if (rowIndex >= node->getRows())
253 : {
254 0 : rowIndex = 0;
255 0 : colIndex++;
256 : }
257 0 : repeat--;
258 : }
259 : }
260 : else
261 : {
262 0 : TIntermSymbol *symbolNode = createTempSymbol(node->getType());
263 0 : sequence->push_back(symbolNode);
264 0 : size -= node->getCols() * node->getRows();
265 : }
266 : }
267 : }
268 0 : }
269 :
270 0 : void ScalarizeArgsTraverser::createTempVariable(TIntermTyped *original)
271 : {
272 0 : ASSERT(original);
273 0 : nextTemporaryIndex();
274 0 : TIntermDeclaration *decl = createTempInitDeclaration(original);
275 :
276 0 : TType type = original->getType();
277 0 : if (mShaderType == GL_FRAGMENT_SHADER &&
278 0 : type.getBasicType() == EbtFloat &&
279 0 : type.getPrecision() == EbpUndefined)
280 : {
281 : // We use the highest available precision for the temporary variable
282 : // to avoid computing the actual precision using the rules defined
283 : // in GLSL ES 1.0 Section 4.5.2.
284 0 : TIntermBinary *init = decl->getSequence()->at(0)->getAsBinaryNode();
285 0 : init->getTypePointer()->setPrecision(mFragmentPrecisionHigh ? EbpHigh : EbpMedium);
286 0 : init->getLeft()->getTypePointer()->setPrecision(mFragmentPrecisionHigh ? EbpHigh
287 0 : : EbpMedium);
288 : }
289 :
290 0 : ASSERT(mBlockStack.size() > 0);
291 0 : TIntermSequence &sequence = mBlockStack.back();
292 0 : sequence.push_back(decl);
293 0 : }
294 :
295 : } // namespace anonymous
296 :
297 0 : void ScalarizeVecAndMatConstructorArgs(TIntermBlock *root,
298 : sh::GLenum shaderType,
299 : bool fragmentPrecisionHigh,
300 : unsigned int *temporaryIndex)
301 : {
302 0 : ScalarizeArgsTraverser scalarizer(shaderType, fragmentPrecisionHigh, temporaryIndex);
303 0 : root->traverse(&scalarizer);
304 0 : }
305 :
306 : } // namespace sh
|