Commit fab397e5 by Ian Elliott Committed by Commit Bot

Vulkan: Enforce an error when initializing a global with a non-const

The ESSL 1.0 spec is clear that "initializers must be a constant expression." Yet, because of "legacy" applications (apparently just WebGL applications), the code was only issuing a warning and not an error. The "KHR-GLES2.shaders.negative.initialize" test requires an error be generated. This change splits the semantics, allowing GLES applications to get an error, and WebGL applications to get a warning. Note: This change is related to https://chromium-review.googlesource.com/829138 (for angleproject:2285). Bug: angleproject:3381 Change-Id: Ie243b7dd72102aeb52df506d121d1d2a8f6974d3 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1716617Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org> Commit-Queue: Ian Elliott <ianelliott@google.com>
parent c327370e
...@@ -1987,7 +1987,8 @@ bool TParseContext::executeInitializer(const TSourceLoc &line, ...@@ -1987,7 +1987,8 @@ bool TParseContext::executeInitializer(const TSourceLoc &line,
bool globalInitWarning = false; bool globalInitWarning = false;
if (symbolTable.atGlobalLevel() && if (symbolTable.atGlobalLevel() &&
!ValidateGlobalInitializer(initializer, mShaderVersion, &globalInitWarning)) !ValidateGlobalInitializer(initializer, mShaderVersion, sh::IsWebGLBasedSpec(mShaderSpec),
&globalInitWarning))
{ {
// Error message does not completely match behavior with ESSL 1.00, but // Error message does not completely match behavior with ESSL 1.00, but
// we want to steer developers towards only using constant expressions. // we want to steer developers towards only using constant expressions.
......
...@@ -19,7 +19,7 @@ const int kMaxAllowedTraversalDepth = 256; ...@@ -19,7 +19,7 @@ const int kMaxAllowedTraversalDepth = 256;
class ValidateGlobalInitializerTraverser : public TIntermTraverser class ValidateGlobalInitializerTraverser : public TIntermTraverser
{ {
public: public:
ValidateGlobalInitializerTraverser(int shaderVersion); ValidateGlobalInitializerTraverser(int shaderVersion, bool isWebGL);
void visitSymbol(TIntermSymbol *node) override; void visitSymbol(TIntermSymbol *node) override;
void visitConstantUnion(TIntermConstantUnion *node) override; void visitConstantUnion(TIntermConstantUnion *node) override;
...@@ -32,6 +32,7 @@ class ValidateGlobalInitializerTraverser : public TIntermTraverser ...@@ -32,6 +32,7 @@ class ValidateGlobalInitializerTraverser : public TIntermTraverser
private: private:
int mShaderVersion; int mShaderVersion;
bool mIsWebGL;
bool mIsValid; bool mIsValid;
bool mIssueWarning; bool mIssueWarning;
}; };
...@@ -50,7 +51,7 @@ void ValidateGlobalInitializerTraverser::visitSymbol(TIntermSymbol *node) ...@@ -50,7 +51,7 @@ void ValidateGlobalInitializerTraverser::visitSymbol(TIntermSymbol *node)
// We allow these cases to be compatible with legacy ESSL 1.00 content. // 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 // Implement stricter rules for ESSL 3.00 since there's no legacy content to deal
// with. // with.
if (mShaderVersion >= 300) if ((mShaderVersion >= 300) || !mIsWebGL)
{ {
mIsValid = false; mIsValid = false;
} }
...@@ -73,7 +74,7 @@ void ValidateGlobalInitializerTraverser::visitConstantUnion(TIntermConstantUnion ...@@ -73,7 +74,7 @@ void ValidateGlobalInitializerTraverser::visitConstantUnion(TIntermConstantUnion
case EvqConst: case EvqConst:
break; break;
case EvqTemporary: case EvqTemporary:
if (mShaderVersion >= 300) if ((mShaderVersion >= 300) || !mIsWebGL)
{ {
mIsValid = false; mIsValid = false;
} }
...@@ -118,9 +119,11 @@ bool ValidateGlobalInitializerTraverser::visitUnary(Visit visit, TIntermUnary *n ...@@ -118,9 +119,11 @@ bool ValidateGlobalInitializerTraverser::visitUnary(Visit visit, TIntermUnary *n
return true; return true;
} }
ValidateGlobalInitializerTraverser::ValidateGlobalInitializerTraverser(int shaderVersion) ValidateGlobalInitializerTraverser::ValidateGlobalInitializerTraverser(int shaderVersion,
bool isWebGL)
: TIntermTraverser(true, false, false, nullptr), : TIntermTraverser(true, false, false, nullptr),
mShaderVersion(shaderVersion), mShaderVersion(shaderVersion),
mIsWebGL(isWebGL),
mIsValid(true), mIsValid(true),
mIssueWarning(false) mIssueWarning(false)
{ {
...@@ -129,9 +132,12 @@ ValidateGlobalInitializerTraverser::ValidateGlobalInitializerTraverser(int shade ...@@ -129,9 +132,12 @@ ValidateGlobalInitializerTraverser::ValidateGlobalInitializerTraverser(int shade
} // namespace } // namespace
bool ValidateGlobalInitializer(TIntermTyped *initializer, int shaderVersion, bool *warning) bool ValidateGlobalInitializer(TIntermTyped *initializer,
int shaderVersion,
bool isWebGL,
bool *warning)
{ {
ValidateGlobalInitializerTraverser validate(shaderVersion); ValidateGlobalInitializerTraverser validate(shaderVersion, isWebGL);
initializer->traverse(&validate); initializer->traverse(&validate);
ASSERT(warning != nullptr); ASSERT(warning != nullptr);
*warning = validate.issueWarning(); *warning = validate.issueWarning();
......
...@@ -13,7 +13,10 @@ namespace sh ...@@ -13,7 +13,10 @@ namespace sh
class TIntermTyped; class TIntermTyped;
// Returns true if the initializer is valid. // Returns true if the initializer is valid.
bool ValidateGlobalInitializer(TIntermTyped *initializer, int shaderVersion, bool *warning); bool ValidateGlobalInitializer(TIntermTyped *initializer,
int shaderVersion,
bool isWebGL,
bool *warning);
} // namespace sh } // namespace sh
......
...@@ -606,8 +606,8 @@ TEST_F(FragmentShaderValidationTest, AssignUniformToGlobalESSL3) ...@@ -606,8 +606,8 @@ TEST_F(FragmentShaderValidationTest, AssignUniformToGlobalESSL3)
} }
// Global variable initializers need to be constant expressions (ESSL 1.00 section 4.3) // Global variable initializers need to be constant expressions (ESSL 1.00 section 4.3)
// Initializing with an uniform should generate a warning // Initializing with an uniform used to generate a warning on ESSL 1.00 because of legacy
// (we don't generate an error on ESSL 1.00 because of legacy compatibility) // compatibility, but that causes dEQP to fail (which expects an error)
TEST_F(FragmentShaderValidationTest, AssignUniformToGlobalESSL1) TEST_F(FragmentShaderValidationTest, AssignUniformToGlobalESSL1)
{ {
const std::string &shaderString = const std::string &shaderString =
...@@ -619,15 +619,7 @@ TEST_F(FragmentShaderValidationTest, AssignUniformToGlobalESSL1) ...@@ -619,15 +619,7 @@ TEST_F(FragmentShaderValidationTest, AssignUniformToGlobalESSL1)
"}\n"; "}\n";
if (compile(shaderString)) if (compile(shaderString))
{ {
if (!hasWarning()) FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
{
FAIL() << "Shader compilation succeeded without warnings, expecting warning:\n"
<< mInfoLog;
}
}
else
{
FAIL() << "Shader compilation failed, expecting success with warning:\n" << mInfoLog;
} }
} }
...@@ -1349,6 +1341,46 @@ TEST_F(WebGL2FragmentShaderValidationTest, IndexFragDataWithNonConstant) ...@@ -1349,6 +1341,46 @@ TEST_F(WebGL2FragmentShaderValidationTest, IndexFragDataWithNonConstant)
} }
} }
// 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 WebGL compatibility)
TEST_F(WebGL2FragmentShaderValidationTest, 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:\n"
<< mInfoLog;
}
}
else
{
FAIL() << "Shader compilation failed, expecting success with warning:\n" << mInfoLog;
}
}
// Test that deferring global variable init works with an empty main().
TEST_F(WebGL2FragmentShaderValidationTest, DeferGlobalVariableInitWithEmptyMain)
{
const std::string &shaderString =
"precision mediump float;\n"
"uniform float u;\n"
"float foo = u;\n"
"void main() {}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Test that a non-constant texture offset is not accepted for textureOffset. // Test that a non-constant texture offset is not accepted for textureOffset.
// ESSL 3.00 section 8.8 // ESSL 3.00 section 8.8
TEST_F(FragmentShaderValidationTest, TextureOffsetNonConst) TEST_F(FragmentShaderValidationTest, TextureOffsetNonConst)
...@@ -1469,6 +1501,46 @@ TEST_F(WebGL1FragmentShaderValidationTest, NonConstantLoopIndex) ...@@ -1469,6 +1501,46 @@ TEST_F(WebGL1FragmentShaderValidationTest, NonConstantLoopIndex)
} }
} }
// 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 WebGL compatibility)
TEST_F(WebGL1FragmentShaderValidationTest, 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:\n"
<< mInfoLog;
}
}
else
{
FAIL() << "Shader compilation failed, expecting success with warning:\n" << mInfoLog;
}
}
// Test that deferring global variable init works with an empty main().
TEST_F(WebGL1FragmentShaderValidationTest, DeferGlobalVariableInitWithEmptyMain)
{
const std::string &shaderString =
"precision mediump float;\n"
"uniform float u;\n"
"float foo = u;\n"
"void main() {}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Check that indices that are not integers are rejected. // Check that indices that are not integers are rejected.
// The check should be done even if ESSL 1.00 Appendix A limitations are not applied. // The check should be done even if ESSL 1.00 Appendix A limitations are not applied.
TEST_F(FragmentShaderValidationTest, NonIntegerIndex) TEST_F(FragmentShaderValidationTest, NonIntegerIndex)
...@@ -2636,20 +2708,6 @@ TEST_F(FragmentShaderValidationTest, ShiftByNegative) ...@@ -2636,20 +2708,6 @@ TEST_F(FragmentShaderValidationTest, ShiftByNegative)
} }
} }
// Test that deferring global variable init works with an empty main().
TEST_F(FragmentShaderValidationTest, DeferGlobalVariableInitWithEmptyMain)
{
const std::string &shaderString =
"precision mediump float;\n"
"uniform float u;\n"
"float foo = u;\n"
"void main() {}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Test that pruning empty declarations from loop init expression works. // Test that pruning empty declarations from loop init expression works.
TEST_F(FragmentShaderValidationTest, EmptyDeclarationAsLoopInit) TEST_F(FragmentShaderValidationTest, EmptyDeclarationAsLoopInit)
{ {
...@@ -6062,4 +6120,4 @@ void main() { ...@@ -6062,4 +6120,4 @@ void main() {
{ {
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog; FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
} }
} }
\ No newline at end of file
...@@ -29,9 +29,6 @@ ...@@ -29,9 +29,6 @@
// fails on Nexus5X with GLES backend (hangs) // fails on Nexus5X with GLES backend (hangs)
// 91531 NEXUS5X GLES : conformance_more_* = SKIP // 91531 NEXUS5X GLES : conformance_more_* = SKIP
// Relates to a difference in spec with ES and Core GL.
3381 : KHR-GLES2.shaders.negative.initialize = FAIL
// Failures with floating point extensions // Failures with floating point extensions
3451 D3D9 D3D11 OPENGL GLES : KHR-GLES2.core.internalformat.texture2d.rgb_half_float_rgb16f = FAIL 3451 D3D9 D3D11 OPENGL GLES : KHR-GLES2.core.internalformat.texture2d.rgb_half_float_rgb16f = FAIL
3451 D3D9 D3D11 OPENGL GLES : KHR-GLES2.core.internalformat.texture2d.rgba_half_float_rgba16f = FAIL 3451 D3D9 D3D11 OPENGL GLES : KHR-GLES2.core.internalformat.texture2d.rgba_half_float_rgba16f = FAIL
...@@ -50,4 +47,4 @@ ...@@ -50,4 +47,4 @@
// Compressed texture tests on Android // Compressed texture tests on Android
3190 VULKAN PIXEL2 : KHR-GLES2.texture_3d.compressed_texture.rgba_astc_* = FAIL 3190 VULKAN PIXEL2 : KHR-GLES2.texture_3d.compressed_texture.rgba_astc_* = FAIL
3190 VULKAN PIXEL2 : KHR-GLES2.texture_3d.compressed_texture.sgb8_alpha8_astc_* = FAIL 3190 VULKAN PIXEL2 : KHR-GLES2.texture_3d.compressed_texture.sgb8_alpha8_astc_* = FAIL
3190 VULKAN PIXEL2 : KHR-GLES2.texture_3d.compressed_texture.srgb8_alpha8_astc_* = FAIL 3190 VULKAN PIXEL2 : KHR-GLES2.texture_3d.compressed_texture.srgb8_alpha8_astc_* = FAIL
\ 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