Bug 1322746 - Support PLANAR_YCBCR, GPU_VIDEO, and D3D11_YCBCR_IMAGE in GLBlitHelper. - r=jerry
☠☠ backed out by 3e55e4de032f ☠ ☠
authorJeff Gilbert <jgilbert@mozilla.com>
Mon, 17 Jul 2017 19:17:41 -0700
changeset 427877 61c7478af98c04eb81486f605cc053521353925f
parent 427876 b202e9377e24fe3d49fe2eaa5562afe7999f6754
child 427878 a4901ec3c32731bfe8fc5d276dfaa8d0aa1bf6a6
push id7761
push userjlund@mozilla.com
push dateFri, 15 Sep 2017 00:19:52 +0000
treeherdermozilla-beta@c38455951db4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjerry
bugs1322746
milestone57.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 1322746 - Support PLANAR_YCBCR, GPU_VIDEO, and D3D11_YCBCR_IMAGE in GLBlitHelper. - r=jerry MozReview-Commit-ID: FNsmq0RPvYB
dom/canvas/CanvasRenderingContext2D.cpp
dom/canvas/TexUnpackBlob.cpp
dom/canvas/WebGLContextValidate.cpp
dom/canvas/WebGLTextureUpload.cpp
gfx/gl/GLBlitHelper.cpp
gfx/gl/GLBlitHelper.h
gfx/gl/GLBlitHelperD3D.cpp
gfx/gl/GLContext.h
gfx/gl/GLContextEGL.h
gfx/gl/GLLibraryEGL.cpp
gfx/gl/GLLibraryEGL.h
gfx/gl/HeapCopyOfStackArray.h
gfx/gl/ScopedGLHelpers.cpp
gfx/gl/ScopedGLHelpers.h
gfx/gl/SharedSurface.cpp
gfx/gl/SharedSurfaceD3D11Interop.cpp
gfx/gl/moz.build
gfx/layers/D3D11YCbCrImage.cpp
gfx/layers/D3D11YCbCrImage.h
gfx/layers/GLImages.cpp
gfx/layers/GPUVideoImage.h
gfx/layers/client/GPUVideoTextureClient.h
gfx/layers/d3d11/TextureD3D11.h
gfx/layers/ipc/LayersSurfaces.ipdlh
gfx/thebes/gfxUtils.cpp
gfx/thebes/gfxUtils.h
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -5244,19 +5244,27 @@ CanvasRenderingContext2D::DrawImage(cons
 
       gl->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGB, srcImage->GetSize().width, srcImage->GetSize().height, 0, LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_SHORT_5_6_5, nullptr);
       gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
       gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
       gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR);
       gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR);
 
       const gl::OriginPos destOrigin = gl::OriginPos::TopLeft;
