зеркало из https://github.com/mozilla/gecko-dev.git
Bug 569431 - Optimization in DrawElements - r=vlad, a=joe
This commit is contained in:
Родитель
1affd5b309
Коммит
0d41b3540e
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче