Commit 7ef89a42 by Martin Radev Committed by Commit Bot

Expose ViewID_OVR impostor in the fragment shader

The OVR_multiview specification states that gl_ViewID_OVR is visible at each pipeline stage. Previously to this patch the ViewID_OVR impostor was declared only in the vertex shader and occurrences of gl_ViewID_OVR in the fragment shader were not being handled. The patch addresses the issue by declaring the ViewID_OVR variable as a vertex output in the vertex shader and as a fragment input in the fragment shader. BUG=angleproject:2062 TEST=angle_unittests Change-Id: I895953e81d3632d9bb873e8ac081fdf36f63f6b7 Reviewed-on: https://chromium-review.googlesource.com/559337 Commit-Queue: Olli Etuaho <oetuaho@nvidia.com> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org>
parent e469de8a
...@@ -223,10 +223,12 @@ const ShCompileOptions SH_TRANSLATE_VIEWID_OVR_TO_UNIFORM = UINT64_C(1) << 31; ...@@ -223,10 +223,12 @@ const ShCompileOptions SH_TRANSLATE_VIEWID_OVR_TO_UNIFORM = UINT64_C(1) << 31;
const ShCompileOptions SH_INITIALIZE_UNINITIALIZED_LOCALS = UINT64_C(1) << 32; const ShCompileOptions SH_INITIALIZE_UNINITIALIZED_LOCALS = UINT64_C(1) << 32;
// The flag modifies the shader in the following way: // The flag modifies the shader in the following way:
// Every occurrence of gl_InstanceID and gl_ViewID_OVR is replaced correspondingly by the global // Every occurrence of gl_InstanceID is replaced by the global temporary variable InstanceID.
// temporary variables InstanceID and ViewID_OVR. The following initializers are added at the // Every occurrence of gl_ViewID_OVR is replaced by the varying variable ViewID_OVR.
// beginning of main(): ViewID_OVR = uint(gl_InstanceID) % num_views; InstanceID = gl_InstanceID / // At the beginning of the body of main() in a vertex shader the following initializers are added:
// num_views; // ViewID_OVR = uint(gl_InstanceID) % num_views;
// InstanceID = gl_InstanceID / num_views;
// ViewID_OVR is added as a varying variable to both the vertex and fragment shaders.
const ShCompileOptions SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW = UINT64_C(1) << 33; const ShCompileOptions SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW = UINT64_C(1) << 33;
// Defines alternate strategies for implementing array index clamping. // Defines alternate strategies for implementing array index clamping.
......
...@@ -429,9 +429,9 @@ TIntermBlock *TCompiler::compileTreeImpl(const char *const shaderStrings[], ...@@ -429,9 +429,9 @@ TIntermBlock *TCompiler::compileTreeImpl(const char *const shaderStrings[],
arrayBoundsClamper.MarkIndirectArrayBoundsForClamping(root); arrayBoundsClamper.MarkIndirectArrayBoundsForClamping(root);
if (success && (compileOptions & SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW) && if (success && (compileOptions & SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW) &&
parseContext.isMultiviewExtensionEnabled() && getShaderType() == GL_VERTEX_SHADER) parseContext.isMultiviewExtensionEnabled() && getShaderType() != GL_COMPUTE_SHADER)
{ {
DeclareAndInitBuiltinsForInstancedMultiview(root, getNumViews()); DeclareAndInitBuiltinsForInstancedMultiview(root, getNumViews(), getShaderType());
} }
// This pass might emit short circuits so keep it before the short circuit unfolding // This pass might emit short circuits so keep it before the short circuit unfolding
......
...@@ -20,28 +20,26 @@ namespace sh ...@@ -20,28 +20,26 @@ namespace sh
namespace namespace
{ {
class RenameVariableAndMarkAsInternalTraverser : public TIntermTraverser class ReplaceVariableTraverser : public TIntermTraverser
{ {
public: public:
RenameVariableAndMarkAsInternalTraverser(const TString &oldName, const TString &newName) ReplaceVariableTraverser(const TString &symbolName, TIntermSymbol *newSymbol)
: TIntermTraverser(true, false, false), mOldName(oldName), mNewName(newName) : TIntermTraverser(true, false, false), mSymbolName(symbolName), mNewSymbol(newSymbol)
{ {
} }
void visitSymbol(TIntermSymbol *node) override void visitSymbol(TIntermSymbol *node) override
{ {
TName &name = node->getName(); TName &name = node->getName();
if (name.getString() == mOldName) if (name.getString() == mSymbolName)
{ {
node->getTypePointer()->setQualifier(EvqTemporary); queueReplacement(node, mNewSymbol->deepCopy(), OriginalNode::IS_DROPPED);
name.setInternal(true);
name.setString(mNewName);
} }
} }
private: private:
TString mOldName; TString mSymbolName;
TString mNewName; TIntermSymbol *mNewSymbol;
}; };
TIntermSymbol *CreateGLInstanceIDSymbol() TIntermSymbol *CreateGLInstanceIDSymbol()
...@@ -49,15 +47,14 @@ TIntermSymbol *CreateGLInstanceIDSymbol() ...@@ -49,15 +47,14 @@ TIntermSymbol *CreateGLInstanceIDSymbol()
return new TIntermSymbol(0, "gl_InstanceID", TType(EbtInt, EbpHigh, EvqInstanceID)); return new TIntermSymbol(0, "gl_InstanceID", TType(EbtInt, EbpHigh, EvqInstanceID));
} }
// Adds initializers for InstanceID and ViewID_OVR at the beginning of main(). void InitializeViewIDAndInstanceID(TIntermBlock *root,
void InitializeBuiltinsInMain(TIntermBlock *root, TIntermTyped *viewIDSymbol,
TIntermSymbol *instanceIDSymbol, TIntermTyped *instanceIDSymbol,
TIntermSymbol *viewIDSymbol, unsigned numberOfViews)
unsigned numberOfViews)
{ {
// Create a signed numberOfViews node. // Create a signed numberOfViews node.
TConstantUnion *numberOfViewsConstant = new TConstantUnion(); TConstantUnion *numberOfViewsConstant = new TConstantUnion();
numberOfViewsConstant->setIConst(numberOfViews); numberOfViewsConstant->setIConst(static_cast<int>(numberOfViews));
TIntermConstantUnion *numberOfViewsIntSymbol = TIntermConstantUnion *numberOfViewsIntSymbol =
new TIntermConstantUnion(numberOfViewsConstant, TType(EbtInt, EbpHigh, EvqConst)); new TIntermConstantUnion(numberOfViewsConstant, TType(EbtInt, EbpHigh, EvqConst));
...@@ -67,15 +64,15 @@ void InitializeBuiltinsInMain(TIntermBlock *root, ...@@ -67,15 +64,15 @@ void InitializeBuiltinsInMain(TIntermBlock *root,
// Create a InstanceID = gl_InstanceID / numberOfViews node. // Create a InstanceID = gl_InstanceID / numberOfViews node.
TIntermBinary *instanceIDInitializer = TIntermBinary *instanceIDInitializer =
new TIntermBinary(EOpAssign, instanceIDSymbol, normalizedInstanceID); new TIntermBinary(EOpAssign, instanceIDSymbol->deepCopy(), normalizedInstanceID);
// Create a uint(gl_InstanceID) node. // Create a uint(gl_InstanceID) node.
TIntermSequence *instanceIDSymbolCastArguments = new TIntermSequence(); TIntermSequence *glInstanceIDSymbolCastArguments = new TIntermSequence();
instanceIDSymbolCastArguments->push_back(CreateGLInstanceIDSymbol()); glInstanceIDSymbolCastArguments->push_back(CreateGLInstanceIDSymbol());
TIntermAggregate *instanceIDAsUint = TIntermAggregate::CreateConstructor( TIntermAggregate *glInstanceIDAsUint = TIntermAggregate::CreateConstructor(
TType(EbtUInt, EbpHigh, EvqTemporary), instanceIDSymbolCastArguments); TType(EbtUInt, EbpHigh, EvqTemporary), glInstanceIDSymbolCastArguments);
// Create a unsigned numberOfViews node. // Create an unsigned numberOfViews node.
TConstantUnion *numberOfViewsUnsignedConstant = new TConstantUnion(); TConstantUnion *numberOfViewsUnsignedConstant = new TConstantUnion();
numberOfViewsUnsignedConstant->setUConst(numberOfViews); numberOfViewsUnsignedConstant->setUConst(numberOfViews);
TIntermConstantUnion *numberOfViewsUintSymbol = TIntermConstantUnion *numberOfViewsUintSymbol =
...@@ -83,79 +80,60 @@ void InitializeBuiltinsInMain(TIntermBlock *root, ...@@ -83,79 +80,60 @@ void InitializeBuiltinsInMain(TIntermBlock *root,
// Create a uint(gl_InstanceID) % numberOfViews node. // Create a uint(gl_InstanceID) % numberOfViews node.
TIntermBinary *normalizedViewID = TIntermBinary *normalizedViewID =
new TIntermBinary(EOpIMod, instanceIDAsUint, numberOfViewsUintSymbol); new TIntermBinary(EOpIMod, glInstanceIDAsUint, numberOfViewsUintSymbol);
// Create a ViewID_OVR = uint(gl_InstanceID) % numberOfViews node. // Create a ViewID_OVR = uint(gl_InstanceID) % numberOfViews node.
TIntermBinary *viewIDInitializer = new TIntermBinary(EOpAssign, viewIDSymbol, normalizedViewID); TIntermBinary *viewIDInitializer =
new TIntermBinary(EOpAssign, viewIDSymbol->deepCopy(), normalizedViewID);
// Add nodes to sequence and insert into main.
TIntermSequence *initializers = new TIntermSequence();
initializers->push_back(viewIDInitializer);
initializers->push_back(instanceIDInitializer);
// Insert init code as a block at the beginning of the main() function.
TIntermBlock *initGlobalsBlock = new TIntermBlock();
initGlobalsBlock->getSequence()->swap(*initializers);
TIntermFunctionDefinition *main = FindMain(root);
ASSERT(main != nullptr);
TIntermBlock *mainBody = main->getBody();
ASSERT(mainBody != nullptr);
mainBody->getSequence()->insert(mainBody->getSequence()->begin(), initGlobalsBlock);
}
// Adds declarations for ViewID_OVR and InstanceID in global scope at the beginning of
// the shader.
void DeclareBuiltinsInGlobalScope(TIntermBlock *root,
TIntermSymbol *instanceIDSymbol,
TIntermSymbol *viewIDSymbol)
{
TIntermSequence *globalSequence = root->getSequence();
TIntermDeclaration *instanceIDDeclaration = new TIntermDeclaration();
instanceIDDeclaration->appendDeclarator(instanceIDSymbol);
TIntermDeclaration *viewIDOVRDeclaration = new TIntermDeclaration(); // Add initializers at the beginning of main().
viewIDOVRDeclaration->appendDeclarator(viewIDSymbol); TIntermBlock *mainBody = FindMainBody(root);
mainBody->getSequence()->insert(mainBody->getSequence()->begin(), instanceIDInitializer);
globalSequence->insert(globalSequence->begin(), viewIDOVRDeclaration); mainBody->getSequence()->insert(mainBody->getSequence()->begin(), viewIDInitializer);
globalSequence->insert(globalSequence->begin(), instanceIDDeclaration);
} }
// Replaces every occurrence of gl_InstanceID with InstanceID, sets the name to internal // Replaces every occurrence of a symbol with the name specified in symbolName with newSymbolNode.
// and changes the qualifier from EvqInstanceID to EvqTemporary. void ReplaceSymbol(TIntermBlock *root, const TString &symbolName, TIntermSymbol *newSymbolNode)
void RenameGLInstanceIDAndMarkAsInternal(TIntermBlock *root)
{ {
RenameVariableAndMarkAsInternalTraverser traverser("gl_InstanceID", "InstanceID"); ReplaceVariableTraverser traverser(symbolName, newSymbolNode);
root->traverse(&traverser); root->traverse(&traverser);
traverser.updateTree();
} }
// Replaces every occurrence of gl_ViewID_OVR with ViewID_OVR, sets the name to internal void DeclareGlobalVariable(TIntermBlock *root, TIntermTyped *typedNode)
// and changes the qualifier from EvqViewIDOVR to EvqTemporary.
void RenameGLViewIDAndMarkAsInternal(TIntermBlock *root)
{ {
RenameVariableAndMarkAsInternalTraverser traverser("gl_ViewID_OVR", "ViewID_OVR"); TIntermSequence *globalSequence = root->getSequence();
root->traverse(&traverser); TIntermDeclaration *declaration = new TIntermDeclaration();
declaration->appendDeclarator(typedNode->deepCopy());
globalSequence->insert(globalSequence->begin(), declaration);
} }
} // namespace } // namespace
void DeclareAndInitBuiltinsForInstancedMultiview(TIntermBlock *root, unsigned numberOfViews) void DeclareAndInitBuiltinsForInstancedMultiview(TIntermBlock *root,
unsigned numberOfViews,
GLenum shaderType)
{ {
TIntermSymbol *instanceIDSymbol = new TIntermSymbol(TSymbolTable::nextUniqueId(), "InstanceID", ASSERT(shaderType == GL_VERTEX_SHADER || shaderType == GL_FRAGMENT_SHADER);
TType(EbtInt, EbpHigh, EvqGlobal));
instanceIDSymbol->setInternal(true);
TQualifier viewIDQualifier = (shaderType == GL_VERTEX_SHADER) ? EvqFlatOut : EvqFlatIn;
TIntermSymbol *viewIDSymbol = new TIntermSymbol(TSymbolTable::nextUniqueId(), "ViewID_OVR", TIntermSymbol *viewIDSymbol = new TIntermSymbol(TSymbolTable::nextUniqueId(), "ViewID_OVR",
TType(EbtUInt, EbpHigh, EvqGlobal)); TType(EbtUInt, EbpHigh, viewIDQualifier));
viewIDSymbol->setInternal(true); viewIDSymbol->setInternal(true);
// Renaming the variables should happen before adding the initializers. DeclareGlobalVariable(root, viewIDSymbol);
RenameGLInstanceIDAndMarkAsInternal(root); ReplaceSymbol(root, "gl_ViewID_OVR", viewIDSymbol);
DeclareBuiltinsInGlobalScope(root, instanceIDSymbol, viewIDSymbol); if (shaderType == GL_VERTEX_SHADER)
InitializeBuiltinsInMain(root, instanceIDSymbol->deepCopy()->getAsSymbolNode(), {
viewIDSymbol->deepCopy()->getAsSymbolNode(), numberOfViews); // Replacing gl_InstanceID with InstanceID should happen before adding the initializers of
RenameGLViewIDAndMarkAsInternal(root); // InstanceID and ViewID.
TIntermSymbol *instanceIDSymbol = new TIntermSymbol(
TSymbolTable::nextUniqueId(), "InstanceID", TType(EbtInt, EbpHigh, EvqGlobal));
instanceIDSymbol->setInternal(true);
DeclareGlobalVariable(root, instanceIDSymbol);
ReplaceSymbol(root, "gl_InstanceID", instanceIDSymbol);
InitializeViewIDAndInstanceID(root, viewIDSymbol, instanceIDSymbol, numberOfViews);
}
} }
} // namespace sh } // namespace sh
\ No newline at end of file
...@@ -3,24 +3,30 @@ ...@@ -3,24 +3,30 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// //
// This pass applies the following modifications to the AST: // Regardless of the shader type, the following AST transformations are applied:
// 1) Replaces every occurrence of gl_InstanceID with InstanceID and marks InstanceID as internal. // - Add declaration of View_ID_OVR.
// 2) Adds declarations of the global variables ViewID_OVR and InstanceID. // - Replace every occurrence of gl_ViewID_OVR with ViewID_OVR, mark ViewID_OVR as internal and
// 3) Initializes ViewID_OVR and InstanceID depending on the number of views. // declare it as a flat varying.
// 4) Replaces every occurrence of gl_ViewID_OVR with ViewID_OVR and marks ViewID_OVR as internal. // If the shader type is a vertex shader, the following AST transformations are applied:
// The pass should be executed before any variables get collected so that usage of gl_InstanceID is // - Replace every occurrence of gl_InstanceID with InstanceID, mark InstanceID as internal and set
// recorded. // its qualifier to EvqTemporary.
// - Add initializers of ViewID_OVR and InstanceID to the beginning of the body of main. The pass
// should be executed before any variables get collected so that usage of gl_InstanceID is recorded.
// //
#ifndef COMPILER_TRANSLATOR_DECLAREANDINITBUILTINSFORINSTANCEDMULTIVIEW_H_ #ifndef COMPILER_TRANSLATOR_DECLAREANDINITBUILTINSFORINSTANCEDMULTIVIEW_H_
#define COMPILER_TRANSLATOR_DECLAREANDINITBUILTINSFORINSTANCEDMULTIVIEW_H_ #define COMPILER_TRANSLATOR_DECLAREANDINITBUILTINSFORINSTANCEDMULTIVIEW_H_
#include "angle_gl.h"
class TIntermBlock; class TIntermBlock;
namespace sh namespace sh
{ {
void DeclareAndInitBuiltinsForInstancedMultiview(TIntermBlock *root, unsigned numberOfViews); void DeclareAndInitBuiltinsForInstancedMultiview(TIntermBlock *root,
unsigned numberOfViews,
GLenum shaderType);
} // namespace sh } // namespace sh
......
...@@ -26,4 +26,13 @@ TIntermFunctionDefinition *FindMain(TIntermBlock *root) ...@@ -26,4 +26,13 @@ TIntermFunctionDefinition *FindMain(TIntermBlock *root)
return nullptr; return nullptr;
} }
TIntermBlock *FindMainBody(TIntermBlock *root)
{
TIntermFunctionDefinition *main = FindMain(root);
ASSERT(main != nullptr);
TIntermBlock *mainBody = main->getBody();
ASSERT(mainBody != nullptr);
return mainBody;
}
} // namespace sh } // namespace sh
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
// found in the LICENSE file. // found in the LICENSE file.
// //
// FindMain.h: Find the main() function definition in a given AST. // FindMain.h: Adds functions to get the main function definition and its body.
#ifndef COMPILER_TRANSLATOR_FINDMAIN_H_ #ifndef COMPILER_TRANSLATOR_FINDMAIN_H_
#define COMPILER_TRANSLATOR_FINDMAIN_H_ #define COMPILER_TRANSLATOR_FINDMAIN_H_
...@@ -16,6 +16,7 @@ class TIntermBlock; ...@@ -16,6 +16,7 @@ class TIntermBlock;
class TIntermFunctionDefinition; class TIntermFunctionDefinition;
TIntermFunctionDefinition *FindMain(TIntermBlock *root); TIntermFunctionDefinition *FindMain(TIntermBlock *root);
TIntermBlock *FindMainBody(TIntermBlock *root);
} // namespace sh } // namespace sh
......
...@@ -14,6 +14,9 @@ ...@@ -14,6 +14,9 @@
using namespace sh; using namespace sh;
namespace
{
class SymbolOccurrenceCounter : public TIntermTraverser class SymbolOccurrenceCounter : public TIntermTraverser
{ {
public: public:
...@@ -66,6 +69,25 @@ class SymbolOccurrenceCounterByName : public SymbolOccurrenceCounter ...@@ -66,6 +69,25 @@ class SymbolOccurrenceCounterByName : public SymbolOccurrenceCounter
TString mSymbolName; TString mSymbolName;
}; };
class SymbolOccurrenceCounterByNameAndQualifier : public SymbolOccurrenceCounter
{
public:
SymbolOccurrenceCounterByNameAndQualifier(const TString &symbolName, TQualifier qualifier)
: mSymbolName(symbolName), mSymbolQualifier(qualifier)
{
}
bool shouldCountSymbol(const TIntermSymbol *node) const override
{
return node->getName().getString() == mSymbolName &&
node->getQualifier() == mSymbolQualifier;
}
private:
TString mSymbolName;
TQualifier mSymbolQualifier;
};
class WEBGLMultiviewVertexShaderTest : public ShaderCompileTreeTest class WEBGLMultiviewVertexShaderTest : public ShaderCompileTreeTest
{ {
public: public:
...@@ -122,6 +144,24 @@ class WEBGLMultiviewVertexShaderOutputCodeTest : public MatchOutputCodeTest ...@@ -122,6 +144,24 @@ class WEBGLMultiviewVertexShaderOutputCodeTest : public MatchOutputCodeTest
} }
}; };
void VariableOccursNTimes(TIntermBlock *root,
const TString &varName,
const TQualifier varQualifier,
unsigned n)
{
// Check that there are n occurrences of the variable with the given name and qualifier.
SymbolOccurrenceCounterByNameAndQualifier viewIDByNameAndQualifier(varName, varQualifier);
root->traverse(&viewIDByNameAndQualifier);
EXPECT_EQ(n, viewIDByNameAndQualifier.getNumberOfOccurrences());
// Check that there are n occurrences of the variable with the given name. By this we guarantee
// that there are no other occurrences of the variable with the same name but different
// qualifier.
SymbolOccurrenceCounterByName viewIDByName(varName);
root->traverse(&viewIDByName);
EXPECT_EQ(n, viewIDByName.getNumberOfOccurrences());
}
// Invalid combination of extensions (restricted in the WEBGL_multiview spec). // Invalid combination of extensions (restricted in the WEBGL_multiview spec).
TEST_F(WEBGLMultiviewVertexShaderTest, InvalidBothMultiviewAndMultiview2) TEST_F(WEBGLMultiviewVertexShaderTest, InvalidBothMultiviewAndMultiview2)
{ {
...@@ -663,9 +703,9 @@ TEST_F(WEBGLMultiviewVertexShaderTest, GLViewIDIsRenamed) ...@@ -663,9 +703,9 @@ TEST_F(WEBGLMultiviewVertexShaderTest, GLViewIDIsRenamed)
mASTRoot->traverse(&glViewIDOVRByQualifier); mASTRoot->traverse(&glViewIDOVRByQualifier);
EXPECT_EQ(0u, glViewIDOVRByQualifier.getNumberOfOccurrences()); EXPECT_EQ(0u, glViewIDOVRByQualifier.getNumberOfOccurrences());
SymbolOccurrenceCounterByName viewIDByName("ViewID_OVR"); SymbolOccurrenceCounterByNameAndQualifier viewIDByNameAndQualifier("ViewID_OVR", EvqFlatOut);
mASTRoot->traverse(&viewIDByName); mASTRoot->traverse(&viewIDByNameAndQualifier);
EXPECT_EQ(6u, viewIDByName.getNumberOfOccurrences()); EXPECT_EQ(6u, viewIDByNameAndQualifier.getNumberOfOccurrences());
} }
// The test checks that ViewID_OVR and InstanceID have the correct initializers based on the // The test checks that ViewID_OVR and InstanceID have the correct initializers based on the
...@@ -741,4 +781,33 @@ TEST_F(WEBGLMultiviewVertexShaderTest, InstaceIDCollectedESSL1) ...@@ -741,4 +781,33 @@ TEST_F(WEBGLMultiviewVertexShaderTest, InstaceIDCollectedESSL1)
isGLInstanceIDFound = (attributes[i].name == "gl_InstanceID"); isGLInstanceIDFound = (attributes[i].name == "gl_InstanceID");
} }
EXPECT_TRUE(isGLInstanceIDFound); EXPECT_TRUE(isGLInstanceIDFound);
} }
\ No newline at end of file
// Test that ViewID_OVR is declared as a flat input variable in an ESSL 3.00 fragment shader.
TEST_F(WEBGLMultiviewFragmentShaderTest, ViewIDDeclaredAsFlatInput)
{
const std::string &shaderString =
"#version 300 es\n"
"#extension GL_OVR_multiview2 : require\n"
"void main()\n"
"{\n"
"}\n";
mExtraCompileOptions |= SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW;
compileAssumeSuccess(shaderString);
VariableOccursNTimes(mASTRoot, "ViewID_OVR", EvqFlatIn, 1u);
}
// Test that ViewID_OVR is declared as a flat output variable in an ESSL 1.00 vertex shader.
TEST_F(WEBGLMultiviewVertexShaderTest, ViewIDDeclaredAsFlatOutput)
{
const std::string &shaderString =
"#extension GL_OVR_multiview2 : require\n"
"void main()\n"
"{\n"
"}\n";
mExtraCompileOptions |= SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW;
compileAssumeSuccess(shaderString);
VariableOccursNTimes(mASTRoot, "ViewID_OVR", EvqFlatOut, 2u);
}
} // namespace
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment