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