Commit 3f6d4dff by Corentin Wallez Committed by Commit Bot

WebGLCompatibility implement and add tests for section 6.4

The checks for VertexAttribPointer were already implemented in a previous patch, so we only add the checks for DrawElements. BUG=angleproject:1523 BUG=chromium:668223 Change-Id: I5da55f9a7e7479627099c7f77618353a63b75a8e Reviewed-on: https://chromium-review.googlesource.com/434958 Commit-Queue: Corentin Wallez <cwallez@chromium.org> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent 2739a272
...@@ -3463,16 +3463,38 @@ bool ValidateDrawElements(ValidationContext *context, ...@@ -3463,16 +3463,38 @@ bool ValidateDrawElements(ValidationContext *context,
const gl::VertexArray *vao = state.getVertexArray(); const gl::VertexArray *vao = state.getVertexArray();
gl::Buffer *elementArrayBuffer = vao->getElementArrayBuffer().get(); gl::Buffer *elementArrayBuffer = vao->getElementArrayBuffer().get();
if (elementArrayBuffer) GLuint typeBytes = gl::GetTypeInfo(type).bytes;
if (context->getExtensions().webglCompatibility)
{ {
const gl::Type &typeInfo = gl::GetTypeInfo(type); ASSERT(isPow2(typeBytes) && typeBytes > 0);
if ((reinterpret_cast<uintptr_t>(indices) & static_cast<uintptr_t>(typeBytes - 1)) != 0)
{
// [WebGL 1.0] Section 6.4 Buffer Offset and Stride Requirements
// The offset arguments to drawElements and [...], must be a multiple of the size of the
// data type passed to the call, or an INVALID_OPERATION error is generated.
context->handleError(Error(GL_INVALID_OPERATION,
"indices must be a multiple of the element type size."));
return false;
}
if (!elementArrayBuffer && count > 0)
{
// [WebGL 1.0] Section 6.2 No Client Side Arrays
// If drawElements is called with a count greater than zero, and no WebGLBuffer is bound
// to the ELEMENT_ARRAY_BUFFER binding point, an INVALID_OPERATION error is generated.
context->handleError(Error(GL_INVALID_OPERATION,
"There is no element array buffer bound and count > 0."));
return false;
}
}
if (elementArrayBuffer)
{
GLint64 offset = reinterpret_cast<GLint64>(indices); GLint64 offset = reinterpret_cast<GLint64>(indices);
GLint64 byteCount = GLint64 byteCount = static_cast<GLint64>(typeBytes) * static_cast<GLint64>(count) + offset;
static_cast<GLint64>(typeInfo.bytes) * static_cast<GLint64>(count) + offset;
// check for integer overflows // check for integer overflows
if (static_cast<GLuint>(count) > (std::numeric_limits<GLuint>::max() / typeInfo.bytes) || if (static_cast<GLuint>(count) > (std::numeric_limits<GLuint>::max() / typeBytes) ||
byteCount > static_cast<GLint64>(std::numeric_limits<GLuint>::max())) byteCount > static_cast<GLint64>(std::numeric_limits<GLuint>::max()))
{ {
context->handleError(Error(GL_OUT_OF_MEMORY)); context->handleError(Error(GL_OUT_OF_MEMORY));
...@@ -3486,16 +3508,7 @@ bool ValidateDrawElements(ValidationContext *context, ...@@ -3486,16 +3508,7 @@ bool ValidateDrawElements(ValidationContext *context,
return false; return false;
} }
} }
else if (context->getExtensions().webglCompatibility && count > 0) else if (!indices && count > 0)
{
// [WebGL 1.0] Section 6.2 No Client Side Arrays
// If drawElements is called with a count greater than zero, and no WebGLBuffer is bound
// to the ELEMENT_ARRAY_BUFFER binding point, an INVALID_OPERATION error is generated.
context->handleError(
Error(GL_INVALID_OPERATION, "There is no element array buffer bound and count > 0."));
return false;
}
else if (!indices)
{ {
// This is an application error that would normally result in a crash, // This is an application error that would normally result in a crash,
// but we catch it and return an error // but we catch it and return an error
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
// //
#include "test_utils/ANGLETest.h" #include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"
using namespace angle; using namespace angle;
...@@ -63,6 +64,47 @@ class DrawElementsTest : public ANGLETest ...@@ -63,6 +64,47 @@ class DrawElementsTest : public ANGLETest
GLuint mProgram; GLuint mProgram;
}; };
// Test no error is generated when using client-side arrays, indices = nullptr and count = 0
TEST_P(DrawElementsTest, ClientSideNullptrArrayZeroCount)
{
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.get(), "a_pos");
ASSERT_NE(-1, posLocation);
glUseProgram(program.get());
const auto &vertices = GetQuadVertices();
GLBuffer vertexBuffer;
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer.get());
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);
ASSERT_GL_NO_ERROR();
glDrawElements(GL_TRIANGLES, 1, GL_UNSIGNED_BYTE, nullptr);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
glDrawElements(GL_TRIANGLES, 0, GL_UNSIGNED_BYTE, nullptr);
ASSERT_GL_NO_ERROR();
}
// Test a state desync that can occur when using a streaming index buffer in GL in concert with // Test a state desync that can occur when using a streaming index buffer in GL in concert with
// deleting the applied index buffer. // deleting the applied index buffer.
TEST_P(DrawElementsTest, DeletingAfterStreamingIndexes) TEST_P(DrawElementsTest, DeletingAfterStreamingIndexes)
......
...@@ -627,6 +627,85 @@ TEST_P(WebGLCompatibilityTest, MaxDrawBuffersAttachmentPoints) ...@@ -627,6 +627,85 @@ TEST_P(WebGLCompatibilityTest, MaxDrawBuffersAttachmentPoints)
} }
} }
// Test that the offset in the index buffer is forced to be a multiple of the element size
TEST_P(WebGLCompatibilityTest, DrawElementsOffsetRestriction)
{
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.get(), "a_pos");
ASSERT_NE(-1, posLocation);
glUseProgram(program.get());
const auto &vertices = GetQuadVertices();
GLBuffer vertexBuffer;
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer.get());
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 indices[] = {0, 0, 0, 0, 0, 0, 0};
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer.get());
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
ASSERT_GL_NO_ERROR();
const char *zeroIndices = nullptr;
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, zeroIndices);
ASSERT_GL_NO_ERROR();
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, zeroIndices);
ASSERT_GL_NO_ERROR();
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, zeroIndices + 1);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
// Test that the offset and stride in the vertex buffer is forced to be a multiple of the element
// size
TEST_P(WebGLCompatibilityTest, VertexAttribPointerOffsetRestriction)
{
const char *zeroOffset = nullptr;
// Base case, vector of two floats
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, zeroOffset);
ASSERT_GL_NO_ERROR();
// Test setting a non-multiple offset
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, zeroOffset + 1);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, zeroOffset + 2);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, zeroOffset + 3);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
// Test setting a non-multiple stride
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 1, zeroOffset);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2, zeroOffset);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 3, zeroOffset);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
// 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