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