Bug 738867 - Implement WebGL OES_element_index_uint extension. r=bjacob
authorJon Buckley <jon@jbuckley.ca>
Mon, 13 May 2013 09:22:30 -0400
changeset 131775 6cc9b139557bf49b5e1ca78198c7e79ae29a8729
parent 131774 e4fad18913b34a1bc357ebc83556d3752d75ad2f
child 131776 a7b3ef7ded5c3aa9967feda0b4a73497e51d0a77
push id24671
push userryanvm@gmail.com
push dateMon, 13 May 2013 20:32:09 +0000
treeherdermozilla-central@81dd97739fa1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbjacob
bugs738867
milestone23.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 738867 - Implement WebGL OES_element_index_uint extension. r=bjacob
content/canvas/compiledtest/TestWebGLElementArrayCache.cpp
content/canvas/src/Makefile.in
content/canvas/src/WebGLContext.cpp
content/canvas/src/WebGLContext.h
content/canvas/src/WebGLContextGL.cpp
content/canvas/src/WebGLElementArrayCache.cpp
content/canvas/src/WebGLElementArrayCache.h
content/canvas/src/WebGLExtensionElementIndexUint.cpp
content/canvas/src/WebGLExtensions.h
dom/bindings/Bindings.conf
dom/webidl/WebGLRenderingContext.webidl
gfx/gl/GLContext.cpp
gfx/gl/GLContext.h
--- a/content/canvas/compiledtest/TestWebGLElementArrayCache.cpp
+++ b/content/canvas/compiledtest/TestWebGLElementArrayCache.cpp
@@ -43,19 +43,25 @@ T RandomInteger(T a, T b)
 {
   T result(a + rand() % (b - a + 1));
   return result;
 }
 
 template<typename T>
 GLenum GLType()
 {
-  return sizeof(T) == 1
-             ? LOCAL_GL_UNSIGNED_BYTE
-             : LOCAL_GL_UNSIGNED_SHORT;
+  switch (sizeof(T))
+  {
+    case 4:  return LOCAL_GL_UNSIGNED_INT;
+    case 2:  return LOCAL_GL_UNSIGNED_SHORT;
+    case 1:  return LOCAL_GL_UNSIGNED_BYTE;
+    default:
+      VERIFY(false);
+      return 0;
+  }
 }
 
 template<typename T>
 void CheckValidateOneType(WebGLElementArrayCache& c, size_t firstByte, size_t countBytes)
 {
   size_t first = firstByte / sizeof(T);
   size_t count = countBytes / sizeof(T);
 
@@ -74,16 +80,17 @@ void CheckValidateOneType(WebGLElementAr
     VERIFY(!c.Validate(type, 0, first, count));
   }
 }
 
 void CheckValidate(WebGLElementArrayCache& c, size_t firstByte, size_t countBytes)
 {
   CheckValidateOneType<uint8_t>(c, firstByte, countBytes);
   CheckValidateOneType<uint16_t>(c, firstByte, countBytes);
+  CheckValidateOneType<uint32_t>(c, firstByte, countBytes);
 }
 
 template<typename T>
 void CheckSanity()
 {
   const size_t numElems = 64; // should be significantly larger than tree leaf size to
                         // ensure we exercise some nontrivial tree-walking
   T data[numElems] = {1,0,3,1,2,6,5,4}; // intentionally specify only 8 elements for now
@@ -146,16 +153,17 @@ void CheckUintOverflow()
 }
 
 int main(int argc, char *argv[])
 {
   srand(0); // do not want a random seed here.
 
   CheckSanity<uint8_t>();
   CheckSanity<uint16_t>();
+  CheckSanity<uint32_t>();
 
   CheckUintOverflow<uint8_t>();
   CheckUintOverflow<uint16_t>();
 
   nsTArray<uint8_t> v, vsub;
   WebGLElementArrayCache b;
 
   for (int maxBufferSize = 1; maxBufferSize <= 4096; maxBufferSize *= 2) {
--- a/content/canvas/src/Makefile.in
+++ b/content/canvas/src/Makefile.in
@@ -35,16 +35,17 @@ CPPSRCS += \
 	WebGLContextValidate.cpp \
 	WebGLElementArrayCache.cpp \
 	WebGLExtensionBase.cpp \
 	WebGLExtensionCompressedTextureATC.cpp \
 	WebGLExtensionCompressedTexturePVRTC.cpp \
 	WebGLExtensionCompressedTextureS3TC.cpp \
 	WebGLExtensionDebugRendererInfo.cpp \
 	WebGLExtensionDepthTexture.cpp \
+	WebGLExtensionElementIndexUint.cpp \
 	WebGLExtensionLoseContext.cpp \
 	WebGLExtensionStandardDerivatives.cpp \
 	WebGLExtensionTextureFilterAnisotropic.cpp \
 	WebGLExtensionTextureFloat.cpp \
 	WebGLFramebuffer.cpp \
 	WebGLObjectModel.cpp \
 	WebGLProgram.cpp \
 	WebGLRenderbuffer.cpp \
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -938,16 +938,18 @@ WebGLContext::MozGetUnderlyingParamStrin
 
 bool WebGLContext::IsExtensionSupported(JSContext *cx, WebGLExtensionID ext) const
 {
     if (mDisableExtensions) {
         return false;
     }
 
     switch (ext) {
+        case OES_element_index_uint:
+            return !gl->IsGLES2() || gl->IsExtensionSupported(GLContext::OES_element_index_uint);
         case OES_standard_derivatives:
         case WEBGL_lose_context:
             // We always support these extensions.
             return true;
         case OES_texture_float:
             return gl->IsExtensionSupported(gl->IsGLES2() ? GLContext::OES_texture_float
                                                           : GLContext::ARB_texture_float);
         case EXT_texture_filter_anisotropic:
@@ -1008,17 +1010,21 @@ WebGLContext::GetExtension(JSContext *cx
     if (!IsContextStable())
         return nullptr;
 
     NS_LossyConvertUTF16toASCII name(aName);
 
     WebGLExtensionID ext = WebGLExtensionID_unknown_extension;
 
     // step 1: figure what extension is wanted
-    if (CompareWebGLExtensionName(name, "OES_texture_float"))
+    if (CompareWebGLExtensionName(name, "OES_element_index_uint"))
+    {
+        ext = OES_element_index_uint;
+    }
+    else if (CompareWebGLExtensionName(name, "OES_texture_float"))
     {
         ext = OES_texture_float;
     }
     else if (CompareWebGLExtensionName(name, "OES_standard_derivatives"))
     {
         ext = OES_standard_derivatives;
     }
     else if (CompareWebGLExtensionName(name, "EXT_texture_filter_anisotropic"))
@@ -1070,16 +1076,19 @@ WebGLContext::GetExtension(JSContext *cx
     if (!IsExtensionSupported(cx, ext)) {
         return nullptr;
     }
 
     // step 3: if the extension hadn't been previously been created, create it now, thus enabling it
     if (!IsExtensionEnabled(ext)) {
         WebGLExtensionBase *obj = nullptr;
         switch (ext) {
+            case OES_element_index_uint:
+                obj = new WebGLExtensionElementIndexUint(this);
+                break;
             case OES_standard_derivatives:
                 obj = new WebGLExtensionStandardDerivatives(this);
                 break;
             case EXT_texture_filter_anisotropic:
                 obj = new WebGLExtensionTextureFilterAnisotropic(this);
                 break;
             case WEBGL_lose_context:
                 obj = new WebGLExtensionLoseContext(this);
@@ -1434,16 +1443,18 @@ void
 WebGLContext::GetSupportedExtensions(JSContext *cx, Nullable< nsTArray<nsString> > &retval)
 {
     retval.SetNull();
     if (!IsContextStable())
         return;
 
     nsTArray<nsString>& arr = retval.SetValue();
 
+    if (IsExtensionSupported(cx, OES_element_index_uint))
+        arr.AppendElement(NS_LITERAL_STRING("OES_element_index_uint"));
     if (IsExtensionSupported(cx, OES_texture_float))
         arr.AppendElement(NS_LITERAL_STRING("OES_texture_float"));
     if (IsExtensionSupported(cx, OES_standard_derivatives))
         arr.AppendElement(NS_LITERAL_STRING("OES_standard_derivatives"));
     if (IsExtensionSupported(cx, EXT_texture_filter_anisotropic))
         arr.AppendElement(NS_LITERAL_STRING("EXT_texture_filter_anisotropic"));
     if (IsExtensionSupported(cx, WEBGL_lose_context))
         arr.AppendElement(NS_LITERAL_STRING("MOZ_WEBGL_lose_context"));
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -907,16 +907,17 @@ protected:
         // script correctly handled the event. We are waiting for the context to
         // be restored.
         ContextLostAwaitingRestore
     };
 
     // extensions
     enum WebGLExtensionID {
         EXT_texture_filter_anisotropic,
+        OES_element_index_uint,
         OES_standard_derivatives,
         OES_texture_float,
         WEBGL_compressed_texture_atc,
         WEBGL_compressed_texture_pvrtc,
         WEBGL_compressed_texture_s3tc,
         WEBGL_debug_renderer_info,
         WEBGL_depth_texture,
         WEBGL_lose_context,
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -1506,16 +1506,21 @@ WebGLContext::DrawElements(WebGLenum mod
     if (type == LOCAL_GL_UNSIGNED_SHORT) {
         checked_byteCount = 2 * CheckedUint32(count);
         if (byteOffset % 2 != 0)
             return ErrorInvalidOperation("drawElements: invalid byteOffset for UNSIGNED_SHORT (must be a multiple of 2)");
         first = byteOffset / 2;
     } else if (type == LOCAL_GL_UNSIGNED_BYTE) {
         checked_byteCount = count;
         first = byteOffset;
+    } else if (type == LOCAL_GL_UNSIGNED_INT && IsExtensionEnabled(OES_element_index_uint)) {
+        checked_byteCount = 4 * CheckedUint32(count);
+        if (byteOffset % 4 != 0)
+            return ErrorInvalidOperation("drawElements: invalid byteOffset for UNSIGNED_INT (must be a multiple of 4)");
+        first = byteOffset / 4;
     } else {
         return ErrorInvalidEnum("drawElements: type must be UNSIGNED_SHORT or UNSIGNED_BYTE");
     }
 
     if (!checked_byteCount.isValid())
         return ErrorInvalidValue("drawElements: overflow in byteCount");
 
     // If there is no current program, this is silently ignored.
--- a/content/canvas/src/WebGLElementArrayCache.cpp
+++ b/content/canvas/src/WebGLElementArrayCache.cpp
@@ -101,18 +101,18 @@ namespace mozilla {
  * specified in the drawElements call. This means that we may potentially have to store caches for
  * multiple element types, for the same element array buffer. Since we don't know yet how many
  * element types we'll eventually support (extensions add more), the concern about memory usage is serious.
  * This is addressed by sSkippedBottomTreeLevels as explained above. Of course, in the typical
  * case where each element array buffer is only ever used with one type, this is also addressed
  * by having WebGLElementArrayCache lazily create trees for each type only upon first use.
  *
  * Another consequence of this constraint is that when invalidating the trees, we have to invalidate
- * all existing trees. So if trees for types uint8_t and uint16_t have ever been constructed for this buffer,
- * every subsequent invalidation will have to invalidate both trees even if one of the two types is never
+ * all existing trees. So if trees for types uint8_t, uint16_t and uint32_t have ever been constructed for this buffer,
+ * every subsequent invalidation will have to invalidate all trees even if one of the types is never
  * used again. This implies that it is important to minimize the cost of invalidation i.e.
  * do lazy updates upon use as opposed to immediately updating invalidated trees. This poses a problem:
  * it is nontrivial to keep track of the part of the tree that's invalidated. The current solution
  * can only keep track of an invalidated interval, from |mFirstInvalidatedLeaf| to |mLastInvalidatedLeaf|.
  * The problem is that if one does two small, far-apart partial buffer updates, the resulting invalidated
  * area is very large even though only a small part of the array really needed to be invalidated.
  * The real solution to this problem would be to use a smarter data structure to keep track of the
  * invalidated area, probably an interval tree. Meanwhile, we can probably live with the current situation
@@ -324,16 +324,22 @@ struct TreeForType<uint8_t>
 };
 
 template<>
 struct TreeForType<uint16_t>
 {
   static WebGLElementArrayCacheTree<uint16_t>*& Run(WebGLElementArrayCache *b) { return b->mUint16Tree; }
 };
 
+template<>
+struct TreeForType<uint32_t>
+{
+  static WebGLElementArrayCacheTree<uint32_t>*& Run(WebGLElementArrayCache *b) { return b->mUint32Tree; }
+};
+
 // When the buffer gets updated from firstByte to lastByte,
 // calling this method will notify the tree accordingly
 template<typename T>
 void WebGLElementArrayCacheTree<T>::Invalidate(size_t firstByte, size_t lastByte)
 {
   lastByte = std::min(lastByte, mNumLeaves * sElementsPerLeaf * sizeof(T) - 1);
   if (firstByte > lastByte) {
     return;
@@ -436,27 +442,31 @@ void WebGLElementArrayCacheTree<T>::Upda
   }
 
   mInvalidated = false;
 }
 
 WebGLElementArrayCache::~WebGLElementArrayCache() {
   delete mUint8Tree;
   delete mUint16Tree;
+  delete mUint32Tree;
   free(mUntypedData);
 }
 
 bool WebGLElementArrayCache::BufferData(const void* ptr, size_t byteSize) {
   mByteSize = byteSize;
   if (mUint8Tree)
     if (!mUint8Tree->ResizeToParentSize())
       return false;
   if (mUint16Tree)
     if (!mUint16Tree->ResizeToParentSize())
       return false;
+  if (mUint32Tree)
+    if (!mUint32Tree->ResizeToParentSize())
+      return false;
   mUntypedData = realloc(mUntypedData, byteSize);
   if (!mUntypedData)
     return false;
   BufferSubData(0, ptr, byteSize);
   return true;
 }
 
 void WebGLElementArrayCache::BufferSubData(size_t pos, const void* ptr, size_t updateByteSize) {
@@ -469,16 +479,18 @@ void WebGLElementArrayCache::BufferSubDa
 }
 
 void WebGLElementArrayCache::InvalidateTrees(size_t firstByte, size_t lastByte)
 {
   if (mUint8Tree)
     mUint8Tree->Invalidate(firstByte, lastByte);
   if (mUint16Tree)
     mUint16Tree->Invalidate(firstByte, lastByte);
+  if (mUint32Tree)
+    mUint32Tree->Invalidate(firstByte, lastByte);
 }
 
 template<typename T>
 bool WebGLElementArrayCache::Validate(uint32_t maxAllowed, size_t firstElement, size_t countElements) {
   // if maxAllowed is >= the max T value, then there is no way that a T index could be invalid
   if (maxAllowed >= std::numeric_limits<T>::max())
     return true;
 
@@ -536,21 +548,25 @@ bool WebGLElementArrayCache::Validate(ui
                         tree->LeafForElement(lastElement));
 }
 
 bool WebGLElementArrayCache::Validate(GLenum type, uint32_t maxAllowed, size_t firstElement, size_t countElements) {
   if (type == LOCAL_GL_UNSIGNED_BYTE)
     return Validate<uint8_t>(maxAllowed, firstElement, countElements);
   if (type == LOCAL_GL_UNSIGNED_SHORT)
     return Validate<uint16_t>(maxAllowed, firstElement, countElements);
+  if (type == LOCAL_GL_UNSIGNED_INT)
+    return Validate<uint32_t>(maxAllowed, firstElement, countElements);
   return false;
 }
 
 size_t WebGLElementArrayCache::SizeOfIncludingThis(nsMallocSizeOfFun 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;
   return aMallocSizeOf(this) +
           mByteSize +
           uint8TreeSize +
-          uint16TreeSize;
+          uint16TreeSize +
+          uint32TreeSize;
 }
 
 } // end namespace mozilla
--- a/content/canvas/src/WebGLElementArrayCache.h
+++ b/content/canvas/src/WebGLElementArrayCache.h
@@ -37,16 +37,17 @@ public:
   template<typename T>
   T Element(size_t i) const { return Elements<T>()[i]; }
 
   WebGLElementArrayCache()
     : mUntypedData(nullptr)
     , mByteSize(0)
     , mUint8Tree(nullptr)
     , mUint16Tree(nullptr)
+    , mUint32Tree(nullptr)
   {}
 
   ~WebGLElementArrayCache();
 
   size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const;
 
 private:
 
@@ -68,14 +69,15 @@ private:
   friend struct WebGLElementArrayCacheTree;
   template<typename T>
   friend struct TreeForType;
 
   void* mUntypedData;
   size_t mByteSize;
   WebGLElementArrayCacheTree<uint8_t>* mUint8Tree;
   WebGLElementArrayCacheTree<uint16_t>* mUint16Tree;
+  WebGLElementArrayCacheTree<uint32_t>* mUint32Tree;
 };
 
 
 } // end namespace mozilla
 
 #endif // WEBGLELEMENTARRAYCACHE_H
new file mode 100644
--- /dev/null
+++ b/content/canvas/src/WebGLExtensionElementIndexUint.cpp
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WebGLContext.h"
+#include "WebGLExtensions.h"
+#include "mozilla/dom/WebGLRenderingContextBinding.h"
+
+using namespace mozilla;
+
+WebGLExtensionElementIndexUint::WebGLExtensionElementIndexUint(WebGLContext* context)
+    : WebGLExtensionBase(context)
+{
+}
+
+WebGLExtensionElementIndexUint::~WebGLExtensionElementIndexUint()
+{
+}
+
+IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionElementIndexUint)
--- a/content/canvas/src/WebGLExtensions.h
+++ b/content/canvas/src/WebGLExtensions.h
@@ -82,16 +82,26 @@ class WebGLExtensionDepthTexture
 {
 public:
     WebGLExtensionDepthTexture(WebGLContext*);
     virtual ~WebGLExtensionDepthTexture();
 
     DECL_WEBGL_EXTENSION_GOOP
 };
 
+class WebGLExtensionElementIndexUint
+    : public WebGLExtensionBase
+{
+public:
+    WebGLExtensionElementIndexUint(WebGLContext*);
+    virtual ~WebGLExtensionElementIndexUint();
+
+    DECL_WEBGL_EXTENSION_GOOP
+};
+
 class WebGLExtensionLoseContext
     : public WebGLExtensionBase
 {
 public:
     WebGLExtensionLoseContext(WebGLContext*);
     virtual ~WebGLExtensionLoseContext();
 
     void LoseContext();
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1097,16 +1097,21 @@ DOMInterfaces = {
    'headerFile': 'WebGLExtensions.h'
 },
 
 'WebGLExtensionDebugRendererInfo': {
    'nativeType': 'mozilla::WebGLExtensionDebugRendererInfo',
    'headerFile': 'WebGLExtensions.h'
 },
 
+'WebGLExtensionElementIndexUint': {
+   'nativeType': 'mozilla::WebGLExtensionElementIndexUint',
+   'headerFile': 'WebGLExtensions.h'
+},
+
 'WebGLExtensionLoseContext': {
    'nativeType': 'mozilla::WebGLExtensionLoseContext',
    'headerFile': 'WebGLExtensions.h'
 },
 
 'WebGLExtensionStandardDerivatives': {
    'nativeType': 'mozilla::WebGLExtensionStandardDerivatives',
    'headerFile': 'WebGLExtensions.h'
--- a/dom/webidl/WebGLRenderingContext.webidl
+++ b/dom/webidl/WebGLRenderingContext.webidl
@@ -802,16 +802,21 @@ interface WebGLExtensionDebugRendererInf
 
 [NoInterfaceObject]
 interface WebGLExtensionDepthTexture
 {
     const GLenum UNSIGNED_INT_24_8_WEBGL = 0x84FA;
 };
 
 [NoInterfaceObject]
+interface WebGLExtensionElementIndexUint
+{
+};
+
+[NoInterfaceObject]
 interface WebGLExtensionLoseContext {
     void loseContext();
     void restoreContext();
 };
 
 [NoInterfaceObject]
 interface WebGLExtensionTextureFilterAnisotropic
 {
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -75,16 +75,17 @@ static const char *sExtensionNames[] = {
     "GL_OES_rgb8_rgba8",
     "GL_ARB_robustness",
     "GL_EXT_robustness",
     "GL_ARB_sync",
     "GL_OES_EGL_image",
     "GL_OES_EGL_sync",
     "GL_OES_EGL_image_external",
     "GL_EXT_packed_depth_stencil",
+    "GL_OES_element_index_uint",
     nullptr
 };
 
 static int64_t sTextureMemoryUsage = 0;
 
 static int64_t
 GetTextureMemoryUsage()
 {
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -1011,16 +1011,17 @@ public:
         OES_rgb8_rgba8,
         ARB_robustness,
         EXT_robustness,
         ARB_sync,
         OES_EGL_image,
         OES_EGL_sync,
         OES_EGL_image_external,
         EXT_packed_depth_stencil,
+        OES_element_index_uint,
         Extensions_Max
     };
 
     bool IsExtensionSupported(GLExtensions aKnownExtension) const {
         return mAvailableExtensions[aKnownExtension];
     }
 
     void MarkExtensionUnsupported(GLExtensions aKnownExtension) {