Bug 1305190 - Fix vertex attrib elem size calculations. - r=ethlin
authorJeff Gilbert (:jgilbert) <jgilbert@mozilla.com>
Fri, 23 Sep 2016 17:28:58 -0700
changeset 315914 0d50320d6916ab7c21348a7532c828e5390ba0a6
parent 315913 8b70b53f6f27a27716706ed305ff69cb17b59606
child 315915 42a1e4dd9f789675c3d8656b4a340a162ec590e1
push id30757
push usercbook@mozilla.com
push dateFri, 30 Sep 2016 10:02:43 +0000
treeherdermozilla-central@5ffed033557e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersethlin
bugs1305190
milestone52.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 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(integerFunc, 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
@@ -528,17 +528,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']: