Bug 1168015 - Dump source image from graphic buffer directly on B2G. r=kamidphish, r=hshih
authorCJKu <cku@mozilla.com>
Thu, 28 May 2015 02:30:00 -0400
changeset 276906 223975f99ca3ec887c80fc009426fffb8720af4d
parent 276905 ff168c00c6bccb763ea81f1d96e3722649d3a8a1
child 276907 8225a3b75df6ec1fecc43ab66855320c6fd924e9
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-beta@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskamidphish, hshih
bugs1168015
milestone41.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 1168015 - Dump source image from graphic buffer directly on B2G. r=kamidphish, r=hshih
gfx/layers/Effects.h
gfx/layers/LayerScope.cpp
gfx/layers/composite/ContentHost.cpp
gfx/layers/composite/ImageHost.cpp
gfx/layers/composite/TiledContentHost.cpp
--- a/gfx/layers/Effects.h
+++ b/gfx/layers/Effects.h
@@ -69,16 +69,17 @@ struct TexturedEffect : public Effect
 
   virtual const char* Name() = 0;
   virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
 
   gfx::Rect mTextureCoords;
   TextureSource* mTexture;
   bool mPremultiplied;
   gfx::Filter mFilter;
+  LayerRenderState mState;
 };
 
 // Support an alpha mask.
 struct EffectMask : public Effect
 {
   EffectMask(TextureSource *aMaskTexture,
              gfx::IntSize aSize,
              const gfx::Matrix4x4 &aMaskTransform)
@@ -243,17 +244,18 @@ struct EffectChain
  * creating an effect that takes several texture sources (like with YCBCR
  * where aFormat would be FOMRAT_YCBCR and each texture source would be
  * a one-channel A8 texture)
  */
 inline TemporaryRef<TexturedEffect>
 CreateTexturedEffect(gfx::SurfaceFormat aFormat,
                      TextureSource* aSource,
                      const gfx::Filter& aFilter,
-                     bool isAlphaPremultiplied)
+                     bool isAlphaPremultiplied,
+                     const LayerRenderState &state = LayerRenderState())
 {
   MOZ_ASSERT(aSource);
   RefPtr<TexturedEffect> result;
   switch (aFormat) {
   case gfx::SurfaceFormat::B8G8R8A8:
   case gfx::SurfaceFormat::B8G8R8X8:
   case gfx::SurfaceFormat::R8G8B8X8:
   case gfx::SurfaceFormat::R5G6B5:
@@ -263,54 +265,59 @@ CreateTexturedEffect(gfx::SurfaceFormat 
   case gfx::SurfaceFormat::YUV:
     result = new EffectYCbCr(aSource, aFilter);
     break;
   default:
     NS_WARNING("unhandled program type");
     break;
   }
 
+  result->mState = state;
+
   return result.forget();
 }
 
 /**
  * Create a textured effect based on aSource format and the presence of
  * aSourceOnWhite.
  *
  * aSourceOnWhite can be null.
  */
 inline TemporaryRef<TexturedEffect>
 CreateTexturedEffect(TextureSource* aSource,
                      TextureSource* aSourceOnWhite,
                      const gfx::Filter& aFilter,
-                     bool isAlphaPremultiplied)
+                     bool isAlphaPremultiplied,
+                     const LayerRenderState &state = LayerRenderState())
 {
   MOZ_ASSERT(aSource);
   if (aSourceOnWhite) {
     MOZ_ASSERT(aSource->GetFormat() == gfx::SurfaceFormat::R8G8B8X8 ||
                aSource->GetFormat() == gfx::SurfaceFormat::B8G8R8X8);
     MOZ_ASSERT(aSource->GetFormat() == aSourceOnWhite->GetFormat());
     return MakeAndAddRef<EffectComponentAlpha>(aSource, aSourceOnWhite, aFilter);
   }
 
   return CreateTexturedEffect(aSource->GetFormat(),
                               aSource,
                               aFilter,
-                              isAlphaPremultiplied);
+                              isAlphaPremultiplied,
+                              state);
 }
 
 /**
  * Create a textured effect based on aSource format.
  *
  * This version excudes the possibility of component alpha.
  */
 inline TemporaryRef<TexturedEffect>
 CreateTexturedEffect(TextureSource *aTexture,
-                     const gfx::Filter& aFilter)
+                     const gfx::Filter& aFilter,
+                     const LayerRenderState &state = LayerRenderState())
 {
-  return CreateTexturedEffect(aTexture, nullptr, aFilter, true);
+  return CreateTexturedEffect(aTexture, nullptr, aFilter, true, state);
 }
 
 
 } // namespace layers
 } // namespace mozilla
 
 #endif