-      bool ok = gl->BlitHelper()->BlitImageToTexture(srcImage, srcImage->GetSize(),
-                                                     videoTexture, LOCAL_GL_TEXTURE_2D,
-                                                     destOrigin);
+      bool ok = false;
+      do {
+        const gl::ScopedFramebufferForTexture autoFBForTex(gl, videoTexture);
+        if (!autoFBForTex.IsComplete()) {
+          MOZ_ASSERT(false, "ScopedFramebufferForTexture not complete.");
+          break;
+        }
+        const gl::ScopedBindFramebuffer bindFB(gl, autoFBForTex.FB());
+        ok = gl->BlitHelper()->BlitImageToFramebuffer(srcImage, srcImage->GetSize(),
+                                                      destOrigin);
+      } while (false);
       if (ok) {
         NativeSurface texSurf;
         texSurf.mType = NativeSurfaceType::OPENGL_TEXTURE;
         texSurf.mFormat = SurfaceFormat::R5G6B5_UINT16;
         texSurf.mSize.width = srcImage->GetSize().width;
         texSurf.mSize.height = srcImage->GetSize().height;
         texSurf.mSurface = (void*)((uintptr_t)videoTexture);
 
--- a/dom/canvas/TexUnpackBlob.cpp
+++ b/dom/canvas/TexUnpackBlob.cpp
@@ -696,19 +696,17 @@ TexUnpackImage::TexOrSubImage(bool isSub
         if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
             fallbackReason = "bug: failed to confirm FB for blit";
             break;
         }
 
         const gfx::IntSize destSize(mWidth, mHeight);
         const auto dstOrigin = (webgl->mPixelStore_FlipY ? gl::OriginPos::TopLeft
                                                          : gl::OriginPos::BottomLeft);
-        if (!gl->BlitHelper()->BlitImageToFramebuffer(mImage, destSize, scopedFB.FB(),
-                                                      dstOrigin))
-        {
+        if (!gl->BlitHelper()->BlitImageToFramebuffer(mImage, destSize, dstOrigin)) {
             fallbackReason = "likely bug: failed to blit";
             break;
         }
 
         // Blitting was successful, so we're done!
         *out_error = 0;
         return true;
     } while (false);
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -716,16 +716,22 @@ WebGLContext::InitAndValidateGL(FailureR
 
     if (IsWebGL2() &&
         !InitWebGL2(out_failReason))
     {
         // Todo: Bug 898404: Only allow WebGL2 on GL>=3.0 on desktop GL.
         return false;
     }
 
+    if (!gl->IsSupported(GLFeature::vertex_array_object)) {
+        *out_failReason = { "FEATURE_FAILURE_WEBGL_VAOS",
+                            "Requires vertex_array_object." };
+        return false;
+    }
+
     mDefaultVertexArray = WebGLVertexArray::Create(this);
     mDefaultVertexArray->mAttribs.SetLength(mGLMaxVertexAttribs);
     mBoundVertexArray = mDefaultVertexArray;
 
     // OpenGL core profiles remove the default VAO object from version
     // 4.0.0. We create a default VAO for all core profiles,
     // regardless of version.
     //
--- a/dom/canvas/WebGLTextureUpload.cpp
+++ b/dom/canvas/WebGLTextureUpload.cpp
@@ -1828,18 +1828,21 @@ ScopedCopyTexImageSource::ScopedCopyTexI
         MOZ_CRASH("GFX: Temp framebuffer is not complete.");
     }
 
     // Restore RB binding.
     scopedRB.Unwrap(); // This function should really have a better name.
 
     // Draw-blit rgbaTex into rgbaFB.
     const gfx::IntSize srcSize(srcWidth, srcHeight);
-    gl->BlitHelper()->DrawBlitTextureToFramebuffer(scopedTex.Texture(), rgbaFB,
-                                                   srcSize, srcSize);
+    {
+        const gl::ScopedBindFramebuffer bindFB(gl, rgbaFB);
+        gl->BlitHelper()->DrawBlitTextureToFramebuffer(scopedTex.Texture(), srcSize,
+                                                       srcSize);
+    }
 
     // Restore Tex2D binding and destroy the temp tex.
     scopedBindTex.Unwrap();
     scopedTex.Unwrap();
 
     // Leave RB and FB alive, and FB bound.
     mRB = rgbaRB;
     mFB = rgbaFB;
--- a/gfx/gl/GLBlitHelper.cpp
+++ b/gfx/gl/GLBlitHelper.cpp
@@ -7,18 +7,20 @@
 #include "gfxUtils.h"
 #include "GLBlitHelper.h"
 #include "GLContext.h"
 #include "GLScreenBuffer.h"
 #include "ScopedGLHelpers.h"
 #include "mozilla/Preferences.h"
 #include "ImageContainer.h"
 #include "HeapCopyOfStackArray.h"
+#include "mozilla/gfx/Logging.h"
 #include "mozilla/gfx/Matrix.h"
 #include "mozilla/UniquePtr.h"
+#include "GPUVideoImage.h"
 
 #ifdef MOZ_WIDGET_ANDROID
 #include "AndroidSurfaceTexture.h"
 #include "GLImages.h"
 #include "GLLibraryEGL.h"
 #endif
 
 #ifdef XP_MACOSX
@@ -27,992 +29,930 @@
 #endif
 
 using mozilla::layers::PlanarYCbCrImage;
 using mozilla::layers::PlanarYCbCrData;
 
 namespace mozilla {
 namespace gl {
 
-GLBlitHelper::GLBlitHelper(GLContext* gl)
+// --
+
+ScopedSaveMultiTex::ScopedSaveMultiTex(GLContext* const gl, const uint8_t texCount,
+                                       const GLenum texTarget)
+    : mGL(*gl)
+    , mTexCount(texCount)
+    , mTexTarget(texTarget)
+    , mOldTexUnit(mGL.GetIntAs<GLenum>(LOCAL_GL_ACTIVE_TEXTURE))
+{
+    GLenum texBinding;
+    switch (mTexTarget) {
+    case LOCAL_GL_TEXTURE_2D:
+        texBinding = LOCAL_GL_TEXTURE_BINDING_2D;
+        break;
+    case LOCAL_GL_TEXTURE_RECTANGLE:
+        texBinding = LOCAL_GL_TEXTURE_BINDING_RECTANGLE;
+        break;
+    case LOCAL_GL_TEXTURE_EXTERNAL:
+        texBinding = LOCAL_GL_TEXTURE_BINDING_EXTERNAL;
+        break;
+    default:
+        gfxCriticalError() << "Unhandled texTarget: " << texTarget;
+    }
+
+    for (uint8_t i = 0; i < mTexCount; i++) {
+        mGL.fActiveTexture(LOCAL_GL_TEXTURE0 + i);
+        if (mGL.IsSupported(GLFeature::sampler_objects)) {
+            mOldTexSampler[i] = mGL.GetIntAs<GLuint>(LOCAL_GL_SAMPLER_BINDING);
+            mGL.fBindSampler(i, 0);
+        }
+        mOldTex[i] = mGL.GetIntAs<GLuint>(texBinding);
+    }
+}
+
+ScopedSaveMultiTex::~ScopedSaveMultiTex()
+{
+    for (uint8_t i = 0; i < mTexCount; i++) {
+        mGL.fActiveTexture(LOCAL_GL_TEXTURE0 + i);
+        if (mGL.IsSupported(GLFeature::sampler_objects)) {
+            mGL.fBindSampler(i, mOldTexSampler[i]);
+        }
+        mGL.fBindTexture(mTexTarget, mOldTex[i]);
+    }
+    mGL.fActiveTexture(mOldTexUnit);
+}
+
+// --
+
+class ScopedBindArrayBuffer final
+{
+    GLContext& mGL;
+    const GLuint mOldVBO;
+
+public:
+    ScopedBindArrayBuffer(GLContext* const gl, const GLuint vbo)
+        : mGL(*gl)
+        , mOldVBO(mGL.GetIntAs<GLuint>(LOCAL_GL_ARRAY_BUFFER_BINDING))
+    {
+        mGL.fBindBuffer(LOCAL_GL_ARRAY_BUFFER, vbo);
+    }
+
+    ~ScopedBindArrayBuffer()
+    {
+        mGL.fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mOldVBO);
+    }
+};
+
+// --
+
+class ScopedBindVAO final
+{
+    GLContext& mGL;
+    const GLuint mOldVAO;
+
+public:
+    ScopedBindVAO(GLContext* const gl, const GLuint vao)
+        : mGL(*gl)
+        , mOldVAO(mGL.GetIntAs<GLuint>(LOCAL_GL_VERTEX_ARRAY_BINDING))
+    {
+        mGL.fBindVertexArray(vao);
+    }
+
+    ~ScopedBindVAO()
+    {
+        mGL.fBindVertexArray(mOldVAO);
+    }
+};
+
+// --
+
+class ScopedShader final
+{
+    GLContext& mGL;
+    const GLuint mName;
+
+public:
+    ScopedShader(GLContext* const gl, const GLenum shaderType)
+        : mGL(*gl)
+        , mName(mGL.fCreateShader(shaderType))
+    { }
+
+    ~ScopedShader()
+    {
+        mGL.fDeleteShader(mName);
+    }
+
+    operator GLuint() const { return mName; }
+};
+
+// --
+
+class SaveRestoreCurrentProgram final
+{
+    GLContext& mGL;
+    const GLuint mOld;
+
+public:
+    explicit SaveRestoreCurrentProgram(GLContext* const gl)
+        : mGL(*gl)
+        , mOld(mGL.GetIntAs<GLuint>(LOCAL_GL_CURRENT_PROGRAM))
+    { }
+
+    ~SaveRestoreCurrentProgram()
+    {
+        mGL.fUseProgram(mOld);
+    }
+};
+
+// --
+
+class ScopedDrawBlitState final
+{
+    GLContext& mGL;
+
+    const bool blend;
+    const bool cullFace;
+    const bool depthTest;
+    const bool dither;
+    const bool polyOffsFill;
+    const bool sampleAToC;
+    const bool sampleCover;
+    const bool scissor;
+    const bool stencil;
+    Maybe<bool> rasterizerDiscard;
+
+    realGLboolean colorMask[4];
+    GLint viewport[4];
+
+public:
+    ScopedDrawBlitState(GLContext* const gl, const gfx::IntSize& destSize)
+        : mGL(*gl)
+        , blend       (mGL.PushEnabled(LOCAL_GL_BLEND,                    false))
+        , cullFace    (mGL.PushEnabled(LOCAL_GL_CULL_FACE,                false))
+        , depthTest   (mGL.PushEnabled(LOCAL_GL_DEPTH_TEST,               false))
+        , dither      (mGL.PushEnabled(LOCAL_GL_DITHER,                   true))
+        , polyOffsFill(mGL.PushEnabled(LOCAL_GL_POLYGON_OFFSET_FILL,      false))
+        , sampleAToC  (mGL.PushEnabled(LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE, false))
+        , sampleCover (mGL.PushEnabled(LOCAL_GL_SAMPLE_COVERAGE,          false))
+        , scissor     (mGL.PushEnabled(LOCAL_GL_SCISSOR_TEST,             false))
+        , stencil     (mGL.PushEnabled(LOCAL_GL_STENCIL_TEST,             false))
+    {
+        if (mGL.IsSupported(GLFeature::transform_feedback2)) {
+            // Technically transform_feedback2 requires transform_feedback, which actually
+            // adds RASTERIZER_DISCARD.
+            rasterizerDiscard = Some(mGL.PushEnabled(LOCAL_GL_RASTERIZER_DISCARD, false));
+        }
+
+        mGL.fGetBooleanv(LOCAL_GL_COLOR_WRITEMASK, colorMask);
+        mGL.fColorMask(true, true, true, true);
+
+        mGL.fGetIntegerv(LOCAL_GL_VIEWPORT, viewport);
+        mGL.fViewport(0, 0, destSize.width, destSize.height);
+    }
+
+    ~ScopedDrawBlitState()
+    {
+        mGL.SetEnabled(LOCAL_GL_BLEND,                    blend       );
+        mGL.SetEnabled(LOCAL_GL_CULL_FACE,                cullFace    );
+        mGL.SetEnabled(LOCAL_GL_DEPTH_TEST,               depthTest   );
+        mGL.SetEnabled(LOCAL_GL_DITHER,                   dither      );
+        mGL.SetEnabled(LOCAL_GL_POLYGON_OFFSET_FILL,      polyOffsFill);
+        mGL.SetEnabled(LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE, sampleAToC  );
+        mGL.SetEnabled(LOCAL_GL_SAMPLE_COVERAGE,          sampleCover );
+        mGL.SetEnabled(LOCAL_GL_SCISSOR_TEST,             scissor     );
+        mGL.SetEnabled(LOCAL_GL_STENCIL_TEST,             stencil     );
+        if (rasterizerDiscard) {
+            mGL.SetEnabled(LOCAL_GL_RASTERIZER_DISCARD, rasterizerDiscard.value());
+        }
+
+        mGL.fColorMask(colorMask[0], colorMask[1], colorMask[2], colorMask[3]);
+        mGL.fViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
+    }
+};
+
+// --
+
+DrawBlitProg::DrawBlitProg(GLBlitHelper* const parent, const GLuint prog)
+    : mParent(*parent)
+    , mProg(prog)
+    , mLoc_u1ForYFlip(mParent.mGL->fGetUniformLocation(mProg, "u1ForYFlip"))
+    , mLoc_uClipRect(mParent.mGL->fGetUniformLocation(mProg, "uClipRect"))
+    , mLoc_uTexSize0(mParent.mGL->fGetUniformLocation(mProg, "uTexSize0"))
+    , mLoc_uTexSize1(mParent.mGL->fGetUniformLocation(mProg, "uTexSize1"))
+    , mLoc_uDivisors(mParent.mGL->fGetUniformLocation(mProg, "uDivisors"))
+    , mLoc_uColorMatrix(mParent.mGL->fGetUniformLocation(mProg, "uColorMatrix"))
+{
+    MOZ_ASSERT(mLoc_u1ForYFlip != -1);
+    MOZ_ASSERT(mLoc_uClipRect != -1);
+    MOZ_ASSERT(mLoc_uTexSize0 != -1);
+    if (mLoc_uColorMatrix != -1) {
+        MOZ_ASSERT(mLoc_uTexSize1 != -1);
+        MOZ_ASSERT(mLoc_uDivisors != -1);
+    }
+}
+
+DrawBlitProg::~DrawBlitProg()
+{
+    const auto& gl = mParent.mGL;
+    if (!gl->MakeCurrent())
+        return;
+
+    gl->fDeleteProgram(mProg);
+}
+
+void
+DrawBlitProg::Draw(const BaseArgs& args, const YUVArgs* const argsYUV) const
+{
+    const auto& gl = mParent.mGL;
+
+    const SaveRestoreCurrentProgram oldProg(gl);
+    gl->fUseProgram(mProg);
+
+    // --
+
+    gl->fUniform1f(mLoc_u1ForYFlip, args.yFlip ? 1 : 0);
+    gl->fUniform4f(mLoc_uClipRect,
+                   args.clipRect.x, args.clipRect.y,
+                   args.clipRect.width, args.clipRect.height);
+    gl->fUniform2f(mLoc_uTexSize0, args.texSize0.width, args.texSize0.height);
+
+    MOZ_ASSERT(bool(argsYUV) == (mLoc_uColorMatrix != -1));
+    if (argsYUV) {
+        gl->fUniform2f(mLoc_uTexSize1, argsYUV->texSize1.width, argsYUV->texSize1.height);
+        gl->fUniform2f(mLoc_uDivisors, argsYUV->divisors.width, argsYUV->divisors.height);
+        const auto& colorMatrix = gfxUtils::YuvToRgbMatrix4x4ColumnMajor(argsYUV->colorSpace);
+        gl->fUniformMatrix4fv(mLoc_uColorMatrix, 1, false, colorMatrix);
+    }
+
+    // --
+
+    const ScopedDrawBlitState drawState(gl, args.destSize);
+    const ScopedBindVAO bindVAO(gl, mParent.mQuadVAO);
+    gl->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4);
+}
+
+// --
+
+GLBlitHelper::GLBlitHelper(GLContext* const gl)
     : mGL(gl)
-    , mTexBlit_Buffer(0)
-    , mTexBlit_VertShader(0)
-    , mTex2DBlit_FragShader(0)
-    , mTex2DRectBlit_FragShader(0)
-    , mTex2DBlit_Program(0)
-    , mTex2DRectBlit_Program(0)
-    , mYFlipLoc(-1)
-    , mTextureTransformLoc(-1)
-    , mTexExternalBlit_FragShader(0)
-    , mTexYUVPlanarBlit_FragShader(0)
-    , mTexNV12PlanarBlit_FragShader(0)
-    , mTexExternalBlit_Program(0)
-    , mTexYUVPlanarBlit_Program(0)
-    , mTexNV12PlanarBlit_Program(0)
-    , mFBO(0)
-    , mSrcTexY(0)
-    , mSrcTexCb(0)
-    , mSrcTexCr(0)
-    , mSrcTexEGL(0)
-    , mYTexScaleLoc(-1)
-    , mCbCrTexScaleLoc(-1)
-    , mYuvColorMatrixLoc(-1)
-    , mTexWidth(0)
-    , mTexHeight(0)
-    , mCurYScale(1.0f)
-    , mCurCbCrScale(1.0f)
+    , mQuadVAO(0)
+    , mYuvUploads{0}
+    , mYuvUploads_YSize(0, 0)
+    , mYuvUploads_UVSize(0, 0)
 {
+    if (!mGL->IsSupported(GLFeature::vertex_array_object)) {
+        gfxCriticalError() << "GLBlitHelper requires vertex_array_object.";
+        return;
+    }
+
+    GLuint vbo = 0;
+    mGL->fGenBuffers(1, &vbo);
+    {
+        const ScopedBindArrayBuffer bindVBO(mGL, vbo);
+
+        const float quadData[] = {
+            0, 0,
+            1, 0,
+            0, 1,
+            1, 1
+        };
+        const HeapCopyOfStackArray<float> heapQuadData(quadData);
+        mGL->fBufferData(LOCAL_GL_ARRAY_BUFFER, heapQuadData.ByteLength(),
+                         heapQuadData.Data(), LOCAL_GL_STATIC_DRAW);
+
+        mGL->fGenVertexArrays(1, &mQuadVAO);
+        const ScopedBindVAO bindVAO(mGL, mQuadVAO);
+        mGL->fEnableVertexAttribArray(0);
+        mGL->fVertexAttribPointer(0, 2, LOCAL_GL_FLOAT, false, 0, 0);
+    }
+    mGL->fDeleteBuffers(1, &vbo);
+
+    // --
+
+    const char kVertSource[] = "\
+        attribute vec2 aVert;                                                \n\
+                                                                             \n\
+        uniform float u1ForYFlip;                                            \n\
+        uniform vec4 uClipRect;                                              \n\
+        uniform vec2 uTexSize0;                                              \n\
+        uniform vec2 uTexSize1;                                              \n\
+        uniform vec2 uDivisors;                                              \n\
+                                                                             \n\
+        varying vec2 vTexCoord0;                                             \n\
+        varying vec2 vTexCoord1;                                             \n\
+                                                                             \n\
+        void main(void)                                                      \n\
+        {                                                                    \n\
+            vec2 vertPos = aVert * 2.0 - 1.0;                                \n\
+            gl_Position = vec4(vertPos, 0.0, 1.0);                           \n\
+                                                                             \n\
+            vec2 texCoord = aVert;                                           \n\
+            texCoord.y = abs(u1ForYFlip - texCoord.y);                       \n\
+            texCoord = texCoord * uClipRect.zw + uClipRect.xy;               \n\
+                                                                             \n\
+            vTexCoord0 = texCoord / uTexSize0;                               \n\
+            vTexCoord1 = texCoord / (uTexSize1 * uDivisors);                 \n\
+        }                                                                    \n\
+    ";
+    const ScopedShader vs(mGL, LOCAL_GL_VERTEX_SHADER);
+    const char* const parts[] = {
+        kVertSource
+    };
+    mGL->fShaderSource(vs, 1, parts, nullptr);
+    mGL->fCompileShader(vs);
+
+    const auto fnCreateProgram = [&](const DrawBlitType type,
+                                     const char* const fragHeader,
+                                     const char* const fragBody)
+    {
+        const ScopedShader fs(mGL, LOCAL_GL_FRAGMENT_SHADER);
+        const char* const parts[] = {
+            fragHeader,
+            fragBody
+        };
+        mGL->fShaderSource(fs, 2, parts, nullptr);
+        mGL->fCompileShader(fs);
+
+        const auto prog = mGL->fCreateProgram();
+        mGL->fAttachShader(prog, vs);
+        mGL->fAttachShader(prog, fs);
+
+        mGL->fBindAttribLocation(prog, 0, "aPosition");
+        mGL->fLinkProgram(prog);
+
+        GLenum status = 0;
+        mGL->fGetProgramiv(prog, LOCAL_GL_LINK_STATUS, (GLint*)&status);
+        if (status == LOCAL_GL_TRUE) {
+            mGL->fUseProgram(prog);
+            const char* samplerNames[] = {
+                "uTex0",
+                "uTex1",
+                "uTex2"
+            };
+            for (int i = 0; i < 3; i++) {
+                const auto loc = mGL->fGetUniformLocation(prog, samplerNames[i]);
+                if (loc == -1)
+                    break;
+                mGL->fUniform1i(loc, i);
+            }
+
+            auto obj = MakeUnique<DrawBlitProg>(this, prog);
+            mDrawBlitProgs.insert({uint8_t(type), Move(obj)});
+            return;
+        }
+
+        GLuint progLogLen = 0;
+        mGL->fGetProgramiv(prog, LOCAL_GL_INFO_LOG_LENGTH, (GLint*)&progLogLen);
+        const UniquePtr<char[]> progLog(new char[progLogLen]);
+        mGL->fGetProgramInfoLog(prog, progLogLen, nullptr, progLog.get());
+
+        GLuint vsLogLen = 0;
+        mGL->fGetShaderiv(vs, LOCAL_GL_INFO_LOG_LENGTH, (GLint*)&vsLogLen);
+        const UniquePtr<char[]> vsLog(new char[vsLogLen]);
+        mGL->fGetShaderInfoLog(vs, vsLogLen, nullptr, vsLog.get());
+
+        GLuint fsLogLen = 0;
+        mGL->fGetShaderiv(fs, LOCAL_GL_INFO_LOG_LENGTH, (GLint*)&fsLogLen);
+        const UniquePtr<char[]> fsLog(new char[fsLogLen]);
+        mGL->fGetShaderInfoLog(fs, fsLogLen, nullptr, fsLog.get());
+
+        gfxCriticalError() << "Link failed for DrawBlitType: " << uint8_t(type) << ":\n"
+                           << "progLog: " << progLog.get() << "\n"
+                           << "vsLog: " << vsLog.get() << "\n"
+                           << "fsLog: " << fsLog.get() << "\n";
+    };
+
+    const char kFragHeader_Tex2D[] = "\
+        #define SAMPLER sampler2D                                            \n\
+        #define TEXTURE texture2D                                            \n\
+    ";
+    const char kFragHeader_Tex2DRect[] = "\
+        #define SAMPLER sampler2DRect                                        \n\
+        #define TEXTURE texture2DRect                                        \n\
+    ";
+    const char kFragHeader_TexExt[] = "\
+        #extension GL_OES_EGL_image_external : require                       \n\
+        #define SAMPLER samplerExternalOES                                   \n\
+        #define TEXTURE texture2D                                            \n\
+    ";
+
+    const char kFragBody_RGBA[] = "\
+        #ifdef GL_FRAGMENT_PRECISION_HIGH                                    \n\
+            precision highp float;                                           \n\
+        #else                                                                \n\
+            precision mediump float;                                         \n\
+        #endif                                                               \n\
+                                                                             \n\
+        varying vec2 vTexCoord0;                                             \n\
+        uniform SAMPLER uTex0;                                               \n\
+                                                                             \n\
+        void main(void)                                                      \n\
+        {                                                                    \n\
+            gl_FragColor = TEXTURE(uTex0, vTexCoord0);                       \n\
+        }                                                                    \n\
+    ";
+    /*
+    const char kFragBody_YUV[] = "\
+        #ifdef GL_FRAGMENT_PRECISION_HIGH                                    \n\
+            precision highp float;                                           \n\
+        #else                                                                \n\
+            precision mediump float;                                         \n\
+        #endif                                                               \n\
+                                                                             \n\
+        varying vec2 vTexCoord0;                                             \n\
+        uniform SAMPLER uTex0;                                               \n\
+        uniform mat4 uColorMatrix;                                           \n\
+                                                                             \n\
+        void main(void)                                                      \n\
+        {                                                                    \n\
+            vec4 yuv = vec4(TEXTURE(uTex0, vTexCoord0).xyz,                  \n\
+                            1.0);                                            \n\
+            vec4 rgb = uColorMatrix * yuv;                                   \n\
+            gl_FragColor = vec4(rgb.rgb, 1.0);                               \n\
+        }                                                                    \n\
+    ";
+    */
+    const char kFragBody_NV12[] = "\
+        #ifdef GL_FRAGMENT_PRECISION_HIGH                                    \n\
+            precision highp float;                                           \n\
+        #else                                                                \n\
+            precision mediump float;                                         \n\
+        #endif                                                               \n\
+                                                                             \n\
+        varying vec2 vTexCoord0;                                             \n\
+        varying vec2 vTexCoord1;                                             \n\
+        uniform SAMPLER uTex0;                                               \n\
+        uniform SAMPLER uTex1;                                               \n\
+        uniform mat4 uColorMatrix;                                           \n\
+                                                                             \n\
+        void main(void)                                                      \n\
+        {                                                                    \n\
+            vec4 yuv = vec4(TEXTURE(uTex0, vTexCoord0).x,                    \n\
+                            TEXTURE(uTex1, vTexCoord1).xy,                   \n\
+                            1.0);                                            \n\
+            vec4 rgb = uColorMatrix * yuv;                                   \n\
+            gl_FragColor = vec4(rgb.rgb, 1.0);                               \n\
+            //gl_FragColor = yuv;                               \n\
+        }                                                                    \n\
+    ";
+    const char kFragBody_PlanarYUV[] = "\
+        #ifdef GL_FRAGMENT_PRECISION_HIGH                                    \n\
+            precision highp float;                                           \n\
+        #else                                                                \n\
+            precision mediump float;                                         \n\
+        #endif                                                               \n\
+                                                                             \n\
+        varying vec2 vTexCoord0;                                             \n\
+        varying vec2 vTexCoord1;                                             \n\
+        uniform SAMPLER uTex0;                                               \n\
+        uniform SAMPLER uTex1;                                               \n\
+        uniform SAMPLER uTex2;                                               \n\
+        uniform mat4 uColorMatrix;                                           \n\
+                                                                             \n\
+        void main(void)                                                      \n\
+        {                                                                    \n\
+            vec4 yuv = vec4(TEXTURE(uTex0, vTexCoord0).x,                    \n\
+                            TEXTURE(uTex1, vTexCoord1).x,                    \n\
+                            TEXTURE(uTex2, vTexCoord1).x,                    \n\
+                            1.0);                                            \n\
+            vec4 rgb = uColorMatrix * yuv;                                   \n\
+            gl_FragColor = vec4(rgb.rgb, 1.0);                               \n\
+        }                                                                    \n\
+    ";
+
+    const SaveRestoreCurrentProgram oldProg(mGL);
+
+    fnCreateProgram(DrawBlitType::Tex2DRGBA, kFragHeader_Tex2D, kFragBody_RGBA);
+    fnCreateProgram(DrawBlitType::Tex2DPlanarYUV, kFragHeader_Tex2D, kFragBody_PlanarYUV);
+    if (mGL->IsExtensionSupported(GLContext::ARB_texture_rectangle)) {
+        fnCreateProgram(DrawBlitType::TexRectRGBA, kFragHeader_Tex2DRect, kFragBody_RGBA);
+    }
+    if (mGL->IsExtensionSupported(GLContext::OES_EGL_image_external)) {
+        fnCreateProgram(DrawBlitType::TexExtNV12, kFragHeader_TexExt, kFragBody_NV12);
+        fnCreateProgram(DrawBlitType::TexExtPlanarYUV, kFragHeader_TexExt, kFragBody_PlanarYUV);
+    }
 }
 
 GLBlitHelper::~GLBlitHelper()
 {
     if (!mGL->MakeCurrent())
         return;
 
-    DeleteTexBlitProgram();
+    mGL->fDeleteVertexArrays(1, &mQuadVAO);
+}
 
-    GLuint tex[] = {
-        mSrcTexY,
-        mSrcTexCb,
-        mSrcTexCr,
-        mSrcTexEGL,
-    };
-
-    mSrcTexY = mSrcTexCb = mSrcTexCr = mSrcTexEGL = 0;
-    mGL->fDeleteTextures(ArrayLength(tex), tex);
-
-    if (mFBO) {
-        mGL->fDeleteFramebuffers(1, &mFBO);
-    }
-    mFBO = 0;
+const DrawBlitProg*
+GLBlitHelper::GetDrawBlitProg(const DrawBlitType type) const
+{
+    const auto itr = mDrawBlitProgs.find(uint8_t(type));
+    if (itr == mDrawBlitProgs.end())
+        return nullptr;
+    return itr->second.get();
 }
 
-// Allowed to be destructive of state we restore in functions below.
-bool
-GLBlitHelper::InitTexQuadProgram(BlitType target)
-{
-    const char kTexBlit_VertShaderSource[] = "\
-        #version 100                                  \n\
-        #ifdef GL_ES                                  \n\
-        precision mediump float;                      \n\
-        #endif                                        \n\
-        attribute vec2 aPosition;                     \n\
-                                                      \n\
-        uniform float uYflip;                         \n\
-        varying vec2 vTexCoord;                       \n\
-                                                      \n\
-        void main(void)                               \n\
-        {                                             \n\
-            vTexCoord = aPosition;                    \n\
-            vTexCoord.y = abs(vTexCoord.y - uYflip);  \n\
-            vec2 vertPos = aPosition * 2.0 - 1.0;     \n\
-            gl_Position = vec4(vertPos, 0.0, 1.0);    \n\
-        }                                             \n\
-    ";
-
-    const char kTex2DBlit_FragShaderSource[] = "\
-        #version 100                                        \n\
-        #ifdef GL_ES                                        \n\
-        #ifdef GL_FRAGMENT_PRECISION_HIGH                   \n\
-            precision highp float;                          \n\
-        #else                                               \n\
-            precision mediump float;                        \n\
-        #endif                                              \n\
-        #endif                                              \n\
-        uniform sampler2D uTexUnit;                         \n\
-                                                            \n\
-        varying vec2 vTexCoord;                             \n\
-                                                            \n\
-        void main(void)                                     \n\
-        {                                                   \n\
-            gl_FragColor = texture2D(uTexUnit, vTexCoord);  \n\
-        }                                                   \n\
-    ";
+// -----------------------------------------------------------------------------
 
-    const char kTex2DRectBlit_FragShaderSource[] = "\
-        #version 100                                                  \n\
-        #ifdef GL_FRAGMENT_PRECISION_HIGH                             \n\
-            precision highp float;                                    \n\
-        #else                                                         \n\
-            precision mediump float;                                  \n\
-        #endif                                                        \n\
-                                                                      \n\
-        uniform sampler2D uTexUnit;                                   \n\
-        uniform vec2 uTexCoordMult;                                   \n\
-                                                                      \n\
-        varying vec2 vTexCoord;                                       \n\
-                                                                      \n\
-        void main(void)                                               \n\
-        {                                                             \n\
-            gl_FragColor = texture2DRect(uTexUnit,                    \n\
-                                         vTexCoord * uTexCoordMult);  \n\
-        }                                                             \n\
-    ";
-#ifdef ANDROID /* MOZ_WIDGET_ANDROID */
-    const char kTexExternalBlit_FragShaderSource[] = "\
-        #version 100                                                    \n\
-        #extension GL_OES_EGL_image_external : require                  \n\
-        #ifdef GL_FRAGMENT_PRECISION_HIGH                               \n\
-            precision highp float;                                      \n\
-        #else                                                           \n\
-            precision mediump float;                                    \n\
-        #endif                                                          \n\
-        varying vec2 vTexCoord;                                         \n\
-        uniform mat4 uTextureTransform;                                 \n\
-        uniform samplerExternalOES uTexUnit;                            \n\
-                                                                        \n\
-        void main()                                                     \n\
-        {                                                               \n\
-            gl_FragColor = texture2D(uTexUnit,                          \n\
-                (uTextureTransform * vec4(vTexCoord, 0.0, 1.0)).xy);    \n\
-        }                                                               \n\
-    ";
-#endif
-    /* From Rec601:
-    [R]   [1.1643835616438356,  0.0,                 1.5960267857142858]      [ Y -  16]
-    [G] = [1.1643835616438358, -0.3917622900949137, -0.8129676472377708]    x [Cb - 128]
-    [B]   [1.1643835616438356,  2.017232142857143,   8.862867620416422e-17]   [Cr - 128]
+bool
+GLBlitHelper::BlitImageToFramebuffer(layers::Image* srcImage,
+                                     const gfx::IntSize& destSize,
+                                     OriginPos destOrigin)
+{
+    switch (srcImage->GetFormat()) {
+    case ImageFormat::PLANAR_YCBCR:
+        return BlitImage(static_cast<PlanarYCbCrImage*>(srcImage), destSize, destOrigin);
 
-    For [0,1] instead of [0,255], and to 5 places:
-    [R]   [1.16438,  0.00000,  1.59603]   [ Y - 0.06275]
-    [G] = [1.16438, -0.39176, -0.81297] x [Cb - 0.50196]
-    [B]   [1.16438,  2.01723,  0.00000]   [Cr - 0.50196]
+#ifdef MOZ_WIDGET_ANDROID
+    case ImageFormat::SURFACE_TEXTURE:
+        return BlitImage(static_cast<layers::SurfaceTextureImage*>(srcImage));
 
-    From Rec709:
-    [R]   [1.1643835616438356,  4.2781193979771426e-17, 1.7927410714285714]     [ Y -  16]
-    [G] = [1.1643835616438358, -0.21324861427372963,   -0.532909328559444]    x [Cb - 128]
-    [B]   [1.1643835616438356,  2.1124017857142854,     0.0]                    [Cr - 128]
-
-    For [0,1] instead of [0,255], and to 5 places:
-    [R]   [1.16438,  0.00000,  1.79274]   [ Y - 0.06275]
-    [G] = [1.16438, -0.21325, -0.53291] x [Cb - 0.50196]
-    [B]   [1.16438,  2.11240,  0.00000]   [Cr - 0.50196]
-    */
-    const char kTexYUVPlanarBlit_FragShaderSource[] = "\
-        #version 100                                                        \n\
-        #ifdef GL_ES                                                        \n\
-        precision mediump float;                                            \n\
-        #endif                                                              \n\
-        varying vec2 vTexCoord;                                             \n\
-        uniform sampler2D uYTexture;                                        \n\
-        uniform sampler2D uCbTexture;                                       \n\
-        uniform sampler2D uCrTexture;                                       \n\
-        uniform vec2 uYTexScale;                                            \n\
-        uniform vec2 uCbCrTexScale;                                         \n\
-        uniform mat3 uYuvColorMatrix;                                       \n\
-        void main()                                                         \n\
-        {                                                                   \n\
-            float y = texture2D(uYTexture, vTexCoord * uYTexScale).r;       \n\
-            float cb = texture2D(uCbTexture, vTexCoord * uCbCrTexScale).r;  \n\
-            float cr = texture2D(uCrTexture, vTexCoord * uCbCrTexScale).r;  \n\
-            y = y - 0.06275;                                                \n\
-            cb = cb - 0.50196;                                              \n\
-            cr = cr - 0.50196;                                              \n\
-            vec3 yuv = vec3(y, cb, cr);                                     \n\
-            gl_FragColor.rgb = uYuvColorMatrix * yuv;                       \n\
-            gl_FragColor.a = 1.0;                                           \n\
-        }                                                                   \n\
-    ";
-
+    case ImageFormat::EGLIMAGE:
+        return BlitImage(static_cast<layers::EGLImageImage*>(srcImage), destSize,
+                         destOrigin);
+#endif
 #ifdef XP_MACOSX
-    const char kTexNV12PlanarBlit_FragShaderSource[] = "\
-        #version 100                                                             \n\
-        #extension GL_ARB_texture_rectangle : require                            \n\
-        #ifdef GL_ES                                                             \n\
-        precision mediump float                                                  \n\
-        #endif                                                                   \n\
-        varying vec2 vTexCoord;                                                  \n\
-        uniform sampler2DRect uYTexture;                                         \n\
-        uniform sampler2DRect uCbCrTexture;                                      \n\
-        uniform vec2 uYTexScale;                                                 \n\
-        uniform vec2 uCbCrTexScale;                                              \n\
-        void main()                                                              \n\
-        {                                                                        \n\
-            float y = texture2DRect(uYTexture, vTexCoord * uYTexScale).r;        \n\
-            float cb = texture2DRect(uCbCrTexture, vTexCoord * uCbCrTexScale).r; \n\
-            float cr = texture2DRect(uCbCrTexture, vTexCoord * uCbCrTexScale).a; \n\
-            y = (y - 0.06275) * 1.16438;                                         \n\
-            cb = cb - 0.50196;                                                   \n\
-            cr = cr - 0.50196;                                                   \n\
-            gl_FragColor.r = y + cr * 1.59603;                                   \n\
-            gl_FragColor.g = y - 0.81297 * cr - 0.39176 * cb;                    \n\
-            gl_FragColor.b = y + cb * 2.01723;                                   \n\
-            gl_FragColor.a = 1.0;                                                \n\
-        }                                                                        \n\
-    ";
+    case ImageFormat::MAC_IOSURFACE:
+        return BlitImage(srcImage->AsMacIOSurfaceImage());
 #endif
-
-    bool success = false;
-
-    GLuint* programPtr;
-    GLuint* fragShaderPtr;
-    const char* fragShaderSource;
-    switch (target) {
-    case ConvertEGLImage:
-    case BlitTex2D:
-        programPtr = &mTex2DBlit_Program;
-        fragShaderPtr = &mTex2DBlit_FragShader;
-        fragShaderSource = kTex2DBlit_FragShaderSource;
-        break;
-    case BlitTexRect:
-        programPtr = &mTex2DRectBlit_Program;
-        fragShaderPtr = &mTex2DRectBlit_FragShader;
-        fragShaderSource = kTex2DRectBlit_FragShaderSource;
-        break;
-#ifdef ANDROID
-    case ConvertSurfaceTexture:
-        programPtr = &mTexExternalBlit_Program;
-        fragShaderPtr = &mTexExternalBlit_FragShader;
-        fragShaderSource = kTexExternalBlit_FragShaderSource;
-        break;
-#endif
-    case ConvertPlanarYCbCr:
-        programPtr = &mTexYUVPlanarBlit_Program;
-        fragShaderPtr = &mTexYUVPlanarBlit_FragShader;
-        fragShaderSource = kTexYUVPlanarBlit_FragShaderSource;
-        break;
-#ifdef XP_MACOSX
-    case ConvertMacIOSurfaceImage:
-        programPtr = &mTexNV12PlanarBlit_Program;
-        fragShaderPtr = &mTexNV12PlanarBlit_FragShader;
-        fragShaderSource = kTexNV12PlanarBlit_FragShaderSource;
-        break;
+#ifdef XP_WIN
+    case ImageFormat::GPU_VIDEO:
+        return BlitImage(static_cast<layers::GPUVideoImage*>(srcImage), destSize,
+                         destOrigin);
+    case ImageFormat::D3D11_YCBCR_IMAGE:
+        return BlitImage((layers::D3D11YCbCrImage*)srcImage, destSize,
+                         destOrigin);
 #endif
     default:
+        gfxCriticalError() << "Unhandled srcImage->GetFormat(): "
+                           << uint32_t(srcImage->GetFormat());
         return false;
     }
-
-    GLuint& program = *programPtr;
-    GLuint& fragShader = *fragShaderPtr;
-
-    // Use do-while(false) to let us break on failure
-    do {
-        if (program) {
-            // Already have it...
-            success = true;
-            break;
-        }
-
-        if (!mTexBlit_Buffer) {
-
-            /* CCW tri-strip:
-             * 2---3
-             * | \ |
-             * 0---1
-             */
-            GLfloat verts[] = {
-                0.0f, 0.0f,
-                1.0f, 0.0f,
-                0.0f, 1.0f,
-                1.0f, 1.0f
-            };
-            HeapCopyOfStackArray<GLfloat> vertsOnHeap(verts);
-
-            MOZ_ASSERT(!mTexBlit_Buffer);
-            mGL->fGenBuffers(1, &mTexBlit_Buffer);
-            mGL->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mTexBlit_Buffer);
-
-            // Make sure we have a sane size.
-            mGL->fBufferData(LOCAL_GL_ARRAY_BUFFER, vertsOnHeap.ByteLength(), vertsOnHeap.Data(), LOCAL_GL_STATIC_DRAW);
-        }
-
-        if (!mTexBlit_VertShader) {
-
-            const char* vertShaderSource = kTexBlit_VertShaderSource;
-
-            mTexBlit_VertShader = mGL->fCreateShader(LOCAL_GL_VERTEX_SHADER);
-            mGL->fShaderSource(mTexBlit_VertShader, 1, &vertShaderSource, nullptr);
-            mGL->fCompileShader(mTexBlit_VertShader);
-        }
-
-        MOZ_ASSERT(!fragShader);
-        fragShader = mGL->fCreateShader(LOCAL_GL_FRAGMENT_SHADER);
-        mGL->fShaderSource(fragShader, 1, &fragShaderSource, nullptr);
-        mGL->fCompileShader(fragShader);
-
-        program = mGL->fCreateProgram();
-        mGL->fAttachShader(program, mTexBlit_VertShader);
-        mGL->fAttachShader(program, fragShader);
-        mGL->fBindAttribLocation(program, 0, "aPosition");
-        mGL->fLinkProgram(program);
-
-        if (GLContext::ShouldSpew()) {
-            GLint status = 0;
-            mGL->fGetShaderiv(mTexBlit_VertShader, LOCAL_GL_COMPILE_STATUS, &status);
-            if (status != LOCAL_GL_TRUE) {
-                NS_ERROR("Vert shader compilation failed.");
-
-                GLint length = 0;
-                mGL->fGetShaderiv(mTexBlit_VertShader, LOCAL_GL_INFO_LOG_LENGTH, &length);
-                if (!length) {
-                    printf_stderr("No shader info log available.\n");
-                    break;
-                }
-
-                auto buffer = MakeUnique<char[]>(length);
-                mGL->fGetShaderInfoLog(mTexBlit_VertShader, length, nullptr, buffer.get());
-
-                printf_stderr("Shader info log (%d bytes): %s\n", length, buffer.get());
-                break;
-            }
-
-            status = 0;
-            mGL->fGetShaderiv(fragShader, LOCAL_GL_COMPILE_STATUS, &status);
-            if (status != LOCAL_GL_TRUE) {
-                NS_ERROR("Frag shader compilation failed.");
-
-                GLint length = 0;
-                mGL->fGetShaderiv(fragShader, LOCAL_GL_INFO_LOG_LENGTH, &length);
-                if (!length) {
-                    printf_stderr("No shader info log available.\n");
-                    break;
-                }
-
-                auto buffer = MakeUnique<char[]>(length);
-                mGL->fGetShaderInfoLog(fragShader, length, nullptr, buffer.get());
-
-                printf_stderr("Shader info log (%d bytes): %s\n", length, buffer.get());
-                break;
-            }
-        }
-
-        GLint status = 0;
-        mGL->fGetProgramiv(program, LOCAL_GL_LINK_STATUS, &status);
-        if (status != LOCAL_GL_TRUE) {
-            if (GLContext::ShouldSpew()) {
-                NS_ERROR("Linking blit program failed.");
-                GLint length = 0;
-                mGL->fGetProgramiv(program, LOCAL_GL_INFO_LOG_LENGTH, &length);
-                if (!length) {
-                    printf_stderr("No program info log available.\n");
-                    break;
-                }
-
-                auto buffer = MakeUnique<char[]>(length);
-                mGL->fGetProgramInfoLog(program, length, nullptr, buffer.get());
-
-                printf_stderr("Program info log (%d bytes): %s\n", length, buffer.get());
-            }
-            break;
-        }
-
-        // Cache and set attribute and uniform
-        mGL->fUseProgram(program);
-        switch (target) {
-#ifdef ANDROID
-            case ConvertSurfaceTexture:
-#endif
-            case BlitTex2D:
-            case BlitTexRect:
-            case ConvertEGLImage: {
-                GLint texUnitLoc = mGL->fGetUniformLocation(program, "uTexUnit");
-                MOZ_ASSERT(texUnitLoc != -1, "uniform uTexUnit not found");
-                mGL->fUniform1i(texUnitLoc, 0);
-                break;
-            }
-            case ConvertPlanarYCbCr: {
-                GLint texY = mGL->fGetUniformLocation(program, "uYTexture");
-                GLint texCb = mGL->fGetUniformLocation(program, "uCbTexture");
-                GLint texCr = mGL->fGetUniformLocation(program, "uCrTexture");
-                mYTexScaleLoc = mGL->fGetUniformLocation(program, "uYTexScale");
-                mCbCrTexScaleLoc = mGL->fGetUniformLocation(program, "uCbCrTexScale");
-                mYuvColorMatrixLoc = mGL->fGetUniformLocation(program, "uYuvColorMatrix");
-
-                DebugOnly<bool> hasUniformLocations = texY != -1 &&
-                                                      texCb != -1 &&
-                                                      texCr != -1 &&
-                                                      mYTexScaleLoc != -1 &&
-                                                      mCbCrTexScaleLoc != -1 &&
-                                                      mYuvColorMatrixLoc != -1;
-                MOZ_ASSERT(hasUniformLocations, "uniforms not found");
-
-                mGL->fUniform1i(texY, Channel_Y);
-                mGL->fUniform1i(texCb, Channel_Cb);
-                mGL->fUniform1i(texCr, Channel_Cr);
-                break;
-            }
-            case ConvertMacIOSurfaceImage: {
-#ifdef XP_MACOSX
-                GLint texY = mGL->fGetUniformLocation(program, "uYTexture");
-                GLint texCbCr = mGL->fGetUniformLocation(program, "uCbCrTexture");
-                mYTexScaleLoc = mGL->fGetUniformLocation(program, "uYTexScale");
-                mCbCrTexScaleLoc= mGL->fGetUniformLocation(program, "uCbCrTexScale");
-
-                DebugOnly<bool> hasUniformLocations = texY != -1 &&
-                                                      texCbCr != -1 &&
-                                                      mYTexScaleLoc != -1 &&
-                                                      mCbCrTexScaleLoc != -1;
-                MOZ_ASSERT(hasUniformLocations, "uniforms not found");
-
-                mGL->fUniform1i(texY, Channel_Y);
-                mGL->fUniform1i(texCbCr, Channel_Cb);
-#endif
-                break;
-            }
-            default:
-                return false;
-        }
-        MOZ_ASSERT(mGL->fGetAttribLocation(program, "aPosition") == 0);
-        mYFlipLoc = mGL->fGetUniformLocation(program, "uYflip");
-        MOZ_ASSERT(mYFlipLoc != -1, "uniform: uYflip not found");
-        mTextureTransformLoc = mGL->fGetUniformLocation(program, "uTextureTransform");
-        if (mTextureTransformLoc >= 0) {
-            // Set identity matrix as default
-            gfx::Matrix4x4 identity;
-            mGL->fUniformMatrix4fv(mTextureTransformLoc, 1, false, &identity._11);
-        }
-        success = true;
-    } while (false);
-
-    if (!success) {
-        // Clean up:
-        DeleteTexBlitProgram();
-        return false;
-    }
-
-    mGL->fUseProgram(program);
-    mGL->fEnableVertexAttribArray(0);
-    mGL->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mTexBlit_Buffer);
-    mGL->fVertexAttribPointer(0,
-                              2,
-                              LOCAL_GL_FLOAT,
-                              false,
-                              0,
-                              nullptr);
-    return true;
-}
-
-bool
-GLBlitHelper::UseTexQuadProgram(BlitType target, const gfx::IntSize& srcSize)
-{
-    if (!InitTexQuadProgram(target)) {
-        return false;
-    }
-
-    if (target == BlitTexRect) {
-        GLint texCoordMultLoc = mGL->fGetUniformLocation(mTex2DRectBlit_Program, "uTexCoordMult");
-        MOZ_ASSERT(texCoordMultLoc != -1, "uniform not found");
-        mGL->fUniform2f(texCoordMultLoc, srcSize.width, srcSize.height);
-    }
-
-    return true;
-}
-
-void
-GLBlitHelper::DeleteTexBlitProgram()
-{
-    if (mTexBlit_Buffer) {
-        mGL->fDeleteBuffers(1, &mTexBlit_Buffer);
-        mTexBlit_Buffer = 0;
-    }
-    if (mTexBlit_VertShader) {
-        mGL->fDeleteShader(mTexBlit_VertShader);
-        mTexBlit_VertShader = 0;
-    }
-    if (mTex2DBlit_FragShader) {
-        mGL->fDeleteShader(mTex2DBlit_FragShader);
-        mTex2DBlit_FragShader = 0;
-    }
-    if (mTex2DRectBlit_FragShader) {
-        mGL->fDeleteShader(mTex2DRectBlit_FragShader);
-        mTex2DRectBlit_FragShader = 0;
-    }
-    if (mTex2DBlit_Program) {
-        mGL->fDeleteProgram(mTex2DBlit_Program);
-        mTex2DBlit_Program = 0;
-    }
-    if (mTex2DRectBlit_Program) {
-        mGL->fDeleteProgram(mTex2DRectBlit_Program);
-        mTex2DRectBlit_Program = 0;
-    }
-    if (mTexExternalBlit_FragShader) {
-        mGL->fDeleteShader(mTexExternalBlit_FragShader);
-        mTexExternalBlit_FragShader = 0;
-    }
-    if (mTexYUVPlanarBlit_FragShader) {
-        mGL->fDeleteShader(mTexYUVPlanarBlit_FragShader);
-        mTexYUVPlanarBlit_FragShader = 0;
-    }
-    if (mTexNV12PlanarBlit_FragShader) {
-        mGL->fDeleteShader(mTexNV12PlanarBlit_FragShader);
-        mTexNV12PlanarBlit_FragShader = 0;
-    }
-    if (mTexExternalBlit_Program) {
-        mGL->fDeleteProgram(mTexExternalBlit_Program);
-        mTexExternalBlit_Program = 0;
-    }
-    if (mTexYUVPlanarBlit_Program) {
-        mGL->fDeleteProgram(mTexYUVPlanarBlit_Program);
-        mTexYUVPlanarBlit_Program = 0;
-    }
-    if (mTexNV12PlanarBlit_Program) {
-        mGL->fDeleteProgram(mTexNV12PlanarBlit_Program);
-        mTexNV12PlanarBlit_Program = 0;
-    }
 }
 
-void
-GLBlitHelper::BlitFramebufferToFramebuffer(GLuint srcFB, GLuint destFB,
-                                           const gfx::IntSize& srcSize,
-                                           const gfx::IntSize& destSize,
-                                           bool internalFBs)
-{
-    MOZ_ASSERT(!srcFB || mGL->fIsFramebuffer(srcFB));
-    MOZ_ASSERT(!destFB || mGL->fIsFramebuffer(destFB));
-
-    MOZ_ASSERT(mGL->IsSupported(GLFeature::framebuffer_blit));
-
-    ScopedBindFramebuffer boundFB(mGL);
-    ScopedGLState scissor(mGL, LOCAL_GL_SCISSOR_TEST, false);
-
-    if (internalFBs) {
-        mGL->Screen()->BindReadFB_Internal(srcFB);
-        mGL->Screen()->BindDrawFB_Internal(destFB);
-    } else {
-        mGL->BindReadFB(srcFB);
-        mGL->BindDrawFB(destFB);
-    }
-
-    mGL->fBlitFramebuffer(0, 0,  srcSize.width,  srcSize.height,
-                          0, 0, destSize.width, destSize.height,
-                          LOCAL_GL_COLOR_BUFFER_BIT,
-                          LOCAL_GL_NEAREST);
-}
-
-void
-GLBlitHelper::BlitFramebufferToFramebuffer(GLuint srcFB, GLuint destFB,
-                                           const gfx::IntSize& srcSize,
-                                           const gfx::IntSize& destSize,
-                                           const GLFormats& srcFormats,
-                                           bool internalFBs)
-{
-    MOZ_ASSERT(!srcFB || mGL->fIsFramebuffer(srcFB));
-    MOZ_ASSERT(!destFB || mGL->fIsFramebuffer(destFB));
-
-    if (mGL->IsSupported(GLFeature::framebuffer_blit)) {
-        BlitFramebufferToFramebuffer(srcFB, destFB,
-                                     srcSize, destSize,
-                                     internalFBs);
-        return;
-    }
-
-    GLuint tex = CreateTextureForOffscreen(mGL, srcFormats, srcSize);
-    MOZ_ASSERT(tex);
-
-    BlitFramebufferToTexture(srcFB, tex, srcSize, srcSize, internalFBs);
-    BlitTextureToFramebuffer(tex, destFB, srcSize, destSize, internalFBs);
-
-    mGL->fDeleteTextures(1, &tex);
-}
-
-void
-GLBlitHelper::BindAndUploadYUVTexture(Channel which,
-                                      uint32_t width,
-                                      uint32_t height,
-                                      void* data,
-                                      bool needsAllocation)
-{
-    MOZ_ASSERT(which < Channel_Max, "Invalid channel!");
-    GLuint* srcTexArr[3] = {&mSrcTexY, &mSrcTexCb, &mSrcTexCr};
-    GLuint& tex = *srcTexArr[which];
-
-    // RED textures aren't valid in GLES2, and ALPHA textures are not valid in desktop GL Core Profiles.
-    // So use R8 textures on GL3.0+ and GLES3.0+, but LUMINANCE/LUMINANCE/UNSIGNED_BYTE otherwise.
-    GLenum format;
-    GLenum internalFormat;
-    if (mGL->IsAtLeast(gl::ContextProfile::OpenGLCore, 300) ||
-        mGL->IsAtLeast(gl::ContextProfile::OpenGLES, 300)) {
-        format = LOCAL_GL_RED;
-        internalFormat = LOCAL_GL_R8;
-    } else {
-        format = LOCAL_GL_LUMINANCE;
-        internalFormat = LOCAL_GL_LUMINANCE;
-    }
-
-    if (!tex) {
-        MOZ_ASSERT(needsAllocation);
-        tex = CreateTexture(mGL, internalFormat, format, LOCAL_GL_UNSIGNED_BYTE,
-                            gfx::IntSize(width, height), false);
-    }
-    mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + which);
-
-    mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, tex);
-    if (!needsAllocation) {
-        mGL->fTexSubImage2D(LOCAL_GL_TEXTURE_2D,
-                            0,
-                            0,
-                            0,
-                            width,
-                            height,
-                            format,
-                            LOCAL_GL_UNSIGNED_BYTE,
-                            data);
-    } else {
-        mGL->fTexImage2D(LOCAL_GL_TEXTURE_2D,
-                         0,
-                         internalFormat,
-                         width,
-                         height,
-                         0,
-                         format,
-                         LOCAL_GL_UNSIGNED_BYTE,
-                         data);
-    }
-}
-
-void
-GLBlitHelper::BindAndUploadEGLImage(EGLImage image, GLuint target)
-{
-    MOZ_ASSERT(image != EGL_NO_IMAGE, "Bad EGLImage");
-
-    if (!mSrcTexEGL) {
-        mGL->fGenTextures(1, &mSrcTexEGL);
-        mGL->fBindTexture(target, mSrcTexEGL);
-        mGL->fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
-        mGL->fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
-        mGL->fTexParameteri(target, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_NEAREST);
-        mGL->fTexParameteri(target, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST);
-    } else {
-        mGL->fBindTexture(target, mSrcTexEGL);
-    }
-    mGL->fEGLImageTargetTexture2D(target, image);
-}
+// -------------------------------------
 
 #ifdef MOZ_WIDGET_ANDROID
