Bug 1048731 - [WebGL2] Implement new buffer binding targets. r=jgilbert draft
authorDan Glastonbury <dglastonbury@mozilla.com>
Fri, 07 Nov 2014 16:59:20 +1000
changeset 226505 3936a97a9ae652b76b359b052a1476164280681f
parent 226504 6d67faad56e5104c157d063fe68bbbbe21a79435
child 226506 5aae98675f87eb3462fb125fad8caa811603d491
push id53
push userdglastonbury@mozilla.com
push dateWed, 12 Nov 2014 02:04:58 +0000
reviewersjgilbert
bugs1048731
milestone36.0a1
Bug 1048731 - [WebGL2] Implement new buffer binding targets. r=jgilbert
dom/canvas/WebGL1Context.h
dom/canvas/WebGL1ContextBuffers.cpp
dom/canvas/WebGL2Context.cpp
dom/canvas/WebGL2Context.h
dom/canvas/WebGL2ContextBuffers.cpp
dom/canvas/WebGLContext.cpp
dom/canvas/WebGLContext.h
dom/canvas/WebGLContextBuffers.cpp
dom/canvas/WebGLContextUnchecked.cpp
dom/canvas/WebGLContextUnchecked.h
dom/canvas/WebGLContextValidate.cpp
dom/canvas/WebGLVertexArrayFake.cpp
--- a/dom/canvas/WebGL1Context.h
+++ b/dom/canvas/WebGL1Context.h
@@ -37,13 +37,14 @@ public:
     // IMPLEMENT nsWrapperCache
 
     virtual JSObject* WrapObject(JSContext *cx) MOZ_OVERRIDE;
 
 private:
     virtual bool ValidateAttribPointerType(bool integerMode, GLenum type, GLsizei* alignment, const char* info) MOZ_OVERRIDE;
     virtual bool ValidateBufferTarget(GLenum target, const char* info) MOZ_OVERRIDE;
     virtual bool ValidateBufferIndexedTarget(GLenum target, const char* info) MOZ_OVERRIDE;
+    virtual bool ValidateBufferForTarget(GLenum target, WebGLBuffer* buffer, const char* info) MOZ_OVERRIDE;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/canvas/WebGL1ContextBuffers.cpp
+++ b/dom/canvas/WebGL1ContextBuffers.cpp
@@ -27,8 +27,22 @@ WebGL1Context::ValidateBufferTarget(GLen
 }
 
 bool
 WebGL1Context::ValidateBufferIndexedTarget(GLenum target, const char* info)
 {
     ErrorInvalidEnumInfo(info, target);
     return false;
 }
+
+/** Buffer and Target validation for BindBuffer */
+bool
+WebGL1Context::ValidateBufferForTarget(GLenum target, WebGLBuffer* buffer, const char* info)
+{
+    if (!buffer)
+        return true;
+
+    bool matchingBinding = !buffer->HasEverBeenBound() || buffer->Target() == target;
+    if (!matchingBinding)
+        ErrorInvalidOperation("%s: buffer already bound to a different target", info);
+
+    return matchingBinding;
+}
--- a/dom/canvas/WebGL2Context.cpp
+++ b/dom/canvas/WebGL2Context.cpp
@@ -73,14 +73,18 @@ WebGLContext::InitWebGL2()
         {
             GenerateWarning("WebGL 2 requires GLFeature::%s!", GLContext::GetFeatureName(feature));
             return false;
         }
     }
 
     // we initialise WebGL 2 related stuff.
     gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &mGLMaxTransformFeedbackSeparateAttribs);
+    gl->GetUIntegerv(LOCAL_GL_MAX_UNIFORM_BUFFER_BINDINGS, &mGLMaxUniformBufferBindings);
+
+    mBoundTransformFeedbackBuffers = MakeUnique<WebGLBufferRefPtr[]>(mGLMaxTransformFeedbackSeparateAttribs);
+    mBoundUniformBuffers = MakeUnique<WebGLBufferRefPtr[]>(mGLMaxUniformBufferBindings);
 
     mDefaultTransformFeedback = new WebGLTransformFeedback(this);
     mBoundTransformFeedback = mDefaultTransformFeedback;
 
     return true;
 }
--- a/dom/canvas/WebGL2Context.h
+++ b/dom/canvas/WebGL2Context.h
@@ -261,13 +261,14 @@ private:
     bool ValidateSizedInternalFormat(GLenum internalFormat, const char* info);
     bool ValidateTexStorage(GLenum target, GLsizei levels, GLenum internalformat,
                                 GLsizei width, GLsizei height, GLsizei depth,
                                 const char* info);
 
     virtual bool ValidateAttribPointerType(bool integerMode, GLenum type, GLsizei* alignment, const char* info) MOZ_OVERRIDE;
     virtual bool ValidateBufferTarget(GLenum target, const char* info) MOZ_OVERRIDE;
     virtual bool ValidateBufferIndexedTarget(GLenum target, const char* info) MOZ_OVERRIDE;
+    virtual bool ValidateBufferForTarget(GLenum target, WebGLBuffer* buffer, const char* info) MOZ_OVERRIDE;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/canvas/WebGL2ContextBuffers.cpp
+++ b/dom/canvas/WebGL2ContextBuffers.cpp
@@ -8,18 +8,23 @@
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 bool
 WebGL2Context::ValidateBufferTarget(GLenum target, const char* info)
 {
     bool valid = (target == LOCAL_GL_ARRAY_BUFFER ||
+                  target == LOCAL_GL_COPY_READ_BUFFER ||
+                  target == LOCAL_GL_COPY_WRITE_BUFFER ||
                   target == LOCAL_GL_ELEMENT_ARRAY_BUFFER ||
-                  target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER);
+                  target == LOCAL_GL_PIXEL_PACK_BUFFER ||
+                  target == LOCAL_GL_PIXEL_UNPACK_BUFFER ||
+                  target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER ||
+                  target == LOCAL_GL_UNIFORM_BUFFER);
 
     if (!valid)
         ErrorInvalidEnumInfo(info, target);
 
     return valid;
 }
 
 bool
