Commit b0c645e2 by Olli Etuaho

Validate global initializer qualifiers

Allow only constants, other globals, temporaries, and uniforms to be used in global variable initialization for now. The spec limits global variable initialization further to only constant expressions, but fully implementing this has a too large compatibility cost for ESSL 1.00, so implement it only partially. In the case of ESSL 3.00 we can use stricter validation, since there's no legacy to worry about. Resubmitting the change, since the previous version neglected to remove incorrect GLSL tests. TEST=angle_unittests, WebGL conformance tests, angle_end2end_tests BUG=angleproject:988 Change-Id: I1bb3b8dc305689a90eadfe8cc7705e5ac3829e03 Reviewed-on: https://chromium-review.googlesource.com/270651Tested-by: 's avatarOlli Etuaho <oetuaho@nvidia.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent fe5f54f5
......@@ -99,6 +99,8 @@
'compiler/translator/Types.h',
'compiler/translator/UnfoldShortCircuitAST.cpp',
'compiler/translator/UnfoldShortCircuitAST.h',
'compiler/translator/ValidateGlobalInitializer.cpp',
'compiler/translator/ValidateGlobalInitializer.h',
'compiler/translator/ValidateLimitations.cpp',
'compiler/translator/ValidateLimitations.h',
'compiler/translator/ValidateOutputs.cpp',
......
......@@ -12,6 +12,7 @@
#include "compiler/preprocessor/SourceLocation.h"
#include "compiler/translator/glslang.h"
#include "compiler/translator/ValidateSwitch.h"
#include "compiler/translator/ValidateGlobalInitializer.h"
#include "compiler/translator/util.h"
///////////////////////////////////////////////////////////////////////
......@@ -1110,6 +1111,20 @@ bool TParseContext::executeInitializer(const TSourceLoc &line, const TString &id
return true;
}
bool globalInitWarning = false;
if (symbolTable.atGlobalLevel() && !ValidateGlobalInitializer(initializer, this, &globalInitWarning))
{
// Error message does not completely match behavior with ESSL 1.00, but
// we want to steer developers towards only using constant expressions.
error(line, "global variable initializers must be constant expressions", "=");
return true;
}
if (globalInitWarning)
{
warning(line, "global variable initializers should be constant expressions "
"(uniforms and globals are allowed in global initializers for legacy compatibility)", "=");
}
//
// identifier must be of type constant, a global, or a temporary
//
......
//
// Copyright (c) 2002-2015 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.
//
#include "compiler/translator/ValidateGlobalInitializer.h"
#include "compiler/translator/ParseContext.h"
namespace
{
class ValidateGlobalInitializerTraverser : public TIntermTraverser
{
public:
ValidateGlobalInitializerTraverser(const TParseContext *context);
void visitSymbol(TIntermSymbol *node) override;
bool isValid() const { return mIsValid; }
bool issueWarning() const { return mIssueWarning; }
private:
const TParseContext *mContext;
bool mIsValid;
bool mIssueWarning;
};
void ValidateGlobalInitializerTraverser::visitSymbol(TIntermSymbol *node)
{
const TSymbol *sym = mContext->symbolTable.find(node->getSymbol(), mContext->getShaderVersion());
if (sym->isVariable())
{
// ESSL 1.00 section 4.3 (or ESSL 3.00 section 4.3):
// Global initializers must be constant expressions.
const TVariable *var = static_cast<const TVariable *>(sym);
switch (var->getType().getQualifier())
{
case EvqConst:
break;
case EvqGlobal:
case EvqTemporary:
case EvqUniform:
// We allow these cases to be compatible with legacy ESSL 1.00 content.
// Implement stricter rules for ESSL 3.00 since there's no legacy content to deal with.
if (mContext->getShaderVersion() >= 300)
{
mIsValid = false;
}
else
{
mIssueWarning = true;
}
break;
default:
mIsValid = false;
}
}
}
ValidateGlobalInitializerTraverser::ValidateGlobalInitializerTraverser(const TParseContext *context)
: TIntermTraverser(true, false, false),
mContext(context),
mIsValid(true),
mIssueWarning(false)
{
}
} // namespace
bool ValidateGlobalInitializer(TIntermTyped *initializer, const TParseContext *context, bool *warning)
{
ValidateGlobalInitializerTraverser validate(context);
initializer->traverse(&validate);
ASSERT(warning != nullptr);
*warning = validate.issueWarning();
return validate.isValid();
}
//
// Copyright (c) 2002-2015 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.
//
#ifndef COMPILER_TRANSLATOR_VALIDATEGLOBALINITIALIZER_H_
#define COMPILER_TRANSLATOR_VALIDATEGLOBALINITIALIZER_H_
class TIntermTyped;
struct TParseContext;
// Returns true if the initializer is valid.
bool ValidateGlobalInitializer(TIntermTyped *initializer, const TParseContext *context, bool *warning);
#endif // COMPILER_TRANSLATOR_VALIDATEGLOBALINITIALIZER_H_
......@@ -42,6 +42,11 @@ class MalformedShaderTest : public testing::Test
return compilationSuccess;
}
bool hasWarning() const
{
return mInfoLog.find("WARNING: ") != std::string::npos;
}
protected:
std::string mInfoLog;
......@@ -441,3 +446,102 @@ TEST_F(MalformedShaderTest, AssignArrayLengthToUnsigned)
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// Global variable initializers need to be constant expressions (ESSL 1.00 section 4.3)
// Initializing with a varying should be an error.
TEST_F(MalformedShaderTest, AssignVaryingToGlobal)
{
const std::string &shaderString =
"precision mediump float;\n"
"varying float a;\n"
"float b = a * 2.0;\n"
"void main() {\n"
" gl_FragColor = vec4(b);\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// Global variable initializers need to be constant expressions (ESSL 3.00 section 4.3)
// Initializing with an uniform should be an error.
TEST_F(MalformedShaderTest, AssignUniformToGlobalESSL3)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"uniform float a;\n"
"float b = a * 2.0;\n"
"out vec4 my_FragColor;\n"
"void main() {\n"
" my_FragColor = vec4(b);\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// Global variable initializers need to be constant expressions (ESSL 1.00 section 4.3)
// Initializing with an uniform should generate a warning
// (we don't generate an error on ESSL 1.00 because of legacy compatibility)
TEST_F(MalformedShaderTest, AssignUniformToGlobalESSL1)
{
const std::string &shaderString =
"precision mediump float;\n"
"uniform float a;\n"
"float b = a * 2.0;\n"
"void main() {\n"
" gl_FragColor = vec4(b);\n"
"}\n";
if (compile(shaderString))
{
if (!hasWarning())
{
FAIL() << "Shader compilation succeeded without warnings, expecting warning " << mInfoLog;
}
}
else
{
FAIL() << "Shader compilation failed, expecting success with warning " << mInfoLog;
}
}
// Global variable initializers need to be constant expressions (ESSL 3.00 section 4.3)
// Initializing with a non-constant global should be an error.
TEST_F(MalformedShaderTest, AssignNonConstGlobalToGlobal)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"float a = 1.0;\n"
"float b = a * 2.0;\n"
"out vec4 my_FragColor;\n"
"void main() {\n"
" my_FragColor = vec4(b);\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
}
}
// Global variable initializers need to be constant expressions (ESSL 3.00 section 4.3)
// Initializing with a constant global should be fine.
TEST_F(MalformedShaderTest, AssignConstGlobalToGlobal)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"const float a = 1.0;\n"
"float b = a * 2.0;\n"
"out vec4 my_FragColor;\n"
"void main() {\n"
" my_FragColor = vec4(b);\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success " << mInfoLog;
}
}
......@@ -961,95 +961,6 @@ TEST_P(GLSLTest, BadIndexBug)
}
}
// Tests that using a global static initialized from a varying works as expected.
// See: https://code.google.com/p/angleproject/issues/detail?id=878
TEST_P(GLSLTest, GlobalStaticAndVarying)
{
const std::string &vertexShaderSource =
"attribute vec4 a_position;\n"
"varying float v;\n"
"void main() {\n"
" gl_Position = a_position;\n"
" v = 1.0;\n"
"}\n";
const std::string &fragmentShaderSource =
"precision highp float;\n"
"varying float v;\n"
"float x = v;"
"float global_v = x;"
"void main() {\n"
" gl_FragColor = vec4(global_v, 0.0, 0.0, 1.0);\n"
"}\n";
GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
ASSERT_NE(0u, program);
drawQuad(program, "a_position", 0.5f);
swapBuffers();
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255);
}
// Tests that using a global static initialized from gl_InstanceID works as expected.
TEST_P(GLSLTest_ES3, GlobalStaticAndInstanceID)
{
const std::string &vertexShaderSource =
"#version 300 es\n"
"precision highp float;\n"
"in vec4 a_position;\n"
"out vec4 vColour;"
"int x = gl_InstanceID;"
"int global_v = x;"
"void main() {\n"
" gl_Position = a_position;\n"
" vColour = vec4(float(global_v)/255., 0.0, 0.0, 1.0);\n"
"}\n";
const std::string &fragmentShaderSource =
"#version 300 es\n"
"precision highp float;\n"
"in vec4 vColour;"
"out vec4 colour;"
"void main() {\n"
" colour = vColour;\n"
"}\n";
GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
ASSERT_NE(0u, program);
GLint positionLocation = glGetAttribLocation(program, "a_position");
glUseProgram(program);
const GLfloat vertices[] =
{
-1.0f, 1.0f, 0.5f,
-1.0f, -1.0f, 0.5f,
1.0f, -1.0f, 0.5f,
-1.0f, 1.0f, 0.5f,
1.0f, -1.0f, 0.5f,
1.0f, 1.0f, 0.5f,
};
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, vertices);
glEnableVertexAttribArray(positionLocation);
glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 7);
glDisableVertexAttribArray(positionLocation);
glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, 0, NULL);
glUseProgram(0);
swapBuffers();
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_EQ(0, 0, 6, 0, 0, 255);
}
// Test that structs defined in uniforms are translated correctly.
TEST_P(GLSLTest, StructSpecifiersUniforms)
{
......
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