Bug 975930 - [LayerScope] All textured buffers show RB swapped which is incorrect. r=jgilbert, r=kamidphish
authorMorris Tseng <mtseng@mozilla.com>
Tue, 11 Mar 2014 11:14:50 -0400
changeset 191176 bcf91c83f550c8bb93007435ba776c1280da076e
parent 191175 a2f674b6536c1a6b9f2d1c4b30177b3e55630294
child 191177 01de281bca375aaf740d88ad93e34a4daee9d013
push id474
push userasasaki@mozilla.com
push dateMon, 02 Jun 2014 21:01:02 +0000
treeherdermozilla-release@967f4cf1b31c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjgilbert, kamidphish
bugs975930
milestone30.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 975930 - [LayerScope] All textured buffers show RB swapped which is incorrect. r=jgilbert, r=kamidphish
gfx/gl/GLReadTexImageHelper.cpp
gfx/gl/GLReadTexImageHelper.h
gfx/layers/LayerScope.cpp
--- a/gfx/gl/GLReadTexImageHelper.cpp
+++ b/gfx/gl/GLReadTexImageHelper.cpp
@@ -226,16 +226,81 @@ static void SwapRAndBComponents(DataSour
       row += 4;
     }
 
     row += rowHole;
     --rows;
   }
 }
 
+static uint16_t PackRGB565(uint8_t r, uint8_t g, uint8_t b)
+{
+    uint16_t pixel = ((r << 11) & 0xf800) |
+                     ((g <<  5) & 0x07e0) |
+                     ((b      ) & 0x001f);
+
+    return pixel;
+}
+
+static void CopyDataSourceSurface(DataSourceSurface* aSource,
+                                  DataSourceSurface* aDest)
+{
+  MOZ_ASSERT(aSource->GetSize() == aDest->GetSize());
+  MOZ_ASSERT(aSource->GetFormat() == SurfaceFormat::R8G8B8A8 ||
+             aSource->GetFormat() == SurfaceFormat::R8G8B8X8);
+
+  uint8_t *srcRow = aSource->GetData();
+  size_t srcRowBytes = aSource->GetSize().width * BytesPerPixel(aSource->GetFormat());
+  size_t srcRowHole = aSource->Stride() - srcRowBytes;
+
+  uint8_t *destRow = aDest->GetData();
+  size_t destRowBytes = aDest->GetSize().width * BytesPerPixel(aDest->GetFormat());
+  size_t destRowHole = aDest->Stride() - destRowBytes;
+
+  bool needsRBSwap = false;
+  if (aDest->GetFormat() == SurfaceFormat::B8G8R8A8 ||
+      aDest->GetFormat() == SurfaceFormat::B8G8R8X8 ||
+      aDest->GetFormat() == SurfaceFormat::R5G6B5) {
+      needsRBSwap = true;
+  }
+
+  bool needsConvertTo16Bits = false;
+  if (aDest->GetFormat() == SurfaceFormat::R5G6B5) {
+      needsConvertTo16Bits = true;
+  }
+
+  size_t rows = aSource->GetSize().height;
+
+  while (rows) {
+    const uint8_t *srcRowEnd = srcRow + srcRowBytes;
+
+    while (srcRow != srcRowEnd) {
+      uint8_t r = needsRBSwap ? srcRow[2] : srcRow[0];
+      uint8_t g = srcRow[1];
+      uint8_t b = needsRBSwap ? srcRow[0] : srcRow[2];
+      uint8_t a = srcRow[3];
+
+      if (needsConvertTo16Bits) {
+        *(uint16_t*)destRow = PackRGB565(r, g, b);
+      } else {
+        destRow[0] = r;
+        destRow[1] = g;
+        destRow[2] = b;
+        destRow[3] = a;
+      }
+      srcRow += BytesPerPixel(aSource->GetFormat());
+      destRow += BytesPerPixel(aDest->GetFormat());
+    }
+
+    srcRow += srcRowHole;
+    destRow += destRowHole;
+    --rows;
+  }
+}
+
 static int
 CalcStride(int width, int pixelSize, int alignment)
 {
     MOZ_ASSERT(alignment);
 
     int stride = width * pixelSize;
     if (stride % alignment) { // Extra at the end of the line?
         int alignmentCount = stride / alignment;
@@ -432,16 +497,144 @@ ReadPixelsIntoImageSurface(GLContext* gl
                 }
             }
             dest->MarkDirty();
         }
     }
 #endif
 }
 
