Commit 2c0d6a94 by Jamie Madill Committed by Commit Bot

WebGL: Fix DrawElements test and remove size check.

Also optimizes the DrawElements validation to remove redundant tests. Also make some more validation WebGL-specific. Bug: angleproject:2979 Change-Id: I2d5c3094ed86ebadbc572e46b8ae105431f7ae68 Reviewed-on: https://chromium-review.googlesource.com/c/1343139Reviewed-by: 's avatarYuly Novikov <ynovikov@chromium.org> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent 5829c03d
...@@ -221,7 +221,6 @@ MSG kLightParameterOutOfRange = "Light parameter out of range."; ...@@ -221,7 +221,6 @@ MSG kLightParameterOutOfRange = "Light parameter out of range.";
MSG kMaterialParameterOutOfRange = "Material parameter out of range."; MSG kMaterialParameterOutOfRange = "Material parameter out of range.";
MSG kMatrixStackOverflow = "Current matrix stack is full."; MSG kMatrixStackOverflow = "Current matrix stack is full.";
MSG kMatrixStackUnderflow = "Current matrix stack has only a single matrix."; MSG kMatrixStackUnderflow = "Current matrix stack has only a single matrix.";
MSG kMismatchedByteCountType = "Buffer size does not align with data type.";
MSG kMismatchedFormat = "Format must match internal format."; MSG kMismatchedFormat = "Format must match internal format.";
MSG kMismatchedTargetAndFormat = "Invalid texture target and format combination."; MSG kMismatchedTargetAndFormat = "Invalid texture target and format combination.";
MSG kMismatchedTypeAndFormat = "Invalid format and type combination."; MSG kMismatchedTypeAndFormat = "Invalid format and type combination.";
......
...@@ -3030,10 +3030,10 @@ bool ValidateDrawElementsCommon(Context *context, ...@@ -3030,10 +3030,10 @@ bool ValidateDrawElementsCommon(Context *context,
Buffer *elementArrayBuffer = vao->getElementArrayBuffer(); Buffer *elementArrayBuffer = vao->getElementArrayBuffer();
GLuint typeBytes = GetTypeInfo(type).bytes; GLuint typeBytes = GetTypeInfo(type).bytes;
ASSERT(isPow2(typeBytes) && typeBytes > 0);
if (context->getExtensions().webglCompatibility) if (context->getExtensions().webglCompatibility)
{ {
ASSERT(isPow2(typeBytes) && typeBytes > 0);
if ((reinterpret_cast<uintptr_t>(indices) & static_cast<uintptr_t>(typeBytes - 1)) != 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 // [WebGL 1.0] Section 6.4 Buffer Offset and Stride Requirements
...@@ -3051,19 +3051,7 @@ bool ValidateDrawElementsCommon(Context *context, ...@@ -3051,19 +3051,7 @@ bool ValidateDrawElementsCommon(Context *context,
context->validationError(GL_INVALID_VALUE, kNegativeOffset); context->validationError(GL_INVALID_VALUE, kNegativeOffset);
return false; return false;
} }
}
else if (elementArrayBuffer && elementArrayBuffer->isMapped())
{
// WebGL buffers cannot be mapped/unmapped because the MapBufferRange,
// FlushMappedBufferRange, and UnmapBuffer entry points are removed from the WebGL 2.0 API.
// https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.14
context->validationError(GL_INVALID_OPERATION, "Index buffer is mapped.");
return false;
}
if (context->getExtensions().webglCompatibility ||
!context->getGLState().areClientArraysEnabled())
{
if (!elementArrayBuffer) if (!elementArrayBuffer)
{ {
// [WebGL 1.0] Section 6.2 No Client Side Arrays // [WebGL 1.0] Section 6.2 No Client Side Arrays
...@@ -3072,86 +3060,103 @@ bool ValidateDrawElementsCommon(Context *context, ...@@ -3072,86 +3060,103 @@ bool ValidateDrawElementsCommon(Context *context,
context->validationError(GL_INVALID_OPERATION, kMustHaveElementArrayBinding); context->validationError(GL_INVALID_OPERATION, kMustHaveElementArrayBinding);
return false; return false;
} }
}
if (count > 0 && !elementArrayBuffer && !indices)
{
// This is an application error that would normally result in a crash, but we catch it and
// return an error
context->validationError(GL_INVALID_OPERATION, "No element array buffer and no pointer.");
return false;
}
if (count > 0 && elementArrayBuffer)
{
// The max possible type size is 8 and count is on 32 bits so doing the multiplication
// in a 64 bit integer is safe. Also we are guaranteed that here count > 0.
static_assert(std::is_same<int, GLsizei>::value, "GLsizei isn't the expected type");
constexpr uint64_t kMaxTypeSize = 8;
constexpr uint64_t kIntMax = std::numeric_limits<int>::max();
constexpr uint64_t kUint64Max = std::numeric_limits<uint64_t>::max();
static_assert(kIntMax < kUint64Max / kMaxTypeSize, "");
uint64_t typeSize = typeBytes;
uint64_t elementCount = static_cast<uint64_t>(count);
ASSERT(elementCount > 0 && typeSize <= kMaxTypeSize);
// Doing the multiplication here is overflow-safe
uint64_t elementDataSizeNoOffset = typeSize * elementCount;
// The offset can be any value, check for overflows
uint64_t offset = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(indices));
if (elementDataSizeNoOffset > kUint64Max - offset)
{
context->validationError(GL_INVALID_OPERATION, kIntegerOverflow);
return false;
}
uint64_t elementDataSizeWithOffset = elementDataSizeNoOffset + offset; if (elementArrayBuffer->isBoundForTransformFeedbackAndOtherUse())
if (elementDataSizeWithOffset > static_cast<uint64_t>(elementArrayBuffer->getSize()))
{ {
context->validationError(GL_INVALID_OPERATION, kInsufficientBufferSize); context->validationError(GL_INVALID_OPERATION,
kElementArrayBufferBoundForTransformFeedback);
return false; return false;
} }
}
ASSERT(isPow2(typeSize) && typeSize > 0); else
if ((elementArrayBuffer->getSize() & (typeSize - 1)) != 0) {
if (elementArrayBuffer)
{ {
context->validationError(GL_INVALID_OPERATION, kMismatchedByteCountType); if (elementArrayBuffer->isMapped())
return false; {
// WebGL buffers cannot be mapped/unmapped because the MapBufferRange,
// FlushMappedBufferRange, and UnmapBuffer entry points are removed from the
// WebGL 2.0 API. https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.14
context->validationError(GL_INVALID_OPERATION, "Index buffer is mapped.");
return false;
}
} }
else if (!context->getGLState().areClientArraysEnabled())
if (context->getExtensions().webglCompatibility &&
elementArrayBuffer->isBoundForTransformFeedbackAndOtherUse())
{ {
context->validationError(GL_INVALID_OPERATION, context->validationError(GL_INVALID_OPERATION, kMustHaveElementArrayBinding);
kElementArrayBufferBoundForTransformFeedback);
return false; return false;
} }
} }
if (!context->getExtensions().robustBufferAccessBehavior && count > 0 && primcount > 0) if (count > 0)
{ {
// Use the parameter buffer to retrieve and cache the index range. if (!elementArrayBuffer)
IndexRange indexRange;
ANGLE_VALIDATION_TRY(vao->getIndexRange(context, type, count, indices, &indexRange));
// If we use an index greater than our maximum supported index range, return an error.
// The ES3 spec does not specify behaviour here, it is undefined, but ANGLE should always
// return an error if possible here.
if (static_cast<GLuint64>(indexRange.end) >= context->getCaps().maxElementIndex)
{ {
context->validationError(GL_INVALID_OPERATION, kExceedsMaxElement); if (!indices)
return false; {
// This is an application error that would normally result in a crash, but we catch
// it and return an error
context->validationError(GL_INVALID_OPERATION,
"No element array buffer and no pointer.");
return false;
}
} }
else
if (!ValidateDrawAttribs(context, primcount, static_cast<GLint>(indexRange.end)))
{ {
return false; // The max possible type size is 8 and count is on 32 bits so doing the multiplication
// in a 64 bit integer is safe. Also we are guaranteed that here count > 0.
static_assert(std::is_same<int, GLsizei>::value, "GLsizei isn't the expected type");
constexpr uint64_t kMaxTypeSize = 8;
constexpr uint64_t kIntMax = std::numeric_limits<int>::max();
constexpr uint64_t kUint64Max = std::numeric_limits<uint64_t>::max();
static_assert(kIntMax < kUint64Max / kMaxTypeSize, "");
uint64_t typeSize = typeBytes;
uint64_t elementCount = static_cast<uint64_t>(count);
ASSERT(elementCount > 0 && typeSize <= kMaxTypeSize);
// Doing the multiplication here is overflow-safe
uint64_t elementDataSizeNoOffset = typeSize * elementCount;
// The offset can be any value, check for overflows
uint64_t offset = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(indices));
if (elementDataSizeNoOffset > kUint64Max - offset)
{
context->validationError(GL_INVALID_OPERATION, kIntegerOverflow);
return false;
}
uint64_t elementDataSizeWithOffset = elementDataSizeNoOffset + offset;
if (elementDataSizeWithOffset > static_cast<uint64_t>(elementArrayBuffer->getSize()))
{
context->validationError(GL_INVALID_OPERATION, kInsufficientBufferSize);
return false;
}
} }
// No op if there are no real indices in the index data (all are primitive restart). if (!context->getExtensions().robustBufferAccessBehavior && primcount > 0)
return (indexRange.vertexIndexCount > 0); {
// Use the parameter buffer to retrieve and cache the index range.
IndexRange indexRange;
ANGLE_VALIDATION_TRY(vao->getIndexRange(context, type, count, indices, &indexRange));
// If we use an index greater than our maximum supported index range, return an error.
// The ES3 spec does not specify behaviour here, it is undefined, but ANGLE should
// always return an error if possible here.
if (static_cast<GLuint64>(indexRange.end) >= context->getCaps().maxElementIndex)
{
context->validationError(GL_INVALID_OPERATION, kExceedsMaxElement);
return false;
}
if (!ValidateDrawAttribs(context, primcount, static_cast<GLint>(indexRange.end)))
{
return false;
}
// No op if there are no real indices in the index data (all are primitive restart).
return (indexRange.vertexIndexCount > 0);
}
} }
return true; return true;
......
...@@ -64,6 +64,12 @@ class DrawElementsTest : public ANGLETest ...@@ -64,6 +64,12 @@ class DrawElementsTest : public ANGLETest
GLuint mProgram; GLuint mProgram;
}; };
class WebGLDrawElementsTest : public DrawElementsTest
{
public:
WebGLDrawElementsTest() { setWebGLCompatibilityEnabled(true); }
};
// Test no error is generated when using client-side arrays, indices = nullptr and count = 0 // Test no error is generated when using client-side arrays, indices = nullptr and count = 0
TEST_P(DrawElementsTest, ClientSideNullptrArrayZeroCount) TEST_P(DrawElementsTest, ClientSideNullptrArrayZeroCount)
{ {
...@@ -260,7 +266,7 @@ TEST_P(DrawElementsTest, DeletingAfterStreamingIndexes) ...@@ -260,7 +266,7 @@ 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 that the offset in the index buffer is forced to be a multiple of the element size
TEST_P(DrawElementsTest, DrawElementsTypeAlignment) TEST_P(WebGLDrawElementsTest, DrawElementsTypeAlignment)
{ {
const std::string &vert = const std::string &vert =
"attribute vec3 a_pos;\n" "attribute vec3 a_pos;\n"
...@@ -297,7 +303,7 @@ TEST_P(DrawElementsTest, DrawElementsTypeAlignment) ...@@ -297,7 +303,7 @@ TEST_P(DrawElementsTest, DrawElementsTypeAlignment)
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, zeroIndices); glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, zeroIndices);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
const GLubyte indices2[] = {0, 0, 0, 0, 0}; const GLushort indices2[] = {0, 0, 0, 0, 0, 0};
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices2), indices2, GL_STATIC_DRAW); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices2), indices2, GL_STATIC_DRAW);
...@@ -306,4 +312,5 @@ TEST_P(DrawElementsTest, DrawElementsTypeAlignment) ...@@ -306,4 +312,5 @@ TEST_P(DrawElementsTest, DrawElementsTypeAlignment)
} }
ANGLE_INSTANTIATE_TEST(DrawElementsTest, ES3_OPENGL(), ES3_OPENGLES()); ANGLE_INSTANTIATE_TEST(DrawElementsTest, ES3_OPENGL(), ES3_OPENGLES());
ANGLE_INSTANTIATE_TEST(WebGLDrawElementsTest, ES2_OPENGL(), ES2_OPENGLES());
} // namespace } // namespace
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