Commit 4da0d315 by Jamie Madill Committed by Commit Bot

Vulkan: Handle embedded struct uniforms.

Also known as nameless structs. Uniform structs without a struct name would not be parsed correctly. This fixes the bug by adding a tree transformation. The transformation gives an internally scoped name to the embedded struct. Bug: angleproject:2665 Change-Id: I43e4dad7d9ad64a40e382066bb136e4f8f719797 Reviewed-on: https://chromium-review.googlesource.com/1101566Reviewed-by: 's avatarOlli Etuaho <oetuaho@nvidia.com> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent 3237f759
...@@ -10357,7 +10357,7 @@ void TSymbolTable::initializeBuiltInVariables(sh::GLenum shaderType, ...@@ -10357,7 +10357,7 @@ void TSymbolTable::initializeBuiltInVariables(sh::GLenum shaderType,
new TStructure(BuiltInId::gl_DepthRangeParameters, BuiltInName::gl_DepthRangeParameters, new TStructure(BuiltInId::gl_DepthRangeParameters, BuiltInName::gl_DepthRangeParameters,
TExtension::UNDEFINED, fields_gl_DepthRangeParameters); TExtension::UNDEFINED, fields_gl_DepthRangeParameters);
mVar_gl_DepthRangeParameters = gl_DepthRangeParameters; mVar_gl_DepthRangeParameters = gl_DepthRangeParameters;
TType *type_gl_DepthRange = new TType(gl_DepthRangeParameters); TType *type_gl_DepthRange = new TType(gl_DepthRangeParameters, false);
type_gl_DepthRange->setQualifier(EvqUniform); type_gl_DepthRange->setQualifier(EvqUniform);
type_gl_DepthRange->realize(); type_gl_DepthRange->realize();
mVar_gl_DepthRange = mVar_gl_DepthRange =
......
...@@ -27,12 +27,89 @@ namespace sh ...@@ -27,12 +27,89 @@ namespace sh
namespace namespace
{ {
// This traverser translates embedded uniform structs into a specifier and declaration.
// This makes the declarations easier to move into uniform blocks.
class NameEmbeddedUniformStructsTraverser : public TIntermTraverser
{
public:
explicit NameEmbeddedUniformStructsTraverser(TSymbolTable *symbolTable)
: TIntermTraverser(true, false, false, symbolTable)
{
}
bool visitDeclaration(Visit visit, TIntermDeclaration *decl) override
{
ASSERT(visit == PreVisit);
if (!mInGlobalScope)
{
return false;
}
const TIntermSequence &sequence = *(decl->getSequence());
ASSERT(sequence.size() == 1);
TIntermTyped *declarator = sequence.front()->getAsTyped();
const TType &type = declarator->getType();
if (type.isStructSpecifier() && type.getQualifier() == EvqUniform)
{
const TStructure *structure = type.getStruct();
if (structure->symbolType() == SymbolType::Empty)
{
doReplacement(decl, declarator, structure);
}
}
return false;
}
private:
void doReplacement(TIntermDeclaration *decl,
TIntermTyped *declarator,
const TStructure *oldStructure)
{
// struct <structName> { ... };
TStructure *structure = new TStructure(mSymbolTable, ImmutableString(""),
&oldStructure->fields(), SymbolType::AngleInternal);
TType *namedType = new TType(structure, true);
namedType->setQualifier(EvqGlobal);
TVariable *structVariable =
new TVariable(mSymbolTable, ImmutableString(""), namedType, SymbolType::Empty);
TIntermSymbol *structDeclarator = new TIntermSymbol(structVariable);
TIntermDeclaration *structDeclaration = new TIntermDeclaration;
structDeclaration->appendDeclarator(structDeclarator);
TIntermSequence *newSequence = new TIntermSequence;
newSequence->push_back(structDeclaration);
// uniform <structName> <structUniformName>;
TIntermSymbol *asSymbol = declarator->getAsSymbolNode();
if (asSymbol && asSymbol->variable().symbolType() != SymbolType::Empty)
{
TIntermDeclaration *namedDecl = new TIntermDeclaration;
TType *uniformType = new TType(structure, false);
uniformType->setQualifier(EvqUniform);
TVariable *newVar = new TVariable(mSymbolTable, asSymbol->getName(), uniformType,
asSymbol->variable().symbolType());
TIntermSymbol *newSymbol = new TIntermSymbol(newVar);
namedDecl->appendDeclarator(newSymbol);
newSequence->push_back(namedDecl);
}
mMultiReplacements.emplace_back(getParentNode()->getAsBlock(), decl, *newSequence);
}
};
// This traverses nodes, find the struct ones and add their declarations to the sink. It also // This traverses nodes, find the struct ones and add their declarations to the sink. It also
// removes the nodes from the tree as it processes them. // removes the nodes from the tree as it processes them.
class DeclareStructTypesTraverser : public TIntermTraverser class DeclareStructTypesTraverser : public TIntermTraverser
{ {
public: public:
DeclareStructTypesTraverser(TOutputVulkanGLSL *outputVulkanGLSL) explicit DeclareStructTypesTraverser(TOutputVulkanGLSL *outputVulkanGLSL)
: TIntermTraverser(true, false, false), mOutputVulkanGLSL(outputVulkanGLSL) : TIntermTraverser(true, false, false), mOutputVulkanGLSL(outputVulkanGLSL)
{ {
} }
...@@ -52,7 +129,11 @@ class DeclareStructTypesTraverser : public TIntermTraverser ...@@ -52,7 +129,11 @@ class DeclareStructTypesTraverser : public TIntermTraverser
if (type.isStructSpecifier()) if (type.isStructSpecifier())
{ {
mOutputVulkanGLSL->writeStructType(type.getStruct()); const TStructure *structure = type.getStruct();
// Embedded structs should be parsed away by now.
ASSERT(structure->symbolType() != SymbolType::Empty);
mOutputVulkanGLSL->writeStructType(structure);
TIntermSymbol *symbolNode = declarator->getAsSymbolNode(); TIntermSymbol *symbolNode = declarator->getAsSymbolNode();
if (symbolNode && symbolNode->variable().symbolType() == SymbolType::Empty) if (symbolNode && symbolNode->variable().symbolType() == SymbolType::Empty)
...@@ -116,8 +197,8 @@ class DeclareDefaultUniformsTraverser : public TIntermTraverser ...@@ -116,8 +197,8 @@ class DeclareDefaultUniformsTraverser : public TIntermTraverser
// Remove the uniform declaration from the tree so it isn't parsed again. // Remove the uniform declaration from the tree so it isn't parsed again.
TIntermSequence emptyReplacement; TIntermSequence emptyReplacement;
mMultiReplacements.push_back(NodeReplaceWithMultipleEntry( mMultiReplacements.emplace_back(getParentNode()->getAsBlock(), node,
getParentNode()->getAsBlock(), node, emptyReplacement)); emptyReplacement);
} }
mInDefaultUniform = false; mInDefaultUniform = false;
...@@ -287,6 +368,10 @@ void TranslatorVulkan::translate(TIntermBlock *root, ...@@ -287,6 +368,10 @@ void TranslatorVulkan::translate(TIntermBlock *root,
// http://anglebug.com/2461 // http://anglebug.com/2461
if (structTypesUsedForUniforms > 0) if (structTypesUsedForUniforms > 0)
{ {
NameEmbeddedUniformStructsTraverser nameStructs(&getSymbolTable());
root->traverse(&nameStructs);
nameStructs.updateTree();
// We must declare the struct types before using them. // We must declare the struct types before using them.
DeclareStructTypesTraverser structTypesTraverser(&outputGLSL); DeclareStructTypesTraverser structTypesTraverser(&outputGLSL);
root->traverse(&structTypesTraverser); root->traverse(&structTypesTraverser);
......
...@@ -194,7 +194,7 @@ TType::TType(const TPublicType &p) ...@@ -194,7 +194,7 @@ TType::TType(const TPublicType &p)
} }
} }
TType::TType(const TStructure *userDef) TType::TType(const TStructure *userDef, bool isStructSpecifier)
: type(EbtStruct), : type(EbtStruct),
precision(EbpUndefined), precision(EbpUndefined),
qualifier(EvqTemporary), qualifier(EvqTemporary),
...@@ -206,7 +206,7 @@ TType::TType(const TStructure *userDef) ...@@ -206,7 +206,7 @@ TType::TType(const TStructure *userDef)
mArraySizes(nullptr), mArraySizes(nullptr),
mInterfaceBlock(nullptr), mInterfaceBlock(nullptr),
mStructure(userDef), mStructure(userDef),
mIsStructSpecifier(false), mIsStructSpecifier(isStructSpecifier),
mMangledName(nullptr) mMangledName(nullptr)
{ {
} }
......
...@@ -100,7 +100,7 @@ class TType ...@@ -100,7 +100,7 @@ class TType
unsigned char ps = 1, unsigned char ps = 1,
unsigned char ss = 1); unsigned char ss = 1);
explicit TType(const TPublicType &p); explicit TType(const TPublicType &p);
explicit TType(const TStructure *userDef); TType(const TStructure *userDef, bool isStructSpecifier);
TType(const TInterfaceBlock *interfaceBlockIn, TType(const TInterfaceBlock *interfaceBlockIn,
TQualifier qualifierIn, TQualifier qualifierIn,
TLayoutQualifier layoutQualifierIn); TLayoutQualifier layoutQualifierIn);
......
...@@ -59,7 +59,7 @@ ...@@ -59,7 +59,7 @@
}, },
"gl_DepthRange":{ "gl_DepthRange":{
"level":"COMMON_BUILTINS", "level":"COMMON_BUILTINS",
"initDynamicType":"TType *{type_name} = new TType(gl_DepthRangeParameters); {type_name}->setQualifier(EvqUniform);" "initDynamicType":"TType *{type_name} = new TType(gl_DepthRangeParameters, false); {type_name}->setQualifier(EvqUniform);"
} }
} }
}, },
......
...@@ -157,10 +157,10 @@ class TIntermTraverser : angle::NonCopyable ...@@ -157,10 +157,10 @@ class TIntermTraverser : angle::NonCopyable
// but also with other nodes like declarations. // but also with other nodes like declarations.
struct NodeReplaceWithMultipleEntry struct NodeReplaceWithMultipleEntry
{ {
NodeReplaceWithMultipleEntry(TIntermAggregateBase *_parent, NodeReplaceWithMultipleEntry(TIntermAggregateBase *parentIn,
TIntermNode *_original, TIntermNode *originalIn,
TIntermSequence _replacements) TIntermSequence replacementsIn)
: parent(_parent), original(_original), replacements(_replacements) : parent(parentIn), original(originalIn), replacements(std::move(replacementsIn))
{ {
} }
......
...@@ -302,7 +302,7 @@ TEST_F(InitOutputVariablesWebGL2VertexShaderTest, OutputStruct) ...@@ -302,7 +302,7 @@ TEST_F(InitOutputVariablesWebGL2VertexShaderTest, OutputStruct)
mASTRoot->traverse(&findStruct); mASTRoot->traverse(&findStruct);
ASSERT(findStruct.isStructureFound()); ASSERT(findStruct.isStructureFound());
TType type(findStruct.getStructure()); TType type(findStruct.getStructure(), false);
type.setQualifier(EvqVertexOut); type.setQualifier(EvqVertexOut);
TIntermTyped *expectedLValue = CreateLValueNode(ImmutableString("out1"), type); TIntermTyped *expectedLValue = CreateLValueNode(ImmutableString("out1"), type);
......
...@@ -2931,6 +2931,59 @@ TEST_P(WebGLGLSLTest, UninitializedNamelessStructInGlobalScope) ...@@ -2931,6 +2931,59 @@ TEST_P(WebGLGLSLTest, UninitializedNamelessStructInGlobalScope)
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
} }
// Tests nameless struct uniforms.
TEST_P(GLSLTest, EmbeddedStructUniform)
{
const char kFragmentShader[] = R"(precision mediump float;
uniform struct { float q; } b;
void main()
{
gl_FragColor = vec4(1, 0, 0, 1);
if (b.q == 0.5)
{
gl_FragColor = vec4(0, 1, 0, 1);
}
})";
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFragmentShader);
glUseProgram(program);
GLint uniLoc = glGetUniformLocation(program, "b.q");
ASSERT_NE(-1, uniLoc);
glUniform1f(uniLoc, 0.5f);
drawQuad(program.get(), essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// Tests two nameless struct uniforms.
TEST_P(GLSLTest, TwoEmbeddedStructUniforms)
{
const char kFragmentShader[] = R"(precision mediump float;
uniform struct { float q; } b, c;
void main()
{
gl_FragColor = vec4(1, 0, 0, 1);
if (b.q == 0.5 && c.q == 1.0)
{
gl_FragColor = vec4(0, 1, 0, 1);
}
})";
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFragmentShader);
glUseProgram(program);
GLint uniLocB = glGetUniformLocation(program, "b.q");
ASSERT_NE(-1, uniLocB);
glUniform1f(uniLocB, 0.5f);
GLint uniLocC = glGetUniformLocation(program, "c.q");
ASSERT_NE(-1, uniLocC);
glUniform1f(uniLocC, 1.0f);
drawQuad(program.get(), essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// Test that a loop condition that has an initializer declares a variable. // Test that a loop condition that has an initializer declares a variable.
TEST_P(GLSLTest_ES3, ConditionInitializerDeclaresVariable) TEST_P(GLSLTest_ES3, ConditionInitializerDeclaresVariable)
{ {
......
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