Bug 887318 - Fix SkiaGL readback when BGRA unavailable r=jgilbert
authorJames Willcox <jwillcox@mozilla.com>
Thu, 27 Jun 2013 10:20:21 -0400
changeset 139173 fb0c86ed08c2a198ea0513202da252a8be675ed5
parent 139172 8e87e5a44607cf2a119715afb8315c87353f0500
child 139174 5e051e9912f81bbefc7846b5d0fcba7bfb0e6d73
push id31266
push userjwillcox@mozilla.com
push dateFri, 19 Jul 2013 02:34:02 +0000
treeherdermozilla-inbound@4303c1af09f6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjgilbert
bugs887318
milestone25.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 887318 - Fix SkiaGL readback when BGRA unavailable r=jgilbert
gfx/gl/GLContextSkia.cpp
gfx/skia/patches/0024-Bug-887318-fix-bgra-readback.patch
gfx/skia/src/gpu/gl/GrGpuGL.cpp
gfx/skia/src/gpu/gl/GrGpuGL.h
--- a/gfx/gl/GLContextSkia.cpp
+++ b/gfx/gl/GLContextSkia.cpp
@@ -305,17 +305,17 @@ const GLubyte* glGetString_mozilla(GrGLe
             return reinterpret_cast<const GLubyte*>("OpenGL ES 2.0");
         } else {
             return reinterpret_cast<const GLubyte*>("2.0");
         }
     } else if (name == LOCAL_GL_EXTENSIONS) {
         // Only expose the bare minimum extensions we want to support to ensure a functional Ganesh
         // as GLContext only exposes certain extensions
         static bool extensionsStringBuilt = false;
-        static char extensionsString[120];
+        static char extensionsString[256];
 
         if (!extensionsStringBuilt) {
             if (sGLContext.get()->IsExtensionSupported(GLContext::EXT_texture_format_BGRA8888)) {
                 strcpy(extensionsString, "GL_EXT_texture_format_BGRA8888 ");
             }
 
             if (sGLContext.get()->IsExtensionSupported(GLContext::OES_packed_depth_stencil)) {
                 strcat(extensionsString, "GL_OES_packed_depth_stencil ");
@@ -324,16 +324,24 @@ const GLubyte* glGetString_mozilla(GrGLe
             if (sGLContext.get()->IsExtensionSupported(GLContext::EXT_packed_depth_stencil)) {
                 strcat(extensionsString, "GL_EXT_packed_depth_stencil ");
             }
 
             if (sGLContext.get()->IsExtensionSupported(GLContext::OES_rgb8_rgba8)) {
                 strcat(extensionsString, "GL_OES_rgb8_rgba8 ");
             }
 
+            if (sGLContext.get()->IsExtensionSupported(GLContext::EXT_bgra)) {
+                strcat(extensionsString, "GL_EXT_bgra ");
+            }
+
+            if (sGLContext.get()->IsExtensionSupported(GLContext::EXT_read_format_bgra)) {
+                strcat(extensionsString, "GL_EXT_read_format_bgra ");
+            }
+
             extensionsStringBuilt = true;
         }
 
         return reinterpret_cast<const GLubyte*>(extensionsString);
 
     } else if (name == LOCAL_GL_SHADING_LANGUAGE_VERSION) {
         if (sGLContext.get()->IsGLES2()) {
             return reinterpret_cast<const GLubyte*>("OpenGL ES GLSL ES 1.0");
new file mode 100644
--- /dev/null
+++ b/gfx/skia/patches/0024-Bug-887318-fix-bgra-readback.patch
@@ -0,0 +1,217 @@
+diff --git a/gfx/gl/GLContextSkia.cpp b/gfx/gl/GLContextSkia.cpp
+--- a/gfx/gl/GLContextSkia.cpp
++++ b/gfx/gl/GLContextSkia.cpp
+@@ -303,39 +303,47 @@ const GLubyte* glGetString_mozilla(GrGLe
+     if (name == LOCAL_GL_VERSION) {
+         if (sGLContext.get()->IsGLES2()) {
+             return reinterpret_cast<const GLubyte*>("OpenGL ES 2.0");
+         } else {
+             return reinterpret_cast<const GLubyte*>("2.0");
+         }
+     } else if (name == LOCAL_GL_EXTENSIONS) {
+         // Only expose the bare minimum extensions we want to support to ensure a functional Ganesh
+         // as GLContext only exposes certain extensions
+         static bool extensionsStringBuilt = false;
+-        static char extensionsString[120];
++        static char extensionsString[256];
+ 
+         if (!extensionsStringBuilt) {
+             if (sGLContext.get()->IsExtensionSupported(GLContext::EXT_texture_format_BGRA8888)) {
+                 strcpy(extensionsString, "GL_EXT_texture_format_BGRA8888 ");
+             }
+ 
+             if (sGLContext.get()->IsExtensionSupported(GLContext::OES_packed_depth_stencil)) {
+                 strcat(extensionsString, "GL_OES_packed_depth_stencil ");
+             }
+ 
+             if (sGLContext.get()->IsExtensionSupported(GLContext::EXT_packed_depth_stencil)) {
+                 strcat(extensionsString, "GL_EXT_packed_depth_stencil ");
+             }
+ 
+             if (sGLContext.get()->IsExtensionSupported(GLContext::OES_rgb8_rgba8)) {
+                 strcat(extensionsString, "GL_OES_rgb8_rgba8 ");
+             }
+ 
++            if (sGLContext.get()->IsExtensionSupported(GLContext::EXT_bgra)) {
++                strcat(extensionsString, "GL_EXT_bgra ");
++            }
++
++            if (sGLContext.get()->IsExtensionSupported(GLContext::EXT_read_format_bgra)) {
++                strcat(extensionsString, "GL_EXT_read_format_bgra ");
++            }
++
+             extensionsStringBuilt = true;
+         }
+ 
+         return reinterpret_cast<const GLubyte*>(extensionsString);
+ 
+     } else if (name == LOCAL_GL_SHADING_LANGUAGE_VERSION) {
+         if (sGLContext.get()->IsGLES2()) {
+             return reinterpret_cast<const GLubyte*>("OpenGL ES GLSL ES 1.0");
+         } else {
+             return reinterpret_cast<const GLubyte*>("1.10");
+diff --git a/gfx/skia/src/gpu/gl/GrGpuGL.cpp b/gfx/skia/src/gpu/gl/GrGpuGL.cpp
+--- a/gfx/skia/src/gpu/gl/GrGpuGL.cpp
++++ b/gfx/skia/src/gpu/gl/GrGpuGL.cpp
+@@ -1,18 +1,18 @@
+ /*
+  * Copyright 2011 Google Inc.
+  *
+  * Use of this source code is governed by a BSD-style license that can be
+  * found in the LICENSE file.
+  */
+ 
+-
++#include <algorithm>
+ #include "GrGpuGL.h"
+ #include "GrGLStencilBuffer.h"
+ #include "GrGLPath.h"
+ #include "GrGLShaderBuilder.h"
+ #include "GrTemplates.h"
+ #include "GrTypes.h"
+ #include "SkTemplates.h"
+ 
+ static const GrGLuint GR_MAX_GLUINT = ~0U;
+ static const GrGLint  GR_INVAL_GLINT = ~0;
+@@ -1381,29 +1381,67 @@ bool GrGpuGL::readPixelsWillPayForYFlip(
+     // Note the rowBytes might be tight to the passed in data, but if data
+     // gets clipped in x to the target the rowBytes will no longer be tight.
+     if (left >= 0 && (left + width) < renderTarget->width()) {
+            return 0 == rowBytes ||
+                   GrBytesPerPixel(config) * width == rowBytes;
+     } else {
+         return false;
+     }
+ }
+ 
++static void swizzleRow(void* buffer, int byteLen) {
++    uint8_t* src = (uint8_t*)buffer;
++    uint8_t* end = src + byteLen;
++
++    GrAssert((end - src) % 4 == 0);
++
++    for (; src != end; src += 4) {
++        std::swap(src[0], src[2]);
++    }
++}
++
++bool GrGpuGL::canReadBGRA() const
++{
++    if (kDesktop_GrGLBinding == this->glBinding() ||
++        this->hasExtension("GL_EXT_bgra"))
++        return true;
++
++    if (this->hasExtension("GL_EXT_read_format_bgra")) {
++        GrGLint readFormat = 0;
++        GrGLint readType = 0;
++
++        GL_CALL(GetIntegerv(GR_GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readFormat));
++        GL_CALL(GetIntegerv(GR_GL_IMPLEMENTATION_COLOR_READ_TYPE, &readType));
++
++        return readFormat == GR_GL_BGRA && readType == GR_GL_UNSIGNED_BYTE;
++    }
++
++    return false;
++}
++
+ bool GrGpuGL::onReadPixels(GrRenderTarget* target,
+                            int left, int top,
+                            int width, int height,
+                            GrPixelConfig config,
+                            void* buffer,
+                            size_t rowBytes) {
+     GrGLenum format;
+     GrGLenum type;
+     bool flipY = kBottomLeft_GrSurfaceOrigin == target->origin();
++    bool needSwizzle = false;
++
++    if (kBGRA_8888_GrPixelConfig == config && !this->canReadBGRA()) {
++        // Read RGBA and swizzle after
++        config = kRGBA_8888_GrPixelConfig;
++        needSwizzle = true;
++    }
++
+     if (!this->configToGLFormats(config, false, NULL, &format, &type)) {
+         return false;
+     }
+     size_t bpp = GrBytesPerPixel(config);
+     if (!adjust_pixel_ops_params(target->width(), target->height(), bpp,
+                                  &left, &top, &width, &height,
+                                  const_cast<const void**>(&buffer),
+                                  &rowBytes)) {
+         return false;
+     }
+@@ -1478,35 +1516,46 @@ bool GrGpuGL::onReadPixels(GrRenderTarge
+             scratch.reset(tightRowBytes);
+             void* tmpRow = scratch.get();
+             // flip y in-place by rows
+             const int halfY = height >> 1;
+             char* top = reinterpret_cast<char*>(buffer);
+             char* bottom = top + (height - 1) * rowBytes;
+             for (int y = 0; y < halfY; y++) {
+                 memcpy(tmpRow, top, tightRowBytes);
+                 memcpy(top, bottom, tightRowBytes);
+                 memcpy(bottom, tmpRow, tightRowBytes);
++
++                if (needSwizzle) {
++                    swizzleRow(top, tightRowBytes);
++                    swizzleRow(bottom, tightRowBytes);
++                }
++
+                 top += rowBytes;
+                 bottom -= rowBytes;
+             }
+         }
+     } else {
+-        GrAssert(readDst != buffer);        GrAssert(rowBytes != tightRowBytes);
++        GrAssert(readDst != buffer);
++        GrAssert(rowBytes != tightRowBytes);
+         // copy from readDst to buffer while flipping y
+         // const int halfY = height >> 1;
+         const char* src = reinterpret_cast<const char*>(readDst);
+         char* dst = reinterpret_cast<char*>(buffer);
+         if (flipY) {
+             dst += (height-1) * rowBytes;
+         }
+         for (int y = 0; y < height; y++) {
+             memcpy(dst, src, tightRowBytes);
++            if (needSwizzle) {
++                swizzleRow(dst, tightRowBytes);
++            }
++
+             src += readDstRowBytes;
+             if (!flipY) {
+                 dst += rowBytes;
+             } else {
+                 dst -= rowBytes;
+             }
+         }
+     }
+     return true;
+ }
+diff --git a/gfx/skia/src/gpu/gl/GrGpuGL.h b/gfx/skia/src/gpu/gl/GrGpuGL.h
+--- a/gfx/skia/src/gpu/gl/GrGpuGL.h
++++ b/gfx/skia/src/gpu/gl/GrGpuGL.h
+@@ -243,20 +243,22 @@ private:
+                        GrPixelConfig dataConfig,
+                        const void* data,
+                        size_t rowBytes);
+ 
+     bool createRenderTargetObjects(int width, int height,
+                                    GrGLuint texID,
+                                    GrGLRenderTarget::Desc* desc);
+ 
+     void fillInConfigRenderableTable();
+ 
++    bool canReadBGRA() const;
++
+     GrGLContext fGLContext;
+ 
+     // GL program-related state
+     ProgramCache*               fProgramCache;
+     SkAutoTUnref<GrGLProgram>   fCurrentProgram;
+ 
+     ///////////////////////////////////////////////////////////////////////////
+     ///@name Caching of GL State
+     ///@{
+     int                         fHWActiveTextureUnitIdx;
--- a/gfx/skia/src/gpu/gl/GrGpuGL.cpp
+++ b/gfx/skia/src/gpu/gl/GrGpuGL.cpp
@@ -1,16 +1,16 @@
 /*
  * Copyright 2011 Google Inc.
  *
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
 
-
+#include <algorithm>
 #include "GrGpuGL.h"
 #include "GrGLStencilBuffer.h"
 #include "GrGLPath.h"
 #include "GrGLShaderBuilder.h"
 #include "GrTemplates.h"
 #include "GrTypes.h"
 #include "SkTemplates.h"
 
@@ -1383,25 +1383,63 @@ bool GrGpuGL::readPixelsWillPayForYFlip(
     if (left >= 0 && (left + width) < renderTarget->width()) {
            return 0 == rowBytes ||
                   GrBytesPerPixel(config) * width == rowBytes;
     } else {
         return false;
     }
 }
 
+static void swizzleRow(void* buffer, int byteLen) {
+    uint8_t* src = (uint8_t*)buffer;
+    uint8_t* end = src + byteLen;
+
+    GrAssert((end - src) % 4 == 0);
+
+    for (; src != end; src += 4) {
+        std::swap(src[0], src[2]);
+    }
+}
+
+bool GrGpuGL::canReadBGRA() const
+{
+    if (kDesktop_GrGLBinding == this->glBinding() ||
+        this->hasExtension("GL_EXT_bgra"))
+        return true;
+
+    if (this->hasExtension("GL_EXT_read_format_bgra")) {
+        GrGLint readFormat = 0;
+        GrGLint readType = 0;
+
+        GL_CALL(GetIntegerv(GR_GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readFormat));
+        GL_CALL(GetIntegerv(GR_GL_IMPLEMENTATION_COLOR_READ_TYPE, &readType));
+
+        return readFormat == GR_GL_BGRA && readType == GR_GL_UNSIGNED_BYTE;
+    }
+
+    return false;
+}
+
 bool GrGpuGL::onReadPixels(GrRenderTarget* target,
                            int left, int top,
                            int width, int height,
                            GrPixelConfig config,
                            void* buffer,
                            size_t rowBytes) {
     GrGLenum format;
     GrGLenum type;
     bool flipY = kBottomLeft_GrSurfaceOrigin == target->origin();
+    bool needSwizzle = false;
+
+    if (kBGRA_8888_GrPixelConfig == config && !this->canReadBGRA()) {
+        // Read RGBA and swizzle after
+        config = kRGBA_8888_GrPixelConfig;
+        needSwizzle = true;
+    }
+
     if (!this->configToGLFormats(config, false, NULL, &format, &type)) {
         return false;
     }
     size_t bpp = GrBytesPerPixel(config);
     if (!adjust_pixel_ops_params(target->width(), target->height(), bpp,
                                  &left, &top, &width, &height,
                                  const_cast<const void**>(&buffer),
                                  &rowBytes)) {
@@ -1480,31 +1518,42 @@ bool GrGpuGL::onReadPixels(GrRenderTarge
             // flip y in-place by rows
             const int halfY = height >> 1;
             char* top = reinterpret_cast<char*>(buffer);
             char* bottom = top + (height - 1) * rowBytes;
             for (int y = 0; y < halfY; y++) {
                 memcpy(tmpRow, top, tightRowBytes);
                 memcpy(top, bottom, tightRowBytes);
                 memcpy(bottom, tmpRow, tightRowBytes);
+
+                if (needSwizzle) {
+                    swizzleRow(top, tightRowBytes);
+                    swizzleRow(bottom, tightRowBytes);
+                }
+
                 top += rowBytes;
                 bottom -= rowBytes;
             }
         }
     } else {
-        GrAssert(readDst != buffer);        GrAssert(rowBytes != tightRowBytes);
+        GrAssert(readDst != buffer);
+        GrAssert(rowBytes != tightRowBytes);
         // copy from readDst to buffer while flipping y
         // const int halfY = height >> 1;
         const char* src = reinterpret_cast<const char*>(readDst);
         char* dst = reinterpret_cast<char*>(buffer);
         if (flipY) {
             dst += (height-1) * rowBytes;
         }
         for (int y = 0; y < height; y++) {
             memcpy(dst, src, tightRowBytes);
+            if (needSwizzle) {
+                swizzleRow(dst, tightRowBytes);
+            }
+
             src += readDstRowBytes;
             if (!flipY) {
                 dst += rowBytes;
             } else {
                 dst -= rowBytes;
             }
         }
     }
--- a/gfx/skia/src/gpu/gl/GrGpuGL.h
+++ b/gfx/skia/src/gpu/gl/GrGpuGL.h
@@ -245,16 +245,18 @@ private:
                        size_t rowBytes);
 
     bool createRenderTargetObjects(int width, int height,
                                    GrGLuint texID,
                                    GrGLRenderTarget::Desc* desc);
 
     void fillInConfigRenderableTable();
 
+    bool canReadBGRA() const;
+
     GrGLContext fGLContext;
 
     // GL program-related state
     ProgramCache*               fProgramCache;
     SkAutoTUnref<GrGLProgram>   fCurrentProgram;
 
     ///////////////////////////////////////////////////////////////////////////
     ///@name Caching of GL State