Bug 569431 - Optimization in DrawElements - r=vlad, a=joe

This commit is contained in:
Benoit Jacob 2011-01-25 22:19:46 -05:00
Родитель 1affd5b309
Коммит 0d41b3540e
3 изменённых файлов: 103 добавлений и 35 удалений

Просмотреть файл

@ -420,7 +420,7 @@ protected:
PRBool SafeToCreateCanvas3DContext(nsHTMLCanvasElement *canvasElement);
PRBool InitAndValidateGL();
PRBool ValidateBuffers(PRUint32 count);
PRBool ValidateBuffers(PRInt32* maxAllowedCount, const char *info);
PRBool ValidateCapabilityEnum(WebGLenum cap, const char *info);
PRBool ValidateBlendEquationEnum(WebGLenum cap, const char *info);
PRBool ValidateBlendFuncDstEnum(WebGLenum mode, const char *info);
@ -723,7 +723,7 @@ public:
// the buffer starting at given offset, consisting of given count of elements. The type T is the type
// to interprete the array elements as, must be GLushort or GLubyte.
template<typename T>
T FindMaximum(GLuint count, GLuint byteOffset)
PRInt32 FindMaxElementInSubArray(GLuint count, GLuint byteOffset)
{
const T* start = reinterpret_cast<T*>(reinterpret_cast<size_t>(mData) + byteOffset);
const T* stop = start + count;
@ -734,6 +734,31 @@ public:
return result;
}
void InvalidateCachedMaxElements() {
mHasCachedMaxUbyteElement = PR_FALSE;
mHasCachedMaxUshortElement = PR_FALSE;
}
PRInt32 FindMaxUbyteElement() {
if (mHasCachedMaxUbyteElement) {
return mCachedMaxUbyteElement;
} else {
mHasCachedMaxUbyteElement = PR_TRUE;
mCachedMaxUbyteElement = FindMaxElementInSubArray<GLubyte>(mByteLength, 0);
return mCachedMaxUbyteElement;
}
}
PRInt32 FindMaxUshortElement() {
if (mHasCachedMaxUshortElement) {
return mCachedMaxUshortElement;
} else {
mHasCachedMaxUshortElement = PR_TRUE;
mCachedMaxUshortElement = FindMaxElementInSubArray<GLshort>(mByteLength>>1, 0);
return mCachedMaxUshortElement;
}
}
NS_DECL_ISUPPORTS
NS_DECL_NSIWEBGLBUFFER
protected:
@ -741,6 +766,12 @@ protected:
PRBool mDeleted;
GLuint mByteLength;
GLenum mTarget;
PRUint8 mCachedMaxUbyteElement;
PRBool mHasCachedMaxUbyteElement;
PRUint16 mCachedMaxUshortElement;
PRBool mHasCachedMaxUshortElement;
void* mData; // in the case of an Element Array Buffer, we keep a copy.
};

Просмотреть файл

