Bug 1186000 - Support screen mirroring to HDMI display on gonk r=mwu,mattwoodrow
authorSotaro Ikeda <sikeda@mozilla.com>
Sat, 08 Aug 2015 13:50:47 -0700
changeset 288623 203271f48e46a124b92bc0fcba57d5f0c9c4888b
parent 288622 ae85c14a2bae38e65b9cfa04b7844574d9a31f86
child 288624 d10584967bb9d020aeecaa212241003d4144e959
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmwu, mattwoodrow
bugs1186000
milestone42.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 1186000 - Support screen mirroring to HDMI display on gonk r=mwu,mattwoodrow
b2g/app/b2g.js
gfx/layers/composite/LayerManagerComposite.cpp
gfx/layers/composite/LayerManagerComposite.h
gfx/thebes/gfxPrefs.h
widget/gonk/HwcComposer2D.cpp
widget/gonk/hwchal/HwcHAL.cpp
widget/gonk/hwchal/HwcHAL.h
widget/gonk/hwchal/HwcHALBase.h
widget/gonk/hwchal/HwcICS.cpp
widget/gonk/hwchal/HwcICS.h
widget/gonk/nsScreenManagerGonk.cpp
widget/gonk/nsScreenManagerGonk.h
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -991,16 +991,19 @@ pref("gfx.canvas.azure.accelerated", tru
 pref("gfx.canvas.skiagl.dynamic-cache", true);
 
 // Limit skia to canvases the size of the device screen or smaller
 pref("gfx.canvas.max-size-for-skia-gl", -1);
 
 // enable fence with readpixels for SurfaceStream
 pref("gfx.gralloc.fence-with-readpixels", true);
 
+// enable screen mirroring to external display
+pref("gfx.screen-mirroring.enabled", true);
+
 // The url of the page used to display network error details.
 pref("b2g.neterror.url", "net_error.html");
 
 // The origin used for the shared themes uri space.
 pref("b2g.theme.origin", "app://theme.gaiamobile.org");
 pref("dom.mozApps.themable", true);
 pref("dom.mozApps.selected_theme", "default_theme.gaiamobile.org");
 
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -50,21 +50,27 @@
 #include "nsISupportsImpl.h"            // for Layer::AddRef, etc
 #include "nsIWidget.h"                  // for nsIWidget
 #include "nsPoint.h"                    // for nsIntPoint
 #include "nsRect.h"                     // for mozilla::gfx::IntRect
 #include "nsRegion.h"                   // for nsIntRegion, etc
 #ifdef MOZ_WIDGET_ANDROID
 #include <android/log.h>
 #include "AndroidBridge.h"
+#endif
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
 #include "opengl/CompositorOGL.h"
 #include "GLContextEGL.h"
 #include "GLContextProvider.h"
 #include "ScopedGLHelpers.h"
 #endif
+#ifdef MOZ_WIDGET_GONK
+#include "nsScreenManagerGonk.h"
+#include "nsWindow.h"
+#endif
 #include "GeckoProfiler.h"
 #include "TextRenderer.h"               // for TextRenderer
 
 class gfxContext;
 
 namespace mozilla {
 namespace layers {
 
@@ -297,17 +303,17 @@ LayerManagerComposite::EndTransaction(co
     // The results of our drawing always go directly into a pixel buffer,
     // so we don't need to pass any global transform here.
     mRoot->ComputeEffectiveTransforms(gfx::Matrix4x4());
 
     nsIntRegion opaque;
     ApplyOcclusionCulling(mRoot, opaque);
 
     Render();
-#ifdef MOZ_WIDGET_ANDROID
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
     RenderToPresentationSurface();
 #endif
     mGeometryChanged = false;
   } else {
     // Modified layer tree
     mGeometryChanged = true;
   }
 
@@ -775,17 +781,17 @@ LayerManagerComposite::Render()
     composer2D->Render(mCompositor->GetWidget());
   }
 
   mCompositor->GetWidget()->PostRender(this);
 
   RecordFrame();
 }
 
-#ifdef MOZ_WIDGET_ANDROID
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
 class ScopedCompositorProjMatrix {
 public:
   ScopedCompositorProjMatrix(CompositorOGL* aCompositor, const Matrix4x4& aProjMatrix):
     mCompositor(aCompositor),
     mOriginalProjMatrix(mCompositor->GetProjMatrix())
   {
     mCompositor->SetProjMatrix(aProjMatrix);
   }
@@ -849,16 +855,17 @@ public:
   }
 private:
   GLContextEGL* const mContext;
 };
 
 void
 LayerManagerComposite::RenderToPresentationSurface()
 {
+#ifdef MOZ_WIDGET_ANDROID
   if (!AndroidBridge::Bridge()) {
     return;
   }
 
   void* window = AndroidBridge::Bridge()->GetPresentationWindow();
 
   if (!window) {
     return;
@@ -881,76 +888,124 @@ LayerManagerComposite::RenderToPresentat
   GLContextEGL* egl = GLContextEGL::Cast(gl);
 
   if (!egl) {
     return;
   }
 
   const IntSize windowSize = AndroidBridge::Bridge()->GetNativeWindowSize(window);
 
+#elif defined(MOZ_WIDGET_GONK)
+  CompositorOGL* compositor = static_cast<CompositorOGL*>(mCompositor.get());
+  nsScreenGonk* screen = static_cast<nsWindow*>(mCompositor->GetWidget())->GetScreen();
+  if (!screen->IsPrimaryScreen()) {
+    // Only primary screen support mirroring
+    return;
+  }
+
+  nsWindow* mirrorScreenWidget = screen->GetMirroringWidget();
+  if (!mirrorScreenWidget) {
+    // No mirroring
+    return;
+  }
+
+  nsScreenGonk* mirrorScreen = mirrorScreenWidget->GetScreen();
+  if (!mirrorScreen->GetTopWindows().IsEmpty()) {
+    return;
+  }
+
+  EGLSurface surface = mirrorScreen->GetEGLSurface();
+  if (surface == LOCAL_EGL_NO_SURFACE) {
+    // Create GLContext
+    nsRefPtr<GLContext> gl = gl::GLContextProvider::CreateForWindow(mirrorScreenWidget);
+    mirrorScreenWidget->SetNativeData(NS_NATIVE_OPENGL_CONTEXT,
+                                      reinterpret_cast<uintptr_t>(gl.get()));
+    surface = mirrorScreen->GetEGLSurface();
+    if (surface == LOCAL_EGL_NO_SURFACE) {
+      // Failed to create EGLSurface
+      return;
+    }
+  }
+  GLContext* gl = compositor->gl();
+  GLContextEGL* egl = GLContextEGL::Cast(gl);
+  const IntSize windowSize = mirrorScreen->GetNaturalBounds().Size();
+#endif
+
   if ((windowSize.width <= 0) || (windowSize.height <= 0)) {
     return;
   }
 
+  ScreenRotation rotation = compositor->GetScreenRotation();
+
   const int actualWidth = windowSize.width;
   const int actualHeight = windowSize.height;
 
   const gfx::IntSize originalSize = compositor->GetDestinationSurfaceSize();
+  const nsIntRect originalRect = nsIntRect(0, 0, originalSize.width, originalSize.height);
 
-  const int pageWidth = originalSize.width;
-  const int pageHeight = originalSize.height;
+  int pageWidth = originalSize.width;
+  int pageHeight = originalSize.height;
+  if (rotation == ROTATION_90 || rotation == ROTATION_270) {
+    pageWidth = originalSize.height;
+    pageHeight = originalSize.width;
+  }
 
   float scale = 1.0;
 
   if ((pageWidth > actualWidth) || (pageHeight > actualHeight)) {
     const float scaleWidth = (float)actualWidth / (float)pageWidth;
     const float scaleHeight = (float)actualHeight / (float)pageHeight;
     scale = scaleWidth <= scaleHeight ? scaleWidth : scaleHeight;
   }
 
   const gfx::IntSize actualSize(actualWidth, actualHeight);
   ScopedCompostitorSurfaceSize overrideSurfaceSize(compositor, actualSize);
 
   const ScreenPoint offset((actualWidth - (int)(scale * pageWidth)) / 2, 0);
-  ScopedCompositorRenderOffset overrideRenderOffset(compositor, offset);
   ScopedContextSurfaceOverride overrideSurface(egl, surface);
 
+  Matrix viewMatrix = ComputeTransformForRotation(originalRect,
+                                                  rotation);
+  viewMatrix.Invert(); // unrotate
+  viewMatrix.PostScale(scale, scale);
+  viewMatrix.PostTranslate(offset.x, offset.y);
+  Matrix4x4 matrix = Matrix4x4::From2D(viewMatrix);
+
+  mRoot->ComputeEffectiveTransforms(matrix);
+  nsIntRegion opaque;
+  ApplyOcclusionCulling(mRoot, opaque);
+
   nsIntRegion invalid;
   Rect bounds(0.0f, 0.0f, scale * pageWidth, (float)actualHeight);
   Rect rect, actualBounds;
 
   mCompositor->BeginFrame(invalid, nullptr, bounds, &rect, &actualBounds);
 
-  // Override the projection matrix since the presentation frame buffer
-  // is probably not the same size as the device frame buffer. The override
-  // projection matrix also scales the content to fit into the presentation
-  // frame buffer.
-  Matrix viewMatrix;
-  viewMatrix.PreTranslate(-1.0, 1.0);
-  viewMatrix.PreScale((2.0f * scale) / (float)actualWidth, (2.0f * scale) / (float)actualHeight);
-  viewMatrix.PreScale(1.0f, -1.0f);
-  viewMatrix.PreTranslate((int)((float)offset.x / scale), offset.y);
-
-  Matrix4x4 projMatrix = Matrix4x4::From2D(viewMatrix);
-
-  ScopedCompositorProjMatrix overrideProjMatrix(compositor, projMatrix);
-
   // The Java side of Fennec sets a scissor rect that accounts for
   // chrome such as the URL bar. Override that so that the entire frame buffer
   // is cleared.
-  ScopedScissorRect screen(egl, 0, 0, actualWidth, actualHeight);
+  ScopedScissorRect scissorRect(egl, 0, 0, actualWidth, actualHeight);
   egl->fClearColor(0.0, 0.0, 0.0, 0.0);
   egl->fClear(LOCAL_GL_COLOR_BUFFER_BIT);
 
-  const IntRect clipRect = IntRect(0, 0, (int)(scale * pageWidth), actualHeight);
+  const IntRect clipRect = IntRect(0, 0, actualWidth, actualHeight);
+
   RootLayer()->Prepare(RenderTargetPixel::FromUntyped(clipRect));
   RootLayer()->RenderLayer(clipRect);
 
   mCompositor->EndFrame();
-  mCompositor->SetDispAcquireFence(mRoot);
+  mCompositor->SetDispAcquireFence(mRoot); // Call after EndFrame()
+
+#ifdef MOZ_WIDGET_GONK
+  nsRefPtr<Composer2D> composer2D;
+  composer2D = mCompositor->GetWidget()->GetComposer2D();
+  if (composer2D) {
+    composer2D->Render(mirrorScreenWidget);
+  }
+#endif
 }
 #endif
 
 static void
 SubtractTransformedRegion(nsIntRegion& aRegion,
                           const nsIntRegion& aRegionToSubtract,
                           const Matrix4x4& aTransform)
 {
--- a/gfx/layers/composite/LayerManagerComposite.h
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -296,17 +296,17 @@ private:
                                              nsIntRegion& aScreenRegion,
                                              nsIntRegion& aLowPrecisionScreenRegion,
                                              const gfx::Matrix4x4& aTransform);
 
   /**
    * Render the current layer tree to the active target.
    */
   void Render();
-#ifdef MOZ_WIDGET_ANDROID
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
   void RenderToPresentationSurface();
 #endif
 
   /**
    * Render debug overlays such as the FPS/FrameCounter above the frame.
    */
   void RenderDebugOverlay(const gfx::Rect& aBounds);
 
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -258,16 +258,17 @@ private:
   DECL_GFX_PREF(Once, "gfx.vsync.compositor",                  VsyncAlignedCompositor, bool, false);
   // On b2g, in really bad cases, I've seen up to 80 ms delays between touch events and the main thread
   // processing them. So 80 ms / 16 = 5 vsync events. Double it up just to be on the safe side, so 10.
   DECL_GFX_PREF(Once, "gfx.vsync.compositor.unobserve-count",  CompositorUnobserveCount, int32_t, 10);
   // Use vsync events generated by hardware
   DECL_GFX_PREF(Once, "gfx.vsync.hw-vsync.enabled",            HardwareVsyncEnabled, bool, false);
   DECL_GFX_PREF(Once, "gfx.vsync.refreshdriver",               VsyncAlignedRefreshDriver, bool, false);
   DECL_GFX_PREF(Once, "gfx.work-around-driver-bugs",           WorkAroundDriverBugs, bool, true);
+  DECL_GFX_PREF(Once, "gfx.screen-mirroring.enabled",          ScreenMirroringEnabled, bool, false);
 
   DECL_GFX_PREF(Live, "gl.msaa-level",                         MSAALevel, uint32_t, 2);
   DECL_GFX_PREF(Live, "gl.require-hardware",                   RequireHardwareGL, bool, false);
 
   DECL_GFX_PREF(Once, "image.cache.size",                      ImageCacheSize, int32_t, 5*1024*1024);
   DECL_GFX_PREF(Once, "image.cache.timeweight",                ImageCacheTimeWeight, int32_t, 500);
   DECL_GFX_PREF(Live, "image.decode-immediately.enabled",      ImageDecodeImmediatelyEnabled, bool, false);
   DECL_GFX_PREF(Live, "image.downscale-during-decode.enabled", ImageDownscaleDuringDecodeEnabled, bool, true);
--- a/widget/gonk/HwcComposer2D.cpp
+++ b/widget/gonk/HwcComposer2D.cpp
@@ -746,16 +746,19 @@ HwcComposer2D::Render(nsIWidget* aWidget
         return false;
     }
 
     if (mPrepared) {
         // No mHwc prepare, if already prepared in current draw cycle
         mList->hwLayers[mList->numHwLayers - 1].handle = dispSurface->lastHandle;
         mList->hwLayers[mList->numHwLayers - 1].acquireFenceFd = dispSurface->GetPrevDispAcquireFd();
     } else {
+        // Update screen rect to handle a case that TryRenderWithHwc() is not called.
+        mScreenRect = screen->GetNaturalBounds();
+
         mList->flags = HWC_GEOMETRY_CHANGED;
         mList->numHwLayers = 2;
         mList->hwLayers[0].hints = 0;
         mList->hwLayers[0].compositionType = HWC_FRAMEBUFFER;
         mList->hwLayers[0].flags = HWC_SKIP_LAYER;
         mList->hwLayers[0].backgroundColor = {0};
         mList->hwLayers[0].acquireFenceFd = -1;
         mList->hwLayers[0].releaseFenceFd = -1;
@@ -768,17 +771,18 @@ HwcComposer2D::Render(nsIWidget* aWidget
 }
 
 void
 HwcComposer2D::Prepare(buffer_handle_t dispHandle, int fence, nsScreenGonk* screen)
 {
     if (mPrepared) {
         LOGE("Multiple hwc prepare calls!");
     }
-    mHal->Prepare(mList, screen->GetDisplayType(), dispHandle, fence);
+    hwc_rect_t dispRect = {0, 0, mScreenRect.width, mScreenRect.height};
+    mHal->Prepare(mList, screen->GetDisplayType(), dispRect, dispHandle, fence);
     mPrepared = true;
 }
 
 bool
 HwcComposer2D::Commit(nsScreenGonk* aScreen)
 {
     for (uint32_t j=0; j < (mList->numHwLayers - 1); j++) {
         mList->hwLayers[j].acquireFenceFd = -1;
--- a/widget/gonk/hwchal/HwcHAL.cpp
+++ b/widget/gonk/hwchal/HwcHAL.cpp
@@ -27,18 +27,16 @@ HwcHAL::HwcHAL()
 {
     // Some HALs don't want to open hwc twice.
     // If GetDisplay already load hwc module, we don't need to load again
     mHwc = (HwcDevice*)GetGonkDisplay()->GetHWCDevice();
     if (!mHwc) {
         printf_stderr("HwcHAL Error: Cannot load hwcomposer");
         return;
     }
-
-    GetHwcAttributes();
 }
 
 HwcHAL::~HwcHAL()
 {
     mHwc = nullptr;
 }
 
 bool
@@ -74,16 +72,17 @@ int
 HwcHAL::ResetHwc()
 {
     return Set(nullptr, HWC_DISPLAY_PRIMARY);
 }
 
 int
 HwcHAL::Prepare(HwcList *aList,
                 uint32_t aDisp,
+                hwc_rect_t aDispRect,
                 buffer_handle_t aHandle,
                 int aFenceFd)
 {
     MOZ_ASSERT(mHwc);
     if (!mHwc) {
         printf_stderr("HwcHAL Error: HwcDevice doesn't exist. A fence might be leaked.");
         return -1;
     }
@@ -98,18 +97,18 @@ HwcHAL::Prepare(HwcList *aList,
 
     const auto idx = aList->numHwLayers - 1;
     aList->hwLayers[idx].hints = 0;
     aList->hwLayers[idx].flags = 0;
     aList->hwLayers[idx].transform = 0;
     aList->hwLayers[idx].handle = aHandle;
     aList->hwLayers[idx].blending = HWC_BLENDING_PREMULT;
     aList->hwLayers[idx].compositionType = HWC_FRAMEBUFFER_TARGET;
-    SetCrop(aList->hwLayers[idx], mHwcRect);
-    aList->hwLayers[idx].displayFrame = mHwcRect;
+    SetCrop(aList->hwLayers[idx], aDispRect);
+    aList->hwLayers[idx].displayFrame = aDispRect;
     aList->hwLayers[idx].visibleRegionScreen.numRects = 1;
     aList->hwLayers[idx].visibleRegionScreen.rects = &aList->hwLayers[idx].displayFrame;
     aList->hwLayers[idx].acquireFenceFd = aFenceFd;
     aList->hwLayers[idx].releaseFenceFd = -1;
 #if ANDROID_VERSION >= 18
     aList->hwLayers[idx].planeAlpha = 0xFF;
 #endif
     return mHwc->prepare(mHwc, HWC_NUM_DISPLAY_TYPES, displays);
@@ -190,30 +189,16 @@ HwcHAL::RegisterHwcEventCallback(const H
     // with JellyBean.
 #if (ANDROID_VERSION == 19 || ANDROID_VERSION >= 21)
     return true;
 #else
     return false;
 #endif
 }
 
-void
-HwcHAL::GetHwcAttributes()
-{
-    int32_t values[2];
-    const uint32_t attrs[] = {
-        HWC_DISPLAY_WIDTH,
-        HWC_DISPLAY_HEIGHT,
-        HWC_DISPLAY_NO_ATTRIBUTE
-    };
-
-    mHwc->getDisplayAttributes(mHwc, 0, 0, attrs, values);
-    mHwcRect = {0, 0, values[0], values[1]};
-}
-
 uint32_t
 HwcHAL::GetAPIVersion() const
 {
     if (!mHwc) {
         // default value: HWC_MODULE_API_VERSION_0_1
         return 1;
     }
     return mHwc->common.version;
--- a/widget/gonk/hwchal/HwcHAL.h
+++ b/widget/gonk/hwchal/HwcHAL.h
@@ -26,49 +26,45 @@ namespace mozilla {
 class HwcHAL final : public HwcHALBase {
 public:
     explicit HwcHAL();
 
     virtual ~HwcHAL();
 
     virtual bool HasHwc() const override { return static_cast<bool>(mHwc); }
 
-    virtual const hwc_rect_t GetHwcRect() const override { return mHwcRect; }
-
     virtual void SetEGLInfo(hwc_display_t aDpy,
                             hwc_surface_t aSur) override { }
 
     virtual bool Query(QueryType aType) override;
 
     virtual int Set(HwcList *aList,
                     uint32_t aDisp) override;
 
     virtual int ResetHwc() override;
 
     virtual int Prepare(HwcList *aList,
                         uint32_t aDisp,
+                        hwc_rect_t aDispRect,
                         buffer_handle_t aHandle,
                         int aFenceFd) override;
 
     virtual bool SupportTransparency() const override;
 
     virtual uint32_t GetGeometryChangedFlag(bool aGeometryChanged) const override;
 
     virtual void SetCrop(HwcLayer &aLayer,
                          const hwc_rect_t &aSrcCrop) const override;
 
     virtual bool EnableVsync(bool aEnable) override;
 
     virtual bool RegisterHwcEventCallback(const HwcHALProcs_t &aProcs) override;
 
 private:
-    void GetHwcAttributes();
-
     uint32_t GetAPIVersion() const;
 
 private:
     HwcDevice  *mHwc = nullptr;
-    hwc_rect_t  mHwcRect = {0};
 };
 
 } // namespace mozilla
 
 #endif // mozilla_HwcHAL
--- a/widget/gonk/hwchal/HwcHALBase.h
+++ b/widget/gonk/hwchal/HwcHALBase.h
@@ -80,19 +80,16 @@ public:
     // Create HwcHAL module, Only HwcComposer2D calls this.
     // If other modules want to use HwcHAL, please use APIs in
     // HwcComposer2D
     static UniquePtr<HwcHALBase> CreateHwcHAL();
 
     // Check if mHwc exists
     virtual bool HasHwc() const = 0;
 
-    // Get HwcRect
-    virtual const hwc_rect_t GetHwcRect() const = 0;
-
     // Set EGL info (only ICS need this info)
     virtual void SetEGLInfo(hwc_display_t aEGLDisplay,
                             hwc_surface_t aEGLSurface) = 0;
 
     // HwcDevice query properties
     virtual bool Query(QueryType aType) = 0;
 
     // HwcDevice set
@@ -100,16 +97,17 @@ public:
                     uint32_t aDisp) = 0;
 
     // Reset HwcDevice
     virtual int ResetHwc() = 0;
 
     // HwcDevice prepare
     virtual int Prepare(HwcList *aList,
                         uint32_t aDisp,
+                        hwc_rect_t aDispRect,
                         buffer_handle_t aHandle,
                         int aFenceFd) = 0;
 
     // Check transparency support
     virtual bool SupportTransparency() const = 0;
 
     // Get a geometry change flag
     virtual uint32_t GetGeometryChangedFlag(bool aGeometryChanged) const = 0;
--- a/widget/gonk/hwchal/HwcICS.cpp
+++ b/widget/gonk/hwchal/HwcICS.cpp
@@ -81,16 +81,17 @@ int
 HwcICS::ResetHwc()
 {
     return -1;
 }
 
 int
 HwcICS::Prepare(HwcList *aList,
                 uint32_t aDisp,
+                hwc_rect_t aDispRect,
                 buffer_handle_t aHandle,
                 int aFenceFd)
 {
     return mHwc->prepare(mHwc, aList);
 }
 
 bool
 HwcICS::SupportTransparency() const
--- a/widget/gonk/hwchal/HwcICS.h
+++ b/widget/gonk/hwchal/HwcICS.h
@@ -26,30 +26,29 @@ namespace mozilla {
 class HwcICS final : public HwcHALBase {
 public:
     explicit HwcICS();
 
     virtual ~HwcICS();
 
     virtual bool HasHwc() const override { return static_cast<bool>(mHwc); }
 
-    virtual const hwc_rect_t GetHwcRect() const override { return {0}; }
-
     virtual void SetEGLInfo(hwc_display_t aEGLDisplay,
                             hwc_surface_t aEGLSurface) override;
 
     virtual bool Query(QueryType aType) override;
 
     virtual int Set(HwcList *aList,
                     uint32_t aDisp) override;
 
     virtual int ResetHwc() override;
 
     virtual int Prepare(HwcList *aList,
                         uint32_t aDisp,
+                        hwc_rect_t aDispRect,
                         buffer_handle_t aHandle,
                         int aFenceFd) override;
 
     virtual bool SupportTransparency() const override;
 
     virtual uint32_t GetGeometryChangedFlag(bool aGeometryChanged) const override;
 
     virtual void SetCrop(HwcLayer &aLayer,
--- a/widget/gonk/nsScreenManagerGonk.cpp
+++ b/widget/gonk/nsScreenManagerGonk.cpp
@@ -116,16 +116,17 @@ nsScreenGonk::nsScreenGonk(uint32_t aId,
     : mId(aId)
     , mNativeWindow(aNativeData.mNativeWindow)
     , mDpi(aNativeData.mXdpi)
     , mScreenRotation(nsIScreen::ROTATION_0_DEG)
     , mPhysicalScreenRotation(nsIScreen::ROTATION_0_DEG)
 #if ANDROID_VERSION >= 17
     , mDisplaySurface(aNativeData.mDisplaySurface)
 #endif
+    , mIsMirroring(false)
     , mDisplayType(aDisplayType)
     , mEGLDisplay(EGL_NO_DISPLAY)
     , mEGLSurface(EGL_NO_SURFACE)
     , mGLContext(nullptr)
 {
     if (mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_WIDTH, &mVirtualBounds.width) ||
         mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_HEIGHT, &mVirtualBounds.height) ||
         mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_FORMAT, &mSurfaceFormat)) {
@@ -138,18 +139,32 @@ nsScreenGonk::nsScreenGonk(uint32_t aId,
         char propValue[PROPERTY_VALUE_MAX];
         property_get("ro.sf.hwrotation", propValue, "0");
         mPhysicalScreenRotation = atoi(propValue) / 90;
     }
 
     mColorDepth = SurfaceFormatToColorDepth(mSurfaceFormat);
 }
 
+static void
+ReleaseGLContextSync(mozilla::gl::GLContext* aGLContext) {
+    MOZ_ASSERT(CompositorParent::IsInCompositorThread());
+    aGLContext->Release();
+}
+
 nsScreenGonk::~nsScreenGonk()
 {
+    // Release GLContext on compositor thread
+    if (mGLContext) {
+        CompositorParent::CompositorLoop()->PostTask(
+            FROM_HERE,
+            NewRunnableFunction(&ReleaseGLContextSync,
+                                mGLContext.forget().take()));
+        mGLContext = nullptr;
+    }
 }
 
 bool
 nsScreenGonk::IsPrimaryScreen()
 {
     return mDisplayType == GonkDisplay::DISPLAY_PRIMARY;
 }
 
@@ -354,17 +369,18 @@ nsScreenGonk::GetPrevDispAcquireFd()
 
 GonkDisplay::DisplayType
 nsScreenGonk::GetDisplayType()
 {
     return mDisplayType;
 }
 
 void
-nsScreenGonk::SetEGLInfo(hwc_display_t aDisplay, hwc_surface_t aSurface,
+nsScreenGonk::SetEGLInfo(hwc_display_t aDisplay,
+                         hwc_surface_t aSurface,
                          gl::GLContext* aGLContext)
 {
     MOZ_ASSERT(CompositorParent::IsInCompositorThread());
     mEGLDisplay = aDisplay;
     mEGLSurface = aSurface;
     mGLContext = aGLContext;
 }
 
@@ -373,18 +389,124 @@ nsScreenGonk::GetEGLDisplay()
 {
     MOZ_ASSERT(CompositorParent::IsInCompositorThread());
     return mEGLDisplay;
 }
 
 hwc_surface_t
 nsScreenGonk::GetEGLSurface()
 {
+    return mEGLSurface;
+}
+
+static void
+UpdateMirroringWidgetSync(nsScreenGonk* aScreen, nsWindow* aWindow) {
     MOZ_ASSERT(CompositorParent::IsInCompositorThread());
-    return mEGLSurface;
+    already_AddRefed<nsWindow> window(aWindow);
+    aScreen->UpdateMirroringWidget(window);
+}
+
+bool
+nsScreenGonk::EnableMirroring()
+{
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(!IsPrimaryScreen());
+
+    nsRefPtr<nsScreenGonk> primaryScreen = nsScreenManagerGonk::GetPrimaryScreen();
+    NS_ENSURE_TRUE(primaryScreen, false);
+
+    bool ret = primaryScreen->SetMirroringScreen(this);
+    NS_ENSURE_TRUE(ret, false);
+
+    // Create a widget for mirroring
+    nsWidgetInitData initData;
+    initData.mScreenId = mId;
+    nsRefPtr<nsWindow> window = new nsWindow();
+    window->Create(nullptr, nullptr, mNaturalBounds, &initData);
+    MOZ_ASSERT(static_cast<nsWindow*>(window)->GetScreen() == this);
+
+    // Update mMirroringWidget on compositor thread
+    CompositorParent::CompositorLoop()->PostTask(
+        FROM_HERE,
+        NewRunnableFunction(&UpdateMirroringWidgetSync,
+                            primaryScreen,
+                            window.forget().take()));
+
+    mIsMirroring = true;
+    return true;
+}
+
+bool
+nsScreenGonk::DisableMirroring()
+{
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(!IsPrimaryScreen());
+
+    mIsMirroring = false;
+    nsRefPtr<nsScreenGonk> primaryScreen = nsScreenManagerGonk::GetPrimaryScreen();
+    NS_ENSURE_TRUE(primaryScreen, false);
+
+    bool ret = primaryScreen->ClearMirroringScreen(this);
+    NS_ENSURE_TRUE(ret, false);
+
+    // Update mMirroringWidget on compositor thread
+    CompositorParent::CompositorLoop()->PostTask(
+        FROM_HERE,
+        NewRunnableFunction(&UpdateMirroringWidgetSync,
+                            primaryScreen,
+                            nullptr));
+    return true;
+}
+
+bool
+nsScreenGonk::SetMirroringScreen(nsScreenGonk* aScreen)
+{
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(IsPrimaryScreen());
+
+    if (mMirroringScreen) {
+        return false;
+    }
+    mMirroringScreen = mMirroringScreen;
+    return true;
+}
+
+bool
+nsScreenGonk::ClearMirroringScreen(nsScreenGonk* aScreen)
+{
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(IsPrimaryScreen());
+
+    if (mMirroringScreen != aScreen) {
+        return false;
+    }
+    mMirroringScreen = nullptr;
+    return true;
+}
+
+void
+nsScreenGonk::UpdateMirroringWidget(already_AddRefed<nsWindow>& aWindow)
+{
+    MOZ_ASSERT(CompositorParent::IsInCompositorThread());
+    MOZ_ASSERT(IsPrimaryScreen());
+
+    if (mMirroringWidget) {
+        nsCOMPtr<nsIWidget> widget = mMirroringWidget.forget();
+        NS_ReleaseOnMainThread(widget);
+    }
+    mMirroringWidget = aWindow;
+}
+
+nsWindow*
+nsScreenGonk::GetMirroringWidget()
+{
+    MOZ_ASSERT(CompositorParent::IsInCompositorThread());
+    MOZ_ASSERT(IsPrimaryScreen());
+
+    return mMirroringWidget;
 }
 
 NS_IMPL_ISUPPORTS(nsScreenManagerGonk, nsIScreenManager)
 
 nsScreenManagerGonk::nsScreenManagerGonk()
     : mInitialized(false)
 {
 }
@@ -606,50 +728,59 @@ NotifyDisplayChange(uint32_t aId, bool a
 {
     NS_DispatchToMainThread(new NotifyTask(aId, aConnected));
 }
 
 } // end of unnamed namespace.
 
 nsresult
 nsScreenManagerGonk::AddScreen(GonkDisplay::DisplayType aDisplayType,
-                               android::IGraphicBufferProducer* aProducer)
+                               android::IGraphicBufferProducer* aSink)
 {
     MOZ_ASSERT(NS_IsMainThread());
 
     NS_ENSURE_TRUE(aDisplayType < GonkDisplay::DisplayType::NUM_DISPLAY_TYPES,
                    NS_ERROR_FAILURE);
 
     uint32_t id = GetIdFromType(aDisplayType);
     NS_ENSURE_TRUE(!IsScreenConnected(id), NS_ERROR_FAILURE);
 
     GonkDisplay::NativeData nativeData =
-        GetGonkDisplay()->GetNativeData(aDisplayType, aProducer);
+        GetGonkDisplay()->GetNativeData(aDisplayType, aSink);
     nsScreenGonk* screen = new nsScreenGonk(id, aDisplayType, nativeData);
 
     mScreens.AppendElement(screen);
 
     NotifyDisplayChange(id, true);
 
+    // By default, non primary screen does mirroring.
+    if (aDisplayType != GonkDisplay::DISPLAY_PRIMARY &&
+        gfxPrefs::ScreenMirroringEnabled()) {
+        screen->EnableMirroring();
+    }
+
     return NS_OK;
 }
 
 nsresult
 nsScreenManagerGonk::RemoveScreen(GonkDisplay::DisplayType aDisplayType)
 {
     MOZ_ASSERT(NS_IsMainThread());
 
     NS_ENSURE_TRUE(aDisplayType < GonkDisplay::DisplayType::NUM_DISPLAY_TYPES,
                    NS_ERROR_FAILURE);
 
     uint32_t screenId = GetIdFromType(aDisplayType);
     NS_ENSURE_TRUE(IsScreenConnected(screenId), NS_ERROR_FAILURE);
 
     for (size_t i = 0; i < mScreens.Length(); i++) {
         if (mScreens[i]->GetId() == screenId) {
+            if (mScreens[i]->IsMirroring()) {
+                mScreens[i]->DisableMirroring();
+            }
             mScreens.RemoveElementAt(i);
             break;
         }
     }
 
     NotifyDisplayChange(screenId, false);
 
     return NS_OK;
--- a/widget/gonk/nsScreenManagerGonk.h
+++ b/widget/gonk/nsScreenManagerGonk.h
@@ -12,24 +12,26 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef nsScreenManagerGonk_h___
 #define nsScreenManagerGonk_h___
 
-#include "mozilla/Hal.h"
-
 #include "cutils/properties.h"
 #include "hardware/hwcomposer.h"
+
 #include "libdisplay/GonkDisplay.h"
+#include "mozilla/Hal.h"
+#include "mozilla/Mutex.h"
 #include "nsBaseScreen.h"
 #include "nsCOMPtr.h"
 #include "nsIScreenManager.h"
+#include "nsProxyRelease.h"
 
 #include <android/native_window.h>
 
 class nsRunnable;
 class nsWindow;
 
 namespace android {
     class DisplaySurface;
@@ -82,42 +84,59 @@ public:
     void UnregisterWindow(nsWindow* aWindow);
     void BringToTop(nsWindow* aWindow);
 
     const nsTArray<nsWindow*>& GetTopWindows() const
     {
         return mTopWindows;
     }
 
-    // Set EGL info of primary display. Used for BLIT Composition.
+    // Non-primary screen only
+    bool EnableMirroring();
+    bool DisableMirroring();
+    bool IsMirroring()
+    {
+        return mIsMirroring;
+    }
+
+    // Primary screen only
+    bool SetMirroringScreen(nsScreenGonk* aScreen);
+    bool ClearMirroringScreen(nsScreenGonk* aScreen);
+
+    // Called only on compositor thread
     void SetEGLInfo(hwc_display_t aDisplay, hwc_surface_t aSurface,
                     mozilla::gl::GLContext* aGLContext);
     hwc_display_t GetEGLDisplay();
     hwc_surface_t GetEGLSurface();
+    void UpdateMirroringWidget(already_AddRefed<nsWindow>& aWindow); // Primary screen only
+    nsWindow* GetMirroringWidget(); // Primary screen only
 
 protected:
     uint32_t mId;
     int32_t mColorDepth;
     android::sp<ANativeWindow> mNativeWindow;
     float mDpi;
     int32_t mSurfaceFormat;
     nsIntRect mNaturalBounds; // Screen bounds w/o rotation taken into account.
     nsIntRect mVirtualBounds; // Screen bounds w/ rotation taken into account.
     uint32_t mScreenRotation;
     uint32_t mPhysicalScreenRotation;
     nsTArray<nsWindow*> mTopWindows;
 #if ANDROID_VERSION >= 17
     android::sp<android::DisplaySurface> mDisplaySurface;
 #endif
+    bool mIsMirroring; // Non-primary screen only
+    nsRefPtr<nsScreenGonk> mMirroringScreen; // Primary screen only
 
     // Accessed and updated only on compositor thread
     GonkDisplay::DisplayType mDisplayType;
     hwc_display_t mEGLDisplay;
     hwc_surface_t mEGLSurface;
-    mozilla::gl::GLContext* mGLContext;
+    nsRefPtr<mozilla::gl::GLContext> mGLContext;
+    nsRefPtr<nsWindow> mMirroringWidget; // Primary screen only
 };
 
 class nsScreenManagerGonk final : public nsIScreenManager
 {
 public:
     typedef mozilla::GonkDisplay GonkDisplay;
 
 public:
@@ -128,17 +147,17 @@ public:
 
     static already_AddRefed<nsScreenManagerGonk> GetInstance();
     static already_AddRefed<nsScreenGonk> GetPrimaryScreen();
 
     void Initialize();
     void DisplayEnabled(bool aEnabled);
 
     nsresult AddScreen(GonkDisplay::DisplayType aDisplayType,
-                       android::IGraphicBufferProducer* aProducer = nullptr);
+                       android::IGraphicBufferProducer* aSink = nullptr);
 
     nsresult RemoveScreen(GonkDisplay::DisplayType aDisplayType);
 
 protected:
     ~nsScreenManagerGonk();
     void VsyncControl(bool aEnabled);
     uint32_t GetIdFromType(GonkDisplay::DisplayType aDisplayType);
     bool IsScreenConnected(uint32_t aId);