mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 22:01:30 +00:00
b=570538; [webgl] fix up buffer validation with interleaved data; r=bjacob
This commit is contained in:
parent
6897f88628
commit
39732698ea
@ -221,28 +221,33 @@ struct WebGLVertexAttribData {
|
||||
GLenum type;
|
||||
PRBool enabled;
|
||||
|
||||
GLuint actualStride() const {
|
||||
if (stride) return stride;
|
||||
GLuint componentSize = 0;
|
||||
GLuint componentSize() const {
|
||||
switch(type) {
|
||||
case LOCAL_GL_BYTE:
|
||||
componentSize = sizeof(GLbyte);
|
||||
return sizeof(GLbyte);
|
||||
break;
|
||||
case LOCAL_GL_UNSIGNED_BYTE:
|
||||
componentSize = sizeof(GLubyte);
|
||||
return sizeof(GLubyte);
|
||||
break;
|
||||
case LOCAL_GL_SHORT:
|
||||
componentSize = sizeof(GLshort);
|
||||
return sizeof(GLshort);
|
||||
break;
|
||||
case LOCAL_GL_UNSIGNED_SHORT:
|
||||
componentSize = sizeof(GLushort);
|
||||
return sizeof(GLushort);
|
||||
break;
|
||||
// XXX case LOCAL_GL_FIXED:
|
||||
case LOCAL_GL_FLOAT:
|
||||
componentSize = sizeof(GLfloat);
|
||||
return sizeof(GLfloat);
|
||||
break;
|
||||
default:
|
||||
NS_ERROR("Should never get here!");
|
||||
return 0;
|
||||
}
|
||||
return size * componentSize;
|
||||
}
|
||||
|
||||
GLuint actualStride() const {
|
||||
if (stride) return stride;
|
||||
return size * componentSize();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -803,15 +803,21 @@ WebGLContext::DrawArrays(GLenum mode, WebGLint first, WebGLsizei count)
|
||||
return ErrorInvalidEnum("DrawArrays: invalid mode");
|
||||
}
|
||||
|
||||
if (first < 0 || count < 0 || first+count < first || first+count < count) {
|
||||
return ErrorInvalidValue("DrawArrays: overflow in first+count");
|
||||
}
|
||||
if (first < 0 || count < 0)
|
||||
return ErrorInvalidValue("DrawArrays: negative first or count");
|
||||
|
||||
// If count is 0, there's nothing to do.
|
||||
if (count == 0)
|
||||
return NS_OK;
|
||||
|
||||
// If there is no current program, this is silently ignored.
|
||||
// Any checks below this depend on a program being available.
|
||||
if (!mCurrentProgram)
|
||||
return NS_OK;
|
||||
|
||||
if (first+count < first || first+count < count)
|
||||
return ErrorInvalidOperation("DrawArrays: overflow in first+count");
|
||||
|
||||
if (!ValidateBuffers(first+count))
|
||||
return ErrorInvalidOperation("DrawArrays: bound vertex attribute buffers do not have sufficient data for given first and count");
|
||||
|
||||
@ -825,10 +831,8 @@ WebGLContext::DrawArrays(GLenum mode, WebGLint first, WebGLsizei count)
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebGLContext::DrawElements(WebGLenum mode, WebGLuint count, WebGLenum type, WebGLuint byteOffset)
|
||||
WebGLContext::DrawElements(WebGLenum mode, WebGLsizei count, WebGLenum type, WebGLint byteOffset)
|
||||
{
|
||||
int elementSize = 0;
|
||||
|
||||
switch (mode) {
|
||||
case LOCAL_GL_TRIANGLES:
|
||||
case LOCAL_GL_TRIANGLE_STRIP:
|
||||
@ -842,42 +846,47 @@ WebGLContext::DrawElements(WebGLenum mode, WebGLuint count, WebGLenum type, WebG
|
||||
return ErrorInvalidEnum("DrawElements: invalid mode");
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case LOCAL_GL_UNSIGNED_SHORT:
|
||||
elementSize = 2;
|
||||
if (byteOffset % 2 != 0)
|
||||
return ErrorInvalidValue("DrawElements: invalid byteOffset for UNSIGNED_SHORT (must be a multiple of 2)");
|
||||
break;
|
||||
if (count < 0 || byteOffset < 0)
|
||||
return ErrorInvalidValue("DrawElements: negative count or offset");
|
||||
|
||||
case LOCAL_GL_UNSIGNED_BYTE:
|
||||
elementSize = 1;
|
||||
break;
|
||||
WebGLuint byteCount;
|
||||
if (type == LOCAL_GL_UNSIGNED_SHORT) {
|
||||
byteCount = WebGLuint(count) << 1;
|
||||
if (byteCount >> 1 != WebGLuint(count))
|
||||
return ErrorInvalidValue("DrawElements: overflow in byteCount");
|
||||
|
||||
default:
|
||||
return ErrorInvalidEnum("DrawElements: type must be UNSIGNED_SHORT or UNSIGNED_BYTE");
|
||||
if (byteOffset % 2 != 0)
|
||||
return ErrorInvalidValue("DrawElements: invalid byteOffset for UNSIGNED_SHORT (must be a multiple of 2)");
|
||||
} else if (type == LOCAL_GL_UNSIGNED_BYTE) {
|
||||
byteCount = count;
|
||||
} else {
|
||||
return ErrorInvalidEnum("DrawElements: type must be UNSIGNED_SHORT or UNSIGNED_BYTE");
|
||||
}
|
||||
|
||||
if (!mBoundElementArrayBuffer)
|
||||
return ErrorInvalidOperation("DrawElements: must have element array buffer binding");
|
||||
|
||||
WebGLuint byteCount = count*elementSize;
|
||||
|
||||
if (count < 0 || byteOffset+byteCount < byteOffset || byteOffset+byteCount < byteCount)
|
||||
return ErrorInvalidValue("DrawElements: overflow in byteOffset+byteCount");
|
||||
|
||||
if (byteOffset + byteCount > mBoundElementArrayBuffer->ByteLength())
|
||||
return ErrorInvalidOperation("DrawElements: bound element array buffer is too small for given count and offset");
|
||||
// If count is 0, there's nothing to do.
|
||||
if (count == 0)
|
||||
return NS_OK;
|
||||
|
||||
// If there is no current program, this is silently ignored.
|
||||
// Any checks below this depend on a program being available.
|
||||
if (!mCurrentProgram)
|
||||
return NS_OK;
|
||||
|
||||
if (!mBoundElementArrayBuffer)
|
||||
return ErrorInvalidOperation("DrawElements: must have element array buffer binding");
|
||||
|
||||
if (byteOffset+byteCount < byteOffset || byteOffset+byteCount < byteCount)
|
||||
return ErrorInvalidOperation("DrawElements: overflow in byteOffset+byteCount");
|
||||
|
||||
if (byteOffset + byteCount > mBoundElementArrayBuffer->ByteLength())
|
||||
return ErrorInvalidOperation("DrawElements: bound element array buffer is too small for given count and offset");
|
||||
|
||||
WebGLuint maxIndex = 0;
|
||||
if (type == LOCAL_GL_UNSIGNED_SHORT)
|
||||
if (type == LOCAL_GL_UNSIGNED_SHORT) {
|
||||
maxIndex = mBoundElementArrayBuffer->FindMaximum<GLushort>(count, byteOffset);
|
||||
else if (type == LOCAL_GL_UNSIGNED_BYTE)
|
||||
} else if (type == LOCAL_GL_UNSIGNED_BYTE) {
|
||||
maxIndex = mBoundElementArrayBuffer->FindMaximum<GLubyte>(count, byteOffset);
|
||||
}
|
||||
|
||||
// maxIndex+1 because ValidateBuffers expects the number of elements needed
|
||||
if (!ValidateBuffers(maxIndex+1)) {
|
||||
@ -1032,16 +1041,21 @@ WebGLContext::GetActiveAttrib(nsIWebGLProgram *pobj, PRUint32 index, nsIWebGLAct
|
||||
|
||||
GLint len = 0;
|
||||
gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &len);
|
||||
if (len == 0)
|
||||
return NS_ERROR_FAILURE; // XXX GL error? This really shouldn't happen.
|
||||
if (len == 0) {
|
||||
// is this an error? can you have a program with no attributes?
|
||||
*retval = nsnull;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsAutoArrayPtr<char> name(new char[len+1]);
|
||||
PRInt32 attrsize = 0;
|
||||
PRUint32 attrtype = 0;
|
||||
|
||||
gl->fGetActiveAttrib(progname, index, len+1, &len, (GLint*) &attrsize, (WebGLuint*) &attrtype, name);
|
||||
if (attrsize == 0 || attrtype == 0)
|
||||
return NS_ERROR_FAILURE;
|
||||
if (attrsize == 0 || attrtype == 0) {
|
||||
*retval = nsnull;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
JSObjectHelper retobj(&js);
|
||||
retobj.DefineProperty("size", attrsize);
|
||||
|
@ -51,6 +51,8 @@ WebGLContext::ValidateBuffers(PRUint32 count)
|
||||
GLint currentProgram = -1;
|
||||
GLint numAttributes = -1;
|
||||
|
||||
NS_ENSURE_TRUE(count > 0, PR_TRUE);
|
||||
|
||||
MakeContextCurrent();
|
||||
|
||||
// XXX cache this per program
|
||||
@ -93,7 +95,10 @@ WebGLContext::ValidateBuffers(PRUint32 count)
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
WebGLuint needed = vd.byteOffset + vd.actualStride() * count;
|
||||
WebGLuint needed = vd.byteOffset + // the base offset
|
||||
vd.actualStride() * (count-1) + // to stride to the start of the last element group
|
||||
vd.componentSize() * vd.size; // and the number of bytes needed for these components
|
||||
|
||||
if (vd.buf->ByteLength() < needed) {
|
||||
LogMessage("VBO too small for bound attrib index %d: need at least %d bytes, but have only %d", i, needed, vd.buf->ByteLength());
|
||||
return PR_FALSE;
|
||||
|
@ -50,10 +50,10 @@ shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(co
|
||||
shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, 0, 10000)");
|
||||
shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, 0, 10000000000000)");
|
||||
shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, 0, -1)");
|
||||
shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, 1, 0)");
|
||||
shouldGenerateGLError(context, context.NO_ERROR, "context.drawArrays(context.TRIANGLES, 1, 0)");
|
||||
shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, -1, 0)");
|
||||
shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, 1, -1)");
|
||||
shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, -1, 1)");
|
||||
shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, 1, -1)");
|
||||
shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, -1, 1)");
|
||||
|
||||
debug("")
|
||||
debug("Test buffer with 3 float vectors")
|
||||
@ -65,8 +65,57 @@ shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(co
|
||||
shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, 0, 10000000000000)");
|
||||
shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, 0, -1)");
|
||||
shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, -1, 0)");
|
||||
shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, 1, -1)");
|
||||
shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, -1, 1)");
|
||||
shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, 1, -1)");
|
||||
shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, -1, 1)");
|
||||
|
||||
debug("")
|
||||
debug("Test buffer with interleaved (3+2) float vectors")
|
||||
|
||||
var program2 = createProgram(context,
|
||||
"attribute vec3 aOne;" +
|
||||
"attribute vec2 aTwo;" +
|
||||
"void main() { gl_Position = vec4(aOne, 1.0) + vec4(aTwo, 0.0, 1.0); }",
|
||||
"void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); }",
|
||||
[ "aOne", "aTwo" ]);
|
||||
if (!program2) {
|
||||
testFailed("failed to create test program");
|
||||
}
|
||||
|
||||
context.useProgram(program2);
|
||||
|
||||
var vbo = context.createBuffer();
|
||||
context.bindBuffer(context.ARRAY_BUFFER, vbo);
|
||||
// enough for 9 vertices, so 3 triangles
|
||||
context.bufferData(context.ARRAY_BUFFER, new WebGLFloatArray(9*5), context.STATIC_DRAW);
|
||||
|
||||
// bind first 3 elements, with a stride of 5 float elements
|
||||
context.vertexAttribPointer(0, 3, context.FLOAT, false, 5*4, 0);
|
||||
// bind 2 elements, starting after the first 3; same stride of 5 float elements
|
||||
context.vertexAttribPointer(1, 2, context.FLOAT, false, 5*4, 3*4);
|
||||
|
||||
context.enableVertexAttribArray(0);
|
||||
context.enableVertexAttribArray(1);
|
||||
|
||||
shouldGenerateGLError(context, context.NO_ERROR, "context.drawArrays(context.TRIANGLES, 0, 9)");
|
||||
|
||||
// negative values must generate INVALID_VALUE; they can never be valid
|
||||
shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, 0, -500)");
|
||||
shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, -200, 1)");
|
||||
shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, -200, -500)");
|
||||
|
||||
// 0xffffffff needs to convert to a 'long' IDL argument as -1, as per
|
||||
// WebIDL 4.1.7. JS ToInt32(0xffffffff) == -1, which is the first step
|
||||
// of the conversion. Thus INVALID_VALUE.
|
||||
shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, 0, 0xffffffff)");
|
||||
shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, 0xffffffff, 1)");
|
||||
shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, 0xffffffff, 0xffffffff)");
|
||||
|
||||
// values that could otherwise be valid but aren't due to bindings generate
|
||||
// INVALID_OPERATION
|
||||
shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, 0, 200)");
|
||||
shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, 0, 0x7fffffff)");
|
||||
shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, 0x7fffffff, 1)");
|
||||
shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, 0x7fffffff, 0x7fffffff)");
|
||||
|
||||
debug("")
|
||||
successfullyParsed = true;
|
||||
|
@ -54,9 +54,9 @@ shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(
|
||||
shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(context.TRIANGLES, 10000, context.UNSIGNED_BYTE, 0)");
|
||||
shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(context.TRIANGLES, 10000000000000, context.UNSIGNED_BYTE, 0)");
|
||||
shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(context.TRIANGLES, 1, context.UNSIGNED_BYTE, 0)");
|
||||
shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(context.TRIANGLES, 0, context.UNSIGNED_BYTE, -1)");
|
||||
shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawElements(context.TRIANGLES, 0, context.UNSIGNED_BYTE, -1)");
|
||||
shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawElements(context.TRIANGLES, -1, context.UNSIGNED_BYTE, 1)");
|
||||
shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(context.TRIANGLES, 1, context.UNSIGNED_BYTE, -1)");
|
||||
shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawElements(context.TRIANGLES, 1, context.UNSIGNED_BYTE, -1)");
|
||||
|
||||
debug("")
|
||||
debug("Test buffer with 3 byte indexes")
|
||||
@ -70,6 +70,61 @@ shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawElements(cont
|
||||
shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawElements(context.TRIANGLES, -1, context.UNSIGNED_BYTE, 1)");
|
||||
shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawElements(context.TRIANGLES, 1, context.UNSIGNED_BYTE, -1)");
|
||||
|
||||
debug("")
|
||||
debug("Test buffer with interleaved (3+2) float vectors")
|
||||
|
||||
var program2 = createProgram(context,
|
||||
"attribute vec3 aOne;" +
|
||||
"attribute vec2 aTwo;" +
|
||||
"void main() { gl_Position = vec4(aOne, 1.0) + vec4(aTwo, 0.0, 1.0); }",
|
||||
"void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); }",
|
||||
[ "aOne", "aTwo" ]);
|
||||
if (!program2) {
|
||||
testFailed("failed to create test program");
|
||||
}
|
||||
|
||||
context.useProgram(program2);
|
||||
|
||||
var vbo = context.createBuffer();
|
||||
context.bindBuffer(context.ARRAY_BUFFER, vbo);
|
||||
// enough for 9 vertices, so 3 triangles
|
||||
context.bufferData(context.ARRAY_BUFFER, new WebGLFloatArray(9*5), context.STATIC_DRAW);
|
||||
|
||||
// bind first 3 elements, with a stride of 5 float elements
|
||||
context.vertexAttribPointer(0, 3, context.FLOAT, false, 5*4, 0);
|
||||
// bind 2 elements, starting after the first 3; same stride of 5 float elements
|
||||
context.vertexAttribPointer(1, 2, context.FLOAT, false, 5*4, 3*4);
|
||||
|
||||
context.enableVertexAttribArray(0);
|
||||
context.enableVertexAttribArray(1);
|
||||
|
||||
var ebo = context.createBuffer();
|
||||
context.bindBuffer(context.ELEMENT_ARRAY_BUFFER, ebo);
|
||||
context.bufferData(context.ELEMENT_ARRAY_BUFFER, new WebGLUnsignedShortArray([ 0, 1, 2,
|
||||
1, 2, 0,
|
||||
2, 0, 1,
|
||||
200, 200, 200,
|
||||
0x7fff, 0x7fff, 0x7fff,
|
||||
0xffff, 0xffff, 0xffff ]),
|
||||
context.STATIC_DRAW);
|
||||
|
||||
shouldGenerateGLError(context, context.NO_ERROR, "context.drawElements(context.TRIANGLES, 9, context.UNSIGNED_SHORT, 0)");
|
||||
|
||||
// invalid type arguments
|
||||
shouldGenerateGLError(context, context.INVALID_ENUM, "context.drawElements(context.TRIANGLES, 9, context.FLOAT, 0)");
|
||||
shouldGenerateGLError(context, context.INVALID_ENUM, "context.drawElements(context.TRIANGLES, 9, context.SHORT, 0)");
|
||||
shouldGenerateGLError(context, context.INVALID_ENUM, "context.drawElements(context.TRIANGLES, 9, context.UNSIGNED_INT, 0)");
|
||||
|
||||
// invalid operation with indices that would be valid with correct bindings
|
||||
shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(context.TRIANGLES, 9, context.UNSIGNED_SHORT, 1000)");
|
||||
shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(context.TRIANGLES, 12, context.UNSIGNED_SHORT, 0)");
|
||||
shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(context.TRIANGLES, 15, context.UNSIGNED_SHORT, 0)");
|
||||
shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(context.TRIANGLES, 18, context.UNSIGNED_SHORT, 0)");
|
||||
shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(context.TRIANGLES, 3, context.UNSIGNED_SHORT, 2*15)");
|
||||
|
||||
shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawElements(context.TRIANGLES, 0xffffffff, context.UNSIGNED_SHORT, 0)");
|
||||
shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(context.TRIANGLES, 0x7fffffff, context.UNSIGNED_SHORT, 0)");
|
||||
|
||||
debug("")
|
||||
successfullyParsed = true;
|
||||
</script>
|
||||
|
@ -638,7 +638,7 @@ interface nsICanvasRenderingContextWebGL : nsISupports
|
||||
void drawArrays(in WebGLenum mode, in WebGLint first, in WebGLsizei count);
|
||||
|
||||
// Modified: void glDrawElements(WebGLenum mode, WebGLsizei count, WebGLenum type, const void* indices);
|
||||
void drawElements(in WebGLenum mode, in WebGLuint count, in WebGLenum type, in WebGLuint offset);
|
||||
void drawElements(in WebGLenum mode, in WebGLsizei count, in WebGLenum type, in WebGLint offset);
|
||||
|
||||
void enable(in WebGLenum cap);
|
||||
void enableVertexAttribArray(in WebGLuint index);
|
||||
|
Loading…
Reference in New Issue
Block a user