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;
const ShCompileOptions SH_INITIALIZE_UNINITIALIZED_LOCALS = UINT64_C(1) << 32;
// The flag modifies the shader in the following way:
// Every occurrence of gl_InstanceID and gl_ViewID_OVR is replaced correspondingly by the global
// temporary variables InstanceID and ViewID_OVR. The following initializers are added at the
// beginning of main(): ViewID_OVR = uint(gl_InstanceID) % num_views; InstanceID = gl_InstanceID /
// num_views;
// Every occurrence of gl_InstanceID is replaced by the global temporary variable InstanceID.
// Every occurrence of gl_ViewID_OVR is replaced by the varying variable ViewID_OVR.
// At the beginning of the body of main() in a vertex shader the following initializers are added:
// 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;
// Defines alternate strategies for implementing array index clamping.
......
......@@ -429,9 +429,9 @@ TIntermBlock *TCompiler::compileTreeImpl(const char *const shaderStrings[],
arrayBoundsClamper.MarkIndirectArrayBoundsForClamping(root);
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
......
......@@ -20,28 +20,26 @@ namespace sh
namespace
{
class RenameVariableAndMarkAsInternalTraverser : public TIntermTraverser
class ReplaceVariableTraverser : public TIntermTraverser
{
public:
RenameVariableAndMarkAsInternalTraverser(const TString &oldName, const TString &newName)
: TIntermTraverser(true, false, false), mOldName(oldName), mNewName(newName)
ReplaceVariableTraverser(const TString &symbolName, TIntermSymbol *newSymbol)
: TIntermTraverser(true, false, false), mSymbolName(symbolName), mNewSymbol(newSymbol)
{
}
void visitSymbol(TIntermSymbol *node) override
{
TName &name = node->getName();
if (name.getString() == mOldName)
if (name.getString() == mSymbolName)
{
node->getTypePointer()->setQualifier(EvqTemporary);
name.setInternal(true);
name.setString(mNewName);
queueReplacement(node, mNewSymbol->deepCopy(), OriginalNode::IS_DROPPED);
}
}
private:
TString mOldName;
TString mNewName;
TString mSymbolName;
TIntermSymbol *mNewSymbol;
};
TIntermSymbol *CreateGLInstanceIDSymbol()
......@@ -49,15 +47,14 @@ TIntermSymbol *CreateGLInstanceIDSymbol()
return new TIntermSymbol(0, "gl_InstanceID", TType(EbtInt, EbpHigh, EvqInstanceID));
}
// Adds initializers for InstanceID and ViewID_OVR at the beginning of main().
void InitializeBuiltinsInMain(TIntermBlock *root,
TIntermSymbol *instanceIDSymbol,
TIntermSymbol *viewIDSymbol,
unsigned numberOfViews)
void InitializeViewIDAndInstanceID(TIntermBlock *root,
TIntermTyped *viewIDSymbol,
TIntermTyped *instanceIDSymbol,
unsigned numberOfViews)
{
// Create a signed numberOfViews node.
TConstantUnion *numberOfViewsConstant = new TConstantUnion();
numberOfViewsConstant->setIConst(numberOfViews);
numberOfViewsConstant->setIConst(static_cast<int>(numberOfViews));
TIntermConstantUnion *numberOfViewsIntSymbol =
new TIntermConstantUnion(numberOfViewsConstant, TType(EbtInt, EbpHigh, EvqConst));
......@@ -67,15 +64,15 @@ void InitializeBuiltinsInMain(TIntermBlock *root,
// Create a InstanceID = gl_InstanceID / numberOfViews node.
TIntermBinary *instanceIDInitializer =
new TIntermBinary(EOpAssign, instanceIDSymbol, normalizedInstanceID);
new TIntermBinary(EOpAssign, instanceIDSymbol->deepCopy(), normalizedInstanceID);
// Create a uint(gl_InstanceID) node.
TIntermSequence *instanceIDSymbolCastArguments = new TIntermSequence();
instanceIDSymbolCastArguments->push_back(CreateGLInstanceIDSymbol());
TIntermAggregate *instanceIDAsUint = TIntermAggregate::CreateConstructor(
TType(EbtUInt, EbpHigh, EvqTemporary), instanceIDSymbolCastArguments);
TIntermSequence *glInstanceIDSymbolCastArguments = new TIntermSequence();
glInstanceIDSymbolCastArguments->push_back(CreateGLInstanceIDSymbol());
TIntermAggregate *glInstanceIDAsUint = TIntermAggregate::CreateConstructor(
TType(EbtUInt, EbpHigh, EvqTemporary), glInstanceIDSymbolCastArguments);
// Create a unsigned numberOfViews node.
// Create an unsigned numberOfViews node.
TConstantUnion *numberOfViewsUnsignedConstant = new TConstantUnion();
numberOfViewsUnsignedConstant->setUConst(numberOfViews);
TIntermConstantUnion *numberOfViewsUintSymbol =
......@@ -83,79 +80,60 @@ void InitializeBuiltinsInMain(TIntermBlock *root,
// Create a uint(gl_InstanceID) % numberOfViews node.
TIntermBinary *normalizedViewID =
new TIntermBinary(EOpIMod, instanceIDAsUint, numberOfViewsUintSymbol);
new TIntermBinary(EOpIMod, glInstanceIDAsUint, numberOfViewsUintSymbol);
// Create a ViewID_OVR = uint(gl_InstanceID) % numberOfViews node.
TIntermBinary *viewIDInitializer = new TIntermBinary(EOpAssign, viewIDSymbol, 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);
TIntermBinary *viewIDInitializer =
new TIntermBinary(EOpAssign, viewIDSymbol->deepCopy(), normalizedViewID);
TIntermDeclaration *viewIDOVRDeclaration = new TIntermDeclaration();
viewIDOVRDeclaration->appendDeclarator(viewIDSymbol);
globalSequence->insert(globalSequence->begin(), viewIDOVRDeclaration);
globalSequence->insert(globalSequence->begin(), instanceIDDeclaration);
// Add initializers at the beginning of main().
TIntermBlock *mainBody = FindMainBody(root);
mainBody->getSequence()->insert(mainBody->getSequence()->begin(), instanceIDInitializer);
mainBody->getSequence()->insert(mainBody->getSequence()->begin(), viewIDInitializer);
}
// Replaces every occurrence of gl_InstanceID with InstanceID, sets the name to internal
// and changes the qualifier from EvqInstanceID to EvqTemporary.
void RenameGLInstanceIDAndMarkAsInternal(TIntermBlock *root)
// Replaces every occurrence of a symbol with the name specified in symbolName with newSymbolNode.
void ReplaceSymbol(TIntermBlock *root, const TString &symbolName, TIntermSymbol *newSymbolNode)
{
RenameVariableAndMarkAsInternalTraverser traverser("gl_InstanceID", "InstanceID");
ReplaceVariableTraverser traverser(symbolName, newSymbolNode);
root->traverse(&traverser);
traverser.updateTree();
}
// Replaces every occurrence of gl_ViewID_OVR with ViewID_OVR, sets the name to internal
// and changes the qualifier from EvqViewIDOVR to EvqTemporary.
void RenameGLViewIDAndMarkAsInternal(TIntermBlock *root)
void DeclareGlobalVariable(TIntermBlock *root, TIntermTyped *typedNode)
{
RenameVariableAndMarkAsInternalTraverser traverser("gl_ViewID_OVR", "ViewID_OVR");
root->traverse(&traverser);
TIntermSequence *globalSequence = root->getSequence();
TIntermDeclaration *declaration = new TIntermDeclaration();
declaration->appendDeclarator(typedNode->deepCopy());
globalSequence->insert(globalSequence->begin(), declaration);
}
} // namespace
void DeclareAndInitBuiltinsForInstancedMultiview(TIntermBlock *root, unsigned numberOfViews)
void DeclareAndInitBuiltinsForInstancedMultiview(TIntermBlock *root,
unsigned numberOfViews,
GLenum shaderType)
{
TIntermSymbol *instanceIDSymbol = new TIntermSymbol(TSymbolTable::nextUniqueId(), "InstanceID",
TType(EbtInt, EbpHigh, EvqGlobal));
instanceIDSymbol->setInternal(true);
ASSERT(shaderType == GL_VERTEX_SHADER || shaderType == GL_FRAGMENT_SHADER);
TQualifier viewIDQualifier = (shaderType == GL_VERTEX_SHADER) ? EvqFlatOut : EvqFlatIn;
TIntermSymbol *viewIDSymbol = new TIntermSymbol(TSymbolTable::nextUniqueId(), "ViewID_OVR",
TType(EbtUInt, EbpHigh, EvqGlobal));
TType(EbtUInt, EbpHigh, viewIDQualifier));
viewIDSymbol->setInternal(true);
// Renaming the variables should happen before adding the initializers.
RenameGLInstanceIDAndMarkAsInternal(root);
DeclareBuiltinsInGlobalScope(root, instanceIDSymbol, viewIDSymbol);
InitializeBuiltinsInMain(root, instanceIDSymbol->deepCopy()->getAsSymbolNode(),
viewIDSymbol->deepCopy()->getAsSymbolNode(), numberOfViews);
RenameGLViewIDAndMarkAsInternal(root);
DeclareGlobalVariable(root, viewIDSymbol);
ReplaceSymbol(root, "gl_ViewID_OVR", viewIDSymbol);
if (shaderType == GL_VERTEX_SHADER)
{
// Replacing gl_InstanceID with InstanceID should happen before adding the initializers of
// 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
\ No newline at end of file
......@@ -3,24 +3,30 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// This pass applies the following modifications to the AST:
// 1) Replaces every occurrence of gl_InstanceID with InstanceID and marks InstanceID as internal.
// 2) Adds declarations of the global variables ViewID_OVR and InstanceID.
// 3) Initializes ViewID_OVR and InstanceID depending on the number of views.
// 4) Replaces every occurrence of gl_ViewID_OVR with ViewID_OVR and marks ViewID_OVR as internal.
// The pass should be executed before any variables get collected so that usage of gl_InstanceID is
// recorded.
// Regardless of the shader type, the following AST transformations are applied:
// - Add declaration of View_ID_OVR.
// - Replace every occurrence of gl_ViewID_OVR with ViewID_OVR, mark ViewID_OVR as internal and
// declare it as a flat varying.
// If the shader type is a vertex shader, the following AST transformations are applied:
// - Replace every occurrence of gl_InstanceID with InstanceID, mark InstanceID as internal and set
// 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_
#define COMPILER_TRANSLATOR_DECLAREANDINITBUILTINSFORINSTANCEDMULTIVIEW_H_
#include "angle_gl.h"
class TIntermBlock;
namespace sh
{
void DeclareAndInitBuiltinsForInstancedMultiview(TIntermBlock *root, unsigned numberOfViews);
void DeclareAndInitBuiltinsForInstancedMultiview(TIntermBlock *root,
unsigned numberOfViews,
GLenum shaderType);
} // namespace sh
......
......@@ -26,4 +26,13 @@ TIntermFunctionDefinition *FindMain(TIntermBlock *root)
return nullptr;
}
TIntermBlock *FindMainBody(TIntermBlock *root)
{
TIntermFunctionDefinition *main = FindMain(root);
ASSERT(main != nullptr);
TIntermBlock *mainBody = main->getBody();
ASSERT(mainBody != nullptr);
return mainBody;
}
} // namespace sh
......@@ -4,7 +4,7 @@
// 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_
#define COMPILER_TRANSLATOR_FINDMAIN_H_
......@@ -16,6 +16,7 @@ class TIntermBlock;
class TIntermFunctionDefinition;
TIntermFunctionDefinition *FindMain(TIntermBlock *root);
TIntermBlock *FindMainBody(TIntermBlock *root);
} // namespace sh
......
......@@ -14,6 +14,9 @@
using namespace sh;
namespace
{
class SymbolOccurrenceCounter : public TIntermTraverser
{
public:
......@@ -66,6 +69,25 @@ class SymbolOccurrenceCounterByName : public SymbolOccurrenceCounter
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
{
public:
......@@ -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).
TEST_F(WEBGLMultiviewVertexShaderTest, InvalidBothMultiviewAndMultiview2)
{
......@@ -663,9 +703,9 @@ TEST_F(WEBGLMultiviewVertexShaderTest, GLViewIDIsRenamed)
mASTRoot->traverse(&glViewIDOVRByQualifier);
EXPECT_EQ(0u, glViewIDOVRByQualifier.getNumberOfOccurrences());
SymbolOccurrenceCounterByName viewIDByName("ViewID_OVR");
mASTRoot->traverse(&viewIDByName);
EXPECT_EQ(6u, viewIDByName.getNumberOfOccurrences());
SymbolOccurrenceCounterByNameAndQualifier viewIDByNameAndQualifier("ViewID_OVR", EvqFlatOut);
mASTRoot->traverse(&viewIDByNameAndQualifier);
EXPECT_EQ(6u, viewIDByNameAndQualifier.getNumberOfOccurrences());
}
// The test checks that ViewID_OVR and InstanceID have the correct initializers based on the
......@@ -741,4 +781,33 @@ TEST_F(WEBGLMultiviewVertexShaderTest, InstaceIDCollectedESSL1)
isGLInstanceIDFound = (attributes[i].name == "gl_InstanceID");
}
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