Fix fast video uploads. draft
authorJeff Gilbert <jdashg@gmail.com>
Thu, 17 Dec 2015 16:16:55 -0800
changeset 316110 28b575dea21805eca8c1529b141f6b12d50fcf92
parent 316109 62958b4e1e632e8de45bbbf7630b0b44d9db26ac
child 316111 556cab8c39d1da5fcec7249892b4c95068930b34
push id8514
push userjgilbert@mozilla.com
push dateFri, 18 Dec 2015 00:24:33 +0000
milestone45.0a1
Fix fast video uploads.
dom/canvas/TexUnpackBlob.cpp
dom/canvas/TexUnpackBlob.h
dom/canvas/WebGLContext.h
dom/canvas/WebGLContextState.cpp
dom/canvas/WebGLTextureUpload.cpp
gfx/angle/src/libANGLE/renderer/d3d/TextureD3D.cpp
gfx/gl/ScopedGLHelpers.cpp
gfx/thebes/gfxPrefs.h
modules/libpref/init/all.js
--- a/dom/canvas/TexUnpackBlob.cpp
+++ b/dom/canvas/TexUnpackBlob.cpp
@@ -26,18 +26,31 @@ DoTexOrSubImage(bool isSubImage, gl::GLC
     if (isSubImage) {
         return DoTexSubImage(gl, target, level, xOffset, yOffset, zOffset, width, height,
                              depth, dui->ToPacking(), data);
     } else {
         return DoTexImage(gl, target, level, dui, width, height, depth, data);
     }
 }
 
+/*static*/ void
+TexUnpackBlob::OriginsForDOM(WebGLContext* webgl, gl::OriginPos* const out_src,
+                             gl::OriginPos* const out_dst)
+{
+    // Our surfaces are TopLeft.
+    *out_src = gl::OriginPos::TopLeft;
+
+    // WebGL specs the default as passing DOM elements top-left first.
+    // Thus y-flip would give us bottom-left.
+    *out_dst = webgl->mPixelStore_FlipY ? gl::OriginPos::BottomLeft
+                                        : gl::OriginPos::TopLeft;
+}
+
 //////////////////////////////////////////////////////////////////////////////////////////
-// TexUnpackBuffer
+// TexUnpackBytes
 
 bool
 TexUnpackBytes::ValidateUnpack(WebGLContext* webgl, const char* funcName, bool isFunc3D,
                                const webgl::PackingInfo& pi)
 {
     if (!mBytes)
         return true;
 
@@ -118,18 +131,18 @@ FormatFromPacking(const webgl::PackingIn
         case LOCAL_GL_RGBA:             return WebGLTexelFormat::RGBA32F;
         }
     }
 
     return WebGLTexelFormat::FormatNotSupportingAnyConversion;
 }
 
 void