@@ -28,25 +33,87 @@ WebGL2Context::ValidateBufferIndexedTarg
     bool valid = (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER ||
                   target == LOCAL_GL_UNIFORM_BUFFER);
     if (!valid)
         ErrorInvalidEnumInfo(info, target);
 
     return valid;
 }
 
+bool
+WebGL2Context::ValidateBufferForTarget(GLenum target, WebGLBuffer* buffer, const char* info)
+{
+    if (!buffer)
+        return true;
+
+    switch (target) {
+    case LOCAL_GL_COPY_READ_BUFFER:
+    case LOCAL_GL_COPY_WRITE_BUFFER:
+        return true;
+
+    case LOCAL_GL_ELEMENT_ARRAY_BUFFER:
+        return !buffer->HasEverBeenBound() ||
+            buffer->Target() == LOCAL_GL_ELEMENT_ARRAY_BUFFER;
+
+    case LOCAL_GL_ARRAY_BUFFER:
+    case LOCAL_GL_PIXEL_PACK_BUFFER:
+    case LOCAL_GL_PIXEL_UNPACK_BUFFER:
+    case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
+    case LOCAL_GL_UNIFORM_BUFFER:
+        return !buffer->HasEverBeenBound() ||
+            buffer->Target() != LOCAL_GL_ELEMENT_ARRAY_BUFFER;
+    }
+
+    ErrorInvalidOperation("%s: buffer already bound to a incompatible target %s",
+                          info, EnumName(buffer->Target().get()));
+    return false;
+}
+
 // -------------------------------------------------------------------------
 // Buffer objects
 
 void
 WebGL2Context::CopyBufferSubData(GLenum readTarget, GLenum writeTarget, GLintptr readOffset,
                                  GLintptr writeOffset, GLsizeiptr size)
 {
-    MakeContextCurrent();
-    gl->fCopyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size);
+    if (IsContextLost())
+        return;
+
+    if (!ValidateBufferTarget(readTarget, "copyBufferSubData") ||
+        !ValidateBufferTarget(writeTarget, "copyBufferSubData"))
+    {
+        return;
+    }
+
+    WebGLBufferRefPtr* readBufferSlot = GetBufferSlotByTarget(readTarget);
+    WebGLBufferRefPtr* writeBufferSlot = GetBufferSlotByTarget(writeTarget);
+    if (!readBufferSlot || !writeBufferSlot)
+        return;
+
+    WebGLBuffer* readBuffer = readBufferSlot->get();
+    if (!readBuffer)
+        return ErrorInvalidOperation("copyBufferSubData: No buffer bound to readTarget");
+
+    WebGLBuffer* writeBuffer = writeBufferSlot->get();
+    if (!writeBuffer)
+        return ErrorInvalidOperation("copyBufferSubData: No buffer bound to writeTarget");
+
+    if (!ValidateDataOffsetSize(readOffset, size, readBuffer->ByteLength(), "copyBufferSubData"))
+        return;
+
+    if (!ValidateDataOffsetSize(writeOffset, size, writeBuffer->ByteLength(), "copyBufferSubData"))
+        return;
+
+    if (readTarget == writeTarget &&
+        !ValidateDataRanges(readOffset, writeOffset, size, "copyBufferSubData"))
+    {
+        return;
+    }
+
+    WebGLContextUnchecked::CopyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size);
 }
 
 void
 WebGL2Context::GetBufferSubData(GLenum target, GLintptr offset, const dom::ArrayBuffer& returnedData)
 {
     MOZ_CRASH("Not Implemented.");
 }
 
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -279,16 +279,17 @@ WebGLContext::WebGLContext()
     mGLMaxTextureImageUnits = 0;
     mGLMaxVertexTextureImageUnits = 0;
     mGLMaxVaryingVectors = 0;
     mGLMaxFragmentUniformVectors = 0;
     mGLMaxVertexUniformVectors = 0;
     mGLMaxColorAttachments = 1;
     mGLMaxDrawBuffers = 1;
     mGLMaxTransformFeedbackSeparateAttribs = 0;
+    mGLMaxUniformBufferBindings = 0;
 
     // See OpenGL ES 2.0.25 spec, 6.2 State Tables, table 6.13
     mPixelStorePackAlignment = 4;
     mPixelStoreUnpackAlignment = 4;
 
     WebGLMemoryTracker::AddWebGLContext(this);
 
     mAllowContextRestore = true;
@@ -344,29 +345,37 @@ WebGLContext::DestroyResourcesAndContext
         return;
 
     gl->MakeCurrent();
 
     mBound2DTextures.Clear();
     mBoundCubeMapTextures.Clear();
     mBound3DTextures.Clear();
     mBoundArrayBuffer = nullptr;
+    mBoundCopyReadBuffer = nullptr;
+    mBoundCopyWriteBuffer = nullptr;
+    mBoundPixelPackBuffer = nullptr;
+    mBoundPixelUnpackBuffer = nullptr;
     mBoundTransformFeedbackBuffer = nullptr;
