Bug 1305190 - Fix vertex attrib elem size calculations. - r=ethlin draft
authorJeff Gilbert (:jgilbert) <jgilbert@mozilla.com>
Fri, 23 Sep 2016 17:28:58 -0700
changeset 417739 3c346875e415a3e42f2388b70191724371fdc438
parent 416906 fa9844b0dee37aeb4c94d027f7c68a94721db320
child 532159 3897874a8b1e76e7da4584d35021607dbf14a84c
push id30477
push userbmo:jgilbert@mozilla.com
push dateMon, 26 Sep 2016 19:32:25 +0000
reviewersethlin
bugs1305190
milestone52.0a1
Bug 1305190 - Fix vertex attrib elem size calculations. - r=ethlin MozReview-Commit-ID: KXpLbIu8pRr
dom/canvas/WebGL2ContextVertices.cpp
dom/canvas/WebGLContextBuffers.cpp
dom/canvas/WebGLContextDraw.cpp
dom/canvas/WebGLContextVertices.cpp
dom/canvas/WebGLVertexArray.h
dom/canvas/WebGLVertexArrayFake.cpp
dom/canvas/WebGLVertexAttribData.cpp
dom/canvas/WebGLVertexAttribData.h
dom/canvas/moz.build
--- a/dom/canvas/WebGL2ContextVertices.cpp
+++ b/dom/canvas/WebGL2ContextVertices.cpp
@@ -75,27 +75,24 @@ WebGL2Context::VertexAttribIPointer(GLui
     return;
   }
 
   MOZ_ASSERT(mBoundVertexArray);
   mBoundVertexArray->EnsureAttrib(index);
 
   InvalidateBufferFetching();
 
-  WebGLVertexAttribData& vd = mBoundVertexArray->mAttribs[index];
-  vd.buf = mBoundArrayBuffer;
-  vd.stride = stride;
-  vd.size = size;
-  vd.byteOffset = offset;
-  vd.type = type;
-  vd.normalized = false;
-  vd.integer = true;
-
   MakeContextCurrent();
   gl->fVertexAttribIPointer(index, size, type, stride, reinterpret_cast<void*>(offset));
