Commit ed5b46f1 by Brandon Jones Committed by Commit Bot

Add additional ES2 and WebGL 1.0 Validation

Adds validation for various cases. Adds corresponding unit tests. Change-Id: I9451d286bcf2d6fa32de495e5d0bdec1eb5c955d Reviewed-on: https://chromium-review.googlesource.com/633157Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent 2e29b13d
...@@ -97,9 +97,14 @@ ContextState::~ContextState() ...@@ -97,9 +97,14 @@ ContextState::~ContextState()
// Handles are released by the Context. // Handles are released by the Context.
} }
bool ContextState::isWebGL() const
{
return mExtensions.webglCompatibility;
}
bool ContextState::isWebGL1() const bool ContextState::isWebGL1() const
{ {
return (mExtensions.webglCompatibility && mClientVersion.major == 2); return (isWebGL() && mClientVersion.major == 2);
} }
const TextureCaps &ContextState::getTextureCap(GLenum internalFormat) const const TextureCaps &ContextState::getTextureCap(GLenum internalFormat) const
......
...@@ -62,6 +62,7 @@ class ContextState final : angle::NonCopyable ...@@ -62,6 +62,7 @@ class ContextState final : angle::NonCopyable
bool usingDisplayTextureShareGroup() const; bool usingDisplayTextureShareGroup() const;
bool isWebGL() const;
bool isWebGL1() const; bool isWebGL1() const;
private: private:
...@@ -130,6 +131,7 @@ class ValidationContext : angle::NonCopyable ...@@ -130,6 +131,7 @@ class ValidationContext : angle::NonCopyable
// Hack for the special WebGL 1 "DEPTH_STENCIL" internal format. // Hack for the special WebGL 1 "DEPTH_STENCIL" internal format.
GLenum getConvertedRenderbufferFormat(GLenum internalformat) const; GLenum getConvertedRenderbufferFormat(GLenum internalformat) const;
bool isWebGL() const { return mState.isWebGL(); }
bool isWebGL1() const { return mState.isWebGL1(); } bool isWebGL1() const { return mState.isWebGL1(); }
template <typename T> template <typename T>
......
...@@ -104,6 +104,7 @@ ERRMSG(InvalidVertexAttrSize, "Vertex attribute size must be 1, 2, 3, or 4."); ...@@ -104,6 +104,7 @@ ERRMSG(InvalidVertexAttrSize, "Vertex attribute size must be 1, 2, 3, or 4.");
ERRMSG(InvalidWidth, "Invalid width."); ERRMSG(InvalidWidth, "Invalid width.");
ERRMSG(InvalidWrapModeTexture, "Invalid wrap mode for texture type."); ERRMSG(InvalidWrapModeTexture, "Invalid wrap mode for texture type.");
ERRMSG(LevelNotZero, "Texture level must be zero."); ERRMSG(LevelNotZero, "Texture level must be zero.");
ERRMSG(MismatchedByteCountType, "Buffer size does not align with data type.");
ERRMSG(MismatchedFormat, "Format must match internal format."); ERRMSG(MismatchedFormat, "Format must match internal format.");
ERRMSG(MismatchedTargetAndFormat, "Invalid texture target and format combination."); ERRMSG(MismatchedTargetAndFormat, "Invalid texture target and format combination.");
ERRMSG(MismatchedTypeAndFormat, "Invalid format and type combination."); ERRMSG(MismatchedTypeAndFormat, "Invalid format and type combination.");
...@@ -157,8 +158,11 @@ ERRMSG(UnknownParameter, "Unknown parameter value."); ...@@ -157,8 +158,11 @@ ERRMSG(UnknownParameter, "Unknown parameter value.");
ERRMSG(VertexArrayNoBuffer, "An enabled vertex array has no buffer."); ERRMSG(VertexArrayNoBuffer, "An enabled vertex array has no buffer.");
ERRMSG(VertexArrayNoBufferPointer, "An enabled vertex array has no buffer and no pointer."); ERRMSG(VertexArrayNoBufferPointer, "An enabled vertex array has no buffer and no pointer.");
ERRMSG(ViewportNegativeSize, "Viewport size cannot be negative."); ERRMSG(ViewportNegativeSize, "Viewport size cannot be negative.");
ERRMSG(Webgl2NameLengthLimitExceeded, "Location lengths must not be greater than 1024 characters.");
ERRMSG(WebglBindAttribLocationReservedPrefix, ERRMSG(WebglBindAttribLocationReservedPrefix,
"Attributes that begin with 'webgl_', or '_webgl_' are not allowed."); "Attributes that begin with 'webgl_', or '_webgl_' are not allowed.");
ERRMSG(WebglNameLengthLimitExceeded,
"Location name lengths must not be greater than 256 characters.");
} }
#undef ERRMSG #undef ERRMSG
#endif // LIBANGLE_ERRORSTRINGS_H_ #endif // LIBANGLE_ERRORSTRINGS_H_
...@@ -2870,6 +2870,13 @@ bool ValidateDrawElementsCommon(ValidationContext *context, ...@@ -2870,6 +2870,13 @@ bool ValidateDrawElementsCommon(ValidationContext *context,
ANGLE_VALIDATION_ERR(context, InvalidOperation(), InsufficientBufferSize); ANGLE_VALIDATION_ERR(context, InvalidOperation(), InsufficientBufferSize);
return false; return false;
} }
ASSERT(typeSize > 0);
if (elementArrayBuffer->getSize() % typeSize != 0)
{
ANGLE_VALIDATION_ERR(context, InvalidOperation(), MismatchedByteCountType);
return false;
}
} }
else if (!indices) else if (!indices)
{ {
......
...@@ -949,6 +949,45 @@ bool IsValidESSLShaderSourceString(const char *str, size_t len, bool lineContinu ...@@ -949,6 +949,45 @@ bool IsValidESSLShaderSourceString(const char *str, size_t len, bool lineContinu
return true; return true;
} }
bool ValidateWebGLNamePrefix(ValidationContext *context, const GLchar *name)
{
ASSERT(context->isWebGL());
// WebGL 1.0 [Section 6.16] GLSL Constructs
// Identifiers starting with "webgl_" and "_webgl_" are reserved for use by WebGL.
if (strncmp(name, "webgl_", 6) == 0 || strncmp(name, "_webgl_", 7) == 0)
{
ANGLE_VALIDATION_ERR(context, InvalidOperation(), WebglBindAttribLocationReservedPrefix);
return false;
}
return true;
}
bool ValidateWebGLNameLength(ValidationContext *context, size_t length)
{
ASSERT(context->isWebGL());
if (context->isWebGL1() && length > 256)
{
// WebGL 1.0 [Section 6.21] Maxmimum Uniform and Attribute Location Lengths
// WebGL imposes a limit of 256 characters on the lengths of uniform and attribute
// locations.
ANGLE_VALIDATION_ERR(context, InvalidValue(), WebglNameLengthLimitExceeded);
return false;
}
else if (length > 1024)
{
// WebGL 2.0 [Section 4.3.2] WebGL 2.0 imposes a limit of 1024 characters on the lengths of
// uniform and attribute locations.
ANGLE_VALIDATION_ERR(context, InvalidValue(), Webgl2NameLengthLimitExceeded);
return false;
}
return true;
}
} // anonymous namespace } // anonymous namespace
bool ValidateES2TexImageParameters(Context *context, bool ValidateES2TexImageParameters(Context *context,
...@@ -4284,12 +4323,22 @@ bool ValidateBindAttribLocation(ValidationContext *context, ...@@ -4284,12 +4323,22 @@ bool ValidateBindAttribLocation(ValidationContext *context,
return false; return false;
} }
// The WebGL spec (section 6.20) disallows strings containing invalid ESSL characters for if (context->isWebGL())
// shader-related entry points
if (context->getExtensions().webglCompatibility && !IsValidESSLString(name, strlen(name)))
{ {
ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidNameCharacters); const size_t length = strlen(name);
return false;
if (!IsValidESSLString(name, length))
{
// The WebGL spec (section 6.20) disallows strings containing invalid ESSL characters
// for shader-related entry points
ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidNameCharacters);
return false;
}
if (!ValidateWebGLNameLength(context, length) || !ValidateWebGLNamePrefix(context, name))
{
return false;
}
} }
return GetValidProgram(context, program) != nullptr; return GetValidProgram(context, program) != nullptr;
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
'<(angle_path)/src/tests/gl_tests/D3D11FormatTablesTest.cpp', '<(angle_path)/src/tests/gl_tests/D3D11FormatTablesTest.cpp',
'<(angle_path)/src/tests/gl_tests/D3D11InputLayoutCacheTest.cpp', '<(angle_path)/src/tests/gl_tests/D3D11InputLayoutCacheTest.cpp',
'<(angle_path)/src/tests/gl_tests/D3DTextureTest.cpp', '<(angle_path)/src/tests/gl_tests/D3DTextureTest.cpp',
'<(angle_path)/src/tests/gl_tests/ErrorMessages.cpp'
], ],
}, },
'dependencies': 'dependencies':
......
...@@ -263,6 +263,58 @@ TEST_P(DrawElementsTest, DeletingAfterStreamingIndexes) ...@@ -263,6 +263,58 @@ TEST_P(DrawElementsTest, DeletingAfterStreamingIndexes)
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
} }
// Test that the offset in the index buffer is forced to be a multiple of the element size
TEST_P(DrawElementsTest, DrawElementsTypeAlignment)
{
const std::string &vert =
"attribute vec3 a_pos;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(a_pos, 1.0);\n"
"}\n";
const std::string &frag =
"precision highp float;\n"
"void main()\n"
"{\n"
" gl_FragColor = vec4(1.0);\n"
"}\n";
ANGLE_GL_PROGRAM(program, vert, frag);
GLint posLocation = glGetAttribLocation(program, "a_pos");
ASSERT_NE(-1, posLocation);
glUseProgram(program);
const auto &vertices = GetQuadVertices();
GLBuffer vertexBuffer;
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), vertices.data(),
GL_STATIC_DRAW);
glVertexAttribPointer(posLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(posLocation);
GLBuffer indexBuffer;
const GLubyte indices1[] = {0, 0, 0, 0, 0, 0};
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices1), indices1, GL_STATIC_DRAW);
ASSERT_GL_NO_ERROR();
const char *zeroIndices = nullptr;
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, zeroIndices);
ASSERT_GL_NO_ERROR();
const GLubyte indices2[] = {0, 0, 0, 0, 0};
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices2), indices2, GL_STATIC_DRAW);
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, zeroIndices + 1);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
ANGLE_INSTANTIATE_TEST(DrawElementsTest, ES3_OPENGL(), ES3_OPENGLES()); ANGLE_INSTANTIATE_TEST(DrawElementsTest, ES3_OPENGL(), ES3_OPENGLES());
} }
//
// Copyright 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.
//
// ErrorMessages.cpp : Tests functionality of internal error error messages
#include "test_utils/ANGLETest.h"
#include "../src/libANGLE/ErrorStrings.h"
#include "test_utils/gl_raii.h"
namespace
{
struct Message
{
GLenum source;
GLenum type;
GLenum id;
GLenum severity;
std::string message;
const void *userParam;
inline bool operator==(Message a)
{
if (a.source == source && a.type == type && a.id == id && a.message == message)
{
return true;
}
else
{
return false;
}
}
};
static void GL_APIENTRY Callback(GLenum source,
GLenum type,
GLuint id,
GLenum severity,
GLsizei length,
const GLchar *message,
const void *userParam)
{
Message m{source, type, id, severity, std::string(message, length), userParam};
std::vector<Message> *messages =
static_cast<std::vector<Message> *>(const_cast<void *>(userParam));
messages->push_back(m);
}
} // namespace
namespace angle
{
class ErrorMessagesTest : public ANGLETest
{
protected:
ErrorMessagesTest()
{
setWindowWidth(128);
setWindowHeight(128);
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
setWebGLCompatibilityEnabled(true);
}
void SetUp() override { ANGLETest::SetUp(); }
};
// Verify functionality of WebGL specific errors using KHR_debug
TEST_P(ErrorMessagesTest, ErrorMessages)
{
if (extensionEnabled("GL_KHR_debug"))
{
glEnable(GL_DEBUG_OUTPUT);
}
else
{
std::cout << "Test skipped because GL_KHR_debug is not available." << std::endl;
return;
}
std::vector<Message> messages;
glDebugMessageCallbackKHR(Callback, &messages);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
constexpr GLenum source = GL_DEBUG_SOURCE_API;
constexpr GLenum type = GL_DEBUG_TYPE_ERROR;
constexpr GLenum severity = GL_DEBUG_SEVERITY_HIGH;
constexpr GLuint id1 = 1282;
const std::string message1 = gl::kErrorWebglBindAttribLocationReservedPrefix;
Message expectedMessage;
GLint numMessages = 0;
glGetIntegerv(GL_DEBUG_LOGGED_MESSAGES, &numMessages);
EXPECT_EQ(0, numMessages);
glBindAttribLocation(0, 0, "_webgl_var");
ASSERT_EQ(1u, messages.size());
expectedMessage.source = source;
expectedMessage.id = id1;
expectedMessage.type = type;
expectedMessage.severity = severity;
expectedMessage.message = message1;
Message &m = messages.front();
ASSERT_TRUE(m == expectedMessage);
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against.
ANGLE_INSTANTIATE_TEST(ErrorMessagesTest,
ES2_D3D9(),
ES2_D3D11(),
ES3_D3D11(),
ES2_OPENGL(),
ES3_OPENGL(),
ES2_OPENGLES(),
ES3_OPENGLES());
} // namespace
...@@ -978,6 +978,21 @@ TEST_P(WebGL2CompatibilityTest, ShaderSourceLineContinuation) ...@@ -978,6 +978,21 @@ TEST_P(WebGL2CompatibilityTest, ShaderSourceLineContinuation)
glDeleteShader(shader); glDeleteShader(shader);
} }
// Tests bindAttribLocations for reserved prefixes and length limits
TEST_P(WebGLCompatibilityTest, BindAttribLocationLimitation)
{
constexpr int maxLocStringLength = 256;
const std::string tooLongString(maxLocStringLength + 1, '_');
glBindAttribLocation(0, 0, "_webgl_var");
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
glBindAttribLocation(0, 0, static_cast<const GLchar *>(tooLongString.c_str()));
EXPECT_GL_ERROR(GL_INVALID_VALUE);
}
// Test the checks for OOB reads in the vertex buffers, instanced version // Test the checks for OOB reads in the vertex buffers, instanced version
TEST_P(WebGL2CompatibilityTest, DrawArraysBufferOutOfBoundsInstanced) TEST_P(WebGL2CompatibilityTest, DrawArraysBufferOutOfBoundsInstanced)
{ {
...@@ -1306,7 +1321,7 @@ TEST_P(WebGLCompatibilityTest, DrawElementsOffsetRestriction) ...@@ -1306,7 +1321,7 @@ TEST_P(WebGLCompatibilityTest, DrawElementsOffsetRestriction)
glEnableVertexAttribArray(posLocation); glEnableVertexAttribArray(posLocation);
GLBuffer indexBuffer; GLBuffer indexBuffer;
const GLubyte indices[] = {0, 0, 0, 0, 0, 0, 0}; const GLubyte indices[] = {0, 0, 0, 0, 0, 0, 0, 0};
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer.get()); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer.get());
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
...@@ -1317,10 +1332,10 @@ TEST_P(WebGLCompatibilityTest, DrawElementsOffsetRestriction) ...@@ -1317,10 +1332,10 @@ TEST_P(WebGLCompatibilityTest, DrawElementsOffsetRestriction)
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, zeroIndices); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, zeroIndices);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, zeroIndices); glDrawElements(GL_TRIANGLES, 4, GL_UNSIGNED_SHORT, zeroIndices);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, zeroIndices + 1); glDrawElements(GL_TRIANGLES, 4, GL_UNSIGNED_SHORT, zeroIndices + 1);
EXPECT_GL_ERROR(GL_INVALID_OPERATION); EXPECT_GL_ERROR(GL_INVALID_OPERATION);
} }
...@@ -2990,6 +3005,17 @@ TEST_P(WebGL2CompatibilityTest, NoAttributeVertexShader) ...@@ -2990,6 +3005,17 @@ TEST_P(WebGL2CompatibilityTest, NoAttributeVertexShader)
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
} }
// Tests bindAttribLocations for length limit
TEST_P(WebGL2CompatibilityTest, BindAttribLocationLimitation)
{
constexpr int maxLocStringLength = 1024;
const std::string tooLongString(maxLocStringLength + 1, '_');
glBindAttribLocation(0, 0, static_cast<const GLchar *>(tooLongString.c_str()));
EXPECT_GL_ERROR(GL_INVALID_VALUE);
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these // Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against. // tests should be run against.
ANGLE_INSTANTIATE_TEST(WebGLCompatibilityTest, ANGLE_INSTANTIATE_TEST(WebGLCompatibilityTest,
......
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