1 //
2 // Copyright 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 // Implementation of texelFetchOffset translation issue workaround.
7 // See header for more info.
8
9 #include "compiler/translator/tree_ops/RewriteTexelFetchOffset.h"
10
11 #include "common/angleutils.h"
12 #include "compiler/translator/SymbolTable.h"
13 #include "compiler/translator/tree_util/IntermNode_util.h"
14 #include "compiler/translator/tree_util/IntermTraverse.h"
15
16 namespace sh
17 {
18
19 namespace
20 {
21
22 class Traverser : public TIntermTraverser
23 {
24 public:
25 ANGLE_NO_DISCARD static bool Apply(TCompiler *compiler,
26 TIntermNode *root,
27 const TSymbolTable &symbolTable,
28 int shaderVersion);
29
30 private:
31 Traverser(const TSymbolTable &symbolTable, int shaderVersion);
32 bool visitAggregate(Visit visit, TIntermAggregate *node) override;
33 void nextIteration();
34
35 const TSymbolTable *symbolTable;
36 const int shaderVersion;
37 bool mFound = false;
38 };
39
Traverser(const TSymbolTable & symbolTable,int shaderVersion)40 Traverser::Traverser(const TSymbolTable &symbolTable, int shaderVersion)
41 : TIntermTraverser(true, false, false), symbolTable(&symbolTable), shaderVersion(shaderVersion)
42 {}
43
44 // static
Apply(TCompiler * compiler,TIntermNode * root,const TSymbolTable & symbolTable,int shaderVersion)45 bool Traverser::Apply(TCompiler *compiler,
46 TIntermNode *root,
47 const TSymbolTable &symbolTable,
48 int shaderVersion)
49 {
50 Traverser traverser(symbolTable, shaderVersion);
51 do
52 {
53 traverser.nextIteration();
54 root->traverse(&traverser);
55 if (traverser.mFound)
56 {
57 if (!traverser.updateTree(compiler, root))
58 {
59 return false;
60 }
61 }
62 } while (traverser.mFound);
63
64 return true;
65 }
66
nextIteration()67 void Traverser::nextIteration()
68 {
69 mFound = false;
70 }
71
visitAggregate(Visit visit,TIntermAggregate * node)72 bool Traverser::visitAggregate(Visit visit, TIntermAggregate *node)
73 {
74 if (mFound)
75 {
76 return false;
77 }
78
79 // Decide if the node represents the call of texelFetchOffset.
80 if (node->getOp() != EOpCallBuiltInFunction)
81 {
82 return true;
83 }
84
85 ASSERT(node->getFunction()->symbolType() == SymbolType::BuiltIn);
86 if (node->getFunction()->name() != "texelFetchOffset")
87 {
88 return true;
89 }
90
91 // Potential problem case detected, apply workaround.
92 const TIntermSequence *sequence = node->getSequence();
93 ASSERT(sequence->size() == 4u);
94
95 // Decide if the sampler is a 2DArray sampler. In that case position is ivec3 and offset is
96 // ivec2.
97 bool is2DArray = sequence->at(1)->getAsTyped()->getNominalSize() == 3 &&
98 sequence->at(3)->getAsTyped()->getNominalSize() == 2;
99
100 // Create new node that represents the call of function texelFetch.
101 // Its argument list will be: texelFetch(sampler, Position+offset, lod).
102
103 TIntermSequence *texelFetchArguments = new TIntermSequence();
104
105 // sampler
106 texelFetchArguments->push_back(sequence->at(0));
107
108 // Position
109 TIntermTyped *texCoordNode = sequence->at(1)->getAsTyped();
110 ASSERT(texCoordNode);
111
112 // offset
113 TIntermTyped *offsetNode = nullptr;
114 ASSERT(sequence->at(3)->getAsTyped());
115 if (is2DArray)
116 {
117 // For 2DArray samplers, Position is ivec3 and offset is ivec2;
118 // So offset must be converted into an ivec3 before being added to Position.
119 TIntermSequence *constructOffsetIvecArguments = new TIntermSequence();
120 constructOffsetIvecArguments->push_back(sequence->at(3)->getAsTyped());
121
122 TIntermTyped *zeroNode = CreateZeroNode(TType(EbtInt));
123 constructOffsetIvecArguments->push_back(zeroNode);
124
125 offsetNode = TIntermAggregate::CreateConstructor(texCoordNode->getType(),
126 constructOffsetIvecArguments);
127 offsetNode->setLine(texCoordNode->getLine());
128 }
129 else
130 {
131 offsetNode = sequence->at(3)->getAsTyped();
132 }
133
134 // Position+offset
135 TIntermBinary *add = new TIntermBinary(EOpAdd, texCoordNode, offsetNode);
136 add->setLine(texCoordNode->getLine());
137 texelFetchArguments->push_back(add);
138
139 // lod
140 texelFetchArguments->push_back(sequence->at(2));
141
142 ASSERT(texelFetchArguments->size() == 3u);
143
144 TIntermTyped *texelFetchNode = CreateBuiltInFunctionCallNode("texelFetch", texelFetchArguments,
145 *symbolTable, shaderVersion);
146 texelFetchNode->setLine(node->getLine());
147
148 // Replace the old node by this new node.
149 queueReplacement(texelFetchNode, OriginalNode::IS_DROPPED);
150 mFound = true;
151 return false;
152 }
153
154 } // anonymous namespace
155
RewriteTexelFetchOffset(TCompiler * compiler,TIntermNode * root,const TSymbolTable & symbolTable,int shaderVersion)156 bool RewriteTexelFetchOffset(TCompiler *compiler,
157 TIntermNode *root,
158 const TSymbolTable &symbolTable,
159 int shaderVersion)
160 {
161 // texelFetchOffset is only valid in GLSL 3.0 and later.
162 if (shaderVersion < 300)
163 return true;
164
165 return Traverser::Apply(compiler, root, symbolTable, shaderVersion);
166 }
167
168 } // namespace sh
169