+void
+ReadPixelsIntoDataSourceSurface(GLContext* gl, DataSourceSurface* dest) {
+    gl->MakeCurrent();
+    MOZ_ASSERT(dest->GetSize().width != 0);
+    MOZ_ASSERT(dest->GetSize().height != 0);
+
+    bool hasAlpha = dest->GetFormat() == SurfaceFormat::B8G8R8A8 ||
+                    dest->GetFormat() == SurfaceFormat::R8G8B8A8;
+
+    int destPixelSize;
+    GLenum destFormat;
+    GLenum destType;
+
+    switch (dest->GetFormat()) {
+        case SurfaceFormat::B8G8R8A8:
+        case SurfaceFormat::B8G8R8X8:
+            // Needs host (little) endian ARGB.
+            destFormat = LOCAL_GL_BGRA;
+            destType = LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV;
+            break;
+        case SurfaceFormat::R8G8B8A8:
+        case SurfaceFormat::R8G8B8X8:
+            // Needs host (little) endian ABGR.
+            destFormat = LOCAL_GL_RGBA;
+            destType = LOCAL_GL_UNSIGNED_BYTE;
+            break;
+        case SurfaceFormat::R5G6B5:
+            destFormat = LOCAL_GL_RGB;
+            destType = LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV;
+            break;
+        default:
+            MOZ_CRASH("Bad format.");
+    }
+    destPixelSize = BytesPerPixel(dest->GetFormat());
+    MOZ_ASSERT(dest->GetSize().width * destPixelSize <= dest->Stride());
+
+    GLenum readFormat = destFormat;
+    GLenum readType = destType;
+    bool needsTempSurf = !GetActualReadFormats(gl,
+                                               destFormat, destType,
+                                               readFormat, readType);
+
+    RefPtr<DataSourceSurface> tempSurf;
+    DataSourceSurface* readSurf = nullptr;
+    int readAlignment = 0;
+    if (needsTempSurf) {
+        if (gl->DebugMode()) {
+            NS_WARNING("Needing intermediary surface for ReadPixels. This will be slow!");
+        }
+        SurfaceFormat readFormatGFX;
+
+        // If needs temp surface, readFormat is always LOCAL_GL_RGBA
+        // and readType is always LOCAL_GL_UNSIGNED_BYTE
+        MOZ_ASSERT(readFormat == LOCAL_GL_RGBA);
+        MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE);
+        readFormatGFX = hasAlpha ? SurfaceFormat::R8G8B8A8
+                                 : SurfaceFormat::R8G8B8X8;
+        readAlignment = 1;
+        int32_t stride = dest->GetSize().width * BytesPerPixel(readFormatGFX);
+        tempSurf = Factory::CreateDataSourceSurfaceWithStride(dest->GetSize(),
+                                                              readFormatGFX,
+                                                              stride);
+        readSurf = tempSurf;
+    } else {
+        // Figure out alignment. We don't need to know why, we just need it
+        // to be valid.
+        readAlignment = GuessAlignment(dest->GetSize().width,
+                                       destPixelSize,
+                                       dest->Stride());
+        readSurf = dest;
+    }
+    MOZ_ASSERT(readAlignment);
+    MOZ_ASSERT(reinterpret_cast<uintptr_t>(readSurf->GetData()) % readAlignment == 0);
+
+    GLint currentPackAlignment = 0;
+    gl->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, &currentPackAlignment);
+
+    if (currentPackAlignment != readAlignment)
+        gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, readAlignment);
+
+    GLsizei width = dest->GetSize().width;
+    GLsizei height = dest->GetSize().height;
+
+    gl->fReadPixels(0, 0,
+                    width, height,
+                    readFormat, readType,
+                    readSurf->GetData());
+
+    if (currentPackAlignment != readAlignment)
+        gl->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, currentPackAlignment);
+
+    if (readSurf != dest) {
+        MOZ_ASSERT(readFormat == LOCAL_GL_RGBA);
+        MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE);
+        CopyDataSourceSurface(readSurf, dest);
+    }
+
+    // Check if GL is giving back 1.0 alpha for
+    // RGBA reads to RGBA images from no-alpha buffers.
+#ifdef XP_MACOSX
+    if (gl->WorkAroundDriverBugs() &&
+        gl->Vendor() == gl::GLVendor::NVIDIA &&
+        (dest->GetFormat() == SurfaceFormat::R8G8B8A8 ||
+         dest->GetFormat() == SurfaceFormat::B8G8R8A8) &&
+        width && height)
+    {
+        GLint alphaBits = 0;
+        gl->fGetIntegerv(LOCAL_GL_ALPHA_BITS, &alphaBits);
+        if (!alphaBits) {
+            const uint32_t alphaMask = gfxPackedPixelNoPreMultiply(0xff,0,0,0);
+
+            MOZ_ASSERT(dest->GetSize().width * destPixelSize == dest->Stride());
+
+            uint32_t* itr = (uint32_t*)dest->GetData();
+            uint32_t testPixel = *itr;
+            if ((testPixel & alphaMask) != alphaMask) {
+                // We need to set the alpha channel to 1.0 manually.
+                uint32_t* itrEnd = itr + width*height;  // Stride is guaranteed to be width*4.
+
+                for (; itr != itrEnd; itr++) {
+                    *itr |= alphaMask;
+                }
+            }
+        }
+    }
+#endif
+}
+
 static TemporaryRef<DataSourceSurface> YInvertImageSurface(DataSourceSurface* aSurf)
 {
   RefPtr<DataSourceSurface> temp =
     Factory::CreateDataSourceSurfaceWithStride(aSurf->GetSize(),
                                                aSurf->GetFormat(),
                                                aSurf->Stride());
   RefPtr<DrawTarget> dt =
     Factory::CreateDrawTargetForData(BackendType::CAIRO,
@@ -514,34 +707,35 @@ ReadScreenIntoImageSurface(GLContext* gl
 
 
 #define CLEANUP_IF_GLERROR_OCCURRED(x)                                      \
     if (DidGLErrorOccur(x)) {                                               \
         isurf = nullptr;                                                    \
         break;                                                              \
     }
 
-already_AddRefed<gfxImageSurface>
+TemporaryRef<DataSourceSurface>
 GLReadTexImageHelper::ReadTexImage(GLuint aTextureId,
                                    GLenum aTextureTarget,
-                                   const gfxIntSize& aSize,
+                                   const gfx::IntSize& aSize,
     /* ShaderConfigOGL.mFeature */ int aConfig,
                                    bool aYInvert)
 {
     MOZ_ASSERT(aTextureTarget == LOCAL_GL_TEXTURE_2D ||
                aTextureTarget == LOCAL_GL_TEXTURE_EXTERNAL ||
                aTextureTarget == LOCAL_GL_TEXTURE_RECTANGLE_ARB);
 
     mGL->MakeCurrent();
 
     /* Allocate resulting image surface */
-    nsRefPtr<gfxImageSurface> isurf = new gfxImageSurface(aSize, gfxImageFormat::ARGB32);
-    if (!isurf || isurf->CairoStatus()) {
-        return nullptr;
-    }
+    int32_t stride = aSize.width * BytesPerPixel(SurfaceFormat::R8G8B8A8);
+    RefPtr<DataSourceSurface> isurf =
+        Factory::CreateDataSourceSurfaceWithStride(aSize,
+                                                   SurfaceFormat::R8G8B8A8,
+                                                   stride);
 
     GLint oldrb, oldfb, oldprog, oldTexUnit, oldTex;
     GLuint rb, fb;
 
     do {
         mGL->fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING, &oldrb);
         mGL->fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, &oldfb);
         mGL->fGetIntegerv(LOCAL_GL_CURRENT_PROGRAM, &oldprog);
@@ -635,17 +829,17 @@ GLReadTexImageHelper::ReadTexImage(GLuin
 
         mGL->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4);
         CLEANUP_IF_GLERROR_OCCURRED("when drawing texture");
 
         mGL->fDisableVertexAttribArray(1);
         mGL->fDisableVertexAttribArray(0);
 
         /* Read-back draw results */
-        ReadPixelsIntoImageSurface(mGL, isurf);
+        ReadPixelsIntoDataSourceSurface(mGL, isurf);
         CLEANUP_IF_GLERROR_OCCURRED("when reading pixels into surface");
     } while (false);
 
     /* Restore GL state */
 //cleanup:
     mGL->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, oldrb);
     mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, oldfb);
     mGL->fUseProgram(oldprog);
