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 (!BuiltInGroup::IsBuiltIn(node->getOp()))
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;
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;
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