+    mBoundUniformBuffer = nullptr;
     mCurrentProgram = nullptr;
     mBoundFramebuffer = nullptr;
     mActiveOcclusionQuery = nullptr;
     mBoundRenderbuffer = nullptr;
     mBoundVertexArray = nullptr;
     mDefaultVertexArray = nullptr;
     mBoundTransformFeedback = nullptr;
     mDefaultTransformFeedback = nullptr;
 
     for (GLuint i = 0; i < mGLMaxTransformFeedbackSeparateAttribs; i++)
         mBoundTransformFeedbackBuffers[i] = nullptr;
 
+    for (GLuint i = 0; i < mGLMaxUniformBufferBindings; i++)
+        mBoundUniformBuffers[i] = nullptr;
+
     while (!mTextures.isEmpty())
         mTextures.getLast()->DeleteOnce();
     while (!mVertexArrays.isEmpty())
         mVertexArrays.getLast()->DeleteOnce();
     while (!mBuffers.isEmpty())
         mBuffers.getLast()->DeleteOnce();
     while (!mRenderbuffers.isEmpty())
         mRenderbuffers.getLast()->DeleteOnce();
@@ -1891,17 +1900,22 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(WebGLCo
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLContext,
   mCanvasElement,
   mExtensions,
   mBound2DTextures,
   mBoundCubeMapTextures,
   mBound3DTextures,
   mBoundArrayBuffer,
+  mBoundCopyReadBuffer,
+  mBoundCopyWriteBuffer,
+  mBoundPixelPackBuffer,
+  mBoundPixelUnpackBuffer,
   mBoundTransformFeedbackBuffer,
+  mBoundUniformBuffer,
   mCurrentProgram,
   mBoundFramebuffer,
   mBoundRenderbuffer,
   mBoundVertexArray,
   mDefaultVertexArray,
   mActiveOcclusionQuery,
   mActiveTransformFeedbackQuery)
 
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -94,16 +94,17 @@ template<typename> struct Nullable;
 }
 
 namespace gfx {
 class SourceSurface;
 }
 
 typedef WebGLRefPtr<WebGLBuffer> WebGLBufferRefPtr;
 typedef WebGLRefPtr<WebGLFramebuffer> WebGLFramebufferRefPtr;
+typedef WebGLRefPtr<WebGLProgram> WebGLProgramRefPtr;
 typedef WebGLRefPtr<WebGLQuery> WebGLQueryRefPtr;
 typedef WebGLRefPtr<WebGLRenderbuffer> WebGLRenderbufferRefPtr;
 typedef WebGLRefPtr<WebGLTexture> WebGLTextureRefPtr;
 typedef WebGLRefPtr<WebGLTransformFeedback> WebGLTransformFeedbackRefPtr;
 typedef WebGLRefPtr<WebGLVertexArray> WebGLVertexArrayRefPtr;
 
 WebGLTexelFormat GetWebGLTexelFormat(TexInternalFormat format);
 
@@ -247,17 +248,17 @@ public:
 
     const char *ErrorName(GLenum error);
 
     /**
      * Return displayable name for GLenum.
      * This version is like gl::GLenumToStr but with out the GL_ prefix to
      * keep consistency with how errors are reported from WebGL.
      */
-    static const char *EnumName(GLenum glenum);
+    static const char* EnumName(GLenum glenum);
 
     bool IsCompressedTextureFormat(GLenum format);
     bool IsTextureFormatCompressed(TexInternalFormat format);
 
     void DummyFramebufferOperation(const char *info);
 
     WebGLTexture* activeBoundTextureForTarget(const TexTarget texTarget) const {
         switch (texTarget.get()) {
@@ -827,41 +828,63 @@ public:
 // -----------------------------------------------------------------------------
 // WEBGL_lose_context
 public:
     void LoseContext();
     void RestoreContext();
 
 // -----------------------------------------------------------------------------
 // Buffer Objects (WebGLContextBuffers.cpp)
+private:
+    void UpdateBoundBuffer(GLenum target, WebGLBuffer* buffer);
+    void UpdateBoundBufferIndexed(GLenum target, GLuint index, WebGLBuffer* buffer);
+
 public:
-    void BindBuffer(GLenum target, WebGLBuffer* buf);
+    void BindBuffer(GLenum target, WebGLBuffer* buffer);
     void BindBufferBase(GLenum target, GLuint index, WebGLBuffer* buffer);
     void BindBufferRange(GLenum target, GLuint index, WebGLBuffer* buffer,
                          WebGLintptr offset, WebGLsizeiptr size);
+
+private:
+    void BufferDataUnchecked(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage);
+    void BufferData(GLenum target, WebGLsizeiptr size, void* data, GLenum usage);
+
+public:
     void BufferData(GLenum target, WebGLsizeiptr size, GLenum usage);
     void BufferData(GLenum target, const dom::ArrayBufferView &data,
                     GLenum usage);
     void BufferData(GLenum target,
                     const Nullable<dom::ArrayBuffer> &maybeData,
                     GLenum usage);
+
+private:
+    void BufferSubDataUnchecked(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data);
+    void BufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data);
+
+public:
     void BufferSubData(GLenum target, WebGLsizeiptr byteOffset,
                        const dom::ArrayBufferView &data);
     void BufferSubData(GLenum target, WebGLsizeiptr byteOffset,
                        const Nullable<dom::ArrayBuffer> &maybeData);
     already_AddRefed<WebGLBuffer> CreateBuffer();
     void DeleteBuffer(WebGLBuffer *buf);
     bool IsBuffer(WebGLBuffer *buffer);
 
 protected:
     // bound buffer state
     WebGLBufferRefPtr mBoundArrayBuffer;
+    WebGLBufferRefPtr mBoundCopyReadBuffer;
+    WebGLBufferRefPtr mBoundCopyWriteBuffer;
+    WebGLBufferRefPtr mBoundPixelPackBuffer;
+    WebGLBufferRefPtr mBoundPixelUnpackBuffer;
     WebGLBufferRefPtr mBoundTransformFeedbackBuffer;
+    WebGLBufferRefPtr mBoundUniformBuffer;
 
     UniquePtr<WebGLBufferRefPtr[]> mBoundTransformFeedbackBuffers;
+    UniquePtr<WebGLBufferRefPtr[]> mBoundUniformBuffers;
 
     WebGLBufferRefPtr* GetBufferSlotByTarget(GLenum target);
     WebGLBufferRefPtr* GetBufferSlotByTargetIndexed(GLenum target, GLuint index);
     bool ValidateBufferUsageEnum(GLenum target, const char* infos);
 
 // -----------------------------------------------------------------------------
 // Queries (WebGL2ContextQueries.cpp)
 protected:
@@ -962,25 +985,16 @@ public:
 private:
     // Cache the max number of vertices and instances that can be read from
     // bound VBOs (result of ValidateBuffers).
     bool mBufferFetchingIsVerified;
     bool mBufferFetchingHasPerVertex;
     uint32_t mMaxFetchedVertices;
     uint32_t mMaxFetchedInstances;
 
-protected:
-    inline void InvalidateBufferFetching()
-    {
-        mBufferFetchingIsVerified = false;
-        mBufferFetchingHasPerVertex = false;
-        mMaxFetchedVertices = 0;
-        mMaxFetchedInstances = 0;
-    }
-
     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,
                             GLuint* out_upperBound);
     bool DrawInstanced_check(const char* info);
     void Draw_cleanup();
 
     void VertexAttrib1fv_base(GLuint idx, uint32_t arrayLength, const GLfloat* ptr);
