1 //
2 // Copyright (c) 2017 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 // Applies the necessary AST transformations to support multiview rendering through instancing.
7 // Check the header file For more information.
8 //
9
10 #include "compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.h"
11
12 #include "compiler/translator/FindMain.h"
13 #include "compiler/translator/InitializeVariables.h"
14 #include "compiler/translator/IntermNode_util.h"
15 #include "compiler/translator/IntermTraverse.h"
16 #include "compiler/translator/SymbolTable.h"
17 #include "compiler/translator/util.h"
18
19 namespace sh
20 {
21
22 namespace
23 {
24
25 class ReplaceVariableTraverser : public TIntermTraverser
26 {
27 public:
ReplaceVariableTraverser(const TString & symbolName,TIntermSymbol * newSymbol)28 ReplaceVariableTraverser(const TString &symbolName, TIntermSymbol *newSymbol)
29 : TIntermTraverser(true, false, false), mSymbolName(symbolName), mNewSymbol(newSymbol)
30 {
31 }
32
visitSymbol(TIntermSymbol * node)33 void visitSymbol(TIntermSymbol *node) override
34 {
35 TName &name = node->getName();
36 if (name.getString() == mSymbolName)
37 {
38 queueReplacement(mNewSymbol->deepCopy(), OriginalNode::IS_DROPPED);
39 }
40 }
41
42 private:
43 TString mSymbolName;
44 TIntermSymbol *mNewSymbol;
45 };
46
CreateGLInstanceIDSymbol(const TSymbolTable & symbolTable)47 TIntermSymbol *CreateGLInstanceIDSymbol(const TSymbolTable &symbolTable)
48 {
49 return ReferenceBuiltInVariable("gl_InstanceID", symbolTable, 300);
50 }
51
52 // Adds the InstanceID and ViewID_OVR initializers to the end of the initializers' sequence.
InitializeViewIDAndInstanceID(TIntermTyped * viewIDSymbol,TIntermTyped * instanceIDSymbol,unsigned numberOfViews,const TSymbolTable & symbolTable,TIntermSequence * initializers)53 void InitializeViewIDAndInstanceID(TIntermTyped *viewIDSymbol,
54 TIntermTyped *instanceIDSymbol,
55 unsigned numberOfViews,
56 const TSymbolTable &symbolTable,
57 TIntermSequence *initializers)
58 {
59 // Create an unsigned numberOfViews node.
60 TConstantUnion *numberOfViewsUnsignedConstant = new TConstantUnion();
61 numberOfViewsUnsignedConstant->setUConst(numberOfViews);
62 TIntermConstantUnion *numberOfViewsUint =
63 new TIntermConstantUnion(numberOfViewsUnsignedConstant, TType(EbtUInt, EbpHigh, EvqConst));
64
65 // Create a uint(gl_InstanceID) node.
66 TIntermSequence *glInstanceIDSymbolCastArguments = new TIntermSequence();
67 glInstanceIDSymbolCastArguments->push_back(CreateGLInstanceIDSymbol(symbolTable));
68 TIntermAggregate *glInstanceIDAsUint = TIntermAggregate::CreateConstructor(
69 TType(EbtUInt, EbpHigh, EvqTemporary), glInstanceIDSymbolCastArguments);
70
71 // Create a uint(gl_InstanceID) / numberOfViews node.
72 TIntermBinary *normalizedInstanceID =
73 new TIntermBinary(EOpDiv, glInstanceIDAsUint, numberOfViewsUint);
74
75 // Create an int(uint(gl_InstanceID) / numberOfViews) node.
76 TIntermSequence *normalizedInstanceIDCastArguments = new TIntermSequence();
77 normalizedInstanceIDCastArguments->push_back(normalizedInstanceID);
78 TIntermAggregate *normalizedInstanceIDAsInt = TIntermAggregate::CreateConstructor(
79 TType(EbtInt, EbpHigh, EvqTemporary), normalizedInstanceIDCastArguments);
80
81 // Create an InstanceID = int(uint(gl_InstanceID) / numberOfViews) node.
82 TIntermBinary *instanceIDInitializer =
83 new TIntermBinary(EOpAssign, instanceIDSymbol->deepCopy(), normalizedInstanceIDAsInt);
84 initializers->push_back(instanceIDInitializer);
85
86 // Create a uint(gl_InstanceID) % numberOfViews node.
87 TIntermBinary *normalizedViewID =
88 new TIntermBinary(EOpIMod, glInstanceIDAsUint->deepCopy(), numberOfViewsUint->deepCopy());
89
90 // Create a ViewID_OVR = uint(gl_InstanceID) % numberOfViews node.
91 TIntermBinary *viewIDInitializer =
92 new TIntermBinary(EOpAssign, viewIDSymbol->deepCopy(), normalizedViewID);
93 initializers->push_back(viewIDInitializer);
94 }
95
96 // Replaces every occurrence of a symbol with the name specified in symbolName with newSymbolNode.
ReplaceSymbol(TIntermBlock * root,const TString & symbolName,TIntermSymbol * newSymbolNode)97 void ReplaceSymbol(TIntermBlock *root, const TString &symbolName, TIntermSymbol *newSymbolNode)
98 {
99 ReplaceVariableTraverser traverser(symbolName, newSymbolNode);
100 root->traverse(&traverser);
101 traverser.updateTree();
102 }
103
DeclareGlobalVariable(TIntermBlock * root,TIntermTyped * typedNode)104 void DeclareGlobalVariable(TIntermBlock *root, TIntermTyped *typedNode)
105 {
106 TIntermSequence *globalSequence = root->getSequence();
107 TIntermDeclaration *declaration = new TIntermDeclaration();
108 declaration->appendDeclarator(typedNode->deepCopy());
109 globalSequence->insert(globalSequence->begin(), declaration);
110 }
111
112 // Adds a branch to write int(ViewID_OVR) to either gl_ViewportIndex or gl_Layer. The branch is
113 // added to the end of the initializers' sequence.
SelectViewIndexInVertexShader(TIntermTyped * viewIDSymbol,TIntermTyped * multiviewBaseViewLayerIndexSymbol,TIntermSequence * initializers,const TSymbolTable & symbolTable)114 void SelectViewIndexInVertexShader(TIntermTyped *viewIDSymbol,
115 TIntermTyped *multiviewBaseViewLayerIndexSymbol,
116 TIntermSequence *initializers,
117 const TSymbolTable &symbolTable)
118 {
119 // Create an int(ViewID_OVR) node.
120 TIntermSequence *viewIDSymbolCastArguments = new TIntermSequence();
121 viewIDSymbolCastArguments->push_back(viewIDSymbol);
122 TIntermAggregate *viewIDAsInt = TIntermAggregate::CreateConstructor(
123 TType(EbtInt, EbpHigh, EvqTemporary), viewIDSymbolCastArguments);
124
125 // Create a gl_ViewportIndex node.
126 TIntermSymbol *viewportIndexSymbol =
127 ReferenceBuiltInVariable("gl_ViewportIndex", symbolTable, 0);
128
129 // Create a { gl_ViewportIndex = int(ViewID_OVR) } node.
130 TIntermBlock *viewportIndexInitializerInBlock = new TIntermBlock();
131 viewportIndexInitializerInBlock->appendStatement(
132 new TIntermBinary(EOpAssign, viewportIndexSymbol, viewIDAsInt));
133
134 // Create a gl_Layer node.
135 TIntermSymbol *layerSymbol = ReferenceBuiltInVariable("gl_Layer", symbolTable, 0);
136
137 // Create an int(ViewID_OVR) + multiviewBaseViewLayerIndex node
138 TIntermBinary *sumOfViewIDAndBaseViewIndex =
139 new TIntermBinary(EOpAdd, viewIDAsInt->deepCopy(), multiviewBaseViewLayerIndexSymbol);
140
141 // Create a { gl_Layer = int(ViewID_OVR) + multiviewBaseViewLayerIndex } node.
142 TIntermBlock *layerInitializerInBlock = new TIntermBlock();
143 layerInitializerInBlock->appendStatement(
144 new TIntermBinary(EOpAssign, layerSymbol, sumOfViewIDAndBaseViewIndex));
145
146 // Create a node to compare whether the base view index uniform is less than zero.
147 TIntermBinary *multiviewBaseViewLayerIndexZeroComparison =
148 new TIntermBinary(EOpLessThan, multiviewBaseViewLayerIndexSymbol->deepCopy(),
149 CreateZeroNode(TType(EbtInt, EbpHigh, EvqConst)));
150
151 // Create an if-else statement to select the code path.
152 TIntermIfElse *multiviewBranch =
153 new TIntermIfElse(multiviewBaseViewLayerIndexZeroComparison,
154 viewportIndexInitializerInBlock, layerInitializerInBlock);
155
156 initializers->push_back(multiviewBranch);
157 }
158
159 } // namespace
160
DeclareAndInitBuiltinsForInstancedMultiview(TIntermBlock * root,unsigned numberOfViews,GLenum shaderType,ShCompileOptions compileOptions,ShShaderOutput shaderOutput,TSymbolTable * symbolTable)161 void DeclareAndInitBuiltinsForInstancedMultiview(TIntermBlock *root,
162 unsigned numberOfViews,
163 GLenum shaderType,
164 ShCompileOptions compileOptions,
165 ShShaderOutput shaderOutput,
166 TSymbolTable *symbolTable)
167 {
168 ASSERT(shaderType == GL_VERTEX_SHADER || shaderType == GL_FRAGMENT_SHADER);
169
170 TQualifier viewIDQualifier = (shaderType == GL_VERTEX_SHADER) ? EvqFlatOut : EvqFlatIn;
171 TIntermSymbol *viewIDSymbol = new TIntermSymbol(symbolTable->nextUniqueId(), "ViewID_OVR",
172 TType(EbtUInt, EbpHigh, viewIDQualifier));
173 viewIDSymbol->setInternal(true);
174
175 DeclareGlobalVariable(root, viewIDSymbol);
176 ReplaceSymbol(root, "gl_ViewID_OVR", viewIDSymbol);
177 if (shaderType == GL_VERTEX_SHADER)
178 {
179 // Replacing gl_InstanceID with InstanceID should happen before adding the initializers of
180 // InstanceID and ViewID.
181 TIntermSymbol *instanceIDSymbol = new TIntermSymbol(
182 symbolTable->nextUniqueId(), "InstanceID", TType(EbtInt, EbpHigh, EvqGlobal));
183 instanceIDSymbol->setInternal(true);
184 DeclareGlobalVariable(root, instanceIDSymbol);
185 ReplaceSymbol(root, "gl_InstanceID", instanceIDSymbol);
186
187 TIntermSequence *initializers = new TIntermSequence();
188 InitializeViewIDAndInstanceID(viewIDSymbol, instanceIDSymbol, numberOfViews, *symbolTable,
189 initializers);
190
191 // The AST transformation which adds the expression to select the viewport index should
192 // be done only for the GLSL and ESSL output.
193 const bool selectView = (compileOptions & SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER) != 0u;
194 // Assert that if the view is selected in the vertex shader, then the output is
195 // either GLSL or ESSL.
196 ASSERT(!selectView || IsOutputGLSL(shaderOutput) || IsOutputESSL(shaderOutput));
197 if (selectView)
198 {
199 // Add a uniform to switch between side-by-side and layered rendering.
200 TIntermSymbol *multiviewBaseViewLayerIndexSymbol =
201 new TIntermSymbol(symbolTable->nextUniqueId(), "multiviewBaseViewLayerIndex",
202 TType(EbtInt, EbpHigh, EvqUniform));
203 multiviewBaseViewLayerIndexSymbol->setInternal(true);
204 DeclareGlobalVariable(root, multiviewBaseViewLayerIndexSymbol);
205
206 // Setting a value to gl_ViewportIndex or gl_Layer should happen after ViewID_OVR's
207 // initialization.
208 SelectViewIndexInVertexShader(viewIDSymbol->deepCopy(),
209 multiviewBaseViewLayerIndexSymbol->deepCopy(),
210 initializers, *symbolTable);
211 }
212
213 // Insert initializers at the beginning of main().
214 TIntermBlock *initializersBlock = new TIntermBlock();
215 initializersBlock->getSequence()->swap(*initializers);
216 TIntermBlock *mainBody = FindMainBody(root);
217 mainBody->getSequence()->insert(mainBody->getSequence()->begin(), initializersBlock);
218 }
219 }
220
221 } // namespace sh