--- a/gfx/layers/LayerScope.cpp
+++ b/gfx/layers/LayerScope.cpp
@@ -265,16 +265,97 @@ public:
 
         return WriteToStream(packet);
     }
 
 protected:
     int64_t mFrameStamp;
 };
 
+#ifdef MOZ_WIDGET_GONK
+// B2G optimization.
+class DebugGLGraphicBuffer final: public DebugGLData {
+public:
+    DebugGLGraphicBuffer(void *layerRef,
+                         GLenum target,
+                         GLuint name,
+                         const LayerRenderState &aState)
+        : DebugGLData(Packet::TEXTURE),
+          mLayerRef(reinterpret_cast<uint64_t>(layerRef)),
+          mTarget(target),
+          mName(name),
+          mState(aState)
+    {
+    }
+
+    virtual bool Write() override {
+        return WriteToStream(mPacket);
+    }
+
+    bool TryPack() {
+        android::sp<android::GraphicBuffer> buffer = mState.mSurface;
+        MOZ_ASSERT(buffer.get());
+
+        mPacket.set_type(mDataType);
+        TexturePacket* tp = mPacket.mutable_texture();
+        tp->set_layerref(mLayerRef);
+        tp->set_name(mName);
+        tp->set_target(mTarget);
+
+        int format = buffer->getPixelFormat();
+        if (HAL_PIXEL_FORMAT_RGBA_8888 != format &&
+            HAL_PIXEL_FORMAT_RGBX_8888 != format) {
+            return false;
+        }
+
+        uint8_t* grallocData;
+        if (BAD_VALUE == buffer->lock(GRALLOC_USAGE_SW_READ_OFTEN |
+                                       GRALLOC_USAGE_SW_WRITE_NEVER,
+                                       reinterpret_cast<void**>(&grallocData))) {
+            return false;
+        }
+
+        int32_t stride = buffer->getStride() * 4;
+        int32_t height = buffer->getHeight();
+        int32_t width = buffer->getWidth();
+        int32_t sourceSize = stride * height;
+        bool    ret = false;
+
+        if (sourceSize > 0) {
+            auto compressedData = MakeUnique<char[]>(LZ4::maxCompressedSize(sourceSize));
+            int compressedSize = LZ4::compress((char*)grallocData,
+                                               sourceSize,
+                                               compressedData.get());
+
+            if (compressedSize > 0) {
+                uint32_t format = mState.FormatRBSwapped() ?
+                  LOCAL_GL_BGRA : LOCAL_GL_RGBA;
+                tp->set_dataformat(format);
+                tp->set_dataformat((1 << 16 | tp->dataformat()));
+                tp->set_width(width);
+                tp->set_height(height);
+                tp->set_stride(stride);
+                tp->set_data(compressedData.get(), compressedSize);
+                ret = true;
+            }
+        }
+
+        buffer->unlock();
+        return ret;
+    }
+
+private:
+    uint64_t mLayerRef;
+    GLenum mTarget;
+    GLuint mName;
+    const LayerRenderState &mState;
+    Packet mPacket;
+};
+#endif
+
 class DebugGLTextureData final: public DebugGLData {
 public:
     DebugGLTextureData(GLContext* cx,
                        void* layerRef,
                        GLenum target,
                        GLuint name,
                        DataSourceSurface* img)
         : DebugGLData(Packet::TEXTURE),
@@ -603,35 +684,43 @@ public:
     static void SetLayersTreeSendable(bool aSet) {sLayersTreeSendable = aSet;}
 
     static void SetLayersBufferSendable(bool aSet) {sLayersBufferSendable = aSet;}
 
     static bool GetLayersTreeSendable() {return sLayersTreeSendable;}
 
     static void ClearTextureIdList();
 
-    static bool IsTextureIdContainsInList(GLuint aTextureId);
 
 // Sender private functions
 private:
     static void SendColor(void* aLayerRef,
                           const gfxRGBA& aColor,
                           int aWidth,
                           int aHeight);
     static void SendTextureSource(GLContext* aGLContext,
                                   void* aLayerRef,
                                   TextureSourceOGL* aSource,
+                                  GLuint aTexID,
                                   bool aFlipY);
+#ifdef MOZ_WIDGET_GONK
+    static bool SendGraphicBuffer(void* aLayerRef,
+                                  TextureSourceOGL* aSource,
+                                  GLuint aTexID,
+                                  const TexturedEffect* aEffect);
+#endif
     static void SendTexturedEffect(GLContext* aGLContext,
                                    void* aLayerRef,
                                    const TexturedEffect* aEffect);
     static void SendYCbCrEffect(GLContext* aGLContext,
                                 void* aLayerRef,
                                 const EffectYCbCr* aEffect);
-
+    static GLuint GetTextureID(GLContext* aGLContext,
+                               TextureSourceOGL* aSource);
+    static bool IsTextureIdContainsInList(GLuint aTextureId);
 // Data fields
 private:
     static bool sLayersTreeSendable;
     static bool sLayersBufferSendable;
     static std::list<GLuint> sTextureIdList;
 };
 
 bool SenderHelper::sLayersTreeSendable = true;
@@ -709,92 +798,145 @@ SenderHelper::SendColor(void* aLayerRef,
                         const gfxRGBA& aColor,
                         int aWidth,
                         int aHeight)
 {
     WebSocketHelper::GetSocketManager()->AppendDebugData(
         new DebugGLColorData(aLayerRef, aColor, aWidth, aHeight));
 }
 
+GLuint
+SenderHelper::GetTextureID(GLContext* aGLContext,
+                           TextureSourceOGL* aSource) {
+    GLenum textureTarget = aSource->GetTextureTarget();
+    aSource->BindTexture(LOCAL_GL_TEXTURE0, gfx::Filter::LINEAR);
+
+    GLuint texID = 0;
+    // This is horrid hack. It assumes that aGLContext matches the context
+    // aSource has bound to.
+    if (textureTarget == LOCAL_GL_TEXTURE_2D) {
+        aGLContext->GetUIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, &texID);
+    } else if (textureTarget == LOCAL_GL_TEXTURE_EXTERNAL) {
+        aGLContext->GetUIntegerv(LOCAL_GL_TEXTURE_BINDING_EXTERNAL, &texID);
+    } else if (textureTarget == LOCAL_GL_TEXTURE_RECTANGLE) {
+        aGLContext->GetUIntegerv(LOCAL_GL_TEXTURE_BINDING_RECTANGLE, &texID);
+    }
+
+    return texID;
+}
+
 void
 SenderHelper::SendTextureSource(GLContext* aGLContext,
                                 void* aLayerRef,
                                 TextureSourceOGL* aSource,
+                                GLuint aTexID,
                                 bool aFlipY)
 {
     MOZ_ASSERT(aGLContext);
     if (!aGLContext) {
         return;
     }
 
     GLenum textureTarget = aSource->GetTextureTarget();
     ShaderConfigOGL config = ShaderConfigFromTargetAndFormat(textureTarget,
                                                              aSource->GetFormat());
     int shaderConfig = config.mFeatures;
 
-    aSource->BindTexture(LOCAL_GL_TEXTURE0, gfx::Filter::LINEAR);
+    gfx::IntSize size = aSource->GetSize();
+
+    // By sending 0 to ReadTextureImage rely upon aSource->BindTexture binding
+    // texture correctly. texID is used for tracking in DebugGLTextureData.
+    RefPtr<DataSourceSurface> img =
+        aGLContext->ReadTexImageHelper()->ReadTexImage(0, textureTarget,
+                                                         size,
+                                                         shaderConfig, aFlipY);
+    WebSocketHelper::GetSocketManager()->AppendDebugData(
+        new DebugGLTextureData(aGLContext, aLayerRef, textureTarget,
+                               aTexID, img));
 
-    GLuint textureId = 0;
-    // This is horrid hack. It assumes that aGLContext matches the context
-    // aSource has bound to.
-    if (textureTarget == LOCAL_GL_TEXTURE_2D) {
-        aGLContext->GetUIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, &textureId);
-    } else if (textureTarget == LOCAL_GL_TEXTURE_EXTERNAL) {
-        aGLContext->GetUIntegerv(LOCAL_GL_TEXTURE_BINDING_EXTERNAL, &textureId);
-    } else if (textureTarget == LOCAL_GL_TEXTURE_RECTANGLE) {
-        aGLContext->GetUIntegerv(LOCAL_GL_TEXTURE_BINDING_RECTANGLE, &textureId);
+    sTextureIdList.push_back(aTexID);
+}
+
+#ifdef MOZ_WIDGET_GONK
+bool
+SenderHelper::SendGraphicBuffer(void* aLayerRef,
+                                TextureSourceOGL* aSource,
+                                GLuint aTexID,
+                                const TexturedEffect* aEffect) {
+    if (!aEffect->mState.mSurface.get()) {
+        return false;
     }
 
-    if (!IsTextureIdContainsInList(textureId)) {
-      gfx::IntSize size = aSource->GetSize();
+    GLenum target = aSource->GetTextureTarget();
+    mozilla::UniquePtr<DebugGLGraphicBuffer> package =
+        MakeUnique<DebugGLGraphicBuffer>(aLayerRef, target, aTexID, aEffect->mState);
 
-      // By sending 0 to ReadTextureImage rely upon aSource->BindTexture binding
-      // texture correctly. textureId is used for tracking in DebugGLTextureData.
-      RefPtr<DataSourceSurface> img =
-          aGLContext->ReadTexImageHelper()->ReadTexImage(0, textureTarget,
-                                                         size,
-                                                         shaderConfig, aFlipY);
-      sTextureIdList.push_back(textureId);
-      WebSocketHelper::GetSocketManager()->AppendDebugData(
-          new DebugGLTextureData(aGLContext, aLayerRef, textureTarget,
-                               textureId, img));
+    if (!package->TryPack()) {
+        return false;
     }
+
+    // Transfer ownership to SocketManager.
+    WebSocketHelper::GetSocketManager()->AppendDebugData(package.release());
+    sTextureIdList.push_back(aTexID);
+    return true;
 }
+#endif
 
 void
 SenderHelper::SendTexturedEffect(GLContext* aGLContext,
                                  void* aLayerRef,
                                  const TexturedEffect* aEffect)
 {
     TextureSourceOGL* source = aEffect->mTexture->AsSourceOGL();
-    if (!source)
+    if (!source) {
         return;
+    }
 
-    bool flipY = false;
-    SendTextureSource(aGLContext, aLayerRef, source, flipY);
+    GLuint texID = GetTextureID(aGLContext, source);
+    if (IsTextureIdContainsInList(texID)) {
+        return;
+    }
+
+#ifdef MOZ_WIDGET_GONK
+    if (SendGraphicBuffer(aLayerRef, source, texID, aEffect)) {
+         return;
+    }
+#endif
+    // Render to texture and read pixels back.
+    SendTextureSource(aGLContext, aLayerRef, source, texID, false);
 }
 
 void
 SenderHelper::SendYCbCrEffect(GLContext* aGLContext,
                               void* aLayerRef,
                               const EffectYCbCr* aEffect)
 {
     TextureSource* sourceYCbCr = aEffect->mTexture;
     if (!sourceYCbCr)
         return;
 
     const int Y = 0, Cb = 1, Cr = 2;
     TextureSourceOGL* sourceY =  sourceYCbCr->GetSubSource(Y)->AsSourceOGL();
     TextureSourceOGL* sourceCb = sourceYCbCr->GetSubSource(Cb)->AsSourceOGL();
     TextureSourceOGL* sourceCr = sourceYCbCr->GetSubSource(Cr)->AsSourceOGL();
 
-    bool flipY = false;
-    SendTextureSource(aGLContext, aLayerRef, sourceY,  flipY);
-    SendTextureSource(aGLContext, aLayerRef, sourceCb, flipY);
-    SendTextureSource(aGLContext, aLayerRef, sourceCr, flipY);
+    GLuint texID = GetTextureID(aGLContext, sourceY);
+    if (!IsTextureIdContainsInList(texID)) {
+        SendTextureSource(aGLContext, aLayerRef, sourceY, texID, false);
+    }
+
+    texID = GetTextureID(aGLContext, sourceCb);
+    if (!IsTextureIdContainsInList(texID)) {
+        SendTextureSource(aGLContext, aLayerRef, sourceCb, texID, false);
+    }
+
+    texID = GetTextureID(aGLContext, sourceCr);
+    if (!IsTextureIdContainsInList(texID)) {
+        SendTextureSource(aGLContext, aLayerRef, sourceCr, texID, false);
+    }
 }
 
 void
 SenderHelper::SendEffectChain(GLContext* aGLContext,
                               const EffectChain& aEffectChain,
                               int aWidth,
                               int aHeight)
 {
--- a/gfx/layers/composite/ContentHost.cpp
+++ b/gfx/layers/composite/ContentHost.cpp
@@ -58,17 +58,18 @@ ContentHostTexture::Composite(EffectChai
     mTextureSourceOnWhite = nullptr;
   }
   if (mTextureHostOnWhite && !mTextureHostOnWhite->BindTextureSource(mTextureSourceOnWhite)) {
     return;
   }
 
   RefPtr<TexturedEffect> effect = CreateTexturedEffect(mTextureSource.get(),
                                                        mTextureSourceOnWhite.get(),
-                                                       aFilter, true);
+                                                       aFilter, true,
+                                                       GetRenderState());
   if (!effect) {
     return;
   }
 
   aEffectChain.mPrimaryEffect = effect;
 
   nsIntRegion tmpRegion;
   const nsIntRegion* renderRegion;
@@ -455,17 +456,18 @@ ContentHostTexture::GenEffect(const gfx:
   if (!mTextureHostOnWhite) {
     mTextureSourceOnWhite = nullptr;
   }
   if (mTextureHostOnWhite && !mTextureHostOnWhite->BindTextureSource(mTextureSourceOnWhite)) {
     return nullptr;
   }
   return CreateTexturedEffect(mTextureSource.get(),
                               mTextureSourceOnWhite.get(),
-                              aFilter, true);
+                              aFilter, true,
+                              GetRenderState());
 }
 
 TemporaryRef<gfx::DataSourceSurface>
 ContentHostTexture::GetAsSurface()
 {
   if (!mTextureHost) {
     return nullptr;
   }
--- a/gfx/layers/composite/ImageHost.cpp
+++ b/gfx/layers/composite/ImageHost.cpp
@@ -101,17 +101,18 @@ ImageHost::Composite(EffectChain& aEffec
     MOZ_ASSERT(false);
     return;
   }
 
   bool isAlphaPremultiplied = !(mFrontBuffer->GetFlags() & TextureFlags::NON_PREMULTIPLIED);
   RefPtr<TexturedEffect> effect = CreateTexturedEffect(mFrontBuffer->GetFormat(),
                                                        mTextureSource.get(),
                                                        aFilter,
-                                                       isAlphaPremultiplied);
+                                                       isAlphaPremultiplied,
+                                                       GetRenderState());
   if (!effect) {
     return;
   }
 
   aEffectChain.mPrimaryEffect = effect;
   IntSize textureSize = mTextureSource->GetSize();
   gfx::Rect gfxPictureRect
     = mHasPictureRect ? gfx::Rect(0, 0, mPictureRect.width, mPictureRect.height)