-
-#define ATTACH_WAIT_MS 50
-
 bool
-GLBlitHelper::BlitSurfaceTextureImage(layers::SurfaceTextureImage* stImage)
+GLBlitHelper::BlitImage(layers::SurfaceTextureImage* srcImage)
 {
     // FIXME
+    const auto& srcOrigin = srcImage->GetOriginPos();
+    (void)srcOrigin;
+    gfxCriticalError() << "BlitImage(SurfaceTextureImage) not implemented.";
     return false;
 }
 
 bool
-GLBlitHelper::BlitEGLImageImage(layers::EGLImageImage* image)
+GLBlitHelper::BlitImage(layers::EGLImageImage* const srcImage,
+                        const gfx::IntSize& destSize, const OriginPos destOrigin)
 {
-    EGLImage eglImage = image->GetImage();
-    EGLSync eglSync = image->GetSync();
-
+    const EGLImage eglImage = srcImage->GetImage();
+    const EGLSync eglSync = srcImage->GetSync();
     if (eglSync) {
         EGLint status = sEGLLibrary.fClientWaitSync(EGL_DISPLAY(), eglSync, 0, LOCAL_EGL_FOREVER);
         if (status != LOCAL_EGL_CONDITION_SATISFIED) {
             return false;
         }
     }
 
-    ScopedBindTextureUnit boundTU(mGL, LOCAL_GL_TEXTURE0);
-
-    int oldBinding = 0;
-    mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, &oldBinding);
-
-    BindAndUploadEGLImage(eglImage, LOCAL_GL_TEXTURE_2D);
-
-    mGL->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4);
-
-    mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, oldBinding);
-    return true;
-}
-
-#endif
-
-bool
-GLBlitHelper::BlitPlanarYCbCrImage(layers::PlanarYCbCrImage* yuvImage)
-{
-    ScopedBindTextureUnit boundTU(mGL, LOCAL_GL_TEXTURE0);
-    const PlanarYCbCrData* yuvData = yuvImage->GetData();
+    GLuint tex = 0;
+    mGL->fGenTextures(1, &tex);
 
-    bool needsAllocation = false;
-    if (mTexWidth != yuvData->mYStride || mTexHeight != yuvData->mYSize.height) {
-        mTexWidth = yuvData->mYStride;
-        mTexHeight = yuvData->mYSize.height;
-        needsAllocation = true;
-    }
-
-    GLint oldTex[3];
-    for (int i = 0; i < 3; i++) {
-        mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
-        mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, &oldTex[i]);
-    }
-
-    {
-        const ResetUnpackState reset(mGL);
-        BindAndUploadYUVTexture(Channel_Y, yuvData->mYStride, yuvData->mYSize.height, yuvData->mYChannel, needsAllocation);
-        BindAndUploadYUVTexture(Channel_Cb, yuvData->mCbCrStride, yuvData->mCbCrSize.height, yuvData->mCbChannel, needsAllocation);
-        BindAndUploadYUVTexture(Channel_Cr, yuvData->mCbCrStride, yuvData->mCbCrSize.height, yuvData->mCrChannel, needsAllocation);
-    }
-
-    if (needsAllocation) {
-        mGL->fUniform2f(mYTexScaleLoc, (float)yuvData->mYSize.width/yuvData->mYStride, 1.0f);
-        mGL->fUniform2f(mCbCrTexScaleLoc, (float)yuvData->mCbCrSize.width/yuvData->mCbCrStride, 1.0f);
-    }
-
-    const auto& yuvToRgb = gfxUtils::YuvToRgbMatrix3x3ColumnMajor(yuvData->mYUVColorSpace);
-    mGL->fUniformMatrix3fv(mYuvColorMatrixLoc, 1, 0, yuvToRgb);
+    const ScopedSaveMultiTex saveTex(mGL, 1, LOCAL_GL_TEXTURE_2D);
+    mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, tex);
+    mGL->TexParams_SetClampNoMips();
+    mGL->fEGLImageTargetTexture2D(LOCAL_GL_TEXTURE_2D, eglImage);
 
-    mGL->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4);
-    for (int i = 0; i < 3; i++) {
-        mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
-        mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, oldTex[i]);
-    }
-    return true;
-}
-
-#ifdef XP_MACOSX
-bool
-GLBlitHelper::BlitMacIOSurfaceImage(layers::MacIOSurfaceImage* ioImage)
-{
-    ScopedBindTextureUnit boundTU(mGL, LOCAL_GL_TEXTURE0);
-    MacIOSurface* surf = ioImage->GetSurface();
-
-    GLint oldTex[2];
-    for (int i = 0; i < 2; i++) {
-        mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
-        mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, &oldTex[i]);
-    }
-
-    GLuint textures[2];
-    mGL->fGenTextures(2, textures);
+    const auto& srcOrigin = srcImage->GetOriginPos();
+    const bool yFlip = destOrigin != srcOrigin;
+    const gfx::IntRect clipRect(0, 0, 1, 1);
+    const gfx::IntSize texSizeDivisor(1, 1);
+    const DrawBlitProg::DrawArgs baseArgs = { destSize, yFlip, clipRect, texSizeDivisor };
 
-    mGL->fActiveTexture(LOCAL_GL_TEXTURE0);
-    mGL->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, textures[0]);
-    mGL->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
-    mGL->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
-    surf->CGLTexImageIOSurface2D(mGL,
-                                 gl::GLContextCGL::Cast(mGL)->GetCGLContext(),
-                                 0);
-    mGL->fUniform2f(mYTexScaleLoc, surf->GetWidth(0), surf->GetHeight(0));
+    const auto& prog = GetDrawBlitProg(DrawBlitType::Tex2DRGB);
+    MOZ_RELEASE_ASSERT(prog);
+    prog->Draw(baseArgs);
 
-    mGL->fActiveTexture(LOCAL_GL_TEXTURE1);
-    mGL->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, textures[1]);
-    mGL->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
-    mGL->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
-    surf->CGLTexImageIOSurface2D(mGL,
-                                 gl::GLContextCGL::Cast(mGL)->GetCGLContext(),
-                                 1);
-    mGL->fUniform2f(mCbCrTexScaleLoc, surf->GetWidth(1), surf->GetHeight(1));
-
-    mGL->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4);
-    for (int i = 0; i < 2; i++) {
-        mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
-        mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, oldTex[i]);
-    }
-
-    mGL->fDeleteTextures(2, textures);
+    mGL->fDeleteTextures(1, &tex);
     return true;
 }
 #endif
 
-bool
-GLBlitHelper::BlitImageToFramebuffer(layers::Image* srcImage,
-                                     const gfx::IntSize& destSize,
-                                     GLuint destFB,
-                                     OriginPos destOrigin)
-{
-    ScopedGLDrawState autoStates(mGL);
-
-    BlitType type;
-    OriginPos srcOrigin;
+// -------------------------------------
 
-    switch (srcImage->GetFormat()) {
-    case ImageFormat::PLANAR_YCBCR:
-        type = ConvertPlanarYCbCr;
-#if defined(MOZ_WIDGET_ANDROID)
-        srcOrigin = OriginPos::TopLeft;
-#else
-        srcOrigin = OriginPos::BottomLeft;
-#endif // defined(MOZ_WIDGET_ANDROID)
-        break;
-
-#ifdef MOZ_WIDGET_ANDROID
-    case ImageFormat::SURFACE_TEXTURE:
-        type = ConvertSurfaceTexture;
-        srcOrigin = srcImage->AsSurfaceTextureImage()->GetOriginPos();
-        break;
-    case ImageFormat::EGLIMAGE:
-        type = ConvertEGLImage;
-        srcOrigin = srcImage->AsEGLImageImage()->GetOriginPos();
-        break;
-#endif
-#ifdef XP_MACOSX
-    case ImageFormat::MAC_IOSURFACE:
-        type = ConvertMacIOSurfaceImage;
-        srcOrigin = OriginPos::TopLeft;
-        break;
-#endif
-
-    default:
+bool
+GuessDivisors(const gfx::IntSize& ySize, const gfx::IntSize& uvSize,
+              gfx::IntSize* const out_divisors)
+{
+    const gfx::IntSize divisors((ySize.width  == uvSize.width ) ? 1 : 2,
+                                (ySize.height == uvSize.height) ? 1 : 2);
+    if (uvSize.width  * divisors.width != ySize.width ||
+        uvSize.height * divisors.height != ySize.height)
+    {
         return false;
     }
-
-    bool init = InitTexQuadProgram(type);
-    if (!init) {
-        return false;
-    }
-
-    const bool needsYFlip = (srcOrigin != destOrigin);
-    mGL->fUniform1f(mYFlipLoc, needsYFlip ? (float)1.0 : (float)0.0);
-
-    ScopedBindFramebuffer boundFB(mGL, destFB);
-    mGL->fColorMask(LOCAL_GL_TRUE, LOCAL_GL_TRUE, LOCAL_GL_TRUE, LOCAL_GL_TRUE);
-    mGL->fViewport(0, 0, destSize.width, destSize.height);
-
-    switch (type) {
-    case ConvertPlanarYCbCr:
-        return BlitPlanarYCbCrImage(static_cast<PlanarYCbCrImage*>(srcImage));
-
-#ifdef MOZ_WIDGET_ANDROID
-    case ConvertSurfaceTexture:
-        return BlitSurfaceTextureImage(static_cast<layers::SurfaceTextureImage*>(srcImage));
-
-    case ConvertEGLImage:
-        return BlitEGLImageImage(static_cast<layers::EGLImageImage*>(srcImage));
-#endif
-
-#ifdef XP_MACOSX
-    case ConvertMacIOSurfaceImage:
-        return BlitMacIOSurfaceImage(srcImage->AsMacIOSurfaceImage());
-#endif
-
-    default:
-        return false;
-    }
+    *out_divisors = divisors;
+    return true;
 }
 
 bool
-GLBlitHelper::BlitImageToTexture(layers::Image* srcImage,
-                                 const gfx::IntSize& destSize,
-                                 GLuint destTex,
-                                 GLenum destTarget,
-                                 OriginPos destOrigin)
+GLBlitHelper::BlitImage(layers::PlanarYCbCrImage* const yuvImage,
+                        const gfx::IntSize& destSize, const OriginPos destOrigin)
 {
-    ScopedFramebufferForTexture autoFBForTex(mGL, destTex, destTarget);
-    if (!autoFBForTex.IsComplete())
+    const auto& prog = GetDrawBlitProg(DrawBlitType::Tex2DPlanarYUV);
+    MOZ_RELEASE_ASSERT(prog);
+
+    if (!mYuvUploads[0]) {
+        mGL->fGenTextures(3, mYuvUploads);
+        const ScopedBindTexture bindTex(mGL, mYuvUploads[0]);
+        mGL->TexParams_SetClampNoMips();
+        mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[1]);
+        mGL->TexParams_SetClampNoMips();
+        mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[2]);
+        mGL->TexParams_SetClampNoMips();
+    }
+
+    // --
+
+    const PlanarYCbCrData* const yuvData = yuvImage->GetData();
+
+    if (yuvData->mYSkip || yuvData->mCbSkip || yuvData->mCrSkip ||
+        yuvData->mYSize.width < 0 || yuvData->mYSize.height < 0 ||
+        yuvData->mCbCrSize.width < 0 || yuvData->mCbCrSize.height < 0 ||
+        yuvData->mYStride < 0 || yuvData->mCbCrStride < 0)
+    {
+        gfxCriticalError() << "Unusual PlanarYCbCrData: "
+                           << yuvData->mYSkip << ","
+                           << yuvData->mCbSkip << ","
+                           << yuvData->mCrSkip << ", "
+                           << yuvData->mYSize.width << ","
+                           << yuvData->mYSize.height << ", "
+                           << yuvData->mCbCrSize.width << ","
+                           << yuvData->mCbCrSize.height << ", "
+                           << yuvData->mYStride << ","
+                           << yuvData->mCbCrStride;
+        return false;
+    }
+
+    const gfx::IntSize yTexSize(yuvData->mYStride, yuvData->mYSize.height);
+    const gfx::IntSize uvTexSize(yuvData->mCbCrStride, yuvData->mCbCrSize.height);
+    gfx::IntSize divisors;
+    if (!GuessDivisors(yTexSize, uvTexSize, &divisors)) {
+        gfxCriticalError() << "GuessDivisors failed:"
+                           << yTexSize.width << ","
+                           << yTexSize.height << ", "
+                           << uvTexSize.width << ","
+                           << uvTexSize.height;
         return false;
+    }
 
-    return BlitImageToFramebuffer(srcImage, destSize, autoFBForTex.FB(), destOrigin);
+    // --
+
+    // RED textures aren't valid in GLES2, and ALPHA textures are not valid in desktop GL Core Profiles.
+    // So use R8 textures on GL3.0+ and GLES3.0+, but LUMINANCE/LUMINANCE/UNSIGNED_BYTE otherwise.
+    GLenum internalFormat;
+    GLenum unpackFormat;
+    if (mGL->IsAtLeast(gl::ContextProfile::OpenGLCore, 300) ||
+        mGL->IsAtLeast(gl::ContextProfile::OpenGLES, 300))
+    {
+        internalFormat = LOCAL_GL_R8;
+        unpackFormat = LOCAL_GL_RED;
+    } else {
+        internalFormat = LOCAL_GL_LUMINANCE;
+        unpackFormat = LOCAL_GL_LUMINANCE;
+    }
+
+    // --
+
+    const ScopedSaveMultiTex saveTex(mGL, 3, LOCAL_GL_TEXTURE_2D);
+    const ResetUnpackState reset(mGL);
+
+    if (yTexSize != mYuvUploads_YSize ||
+        uvTexSize != mYuvUploads_UVSize)
+    {
+        mYuvUploads_YSize = yTexSize;
+        mYuvUploads_UVSize = uvTexSize;
+
+        mGL->fActiveTexture(LOCAL_GL_TEXTURE0);
+        mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[0]);
+        mGL->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, internalFormat,
+                         yTexSize.width, yTexSize.height, 0,
+                         unpackFormat, LOCAL_GL_UNSIGNED_BYTE, nullptr);
+        for (int i = 1; i < 2; i++) {
+            mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
+            mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[i]);
+            mGL->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, internalFormat,
+                             uvTexSize.width, uvTexSize.height, 0,
+                             unpackFormat, LOCAL_GL_UNSIGNED_BYTE, nullptr);
+        }
+    }
+
+    // --
+
+    mGL->fActiveTexture(LOCAL_GL_TEXTURE0);
+    mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[0]);
+    mGL->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, 0, 0, 0,
+                        yTexSize.width, yTexSize.height,
+                        unpackFormat, LOCAL_GL_UNSIGNED_BYTE, yuvData->mYChannel);
+    mGL->fActiveTexture(LOCAL_GL_TEXTURE1);
+    mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[1]);
+    mGL->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, 0, 0, 0,
+                        uvTexSize.width, uvTexSize.height,
+                        unpackFormat, LOCAL_GL_UNSIGNED_BYTE, yuvData->mCbChannel);
+    mGL->fActiveTexture(LOCAL_GL_TEXTURE2);
+    mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[2]);
+    mGL->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, 0, 0, 0,
+                        uvTexSize.width, uvTexSize.height,
+                        unpackFormat, LOCAL_GL_UNSIGNED_BYTE, yuvData->mCrChannel);
+
+    // --
+
+    const auto srcOrigin = OriginPos::BottomLeft;
+    const bool yFlip = (destOrigin != srcOrigin);
+    const auto& clipRect = yuvData->GetPictureRect();
+    const auto& colorSpace = yuvData->mYUVColorSpace;
+
+    const DrawBlitProg::BaseArgs baseArgs = { destSize, yFlip, clipRect, yTexSize };
+    const DrawBlitProg::YUVArgs yuvArgs = { uvTexSize, divisors, colorSpace };
+
+    prog->Draw(baseArgs, &yuvArgs);
+    return true;
+}
+
+// -------------------------------------
+
+#ifdef XP_MACOSX
+#error TODO
+bool
+GLBlitHelper::BlitImage(layers::MacIOSurfaceImage* ioImage)
+{
+    MacIOSurface* const iosurf = ioImage->GetSurface();
+MacIOSurfaceLib::IOSurfaceGetPixelFormat
+    const uint32_t pixelFormat = MacIOSurfaceLib::iosurf->GetPixelFormat();
+    DrawBlitType type;
+    int planes;
+    Maybe<YUVColorSpace> colorSpace = Nothing();
+    if (pixelFormat == '420v') {
+        type = DrawBlitType::TexRectNV12;
+        planes = 2;
+        colorSpace = Some(
+    } else if (pixelFormat == '2vuy') {
+        type = DrawBlitType::TexRectRGB;
+        planes = 1;
+    } else {
+        gfxCriticalError() << "Unrecognized pixelFormat: " << pixelFormat;
+        return false;
+    }
+
+    const auto& prog = GetDrawBlitProg(type);
+    MOZ_RELEASE_ASSERT(prog);
+
+    if (!mIOSurfaceTexs[0]) {
+        mGL->fGenTextures(2, &mIOSurfaceTexs);
+        const ScopedBindTexture bindTex(mGL, mIOSurfaceTexs[0], LOCAL_GL_TEXTURE_RECTANGLE);
+        mGL->TexParams_SetClampNoMips(LOCAL_GL_TEXTURE_RECTANGLE);
+        mGL->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE, mIOSurfaceTexs[1]);
+        mGL->TexParams_SetClampNoMips(LOCAL_GL_TEXTURE_RECTANGLE);
+    }
+
+    const ScopedBindMultiTex bindTex(mGL, LOCAL_GL_TEXTURE_RECTANGLE, planes,
+                                     mIOSurfaceTexs);
+    const auto& cglContext = gl::GLContextCGL::Cast(mGL)->GetCGLContext();
+    for (int i = 0; i < planes; i++) {
+        mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
+        surf->CGLTexImageIOSurface2D(mGL, cglContext, i);
+    }
+    mGL->fUniform2f(mYTexScaleLoc, surf->GetWidth(0), surf->GetHeight(0));
+    mGL->fUniform2f(mCbCrTexScaleLoc, surf->GetWidth(1), surf->GetHeight(1));
+
+    const auto& srcOrigin = OriginPos::TopLeft;
+    const auto& texMatrix = TexMatrixForOrigins(srcOrigin, destOrigin);
+    prog->Draw(texMatrix, destSize);
+    return true;
+}
+#endif
+
+// -----------------------------------------------------------------------------
+
+void
+GLBlitHelper::DrawBlitTextureToFramebuffer(const GLuint srcTex,
+                                           const gfx::IntSize& srcSize,
+                                           const gfx::IntSize& destSize,
+                                           const GLenum srcTarget) const
+{
+    const gfx::IntRect clipRect(0, 0, srcSize.width, srcSize.height);
+
+    DrawBlitType type;
+    gfx::IntSize texSizeDivisor;
+    switch (srcTarget) {
+    case LOCAL_GL_TEXTURE_2D:
+        type = DrawBlitType::Tex2DRGBA;
+        texSizeDivisor = srcSize;
+        break;
+    case LOCAL_GL_TEXTURE_RECTANGLE_ARB:
+        type = DrawBlitType::TexRectRGBA;
+        texSizeDivisor = gfx::IntSize(1, 1);
+        break;
+    default:
+        gfxCriticalError() << "Unexpected srcTarget: " << srcTarget;
+    }
+    const auto& prog = GetDrawBlitProg(type);
+    MOZ_ASSERT(prog);
+
+    const ScopedSaveMultiTex saveTex(mGL, 1, srcTarget);
+    mGL->fBindTexture(srcTarget, srcTex);
+
+    const bool yFlip = false;
+    const DrawBlitProg::BaseArgs baseArgs = { destSize, yFlip, clipRect, texSizeDivisor };
+    prog->Draw(baseArgs);
+}
+
+// -----------------------------------------------------------------------------
+
+void
+GLBlitHelper::BlitFramebuffer(const gfx::IntSize& srcSize,
+                              const gfx::IntSize& destSize) const
+{
+    MOZ_ASSERT(mGL->IsSupported(GLFeature::framebuffer_blit));
+
+    const ScopedGLState scissor(mGL, LOCAL_GL_SCISSOR_TEST, false);
+    mGL->fBlitFramebuffer(0, 0,  srcSize.width,  srcSize.height,
+                          0, 0, destSize.width, destSize.height,
+                          LOCAL_GL_COLOR_BUFFER_BIT,
+                          LOCAL_GL_NEAREST);
+}
+
+// --
+
+void
+GLBlitHelper::BlitFramebufferToFramebuffer(const GLuint srcFB, const GLuint destFB,
+                                           const gfx::IntSize& srcSize,
+                                           const gfx::IntSize& destSize) const
+{
+    MOZ_ASSERT(mGL->IsSupported(GLFeature::framebuffer_blit));
+    MOZ_ASSERT(!srcFB || mGL->fIsFramebuffer(srcFB));
+    MOZ_ASSERT(!destFB || mGL->fIsFramebuffer(destFB));
+
+    const ScopedBindFramebuffer boundFB(mGL);
+    mGL->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, srcFB);
+    mGL->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, destFB);
+
+    BlitFramebuffer(srcSize, destSize);
 }
 
 void
-GLBlitHelper::BlitTextureToFramebuffer(GLuint srcTex, GLuint destFB,
-                                       const gfx::IntSize& srcSize,
+GLBlitHelper::BlitTextureToFramebuffer(GLuint srcTex, const gfx::IntSize& srcSize,
                                        const gfx::IntSize& destSize,
-                                       GLenum srcTarget,
-                                       bool internalFBs)
+                                       GLenum srcTarget) const
 {
     MOZ_ASSERT(mGL->fIsTexture(srcTex));
-    MOZ_ASSERT(!destFB || mGL->fIsFramebuffer(destFB));
 
     if (mGL->IsSupported(GLFeature::framebuffer_blit)) {
-        ScopedFramebufferForTexture srcWrapper(mGL, srcTex, srcTarget);
-        MOZ_DIAGNOSTIC_ASSERT(srcWrapper.IsComplete());
-
-        BlitFramebufferToFramebuffer(srcWrapper.FB(), destFB,
-                                     srcSize, destSize,
-                                     internalFBs);
+        const ScopedFramebufferForTexture srcWrapper(mGL, srcTex, srcTarget);
+        const ScopedBindFramebuffer bindFB(mGL);
+        mGL->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, srcWrapper.FB());
+        BlitFramebuffer(srcSize, destSize);
         return;
     }
 
-    DrawBlitTextureToFramebuffer(srcTex, destFB, srcSize, destSize, srcTarget,
-                                 internalFBs);
-}
-
-
-void
-GLBlitHelper::DrawBlitTextureToFramebuffer(GLuint srcTex, GLuint destFB,
-                                           const gfx::IntSize& srcSize,
-                                           const gfx::IntSize& destSize,
-                                           GLenum srcTarget,
-                                           bool internalFBs)
-{
-    BlitType type;
-    switch (srcTarget) {
-    case LOCAL_GL_TEXTURE_2D:
-        type = BlitTex2D;
-        break;
-    case LOCAL_GL_TEXTURE_RECTANGLE_ARB:
-        type = BlitTexRect;
-        break;
-    default:
-        MOZ_CRASH("GFX: Fatal Error: Bad `srcTarget`.");
-        break;
-    }
-
-    ScopedGLDrawState autoStates(mGL);
-    const ScopedBindFramebuffer bindFB(mGL);
-    if (internalFBs) {
-        mGL->Screen()->BindFB_Internal(destFB);
-    } else {
-        mGL->BindFB(destFB);
-    }
-
-    // Does destructive things to (only!) what we just saved above.
-    bool good = UseTexQuadProgram(type, srcSize);
-    if (!good) {
-        // We're up against the wall, so bail.
-        MOZ_DIAGNOSTIC_ASSERT(false,
-                              "Error: Failed to prepare to blit texture->framebuffer.\n");
-        mGL->fScissor(0, 0, destSize.width, destSize.height);
-        mGL->fColorMask(1, 1, 1, 1);
-        mGL->fClear(LOCAL_GL_COLOR_BUFFER_BIT);
-        return;
-    }
-
-    const ScopedBindTexture bindTex(mGL, srcTex, srcTarget);
-    mGL->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4);
+    DrawBlitTextureToFramebuffer(srcTex, srcSize, destSize, srcTarget);
 }
 
 void
-GLBlitHelper::BlitFramebufferToTexture(GLuint srcFB, GLuint destTex,
+GLBlitHelper::BlitFramebufferToTexture(GLuint destTex,
                                        const gfx::IntSize& srcSize,
                                        const gfx::IntSize& destSize,
-                                       GLenum destTarget,
-                                       bool internalFBs)
+                                       GLenum destTarget) const
 {
-    // On the Android 4.3 emulator, IsFramebuffer may return false incorrectly.
-    MOZ_ASSERT_IF(mGL->Renderer() != GLRenderer::AndroidEmulator, !srcFB || mGL->fIsFramebuffer(srcFB));
     MOZ_ASSERT(mGL->fIsTexture(destTex));
 
     if (mGL->IsSupported(GLFeature::framebuffer_blit)) {
-        ScopedFramebufferForTexture destWrapper(mGL, destTex, destTarget);
-
-        BlitFramebufferToFramebuffer(srcFB, destWrapper.FB(),
-                                     srcSize, destSize,
-                                     internalFBs);
+        const ScopedFramebufferForTexture destWrapper(mGL, destTex, destTarget);
+        const ScopedBindFramebuffer bindFB(mGL);
+        mGL->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, destWrapper.FB());
+        BlitFramebuffer(srcSize, destSize);
         return;
     }
 
     ScopedBindTexture autoTex(mGL, destTex, destTarget);
-
-    ScopedBindFramebuffer boundFB(mGL);
-    if (internalFBs) {
-        mGL->Screen()->BindFB_Internal(srcFB);
-    } else {
-        mGL->BindFB(srcFB);
-    }
-
     ScopedGLState scissor(mGL, LOCAL_GL_SCISSOR_TEST, false);
     mGL->fCopyTexSubImage2D(destTarget, 0,
-                       0, 0,
-                       0, 0,
-                       srcSize.width, srcSize.height);
+                            0, 0,
+                            0, 0,
+                            srcSize.width, srcSize.height);
 }
 
 void
 GLBlitHelper::BlitTextureToTexture(GLuint srcTex, GLuint destTex,
                                    const gfx::IntSize& srcSize,
                                    const gfx::IntSize& destSize,
-                                   GLenum srcTarget, GLenum destTarget)
+                                   GLenum srcTarget, GLenum destTarget) const
 {
     MOZ_ASSERT(mGL->fIsTexture(srcTex));
     MOZ_ASSERT(mGL->fIsTexture(destTex));
 
-    // Generally, just use the CopyTexSubImage path
-    ScopedFramebufferForTexture srcWrapper(mGL, srcTex, srcTarget);
-
-    BlitFramebufferToTexture(srcWrapper.FB(), destTex,
-                             srcSize, destSize, destTarget);
+    // Start down the CopyTexSubImage path, not the DrawBlit path.
+    const ScopedFramebufferForTexture srcWrapper(mGL, srcTex, srcTarget);
+    const ScopedBindFramebuffer bindFB(mGL, srcWrapper.FB());
+    BlitFramebufferToTexture(destTex, srcSize, destSize, destTarget);
 }
 
 } // namespace gl
 } // namespace mozilla
--- a/gfx/gl/GLBlitHelper.h
+++ b/gfx/gl/GLBlitHelper.h
@@ -5,160 +5,186 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GLBLITHELPER_H_
 #define GLBLITHELPER_H_
 
 #include "GLContextTypes.h"
 #include "GLConsts.h"
 #include "nsSize.h"
+#include "ipc/IPCMessageUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/gfx/Point.h"
 
+#ifdef XP_WIN
+#include <windows.h>
+#endif
+
 namespace mozilla {
 
 namespace layers {
+class D3D11YCbCrImage;
 class Image;
+class GPUVideoImage;
 class PlanarYCbCrImage;
 class SurfaceTextureImage;
 class MacIOSurfaceImage;
 class EGLImageImage;
+class SurfaceDescriptorD3D10;
+class SurfaceDescriptorDXGIYCbCr;
 } // namespace layers
 
 namespace gl {
 
+class BindAnglePlanes;
 class GLContext;
 
+bool
+GuessDivisors(const gfx::IntSize& ySize, const gfx::IntSize& uvSize,
+              gfx::IntSize* const out_divisors);
+
+class DrawBlitProg final
+{
+    GLBlitHelper& mParent;
+    const GLuint mProg;
+    const GLint mLoc_u1ForYFlip;
+    const GLint mLoc_uClipRect;
+    const GLint mLoc_uTexSize0;
+    const GLint mLoc_uTexSize1;
+    const GLint mLoc_uDivisors;
+    const GLint mLoc_uColorMatrix;
+
+public:
+    DrawBlitProg(GLBlitHelper* parent, GLuint prog);
+    ~DrawBlitProg();
+
+    struct BaseArgs final {
+        gfx::IntSize destSize;
+        bool yFlip;
+        gfx::IntRect clipRect;
+        gfx::IntSize texSize0;
+    };
+    struct YUVArgs final {
+        gfx::IntSize texSize1;
+        gfx::IntSize divisors;
+        YUVColorSpace colorSpace;
+    };
+
+    void Draw(const BaseArgs& args, const YUVArgs* argsYUV = nullptr) const;
+};
+
+class ScopedSaveMultiTex final
+{
+    GLContext& mGL;
+    const uint8_t mTexCount;
+    const GLenum mTexTarget;
+    const GLuint mOldTexUnit;
+    GLuint mOldTexSampler[3];
+    GLuint mOldTex[3];
+
+public:
+    ScopedSaveMultiTex(GLContext* gl, uint8_t texCount, GLenum texTarget);
+    ~ScopedSaveMultiTex();
+};
+
 /** Buffer blitting helper */
 class GLBlitHelper final
 {
-    enum Channel
+    friend class BindAnglePlanes;
+    friend class DrawBlitProg;
+    friend class GLContext;
+
+    enum class DrawBlitType : uint8_t
     {
-        Channel_Y = 0,
-        Channel_Cb,
-        Channel_Cr,
-        Channel_Max,
+        Tex2DRGBA,
+        Tex2DPlanarYUV,
+        TexRectRGBA,
+        //TexExtYUV,
+        TexExtNV12,
+        TexExtPlanarYUV,
     };
 
-    /**
-     * BlitTex2D is used to copy blit the content of a GL_TEXTURE_2D object,
-     * BlitTexRect is used to copy blit the content of a GL_TEXTURE_RECT object,
-     * The difference between BlitTex2D and BlitTexRect is the texture type, which affect
-     * the fragment shader a bit.
-     *
-     * ConvertPlnarYcbCr is used to color convert copy blit the PlanarYCbCrImage
-     * into a normal RGB texture by create textures of each color channel, and
-     * convert it in GPU.
-     * Convert type is created for canvas.
-     */
-    enum BlitType
-    {
-        BlitTex2D,
-        BlitTexRect,
-        ConvertPlanarYCbCr,
-        ConvertSurfaceTexture,
-        ConvertEGLImage,
-        ConvertMacIOSurfaceImage
-    };
-    // The GLContext is the sole owner of the GLBlitHelper.
-    GLContext* mGL;
+    GLContext* const mGL;
+    std::map<uint8_t, UniquePtr<DrawBlitProg>> mDrawBlitProgs;
 
-    GLuint mTexBlit_Buffer;
-    GLuint mTexBlit_VertShader;
-    GLuint mTex2DBlit_FragShader;
-    GLuint mTex2DRectBlit_FragShader;
-    GLuint mTex2DBlit_Program;
-    GLuint mTex2DRectBlit_Program;
+    GLuint mQuadVAO;
 
-    GLint mYFlipLoc;
+    GLuint mYuvUploads[3];
+    gfx::IntSize mYuvUploads_YSize;
+    gfx::IntSize mYuvUploads_UVSize;
 
-    GLint mTextureTransformLoc;
+#ifdef XP_WIN
+    mutable RefPtr<ID3D11Device> mD3D11;
+
+    ID3D11Device* GetD3D11() const;
+#endif
 
-    // Data for image blit path
-    GLuint mTexExternalBlit_FragShader;
-    GLuint mTexYUVPlanarBlit_FragShader;
-    GLuint mTexNV12PlanarBlit_FragShader;
-    GLuint mTexExternalBlit_Program;
-    GLuint mTexYUVPlanarBlit_Program;
-    GLuint mTexNV12PlanarBlit_Program;
-    GLuint mFBO;
-    GLuint mSrcTexY;
-    GLuint mSrcTexCb;
-    GLuint mSrcTexCr;
-    GLuint mSrcTexEGL;
-    GLint mYTexScaleLoc;
-    GLint mCbCrTexScaleLoc;
-    GLint mYuvColorMatrixLoc;
-    int mTexWidth;
-    int mTexHeight;
+
 
-    // Cache some uniform values
-    float mCurYScale;
-    float mCurCbCrScale;
-
-    void UseBlitProgram();
-    void SetBlitFramebufferForDestTexture(GLuint aTexture);
+    const DrawBlitProg* GetDrawBlitProg(DrawBlitType type) const;
 
-    bool UseTexQuadProgram(BlitType target, const gfx::IntSize& srcSize);
-    bool InitTexQuadProgram(BlitType target = BlitTex2D);
-    void DeleteTexBlitProgram();
-    void BindAndUploadYUVTexture(Channel which, uint32_t width, uint32_t height, void* data, bool allocation);
-    void BindAndUploadEGLImage(EGLImage image, GLuint target);
-
-    bool BlitPlanarYCbCrImage(layers::PlanarYCbCrImage* yuvImage);
+    bool BlitImage(layers::PlanarYCbCrImage* yuvImage, const gfx::IntSize& destSize,
+                   OriginPos destOrigin);
 #ifdef MOZ_WIDGET_ANDROID
     // Blit onto the current FB.
-    bool BlitSurfaceTextureImage(layers::SurfaceTextureImage* stImage);
-    bool BlitEGLImageImage(layers::EGLImageImage* eglImage);
+    bool BlitImage(layers::SurfaceTextureImage* stImage);
+    bool BlitImage(layers::EGLImageImage* eglImage);
 #endif
 #ifdef XP_MACOSX
-    bool BlitMacIOSurfaceImage(layers::MacIOSurfaceImage* ioImage);
+    bool BlitImage(layers::MacIOSurfaceImage* ioImage);
 #endif
 
     explicit GLBlitHelper(GLContext* gl);
-
-    friend class GLContext;
-
 public:
     ~GLBlitHelper();
 
-    // If you don't have |srcFormats| for the 2nd definition,
-    // then you'll need the framebuffer_blit extensions to use
-    // the first BlitFramebufferToFramebuffer.
-    void BlitFramebufferToFramebuffer(GLuint srcFB, GLuint destFB,
-                                      const gfx::IntSize& srcSize,
-                                      const gfx::IntSize& destSize,
-                                      bool internalFBs = false);
+    void BlitFramebuffer(const gfx::IntSize& srcSize,
+                         const gfx::IntSize& destSize) const;
     void BlitFramebufferToFramebuffer(GLuint srcFB, GLuint destFB,
                                       const gfx::IntSize& srcSize,
-                                      const gfx::IntSize& destSize,
-                                      const GLFormats& srcFormats,
-                                      bool internalFBs = false);
-    void BlitTextureToFramebuffer(GLuint srcTex, GLuint destFB,
-                                  const gfx::IntSize& srcSize,
+                                      const gfx::IntSize& destSize) const;
+    void BlitFramebufferToTexture(GLuint destTex, const gfx::IntSize& srcSize,
                                   const gfx::IntSize& destSize,
-                                  GLenum srcTarget = LOCAL_GL_TEXTURE_2D,
-                                  bool internalFBs = false);
-    void DrawBlitTextureToFramebuffer(GLuint srcTex, GLuint destFB,
-                                      const gfx::IntSize& srcSize,
-                                      const gfx::IntSize& destSize,
-                                      GLenum srcTarget = LOCAL_GL_TEXTURE_2D,
-                                      bool internalFBs = false);
-    void BlitFramebufferToTexture(GLuint srcFB, GLuint destTex,
-                                  const gfx::IntSize& srcSize,
+                                  GLenum destTarget = LOCAL_GL_TEXTURE_2D) const;
+    void BlitTextureToFramebuffer(GLuint srcTex, const gfx::IntSize& srcSize,
                                   const gfx::IntSize& destSize,
-                                  GLenum destTarget = LOCAL_GL_TEXTURE_2D,
-                                  bool internalFBs = false);
+                                  GLenum srcTarget = LOCAL_GL_TEXTURE_2D) const;
     void BlitTextureToTexture(GLuint srcTex, GLuint destTex,
                               const gfx::IntSize& srcSize,
                               const gfx::IntSize& destSize,
                               GLenum srcTarget = LOCAL_GL_TEXTURE_2D,
-                              GLenum destTarget = LOCAL_GL_TEXTURE_2D);
+                              GLenum destTarget = LOCAL_GL_TEXTURE_2D) const;
+
+
+    void DrawBlitTextureToFramebuffer(GLuint srcTex, const gfx::IntSize& srcSize,
+                                      const gfx::IntSize& destSize,
+                                      GLenum srcTarget = LOCAL_GL_TEXTURE_2D) const;
+
     bool BlitImageToFramebuffer(layers::Image* srcImage, const gfx::IntSize& destSize,
-                                GLuint destFB, OriginPos destOrigin);
-    bool BlitImageToTexture(layers::Image* srcImage, const gfx::IntSize& destSize,
-                            GLuint destTex, GLenum destTarget, OriginPos destOrigin);
+                                OriginPos destOrigin);
+
+private:
+#ifdef XP_WIN
+    // GLBlitHelperD3D.cpp:
+    bool BlitImage(layers::GPUVideoImage* srcImage, const gfx::IntSize& destSize,
+                   OriginPos destOrigin) const;
+    bool BlitImage(layers::D3D11YCbCrImage* srcImage, const gfx::IntSize& destSize,
+                   OriginPos destOrigin) const;
+
+    bool BlitDescriptor(const layers::SurfaceDescriptorD3D10& desc,
+                        const gfx::IntSize& destSize, OriginPos destOrigin) const;
+
+    bool BlitAngleYCbCr(const WindowsHandle (&handleList)[3],
+                        const gfx::IntRect& clipRect,
+                        const gfx::IntSize& ySize, const gfx::IntSize& uvSize,
+                        const YUVColorSpace colorSpace,
+                        const gfx::IntSize& destSize, OriginPos destOrigin) const;
+
+    bool BlitAnglePlanes(uint8_t numPlanes, const RefPtr<ID3D11Texture2D>* texD3DList,
+                         const DrawBlitProg* prog, const DrawBlitProg::BaseArgs& baseArgs,
+                         const DrawBlitProg::YUVArgs* const yuvArgs) const;
+#endif
 };
 
 } // namespace gl
 } // namespace mozilla
 
 #endif // GLBLITHELPER_H_
new file mode 100644
--- /dev/null
+++ b/gfx/gl/GLBlitHelperD3D.cpp
@@ -0,0 +1,334 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=4 et sw=4 tw=80: */
+/* 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 "GLBlitHelper.h"
+
+#include <d3d11.h>
+
+#include "GLContext.h"
+#include "GLLibraryEGL.h"
+#include "GPUVideoImage.h"
+#include "ScopedGLHelpers.h"
+
+#include "mozilla/layers/D3D11YCbCrImage.h"
+#include "mozilla/layers/TextureD3D11.h"
+
+namespace mozilla {
+namespace gl {
+
+static EGLStreamKHR
+StreamFromD3DTexture(ID3D11Texture2D* const texD3D,
+                     const EGLAttrib* const postAttribs)
+{
+    auto& egl = sEGLLibrary;
+    const auto& display = egl.Display();
+    const auto stream = egl.fCreateStreamKHR(display, nullptr);
+    MOZ_ASSERT(stream);
+    if (!stream)
+        return 0;
+    bool ok = true;
+    MOZ_ALWAYS_TRUE( ok &= bool(egl.fStreamConsumerGLTextureExternalAttribsNV(display,
+                                                                              stream,
+                                                                              nullptr)) );
+    MOZ_ALWAYS_TRUE( ok &= bool(egl.fCreateStreamProducerD3DTextureNV12ANGLE(display,
+                                                                             stream,
+                                                                             nullptr)) );
+    MOZ_ALWAYS_TRUE( ok &= bool(egl.fStreamPostD3DTextureNV12ANGLE(display, stream,
+                                                                   texD3D,
+                                                                   postAttribs)) );
+    if (ok)
+        return stream;
+
+    (void)egl.fDestroyStreamKHR(display, stream);
+    return 0;
+}
+
+static RefPtr<ID3D11Texture2D>
+OpenSharedTexture(ID3D11Device* const d3d, const WindowsHandle handle)
+{
+    RefPtr<ID3D11Texture2D> tex;
+    auto hr = d3d->OpenSharedResource((HANDLE)handle, __uuidof(ID3D11Texture2D),
+                                      (void**)(ID3D11Texture2D**)getter_AddRefs(tex));
+    if (FAILED(hr)) {
+        MOZ_ASSERT(false, "OpenSharedResource should not fail");
+        return nullptr;
+    }
+    return tex;
+}
+
+// -------------------------------------
+
+class BindAnglePlanes final
+{
+    const GLBlitHelper& mParent;
+    const uint8_t mNumPlanes;
+    const ScopedSaveMultiTex mMultiTex;
+    GLuint mTempTexs[3];
+    EGLStreamKHR mStreams[3];
+    RefPtr<IDXGIKeyedMutex> mMutexList[3];
+    bool mSuccess;
+
+public:
+    BindAnglePlanes(const GLBlitHelper* const parent, const uint8_t numPlanes,
+                    const RefPtr<ID3D11Texture2D>* const texD3DList,
+                    const EGLAttrib* const* postAttribsList = nullptr)
+        : mParent(*parent)
+        , mNumPlanes(numPlanes)
+        , mMultiTex(mParent.mGL, mNumPlanes, LOCAL_GL_TEXTURE_EXTERNAL)
+        , mTempTexs{0}
+        , mStreams{0}
+        , mSuccess(true)
+    {
+        MOZ_RELEASE_ASSERT(numPlanes >= 1 && numPlanes <= 3);
+
+        const auto& gl = mParent.mGL;
+        auto& egl = sEGLLibrary;
+        const auto& display = egl.Display();
+
+        gl->fGenTextures(numPlanes, mTempTexs);
+
+        for (uint8_t i = 0; i < mNumPlanes; i++) {
+            gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
+            gl->fBindTexture(LOCAL_GL_TEXTURE_EXTERNAL, mTempTexs[i]);
+            const EGLAttrib* postAttribs = nullptr;
+            if (postAttribsList) {
+                postAttribs = postAttribsList[i];
+            }
+            mStreams[i] = StreamFromD3DTexture(texD3DList[i], postAttribs);
+            mSuccess &= bool(mStreams[i]);
+        }
+
+        if (mSuccess) {
+            for (uint8_t i = 0; i < mNumPlanes; i++) {
+                MOZ_ALWAYS_TRUE( egl.fStreamConsumerAcquireKHR(display, mStreams[i]) );
+
+                auto& mutex = mMutexList[i];
+                texD3DList[i]->QueryInterface(_uuidof(IDXGIKeyedMutex),
+                                              (void**)getter_AddRefs(mutex));
+                if (mutex) {
+                    const auto hr = mutex->AcquireSync(0, 100);
+                    if (FAILED(hr)) {
+                        NS_WARNING("BindAnglePlanes failed to acquire KeyedMutex.");
+                        mSuccess = false;
+                    }
+                }
+            }
+        }
+    }
+
+    ~BindAnglePlanes()
+    {
+        const auto& gl = mParent.mGL;
+        auto& egl = sEGLLibrary;
+        const auto& display = egl.Display();
+
+        if (mSuccess) {
+            for (uint8_t i = 0; i < mNumPlanes; i++) {
+                MOZ_ALWAYS_TRUE( egl.fStreamConsumerReleaseKHR(display, mStreams[i]) );
+                if (mMutexList[i]) {
+                    mMutexList[i]->ReleaseSync(0);
+                }
+            }
+        }
+
+        for (uint8_t i = 0; i < mNumPlanes; i++) {
+            (void)egl.fDestroyStreamKHR(display, mStreams[i]);
+        }
+
+        gl->fDeleteTextures(mNumPlanes, mTempTexs);
+    }
+
+    const bool& Success() const { return mSuccess; }
+};
+
+// -------------------------------------
+
+ID3D11Device*
+GLBlitHelper::GetD3D11() const
+{
+    if (mD3D11)
+        return mD3D11;
+
+    if (!mGL->IsANGLE())
+        return nullptr;
+
+    auto& egl = sEGLLibrary;
+    EGLDeviceEXT deviceEGL = 0;
+    MOZ_ALWAYS_TRUE( egl.fQueryDisplayAttribEXT(egl.Display(), LOCAL_EGL_DEVICE_EXT,
+                                                (EGLAttrib*)&deviceEGL) );
+    if (!egl.fQueryDeviceAttribEXT(deviceEGL, LOCAL_EGL_D3D11_DEVICE_ANGLE,
+                                   (EGLAttrib*)(ID3D11Device**)getter_AddRefs(mD3D11)))
+    {
+        MOZ_ASSERT(false, "d3d9?");
+        return nullptr;
+    }
+    return mD3D11;
+}
+
+// -------------------------------------
+
+bool
+GLBlitHelper::BlitImage(layers::GPUVideoImage* const srcImage,
+                        const gfx::IntSize& destSize, const OriginPos destOrigin) const
+{
+    const auto& data = srcImage->GetData();
+    if (!data)
+        return false;
+
+    const auto& desc = data->SD();
+    const auto& subdescUnion = desc.subdesc();
+    switch (subdescUnion.type()) {
+    case subdescUnion.TSurfaceDescriptorD3D10:
+        {
+            const auto& subdesc = subdescUnion.get_SurfaceDescriptorD3D10();
+            return BlitDescriptor(subdesc, destSize, destOrigin);
+        }
+    case subdescUnion.TSurfaceDescriptorDXGIYCbCr:
+        {
+            const auto& subdesc = subdescUnion.get_SurfaceDescriptorDXGIYCbCr();
+
+            const auto& clipSize = subdesc.size();
+            const auto& ySize = subdesc.sizeY();
+            const auto& uvSize = subdesc.sizeCbCr();
+
+            const gfx::IntRect clipRect(0, 0, clipSize.width, clipSize.height);
+            const auto colorSpace = YUVColorSpace::BT601;
+
+            const WindowsHandle handles[3] = {
+                subdesc.handleY(),
+                subdesc.handleCb(),
+                subdesc.handleCr()
+            };
+            return BlitAngleYCbCr(handles, clipRect, ySize, uvSize, colorSpace, destSize,
+                                  destOrigin);
+        }
+    default:
+        gfxCriticalError() << "Unhandled subdesc type: " << uint32_t(subdescUnion.type());
+        return false;
+    }
+}
+
+// -------------------------------------
+
+bool
+GLBlitHelper::BlitImage(layers::D3D11YCbCrImage* const srcImage,
+                        const gfx::IntSize& destSize, const OriginPos destOrigin) const
+{
+    const auto& data = srcImage->GetData();
+    if (!data)
+        return false;
+
+    const auto& clipRect = srcImage->mPictureRect;
+    const auto& colorSpace = srcImage->mColorSpace;
+
+    const WindowsHandle handles[3] = {
+        (WindowsHandle)data->mHandles[0],
+        (WindowsHandle)data->mHandles[1],
+        (WindowsHandle)data->mHandles[2]
+    };
+    return BlitAngleYCbCr(handles, srcImage->mPictureRect, srcImage->mYSize,
+                          srcImage->mCbCrSize, srcImage->mColorSpace, destSize,
+                          destOrigin);
+}
+
+// -------------------------------------
+
+bool
+GLBlitHelper::BlitDescriptor(const layers::SurfaceDescriptorD3D10& desc,
+                             const gfx::IntSize& destSize, OriginPos destOrigin) const
+{
+    const auto& d3d = GetD3D11();
+    if (!d3d)
+        return false;
+
+    const auto& handle = desc.handle();
+    const auto& format = desc.format();
+    const auto& clipSize = desc.size();
+
+    const auto srcOrigin = OriginPos::BottomLeft;
+    const gfx::IntRect clipRect(0, 0, clipSize.width, clipSize.height);
+    const auto colorSpace = YUVColorSpace::BT601;
+
+    if (format != gfx::SurfaceFormat::NV12) {
+        gfxCriticalError() << "Non-NV12 format for SurfaceDescriptorD3D10: "
+                           << uint32_t(format);
+        return nullptr;
+    }
+
+    const auto tex = OpenSharedTexture(d3d, handle);
+    const RefPtr<ID3D11Texture2D> texList[2] = { tex, tex };
+    const EGLAttrib postAttribs0[] = {
+        LOCAL_EGL_NATIVE_BUFFER_PLANE_OFFSET_IMG, 0,
+        LOCAL_EGL_NONE
+    };
+    const EGLAttrib postAttribs1[] = {
+        LOCAL_EGL_NATIVE_BUFFER_PLANE_OFFSET_IMG, 1,
+        LOCAL_EGL_NONE
+    };
+    const EGLAttrib* const postAttribsList[2] = { postAttribs0, postAttribs1 };
+    // /layers/d3d11/CompositorD3D11.cpp uses bt601 for EffectTypes::NV12.
+    //return BlitAngleNv12(tex, YUVColorSpace::BT601, destSize, destOrigin);
+
+    const BindAnglePlanes bindPlanes(this, 2, texList, postAttribsList);
+
+    D3D11_TEXTURE2D_DESC texDesc = {0};
+    tex->GetDesc(&texDesc);
+
+    const gfx::IntSize ySize(texDesc.Width, texDesc.Height);
+    const gfx::IntSize divisors(2, 2);
+    MOZ_ASSERT(ySize.width % divisors.width == 0);
+    MOZ_ASSERT(ySize.height % divisors.height == 0);
+    const gfx::IntSize uvSize(ySize.width / divisors.width,
+                              ySize.height / divisors.height);
+
+    const bool yFlip = destOrigin != srcOrigin;
+    const DrawBlitProg::BaseArgs baseArgs = { destSize, yFlip, clipRect, ySize };
+    const DrawBlitProg::YUVArgs yuvArgs = { uvSize, divisors, colorSpace };
+
+    const auto& prog = GetDrawBlitProg(DrawBlitType::TexExtNV12);
+    MOZ_RELEASE_ASSERT(prog);
+    prog->Draw(baseArgs, &yuvArgs);
+    return true;
+}
+
+// --
+
+bool
+GLBlitHelper::BlitAngleYCbCr(const WindowsHandle (&handleList)[3],
+                             const gfx::IntRect& clipRect,
+                             const gfx::IntSize& ySize, const gfx::IntSize& uvSize,
+                             const YUVColorSpace colorSpace,
+                             const gfx::IntSize& destSize, OriginPos destOrigin) const
+{
+    const auto& d3d = GetD3D11();
+    if (!d3d)
+        return false;
+
+    const auto srcOrigin = OriginPos::BottomLeft;
+
+    gfx::IntSize divisors;
+    if (!GuessDivisors(ySize, uvSize, &divisors))
+        return false;
+
+    const RefPtr<ID3D11Texture2D> texList[3] = {
+        OpenSharedTexture(d3d, handleList[0]),
+        OpenSharedTexture(d3d, handleList[1]),
+        OpenSharedTexture(d3d, handleList[2])
+    };
+    const BindAnglePlanes bindPlanes(this, 3, texList);
+
+    const bool yFlip = destOrigin != srcOrigin;
+    const DrawBlitProg::BaseArgs baseArgs = { destSize, yFlip, clipRect, ySize };
+    const DrawBlitProg::YUVArgs yuvArgs = { uvSize, divisors, colorSpace };
+
+    const auto& prog = GetDrawBlitProg(DrawBlitType::TexExtPlanarYUV);
+    MOZ_RELEASE_ASSERT(prog);
+    prog->Draw(baseArgs, &yuvArgs);
+    return true;
+}
+
+} // namespace gl
+} // namespace mozilla
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -1412,16 +1412,32 @@ public:
 
     realGLboolean fIsEnabled(GLenum capability) {
         BEFORE_GL_CALL;
         realGLboolean retval = mSymbols.fIsEnabled(capability);
         AFTER_GL_CALL;
         return retval;
     }
 
+    void SetEnabled(const GLenum cap, const bool val) {
+        if (val) {
+            fEnable(cap);
+        } else {
+            fDisable(cap);
+        }
+    }
+
+    bool PushEnabled(const GLenum cap, const bool newVal) {
+        const bool oldVal = fIsEnabled(cap);
+        if (oldVal != newVal) {
+            SetEnabled(cap, newVal);
+        }
+        return oldVal;
+    }
+
     realGLboolean fIsProgram(GLuint program) {
         BEFORE_GL_CALL;
         realGLboolean retval = mSymbols.fIsProgram(program);
         AFTER_GL_CALL;
         return retval;
     }
 
     realGLboolean fIsShader(GLuint shader) {
--- a/gfx/gl/GLContextEGL.h
+++ b/gfx/gl/GLContextEGL.h
@@ -109,17 +109,17 @@ public:
                                      const SurfaceCaps& minCaps,
                                      nsACString* const out_FailureId);
 
 protected:
     friend class GLContextProviderEGL;
     friend class GLContextEGLFactory;
 
 public:
-    const EGLConfig  mConfig;
+    const EGLConfig mConfig;
 protected:
     EGLSurface mSurface;
 public:
     const EGLContext mContext;
 protected:
     EGLSurface mSurfaceOverride;
     RefPtr<gfxASurface> mThebesSurface;
     bool mBound;
--- a/gfx/gl/GLLibraryEGL.cpp
+++ b/gfx/gl/GLLibraryEGL.cpp
@@ -605,16 +605,17 @@ GLLibraryEGL::EnsureInitialized(bool for
         if (!fnLoadSymbols(streamSymbols)) {
             NS_ERROR("EGL supports KHR_stream without exposing its functions!");
             MarkExtensionUnsupported(KHR_stream);
         }
     }
 
     if (IsExtensionSupported(KHR_stream_consumer_gltexture)) {
         const GLLibraryLoader::SymLoadStruct streamConsumerSymbols[] = {
+            SYMBOL(StreamConsumerGLTextureExternalKHR),
             SYMBOL(StreamConsumerAcquireKHR),
             SYMBOL(StreamConsumerReleaseKHR),
             END_OF_SYMBOLS
         };
         if (!fnLoadSymbols(streamConsumerSymbols)) {
             NS_ERROR("EGL supports KHR_stream_consumer_gltexture without exposing its functions!");
             MarkExtensionUnsupported(KHR_stream_consumer_gltexture);
         }
--- a/gfx/gl/GLLibraryEGL.h
+++ b/gfx/gl/GLLibraryEGL.h
@@ -295,16 +295,19 @@ public:
 
     EGLBoolean  fDestroyStreamKHR(EGLDisplay dpy, EGLStreamKHR stream) const
         WRAP(   fDestroyStreamKHR(dpy, stream) )
 
     EGLBoolean  fQueryStreamKHR(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, EGLint* value) const
         WRAP(   fQueryStreamKHR(dpy, stream, attribute, value) )
 
     // KHR_stream_consumer_gltexture
+    EGLBoolean  fStreamConsumerGLTextureExternalKHR(EGLDisplay dpy, EGLStreamKHR stream) const
+        WRAP(   fStreamConsumerGLTextureExternalKHR(dpy, stream) )
+
     EGLBoolean  fStreamConsumerAcquireKHR(EGLDisplay dpy, EGLStreamKHR stream) const
         WRAP(   fStreamConsumerAcquireKHR(dpy, stream) )
 
     EGLBoolean  fStreamConsumerReleaseKHR(EGLDisplay dpy, EGLStreamKHR stream) const
         WRAP(   fStreamConsumerReleaseKHR(dpy, stream) )
 
     // EXT_device_query
     EGLBoolean  fQueryDisplayAttribEXT(EGLDisplay dpy, EGLint attribute, EGLAttrib* value) const
@@ -462,16 +465,18 @@ private:
         //KHR_stream
         EGLStreamKHR (GLAPIENTRY * fCreateStreamKHR)(EGLDisplay dpy, const EGLint* attrib_list);
         EGLBoolean (GLAPIENTRY * fDestroyStreamKHR)(EGLDisplay dpy, EGLStreamKHR stream);
         EGLBoolean (GLAPIENTRY * fQueryStreamKHR)(EGLDisplay dpy,
                                                   EGLStreamKHR stream,
                                                   EGLenum attribute,
                                                   EGLint* value);
         // KHR_stream_consumer_gltexture
+        EGLBoolean (GLAPIENTRY * fStreamConsumerGLTextureExternalKHR)(EGLDisplay dpy,
+                                                                      EGLStreamKHR stream);
         EGLBoolean (GLAPIENTRY * fStreamConsumerAcquireKHR)(EGLDisplay dpy,
                                                             EGLStreamKHR stream);
         EGLBoolean (GLAPIENTRY * fStreamConsumerReleaseKHR)(EGLDisplay dpy,
                                                             EGLStreamKHR stream);
         // EXT_device_query
         EGLBoolean (GLAPIENTRY * fQueryDisplayAttribEXT)(EGLDisplay dpy,
                                                          EGLint attribute,
                                                          EGLAttrib* value);
--- a/gfx/gl/HeapCopyOfStackArray.h
+++ b/gfx/gl/HeapCopyOfStackArray.h
@@ -18,17 +18,17 @@ namespace mozilla {
 // Useful to retain the convenience of declaring static arrays, while
 // avoiding passing stack pointers to the GL (see bug 1005658).
 
 template <typename ElemType>
 class HeapCopyOfStackArray
 {
 public:
   template<size_t N>
-  MOZ_IMPLICIT HeapCopyOfStackArray(ElemType (&array)[N])
+  MOZ_IMPLICIT HeapCopyOfStackArray(const ElemType (&array)[N])
     : mArrayLength(N)
     , mArrayData(MakeUnique<ElemType[]>(N))
   {
     memcpy(mArrayData.get(), &array[0], N * sizeof(ElemType));
   }
 
   ElemType* Data() const { return mArrayData.get(); }
   size_t ArrayLength() const { return mArrayLength; }
--- a/gfx/gl/ScopedGLHelpers.cpp
+++ b/gfx/gl/ScopedGLHelpers.cpp
@@ -418,85 +418,16 @@ ScopedVertexAttribPointer::UnwrapImpl()
     mGL->fVertexAttribPointer(mAttribIndex, mAttribSize, mAttribType, mAttribNormalized, mAttribStride, mAttribPointer);
     if (mAttribEnabled)
         mGL->fEnableVertexAttribArray(mAttribIndex);
     else
         mGL->fDisableVertexAttribArray(mAttribIndex);
     mGL->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mBoundBuffer);
 }
 
-ScopedGLDrawState::ScopedGLDrawState(GLContext* aGL)
-    : blend       (aGL, LOCAL_GL_BLEND,      false)
-    , cullFace    (aGL, LOCAL_GL_CULL_FACE,  false)
-    , depthTest   (aGL, LOCAL_GL_DEPTH_TEST, false)
-    , dither      (aGL, LOCAL_GL_DITHER,     false)
-    , polyOffsFill(aGL, LOCAL_GL_POLYGON_OFFSET_FILL,      false)
-    , sampleAToC  (aGL, LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE, false)
-    , sampleCover (aGL, LOCAL_GL_SAMPLE_COVERAGE, false)
-    , scissor     (aGL, LOCAL_GL_SCISSOR_TEST,    false)
-    , stencil     (aGL, LOCAL_GL_STENCIL_TEST,    false)
-    , mGL(aGL)
-{
-    mGL->GetUIntegerv(LOCAL_GL_CURRENT_PROGRAM, &boundProgram);
-    mGL->GetUIntegerv(LOCAL_GL_ARRAY_BUFFER_BINDING, &boundBuffer);
-    mGL->GetUIntegerv(LOCAL_GL_MAX_VERTEX_ATTRIBS, &maxAttrib);
-    attrib_enabled = MakeUnique<GLint[]>(maxAttrib);
-
-    for (GLuint i = 0; i < maxAttrib; i++) {
-        mGL->fGetVertexAttribiv(i, LOCAL_GL_VERTEX_ATTRIB_ARRAY_ENABLED, &attrib_enabled[i]);
-        mGL->fDisableVertexAttribArray(i);
-    }
-    // Only Attrib0's client side state affected
-    mGL->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_SIZE, &attrib0_size);
-    mGL->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_STRIDE, &attrib0_stride);
-    mGL->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_TYPE, &attrib0_type);
-    mGL->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &attrib0_normalized);
-    mGL->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &attrib0_bufferBinding);
-    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],
-                    colorMask[2],
-                    colorMask[3]);
-
-    for (unsigned int i = 0; i < maxAttrib; i++) {
-        if (attrib_enabled[i])
-            mGL->fEnableVertexAttribArray(i);
-        else
-            mGL->fDisableVertexAttribArray(i);
-    }
-
-
-    mGL->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, attrib0_bufferBinding);
-    mGL->fVertexAttribPointer(0,
-                              attrib0_size,
-                              attrib0_type,
-                              attrib0_normalized,
-                              attrib0_stride,
-                              attrib0_pointer);
-
-    mGL->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, boundBuffer);
-
-    mGL->fUseProgram(boundProgram);
-}
-
 ////////////////////////////////////////////////////////////////////////
 // ScopedPackState
 
 ScopedPackState::ScopedPackState(GLContext* gl)
     : ScopedGLWrapper<ScopedPackState>(gl)
 {
     mGL->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, &mAlignment);
 
--- a/gfx/gl/ScopedGLHelpers.h
+++ b/gfx/gl/ScopedGLHelpers.h
@@ -117,17 +117,17 @@ struct ScopedTexture
 {
     friend struct ScopedGLWrapper<ScopedTexture>;
 
 protected:
     GLuint mTexture;
 
 public:
     explicit ScopedTexture(GLContext* aGL);
-    GLuint Texture() { return mTexture; }
+    GLuint Texture() const { return mTexture; }
 
 protected:
     void UnwrapImpl();
 };
 
 
 struct ScopedFramebuffer
     : public ScopedGLWrapper<ScopedFramebuffer>
@@ -304,49 +304,16 @@ public:
                               GLsizei stride, GLuint buffer, const GLvoid* pointer);
     explicit ScopedVertexAttribPointer(GLContext* aGL, GLuint index);
 
 protected:
     void WrapImpl(GLuint index);
     void UnwrapImpl();
 };
 
-struct ScopedGLDrawState
-{
-    explicit ScopedGLDrawState(GLContext* gl);
-    ~ScopedGLDrawState();
-
-    GLuint boundProgram;
-    GLuint boundBuffer;
-
-    ScopedGLState blend;
-    ScopedGLState cullFace;
-    ScopedGLState depthTest;
-    ScopedGLState dither;
-    ScopedGLState polyOffsFill;
-    ScopedGLState sampleAToC;
-    ScopedGLState sampleCover;
-    ScopedGLState scissor;
-    ScopedGLState stencil;
-
-    GLuint maxAttrib;
-    UniquePtr<GLint[]> attrib_enabled;
-    GLint attrib0_size;
-    GLint attrib0_stride;
-    GLint attrib0_type;
-    GLint attrib0_normalized;
-    GLint attrib0_bufferBinding;
-    void* attrib0_pointer;
-
-    realGLboolean colorMask[4];
-    GLint viewport[4];
-    GLint scissorBox[4];
-    GLContext* const mGL;
-};
-
 struct ScopedPackState
     : public ScopedGLWrapper<ScopedPackState>
 {
     friend struct ScopedGLWrapper<ScopedPackState>;
 
 protected:
     GLint mAlignment;
 
--- a/gfx/gl/SharedSurface.cpp
+++ b/gfx/gl/SharedSurface.cpp
@@ -60,17 +60,19 @@ SharedSurface::ProdCopy(SharedSurface* s
             src->LockProd();
             srcNeedsUnlock = true;
         }
 
         if (dest->mAttachType == AttachmentType::GLTexture) {
             GLuint destTex = dest->ProdTexture();
             GLenum destTarget = dest->ProdTextureTarget();
 
-            gl->BlitHelper()->BlitFramebufferToTexture(0, destTex,
+            const ScopedBindFramebuffer bindFB(gl, 0);
+
+            gl->BlitHelper()->BlitFramebufferToTexture(destTex,
                                                        src->mSize,
                                                        dest->mSize,
                                                        destTarget,
                                                        true);
         } else if (dest->mAttachType == AttachmentType::GLRenderbuffer) {
             GLuint destRB = dest->ProdRenderbuffer();
             ScopedFramebufferForRenderbuffer destWrapper(gl, destRB);
 
@@ -105,17 +107,19 @@ SharedSurface::ProdCopy(SharedSurface* s
             dest->LockProd();
             destNeedsUnlock = true;
         }
 
         if (src->mAttachType == AttachmentType::GLTexture) {
             GLuint srcTex = src->ProdTexture();
             GLenum srcTarget = src->ProdTextureTarget();
 
-            gl->BlitHelper()->BlitTextureToFramebuffer(srcTex, 0,
+            const ScopedBindFramebuffer bindFB(gl, 0);
+
+            gl->BlitHelper()->BlitTextureToFramebuffer(srcTex,
                                                        src->mSize,
                                                        dest->mSize,
                                                        srcTarget,
                                                        !!gl->Screen());
         } else if (src->mAttachType == AttachmentType::GLRenderbuffer) {
             GLuint srcRB = src->ProdRenderbuffer();
             ScopedFramebufferForRenderbuffer srcWrapper(gl, srcRB);
 
@@ -153,36 +157,37 @@ SharedSurface::ProdCopy(SharedSurface* s
                                                    srcTarget, destTarget);
 
             return;
         }
 
         if (dest->mAttachType == AttachmentType::GLRenderbuffer) {
             GLuint destRB = dest->ProdRenderbuffer();
             ScopedFramebufferForRenderbuffer destWrapper(gl, destRB);
-
-            gl->BlitHelper()->BlitTextureToFramebuffer(srcTex, destWrapper.FB(),
-                                                       src->mSize, dest->mSize, srcTarget);
+            const ScopedBindFramebuffer bindFB(gl, destWrapper.FB());
+            gl->BlitHelper()->BlitTextureToFramebuffer(srcTex, src->mSize, dest->mSize,
+                                                       srcTarget);
 
             return;
         }
 
         MOZ_CRASH("GFX: Unhandled dest->mAttachType 3.");
     }
 
     if (src->mAttachType == AttachmentType::GLRenderbuffer) {
         GLuint srcRB = src->ProdRenderbuffer();
         ScopedFramebufferForRenderbuffer srcWrapper(gl, srcRB);
 
         if (dest->mAttachType == AttachmentType::GLTexture) {
             GLuint destTex = dest->ProdTexture();
             GLenum destTarget = dest->ProdTextureTarget();
+            const ScopedBindFramebuffer bindFB(gl, srcWrapper.FB());
 
-            gl->BlitHelper()->BlitFramebufferToTexture(srcWrapper.FB(), destTex,
-                                                       src->mSize, dest->mSize, destTarget);
+            gl->BlitHelper()->BlitFramebufferToTexture(destTex, src->mSize, dest->mSize,
+                                                       destTarget);
 
             return;
         }
 
         if (dest->mAttachType == AttachmentType::GLRenderbuffer) {
             GLuint destRB = dest->ProdRenderbuffer();
             ScopedFramebufferForRenderbuffer destWrapper(gl, destRB);
 
--- a/gfx/gl/SharedSurfaceD3D11Interop.cpp
+++ b/gfx/gl/SharedSurfaceD3D11Interop.cpp
@@ -466,18 +466,18 @@ SharedSurface_D3D11Interop::ProducerAcqu
 }
 
 void
 SharedSurface_D3D11Interop::ProducerReleaseImpl()
 {
     MOZ_ASSERT(mLockedForGL);
 
     if (mProdTex) {
-        mGL->BlitHelper()->DrawBlitTextureToFramebuffer(mProdTex, mInteropFB, mSize,
-                                                        mSize);
+        const ScopedBindFramebuffer bindFB(mGL, mInteropFB);
+        mGL->BlitHelper()->DrawBlitTextureToFramebuffer(mProdTex, mSize, mSize);
     }
 
     if (mNeedsFinish) {
         mGL->fFinish();
     } else {
         // We probably don't even need this.
         mGL->fFlush();
     }
--- a/gfx/gl/moz.build
+++ b/gfx/gl/moz.build
@@ -64,16 +64,17 @@ if CONFIG['MOZ_X11']:
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     EXPORTS += [
         'GLContextWGL.h',
         'SharedSurfaceANGLE.h', # Needs <windows.h> for `HANDLE`.
         'SharedSurfaceD3D11Interop.h',
         'WGLLibrary.h',
     ]
     UNIFIED_SOURCES += [
+        'GLBlitHelperD3D.cpp',
         'GLContextProviderWGL.cpp',
         'SharedSurfaceANGLE.cpp',
         'SharedSurfaceD3D11Interop.cpp',
     ]
 if CONFIG['MOZ_ENABLE_SKIA_GPU']:
     EXPORTS += ['SkiaGLGlue.h']
     SOURCES += [
         'SkiaGLGlue.cpp',
--- a/gfx/layers/D3D11YCbCrImage.cpp
+++ b/gfx/layers/D3D11YCbCrImage.cpp
@@ -92,32 +92,41 @@ D3D11YCbCrImage::SetData(KnowsCompositor
                          aData.mCbCrStride * aData.mCbCrSize.height);
   ctx->UpdateSubresource(textureCr,
                          0,
                          nullptr,
                          aData.mCrChannel,
                          aData.mCbCrStride,
                          aData.mCbCrStride * aData.mCbCrSize.height);
 
-  
+
   return true;
 }
 
 IntSize
 D3D11YCbCrImage::GetSize()
 {
   return mPictureRect.Size();
 }
 
 TextureClient*
 D3D11YCbCrImage::GetTextureClient(KnowsCompositor* aForwarder)
 {
   return mTextureClient;
 }
 
+const DXGIYCbCrTextureData*
+D3D11YCbCrImage::GetData() const
+{
+  if (!mTextureClient)
+    return nullptr;
+
+  return static_cast<DXGIYCbCrTextureData*>(mTextureClient->GetInternalData());
+}
+
 already_AddRefed<SourceSurface>
 D3D11YCbCrImage::GetAsSourceSurface()
 {
   if (!mTextureClient) {
     gfxWarning()
       << "GetAsSourceSurface() called on uninitialized D3D11YCbCrImage.";
     return nullptr;
   }
--- a/gfx/layers/D3D11YCbCrImage.h
+++ b/gfx/layers/D3D11YCbCrImage.h
@@ -7,20 +7,24 @@
 #define GFX_D3D11_YCBCR_IMAGE_H
 
 #include "d3d11.h"
 #include "mozilla/layers/TextureClientRecycleAllocator.h"
 #include "mozilla/Maybe.h"
 #include "ImageContainer.h"
 
 namespace mozilla {
+namespace gl {
+class GLBlitHelper;
+}
 namespace layers {
 
 class ImageContainer;
 class DXGIYCbCrTextureClient;
+class DXGIYCbCrTextureData;
 
 class D3D11YCbCrRecycleAllocator : public TextureClientRecycleAllocator
 {
 public:
   explicit D3D11YCbCrRecycleAllocator(KnowsCompositor* aAllocator,
                                       ID3D11Device* aDevice)
     : TextureClientRecycleAllocator(aAllocator)
     , mDevice(aDevice)
@@ -41,16 +45,17 @@ protected:
 
   RefPtr<ID3D11Device> mDevice;
   Maybe<gfx::IntSize> mYSize;
   Maybe<gfx::IntSize> mCbCrSize;
 };
 
 class D3D11YCbCrImage : public Image
 {
+  friend class gl::GLBlitHelper;
 public:
   D3D11YCbCrImage();
   virtual ~D3D11YCbCrImage();
 
   // Copies the surface into a sharable texture's surface, and initializes
   // the image.
   bool SetData(KnowsCompositor* aAllocator,
                ImageContainer* aContainer,
@@ -60,16 +65,18 @@ public:
 
   already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
 
   TextureClient* GetTextureClient(KnowsCompositor* aForwarder) override;
 
   gfx::IntRect GetPictureRect() override { return mPictureRect; }
 
 private:
+  const DXGIYCbCrTextureData* GetData() const;
+
   gfx::IntSize mYSize;
   gfx::IntSize mCbCrSize;
   gfx::IntRect mPictureRect;
   YUVColorSpace mColorSpace;
   RefPtr<TextureClient> mTextureClient;
 };
 
 } // namepace layers
--- a/gfx/layers/GLImages.cpp
+++ b/gfx/layers/GLImages.cpp
@@ -73,22 +73,21 @@ GLImage::GetAsSourceSurface()
 
   ScopedFramebufferForTexture autoFBForTex(sSnapshotContext, scopedTex.Texture());
   if (!autoFBForTex.IsComplete()) {
       gfxCriticalError() << "GetAsSourceSurface: ScopedFramebufferForTexture failed.";
       return nullptr;
   }
 
   const gl::OriginPos destOrigin = gl::OriginPos::TopLeft;
-
-  if (!sSnapshotContext->BlitHelper()->BlitImageToFramebuffer(this, size,
-                                                              autoFBForTex.FB(),
-                                                              destOrigin))
   {
-    return nullptr;
+    const ScopedBindFramebuffer bindFB(sSnapshotContext, autoFBForTex.FB());
+    if (!sSnapshotContext->BlitHelper()->BlitImageToFramebuffer(this, size, destOrigin)) {
+      return nullptr;
+    }
   }
 
   RefPtr<gfx::DataSourceSurface> source =
         gfx::Factory::CreateDataSourceSurface(size, gfx::SurfaceFormat::B8G8R8A8);
   if (NS_WARN_IF(!source)) {
     return nullptr;
   }
 
--- a/gfx/layers/GPUVideoImage.h
+++ b/gfx/layers/GPUVideoImage.h
@@ -11,21 +11,25 @@
 #include "mozilla/layers/GPUVideoTextureClient.h"
 #include "mozilla/layers/CompositableClient.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 
 namespace mozilla {
 namespace dom {
 class VideoDecoderManagerChild;
 }
+namespace gl {
+class GLBlitHelper;
+}
 namespace layers {
 
 // Image class that refers to a decoded video frame within
 // the GPU process.
 class GPUVideoImage final : public Image {
+  friend class gl::GLBlitHelper;
 public:
   GPUVideoImage(dom::VideoDecoderManagerChild* aManager,
                 const SurfaceDescriptorGPUVideo& aSD,
                 const gfx::IntSize& aSize)
     : Image(nullptr, ImageFormat::GPU_VIDEO)
     , mSize(aSize)
   {
     // Create the TextureClient immediately since the GPUVideoTextureData
@@ -40,22 +44,31 @@ public:
                                     TextureFlags::RECYCLE,
                                     ImageBridgeChild::GetSingleton().get());
   }
 
   ~GPUVideoImage() override {}
 
   gfx::IntSize GetSize() override { return mSize; }
 
-  virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override
-  {
+private:
+  GPUVideoTextureData* GetData() const {
     if (!mTextureClient) {
       return nullptr;
     }
-    GPUVideoTextureData* data = mTextureClient->GetInternalData()->AsGPUVideoTextureData();
+    return mTextureClient->GetInternalData()->AsGPUVideoTextureData();
+  }
+
+public:
+  virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override
+  {
+    GPUVideoTextureData* data = GetData();
+    if (!data) {
+      return nullptr;
+    }
     return data->GetAsSourceSurface();
   }
 
   virtual TextureClient* GetTextureClient(KnowsCompositor* aForwarder) override
   {
     MOZ_ASSERT(aForwarder == ImageBridgeChild::GetSingleton(), "Must only use GPUVideo on ImageBridge");
     return mTextureClient;
   }
--- a/gfx/layers/client/GPUVideoTextureClient.h
+++ b/gfx/layers/client/GPUVideoTextureClient.h
@@ -43,14 +43,17 @@ public:
   {
     return this;
   }
 
 protected:
   RefPtr<dom::VideoDecoderManagerChild> mManager;
   SurfaceDescriptorGPUVideo mSD;
   gfx::IntSize mSize;
+
+public:
+  const decltype(mSD)& SD() const { return mSD; }
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // MOZILLA_GFX_GPUVIDEOTEXTURECLIENT_H
--- a/gfx/layers/d3d11/TextureD3D11.h
+++ b/gfx/layers/d3d11/TextureD3D11.h
@@ -13,16 +13,20 @@
 #include "mozilla/layers/TextureHost.h"
 #include "gfxWindowsPlatform.h"
 #include "mozilla/GfxMessageUtils.h"
 #include <d3d11.h>
 #include "d3d9.h"
 #include <vector>
 
 namespace mozilla {
+namespace gl {
+class GLBlitHelper;
+}
+
 namespace layers {
 
 class MOZ_RAII AutoTextureLock
 {
 public:
   AutoTextureLock(IDXGIKeyedMutex* aMutex, HRESULT& aResult,
                   uint32_t aTimeout = 0);
   ~AutoTextureLock();
@@ -123,16 +127,17 @@ protected:
 already_AddRefed<TextureClient>
 CreateD3D11extureClientWithDevice(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
                                   TextureFlags aTextureFlags, TextureAllocationFlags aAllocFlags,
                                   ID3D11Device* aDevice,
                                   LayersIPCChannel* aAllocator);
 
 class DXGIYCbCrTextureData : public TextureData
 {
+  friend class gl::GLBlitHelper;
 public:
   static DXGIYCbCrTextureData*
   Create(IDirect3DTexture9* aTextureY,
          IDirect3DTexture9* aTextureCb,
          IDirect3DTexture9* aTextureCr,
          HANDLE aHandleY,
          HANDLE aHandleCb,
          HANDLE aHandleCr,
--- a/gfx/layers/ipc/LayersSurfaces.ipdlh
+++ b/gfx/layers/ipc/LayersSurfaces.ipdlh
@@ -84,17 +84,17 @@ struct SurfaceDescriptorSharedGLTexture 
 union GPUVideoSubDescriptor {
   SurfaceDescriptorD3D10;
   SurfaceDescriptorDXGIYCbCr;
   null_t;
 };
 
 struct SurfaceDescriptorGPUVideo {
   uint64_t handle;
-  GPUVideoSubDescriptor desc;
+  GPUVideoSubDescriptor subdesc;
 };
 
 struct RGBDescriptor {
   IntSize size;
   SurfaceFormat format;
   bool hasIntermediateBuffer;
 };
 
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -1106,57 +1106,39 @@ gfxUtils::EncodeSourceSurface(SourceSurf
                               const nsAString& aOutputOptions,
                               BinaryOrData aBinaryOrData,
                               FILE* aFile)
 {
   return EncodeSourceSurfaceInternal(aSurface, aMimeType, aOutputOptions,
                                      aBinaryOrData, aFile, nullptr);
 }
 
-/* From Rec601:
-[R]   [1.1643835616438356,  0.0,                 1.5960267857142858]      [ Y -  16]
-[G] = [1.1643835616438358, -0.3917622900949137, -0.8129676472377708]    x [Cb - 128]
-[B]   [1.1643835616438356,  2.017232142857143,   8.862867620416422e-17]   [Cr - 128]
-
-For [0,1] instead of [0,255], and to 5 places:
-[R]   [1.16438,  0.00000,  1.59603]   [ Y - 0.06275]
-[G] = [1.16438, -0.39176, -0.81297] x [Cb - 0.50196]
-[B]   [1.16438,  2.01723,  0.00000]   [Cr - 0.50196]
-
-From Rec709:
-[R]   [1.1643835616438356,  4.2781193979771426e-17, 1.7927410714285714]     [ Y -  16]
-[G] = [1.1643835616438358, -0.21324861427372963,   -0.532909328559444]    x [Cb - 128]
-[B]   [1.1643835616438356,  2.1124017857142854,     0.0]                    [Cr - 128]
-
-For [0,1] instead of [0,255], and to 5 places:
-[R]   [1.16438,  0.00000,  1.79274]   [ Y - 0.06275]
-[G] = [1.16438, -0.21325, -0.53291] x [Cb - 0.50196]
-[B]   [1.16438,  2.11240,  0.00000]   [Cr - 0.50196]
-*/
-
-static const float kRec601[9] = {
-  1.16438f, 0.00000f, 1.59603f,
-  1.16438f,-0.39176f,-0.81297f,
-  1.16438f, 2.01723f, 0.00000f,
+// https://jdashg.github.io/misc/colors/from-coeffs.html
+const float kBT601NarrowYCbCrToRGB_RowMajor[16] = {
+  1.16438f, 0.00000f, 1.59603f,-0.87420f,
+  1.16438f,-0.39176f,-0.81297f, 0.53167f,
+  1.16438f, 2.01723f, 0.00000f,-1.08563f,
+  0.00000f, 0.00000f, 0.00000f, 1.00000f
 };
-static const float kRec709[9] = {
-  1.16438f, 0.00000f, 1.79274f,
-  1.16438f,-0.21325f,-0.53291f,
-  1.16438f, 2.11240f, 0.00000f,
+const float kBT709NarrowYCbCrToRGB_RowMajor[16] = {
+  1.16438f, 0.00000f, 1.79274f,-0.97295f,
+  1.16438f,-0.21325f,-0.53291f, 0.30148f,
+  1.16438f, 2.11240f, 0.00000f,-1.13340f,
+  0.00000f, 0.00000f, 0.00000f, 1.00000f
 };
 
 /* static */ const float*
 gfxUtils::YuvToRgbMatrix4x3RowMajor(YUVColorSpace aYUVColorSpace)
 {
-  #define X(x) { x[0], x[1], x[2], 0.0f, \
-                 x[3], x[4], x[5], 0.0f, \
-                 x[6], x[7], x[8], 0.0f }
+  #define X(x) { x[0], x[1], x[ 2], 0.0f, \
+                 x[4], x[5], x[ 6], 0.0f, \
+                 x[8], x[9], x[10], 0.0f }
 
-  static const float rec601[12] = X(kRec601);
-  static const float rec709[12] = X(kRec709);
+  static const float rec601[12] = X(kBT601NarrowYCbCrToRGB_RowMajor);
+  static const float rec709[12] = X(kBT709NarrowYCbCrToRGB_RowMajor);
 
   #undef X
 
   switch (aYUVColorSpace) {
   case YUVColorSpace::BT601:
     return rec601;
   case YUVColorSpace::BT709:
     return rec709;
@@ -1164,22 +1146,46 @@ gfxUtils::YuvToRgbMatrix4x3RowMajor(YUVC
     MOZ_ASSERT(false, "unknown aYUVColorSpace");
     return rec601;
   }
 }
 
 /* static */ const float*
 gfxUtils::YuvToRgbMatrix3x3ColumnMajor(YUVColorSpace aYUVColorSpace)
 {
-  #define X(x) { x[0], x[3], x[6], \
-                 x[1], x[4], x[7], \
-                 x[2], x[5], x[8] }
+  #define X(x) { x[0], x[4], x[ 8], \
+                 x[1], x[5], x[ 9], \
+                 x[2], x[6], x[10] }
+
+  static const float rec601[9] = X(kBT601NarrowYCbCrToRGB_RowMajor);
+  static const float rec709[9] = X(kBT709NarrowYCbCrToRGB_RowMajor);
+
+  #undef X
 