+
+  WebGLVertexAttribData& vd = mBoundVertexArray->mAttribs[index];
+  const bool integerFunc = true;
+  const bool normalized = false;
+  vd.VertexAttribPointer(true, mBoundArrayBuffer, size, type, normalized, stride,
+                         offset);
 }
 
 void
 WebGL2Context::VertexAttribI4i(GLuint index, GLint x, GLint y, GLint z, GLint w)
 {
   if (IsContextLost())
     return;
 
--- a/dom/canvas/WebGLContextBuffers.cpp
+++ b/dom/canvas/WebGLContextBuffers.cpp
@@ -519,17 +519,17 @@ WebGLContext::DeleteBuffer(WebGLBuffer* 
 
         for (auto& binding : mIndexedUniformBufferBindings) {
             fnClearIfBuffer(binding.mBufferBinding);
         }
     }
 
     for (int32_t i = 0; i < mGLMaxVertexAttribs; i++) {
         if (mBoundVertexArray->HasAttrib(i)) {
-            fnClearIfBuffer(mBoundVertexArray->mAttribs[i].buf);
+            fnClearIfBuffer(mBoundVertexArray->mAttribs[i].mBuf);
         }
     }
 
     ////
 
     buffer->RequestDelete();
 }
 
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -813,20 +813,20 @@ WebGLContext::ValidateBufferFetching(con
     uint32_t maxVertices = UINT32_MAX;
     uint32_t maxInstances = UINT32_MAX;
     const uint32_t attribCount = mBoundVertexArray->mAttribs.Length();
 
     uint32_t i = 0;
     for (const auto& vd : mBoundVertexArray->mAttribs) {
         // If the attrib array isn't enabled, there's nothing to check;
         // it's a static value.
-        if (!vd.enabled)
+        if (!vd.mEnabled)
             continue;
 
-        if (vd.buf == nullptr) {
+        if (!vd.mBuf) {
             ErrorInvalidOperation("%s: no VBO bound to enabled vertex attrib index %du!",
                                   info, i);
             return false;
         }
 
         ++i;
     }
 
@@ -838,62 +838,49 @@ WebGLContext::ValidateBufferFetching(con
         if (attribLoc >= attribCount)
             continue;
 
         if (attribLoc == 0) {
             mBufferFetch_IsAttrib0Active = true;
         }
 
         const auto& vd = mBoundVertexArray->mAttribs[attribLoc];
-        if (!vd.enabled)
+        if (!vd.mEnabled)
             continue;
 
-        // the base offset
-        CheckedUint32 checked_byteLength = CheckedUint32(vd.buf->ByteLength()) - vd.byteOffset;
-        CheckedUint32 checked_sizeOfLastElement = CheckedUint32(vd.componentSize()) * vd.size;
-
-        if (!checked_byteLength.isValid() ||
-            !checked_sizeOfLastElement.isValid())
-        {
-            ErrorInvalidOperation("%s: Integer overflow occured while checking vertex"
-                                  " attrib %u.",
-                                  info, attribLoc);
-            return false;
-        }
-
-        if (checked_byteLength.value() < checked_sizeOfLastElement.value()) {
+        const auto& bufByteLen = vd.mBuf->ByteLength();
+        if (vd.ByteOffset() > bufByteLen) {
             maxVertices = 0;
             maxInstances = 0;
             break;
         }
 
-        CheckedUint32 checked_maxAllowedCount = ((checked_byteLength - checked_sizeOfLastElement) / vd.actualStride()) + 1;
+        size_t availBytes = bufByteLen - vd.ByteOffset();
+        if (vd.BytesPerVertex() > availBytes) {
+            maxVertices = 0;
+            maxInstances = 0;
+            break;
+        }
+        availBytes -= vd.BytesPerVertex();
+        const size_t vertCapacity = 1 + availBytes / vd.ExplicitStride();
 
-        if (!checked_maxAllowedCount.isValid()) {
-            ErrorInvalidOperation("%s: Integer overflow occured while checking vertex"
-                                  " attrib %u.",
-                                  info, attribLoc);
-            return false;
-        }
-
-        if (vd.divisor == 0) {
-            maxVertices = std::min(maxVertices, checked_maxAllowedCount.value());
+        if (vd.mDivisor == 0) {
+            if (vertCapacity < maxVertices) {
+                maxVertices = vertCapacity;
+            }
             hasPerVertex = true;
         } else {
-            CheckedUint32 checked_curMaxInstances = checked_maxAllowedCount * vd.divisor;
-
-            uint32_t curMaxInstances = UINT32_MAX;
-            // If this isn't valid, it's because we overflowed our
-            // uint32 above. Just leave this as UINT32_MAX, since
-            // sizeof(uint32) becomes our limiting factor.
-            if (checked_curMaxInstances.isValid()) {
-                curMaxInstances = checked_curMaxInstances.value();
+            const auto curMaxInstances = CheckedInt<size_t>(vertCapacity) * vd.mDivisor;
+            // If this isn't valid, it's because we overflowed, which means we can support
+            // *too much*. Don't update maxInstances in this case.
+            if (curMaxInstances.isValid() &&
+                curMaxInstances.value() < maxInstances)
+            {
+                maxInstances = curMaxInstances.value();
             }
-
-            maxInstances = std::min(maxInstances, curMaxInstances);
         }
     }
 
     mBufferFetchingIsVerified = true;
     mBufferFetchingHasPerVertex = hasPerVertex;
     mMaxFetchedVertices = maxVertices;
     mMaxFetchedInstances = maxInstances;
 
@@ -1020,33 +1007,20 @@ WebGLContext::DoFakeVertexAttrib0(GLuint
 void
 WebGLContext::UndoFakeVertexAttrib0()
 {
     WebGLVertexAttrib0Status whatDoesAttrib0Need = WhatDoesVertexAttrib0Need();
 
     if (MOZ_LIKELY(whatDoesAttrib0Need == WebGLVertexAttrib0Status::Default))
         return;
 
-    if (mBoundVertexArray->HasAttrib(0) && mBoundVertexArray->mAttribs[0].buf) {
+    if (mBoundVertexArray->HasAttrib(0) && mBoundVertexArray->mAttribs[0].mBuf) {
         const WebGLVertexAttribData& attrib0 = mBoundVertexArray->mAttribs[0];
-        gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, attrib0.buf->mGLName);
-        if (attrib0.integer) {
-            gl->fVertexAttribIPointer(0,
-                                      attrib0.size,
-                                      attrib0.type,
-                                      attrib0.stride,
-                                      reinterpret_cast<const GLvoid*>(attrib0.byteOffset));
-        } else {
-            gl->fVertexAttribPointer(0,
-                                     attrib0.size,
-                                     attrib0.type,
-                                     attrib0.normalized,
-                                     attrib0.stride,
-                                     reinterpret_cast<const GLvoid*>(attrib0.byteOffset));
-        }
+        gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, attrib0.mBuf->mGLName);
+        attrib0.DoVertexAttribPointer(gl, 0);
     } else {
         gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
     }
 
     gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mBoundArrayBuffer ? mBoundArrayBuffer->mGLName : 0);
 }
 
 static GLuint
--- a/dom/canvas/WebGLContextVertices.cpp
+++ b/dom/canvas/WebGLContextVertices.cpp
@@ -295,37 +295,38 @@ WebGLContext::EnableVertexAttribArray(GL
 
     MakeContextCurrent();
     InvalidateBufferFetching();
 
     gl->fEnableVertexAttribArray(index);
 
     MOZ_ASSERT(mBoundVertexArray);
     mBoundVertexArray->EnsureAttrib(index);
-    mBoundVertexArray->mAttribs[index].enabled = true;
+    mBoundVertexArray->mAttribs[index].mEnabled = true;
 }
 
 void
 WebGLContext::DisableVertexAttribArray(GLuint index)
 {
     if (IsContextLost())
         return;
 
     if (!ValidateAttribIndex(index, "disableVertexAttribArray"))
         return;
 
     MakeContextCurrent();
     InvalidateBufferFetching();
 
-    if (index || gl->IsGLES())
+    if (index || gl->IsGLES()) {
         gl->fDisableVertexAttribArray(index);
+    }
 
     MOZ_ASSERT(mBoundVertexArray);
     mBoundVertexArray->EnsureAttrib(index);
-    mBoundVertexArray->mAttribs[index].enabled = false;
+    mBoundVertexArray->mAttribs[index].mEnabled = false;
 }
 
 JS::Value
 WebGLContext::GetVertexAttrib(JSContext* cx, GLuint index, GLenum pname,
                               ErrorResult& rv)
 {
     if (IsContextLost())
         return JS::NullValue();
@@ -335,38 +336,38 @@ WebGLContext::GetVertexAttrib(JSContext*
 
     MOZ_ASSERT(mBoundVertexArray);
     mBoundVertexArray->EnsureAttrib(index);
 
     MakeContextCurrent();
 
     switch (pname) {
     case LOCAL_GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:
-        return WebGLObjectAsJSValue(cx, mBoundVertexArray->mAttribs[index].buf.get(), rv);
+        return WebGLObjectAsJSValue(cx, mBoundVertexArray->mAttribs[index].mBuf.get(), rv);
 
     case LOCAL_GL_VERTEX_ATTRIB_ARRAY_STRIDE:
-        return JS::Int32Value(mBoundVertexArray->mAttribs[index].stride);
+        return JS::Int32Value(mBoundVertexArray->mAttribs[index].Stride());
 
     case LOCAL_GL_VERTEX_ATTRIB_ARRAY_SIZE:
-        return JS::Int32Value(mBoundVertexArray->mAttribs[index].size);
+        return JS::Int32Value(mBoundVertexArray->mAttribs[index].Size());
 
     case LOCAL_GL_VERTEX_ATTRIB_ARRAY_TYPE:
-        return JS::Int32Value(mBoundVertexArray->mAttribs[index].type);
+        return JS::Int32Value(mBoundVertexArray->mAttribs[index].Type());
 
     case LOCAL_GL_VERTEX_ATTRIB_ARRAY_INTEGER:
         if (IsWebGL2())
-            return JS::BooleanValue(mBoundVertexArray->mAttribs[index].integer);
+            return JS::BooleanValue(mBoundVertexArray->mAttribs[index].IntegerFunc());
 
         break;
 
     case LOCAL_GL_VERTEX_ATTRIB_ARRAY_DIVISOR:
         if (IsWebGL2() ||
             IsExtensionEnabled(WebGLExtensionID::ANGLE_instanced_arrays))
         {
-            return JS::Int32Value(mBoundVertexArray->mAttribs[index].divisor);
+            return JS::Int32Value(mBoundVertexArray->mAttribs[index].mDivisor);
         }
         break;
 
     case LOCAL_GL_CURRENT_VERTEX_ATTRIB:
         {
             JS::RootedObject obj(cx);
             switch (mVertexAttribType[index]) {
             case LOCAL_GL_FLOAT:
@@ -383,20 +384,20 @@ WebGLContext::GetVertexAttrib(JSContext*
             }
 
             if (!obj)
                 rv.Throw(NS_ERROR_OUT_OF_MEMORY);
             return JS::ObjectOrNullValue(obj);
         }
 
     case LOCAL_GL_VERTEX_ATTRIB_ARRAY_ENABLED:
-        return JS::BooleanValue(mBoundVertexArray->mAttribs[index].enabled);
+        return JS::BooleanValue(mBoundVertexArray->mAttribs[index].mEnabled);
 
     case LOCAL_GL_VERTEX_ATTRIB_ARRAY_NORMALIZED:
-        return JS::BooleanValue(mBoundVertexArray->mAttribs[index].normalized);
+        return JS::BooleanValue(mBoundVertexArray->mAttribs[index].Normalized());
 
     default:
         break;
     }
 
     ErrorInvalidEnumInfo("getVertexAttrib: parameter", pname);
     return JS::NullValue();
 }
@@ -412,17 +413,17 @@ WebGLContext::GetVertexAttribOffset(GLui
 
     if (pname != LOCAL_GL_VERTEX_ATTRIB_ARRAY_POINTER) {
         ErrorInvalidEnum("getVertexAttribOffset: bad parameter");
         return 0;
     }
 
     MOZ_ASSERT(mBoundVertexArray);
     mBoundVertexArray->EnsureAttrib(index);
-    return mBoundVertexArray->mAttribs[index].byteOffset;
+    return mBoundVertexArray->mAttribs[index].ByteOffset();
 }
 
 void
 WebGLContext::VertexAttribPointer(GLuint index, GLint size, GLenum type,
                                   WebGLboolean normalized, GLsizei stride,
                                   WebGLintptr byteOffset)
 {
     if (IsContextLost())
@@ -439,45 +440,40 @@ WebGLContext::VertexAttribPointer(GLuint
 
     InvalidateBufferFetching();
 
     /* XXX make work with bufferSubData & heterogeneous types
      if (type != mBoundArrayBuffer->GLType())
      return ErrorInvalidOperation("vertexAttribPointer: type must match bound VBO type: %d != %d", type, mBoundArrayBuffer->GLType());
      */
 
-    WebGLVertexAttribData& vd = mBoundVertexArray->mAttribs[index];
-
-    vd.buf = mBoundArrayBuffer;
-    vd.stride = stride;
-    vd.size = size;
-    vd.byteOffset = byteOffset;
-    vd.type = type;
-    vd.normalized = normalized;
-    vd.integer = false;
-
     MakeContextCurrent();
     gl->fVertexAttribPointer(index, size, type, normalized, stride,
                              reinterpret_cast<void*>(byteOffset));
+
+    WebGLVertexAttribData& vd = mBoundVertexArray->mAttribs[index];
+    const bool integerFunc = false;
+    vd.VertexAttribPointer(integerFunc, mBoundArrayBuffer, size, type, normalized, stride,
+                           byteOffset);
 }
 
 void
 WebGLContext::VertexAttribDivisor(GLuint index, GLuint divisor)
 {
     if (IsContextLost())
         return;
 
     if (!ValidateAttribIndex(index, "vertexAttribDivisor"))
         return;
 
     MOZ_ASSERT(mBoundVertexArray);
     mBoundVertexArray->EnsureAttrib(index);
 
     WebGLVertexAttribData& vd = mBoundVertexArray->mAttribs[index];
-    vd.divisor = divisor;
+    vd.mDivisor = divisor;
 
     InvalidateBufferFetching();
 
     MakeContextCurrent();
 
     gl->fVertexAttribDivisor(index, divisor);
 }
 
--- a/dom/canvas/WebGLVertexArray.h
+++ b/dom/canvas/WebGLVertexArray.h
@@ -34,17 +34,17 @@ public:
         BindVertexArrayImpl();
     };
 
     void EnsureAttrib(GLuint index);
     bool HasAttrib(GLuint index) const {
         return index < mAttribs.Length();
     }
     bool IsAttribArrayEnabled(GLuint index) const {
-        return HasAttrib(index) && mAttribs[index].enabled;
+        return HasAttrib(index) && mAttribs[index].mEnabled;
     }
 
     // Implement parent classes:
     void Delete();
     bool IsVertexArray();
 
     WebGLContext* GetParentObject() const {
         return mContext;
--- a/dom/canvas/WebGLVertexArrayFake.cpp
+++ b/dom/canvas/WebGLVertexArrayFake.cpp
@@ -24,41 +24,36 @@ WebGLVertexArrayFake::BindVertexArrayImp
 
     WebGLRefPtr<WebGLVertexArray> prevVertexArray = mContext->mBoundVertexArray;
 
     mContext->mBoundVertexArray = this;
 
     WebGLRefPtr<WebGLBuffer> prevBuffer = mContext->mBoundArrayBuffer;
     mContext->BindBuffer(LOCAL_GL_ELEMENT_ARRAY_BUFFER, mElementArrayBuffer);
 
-    for (size_t i = 0; i < mAttribs.Length(); ++i) {
-        const WebGLVertexAttribData& vd = mAttribs[i];
+    size_t i = 0;
+    for (const auto& vd : mAttribs) {
+        mContext->BindBuffer(LOCAL_GL_ARRAY_BUFFER, vd.mBuf);
+        vd.DoVertexAttribPointer(gl, i);
 
-        mContext->BindBuffer(LOCAL_GL_ARRAY_BUFFER, vd.buf);
-
-        if (vd.integer) {
-            gl->fVertexAttribIPointer(i, vd.size, vd.type, vd.stride,
-                                      reinterpret_cast<const GLvoid*>(vd.byteOffset));
+        if (vd.mEnabled) {
+            gl->fEnableVertexAttribArray(i);
         } else {
-            gl->fVertexAttribPointer(i, vd.size, vd.type, vd.normalized, vd.stride,
-                                     reinterpret_cast<const GLvoid*>(vd.byteOffset));
+            gl->fDisableVertexAttribArray(i);
         }
-
-        if (vd.enabled)
-            gl->fEnableVertexAttribArray(i);
-        else
-            gl->fDisableVertexAttribArray(i);
+        ++i;
     }
 
     size_t len = prevVertexArray->mAttribs.Length();
-    for (size_t i = mAttribs.Length(); i < len; ++i) {
-        const WebGLVertexAttribData& vd = prevVertexArray->mAttribs[i];
+    for (; i < len; ++i) {
+        const auto& vd = prevVertexArray->mAttribs[i];
 
-        if (vd.enabled)
+        if (vd.mEnabled) {
             gl->fDisableVertexAttribArray(i);
+        }
     }
 
     mContext->BindBuffer(LOCAL_GL_ARRAY_BUFFER, prevBuffer);
     mIsVAO = true;
 }
 
 void
 WebGLVertexArrayFake::DeleteImpl()
new file mode 100644
--- /dev/null
+++ b/dom/canvas/WebGLVertexAttribData.cpp
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 4; 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 "WebGLVertexAttribData.h"
+
+#include "GLContext.h"
+
+namespace mozilla {
+
+static uint8_t
+CalcBytesPerVertex(GLenum type, uint8_t size)
+{
+    uint8_t bytesPerType;
+    switch (type) {
+    case LOCAL_GL_INT_2_10_10_10_REV:
+    case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV:
+        return 4;
+
+    case LOCAL_GL_BYTE:
+    case LOCAL_GL_UNSIGNED_BYTE:
+        bytesPerType = 1;
+        break;
+
+    case LOCAL_GL_HALF_FLOAT:
+    case LOCAL_GL_SHORT:
+    case LOCAL_GL_UNSIGNED_SHORT:
+        bytesPerType = 2;
+        break;
+
+    case LOCAL_GL_FIXED: // GLES 3.0.4 p9: 32-bit signed, with 16 fractional bits.
+    case LOCAL_GL_FLOAT:
+    case LOCAL_GL_INT:
+    case LOCAL_GL_UNSIGNED_INT:
+        bytesPerType = 4;
+        break;
+
+    default:
+        MOZ_CRASH("Bad `type`.");
+    }
+
+    return bytesPerType * size;
+}
+
+void
+WebGLVertexAttribData::VertexAttribPointer(bool integerFunc, WebGLBuffer* buf,
+                                           uint8_t size, GLenum type, bool normalized,
+                                           uint32_t stride, uint64_t byteOffset)
+{
+    mIntegerFunc = integerFunc;
+    mBuf = buf;
+    mType = type;
+    mSize = size;
+    mBytesPerVertex = CalcBytesPerVertex(mType, mSize);
+    mNormalized = normalized;
+    mStride = stride;
+    mExplicitStride = (mStride ? mStride : mBytesPerVertex);
+    mByteOffset = byteOffset;
+}
+
+void
+WebGLVertexAttribData::DoVertexAttribPointer(gl::GLContext* gl, GLuint index) const
+{
+    if (mIntegerFunc) {
+        gl->fVertexAttribIPointer(index, mSize, mType, mStride,
+                                  (const void*)mByteOffset);
+    } else {
+        gl->fVertexAttribPointer(index, mSize, mType, mNormalized, mStride,
+                                 (const void*)mByteOffset);
+    }
+}
+
+} // namespace mozilla
--- a/dom/canvas/WebGLVertexAttribData.h
+++ b/dom/canvas/WebGLVertexAttribData.h
@@ -8,82 +8,69 @@
 
 #include "GLDefs.h"
 #include "WebGLObjectModel.h"
 
 namespace mozilla {
 
 class WebGLBuffer;
 
-struct WebGLVertexAttribData
+class WebGLVertexAttribData final
 {
+public:
+    uint32_t mDivisor;
+    bool mEnabled;
+
+private:
+    bool mIntegerFunc;
+public:
+    WebGLRefPtr<WebGLBuffer> mBuf;
+private:
+    GLenum mType;
+    uint8_t mSize; // num of mType vals per vert
+    uint8_t mBytesPerVertex;
+    bool mNormalized;
+    uint32_t mStride; // bytes
+    uint32_t mExplicitStride;
+    uint64_t mByteOffset;
+
+public:
+
+#define GETTER(X) const decltype(m##X)& X() const { return m##X; }
+
+    GETTER(IntegerFunc)
+    GETTER(Type)
+    GETTER(Size)
+    GETTER(BytesPerVertex)
+    GETTER(Normalized)
+    GETTER(Stride)
+    GETTER(ExplicitStride)
+    GETTER(ByteOffset)
+
+#undef GETTER
+
     // note that these initial values are what GL initializes vertex attribs to
     WebGLVertexAttribData()
-        : buf(0)
-        , stride(0)
-        , size(4)
-        , divisor(0) // OpenGL ES 3.0 specs paragraphe 6.2 p240
-        , byteOffset(0)
-        , type(LOCAL_GL_FLOAT)
-        , enabled(false)
-        , normalized(false)
-        , integer(false)
-    {}
-
-    WebGLRefPtr<WebGLBuffer> buf;
-    GLuint stride;
-    GLuint size;
-    GLuint divisor;
-    GLuint byteOffset;
-    GLenum type;
-    bool enabled;
-    bool normalized;
-    bool integer;
-
-    GLuint componentSize() const {
-        switch(type) {
-        case LOCAL_GL_BYTE:
-        case LOCAL_GL_UNSIGNED_BYTE:
-            return 1;
-
-        case LOCAL_GL_SHORT:
-        case LOCAL_GL_UNSIGNED_SHORT:
-        case LOCAL_GL_HALF_FLOAT:
-        case LOCAL_GL_HALF_FLOAT_OES:
-            return 2;
-
-        case LOCAL_GL_INT:
-        case LOCAL_GL_UNSIGNED_INT:
-        case LOCAL_GL_FLOAT:
-            return 4;
-
-        default:
-            MOZ_ASSERT(false, "Should never get here!");
-            return 0;
-        }
+        : mDivisor(0)
+        , mEnabled(false)
+    {
+        VertexAttribPointer(false, nullptr, 4, LOCAL_GL_FLOAT, false, 0, 0);
     }
 
-    GLuint actualStride() const {
-        if (stride)
-            return stride;
+    void VertexAttribPointer(bool integerFunc, WebGLBuffer* buf, uint8_t size,
+                             GLenum type, bool normalized, uint32_t stride,
+                             uint64_t byteOffset);
 
-        return size * componentSize();
-    }
+    void DoVertexAttribPointer(gl::GLContext* gl, GLuint index) const;
 };
 
 } // namespace mozilla
 
 inline void
-ImplCycleCollectionUnlink(mozilla::WebGLVertexAttribData& field)
-{
-    field.buf = nullptr;
-}
-
-inline void
 ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
                             mozilla::WebGLVertexAttribData& field,
                             const char* name,
                             uint32_t flags = 0)
 {
-    CycleCollectionNoteChild(callback, field.buf.get(), name, flags);
+    CycleCollectionNoteChild(callback, field.mBuf.get(), name, flags);
 }
 
 #endif // WEBGL_VERTEX_ATTRIB_DATA_H_
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -152,16 +152,17 @@ UNIFIED_SOURCES += [
     'WebGLTimerQuery.cpp',
     'WebGLTransformFeedback.cpp',
     'WebGLUniformLocation.cpp',
     'WebGLValidateStrings.cpp',
     'WebGLVertexArray.cpp',
     'WebGLVertexArrayFake.cpp',
     'WebGLVertexArrayGL.cpp',
     'WebGLVertexArrayObject.cpp',
+    'WebGLVertexAttribData.cpp',
 ]
 
 SOURCES += [
     'MurmurHash3.cpp',
 ]
 
 # Suppress warnings from third-party code.
 if CONFIG['CLANG_CXX']: