Bug 942503 - Move BlitTextureImage out of GLContext - r=jgilbert
authorBenoit Jacob <bjacob@mozilla.com>
Thu, 28 Nov 2013 15:57:19 -0500
changeset 157996 1b0fc4fd9a75dafdbae4344ca847a24abbb6a01a
parent 157995 843b0a943af7de85d990d831d09788fc9fedefd4
child 157997 129bf6f1933cb4fa535b1dfd106cd731f9cf1e99
push id36900
push userbjacob@mozilla.com
push dateThu, 28 Nov 2013 20:57:35 +0000
treeherdermozilla-inbound@129bf6f1933c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjgilbert
bugs942503
milestone28.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 942503 - Move BlitTextureImage out of GLContext - r=jgilbert
gfx/gl/GLBlitTextureImageHelper.cpp
gfx/gl/GLBlitTextureImageHelper.h
gfx/gl/GLContext.cpp
gfx/gl/GLContext.h
gfx/gl/moz.build
gfx/layers/opengl/TextureHostOGL.cpp
new file mode 100644
--- /dev/null
+++ b/gfx/gl/GLBlitTextureImageHelper.cpp
@@ -0,0 +1,281 @@
+/* -*- 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 "GLBlitTextureImageHelper.h"
+#include "GLContext.h"
+#include "nsRect.h"
+#include "gfx2DGlue.h"
+#include "gfxUtils.h"
+
+namespace mozilla {
+namespace gl {
+
+GLBlitTextureImageHelper::GLBlitTextureImageHelper(GLContext* gl)
+    : mGL(gl)
+    , mBlitProgram(0)
+    , mBlitFramebuffer(0)
+
+{
+}
+
+GLBlitTextureImageHelper::~GLBlitTextureImageHelper()
+{
+    // Likely used by OGL Layers.
+    mGL->fDeleteProgram(mBlitProgram);
+    mGL->fDeleteFramebuffers(1, &mBlitFramebuffer);
+}
+
+void
+GLBlitTextureImageHelper::BlitTextureImage(TextureImage *aSrc, const nsIntRect& aSrcRect,
+                                           TextureImage *aDst, const nsIntRect& aDstRect)
+{
+    NS_ASSERTION(!aSrc->InUpdate(), "Source texture is in update!");
+    NS_ASSERTION(!aDst->InUpdate(), "Destination texture is in update!");
+
+    if (aSrcRect.IsEmpty() || aDstRect.IsEmpty())
+        return;
+
+    int savedFb = 0;
+    mGL->fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, &savedFb);
+
+    mGL->fDisable(LOCAL_GL_SCISSOR_TEST);
+    mGL->fDisable(LOCAL_GL_BLEND);
+
+    // 2.0 means scale up by two
+    float blitScaleX = float(aDstRect.width) / float(aSrcRect.width);
+    float blitScaleY = float(aDstRect.height) / float(aSrcRect.height);
+
+    // We start iterating over all destination tiles
+    aDst->BeginTileIteration();
+    do {
+        // calculate portion of the tile that is going to be painted to
+        nsIntRect dstSubRect;
+        nsIntRect dstTextureRect = ThebesIntRect(aDst->GetTileRect());
+        dstSubRect.IntersectRect(aDstRect, dstTextureRect);
+
+        // this tile is not part of the destination rectangle aDstRect
+        if (dstSubRect.IsEmpty())
+            continue;
+
+        // (*) transform the rect of this tile into the rectangle defined by aSrcRect...
+        nsIntRect dstInSrcRect(dstSubRect);
+        dstInSrcRect.MoveBy(-aDstRect.TopLeft());
+        // ...which might be of different size, hence scale accordingly
+        dstInSrcRect.ScaleRoundOut(1.0f / blitScaleX, 1.0f / blitScaleY);
+        dstInSrcRect.MoveBy(aSrcRect.TopLeft());
+
+        SetBlitFramebufferForDestTexture(aDst->GetTextureID());
+        UseBlitProgram();
+
+        aSrc->BeginTileIteration();
+        // now iterate over all tiles in the source Image...
+        do {
+            // calculate portion of the source tile that is in the source rect
+            nsIntRect srcSubRect;
+            nsIntRect srcTextureRect = ThebesIntRect(aSrc->GetTileRect());
+            srcSubRect.IntersectRect(aSrcRect, srcTextureRect);
+
+            // this tile is not part of the source rect
+            if (srcSubRect.IsEmpty()) {
+                continue;
+            }
+            // calculate intersection of source rect with destination rect
+            srcSubRect.IntersectRect(srcSubRect, dstInSrcRect);
+            // this tile does not overlap the current destination tile
+            if (srcSubRect.IsEmpty()) {
+                continue;
+            }
+            // We now have the intersection of
+            //     the current source tile
+            // and the desired source rectangle
+            // and the destination tile
+            // and the desired destination rectange
+            // in destination space.
+            // We need to transform this back into destination space, inverting the transform from (*)
+            nsIntRect srcSubInDstRect(srcSubRect);
+            srcSubInDstRect.MoveBy(-aSrcRect.TopLeft());
+            srcSubInDstRect.ScaleRoundOut(blitScaleX, blitScaleY);
+            srcSubInDstRect.MoveBy(aDstRect.TopLeft());
+
+            // we transform these rectangles to be relative to the current src and dst tiles, respectively
+            nsIntSize srcSize = srcTextureRect.Size();
+            nsIntSize dstSize = dstTextureRect.Size();
+            srcSubRect.MoveBy(-srcTextureRect.x, -srcTextureRect.y);
+            srcSubInDstRect.MoveBy(-dstTextureRect.x, -dstTextureRect.y);
+
+            float dx0 = 2.0f * float(srcSubInDstRect.x) / float(dstSize.width) - 1.0f;
+            float dy0 = 2.0f * float(srcSubInDstRect.y) / float(dstSize.height) - 1.0f;
+            float dx1 = 2.0f * float(srcSubInDstRect.x + srcSubInDstRect.width) / float(dstSize.width) - 1.0f;
+            float dy1 = 2.0f * float(srcSubInDstRect.y + srcSubInDstRect.height) / float(dstSize.height) - 1.0f;
+            mGL->PushViewportRect(nsIntRect(0, 0, dstSize.width, dstSize.height));
+
+            RectTriangles rects;
+
+            nsIntSize realTexSize = srcSize;
+            if (!mGL->CanUploadNonPowerOfTwo()) {
+                realTexSize = nsIntSize(gfx::NextPowerOfTwo(srcSize.width),
+                                        gfx::NextPowerOfTwo(srcSize.height));
+            }
+
+            if (aSrc->GetWrapMode() == LOCAL_GL_REPEAT) {
+                rects.addRect(/* dest rectangle */
+                        dx0, dy0, dx1, dy1,
+                        /* tex coords */
+                        srcSubRect.x / float(realTexSize.width),
+                        srcSubRect.y / float(realTexSize.height),
+                        srcSubRect.XMost() / float(realTexSize.width),
+                        srcSubRect.YMost() / float(realTexSize.height));
+            } else {
+                DecomposeIntoNoRepeatTriangles(srcSubRect, realTexSize, rects);
+
+                // now put the coords into the d[xy]0 .. d[xy]1 coordinate space
+                // from the 0..1 that it comes out of decompose
+                RectTriangles::vert_coord* v = (RectTriangles::vert_coord*)rects.vertexPointer();
+
+                for (unsigned int i = 0; i < rects.elements(); ++i) {
+                    v[i].x = (v[i].x * (dx1 - dx0)) + dx0;
+                    v[i].y = (v[i].y * (dy1 - dy0)) + dy0;
+                }
+            }
+
+            TextureImage::ScopedBindTexture texBind(aSrc, LOCAL_GL_TEXTURE0);
+
+            mGL->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
+
+            mGL->fVertexAttribPointer(0, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, rects.vertexPointer());
+            mGL->fVertexAttribPointer(1, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, rects.texCoordPointer());
+
+            mGL->fEnableVertexAttribArray(0);
+            mGL->fEnableVertexAttribArray(1);
+
+            mGL->fDrawArrays(LOCAL_GL_TRIANGLES, 0, rects.elements());
+
+            mGL->fDisableVertexAttribArray(0);
+            mGL->fDisableVertexAttribArray(1);
+
+            mGL->PopViewportRect();
+        } while (aSrc->NextTile());
+    } while (aDst->NextTile());
+
+    mGL->fVertexAttribPointer(0, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, nullptr);
+    mGL->fVertexAttribPointer(1, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, nullptr);
+
+    // unbind the previous texture from the framebuffer
+    SetBlitFramebufferForDestTexture(0);
+
+    mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, savedFb);
+
+    mGL->fEnable(LOCAL_GL_SCISSOR_TEST);
+    mGL->fEnable(LOCAL_GL_BLEND);
+}
+
+void
+GLBlitTextureImageHelper::SetBlitFramebufferForDestTexture(GLuint aTexture)
+{
+    if (!mBlitFramebuffer) {
+        mGL->fGenFramebuffers(1, &mBlitFramebuffer);
+    }
+
+    mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mBlitFramebuffer);
+    mGL->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
+                               LOCAL_GL_COLOR_ATTACHMENT0,
+                               LOCAL_GL_TEXTURE_2D,
+                               aTexture,
+                               0);
+
+    GLenum result = mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
+    if (aTexture && (result != LOCAL_GL_FRAMEBUFFER_COMPLETE)) {
+        nsAutoCString msg;
+        msg.Append("Framebuffer not complete -- error 0x");
+        msg.AppendInt(result, 16);
+        // Note: if you are hitting this, it is likely that
+        // your texture is not texture complete -- that is, you
+        // allocated a texture name, but didn't actually define its
+        // size via a call to TexImage2D.
+        NS_RUNTIMEABORT(msg.get());
+    }
+}
+
+void
+GLBlitTextureImageHelper::UseBlitProgram()
+{
+    if (mBlitProgram) {
+        mGL->fUseProgram(mBlitProgram);
+        return;
+    }
+
+    mBlitProgram = mGL->fCreateProgram();
+
+    GLuint shaders[2];
+    shaders[0] = mGL->fCreateShader(LOCAL_GL_VERTEX_SHADER);
+    shaders[1] = mGL->fCreateShader(LOCAL_GL_FRAGMENT_SHADER);
+
+    const char *blitVSSrc =
+        "attribute vec2 aVertex;"
+        "attribute vec2 aTexCoord;"
+        "varying vec2 vTexCoord;"
+        "void main() {"
+        "  vTexCoord = aTexCoord;"
+        "  gl_Position = vec4(aVertex, 0.0, 1.0);"
+        "}";
+    const char *blitFSSrc = "#ifdef GL_ES\nprecision mediump float;\n#endif\n"
+        "uniform sampler2D uSrcTexture;"
+        "varying vec2 vTexCoord;"
+        "void main() {"
+        "  gl_FragColor = texture2D(uSrcTexture, vTexCoord);"
+        "}";
+
+    mGL->fShaderSource(shaders[0], 1, (const GLchar**) &blitVSSrc, nullptr);
+    mGL->fShaderSource(shaders[1], 1, (const GLchar**) &blitFSSrc, nullptr);
+
+    for (int i = 0; i < 2; ++i) {
+        GLint success, len = 0;
+
+        mGL->fCompileShader(shaders[i]);
+        mGL->fGetShaderiv(shaders[i], LOCAL_GL_COMPILE_STATUS, &success);
+        NS_ASSERTION(success, "Shader compilation failed!");
+
+        if (!success) {
+            nsAutoCString log;
+            mGL->fGetShaderiv(shaders[i], LOCAL_GL_INFO_LOG_LENGTH, (GLint*) &len);
+            log.SetCapacity(len);
+            mGL->fGetShaderInfoLog(shaders[i], len, (GLint*) &len, (char*) log.BeginWriting());
+            log.SetLength(len);
+
+            printf_stderr("Shader %d compilation failed:\n%s\n", log.get());
+            return;
+        }
+
+        mGL->fAttachShader(mBlitProgram, shaders[i]);
+        mGL->fDeleteShader(shaders[i]);
+    }
+
+    mGL->fBindAttribLocation(mBlitProgram, 0, "aVertex");
+    mGL->fBindAttribLocation(mBlitProgram, 1, "aTexCoord");
+
+    mGL->fLinkProgram(mBlitProgram);
+
+    GLint success, len = 0;
+    mGL->fGetProgramiv(mBlitProgram, LOCAL_GL_LINK_STATUS, &success);
+    NS_ASSERTION(success, "Shader linking failed!");
+
+    if (!success) {
+        nsAutoCString log;
+        mGL->fGetProgramiv(mBlitProgram, LOCAL_GL_INFO_LOG_LENGTH, (GLint*) &len);
+        log.SetCapacity(len);
+        mGL->fGetProgramInfoLog(mBlitProgram, len, (GLint*) &len, (char*) log.BeginWriting());
+        log.SetLength(len);
+
+        printf_stderr("Program linking failed:\n%s\n", log.get());
+        return;
+    }
+
+    mGL->fUseProgram(mBlitProgram);
+    mGL->fUniform1i(mGL->fGetUniformLocation(mBlitProgram, "uSrcTexture"), 0);
+}
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/gfx/gl/GLBlitTextureImageHelper.h
@@ -0,0 +1,69 @@
+/* -*- 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/. */
+
+#ifndef GLBLITTEXTUREIMAGEHELPER_H_
+#define GLBLITTEXTUREIMAGEHELPER_H_
+
+#include "mozilla/Attributes.h"
+#include "GLContextTypes.h"
+#include "GLConsts.h"
+
+struct nsIntRect;
+
+namespace mozilla {
+namespace gl {
+
+class GLContext;
+class TextureImage;
+
+class GLBlitTextureImageHelper MOZ_FINAL
+{
+    // The GLContext is the sole owner of the GLBlitTextureImageHelper.
+    GLContext* mGL;
+
+    // lazy-initialized things
+    GLuint mBlitProgram, mBlitFramebuffer;
+    void UseBlitProgram();
+    void SetBlitFramebufferForDestTexture(GLuint aTexture);
+
+public:
+
+    GLBlitTextureImageHelper(GLContext *gl);
+    ~GLBlitTextureImageHelper();
+
+    /**
+     * Copy a rectangle from one TextureImage into another.  The
+     * source and destination are given in integer coordinates, and
+     * will be converted to texture coordinates.
+     *
+     * For the source texture, the wrap modes DO apply -- it's valid
+     * to use REPEAT or PAD and expect appropriate behaviour if the source
+     * rectangle extends beyond its bounds.
+     *
+     * For the destination texture, the wrap modes DO NOT apply -- the
+     * destination will be clipped by the bounds of the texture.
+     *
+     * Note: calling this function will cause the following OpenGL state
+     * to be changed:
+     *
+     *   - current program
+     *   - framebuffer binding
+     *   - viewport
+     *   - blend state (will be enabled at end)
+     *   - scissor state (will be enabled at end)
+     *   - vertex attrib 0 and 1 (pointer and enable state [enable state will be disabled at exit])
+     *   - array buffer binding (will be 0)
+     *   - active texture (will be 0)
+     *   - texture 0 binding
+     */
+    void BlitTextureImage(TextureImage *aSrc, const nsIntRect& aSrcRect,
+                          TextureImage *aDst, const nsIntRect& aDstRect);
+};
+
+}
+}
+
+#endif // GLBLITTEXTUREIMAGEHELPER_H_
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -6,16 +6,17 @@
 
 #include <algorithm>
 #include <stdio.h>
 #include <string.h>
 #include <ctype.h>
 
 #include "GLContext.h"
 #include "GLBlitHelper.h"
+#include "GLBlitTextureImageHelper.h"
 
 #include "gfxCrashReporterUtils.h"
 #include "gfxPlatform.h"
 #include "gfxUtils.h"
 #include "GLContextProvider.h"
 #include "GLTextureImage.h"
 #include "nsPrintfCString.h"
 #include "nsThreadUtils.h"
@@ -255,18 +256,16 @@ GLContext::GLContext(const SurfaceCaps& 
     mVendor(-1),
     mRenderer(-1),
     mHasRobustness(false),
 #ifdef DEBUG
     mGLError(LOCAL_GL_NO_ERROR),
 #endif
     mSharedContext(sharedContext),
     mFlipped(false),
-    mBlitProgram(0),
-    mBlitFramebuffer(0),
     mCaps(caps),
     mScreen(nullptr),
     mLockedSurface(nullptr),
     mMaxTextureSize(0),
     mMaxCubeMapTextureSize(0),
     mMaxTextureImageSize(0),
     mMaxRenderbufferSize(0),
     mNeedsTextureSizeChecks(false),
@@ -1848,22 +1847,17 @@ GLContext::MarkDestroyed()
 {
     if (IsDestroyed())
         return;
 
     if (MakeCurrent()) {
         DestroyScreenBuffer();
 
         mBlitHelper = nullptr;
-
-        // Likely used by OGL Layers.
-        fDeleteProgram(mBlitProgram);
-        mBlitProgram = 0;
-        fDeleteFramebuffers(1, &mBlitFramebuffer);
-        mBlitFramebuffer = 0;
+        mBlitTextureImageHelper = nullptr;
 
         mTexGarbageBin->GLContextTeardown();
     } else {
         NS_WARNING("MakeCurrent() failed during MarkDestroyed! Skipping GL object teardown.");
     }
 
     mSymbols.Zero();
 }
@@ -2319,160 +2313,16 @@ GLContext::ReadPixelsIntoImageSurface(gf
                 }
             }
             dest->MarkDirty();
         }
     }
 #endif
 }
 
-void
-GLContext::BlitTextureImage(TextureImage *aSrc, const nsIntRect& aSrcRect,
-                            TextureImage *aDst, const nsIntRect& aDstRect)
-{
-    NS_ASSERTION(!aSrc->InUpdate(), "Source texture is in update!");
-    NS_ASSERTION(!aDst->InUpdate(), "Destination texture is in update!");
-
-    if (aSrcRect.IsEmpty() || aDstRect.IsEmpty())
-        return;
-
-    int savedFb = 0;
-    fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, &savedFb);
-
-    fDisable(LOCAL_GL_SCISSOR_TEST);
-    fDisable(LOCAL_GL_BLEND);
-
-    // 2.0 means scale up by two
-    float blitScaleX = float(aDstRect.width) / float(aSrcRect.width);
-    float blitScaleY = float(aDstRect.height) / float(aSrcRect.height);
-
-    // We start iterating over all destination tiles
-    aDst->BeginTileIteration();
-    do {
-        // calculate portion of the tile that is going to be painted to
-        nsIntRect dstSubRect;
-        nsIntRect dstTextureRect = ThebesIntRect(aDst->GetTileRect());
-        dstSubRect.IntersectRect(aDstRect, dstTextureRect);
-
-        // this tile is not part of the destination rectangle aDstRect
-        if (dstSubRect.IsEmpty())
-            continue;
-
-        // (*) transform the rect of this tile into the rectangle defined by aSrcRect...
-        nsIntRect dstInSrcRect(dstSubRect);
-        dstInSrcRect.MoveBy(-aDstRect.TopLeft());
-        // ...which might be of different size, hence scale accordingly
-        dstInSrcRect.ScaleRoundOut(1.0f / blitScaleX, 1.0f / blitScaleY);
-        dstInSrcRect.MoveBy(aSrcRect.TopLeft());
-
-        SetBlitFramebufferForDestTexture(aDst->GetTextureID());
-        UseBlitProgram();
-
-        aSrc->BeginTileIteration();
-        // now iterate over all tiles in the source Image...
-        do {
-            // calculate portion of the source tile that is in the source rect
-            nsIntRect srcSubRect;
-            nsIntRect srcTextureRect = ThebesIntRect(aSrc->GetTileRect());
-            srcSubRect.IntersectRect(aSrcRect, srcTextureRect);
-
-            // this tile is not part of the source rect
-            if (srcSubRect.IsEmpty()) {
-                continue;
-            }
-            // calculate intersection of source rect with destination rect
-            srcSubRect.IntersectRect(srcSubRect, dstInSrcRect);
-            // this tile does not overlap the current destination tile
-            if (srcSubRect.IsEmpty()) {
-                continue;
-            }
-            // We now have the intersection of
-            //     the current source tile
-            // and the desired source rectangle
-            // and the destination tile
-            // and the desired destination rectange
-            // in destination space.
-            // We need to transform this back into destination space, inverting the transform from (*)
-            nsIntRect srcSubInDstRect(srcSubRect);
-            srcSubInDstRect.MoveBy(-aSrcRect.TopLeft());
-            srcSubInDstRect.ScaleRoundOut(blitScaleX, blitScaleY);
-            srcSubInDstRect.MoveBy(aDstRect.TopLeft());
-
-            // we transform these rectangles to be relative to the current src and dst tiles, respectively
-            nsIntSize srcSize = srcTextureRect.Size();
-            nsIntSize dstSize = dstTextureRect.Size();
-            srcSubRect.MoveBy(-srcTextureRect.x, -srcTextureRect.y);
-            srcSubInDstRect.MoveBy(-dstTextureRect.x, -dstTextureRect.y);
-
-            float dx0 = 2.0f * float(srcSubInDstRect.x) / float(dstSize.width) - 1.0f;
-            float dy0 = 2.0f * float(srcSubInDstRect.y) / float(dstSize.height) - 1.0f;
-            float dx1 = 2.0f * float(srcSubInDstRect.x + srcSubInDstRect.width) / float(dstSize.width) - 1.0f;
-            float dy1 = 2.0f * float(srcSubInDstRect.y + srcSubInDstRect.height) / float(dstSize.height) - 1.0f;
-            PushViewportRect(nsIntRect(0, 0, dstSize.width, dstSize.height));
-
-            RectTriangles rects;
-
-            nsIntSize realTexSize = srcSize;
-            if (!CanUploadNonPowerOfTwo()) {
-                realTexSize = nsIntSize(NextPowerOfTwo(srcSize.width),
-                                        NextPowerOfTwo(srcSize.height));
-            }
-
-            if (aSrc->GetWrapMode() == LOCAL_GL_REPEAT) {
-                rects.addRect(/* dest rectangle */
-                        dx0, dy0, dx1, dy1,
-                        /* tex coords */
-                        srcSubRect.x / float(realTexSize.width),
-                        srcSubRect.y / float(realTexSize.height),
-                        srcSubRect.XMost() / float(realTexSize.width),
-                        srcSubRect.YMost() / float(realTexSize.height));
-            } else {
-                DecomposeIntoNoRepeatTriangles(srcSubRect, realTexSize, rects);
-
-                // now put the coords into the d[xy]0 .. d[xy]1 coordinate space
-                // from the 0..1 that it comes out of decompose
-                RectTriangles::vert_coord* v = (RectTriangles::vert_coord*)rects.vertexPointer();
-
-                for (unsigned int i = 0; i < rects.elements(); ++i) {
-                    v[i].x = (v[i].x * (dx1 - dx0)) + dx0;
-                    v[i].y = (v[i].y * (dy1 - dy0)) + dy0;
-                }
-            }
-
-            TextureImage::ScopedBindTexture texBind(aSrc, LOCAL_GL_TEXTURE0);
-
-            fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
-
-            fVertexAttribPointer(0, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, rects.vertexPointer());
-            fVertexAttribPointer(1, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, rects.texCoordPointer());
-
-            fEnableVertexAttribArray(0);
-            fEnableVertexAttribArray(1);
-
-            fDrawArrays(LOCAL_GL_TRIANGLES, 0, rects.elements());
-
-            fDisableVertexAttribArray(0);
-            fDisableVertexAttribArray(1);
-
-            PopViewportRect();
-        } while (aSrc->NextTile());
-    } while (aDst->NextTile());
-
-    fVertexAttribPointer(0, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, nullptr);
-    fVertexAttribPointer(1, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, nullptr);
-
-    // unbind the previous texture from the framebuffer
-    SetBlitFramebufferForDestTexture(0);
-
-    fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, savedFb);
-
-    fEnable(LOCAL_GL_SCISSOR_TEST);
-    fEnable(LOCAL_GL_BLEND);
-}
-
 static unsigned int
 DataOffset(const nsIntPoint &aPoint, int32_t aStride, gfxImageFormat aFormat)
 {
   unsigned int data = aPoint.y * aStride;
   data += aPoint.x * gfxASurface::BytePerPixelFromFormat(aFormat);
   return data;
 }
 
@@ -2954,121 +2804,16 @@ GLContext::TexSubImage2DWithoutUnpackSub
                     height,
                     format,
                     type,
                     newPixels);
     delete [] newPixels;
     fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
 }
 
-void
-GLContext::UseBlitProgram()
-{
-    if (mBlitProgram) {
-        fUseProgram(mBlitProgram);
-        return;
-    }
-
-    mBlitProgram = fCreateProgram();
-
-    GLuint shaders[2];
-    shaders[0] = fCreateShader(LOCAL_GL_VERTEX_SHADER);
-    shaders[1] = fCreateShader(LOCAL_GL_FRAGMENT_SHADER);
-
-    const char *blitVSSrc =
-        "attribute vec2 aVertex;"
-        "attribute vec2 aTexCoord;"
-        "varying vec2 vTexCoord;"
-        "void main() {"
-        "  vTexCoord = aTexCoord;"
-        "  gl_Position = vec4(aVertex, 0.0, 1.0);"
-        "}";
-    const char *blitFSSrc = "#ifdef GL_ES\nprecision mediump float;\n#endif\n"
-        "uniform sampler2D uSrcTexture;"
-        "varying vec2 vTexCoord;"
-        "void main() {"
-        "  gl_FragColor = texture2D(uSrcTexture, vTexCoord);"
-        "}";
-
-    fShaderSource(shaders[0], 1, (const GLchar**) &blitVSSrc, nullptr);
-    fShaderSource(shaders[1], 1, (const GLchar**) &blitFSSrc, nullptr);
-
-    for (int i = 0; i < 2; ++i) {
-        GLint success, len = 0;
-
-        fCompileShader(shaders[i]);
-        fGetShaderiv(shaders[i], LOCAL_GL_COMPILE_STATUS, &success);
-        NS_ASSERTION(success, "Shader compilation failed!");
-
-        if (!success) {
-            nsAutoCString log;
-            fGetShaderiv(shaders[i], LOCAL_GL_INFO_LOG_LENGTH, (GLint*) &len);
-            log.SetCapacity(len);
-            fGetShaderInfoLog(shaders[i], len, (GLint*) &len, (char*) log.BeginWriting());
-            log.SetLength(len);
-
-            printf_stderr("Shader %d compilation failed:\n%s\n", log.get());
-            return;
-        }
-
-        fAttachShader(mBlitProgram, shaders[i]);
-        fDeleteShader(shaders[i]);
-    }
-
-    fBindAttribLocation(mBlitProgram, 0, "aVertex");
-    fBindAttribLocation(mBlitProgram, 1, "aTexCoord");
-
-    fLinkProgram(mBlitProgram);
-
-    GLint success, len = 0;
-    fGetProgramiv(mBlitProgram, LOCAL_GL_LINK_STATUS, &success);
-    NS_ASSERTION(success, "Shader linking failed!");
-
-    if (!success) {
-        nsAutoCString log;
-        fGetProgramiv(mBlitProgram, LOCAL_GL_INFO_LOG_LENGTH, (GLint*) &len);
-        log.SetCapacity(len);
-        fGetProgramInfoLog(mBlitProgram, len, (GLint*) &len, (char*) log.BeginWriting());
-        log.SetLength(len);
-
-        printf_stderr("Program linking failed:\n%s\n", log.get());
-        return;
-    }
-
-    fUseProgram(mBlitProgram);
-    fUniform1i(fGetUniformLocation(mBlitProgram, "uSrcTexture"), 0);
-}
-
-void
-GLContext::SetBlitFramebufferForDestTexture(GLuint aTexture)
-{
-    if (!mBlitFramebuffer) {
-        fGenFramebuffers(1, &mBlitFramebuffer);
-    }
-
-    fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mBlitFramebuffer);
-    fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
-                          LOCAL_GL_COLOR_ATTACHMENT0,
-                          LOCAL_GL_TEXTURE_2D,
-                          aTexture,
-                          0);
-
-    GLenum result = fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
-    if (aTexture && (result != LOCAL_GL_FRAMEBUFFER_COMPLETE)) {
-        nsAutoCString msg;
-        msg.Append("Framebuffer not complete -- error 0x");
-        msg.AppendInt(result, 16);
-        // Note: if you are hitting this, it is likely that
-        // your texture is not texture complete -- that is, you
-        // allocated a texture name, but didn't actually define its
-        // size via a call to TexImage2D.
-        NS_RUNTIMEABORT(msg.get());
-    }
-}
-
 #ifdef DEBUG
 
 void
 GLContext::CreatedProgram(GLContext *aOrigin, GLuint aName)
 {
     mTrackedPrograms.AppendElement(NamedResource(aOrigin, aName));
 }
 
@@ -3356,16 +3101,26 @@ GLContext::BlitHelper()
 {
     if (!mBlitHelper) {
         mBlitHelper = new GLBlitHelper(this);
     }
 
     return mBlitHelper;
 }
 
+GLBlitTextureImageHelper*
+GLContext::BlitTextureImageHelper()
+{
+    if (!mBlitTextureImageHelper) {
+        mBlitTextureImageHelper = new GLBlitTextureImageHelper(this);
+    }
+
+    return mBlitTextureImageHelper;
+}
+
 bool
 DoesStringMatch(const char* aString, const char *aWantedString)
 {
     if (!aString || !aWantedString)
         return false;
 
     const char *occurrence = strstr(aString, aWantedString);
 
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -59,16 +59,17 @@ namespace mozilla {
     }
 
     namespace gl {
         class GLContext;
         class GLLibraryEGL;
         class GLScreenBuffer;
         class TextureGarbageBin;
         class GLBlitHelper;
+        class GLBlitTextureImageHelper;
     }
 
     namespace layers {
         class ColorTextureLayerProgram;
     }
 }
 
 namespace mozilla {
@@ -2758,44 +2759,16 @@ public:
 
     // Similar to ReadPixelsIntoImageSurface, but pulls from the screen
     // instead of the currently bound framebuffer.
     void ReadScreenIntoImageSurface(gfxImageSurface* dest);
 
     TemporaryRef<gfx::SourceSurface> ReadPixelsToSourceSurface(const gfx::IntSize &aSize);
 
     /**
-     * Copy a rectangle from one TextureImage into another.  The
-     * source and destination are given in integer coordinates, and
-     * will be converted to texture coordinates.
-     *
-     * For the source texture, the wrap modes DO apply -- it's valid
-     * to use REPEAT or PAD and expect appropriate behaviour if the source
-     * rectangle extends beyond its bounds.
-     *
-     * For the destination texture, the wrap modes DO NOT apply -- the
-     * destination will be clipped by the bounds of the texture.
-     *
-     * Note: calling this function will cause the following OpenGL state
-     * to be changed:
-     *
-     *   - current program
-     *   - framebuffer binding
-     *   - viewport
-     *   - blend state (will be enabled at end)
-     *   - scissor state (will be enabled at end)
-     *   - vertex attrib 0 and 1 (pointer and enable state [enable state will be disabled at exit])
-     *   - array buffer binding (will be 0)
-     *   - active texture (will be 0)
-     *   - texture 0 binding
-     */
-    void BlitTextureImage(TextureImage *aSrc, const nsIntRect& aSrcRect,
-                          TextureImage *aDst, const nsIntRect& aDstRect);
-
-    /**
      * Creates a RGB/RGBA texture (or uses one provided) and uploads the surface
      * contents to it within aSrcRect.
      *
      * aSrcRect.x/y will be uploaded to 0/0 in the texture, and the size
      * of the texture with be aSrcRect.width/height.
      *
      * If an existing texture is passed through aTexture, it is assumed it
      * has already been initialised with glTexImage2D (or this function),
@@ -2938,26 +2911,23 @@ protected:
     // to a GLContext that isn't current on the current
     // thread.
     // Store the current context when binding to thread local
     // storage to support DebugMode on an arbitrary thread.
     static unsigned sCurrentGLContextTLS;
 #endif
     bool mFlipped;
 
-    // lazy-initialized things
-    GLuint mBlitProgram, mBlitFramebuffer;
-    void UseBlitProgram();
-    void SetBlitFramebufferForDestTexture(GLuint aTexture);
-
     ScopedDeletePtr<GLBlitHelper> mBlitHelper;
+    ScopedDeletePtr<GLBlitTextureImageHelper> mBlitTextureImageHelper;
 
 public:
 
     GLBlitHelper* BlitHelper();
+    GLBlitTextureImageHelper* BlitTextureImageHelper();
 
     // Assumes shares are created by all sharing with the same global context.
     bool SharesWith(const GLContext* other) const {
         MOZ_ASSERT(!this->mSharedContext || !this->mSharedContext->mSharedContext);
         MOZ_ASSERT(!other->mSharedContext || !other->mSharedContext->mSharedContext);
         MOZ_ASSERT(!this->mSharedContext ||
                    !other->mSharedContext ||
                    this->mSharedContext == other->mSharedContext);
--- a/gfx/gl/moz.build
+++ b/gfx/gl/moz.build
@@ -24,16 +24,17 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'go
 
 if CONFIG['MOZ_GL_PROVIDER']:
     gl_provider = CONFIG['MOZ_GL_PROVIDER']
 
 EXPORTS += [
     'DecomposeIntoNoRepeatTriangles.h',
     'ForceDiscreteGPUHelperCGL.h',
     'GfxTexturesReporter.h',
+    'GLBlitTextureImageHelper.h',
     'GLConsts.h',
     'GLContext.h',
     'GLContextProvider.h',
     'GLContextProviderImpl.h',
     'GLContextSymbols.h',
     'GLContextTypes.h',
     'GLContextUtils.h',
     'GLDefs.h',
@@ -101,16 +102,17 @@ else:
     UNIFIED_SOURCES += [
         'GLContextProvider%s.cpp' % gl_provider,
     ]
 
 UNIFIED_SOURCES += [
     'DecomposeIntoNoRepeatTriangles.cpp',
     'GfxTexturesReporter.cpp',
     'GLBlitHelper.cpp',
+    'GLBlitTextureImageHelper.cpp',
     'GLContext.cpp',
     'GLContextFeatures.cpp',
     'GLContextTypes.cpp',
     'GLContextUtils.cpp',
     'GLLibraryEGL.cpp',
     'GLLibraryLoader.cpp',
     'GLScreenBuffer.cpp',
     'GLTextureImage.cpp',
--- a/gfx/layers/opengl/TextureHostOGL.cpp
+++ b/gfx/layers/opengl/TextureHostOGL.cpp
@@ -24,16 +24,17 @@
 # include "GrallocImages.h"  // for GrallocImage
 #endif
 #include "mozilla/layers/ISurfaceAllocator.h"
 #include "mozilla/layers/YCbCrImageDataSerializer.h"
 #include "mozilla/layers/GrallocTextureHost.h"
 #include "nsPoint.h"                    // for nsIntPoint
 #include "nsRegion.h"                   // for nsIntRegion
 #include "GfxTexturesReporter.h"        // for GfxTexturesReporter
+#include "GLBlitTextureImageHelper.h"
 #ifdef XP_MACOSX
 #include "SharedSurfaceIO.h"
 #include "mozilla/layers/MacIOSurfaceTextureHostOGL.h"
 #endif
 #include "GeckoProfiler.h"
 
 using namespace mozilla::gl;
 using namespace mozilla::gfx;
@@ -510,18 +511,18 @@ TextureImageDeprecatedTextureHostOGL::Co
                                    DeprecatedTextureHost *aDest,
                                    const nsIntRect& aDestRect)
 {
   MOZ_ASSERT(aDest->AsSourceOGL(), "Incompatible destination type!");
   TextureImageDeprecatedTextureHostOGL *dest =
     aDest->AsSourceOGL()->AsTextureImageDeprecatedTextureHost();
   MOZ_ASSERT(dest, "Incompatible destination type!");
 
-  mGL->BlitTextureImage(mTexture, aSourceRect,
-                        dest->mTexture, aDestRect);
+  mGL->BlitTextureImageHelper()->BlitTextureImage(mTexture, aSourceRect,
+                                                  dest->mTexture, aDestRect);
   dest->mTexture->MarkValid();
 }
 
 void
 TextureImageDeprecatedTextureHostOGL::UpdateImpl(const SurfaceDescriptor& aImage,
                                        nsIntRegion* aRegion,
                                        nsIntPoint* aOffset)
 {