From c0d67d37f58f1ac828aef7ee9becac261b01ef73 Mon Sep 17 00:00:00 2001 From: Jeff Gilbert Date: Mon, 17 Mar 2014 10:52:56 -0400 Subject: [PATCH] Bug 982960 - Use glDrawRangeElements in WebGL.drawElements. r=kamidphish --- content/canvas/src/WebGLBuffer.cpp | 20 ++++- content/canvas/src/WebGLBuffer.h | 18 ++--- content/canvas/src/WebGLContext.h | 3 +- content/canvas/src/WebGLContextVertices.cpp | 50 ++++++++---- content/canvas/src/WebGLElementArrayCache.cpp | 79 +++++++++++++++---- content/canvas/src/WebGLElementArrayCache.h | 6 +- gfx/gl/GLContextFeatures.cpp | 2 +- 7 files changed, 129 insertions(+), 49 deletions(-) diff --git a/content/canvas/src/WebGLBuffer.cpp b/content/canvas/src/WebGLBuffer.cpp index 7703e1b0d1e4..523675f0f986 100644 --- a/content/canvas/src/WebGLBuffer.cpp +++ b/content/canvas/src/WebGLBuffer.cpp @@ -4,9 +4,11 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "WebGLBuffer.h" -#include "WebGLContext.h" + #include "GLContext.h" #include "mozilla/dom/WebGLRenderingContextBinding.h" +#include "WebGLContext.h" +#include "WebGLElementArrayCache.h" using namespace mozilla; @@ -55,6 +57,22 @@ WebGLBuffer::ElementArrayCacheBufferSubData(size_t pos, const void* ptr, size_t mCache->BufferSubData(pos, ptr, update_size_in_bytes); } +size_t +WebGLBuffer::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const +{ + size_t sizeOfCache = mCache ? mCache->SizeOfIncludingThis(aMallocSizeOf) : 0; + return aMallocSizeOf(this) + sizeOfCache; +} + +bool +WebGLBuffer::Validate(GLenum type, uint32_t max_allowed, + size_t first, size_t count, + uint32_t* out_upperBound) +{ + return mCache->Validate(type, max_allowed, first, count, out_upperBound); +} + + JSObject* WebGLBuffer::WrapObject(JSContext *cx, JS::Handle scope) { return dom::WebGLBufferBinding::Wrap(cx, scope, this); diff --git a/content/canvas/src/WebGLBuffer.h b/content/canvas/src/WebGLBuffer.h index a653bd74839b..f865ce8b7252 100644 --- a/content/canvas/src/WebGLBuffer.h +++ b/content/canvas/src/WebGLBuffer.h @@ -6,14 +6,12 @@ #ifndef WEBGLBUFFER_H_ #define WEBGLBUFFER_H_ -#include "WebGLObjectModel.h" -#include "WebGLElementArrayCache.h" #include "GLDefs.h" - -#include "nsWrapperCache.h" - #include "mozilla/LinkedList.h" #include "mozilla/MemoryReporting.h" +#include "nsWrapperCache.h" +#include "WebGLObjectModel.h" +#include "WebGLTypes.h" namespace mozilla { @@ -32,10 +30,7 @@ public: void Delete(); - size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const { - size_t sizeOfCache = mCache ? mCache->SizeOfIncludingThis(aMallocSizeOf) : 0; - return aMallocSizeOf(this) + sizeOfCache; - } + size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; bool HasEverBeenBound() { return mHasEverBeenBound; } void SetHasEverBeenBound(bool x) { mHasEverBeenBound = x; } @@ -51,9 +46,8 @@ public: void ElementArrayCacheBufferSubData(size_t pos, const void* ptr, size_t update_size_in_bytes); - bool Validate(GLenum type, uint32_t max_allowed, size_t first, size_t count) { - return mCache->Validate(type, max_allowed, first, count); - } + bool Validate(GLenum type, uint32_t max_allowed, size_t first, size_t count, + uint32_t* out_upperBound); WebGLContext *GetParentObject() const { return Context(); diff --git a/content/canvas/src/WebGLContext.h b/content/canvas/src/WebGLContext.h index 2c56966b44dc..81e2ac6921d6 100644 --- a/content/canvas/src/WebGLContext.h +++ b/content/canvas/src/WebGLContext.h @@ -787,7 +787,8 @@ private: bool DrawArrays_check(GLint first, GLsizei count, GLsizei primcount, const char* info); bool DrawElements_check(GLsizei count, GLenum type, WebGLintptr byteOffset, - GLsizei primcount, const char* info); + GLsizei primcount, const char* info, + GLuint* out_upperBound = nullptr); bool DrawInstanced_check(const char* info); void Draw_cleanup(); diff --git a/content/canvas/src/WebGLContextVertices.cpp b/content/canvas/src/WebGLContextVertices.cpp index 51737375174b..e7786c0966f4 100644 --- a/content/canvas/src/WebGLContextVertices.cpp +++ b/content/canvas/src/WebGLContextVertices.cpp @@ -4,16 +4,18 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "WebGLContext.h" -#include "WebGLBuffer.h" -#include "WebGLVertexAttribData.h" -#include "WebGLVertexArray.h" -#include "WebGLTexture.h" -#include "WebGLRenderbuffer.h" -#include "WebGLFramebuffer.h" -#include "WebGLUniformInfo.h" -#include "WebGLShader.h" -#include "WebGLProgram.h" + #include "GLContext.h" +#include "mozilla/CheckedInt.h" +#include "WebGLBuffer.h" +#include "WebGLFramebuffer.h" +#include "WebGLProgram.h" +#include "WebGLRenderbuffer.h" +#include "WebGLShader.h" +#include "WebGLTexture.h" +#include "WebGLUniformInfo.h" +#include "WebGLVertexArray.h" +#include "WebGLVertexAttribData.h" using namespace mozilla; using namespace dom; @@ -554,7 +556,9 @@ WebGLContext::DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsiz } bool -WebGLContext::DrawElements_check(GLsizei count, GLenum type, WebGLintptr byteOffset, GLsizei primcount, const char* info) +WebGLContext::DrawElements_check(GLsizei count, GLenum type, + WebGLintptr byteOffset, GLsizei primcount, + const char* info, GLuint* out_upperBound) { if (count < 0 || byteOffset < 0) { ErrorInvalidValue("%s: negative count or offset", info); @@ -620,7 +624,9 @@ WebGLContext::DrawElements_check(GLsizei count, GLenum type, WebGLintptr byteOff return false; } - if (!mBoundVertexArray->mBoundElementArrayBuffer->ByteLength()) { + WebGLBuffer& elemArrayBuffer = *mBoundVertexArray->mBoundElementArrayBuffer; + + if (!elemArrayBuffer.ByteLength()) { ErrorInvalidOperation("%s: bound element array buffer doesn't have any data", info); return false; } @@ -632,7 +638,7 @@ WebGLContext::DrawElements_check(GLsizei count, GLenum type, WebGLintptr byteOff return false; } - if (uint32_t(checked_neededByteCount.value()) > mBoundVertexArray->mBoundElementArrayBuffer->ByteLength()) { + if (uint32_t(checked_neededByteCount.value()) > elemArrayBuffer.ByteLength()) { ErrorInvalidOperation("%s: bound element array buffer is too small for given count and offset", info); return false; } @@ -641,7 +647,7 @@ WebGLContext::DrawElements_check(GLsizei count, GLenum type, WebGLintptr byteOff return false; if (!mMaxFetchedVertices || - !mBoundVertexArray->mBoundElementArrayBuffer->Validate(type, mMaxFetchedVertices - 1, first, count)) + !elemArrayBuffer.Validate(type, mMaxFetchedVertices - 1, first, count, out_upperBound)) { ErrorInvalidOperation( "%s: bound vertex attribute buffers do not have sufficient " @@ -673,7 +679,7 @@ WebGLContext::DrawElements_check(GLsizei count, GLenum type, WebGLintptr byteOff void WebGLContext::DrawElements(GLenum mode, GLsizei count, GLenum type, - WebGLintptr byteOffset) + WebGLintptr byteOffset) { if (IsContextLost()) return; @@ -681,18 +687,28 @@ WebGLContext::DrawElements(GLenum mode, GLsizei count, GLenum type, if (!ValidateDrawModeEnum(mode, "drawElements: mode")) return; - if (!DrawElements_check(count, type, byteOffset, 1, "drawElements")) + GLuint upperBound = UINT_MAX; + if (!DrawElements_check(count, type, byteOffset, 1, "drawElements", + &upperBound)) + { return; + } SetupContextLossTimer(); - gl->fDrawElements(mode, count, type, reinterpret_cast(byteOffset)); + + if (gl->IsSupported(gl::GLFeature::draw_range_elements)) { + gl->fDrawRangeElements(mode, 0, upperBound, + count, type, reinterpret_cast(byteOffset)); + } else { + gl->fDrawElements(mode, count, type, reinterpret_cast(byteOffset)); + } Draw_cleanup(); } void WebGLContext::DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, - WebGLintptr byteOffset, GLsizei primcount) + WebGLintptr byteOffset, GLsizei primcount) { if (IsContextLost()) return; diff --git a/content/canvas/src/WebGLElementArrayCache.cpp b/content/canvas/src/WebGLElementArrayCache.cpp index af03ec4567e2..03b855b67e2f 100644 --- a/content/canvas/src/WebGLElementArrayCache.cpp +++ b/content/canvas/src/WebGLElementArrayCache.cpp @@ -16,6 +16,24 @@ namespace mozilla { +static void +SetUpperBound(uint32_t* out_upperBound, uint32_t newBound) +{ + if (!out_upperBound) + return; + + *out_upperBound = newBound; +} + +static void +UpdateUpperBound(uint32_t* out_upperBound, uint32_t newBound) +{ + if (!out_upperBound) + return; + + *out_upperBound = std::max(*out_upperBound, newBound); +} + /* * WebGLElementArrayCacheTree contains most of the implementation of WebGLElementArrayCache, * which performs WebGL element array buffer validation for drawElements. @@ -227,7 +245,9 @@ public: return ((numElements - 1) | sElementsPerLeafMask) + 1; } - bool Validate(T maxAllowed, size_t firstLeaf, size_t lastLeaf) { + bool Validate(T maxAllowed, size_t firstLeaf, size_t lastLeaf, + uint32_t* out_upperBound) + { MOZ_ASSERT(!mInvalidated); size_t firstTreeIndex = TreeIndexForLeaf(firstLeaf); @@ -240,13 +260,17 @@ public: // final case where there is only 1 node to validate at the current tree level if (lastTreeIndex == firstTreeIndex) { - return mTreeData[firstTreeIndex] <= maxAllowed; + const T& curData = mTreeData[firstTreeIndex]; + UpdateUpperBound(out_upperBound, curData); + return curData <= maxAllowed; } // if the first node at current tree level is a right node, handle it individually // and replace it with its right neighbor, which is a left node if (IsRightNode(firstTreeIndex)) { - if (mTreeData[firstTreeIndex] > maxAllowed) + const T& curData = mTreeData[firstTreeIndex]; + UpdateUpperBound(out_upperBound, curData); + if (curData > maxAllowed) return false; firstTreeIndex = RightNeighborNode(firstTreeIndex); } @@ -254,7 +278,9 @@ public: // if the last node at current tree level is a left node, handle it individually // and replace it with its left neighbor, which is a right node if (IsLeftNode(lastTreeIndex)) { - if (mTreeData[lastTreeIndex] > maxAllowed) + const T& curData = mTreeData[lastTreeIndex]; + UpdateUpperBound(out_upperBound, curData); + if (curData > maxAllowed) return false; lastTreeIndex = LeftNeighborNode(lastTreeIndex); } @@ -490,10 +516,18 @@ void WebGLElementArrayCache::InvalidateTrees(size_t firstByte, size_t lastByte) } template -bool WebGLElementArrayCache::Validate(uint32_t maxAllowed, size_t firstElement, size_t countElements) { +bool +WebGLElementArrayCache::Validate(uint32_t maxAllowed, size_t firstElement, + size_t countElements, uint32_t* out_upperBound) +{ + SetUpperBound(out_upperBound, 0); + // if maxAllowed is >= the max T value, then there is no way that a T index could be invalid - if (maxAllowed >= std::numeric_limits::max()) + uint32_t maxTSize = std::numeric_limits::max(); + if (maxAllowed >= maxTSize) { + SetUpperBound(out_upperBound, maxTSize); return true; + } T maxAllowedT(maxAllowed); @@ -515,8 +549,10 @@ bool WebGLElementArrayCache::Validate(uint32_t maxAllowed, size_t firstElement, // fast exit path when the global maximum for the whole element array buffer // falls in the allowed range - if (tree->GlobalMaximum() <= maxAllowedT) + T globalMax = tree->GlobalMaximum(); + if (globalMax <= maxAllowedT) { + SetUpperBound(out_upperBound, globalMax); return true; } @@ -527,14 +563,18 @@ bool WebGLElementArrayCache::Validate(uint32_t maxAllowed, size_t firstElement, size_t firstElementAdjustmentEnd = std::min(lastElement, tree->LastElementUnderSameLeaf(firstElement)); while (firstElement <= firstElementAdjustmentEnd) { - if (elements[firstElement] > maxAllowedT) + const T& curData = elements[firstElement]; + UpdateUpperBound(out_upperBound, curData); + if (curData > maxAllowedT) return false; firstElement++; } size_t lastElementAdjustmentEnd = std::max(firstElement, tree->FirstElementUnderSameLeaf(lastElement)); while (lastElement >= lastElementAdjustmentEnd) { - if (elements[lastElement] > maxAllowedT) + const T& curData = elements[lastElement]; + UpdateUpperBound(out_upperBound, curData); + if (curData > maxAllowedT) return false; lastElement--; } @@ -546,20 +586,29 @@ bool WebGLElementArrayCache::Validate(uint32_t maxAllowed, size_t firstElement, // general case return tree->Validate(maxAllowedT, tree->LeafForElement(firstElement), - tree->LeafForElement(lastElement)); + tree->LeafForElement(lastElement), + out_upperBound); } -bool WebGLElementArrayCache::Validate(GLenum type, uint32_t maxAllowed, size_t firstElement, size_t countElements) { +bool +WebGLElementArrayCache::Validate(GLenum type, uint32_t maxAllowed, + size_t firstElement, size_t countElements, + uint32_t* out_upperBound) +{ if (type == LOCAL_GL_UNSIGNED_BYTE) - return Validate(maxAllowed, firstElement, countElements); + return Validate(maxAllowed, firstElement, countElements, out_upperBound); if (type == LOCAL_GL_UNSIGNED_SHORT) - return Validate(maxAllowed, firstElement, countElements); + return Validate(maxAllowed, firstElement, countElements, out_upperBound); if (type == LOCAL_GL_UNSIGNED_INT) - return Validate(maxAllowed, firstElement, countElements); + return Validate(maxAllowed, firstElement, countElements, out_upperBound); + + MOZ_ASSERT(false, "Invalid type."); return false; } -size_t WebGLElementArrayCache::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const { +size_t +WebGLElementArrayCache::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const +{ size_t uint8TreeSize = mUint8Tree ? mUint8Tree->SizeOfIncludingThis(aMallocSizeOf) : 0; size_t uint16TreeSize = mUint16Tree ? mUint16Tree->SizeOfIncludingThis(aMallocSizeOf) : 0; size_t uint32TreeSize = mUint32Tree ? mUint32Tree->SizeOfIncludingThis(aMallocSizeOf) : 0; diff --git a/content/canvas/src/WebGLElementArrayCache.h b/content/canvas/src/WebGLElementArrayCache.h index e6ee10ea738c..842a802e499c 100644 --- a/content/canvas/src/WebGLElementArrayCache.h +++ b/content/canvas/src/WebGLElementArrayCache.h @@ -33,7 +33,8 @@ public: bool BufferData(const void* ptr, size_t byteSize); void BufferSubData(size_t pos, const void* ptr, size_t updateByteSize); - bool Validate(GLenum type, uint32_t maxAllowed, size_t first, size_t count); + bool Validate(GLenum type, uint32_t maxAllowed, size_t first, size_t count, + uint32_t* out_upperBound = nullptr); template T Element(size_t i) const { return Elements()[i]; } @@ -53,7 +54,8 @@ public: private: template - bool Validate(uint32_t maxAllowed, size_t first, size_t count); + bool Validate(uint32_t maxAllowed, size_t first, size_t count, + uint32_t* out_upperBound); size_t ByteSize() const { return mByteSize; diff --git a/gfx/gl/GLContextFeatures.cpp b/gfx/gl/GLContextFeatures.cpp index 7269b6d9d34a..9a1ff3b43bbb 100644 --- a/gfx/gl/GLContextFeatures.cpp +++ b/gfx/gl/GLContextFeatures.cpp @@ -86,7 +86,7 @@ static const FeatureInfo sFeatureInfoArr[] = { }, { "draw_range_elements", - 200, // OpenGL version + 120, // OpenGL version 300, // OpenGL ES version { GLContext::EXT_draw_range_elements,