Commit 69056a1e by Martin Radev Committed by Commit Bot

Declare and initialize ViewID_OVR and InstanceID

The patch modifies the vertex shader's AST to declare and initialize the global variables ViewID_OVR and InstanceID. Every occurrence of gl_ViewID_OVR gets replaced by ViewID_OVR and initialized in main with a value dependent on gl_InstanceID and the number of views. To guarantee correct results for instanced rendering, each occurrence of gl_InstanceID is replaced with InstanceID and initialized similarly. BUG=angleproject:2062 TEST=angle_unittests Change-Id: I48be688605b5af869bc370758e70ccc209ea4419 Reviewed-on: https://chromium-review.googlesource.com/548596 Commit-Queue: Olli Etuaho <oetuaho@nvidia.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent f0be43fe
......@@ -222,6 +222,13 @@ const ShCompileOptions SH_TRANSLATE_VIEWID_OVR_TO_UNIFORM = UINT64_C(1) << 31;
// output variables are initialized regardless of if this flag is set.
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;
const ShCompileOptions SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW = UINT64_C(1) << 33;
// Defines alternate strategies for implementing array index clamping.
enum ShArrayIndexClampingStrategy
{
......
......@@ -217,7 +217,7 @@ int main(int argc, char *argv[])
case 'a': resources.ARM_shader_framebuffer_fetch = 1; break;
case 'm':
resources.OVR_multiview = 1;
compileOptions |= SH_TRANSLATE_VIEWID_OVR_TO_UNIFORM;
compileOptions |= SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW;
break;
case 'y': resources.EXT_YUV_target = 1; break;
default: failCode = EFailUsage;
......
......@@ -39,6 +39,8 @@
'compiler/translator/Compiler.h',
'compiler/translator/ConstantUnion.cpp',
'compiler/translator/ConstantUnion.h',
'compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.h',
'compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.cpp',
'compiler/translator/DeferGlobalInitializers.cpp',
'compiler/translator/DeferGlobalInitializers.h',
'compiler/translator/Diagnostics.cpp',
......
......@@ -13,6 +13,7 @@
#include "compiler/translator/AddAndTrueToLoopCondition.h"
#include "compiler/translator/Cache.h"
#include "compiler/translator/CallDAG.h"
#include "compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.h"
#include "compiler/translator/DeferGlobalInitializers.h"
#include "compiler/translator/EmulateGLFragColorBroadcast.h"
#include "compiler/translator/EmulatePrecision.h"
......@@ -406,6 +407,12 @@ TIntermBlock *TCompiler::compileTreeImpl(const char *const shaderStrings[],
if (success && (compileOptions & SH_CLAMP_INDIRECT_ARRAY_BOUNDS))
arrayBoundsClamper.MarkIndirectArrayBoundsForClamping(root);
if (success && (compileOptions & SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW) &&
parseContext.isMultiviewExtensionEnabled() && getShaderType() == GL_VERTEX_SHADER)
{
DeclareAndInitBuiltinsForInstancedMultiview(root, getNumViews());
}
// gl_Position is always written in compatibility output mode
if (success && shaderType == GL_VERTEX_SHADER &&
((compileOptions & SH_INIT_GL_POSITION) ||
......
//
// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Applies the necessary AST transformations to support multiview rendering through instancing.
// Check the header file For more information.
//
#include "compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.h"
#include "compiler/translator/FindMain.h"
#include "compiler/translator/InitializeVariables.h"
#include "compiler/translator/IntermNode.h"
#include "compiler/translator/SymbolTable.h"
namespace sh
{
namespace
{
class RenameVariableAndMarkAsInternalTraverser : public TIntermTraverser
{
public:
RenameVariableAndMarkAsInternalTraverser(const TString &oldName, const TString &newName)
: TIntermTraverser(true, false, false), mOldName(oldName), mNewName(newName)
{
}
void visitSymbol(TIntermSymbol *node) override
{
TName &name = node->getName();
if (name.getString() == mOldName)
{
node->getTypePointer()->setQualifier(EvqTemporary);
name.setInternal(true);
name.setString(mNewName);
}
}
private:
TString mOldName;
TString mNewName;
};
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)
{
// Create a signed numberOfViews node.
TConstantUnion *numberOfViewsConstant = new TConstantUnion();
numberOfViewsConstant->setIConst(numberOfViews);
TIntermConstantUnion *numberOfViewsIntSymbol =
new TIntermConstantUnion(numberOfViewsConstant, TType(EbtInt, EbpHigh, EvqConst));
// Create a gl_InstanceID / numberOfViews node.
TIntermBinary *normalizedInstanceID =
new TIntermBinary(EOpDiv, CreateGLInstanceIDSymbol(), numberOfViewsIntSymbol);
// Create a InstanceID = gl_InstanceID / numberOfViews node.
TIntermBinary *instanceIDInitializer =
new TIntermBinary(EOpAssign, instanceIDSymbol, normalizedInstanceID);
// Create a uint(gl_InstanceID) node.
TIntermSequence *instanceIDSymbolCastArguments = new TIntermSequence();
instanceIDSymbolCastArguments->push_back(CreateGLInstanceIDSymbol());
TIntermAggregate *instanceIDAsUint = TIntermAggregate::CreateConstructor(
TType(EbtUInt, EbpHigh, EvqTemporary), instanceIDSymbolCastArguments);
// Create a unsigned numberOfViews node.
TConstantUnion *numberOfViewsUnsignedConstant = new TConstantUnion();
numberOfViewsUnsignedConstant->setUConst(numberOfViews);
TIntermConstantUnion *numberOfViewsUintSymbol =
new TIntermConstantUnion(numberOfViewsUnsignedConstant, TType(EbtUInt, EbpHigh, EvqConst));
// Create a uint(gl_InstanceID) % numberOfViews node.
TIntermBinary *normalizedViewID =
new TIntermBinary(EOpIMod, instanceIDAsUint, 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);
TIntermDeclaration *viewIDOVRDeclaration = new TIntermDeclaration();
viewIDOVRDeclaration->appendDeclarator(viewIDSymbol);
globalSequence->insert(globalSequence->begin(), viewIDOVRDeclaration);
globalSequence->insert(globalSequence->begin(), instanceIDDeclaration);
}
// 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)
{
RenameVariableAndMarkAsInternalTraverser traverser("gl_InstanceID", "InstanceID");
root->traverse(&traverser);
}
// 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)
{
RenameVariableAndMarkAsInternalTraverser traverser("gl_ViewID_OVR", "ViewID_OVR");
root->traverse(&traverser);
}
} // namespace
void DeclareAndInitBuiltinsForInstancedMultiview(TIntermBlock *root, unsigned numberOfViews)
{
TIntermSymbol *instanceIDSymbol = new TIntermSymbol(TSymbolTable::nextUniqueId(), "InstanceID",
TType(EbtInt, EbpHigh, EvqGlobal));
instanceIDSymbol->setInternal(true);
TIntermSymbol *viewIDSymbol = new TIntermSymbol(TSymbolTable::nextUniqueId(), "ViewID_OVR",
TType(EbtUInt, EbpHigh, EvqGlobal));
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);
}
} // namespace sh
\ No newline at end of file
//
// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
// 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.
//
#ifndef COMPILER_TRANSLATOR_DECLAREANDINITBUILTINSFORINSTANCEDMULTIVIEW_H_
#define COMPILER_TRANSLATOR_DECLAREANDINITBUILTINSFORINSTANCEDMULTIVIEW_H_
class TIntermBlock;
namespace sh
{
void DeclareAndInitBuiltinsForInstancedMultiview(TIntermBlock *root, unsigned numberOfViews);
} // namespace sh
#endif // COMPILER_TRANSLATOR_DECLAREANDINITBUILTINSFORINSTANCEDMULTIVIEW_H_
\ No newline at end of file
......@@ -85,6 +85,11 @@ void GetDeferredInitializers(TIntermDeclaration *declaration,
{
TIntermSymbol *symbolNode = declarator->getAsSymbolNode();
ASSERT(symbolNode);
// Ignore ANGLE internal variables.
if (symbolNode->getName().isInternal())
continue;
if (symbolNode->getQualifier() == EvqGlobal && symbolNode->getSymbol() != "")
{
TIntermSequence *initCode = CreateInitCode(symbolNode);
......
......@@ -272,6 +272,7 @@ class TIntermSymbol : public TIntermTyped
int getId() const { return mId; }
const TString &getSymbol() const { return mSymbol.getString(); }
const TName &getName() const { return mSymbol; }
TName &getName() { return mSymbol; }
void setId(int newId) { mId = newId; }
......
......@@ -7,13 +7,65 @@
// Test that shaders with gl_ViewID_OVR are validated correctly.
//
#include "angle_gl.h"
#include "gtest/gtest.h"
#include "GLSLANG/ShaderLang.h"
#include "compiler/translator/IntermNode.h"
#include "tests/test_utils/ShaderCompileTreeTest.h"
#include "tests/test_utils/compiler_test.h"
using namespace sh;
class SymbolOccurrenceCounter : public TIntermTraverser
{
public:
SymbolOccurrenceCounter() : TIntermTraverser(true, false, false), mNumberOfOccurrences(0u) {}
void visitSymbol(TIntermSymbol *node) override
{
if (shouldCountSymbol(node))
{
++mNumberOfOccurrences;
}
}
virtual bool shouldCountSymbol(const TIntermSymbol *node) const = 0;
unsigned getNumberOfOccurrences() const { return mNumberOfOccurrences; }
private:
unsigned mNumberOfOccurrences;
};
class SymbolOccurrenceCounterByQualifier : public SymbolOccurrenceCounter
{
public:
SymbolOccurrenceCounterByQualifier(TQualifier symbolQualifier)
: mSymbolQualifier(symbolQualifier)
{
}
bool shouldCountSymbol(const TIntermSymbol *node) const override
{
return node->getQualifier() == mSymbolQualifier;
}
private:
TQualifier mSymbolQualifier;
};
class SymbolOccurrenceCounterByName : public SymbolOccurrenceCounter
{
public:
SymbolOccurrenceCounterByName(const TString &symbolName) : mSymbolName(symbolName) {}
bool shouldCountSymbol(const TIntermSymbol *node) const override
{
return node->getName().getString() == mSymbolName;
}
private:
TString mSymbolName;
};
class WEBGLMultiviewVertexShaderTest : public ShaderCompileTreeTest
{
public:
......@@ -42,6 +94,34 @@ class WEBGLMultiviewFragmentShaderTest : public ShaderCompileTreeTest
}
};
class WEBGLMultiviewVertexShaderOutputCodeTest : public MatchOutputCodeTest
{
public:
WEBGLMultiviewVertexShaderOutputCodeTest()
: MatchOutputCodeTest(GL_VERTEX_SHADER, 0, SH_ESSL_OUTPUT)
{
addOutputType(SH_GLSL_COMPATIBILITY_OUTPUT);
#if defined(ANGLE_ENABLE_HLSL)
addOutputType(SH_HLSL_4_1_OUTPUT);
#endif
getResources()->OVR_multiview = 1;
getResources()->MaxViewsOVR = 4;
}
bool foundInAllGLSLCode(const char *str)
{
return foundInCode(SH_GLSL_COMPATIBILITY_OUTPUT, str) && foundInCode(SH_ESSL_OUTPUT, str);
}
bool foundInHLSLCode(const char *stringToFind) const
{
#if defined(ANGLE_ENABLE_HLSL)
return foundInCode(SH_HLSL_4_1_OUTPUT, stringToFind);
#else
return true;
#endif
}
};
// Invalid combination of extensions (restricted in the WEBGL_multiview spec).
TEST_F(WEBGLMultiviewVertexShaderTest, InvalidBothMultiviewAndMultiview2)
{
......@@ -518,3 +598,96 @@ TEST_F(WEBGLMultiviewFragmentShaderTest, ESSL1ShaderUnsupportedInStorageQualifie
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Test that gl_InstanceID gets correctly replaced by InstanceID. gl_InstanceID should only be used
// twice: once to initialize ViewID_OVR and once for InstanceID. The number of occurrences of
// InstanceID in the AST should be the sum of two and the number of occurrences of gl_InstanceID
// before any renaming.
TEST_F(WEBGLMultiviewVertexShaderTest, GLInstanceIDIsRenamed)
{
const std::string &shaderString =
"#version 300 es\n"
"#extension GL_OVR_multiview : require\n"
"layout(num_views = 2) in;\n"
"flat out int myInstance;\n"
"out float myInstanceF;\n"
"out float myInstanceF2;\n"
"void main()\n"
"{\n"
" gl_Position.x = gl_ViewID_OVR == 0u ? 0. : 1.;\n"
" gl_Position.yzw = vec3(0., 0., 1.);\n"
" myInstance = gl_InstanceID;\n"
" myInstanceF = float(gl_InstanceID) + .5;\n"
" myInstanceF2 = float(gl_InstanceID) + .1;\n"
"}\n";
mExtraCompileOptions |= SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW;
compileAssumeSuccess(shaderString);
SymbolOccurrenceCounterByName glInstanceIDByName("gl_InstanceID");
mASTRoot->traverse(&glInstanceIDByName);
EXPECT_EQ(2u, glInstanceIDByName.getNumberOfOccurrences());
SymbolOccurrenceCounterByQualifier glInstanceIDByQualifier(EvqInstanceID);
mASTRoot->traverse(&glInstanceIDByQualifier);
EXPECT_EQ(2u, glInstanceIDByQualifier.getNumberOfOccurrences());
SymbolOccurrenceCounterByName instanceIDByName("InstanceID");
mASTRoot->traverse(&instanceIDByName);
EXPECT_EQ(5u, instanceIDByName.getNumberOfOccurrences());
}
// Test that gl_ViewID_OVR gets correctly replaced by ViewID_OVR. gl_ViewID_OVR should not be found
// by either name or qualifier. The number of occurrences of ViewID_OVR in the AST should be the sum
// of two and the number of occurrences of gl_ViewID_OVR before any renaming.
TEST_F(WEBGLMultiviewVertexShaderTest, GLViewIDIsRenamed)
{
const std::string &shaderString =
"#version 300 es\n"
"#extension GL_OVR_multiview2 : require\n"
"layout(num_views = 2) in;\n"
"flat out uint a;\n"
"void main()\n"
"{\n"
" gl_Position.x = gl_ViewID_OVR == 0u ? 0. : 1.;\n"
" gl_Position.yzw = vec3(0., 0., 1.);\n"
" a = gl_ViewID_OVR == 0u ? (gl_ViewID_OVR+2u) : gl_ViewID_OVR;\n"
"}\n";
mExtraCompileOptions |= SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW;
compileAssumeSuccess(shaderString);
SymbolOccurrenceCounterByName glViewIDOVRByName("gl_ViewID_OVR");
mASTRoot->traverse(&glViewIDOVRByName);
EXPECT_EQ(0u, glViewIDOVRByName.getNumberOfOccurrences());
SymbolOccurrenceCounterByQualifier glViewIDOVRByQualifier(EvqViewIDOVR);
mASTRoot->traverse(&glViewIDOVRByQualifier);
EXPECT_EQ(0u, glViewIDOVRByQualifier.getNumberOfOccurrences());
SymbolOccurrenceCounterByName viewIDByName("ViewID_OVR");
mASTRoot->traverse(&viewIDByName);
EXPECT_EQ(6u, viewIDByName.getNumberOfOccurrences());
}
// The test checks that ViewID_OVR and InstanceID have the correct initializers based on the
// number of views.
TEST_F(WEBGLMultiviewVertexShaderOutputCodeTest, ViewIDAndInstanceIDHaveCorrectValues)
{
const std::string &shaderString =
"#version 300 es\n"
"#extension GL_OVR_multiview : require\n"
"layout(num_views = 3) in;\n"
"flat out int myInstance;\n"
"void main()\n"
"{\n"
" gl_Position.x = gl_ViewID_OVR == 0u ? 0. : 1.;\n"
" gl_Position.yzw = vec3(0., 0., 1.);\n"
" myInstance = gl_InstanceID;\n"
"}\n";
compile(shaderString, SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW);
EXPECT_TRUE(foundInAllGLSLCode("webgl_angle_ViewID_OVR = (uint(gl_InstanceID) % 3u)"));
EXPECT_TRUE(foundInAllGLSLCode("webgl_angle_InstanceID = (gl_InstanceID / 3)"));
EXPECT_TRUE(foundInHLSLCode("ViewID_OVR = (uvec1(gl_InstanceID) % 3)"));
EXPECT_TRUE(foundInHLSLCode("InstanceID = (gl_InstanceID / 3)"));
}
\ No newline at end of file
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