@@ -1018,16 +1032,24 @@ protected:
 
     virtual JS::Value GetTexParameterInternal(const TexTarget& target, GLenum pname);
 
     // Returns x rounded to the next highest multiple of y.
     static CheckedUint32 RoundedToNextMultipleOf(CheckedUint32 x, CheckedUint32 y) {
         return ((x + y - 1) / y) * y;
     }
 
+    inline void InvalidateBufferFetching()
+    {
+        mBufferFetchingIsVerified = false;
+        mBufferFetchingHasPerVertex = false;
+        mMaxFetchedVertices = 0;
+        mMaxFetchedInstances = 0;
+    }
+
     CheckedUint32 mGeneration;
 
     WebGLContextOptions mOptions;
 
     bool mInvalidated;
     bool mResetLayer;
     bool mOptionsFrozen;
     bool mMinCapability;
@@ -1065,16 +1087,17 @@ protected:
     int32_t mGLMaxTextureImageUnits;
     int32_t mGLMaxVertexTextureImageUnits;
     int32_t mGLMaxVaryingVectors;
     int32_t mGLMaxFragmentUniformVectors;
     int32_t mGLMaxVertexUniformVectors;
     int32_t mGLMaxColorAttachments;
     int32_t mGLMaxDrawBuffers;
     GLuint  mGLMaxTransformFeedbackSeparateAttribs;
+    GLuint  mGLMaxUniformBufferBindings;
 
 public:
     GLuint MaxVertexAttribs() const {
         return mGLMaxVertexAttribs;
     }
 
 protected:
     // Represents current status of the context with respect to context loss.
@@ -1131,16 +1154,18 @@ protected:
     // Validation functions (implemented in WebGLContextValidate.cpp)
     bool CreateOffscreenGL(bool forceEnabled);
     bool InitAndValidateGL();
     bool ResizeBackbuffer(uint32_t width, uint32_t height);
     bool ValidateBlendEquationEnum(GLenum cap, const char *info);
     bool ValidateBlendFuncDstEnum(GLenum mode, const char *info);
     bool ValidateBlendFuncSrcEnum(GLenum mode, const char *info);
     bool ValidateBlendFuncEnumsCompatibility(GLenum sfactor, GLenum dfactor, const char *info);
+    bool ValidateDataOffsetSize(WebGLintptr offset, WebGLsizeiptr size, WebGLsizeiptr bufferSize, const char* info);
+    bool ValidateDataRanges(WebGLintptr readOffset, WebGLintptr writeOffset, WebGLsizeiptr size, const char* info);
     bool ValidateTextureTargetEnum(GLenum target, const char *info);
     bool ValidateComparisonEnum(GLenum target, const char *info);
     bool ValidateStencilOpEnum(GLenum action, const char *info);
     bool ValidateFaceEnum(GLenum face, const char *info);
     bool ValidateTexInputData(GLenum type,
                               js::Scalar::Type jsArrayType,
                               WebGLTexImageFunc func,
                               WebGLTexDimensions dims);
@@ -1298,16 +1323,17 @@ private:
     bool ValidateObjectAssumeNonNull(const char* info, ObjectType *aObject);
 
 private:
     // -------------------------------------------------------------------------
     // Context customization points
     virtual bool ValidateAttribPointerType(bool integerMode, GLenum type, GLsizei* alignment, const char* info) = 0;
     virtual bool ValidateBufferTarget(GLenum target, const char* info) = 0;
     virtual bool ValidateBufferIndexedTarget(GLenum target, const char* info) = 0;