-TexUnpackBytes::TexOrSubImage(bool isSubImage, const char* funcName, WebGLTexture* tex,
-                              TexImageTarget target, GLint level,
+TexUnpackBytes::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
+                              WebGLTexture* tex, TexImageTarget target, GLint level,
                               const webgl::DriverUnpackInfo* dui, GLint xOffset,
                               GLint yOffset, GLint zOffset, GLenum* const out_glError)
 {
     WebGLContext* webgl = tex->mContext;
     gl::GLContext* gl = webgl->gl;
 
     const void* uploadBytes = mBytes;
     UniqueBuffer tempBuffer;
@@ -218,17 +231,95 @@ TexUnpackBytes::TexOrSubImage(bool isSub
 
     GLenum error = DoTexOrSubImage(isSubImage, gl, target, level, dui, xOffset, yOffset,
                                    zOffset, mWidth, mHeight, mDepth, uploadBytes);
     *out_glError = error;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////////
-// TexUnpackDataSurface
+// TexUnpackImage
+
+TexUnpackImage::TexUnpackImage(const RefPtr<layers::Image>& image, bool isAlphaPremult)
+    : TexUnpackBlob(image->GetSize().width, image->GetSize().height, 1, true)
+    , mImage(image)
+    , mIsAlphaPremult(isAlphaPremult)
+{ }
+
+void
+TexUnpackImage::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
+                              WebGLTexture* tex, TexImageTarget target, GLint level,
+                              const webgl::DriverUnpackInfo* dui, GLint xOffset,
+                              GLint yOffset, GLint zOffset, GLenum* const out_glError)
+{
+    MOZ_ASSERT_IF(needsRespec, !isSubImage);
+    *out_glError = 0;
+
+    WebGLContext* webgl = tex->mContext;
+
+    gl::GLContext* gl = webgl->GL();
+    gl->MakeCurrent();
+
+    if (needsRespec) {
+        GLenum error = DoTexOrSubImage(isSubImage, gl, target.get(), level, dui, xOffset,
+                                       yOffset, zOffset, mWidth, mHeight, mDepth,
+                                       nullptr);
+        if (error) {
+            MOZ_ASSERT(!error);
+            *out_glError = LOCAL_GL_OUT_OF_MEMORY;
+            return;
+        }
+    }
+
+    do {
+        if (dui->unpackFormat != LOCAL_GL_RGB && dui->unpackFormat != LOCAL_GL_RGBA)
+            break;
+
+        if (dui->unpackType != LOCAL_GL_UNSIGNED_BYTE)
+            break;
+
+        gl::ScopedFramebuffer scopedFB(gl);
+        gl::ScopedBindFramebuffer bindFB(gl, scopedFB.FB());
+
+        {
+            gl::GLContext::LocalErrorScope errorScope(*gl);
+
+            gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
+                                      target.get(), tex->mGLName, level);
+
+            if (errorScope.GetError())
+                break;
+        }
+
+        const GLenum status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
+        if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE)
+            break;
+
+        gl::OriginPos srcOrigin, dstOrigin;
+        OriginsForDOM(webgl, &srcOrigin, &dstOrigin);
+
+        const gfx::IntSize destSize(mWidth, mHeight);
+        if (!gl->BlitHelper()->BlitImageToFramebuffer(mImage, destSize, scopedFB.FB(),
+                                                      dstOrigin))
+        {
+            break;
+        }
+
+        return;
+    } while (false);
+
+    TexUnpackSurface surfBlob(mImage->GetAsSourceSurface(), mIsAlphaPremult);
+
+    surfBlob.TexOrSubImage(isSubImage, needsRespec, funcName, tex, target, level, dui,
+                           xOffset, yOffset, zOffset, out_glError);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+// TexUnpackSurface
 
 static bool
 GuessAlignment(const void* data, size_t bytesPerRow, size_t stride, size_t maxAlignment,
                size_t* const out_alignment)
 {
     size_t alignmentGuess = maxAlignment;
     while (alignmentGuess) {
         size_t guessStride = RoundUpToMultipleOf(bytesPerRow, alignmentGuess);
@@ -247,50 +338,36 @@ static bool
 SupportsBGRA(gl::GLContext* gl)
 {
     if (gl->IsANGLE())
         return true;
 
     return false;
 }
 
-/*static*/ void
-TexUnpackSurface::OriginsForDOM(WebGLContext* webgl, gl::OriginPos* const out_src,
-                                gl::OriginPos* const out_dst)
-{
-    // Our surfaces are TopLeft.
-    *out_src = gl::OriginPos::TopLeft;
-
-    // WebGL specs the default as passing DOM elements top-left first.
-    // Thus y-flip would give us bottom-left.
-    *out_dst = webgl->mPixelStore_FlipY ? gl::OriginPos::BottomLeft
-                                        : gl::OriginPos::TopLeft;
-}
-
 /*static*/ bool
 TexUnpackSurface::UploadDataSurface(bool isSubImage, WebGLContext* webgl,
                                     TexImageTarget target, GLint level,
                                     const webgl::DriverUnpackInfo* dui, GLint xOffset,
                                     GLint yOffset, GLint zOffset, GLsizei width,
                                     GLsizei height, gfx::DataSourceSurface* surf,
                                     bool isSurfAlphaPremult, GLenum* const out_glError)
 {
-    MOZ_ASSERT(webgl->gl->IsCurrent());
+    gl::GLContext* gl = webgl->GL();
+    MOZ_ASSERT(gl->IsCurrent());
     *out_glError = 0;
 
     if (isSurfAlphaPremult != webgl->mPixelStore_PremultiplyAlpha)
         return false;
 
     gl::OriginPos srcOrigin, dstOrigin;
     OriginsForDOM(webgl, &srcOrigin, &dstOrigin);
     if (srcOrigin != dstOrigin)
         return false;
 
-    gl::GLContext* gl = webgl->gl;
-
     // This differs from the raw-data upload in that we choose how we do the unpack.
     // (alignment, etc.)
 
     // Uploading RGBX as RGBA and blitting to RGB is faster than repacking RGBX into
     // RGB on the CPU. However, this is optimization is out-of-scope for now.
 
     static const webgl::DriverUnpackInfo kInfoBGRA = {
         LOCAL_GL_BGRA,
@@ -626,27 +703,25 @@ TexUnpackSurface::ConvertSurface(WebGLCo
 TexUnpackSurface::TexUnpackSurface(const RefPtr<gfx::SourceSurface>& surf,
                                    bool isAlphaPremult)
     : TexUnpackBlob(surf->GetSize().width, surf->GetSize().height, 1, true)
     , mSurf(surf)
     , mIsAlphaPremult(isAlphaPremult)
 { }
 
 void
-TexUnpackSurface::TexOrSubImage(bool isSubImage, const char* funcName, WebGLTexture* tex,
-                                TexImageTarget target, GLint level,
+TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
+                                WebGLTexture* tex, TexImageTarget target, GLint level,
                                 const webgl::DriverUnpackInfo* dui, GLint xOffset,
                                 GLint yOffset, GLint zOffset, GLenum* const out_glError)
 {
     *out_glError = 0;
 
     WebGLContext* webgl = tex->mContext;
 
-    // TODO: Do blitting of the native SourceSurface.
-
     // MakeCurrent is a big mess in here, because mapping (and presumably unmapping) on
     // OSX can lose our MakeCurrent. Therefore it's easiest to MakeCurrent just before we
     // call into GL, instead of trying to keep MakeCurrent-ed.
 
     RefPtr<gfx::DataSourceSurface> dataSurf = mSurf->GetDataSurface();
     MOZ_ASSERT(dataSurf);
 
     GLenum error;
@@ -660,17 +735,16 @@ TexUnpackSurface::TexOrSubImage(bool isS
         return;
     }
 
     // CPU conversion. (++numCopies)
 
     UniqueBuffer convertedBuffer;
     uint8_t convertedAlignment;
     bool outOfMemory;
-
     if (!ConvertSurface(webgl, dui, dataSurf, mIsAlphaPremult, &convertedBuffer,
                         &convertedAlignment, &outOfMemory))
     {
         if (outOfMemory) {
             *out_glError = LOCAL_GL_OUT_OF_MEMORY;
         } else {
             NS_ERROR("Failed to convert surface.");
             *out_glError = LOCAL_GL_OUT_OF_MEMORY;
--- a/dom/canvas/TexUnpackBlob.h
+++ b/dom/canvas/TexUnpackBlob.h
@@ -59,21 +59,24 @@ protected:
     { }
 
 public:
     virtual ~TexUnpackBlob() {}
 
     virtual bool ValidateUnpack(WebGLContext* webgl, const char* funcName, bool isFunc3D,
                                 const webgl::PackingInfo& pi) = 0;
 
-    virtual void TexOrSubImage(bool isSubImage, const char* funcName, WebGLTexture* tex,
-                               TexImageTarget target, GLint level,
+    virtual void TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
+                               WebGLTexture* tex, TexImageTarget target, GLint level,
                                const webgl::DriverUnpackInfo* dui, GLint xOffset,
                                GLint yOffset, GLint zOffset,
                                GLenum* const out_glError) = 0;
+
+    static void OriginsForDOM(WebGLContext* webgl, gl::OriginPos* const out_src,
+                              gl::OriginPos* const out_dst);
 };
 
 class TexUnpackBytes : public TexUnpackBlob
 {
 public:
     const size_t mByteCount;
     const void* const mBytes;
 
@@ -82,18 +85,39 @@ public:
         : TexUnpackBlob(width, height, depth, bool(bytes))
         , mByteCount(byteCount)
         , mBytes(bytes)
     { }
 
     virtual bool ValidateUnpack(WebGLContext* webgl, const char* funcName, bool isFunc3D,
                                 const webgl::PackingInfo& pi) override;
 
-    virtual void TexOrSubImage(bool isSubImage, const char* funcName, WebGLTexture* tex,
-                               TexImageTarget target, GLint level,
+    virtual void TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
+                               WebGLTexture* tex, TexImageTarget target, GLint level,
+                               const webgl::DriverUnpackInfo* dui, GLint xOffset,
+                               GLint yOffset, GLint zOffset,
+                               GLenum* const out_glError) override;
+};
+
+class TexUnpackImage : public TexUnpackBlob
+{
+public:
+    const RefPtr<layers::Image> mImage;
+    const bool mIsAlphaPremult;
+
+    TexUnpackImage(const RefPtr<layers::Image>& image, bool isAlphaPremult);
+
+    virtual bool ValidateUnpack(WebGLContext* webgl, const char* funcName, bool isFunc3D,
+                                const webgl::PackingInfo& pi) override
+    {
+        return true;
+    }
+
+    virtual void TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
+                               WebGLTexture* tex, TexImageTarget target, GLint level,
                                const webgl::DriverUnpackInfo* dui, GLint xOffset,
                                GLint yOffset, GLint zOffset,
                                GLenum* const out_glError) override;
 };
 
 class TexUnpackSurface : public TexUnpackBlob
 {
 public:
@@ -103,35 +127,32 @@ public:
     TexUnpackSurface(const RefPtr<gfx::SourceSurface>& surf, bool isAlphaPremult);
 
     virtual bool ValidateUnpack(WebGLContext* webgl, const char* funcName, bool isFunc3D,
                                 const webgl::PackingInfo& pi) override
     {
         return true;
     }
 
-    virtual void TexOrSubImage(bool isSubImage, const char* funcName, WebGLTexture* tex,
-                               TexImageTarget target, GLint level,
+    virtual void TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
+                               WebGLTexture* tex, TexImageTarget target, GLint level,
                                const webgl::DriverUnpackInfo* dui, GLint xOffset,
                                GLint yOffset, GLint zOffset,
                                GLenum* const out_glError) override;
 
 protected:
     static bool ConvertSurface(WebGLContext* webgl, const webgl::DriverUnpackInfo* dui,
                                gfx::DataSourceSurface* surf, bool isSurfAlphaPremult,
                                UniqueBuffer* const out_convertedBuffer,
                                uint8_t* const out_convertedAlignment,
                                bool* const out_outOfMemory);
     static bool UploadDataSurface(bool isSubImage, WebGLContext* webgl,
                                   TexImageTarget target, GLint level,
                                   const webgl::DriverUnpackInfo* dui, GLint xOffset,
                                   GLint yOffset, GLint zOffset, GLsizei width,
                                   GLsizei height, gfx::DataSourceSurface* surf,
                                   bool isSurfAlphaPremult, GLenum* const out_glError);
-
-    static void OriginsForDOM(WebGLContext* webgl, gl::OriginPos* const out_src,
-                              gl::OriginPos* const out_dst);
 };
 
 } // namespace webgl
 } // namespace mozilla
 
 #endif // TEX_UNPACK_BLOB_H_
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -117,19 +117,17 @@ template<typename> struct Nullable;
 
 namespace gfx {
 class SourceSurface;
 } // namespace gfx
 
 namespace webgl {
 struct LinkedProgramInfo;
 class ShaderValidator;
-class TexUnpackBuffer;
-class TexUnpackImage;
-class TexUnpackSrcSurface;
+class TexUnpackBlob;
 } // namespace webgl
 
 WebGLTexelFormat GetWebGLTexelFormat(TexInternalFormat format);
 
 void AssertUintParamCorrect(gl::GLContext* gl, GLenum pname, GLuint shadow);
 
 struct WebGLContextOptions
 {
@@ -1572,16 +1570,17 @@ public:
 
     virtual UniquePtr<webgl::FormatUsageAuthority>
     CreateFormatUsage(gl::GLContext* gl) const = 0;
 
     // Friend list
     friend class ScopedCopyTexImageSource;
     friend class ScopedResolveTexturesForDraw;
     friend class ScopedUnpackReset;
+    friend class webgl::TexUnpackBlob;
     friend class webgl::TexUnpackBytes;
     friend class webgl::TexUnpackSurface;
     friend class WebGLTexture;
     friend class WebGLFBAttachPoint;
     friend class WebGLFramebuffer;
     friend class WebGLRenderbuffer;
     friend class WebGLProgram;
     friend class WebGLQuery;
--- a/dom/canvas/WebGLContextState.cpp
+++ b/dom/canvas/WebGLContextState.cpp
@@ -72,17 +72,19 @@ StringValue(JSContext* cx, const nsAStri
     return JS::StringValue(jsStr);
 }
 
 bool
 WebGLContext::GetStencilBits(GLint* out_stencilBits)
 {
     *out_stencilBits = 0;
     if (mBoundDrawFramebuffer) {
-        if (mBoundDrawFramebuffer->HasDepthStencilConflict()) {
+        if (mBoundDrawFramebuffer->StencilAttachment().IsDefined() &&
+            mBoundDrawFramebuffer->DepthStencilAttachment().IsDefined())
+        {
             // Error, we don't know which stencil buffer's bits to use
             ErrorInvalidFramebufferOperation("getParameter: framebuffer has two stencil buffers bound");
             return false;
         }
 
         if (mBoundDrawFramebuffer->StencilAttachment().IsDefined() ||
             mBoundDrawFramebuffer->DepthStencilAttachment().IsDefined())
         {
--- a/dom/canvas/WebGLTextureUpload.cpp
+++ b/dom/canvas/WebGLTextureUpload.cpp
@@ -49,44 +49,16 @@ namespace mozilla {
  * CopyTexImage2D(texImageTarget, level, internalFormat, x, y, width, height, border)
  * CopyTexImage3D - "Because the framebuffer is inhererntly two-dimensional, there is no
  *                   CopyTexImage3D command."
  * CopyTexSubImage2D(texImageTarget, level, xOffset, yOffset, x, y, width, height)
  * CopyTexSubImage3D(texImageTarget, level, xOffset, yOffset, zOffset, x, y, width,
  *                   height)
  */
 
-//////////////////////////////////////////////////////////////////////////////////////////
-// Some functions need an extra level of indirection, particularly for DOM Elements.
-
-already_AddRefed<mozilla::layers::Image>
-ImageFromVideo(dom::HTMLVideoElement* elem)
-{
-    uint16_t readyState;
-    if (NS_SUCCEEDED(elem->GetReadyState(&readyState)) &&
-        readyState < elem->HAVE_CURRENT_DATA)
-    {
-        // No frame inside, just return
-        return nullptr;
-    }
-
-    RefPtr<layers::ImageContainer> container = elem->GetImageContainer();
-    if (!container)
-        return nullptr;
-
-    nsAutoTArray<layers::ImageContainer::OwningImage, 4> currentImages;
-    container->GetCurrentImages(&currentImages);
-
-    if (!currentImages.Length())
-        return nullptr;
-
-    RefPtr<mozilla::layers::Image> ret = currentImages[0].mImage;
-    return ret.forget();
-}
-
 ////////////////////////////////////////
 // ArrayBufferView?
 
 static inline bool
 DoesJSTypeMatchUnpackType(GLenum unpackType, js::Scalar::Type jsType)
 {
     switch (unpackType) {
     case LOCAL_GL_BYTE:
@@ -248,23 +220,16 @@ void
 WebGLTexture::TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarget target,
                             GLint level, GLenum internalFormat, GLint xOffset,
                             GLint yOffset, GLint zOffset, GLenum unpackFormat,
                             GLenum unpackType, dom::Element* elem,
                             ErrorResult* const out_error)
 {
     auto sfer = mContext->SurfaceFromElement(elem);
 
-    const auto& layersImage = sfer.mLayersImage;
-    if (!layersImage && !sfer.GetSourceSurface()) {
-        mContext->ErrorInvalidOperation("%s: Failed to get data from DOM element.",
-                                        funcName);
-        return;
-    }
-
     if (sfer.mIsWriteOnly) {
         mContext->GenerateWarning("%s: Element is write-only.", funcName);
         out_error->Throw(NS_ERROR_DOM_SECURITY_ERR);
         return;
     }
 
     if (!sfer.mCORSUsed) {
         auto& srcPrincipal = sfer.mPrincipal;
@@ -273,20 +238,30 @@ WebGLTexture::TexOrSubImage(bool isSubIm
         if (!dstPrincipal->Subsumes(srcPrincipal)) {
             mContext->GenerateWarning("%s: Cross-origin elements require CORS.",
                                       funcName);
             out_error->Throw(NS_ERROR_DOM_SECURITY_ERR);
             return;
         }
     }
 
-    auto& srcSurf = sfer.GetSourceSurface();
+    UniquePtr<webgl::TexUnpackBlob> blob;
+    const bool isAlphaPremult = sfer.mIsPremultiplied;
 
-    UniquePtr<webgl::TexUnpackBlob> blob;
-    blob.reset(new webgl::TexUnpackSurface(srcSurf, sfer.mIsPremultiplied));
+    const auto& layersImage = sfer.mLayersImage;
+    if (layersImage && !gfxPrefs::WebGLDisableDOMBlitUploads()) {
+        blob.reset(new webgl::TexUnpackImage(layersImage, isAlphaPremult));
+    } else if (sfer.GetSourceSurface()) {
+        blob.reset(new webgl::TexUnpackSurface(sfer.GetSourceSurface(), isAlphaPremult));
+    } else {
+        mContext->ErrorInvalidOperation("%s: Failed to get data from DOM element.",
+                                        funcName);
+        return;
+    }
+    MOZ_ASSERT(blob);
 
     const GLint border = 0;
     TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset, yOffset,
                   zOffset, border, unpackFormat, unpackType, blob.get());
 }
 
 
 //////////////////////////////////////////////////////////////////////////////////////////
@@ -1141,24 +1116,31 @@ WebGLTexture::TexImage(const char* funcN
     // Do the thing!
 
     MOZ_ALWAYS_TRUE( mContext->gl->MakeCurrent() );
     MOZ_ASSERT(mContext->gl->IsCurrent());
 
     // It's tempting to do allocation first, and TexSubImage second, but this is generally
     // slower.
 
+    const ImageInfo newImageInfo(dstUsage, blob->mWidth, blob->mHeight, blob->mDepth,
+                                 blob->mHasData);
+
     const bool isSubImage = false;
+    const bool needsRespec = (imageInfo->mWidth  != newImageInfo.mWidth ||
+                              imageInfo->mHeight != newImageInfo.mHeight ||
+                              imageInfo->mDepth  != newImageInfo.mDepth ||
+                              imageInfo->mFormat != newImageInfo.mFormat);
     const GLint xOffset = 0;
     const GLint yOffset = 0;
     const GLint zOffset = 0;
 
     GLenum glError;
-    blob->TexOrSubImage(isSubImage, funcName, this, target, level, driverUnpackInfo,
-                        xOffset, yOffset, zOffset, &glError);
+    blob->TexOrSubImage(isSubImage, needsRespec, funcName, this, target, level,
+                        driverUnpackInfo, xOffset, yOffset, zOffset, &glError);
 
     if (glError == LOCAL_GL_OUT_OF_MEMORY) {
         mContext->ErrorOutOfMemory("%s: Driver ran out of memory during upload.",
                                    funcName);
         return;
     }
 
     if (glError) {
@@ -1168,18 +1150,16 @@ WebGLTexture::TexImage(const char* funcN
                       driverUnpackInfo->unpackFormat, driverUnpackInfo->unpackType);
         MOZ_ASSERT(false, "Unexpected GL error.");
         return;
     }
 
     ////////////////////////////////////
     // Update our specification data.
 
-    const ImageInfo newImageInfo(dstUsage, blob->mWidth, blob->mHeight, blob->mDepth,
-                                 blob->mHasData);
     SetImageInfo(imageInfo, newImageInfo);
 }
 
 void
 WebGLTexture::TexSubImage(const char* funcName, TexImageTarget target, GLint level,
                           GLint xOffset, GLint yOffset, GLint zOffset,
                           GLenum unpackFormat, GLenum unpackType,
                           webgl::TexUnpackBlob* blob)
@@ -1241,20 +1221,21 @@ WebGLTexture::TexSubImage(const char* fu
                                              yOffset, zOffset, blob->mWidth,
                                              blob->mHeight, blob->mDepth, imageInfo,
                                              &uploadWillInitialize))
     {
         return;
     }
 
     const bool isSubImage = true;
+    const bool needsRespect = false;
 
     GLenum glError;
-    blob->TexOrSubImage(isSubImage, funcName, this, target, level, driverUnpackInfo,
-                        xOffset, yOffset, zOffset, &glError);
+    blob->TexOrSubImage(isSubImage, needsRespect, funcName, this, target, level,
+                        driverUnpackInfo, xOffset, yOffset, zOffset, &glError);
 
     if (glError == LOCAL_GL_OUT_OF_MEMORY) {
         mContext->ErrorOutOfMemory("%s: Driver ran out of memory during upload.",
                                    funcName);
         return;
     }
 
     if (glError) {
--- a/gfx/angle/src/libANGLE/renderer/d3d/TextureD3D.cpp
+++ b/gfx/angle/src/libANGLE/renderer/d3d/TextureD3D.cpp
@@ -136,16 +136,19 @@ GLint TextureD3D::getBaseLevelDepth() co
 GLenum TextureD3D::getBaseLevelInternalFormat() const
 {
     const ImageD3D *baseImage = getBaseLevelImage();
     return (baseImage ? baseImage->getInternalFormat() : GL_NONE);
 }
 
 bool TextureD3D::shouldUseSetData(const ImageD3D *image) const
 {
+    if (image->isDirty())
+        return false;
+
     if (!mRenderer->getWorkarounds().setDataFasterThanImageUpload)
     {
         return false;
     }
 
     gl::InternalFormat internalFormat = gl::GetInternalFormatInfo(image->getInternalFormat());
 
     // We can only handle full updates for depth-stencil textures, so to avoid complications
--- a/gfx/gl/ScopedGLHelpers.cpp
+++ b/gfx/gl/ScopedGLHelpers.cpp
@@ -476,16 +476,18 @@ ScopedGLDrawState::ScopedGLDrawState(GLC
     mGL->fGetVertexAttribPointerv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_POINTER, &attrib0_pointer);
     mGL->fGetBooleanv(LOCAL_GL_COLOR_WRITEMASK, colorMask);
     mGL->fGetIntegerv(LOCAL_GL_VIEWPORT, viewport);
     mGL->fGetIntegerv(LOCAL_GL_SCISSOR_BOX, scissorBox);
 }
 
 ScopedGLDrawState::~ScopedGLDrawState()
 {
+    MOZ_ASSERT(mGL->IsCurrent());
+
     mGL->fScissor(scissorBox[0], scissorBox[1],
                   scissorBox[2], scissorBox[3]);
 
     mGL->fViewport(viewport[0], viewport[1],
                    viewport[2], viewport[3]);
 
     mGL->fColorMask(colorMask[0],
                     colorMask[1],
@@ -556,26 +558,21 @@ ScopedUnpackAlignment::ScopedUnpackAlign
                scopedVal == 2 ||
                scopedVal == 4 ||
                scopedVal == 8);
 
     gl->fGetIntegerv(LOCAL_GL_UNPACK_ALIGNMENT, &mOldVal);
 
     if (scopedVal != mOldVal) {
         gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, scopedVal);
-    } else {
-        // Don't try to re-set it during unwrap.
-        mOldVal = 0;
     }
 }
 
 void
 ScopedUnpackAlignment::UnwrapImpl() {
     // Check that we're not falling out of scope after the current context changed.
     MOZ_ASSERT(mGL->IsCurrent());
 
-    if (mOldVal) {
-        mGL->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, mOldVal);
-    }
+    mGL->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, mOldVal);
 }
 
 } /* namespace gl */
 } /* namespace mozilla */
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -409,16 +409,19 @@ private:
   DECL_GFX_PREF(Live, "webgl.bypass-shader-validation",        WebGLBypassShaderValidator, bool, true);
   DECL_GFX_PREF(Live, "webgl.can-lose-context-in-foreground",  WebGLCanLoseContextInForeground, bool, true);
   DECL_GFX_PREF(Live, "webgl.default-no-alpha",                WebGLDefaultNoAlpha, bool, false);
   DECL_GFX_PREF(Live, "webgl.disable-angle",                   WebGLDisableANGLE, bool, false);
   DECL_GFX_PREF(Live, "webgl.disable-extensions",              WebGLDisableExtensions, bool, false);
 
   DECL_GFX_PREF(Live, "webgl.disable-fail-if-major-performance-caveat",
                 WebGLDisableFailIfMajorPerformanceCaveat, bool, false);
+  DECL_GFX_PREF(Live, "webgl.disable-DOM-blit-uploads",
+                WebGLDisableDOMBlitUploads, bool, false);
+
   DECL_GFX_PREF(Live, "webgl.disabled",                        WebGLDisabled, bool, false);
 
   DECL_GFX_PREF(Live, "webgl.enable-draft-extensions",         WebGLDraftExtensionsEnabled, bool, false);
   DECL_GFX_PREF(Live, "webgl.enable-privileged-extensions",    WebGLPrivilegedExtensionsEnabled, bool, false);
   DECL_GFX_PREF(Live, "webgl.force-enabled",                   WebGLForceEnabled, bool, false);
   DECL_GFX_PREF(Once, "webgl.force-layers-readback",           WebGLForceLayersReadback, bool, false);
   DECL_GFX_PREF(Live, "webgl.lose-context-on-memory-pressure", WebGLLoseContextOnMemoryPressure, bool, false);
   DECL_GFX_PREF(Live, "webgl.max-warnings-per-context",        WebGLMaxWarningsPerContext, uint32_t, 32);
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4198,16 +4198,17 @@ pref("webgl.lose-context-on-memory-press
 pref("webgl.can-lose-context-in-foreground", true);
 pref("webgl.restore-context-when-visible", true);
 pref("webgl.max-warnings-per-context", 32);
 pref("webgl.enable-draft-extensions", false);
 pref("webgl.enable-privileged-extensions", false);
 pref("webgl.bypass-shader-validation", false);
 pref("webgl.enable-prototype-webgl2", false);
 pref("webgl.disable-fail-if-major-performance-caveat", false);
+pref("webgl.disable-DOM-blit-uploads", false);
 
 #ifdef RELEASE_BUILD
 // Keep this disabled on Release and Beta for now. (see bug 1171228)
 pref("webgl.enable-debug-renderer-info", false);
 #else
 pref("webgl.enable-debug-renderer-info", true);
 #endif