@ -394,6 +394,7 @@ WebGLContext::BufferData_size(WebGLenum target, WebGLsizei size, WebGLenum usage
boundBuffer->SetByteLength(size);
boundBuffer->ZeroDataIfElementArray();
boundBuffer->InvalidateCachedMaxElements();
gl->fBufferData(target, size, 0, usage);
@ -423,6 +424,7 @@ WebGLContext::BufferData_buf(WebGLenum target, js::ArrayBuffer *wb, WebGLenum us
boundBuffer->SetByteLength(wb->byteLength);
boundBuffer->CopyDataIfElementArray(wb->data);
boundBuffer->InvalidateCachedMaxElements();
gl->fBufferData(target, wb->byteLength, wb->data, usage);
@ -452,6 +454,7 @@ WebGLContext::BufferData_array(WebGLenum target, js::TypedArray *wa, WebGLenum u
boundBuffer->SetByteLength(wa->byteLength);
boundBuffer->CopyDataIfElementArray(wa->data);
boundBuffer->InvalidateCachedMaxElements();
gl->fBufferData(target, wa->byteLength, wa->data, usage);
@ -497,6 +500,7 @@ WebGLContext::BufferSubData_buf(GLenum target, WebGLsizei byteOffset, js::ArrayB
MakeContextCurrent();
boundBuffer->CopySubDataIfElementArray(byteOffset, wb->byteLength, wb->data);
boundBuffer->InvalidateCachedMaxElements();
gl->fBufferSubData(target, byteOffset, wb->byteLength, wb->data);
@ -530,6 +534,7 @@ WebGLContext::BufferSubData_array(WebGLenum target, WebGLsizei byteOffset, js::T
MakeContextCurrent();
boundBuffer->CopySubDataIfElementArray(byteOffset, wa->byteLength, wa->data);
boundBuffer->InvalidateCachedMaxElements();
gl->fBufferSubData(target, byteOffset, wa->byteLength, wa->data);
@ -1226,13 +1231,17 @@ WebGLContext::DrawArrays(GLenum mode, WebGLint first, WebGLsizei count)
if (!mCurrentProgram)
return NS_OK;
PRInt32 maxAllowedCount = 0;
if (!ValidateBuffers(&maxAllowedCount, "drawArrays"))
return NS_OK;
CheckedInt32 checked_firstPlusCount = CheckedInt32(first) + count;
if (!checked_firstPlusCount.valid())
return ErrorInvalidOperation("drawArrays: overflow in first+count");
if (!ValidateBuffers(checked_firstPlusCount.value()))
return ErrorInvalidOperation("DrawArrays: bound vertex attribute buffers do not have sufficient size for given first and count");
if (checked_firstPlusCount.value() > maxAllowedCount)
return ErrorInvalidOperation("drawArrays: bound vertex attribute buffers do not have sufficient size for given first and count");
MakeContextCurrent();
@ -1299,22 +1308,37 @@ WebGLContext::DrawElements(WebGLenum mode, WebGLsizei count, WebGLenum type, Web
if (checked_neededByteCount.value() > 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) {
maxIndex = mBoundElementArrayBuffer->FindMaximum<GLushort>(count, byteOffset);
} else if (type == LOCAL_GL_UNSIGNED_BYTE) {
maxIndex = mBoundElementArrayBuffer->FindMaximum<GLubyte>(count, byteOffset);
}
PRInt32 maxAllowedCount = 0;
if (!ValidateBuffers(&maxAllowedCount, "drawElements"))
return NS_OK;
// maxIndex+1 because ValidateBuffers expects the number of elements needed.
// it is very important here to check tha maxIndex+1 doesn't overflow, otherwise the buffer validation is bypassed !!!
// maxIndex is a WebGLuint, ValidateBuffers takes a PRUint32, we validate maxIndex+1 as a PRUint32.
CheckedUint32 checked_neededCount = CheckedUint32(maxIndex) + 1;
if (!checked_neededCount.valid())
return ErrorInvalidOperation("drawElements: overflow in maxIndex+1");
if (!ValidateBuffers(checked_neededCount.value())) {
return ErrorInvalidOperation("DrawElements: bound vertex attribute buffers do not have sufficient "
"size for given indices from the bound element array");
PRInt32 maxIndex
= type == LOCAL_GL_UNSIGNED_SHORT
? mBoundElementArrayBuffer->FindMaxUshortElement()
: mBoundElementArrayBuffer->FindMaxUbyteElement();
CheckedInt32 checked_maxIndexPlusOne = CheckedInt32(maxIndex) + 1;
if (!checked_maxIndexPlusOne.valid() ||
checked_maxIndexPlusOne.value() > maxAllowedCount)
{
// the index array contains invalid indices for the current drawing state, but they
// might not be used by the present drawElements call, depending on first and count.
PRInt32 maxIndexInSubArray
= type == LOCAL_GL_UNSIGNED_SHORT
? mBoundElementArrayBuffer->FindMaxElementInSubArray<GLushort>(count, byteOffset)
: mBoundElementArrayBuffer->FindMaxElementInSubArray<GLubyte>(count, byteOffset);
CheckedInt32 checked_maxIndexInSubArrayPlusOne = CheckedInt32(maxIndexInSubArray) + 1;
if (!checked_maxIndexInSubArrayPlusOne.valid() ||
checked_maxIndexInSubArrayPlusOne.value() > maxAllowedCount)
{
return ErrorInvalidOperation(
"DrawElements: bound vertex attribute buffers do not have sufficient "
"size for given indices from the bound element array");
}
}
MakeContextCurrent();
@ -1323,7 +1347,7 @@ WebGLContext::DrawElements(WebGLenum mode, WebGLsizei count, WebGLenum type, Web
return NS_OK;
BindFakeBlackTextures();
DoFakeVertexAttrib0(checked_neededCount.value());
DoFakeVertexAttrib0(checked_maxIndexPlusOne.value());
gl->fDrawElements(mode, count, type, (GLvoid*) (byteOffset));

Просмотреть файл

@ -83,14 +83,13 @@ WebGLProgram::UpdateInfo(gl::GLContext *gl)
}
/*
* Verify that we can read count consecutive elements from each bound VBO.
* Verify that state is consistent for drawing, and compute max number of elements (maxAllowedCount)
* that will be legal to be read from bound VBOs.
*/
PRBool
WebGLContext::ValidateBuffers(PRUint32 count)
WebGLContext::ValidateBuffers(PRInt32 *maxAllowedCount, const char *info)
{
NS_ENSURE_TRUE(count > 0, PR_TRUE);
#ifdef DEBUG
GLuint currentProgram = 0;
MakeContextCurrent();
@ -101,6 +100,8 @@ WebGLContext::ValidateBuffers(PRUint32 count)
return PR_FALSE;
#endif
*maxAllowedCount = -1;
PRUint32 attribs = mAttribBuffers.Length();
for (PRUint32 i = 0; i < attribs; ++i) {
const WebGLVertexAttribData& vd = mAttribBuffers[i];
@ -111,7 +112,7 @@ WebGLContext::ValidateBuffers(PRUint32 count)
continue;
if (vd.buf == nsnull) {
LogMessageIfVerbose("No VBO bound to enabled attrib index %d!", i);
ErrorInvalidOperation("%s: no VBO bound to enabled vertex attrib index %d!", info, i);
return PR_FALSE;
}
@ -120,20 +121,32 @@ WebGLContext::ValidateBuffers(PRUint32 count)
if (!mCurrentProgram->IsAttribInUse(i))
continue;
// compute the number of bytes we actually need
CheckedUint32 checked_needed = CheckedUint32(vd.byteOffset) + // the base offset
CheckedUint32(vd.actualStride()) * (count-1) + // to stride to the start of the last element group
CheckedUint32(vd.componentSize()) * vd.size; // and the number of bytes needed for these components
// the base offset
CheckedUint32 checked_byteLength
= CheckedUint32(vd.buf->ByteLength()) - vd.byteOffset;
CheckedUint32 checked_sizeOfLastElement
= CheckedUint32(vd.componentSize()) * vd.size;
if (!checked_needed.valid()) {
LogMessageIfVerbose("Integer overflow computing the size of bound vertex attrib buffer at index %d", i);
return PR_FALSE;
if (!checked_byteLength.valid() ||
!checked_sizeOfLastElement.valid())
{
ErrorInvalidOperation("%s: integer overflow occured while checking vertex attrib %d", info, i);
return PR_FALSE;
}
if (vd.buf->ByteLength() < checked_needed.value()) {
LogMessageIfVerbose("VBO too small for bound attrib index %d: need at least %d bytes, but have only %d",
i, checked_needed.value(), vd.buf->ByteLength());
if (checked_byteLength.value() < checked_sizeOfLastElement.value()) {
*maxAllowedCount = 0;
} else {
CheckedUint32 checked_maxAllowedCount
= ((checked_byteLength - checked_sizeOfLastElement) / vd.actualStride()) + 1;
if (!checked_maxAllowedCount.valid()) {
ErrorInvalidOperation("%s: integer overflow occured while checking vertex attrib %d", info, i);
return PR_FALSE;
}
if (*maxAllowedCount == -1 || *maxAllowedCount > checked_maxAllowedCount.value())
*maxAllowedCount = checked_maxAllowedCount.value();
}
}