+    virtual bool ValidateBufferForTarget(GLenum target, WebGLBuffer* buffer, const char* info) = 0;
 
 protected:
     int32_t MaxTextureSizeForTarget(TexTarget target) const {
         return (target == LOCAL_GL_TEXTURE_2D) ? mGLMaxTextureSize : mGLMaxCubeMapTextureSize;
     }
 
     int32_t MaxTextureLevelForTexImageTarget(TexImageTarget texImageTarget) const {
         const TexTarget target = TexImageTargetToTexTarget(texImageTarget);
@@ -1334,17 +1360,17 @@ protected:
 
     void ForceLoseContext(bool simulateLosing = false);
     void ForceRestoreContext();
 
     nsTArray<WebGLTextureRefPtr> mBound2DTextures;
     nsTArray<WebGLTextureRefPtr> mBoundCubeMapTextures;
     nsTArray<WebGLTextureRefPtr> mBound3DTextures;
 
-    WebGLRefPtr<WebGLProgram> mCurrentProgram;
+    WebGLProgramRefPtr mCurrentProgram;
 
     uint32_t mMaxFramebufferColorAttachments;
 
     WebGLFramebufferRefPtr mBoundFramebuffer;
     WebGLRenderbufferRefPtr mBoundRenderbuffer;
     WebGLTransformFeedbackRefPtr mBoundTransformFeedback;
     WebGLVertexArrayRefPtr mBoundVertexArray;
 
@@ -1356,18 +1382,18 @@ protected:
     LinkedList<WebGLRenderbuffer> mRenderbuffers;
     LinkedList<WebGLFramebuffer> mFramebuffers;
     LinkedList<WebGLVertexArray> mVertexArrays;
 
     // TODO(djg): Does this need a rethink? Should it be WebGL2Context?
     LinkedList<WebGLSampler> mSamplers;
     LinkedList<WebGLTransformFeedback> mTransformFeedbacks;
 
-    WebGLRefPtr<WebGLVertexArray> mDefaultVertexArray;
     WebGLTransformFeedbackRefPtr mDefaultTransformFeedback;
+    WebGLVertexArrayRefPtr mDefaultVertexArray;
 
     // PixelStore parameters
     uint32_t mPixelStorePackAlignment, mPixelStoreUnpackAlignment, mPixelStoreColorspaceConversion;
     bool mPixelStoreFlipY, mPixelStorePremultiplyAlpha;
 
     WebGLContextFakeBlackStatus mFakeBlackStatus;
 
     class FakeBlackTexture
--- a/dom/canvas/WebGLContextBuffers.cpp
+++ b/dom/canvas/WebGLContextBuffers.cpp
@@ -7,93 +7,103 @@
 #include "GLContext.h"
 #include "WebGLBuffer.h"
 #include "WebGLVertexArray.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 void
-WebGLContext::BindBuffer(GLenum target, WebGLBuffer *buffer)
+WebGLContext::UpdateBoundBuffer(GLenum target, WebGLBuffer* buffer)
+{
+    WebGLBufferRefPtr* bufferSlot = GetBufferSlotByTarget(target);
+    if (!bufferSlot)
+        return;
+
+    *bufferSlot = buffer;
+
+    if (!buffer)
+        return;
+
+    /* https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.1 */
+    if (target != LOCAL_GL_COPY_READ_BUFFER && target != LOCAL_GL_COPY_WRITE_BUFFER)
+        buffer->BindTo(target);
+}
+
+void
+WebGLContext::UpdateBoundBufferIndexed(GLenum target, GLuint index, WebGLBuffer* buffer)
+{
+    UpdateBoundBuffer(target, buffer);
+
+    WebGLBufferRefPtr* bufferIndexSlot = GetBufferSlotByTargetIndexed(target, index);
+    *bufferIndexSlot = buffer;
+}
+
+void
+WebGLContext::BindBuffer(GLenum target, WebGLBuffer* buffer)
 {
     if (IsContextLost())
         return;
 
     if (!ValidateObjectAllowDeletedOrNull("bindBuffer", buffer))
         return;
 
     // silently ignore a deleted buffer
     if (buffer && buffer->IsDeleted())
         return;
 
     if (!ValidateBufferTarget(target, "bindBuffer"))
         return;
 
-    WebGLRefPtr<WebGLBuffer>* bufferSlot = GetBufferSlotByTarget(target);
-    MOZ_ASSERT(bufferSlot);
+    if (!ValidateBufferForTarget(target, buffer, "bindBuffer"))
+        return;
 
-    if (buffer) {
-        if (!buffer->HasEverBeenBound()) {
-            buffer->BindTo(target);
-        } else if (target != buffer->Target()) {
-            return ErrorInvalidOperation("bindBuffer: buffer already bound to a different target");
-        }
-    }
+    WebGLContextUnchecked::BindBuffer(target, buffer);
 
-    *bufferSlot = buffer;
-
-    MakeContextCurrent();
-
-    gl->fBindBuffer(target, buffer ? buffer->GLName() : 0);
+    UpdateBoundBuffer(target, buffer);
 }
 
 void
 WebGLContext::BindBufferBase(GLenum target, GLuint index, WebGLBuffer* buffer)
 {
     if (IsContextLost())
         return;
 
     if (!ValidateObjectAllowDeletedOrNull("bindBufferBase", buffer))
         return;
 
     // silently ignore a deleted buffer
-    if (buffer && buffer->IsDeleted()) {
+    if (buffer && buffer->IsDeleted())
         return;
-    }
 
     // ValidateBufferTarget
     switch (target) {
     case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
         if (index >= mGLMaxTransformFeedbackSeparateAttribs)
             return ErrorInvalidValue("bindBufferBase: index should be less than "
                                      "MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS");
+
+    case LOCAL_GL_UNIFORM_BUFFER:
+        if (index >= mGLMaxUniformBufferBindings)
+            return ErrorInvalidValue("bindBufferBase: index should be less than "
+                                     "MAX_UNIFORM_BUFFER_BINDINGS");
+
     default:
         return ErrorInvalidEnumInfo("bindBufferBase: target", target);
     }
 
     WebGLBufferRefPtr* indexedBufferSlot = GetBufferSlotByTargetIndexed(target, index);
     MOZ_ASSERT(indexedBufferSlot);
 
-    if (buffer) {
-        if (!buffer->HasEverBeenBound())
-            buffer->BindTo(target);
-
-        if (target != buffer->Target())
-            return ErrorInvalidOperation("bindBuffer: buffer already bound to a different target");
-    }
+    if (!ValidateBufferForTarget(target, buffer, "bindBufferBase"))
+        return;
 
-    WebGLRefPtr<WebGLBuffer>* bufferSlot = GetBufferSlotByTarget(target);
-    MOZ_ASSERT(bufferSlot, "GetBufferSlotByTarget(Indexed) mismatch");
+    WebGLContextUnchecked::BindBufferBase(target, index, buffer);
 
-    *indexedBufferSlot = buffer;
-    *bufferSlot = buffer;
-
-    MakeContextCurrent();
-
-    gl->fBindBufferBase(target, index, buffer ? buffer->GLName() : 0);
+    UpdateBoundBufferIndexed(target, index, buffer);
 }
 
 void
 WebGLContext::BindBufferRange(GLenum target, GLuint index, WebGLBuffer* buffer,
                               WebGLintptr offset, WebGLsizeiptr size)
 {
     if (IsContextLost())
         return;
@@ -107,47 +117,33 @@ WebGLContext::BindBufferRange(GLenum tar
 
     // ValidateBufferTarget
     switch (target) {
     case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
         if (index >= mGLMaxTransformFeedbackSeparateAttribs)
             return ErrorInvalidValue("bindBufferRange: index should be less than "
                                      "MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS");
 
+    case LOCAL_GL_UNIFORM_BUFFER:
+        if (index >= mGLMaxUniformBufferBindings)
+            return ErrorInvalidValue("bindBufferRange: index should be less than "
+                                     "MAX_UNIFORM_BUFFER_BINDINGS");
     default:
         return ErrorInvalidEnumInfo("bindBufferRange: target", target);
     }
 
     WebGLBufferRefPtr* indexedBufferSlot = GetBufferSlotByTargetIndexed(target, index);
     MOZ_ASSERT(indexedBufferSlot);
 
-    if (buffer) {
-        if (!buffer->HasEverBeenBound())
-            buffer->BindTo(target);
-
-        if (target != buffer->Target())
-            return ErrorInvalidOperation("bindBuffer: buffer already bound to a different target");
+    if (!ValidateBufferForTarget(target, buffer, "bindBufferRange"))
+        return;
 
-        CheckedInt<WebGLsizeiptr> checked_neededByteLength = CheckedInt<WebGLsizeiptr>(offset) + size;
-        if (!checked_neededByteLength.isValid() ||
-            checked_neededByteLength.value() > buffer->ByteLength())
-        {
-            return ErrorInvalidValue("bindBufferRange: invalid range");
-        }
-    }
+    WebGLContextUnchecked::BindBufferRange(target, index, buffer, offset, size);
 
-    WebGLBufferRefPtr* bufferSlot = GetBufferSlotByTarget(target);
-    MOZ_ASSERT(bufferSlot, "GetBufferSlotByTarget(Indexed) mismatch");
-
-    *indexedBufferSlot = buffer;
-    *bufferSlot = buffer;
-
-    MakeContextCurrent();
-
-    gl->fBindBufferRange(target, index, buffer ? buffer->GLName() : 0, offset, size);
+    UpdateBoundBufferIndexed(target, index, buffer);
 }
 
 void
 WebGLContext::BufferData(GLenum target, WebGLsizeiptr size,
                          GLenum usage)
 {
     if (IsContextLost())
         return;
@@ -256,17 +252,16 @@ WebGLContext::BufferData(GLenum target, 
 
     WebGLBufferRefPtr* bufferSlot = GetBufferSlotByTarget(target);
     MOZ_ASSERT(bufferSlot);
 
     if (!ValidateBufferUsageEnum(usage, "bufferData: usage"))
         return;
 
     WebGLBuffer* boundBuffer = bufferSlot->get();
-
     if (!boundBuffer)
         return ErrorInvalidOperation("bufferData: no buffer bound!");
 
     data.ComputeLengthAndData();
 
     // Careful: data.Length() could conceivably be any uint32_t, but GLsizeiptr
     // is like intptr_t.
     if (!CheckedInt<GLsizeiptr>(data.Length()).isValid())
@@ -304,17 +299,16 @@ WebGLContext::BufferSubData(GLenum targe
 
     WebGLBufferRefPtr* bufferSlot = GetBufferSlotByTarget(target);
     MOZ_ASSERT(bufferSlot);
 
     if (byteOffset < 0)
         return ErrorInvalidValue("bufferSubData: negative offset");
 
     WebGLBuffer* boundBuffer = bufferSlot->get();
-
     if (!boundBuffer)
         return ErrorInvalidOperation("bufferData: no buffer bound!");
 
     const ArrayBuffer& data = maybeData.Value();
     data.ComputeLengthAndData();
 
     CheckedInt<WebGLsizeiptr> checked_neededByteLength = CheckedInt<WebGLsizeiptr>(byteOffset) + data.Length();
     if (!checked_neededByteLength.isValid())
@@ -343,17 +337,16 @@ WebGLContext::BufferSubData(GLenum targe
 
     WebGLRefPtr<WebGLBuffer>* bufferSlot = GetBufferSlotByTarget(target);
     MOZ_ASSERT(bufferSlot);
 
     if (byteOffset < 0)
         return ErrorInvalidValue("bufferSubData: negative offset");
 
     WebGLBuffer* boundBuffer = bufferSlot->get();
-
     if (!boundBuffer)
         return ErrorInvalidOperation("bufferSubData: no buffer bound!");
 
     data.ComputeLengthAndData();
 
     CheckedInt<WebGLsizeiptr> checked_neededByteLength = CheckedInt<WebGLsizeiptr>(byteOffset) + data.Length();
     if (!checked_neededByteLength.isValid())
         return ErrorInvalidValue("bufferSubData: integer overflow computing the needed byte length");
@@ -435,32 +428,36 @@ WebGLContext::ValidateBufferUsageEnum(GL
     return false;
 }
 
 WebGLBufferRefPtr*
 WebGLContext::GetBufferSlotByTarget(GLenum target)
 {
     /* This function assumes that target has been validated for either WebGL1 or WebGL. */
     switch (target) {
-        case LOCAL_GL_ARRAY_BUFFER:              return &mBoundArrayBuffer;
-        case LOCAL_GL_ELEMENT_ARRAY_BUFFER:      return &mBoundVertexArray->mElementArrayBuffer;
-        case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER: return &mBoundTransformFeedbackBuffer;
-        default:
-            return nullptr;
+    case LOCAL_GL_ARRAY_BUFFER:              return &mBoundArrayBuffer;
+    case LOCAL_GL_COPY_READ_BUFFER:          return &mBoundCopyReadBuffer;
+    case LOCAL_GL_COPY_WRITE_BUFFER:         return &mBoundCopyWriteBuffer;
+    case LOCAL_GL_ELEMENT_ARRAY_BUFFER:      return &mBoundVertexArray->mElementArrayBuffer;
+    case LOCAL_GL_PIXEL_PACK_BUFFER:         return &mBoundPixelPackBuffer;
+    case LOCAL_GL_PIXEL_UNPACK_BUFFER:       return &mBoundPixelUnpackBuffer;
+    case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER: return &mBoundTransformFeedbackBuffer;
+    case LOCAL_GL_UNIFORM_BUFFER:            return &mBoundUniformBuffer;
+    default:
+        return nullptr;
     }
 }
 
 WebGLBufferRefPtr*
 WebGLContext::GetBufferSlotByTargetIndexed(GLenum target, GLuint index)
 {
     /* This function assumes that target has been validated for either WebGL1 or WebGL. */
     switch (target) {
-    case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
-        return &mBoundTransformFeedbackBuffers[index];
-
+    case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER: return &mBoundTransformFeedbackBuffers[index];
+    case LOCAL_GL_UNIFORM_BUFFER:            return &mBoundUniformBuffers[index];
     default:
         return nullptr;
     }
 }
 
 GLenum
 WebGLContext::CheckedBufferData(GLenum target,
                                 GLsizeiptr size,
@@ -471,22 +468,21 @@ WebGLContext::CheckedBufferData(GLenum t
     // bug 790879
     if (gl->WorkAroundDriverBugs() &&
         int64_t(size) > INT32_MAX) // the cast avoids a potential always-true warning on 32bit
     {
         GenerateWarning("Rejecting valid bufferData call with size %lu to avoid a Mac bug", size);
         return LOCAL_GL_INVALID_VALUE;
     }
 #endif
-    WebGLBuffer *boundBuffer = nullptr;
-    if (target == LOCAL_GL_ARRAY_BUFFER) {
-        boundBuffer = mBoundArrayBuffer;
-    } else if (target == LOCAL_GL_ELEMENT_ARRAY_BUFFER) {
-        boundBuffer = mBoundVertexArray->mElementArrayBuffer;
-    }
+
+    WebGLBufferRefPtr* bufferSlot = GetBufferSlotByTarget(target);
+    MOZ_ASSERT(bufferSlot);
+
+    WebGLBuffer* boundBuffer = bufferSlot->get();
     MOZ_ASSERT(boundBuffer != nullptr, "no buffer bound for this target");
 
     bool sizeChanges = uint32_t(size) != boundBuffer->ByteLength();
     if (sizeChanges) {
         GetAndFlushUnderlyingGLErrors();
         gl->fBufferData(target, size, data, usage);
         GLenum error = GetAndFlushUnderlyingGLErrors();
         return error;
--- a/dom/canvas/WebGLContextUnchecked.cpp
+++ b/dom/canvas/WebGLContextUnchecked.cpp
@@ -13,16 +13,48 @@
 namespace mozilla {
 
 WebGLContextUnchecked::WebGLContextUnchecked(gl::GLContext* gl)
     : gl(gl)
 { }
 
 
 // -----------------------------------------------------------------------------
+// Buffer Objects
+
+void
+WebGLContextUnchecked::BindBuffer(GLenum target, WebGLBuffer* buffer)
+{
+    gl->MakeCurrent();
+    gl->fBindBuffer(target, buffer ? buffer->GLName() : 0);
+}
+
+void
+WebGLContextUnchecked::BindBufferBase(GLenum target, GLuint index, WebGLBuffer* buffer)
+{
+    gl->MakeCurrent();
+    gl->fBindBufferBase(target, index, buffer ? buffer->GLName() : 0);
+}
+
+void
+WebGLContextUnchecked::BindBufferRange(GLenum target, GLuint index, WebGLBuffer* buffer, WebGLintptr offset, WebGLsizeiptr size)
+{
+    gl->MakeCurrent();
+    gl->fBindBufferRange(target, index, buffer ? buffer->GLName() : 0, offset, size);
+}
+
+void
+WebGLContextUnchecked::CopyBufferSubData(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size)
+{
+    gl->MakeCurrent();
+    gl->fCopyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size);
+}
+
+
+// -----------------------------------------------------------------------------
 // Sampler Objects
 
 void
 WebGLContextUnchecked::BindSampler(WebGLSampler* sampler, GLuint unit)
 {
     gl->MakeCurrent();
     gl->fBindSampler(unit, sampler ? sampler->GLName() : 0);
     if (sampler)
--- a/dom/canvas/WebGLContextUnchecked.h
+++ b/dom/canvas/WebGLContextUnchecked.h
@@ -4,31 +4,37 @@
  * 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/. */
 
 #ifndef WEBGLCONTEXTUNCHECKED_H
 #define WEBGLCONTEXTUNCHECKED_H
 
 #include "GLDefs.h"
 #include "WebGLTypes.h"
-#include "nsAutoPtr.h"
-#include "nsTArray.h"
 
 namespace mozilla {
 
+class WebGLBuffer;
 class WebGLSampler;
 
 namespace gl { class GLContext; }
 
 class WebGLContextUnchecked
 {
 public:
     explicit WebGLContextUnchecked(gl::GLContext* gl);
 
     // -------------------------------------------------------------------------
+    // Buffer Objects
+    void BindBuffer(GLenum target, WebGLBuffer* buffer);
+    void BindBufferBase(GLenum target, GLuint index, WebGLBuffer* buffer);
+    void BindBufferRange(GLenum taret, GLuint index, WebGLBuffer* buffer, WebGLintptr offset, WebGLsizeiptr size);
+    void CopyBufferSubData(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size);
+
+    // -------------------------------------------------------------------------
     // Sampler Objects
     void BindSampler(WebGLSampler* sampler, GLuint unit);
     void GetSamplerParameteriv(WebGLSampler* sampler, GLenum pname, GLint* param);
     void GetSamplerParameterfv(WebGLSampler* sampler, GLenum pname, GLfloat* param);
     void SamplerParameteri(WebGLSampler* sampler, GLenum pname, GLint param);
     void SamplerParameteriv(WebGLSampler* sampler, GLenum pname, const GLint* param);
     void SamplerParameterf(WebGLSampler* sampler, GLenum pname, GLfloat param);
     void SamplerParameterfv(WebGLSampler* sampler, GLenum pname, const GLfloat* param);
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -207,16 +207,60 @@ bool WebGLContext::ValidateBlendFuncEnum
          (dfactorIsConstantColor && sfactorIsConstantAlpha) ) {
         ErrorInvalidOperation("%s are mutually incompatible, see section 6.8 in the WebGL 1.0 spec", info);
         return false;
     } else {
         return true;
     }
 }
 
+bool
+WebGLContext::ValidateDataOffsetSize(WebGLintptr offset, WebGLsizeiptr size, WebGLsizeiptr bufferSize, const char* info)
+{
+    if (offset < 0) {
+        ErrorInvalidValue("%s: offset must be positive", info);
+        return false;
+    }
+
+    if (size < 0) {
+        ErrorInvalidValue("%s: size must be positive", info);
+        return false;
+    }
+
+    // *** Careful *** WebGLsizeiptr is always 64-bits but GLsizeiptr
+    // is like intptr_t. On some platforms it is 32-bits.
+    CheckedInt<GLsizeiptr> neededBytes = CheckedInt<GLsizeiptr>(offset) + size;
+    if (!neededBytes.isValid() || neededBytes.value() > bufferSize) {
+        ErrorInvalidValue("%s: invalid range", info);
+        return false;
+    }
+
+    return true;
+}
+
+/**
+ * Check data ranges [readOffset, readOffset + size] and [writeOffset,
+ * writeOffset + size] for overlap.
+ *
+ * It is assumed that offset and size have already been validated with
+ * ValidateDataOffsetSize().
+ */
+bool
+WebGLContext::ValidateDataRanges(WebGLintptr readOffset, WebGLintptr writeOffset, WebGLsizeiptr size, const char* info)
+{
+    MOZ_ASSERT((CheckedInt<WebGLsizeiptr>(readOffset) + size).isValid());
+    MOZ_ASSERT((CheckedInt<WebGLsizeiptr>(writeOffset) + size).isValid());
+
+    bool separate = (readOffset + size < writeOffset || writeOffset + size < readOffset);
+    if (!separate)
+        ErrorInvalidValue("%s: ranges [readOffset, readOffset + size) and [writeOffset, writeOffset + size) overlap");
+
+    return separate;
+}
+
 bool WebGLContext::ValidateTextureTargetEnum(GLenum target, const char *info)
 {
     switch (target) {
         case LOCAL_GL_TEXTURE_2D:
         case LOCAL_GL_TEXTURE_CUBE_MAP:
             return true;
         case LOCAL_GL_TEXTURE_3D: {
             const bool isValid = IsWebGL2();
--- a/dom/canvas/WebGLVertexArrayFake.cpp
+++ b/dom/canvas/WebGLVertexArrayFake.cpp
@@ -16,17 +16,17 @@ WebGLVertexArrayFake::BindVertexArrayImp
     // Go through and re-bind all buffers and setup all
     // vertex attribute pointers
     gl::GLContext* gl = mContext->gl;
 
     WebGLRefPtr<WebGLVertexArray> prevVertexArray = mContext->mBoundVertexArray;
 
     mContext->mBoundVertexArray = this;
 
-    WebGLRefPtr<WebGLBuffer> prevBuffer = mContext->mBoundArrayBuffer;
+    WebGLBufferRefPtr 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];
 
         mContext->BindBuffer(LOCAL_GL_ARRAY_BUFFER, vd.buf);
 
         if (vd.integer) {