-  static const float rec601[9] = X(kRec601);
-  static const float rec709[9] = X(kRec709);
+  switch (aYUVColorSpace) {
+  case YUVColorSpace::BT601:
+    return rec601;
+  case YUVColorSpace::BT709:
+    return rec709;
+  default: // YUVColorSpace::UNKNOWN
+    MOZ_ASSERT(false, "unknown aYUVColorSpace");
+    return rec601;
+  }
+}
+
+/* static */ const float*
+gfxUtils::YuvToRgbMatrix4x4ColumnMajor(YUVColorSpace aYUVColorSpace)
+{
+  #define X(x) { x[0], x[4], x[ 8], x[12], \
+                 x[1], x[5], x[ 9], x[13], \
+                 x[2], x[6], x[10], x[14], \
+                 x[3], x[7], x[11], x[15] }
+
+  static const float rec601[16] = X(kBT601NarrowYCbCrToRGB_RowMajor);
+  static const float rec709[16] = X(kBT709NarrowYCbCrToRGB_RowMajor);
 
   #undef X
 
   switch (aYUVColorSpace) {
   case YUVColorSpace::BT601:
     return rec601;
   case YUVColorSpace::BT709:
     return rec709;
--- a/gfx/thebes/gfxUtils.h
+++ b/gfx/thebes/gfxUtils.h
@@ -153,16 +153,17 @@ public:
 
     /**
      * Clears surface to aColor (which defaults to transparent black).
      */
     static void ClearThebesSurface(gfxASurface* aSurface);
 
     static const float* YuvToRgbMatrix4x3RowMajor(mozilla::YUVColorSpace aYUVColorSpace);
     static const float* YuvToRgbMatrix3x3ColumnMajor(mozilla::YUVColorSpace aYUVColorSpace);
+    static const float* YuvToRgbMatrix4x4ColumnMajor(mozilla::YUVColorSpace aYUVColorSpace);
 
     /**
      * Creates a copy of aSurface, but having the SurfaceFormat aFormat.
      *
      * This function always creates a new surface. Do not call it if aSurface's
      * format is the same as aFormat. Such a non-conversion would just be an
      * unnecessary and wasteful copy (this function asserts to prevent that).
      *