1 //
2 // Copyright 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/tree_ops/DeclareAndInitBuiltinsForInstancedMultiview.h"
11
12 #include "compiler/translator/Compiler.h"
13 #include "compiler/translator/StaticType.h"
14 #include "compiler/translator/SymbolTable.h"
15 #include "compiler/translator/tree_ops/InitializeVariables.h"
16 #include "compiler/translator/tree_util/BuiltIn.h"
17 #include "compiler/translator/tree_util/FindMain.h"
18 #include "compiler/translator/tree_util/IntermNode_util.h"
19 #include "compiler/translator/tree_util/IntermTraverse.h"
20 #include "compiler/translator/tree_util/ReplaceVariable.h"
21 #include "compiler/translator/util.h"
22
23 namespace sh
24 {
25
26 namespace
27 {
28
29 constexpr const ImmutableString kViewIDVariableName("ViewID_OVR");
30 constexpr const ImmutableString kInstanceIDVariableName("InstanceID");
31 constexpr const ImmutableString kMultiviewBaseViewLayerIndexVariableName(
32 "multiviewBaseViewLayerIndex");
33
34 // Adds the InstanceID and ViewID_OVR initializers to the end of the initializers' sequence.
InitializeViewIDAndInstanceID(const TVariable * viewID,const TVariable * instanceID,unsigned numberOfViews,const TSymbolTable & symbolTable,TIntermSequence * initializers)35 void InitializeViewIDAndInstanceID(const TVariable *viewID,
36 const TVariable *instanceID,
37 unsigned numberOfViews,
38 const TSymbolTable &symbolTable,
39 TIntermSequence *initializers)
40 {
41 // Create an unsigned numberOfViews node.
42 TConstantUnion *numberOfViewsUnsignedConstant = new TConstantUnion();
43 numberOfViewsUnsignedConstant->setUConst(numberOfViews);
44 TIntermConstantUnion *numberOfViewsUint =
45 new TIntermConstantUnion(numberOfViewsUnsignedConstant, TType(EbtUInt, EbpHigh, EvqConst));
46
47 // Create a uint(gl_InstanceID) node.
48 TIntermSequence glInstanceIDSymbolCastArguments;
49 glInstanceIDSymbolCastArguments.push_back(new TIntermSymbol(BuiltInVariable::gl_InstanceID()));
50 TIntermAggregate *glInstanceIDAsUint = TIntermAggregate::CreateConstructor(
51 TType(EbtUInt, EbpHigh, EvqTemporary), &glInstanceIDSymbolCastArguments);
52
53 // Create a uint(gl_InstanceID) / numberOfViews node.
54 TIntermBinary *normalizedInstanceID =
55 new TIntermBinary(EOpDiv, glInstanceIDAsUint, numberOfViewsUint);
56
57 // Create an int(uint(gl_InstanceID) / numberOfViews) node.
58 TIntermSequence normalizedInstanceIDCastArguments;
59 normalizedInstanceIDCastArguments.push_back(normalizedInstanceID);
60 TIntermAggregate *normalizedInstanceIDAsInt = TIntermAggregate::CreateConstructor(
61 TType(EbtInt, EbpHigh, EvqTemporary), &normalizedInstanceIDCastArguments);
62
63 // Create an InstanceID = int(uint(gl_InstanceID) / numberOfViews) node.
64 TIntermBinary *instanceIDInitializer =
65 new TIntermBinary(EOpAssign, new TIntermSymbol(instanceID), normalizedInstanceIDAsInt);
66 initializers->push_back(instanceIDInitializer);
67
68 // Create a uint(gl_InstanceID) % numberOfViews node.
69 TIntermBinary *normalizedViewID =
70 new TIntermBinary(EOpIMod, glInstanceIDAsUint->deepCopy(), numberOfViewsUint->deepCopy());
71
72 // Create a ViewID_OVR = uint(gl_InstanceID) % numberOfViews node.
73 TIntermBinary *viewIDInitializer =
74 new TIntermBinary(EOpAssign, new TIntermSymbol(viewID), normalizedViewID);
75 initializers->push_back(viewIDInitializer);
76 }
77
78 // Adds a branch to write int(ViewID_OVR) to either gl_ViewportIndex or gl_Layer. The branch is
79 // added to the end of the initializers' sequence.
SelectViewIndexInVertexShader(const TVariable * viewID,const TVariable * multiviewBaseViewLayerIndex,TIntermSequence * initializers,const TSymbolTable & symbolTable)80 void SelectViewIndexInVertexShader(const TVariable *viewID,
81 const TVariable *multiviewBaseViewLayerIndex,
82 TIntermSequence *initializers,
83 const TSymbolTable &symbolTable)
84 {
85 // Create an int(ViewID_OVR) node.
86 TIntermSequence viewIDSymbolCastArguments;
87 viewIDSymbolCastArguments.push_back(new TIntermSymbol(viewID));
88 TIntermAggregate *viewIDAsInt = TIntermAggregate::CreateConstructor(
89 TType(EbtInt, EbpHigh, EvqTemporary), &viewIDSymbolCastArguments);
90
91 // Create a gl_ViewportIndex node.
92 TIntermSymbol *viewportIndexSymbol = new TIntermSymbol(BuiltInVariable::gl_ViewportIndex());
93
94 // Create a { gl_ViewportIndex = int(ViewID_OVR) } node.
95 TIntermBlock *viewportIndexInitializerInBlock = new TIntermBlock();
96 viewportIndexInitializerInBlock->appendStatement(
97 new TIntermBinary(EOpAssign, viewportIndexSymbol, viewIDAsInt));
98
99 // Create a gl_Layer node.
100 TIntermSymbol *layerSymbol = new TIntermSymbol(BuiltInVariable::gl_LayerVS());
101
102 // Create an int(ViewID_OVR) + multiviewBaseViewLayerIndex node
103 TIntermBinary *sumOfViewIDAndBaseViewIndex = new TIntermBinary(
104 EOpAdd, viewIDAsInt->deepCopy(), new TIntermSymbol(multiviewBaseViewLayerIndex));
105
106 // Create a { gl_Layer = int(ViewID_OVR) + multiviewBaseViewLayerIndex } node.
107 TIntermBlock *layerInitializerInBlock = new TIntermBlock();
108 layerInitializerInBlock->appendStatement(
109 new TIntermBinary(EOpAssign, layerSymbol, sumOfViewIDAndBaseViewIndex));
110
111 // Create a node to compare whether the base view index uniform is less than zero.
112 TIntermBinary *multiviewBaseViewLayerIndexZeroComparison =
113 new TIntermBinary(EOpLessThan, new TIntermSymbol(multiviewBaseViewLayerIndex),
114 CreateZeroNode(TType(EbtInt, EbpHigh, EvqConst)));
115
116 // Create an if-else statement to select the code path.
117 TIntermIfElse *multiviewBranch =
118 new TIntermIfElse(multiviewBaseViewLayerIndexZeroComparison,
119 viewportIndexInitializerInBlock, layerInitializerInBlock);
120
121 initializers->push_back(multiviewBranch);
122 }
123
124 } // namespace
125
DeclareAndInitBuiltinsForInstancedMultiview(TCompiler * compiler,TIntermBlock * root,unsigned numberOfViews,GLenum shaderType,ShCompileOptions compileOptions,ShShaderOutput shaderOutput,TSymbolTable * symbolTable)126 bool DeclareAndInitBuiltinsForInstancedMultiview(TCompiler *compiler,
127 TIntermBlock *root,
128 unsigned numberOfViews,
129 GLenum shaderType,
130 ShCompileOptions compileOptions,
131 ShShaderOutput shaderOutput,
132 TSymbolTable *symbolTable)
133 {
134 ASSERT(shaderType == GL_VERTEX_SHADER || shaderType == GL_FRAGMENT_SHADER);
135
136 TQualifier viewIDQualifier = (shaderType == GL_VERTEX_SHADER) ? EvqFlatOut : EvqFlatIn;
137 const TVariable *viewID =
138 new TVariable(symbolTable, kViewIDVariableName,
139 new TType(EbtUInt, EbpHigh, viewIDQualifier), SymbolType::AngleInternal);
140
141 DeclareGlobalVariable(root, viewID);
142 if (!ReplaceVariable(compiler, root, BuiltInVariable::gl_ViewID_OVR(), viewID))
143 {
144 return false;
145 }
146 if (shaderType == GL_VERTEX_SHADER)
147 {
148 // Replacing gl_InstanceID with InstanceID should happen before adding the initializers of
149 // InstanceID and ViewID.
150 const TType *instanceIDVariableType = StaticType::Get<EbtInt, EbpHigh, EvqGlobal, 1, 1>();
151 const TVariable *instanceID =
152 new TVariable(symbolTable, kInstanceIDVariableName, instanceIDVariableType,
153 SymbolType::AngleInternal);
154 DeclareGlobalVariable(root, instanceID);
155 if (!ReplaceVariable(compiler, root, BuiltInVariable::gl_InstanceID(), instanceID))
156 {
157 return false;
158 }
159
160 TIntermSequence initializers;
161 InitializeViewIDAndInstanceID(viewID, instanceID, numberOfViews, *symbolTable,
162 &initializers);
163
164 // The AST transformation which adds the expression to select the viewport index should
165 // be done only for the GLSL and ESSL output.
166 const bool selectView = (compileOptions & SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER) != 0;
167 // Assert that if the view is selected in the vertex shader, then the output is
168 // either GLSL or ESSL.
169 ASSERT(!selectView || IsOutputGLSL(shaderOutput) || IsOutputESSL(shaderOutput));
170 if (selectView)
171 {
172 // Add a uniform to switch between side-by-side and layered rendering.
173 const TType *baseLayerIndexVariableType =
174 StaticType::Get<EbtInt, EbpHigh, EvqUniform, 1, 1>();
175 const TVariable *multiviewBaseViewLayerIndex =
176 new TVariable(symbolTable, kMultiviewBaseViewLayerIndexVariableName,
177 baseLayerIndexVariableType, SymbolType::AngleInternal);
178 DeclareGlobalVariable(root, multiviewBaseViewLayerIndex);
179
180 // Setting a value to gl_ViewportIndex or gl_Layer should happen after ViewID_OVR's
181 // initialization.
182 SelectViewIndexInVertexShader(viewID, multiviewBaseViewLayerIndex, &initializers,
183 *symbolTable);
184 }
185
186 // Insert initializers at the beginning of main().
187 TIntermBlock *initializersBlock = new TIntermBlock();
188 initializersBlock->getSequence()->swap(initializers);
189 TIntermBlock *mainBody = FindMainBody(root);
190 mainBody->getSequence()->insert(mainBody->getSequence()->begin(), initializersBlock);
191 }
192
193 return compiler->validateAST(root);
194 }
195
196 } // namespace sh
197