@@ -293,17 +294,18 @@ ImageHost::GenEffect(const gfx::Filter& 
   }
   bool isAlphaPremultiplied = true;
   if (mFrontBuffer->GetFlags() & TextureFlags::NON_PREMULTIPLIED)
     isAlphaPremultiplied = false;
 
   return CreateTexturedEffect(mFrontBuffer->GetFormat(),
                               mTextureSource,
                               aFilter,
-                              isAlphaPremultiplied);
+                              isAlphaPremultiplied,
+                              GetRenderState());
 }
 
 #ifdef MOZ_WIDGET_GONK
 ImageHostOverlay::ImageHostOverlay(const TextureInfo& aTextureInfo)
   : CompositableHost(aTextureInfo)
   , mHasPictureRect(false)
 {
 }
--- a/gfx/layers/composite/TiledContentHost.cpp
+++ b/gfx/layers/composite/TiledContentHost.cpp
@@ -463,17 +463,22 @@ TiledContentHost::RenderTile(TileHost& a
   if (!aTile.mTextureHost->BindTextureSource(aTile.mTextureSource)) {
     return;
   }
 
   if (aTile.mTextureHostOnWhite && !aTile.mTextureHostOnWhite->BindTextureSource(aTile.mTextureSourceOnWhite)) {
     return;
   }
 
-  RefPtr<TexturedEffect> effect = CreateTexturedEffect(aTile.mTextureSource, aTile.mTextureSourceOnWhite, aFilter, true);
+  RefPtr<TexturedEffect> effect =
+    CreateTexturedEffect(aTile.mTextureSource,
+                         aTile.mTextureSourceOnWhite,
+                         aFilter,
+                         true,
+                         aTile.mTextureHost->GetRenderState());
   if (!effect) {
     return;
   }
 
   aEffectChain.mPrimaryEffect = effect;
 
   nsIntRegionRectIterator it(aScreenRegion);
   for (const IntRect* rect = it.Next(); rect != nullptr; rect = it.Next()) {