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