--- a/gfx/gl/GLReadTexImageHelper.h
+++ b/gfx/gl/GLReadTexImageHelper.h
@@ -55,21 +55,21 @@ public:
       *
       * THIS IS EXPENSIVE.  It is ridiculously expensive.  Only do this
       * if you absolutely positively must, and never in any performance
       * critical path.
       *
       * NOTE: aShaderProgram is really mozilla::layers::ShaderProgramType. It is
       * passed as int to eliminate including LayerManagerOGLProgram.h here.
       */
-    already_AddRefed<gfxImageSurface> ReadTexImage(GLuint aTextureId,
-                                                   GLenum aTextureTarget,
-                                                   const gfxIntSize& aSize,
-                           /* ShaderProgramType */ int aShaderProgram,
-                                                   bool aYInvert = false);
+    TemporaryRef<gfx::DataSourceSurface> ReadTexImage(GLuint aTextureId,
+                                                      GLenum aTextureTarget,
+                                                      const gfx::IntSize& aSize,
+                              /* ShaderProgramType */ int aShaderProgram,
+                                                      bool aYInvert = false);
 
 
 };
 
 }
 }
 
 #endif
--- a/gfx/layers/LayerScope.cpp
+++ b/gfx/layers/LayerScope.cpp
@@ -416,27 +416,27 @@ public:
     } PACKED_STRUCT TexturePacket;
 #ifdef _MSC_VER
 #pragma pack(pop)
 #endif
 };
 
 class DebugGLTextureData : public DebugGLData {
 public:
-    DebugGLTextureData(GLContext* cx, void* layerRef, GLuint target, GLenum name, gfxImageSurface* img)
+    DebugGLTextureData(GLContext* cx, void* layerRef, GLuint target, GLenum name, DataSourceSurface* img)
         : DebugGLData(DebugGLData::TextureData, cx),
           mLayerRef(layerRef),
           mTarget(target),
           mName(name),
           mImage(img)
     { }
 
     void *GetLayerRef() const { return mLayerRef; }
     GLuint GetName() const { return mName; }
-    gfxImageSurface* GetImage() const { return mImage; }
+    DataSourceSurface* GetImage() const { return mImage; }
     GLenum GetTextureTarget() const { return mTarget; }
 
     virtual bool Write() {
         DebugGLData::TexturePacket packet;
         char* dataptr = nullptr;
         uint32_t datasize = 0;
         std::auto_ptr<char> compresseddata;
 
@@ -444,23 +444,23 @@ public:
         packet.ptr = static_cast<uint64_t>(mContextAddress);
         packet.layerref = reinterpret_cast<uint64_t>(mLayerRef);
         packet.name = mName;
         packet.format = 0;
         packet.target = mTarget;
         packet.dataFormat = LOCAL_GL_RGBA;
 
         if (mImage) {
-            packet.width = mImage->Width();
-            packet.height = mImage->Height();
+            packet.width = mImage->GetSize().width;
+            packet.height = mImage->GetSize().height;
             packet.stride = mImage->Stride();
-            packet.dataSize = mImage->GetDataSize();
+            packet.dataSize = mImage->GetSize().height * mImage->Stride();
 
-            dataptr = (char*) mImage->Data();
-            datasize = mImage->GetDataSize();
+            dataptr = (char*) mImage->GetData();
+            datasize = packet.dataSize;
 
             compresseddata = std::auto_ptr<char>((char*) moz_malloc(LZ4::maxCompressedSize(datasize)));
             if (compresseddata.get()) {
                 int ndatasize = LZ4::compress(dataptr, datasize, compresseddata.get());
                 if (ndatasize > 0) {
                     datasize = ndatasize;
                     dataptr = compresseddata.get();
 
@@ -492,17 +492,17 @@ public:
 
         return true;
     }
 
 protected:
     void* mLayerRef;
     GLenum mTarget;
     GLuint mName;
-    nsRefPtr<gfxImageSurface> mImage;
+    RefPtr<DataSourceSurface> mImage;
 };
 
 class DebugGLColorData : public DebugGLData {
 public:
     DebugGLColorData(void* layerRef, const gfxRGBA& color, int width, int height)
         : DebugGLData(DebugGLData::ColorData),
           mColor(color.Packed()),
           mSize(width, height)
@@ -717,19 +717,19 @@ SendTextureSource(GLContext* aGLContext,
     } else if (textureTarget == LOCAL_GL_TEXTURE_RECTANGLE) {
         aGLContext->GetUIntegerv(LOCAL_GL_TEXTURE_BINDING_RECTANGLE, &textureId);
     }
 
     gfx::IntSize size = aSource->GetSize();
 
     // By sending 0 to ReadTextureImage rely upon aSource->BindTexture binding
     // texture correctly. textureId is used for tracking in DebugGLTextureData.
-    nsRefPtr<gfxImageSurface> img =
+    RefPtr<DataSourceSurface> img =
         aGLContext->ReadTexImageHelper()->ReadTexImage(0, textureTarget,
-                                                       gfxIntSize(size.width, size.height),
+                                                       size,
                                                        shaderConfig, aFlipY);
 
     gLayerScopeWebSocketManager->AppendDebugData(
         new DebugGLTextureData(aGLContext, aLayerRef, textureTarget,
                                textureId, img));
 }
 
 static void