Bug 804852: Support for the hwc implementation of Composer2D. r=mattwoodrow,mwu,roc sr=roc FIREFOX_AURORA_19_BASE
authorChris Jones <jones.chris.g@gmail.com>
Mon, 19 Nov 2012 09:58:38 -0800
changeset 113672 cf8750abee06cde395c659f8ecd8ae019d7512e3
parent 113671 c5fd901c5f95f07e29744decb259dc35d5873709
child 113673 2d2d1df0de8e72967f70fabb78a9aed54d28574e
push id23885
push usercjones@mozilla.com
push dateMon, 19 Nov 2012 17:58:43 +0000
treeherdermozilla-central@cf8750abee06 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow, mwu, roc, roc
bugs804852
milestone19.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 804852: Support for the hwc implementation of Composer2D. r=mattwoodrow,mwu,roc sr=roc This is a rollup of the following patches part 0: Add a dynamic cast to ColorLayer* part 1: Add a Composer2D interface to enable implementations to more efficiently compose layer trees part 2: Let widgets expose Composer2Ds, if they have them part 3: Expose a layers ogl "friend" API that Composer2D will consume part 4: Hook Composer2D into the LayerManagerOGL rendering pipeline part 5: Implement all the goop to let widget/gonk use a Composer2D (HwcComposer2D)
gfx/gl/GLContextProviderEGL.cpp
gfx/layers/Layers.h
gfx/layers/Makefile.in
gfx/layers/opengl/CanvasLayerOGL.cpp
gfx/layers/opengl/CanvasLayerOGL.h
gfx/layers/opengl/Composer2D.h
gfx/layers/opengl/ImageLayerOGL.cpp
gfx/layers/opengl/ImageLayerOGL.h
gfx/layers/opengl/LayerManagerOGL.cpp
gfx/layers/opengl/LayerManagerOGL.h
gfx/layers/opengl/ThebesLayerOGL.cpp
gfx/layers/opengl/ThebesLayerOGL.h
modules/libpref/src/init/all.js
widget/gonk/HWComposer.h
widget/gonk/Makefile.in
widget/gonk/nsWindow.cpp
widget/gonk/nsWindow.h
widget/nsIWidget.h
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -14,17 +14,17 @@
 #define GET_NATIVE_WINDOW(aWidget) (EGLNativeWindowType)GDK_WINDOW_XID((GdkWindow *) aWidget->GetNativeData(NS_NATIVE_WINDOW))
 #elif defined(MOZ_WIDGET_QT)
 #include <QtOpenGL/QGLContext>
 #define GLdouble_defined 1
 // we're using default display for now
 #define GET_NATIVE_WINDOW(aWidget) (EGLNativeWindowType)static_cast<QWidget*>(aWidget->GetNativeData(NS_NATIVE_SHELLWIDGET))->winId()
 #elif defined(MOZ_WIDGET_GONK)
 #define GET_NATIVE_WINDOW(aWidget) ((EGLNativeWindowType)aWidget->GetNativeData(NS_NATIVE_WINDOW))
-#include "HWComposer.h"
+#include "HwcComposer2D.h"
 #endif
 
 #if defined(MOZ_X11)
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
 #include "mozilla/X11Util.h"
 #include "gfxXlibSurface.h"
 #endif
@@ -260,22 +260,24 @@ public:
     {
         // any EGL contexts will always be GLESv2
         SetIsGLES2(true);
 
 #ifdef DEBUG
         printf_stderr("Initializing context %p surface %p on display %p\n", mContext, mSurface, EGL_DISPLAY());
 #endif
 #ifdef MOZ_WIDGET_GONK
-        if (!aIsOffscreen)
-            mHwc = new HWComposer();
-
-        if (mHwc && mHwc->init()) {
-            NS_WARNING("HWComposer initialization failed!");
-            mHwc = nullptr;
+        if (!aIsOffscreen) {
+            mHwc = HwcComposer2D::GetInstance();
+            MOZ_ASSERT(!mHwc->Initialized());
+
+            if (mHwc->Init(EGL_DISPLAY(), mSurface)) {
+                NS_WARNING("HWComposer initialization failed!");
+                mHwc = nullptr;
+            }
         }
 #endif
     }
 
     ~GLContextEGL()
     {
         if (MakeCurrent()) {
             if (mTemporaryEGLImageTexture != 0) {
@@ -667,17 +669,17 @@ protected:
     nsRefPtr<gfxASurface> mThebesSurface;
     bool mBound;
 
     bool mIsPBuffer;
     bool mIsDoubleBuffered;
     bool mCanBindToTexture;
     bool mShareWithEGLImage;
 #ifdef MOZ_WIDGET_GONK
-    nsAutoPtr<HWComposer> mHwc;
+    nsRefPtr<HwcComposer2D> mHwc;
 #endif
 
     // A dummy texture ID that can be used when we need a texture object whose
     // images we're going to define with EGLImageTargetTexture2D.
     GLuint mTemporaryEGLImageTexture;
 
     static EGLSurface CreatePBufferSurfaceTryingPowerOfTwo(EGLConfig config,
                                                            EGLenum bindToTextureFormat,
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -895,16 +895,22 @@ public:
   virtual ContainerLayer* AsContainerLayer() { return nullptr; }
 
    /**
     * Dynamic cast to a RefLayer. Returns null if this is not a
     * RefLayer.
     */
   virtual RefLayer* AsRefLayer() { return nullptr; }
 
+   /**
+    * Dynamic cast to a Color. Returns null if this is not a
+    * ColorLayer.
+    */
+  virtual ColorLayer* AsColorLayer() { return nullptr; }
+
   /**
    * Dynamic cast to a ShadowLayer.  Return null if this is not a
    * ShadowLayer.  Can be used anytime.
    */
   virtual ShadowLayer* AsShadowLayer() { return nullptr; }
 
   /**
    * Dynamic cast to a ShadowableLayer.  Return null if this is not a
@@ -1358,16 +1364,18 @@ protected:
 
 /**
  * A Layer which just renders a solid color in its visible region. It actually
  * can fill any area that contains the visible region, so if you need to
  * restrict the area filled, set a clip region on this layer.
  */
 class THEBES_API ColorLayer : public Layer {
 public:
+  virtual ColorLayer* AsColorLayer() { return this; }
+
   /**
    * CONSTRUCTION PHASE ONLY
    * Set the color of the layer.
    */
   virtual void SetColor(const gfxRGBA& aColor)
   {
     mColor = aColor;
   }
--- a/gfx/layers/Makefile.in
+++ b/gfx/layers/Makefile.in
@@ -26,16 +26,17 @@ DEFINES += -DIMPL_THEBES
 ifdef MOZ_DEBUG
 DEFINES += -DD3D_DEBUG_INFO
 endif
 
 EXPORTS = \
         BasicLayers.h \
         BasicTiledThebesLayer.h \
         BasicImplData.h \
+        Composer2D.h \
         GonkIOSurfaceImage.h \
         FrameMetrics.h \
         CompositorChild.h \
         CompositorParent.h \
         ImageContainer.h \
         ImageLayers.h \
         ImageTypes.h \
         Layers.h \
--- a/gfx/layers/opengl/CanvasLayerOGL.cpp
+++ b/gfx/layers/opengl/CanvasLayerOGL.cpp
@@ -471,16 +471,26 @@ ShadowCanvasLayerOGL::Destroy()
 }
 
 Layer*
 ShadowCanvasLayerOGL::GetLayer()
 {
   return this;
 }
 
+LayerRenderState
+ShadowCanvasLayerOGL::GetRenderState()
+{
+  if (mDestroyed) {
+    return LayerRenderState();
+  }
+  return LayerRenderState(&mFrontBufferDescriptor,
+                          mNeedsYFlip ? LAYER_RENDER_STATE_Y_FLIPPED : 0);
+}
+
 void
 ShadowCanvasLayerOGL::RenderLayer(int aPreviousFrameBuffer,
                                   const nsIntPoint& aOffset)
 {
   if (!mTexImage && !IsValidSharedTexDescriptor(mFrontBufferDescriptor)) {
     return;
   }
 
--- a/gfx/layers/opengl/CanvasLayerOGL.h
+++ b/gfx/layers/opengl/CanvasLayerOGL.h
@@ -121,16 +121,17 @@ public:
 
   virtual void DestroyFrontBuffer();
 
   virtual void Disconnect();
 
   // LayerOGL impl
   void Destroy();
   Layer* GetLayer();
+  virtual LayerRenderState GetRenderState() MOZ_OVERRIDE;
   virtual void RenderLayer(int aPreviousFrameBuffer,
                            const nsIntPoint& aOffset);
   virtual void CleanupResources();
 
 private:
   nsRefPtr<TextureImage> mTexImage;
 
   bool mNeedsYFlip;
new file mode 100644
--- /dev/null
+++ b/gfx/layers/opengl/Composer2D.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* 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 mozilla_layers_Composer2D_h
+#define mozilla_layers_Composer2D_h
+
+#include "gfxTypes.h"
+#include "nsISupportsImpl.h"
+
+/**
+ * Many platforms have dedicated hardware for simple composition.
+ * This hardware is usually faster or more power efficient than the
+ * GPU.  However, in exchange for this better performance, generality
+ * has to be sacrificed: no 3d transforms, no intermediate surfaces,
+ * no special shader effects, loss of other goodies depending on the
+ * platform.
+ *
+ * Composer2D is a very simple interface to this class of hardware
+ * that allows an implementation to "try rendering" with the fast
+ * path.  If the given layer tree requires more generality than the
+ * hardware provides, the implementation should bail and have the
+ * layer manager fall back on full GPU composition.
+ */
+
+class gfxMatrix;
+
+namespace mozilla {
+namespace layers {
+
+class Layer;
+
+class THEBES_API Composer2D {
+  NS_INLINE_DECL_REFCOUNTING(Composer2D)
+
+public:
+  virtual ~Composer2D() {}
+
+  /**
+   * Return true if |aRoot| met the implementation's criteria for fast
+   * composition and the render was successful.  Return false to fall
+   * back on the GPU.
+   *
+   * |aWorldTransform| must be applied to |aRoot|'s subtree when
+   * rendering to the framebuffer.  This is a global transform on the
+   * entire scene, defined in GL space.  If the Composer2D
+   * implementation is unable to honor the transform, it should return
+   * false.
+   *
+   * Currently, when TryRender() returns true, the entire framebuffer
+   * must have been rendered.
+   */
+  virtual bool TryRender(Layer* aRoot, const gfxMatrix& aWorldTransform) = 0;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_Composer2D_h
--- a/gfx/layers/opengl/ImageLayerOGL.cpp
+++ b/gfx/layers/opengl/ImageLayerOGL.cpp
@@ -812,16 +812,38 @@ ShadowImageLayerOGL::Destroy()
 }
 
 Layer*
 ShadowImageLayerOGL::GetLayer()
 {
   return this;
 }
 
+LayerRenderState
+ShadowImageLayerOGL::GetRenderState()
+{
+  if (!mImageContainerID) {
+    return LayerRenderState();
+  }
+
+  // Update the associated compositor ID in case Composer2D succeeds,
+  // because we won't enter RenderLayer() if so ...
+  ImageContainerParent::SetCompositorIDForImage(
+    mImageContainerID, mOGLManager->GetCompositorID());
+  // ... but do *not* try to update the local image version.  We need
+  // to retain that information in case we fall back on GL, so that we
+  // can upload / attach buffers properly.
+
+  SharedImage* img = ImageContainerParent::GetSharedImage(mImageContainerID);
+  if (img && img->type() == SharedImage::TSurfaceDescriptor) {
+    return LayerRenderState(&img->get_SurfaceDescriptor());
+  }
+  return LayerRenderState();
+}
+
 void ShadowImageLayerOGL::UploadSharedYUVToTexture(const YUVImage& yuv)
 {
   AutoOpenSurface asurfY(OPEN_READ_ONLY, yuv.Ydata());
   AutoOpenSurface asurfU(OPEN_READ_ONLY, yuv.Udata());
   AutoOpenSurface asurfV(OPEN_READ_ONLY, yuv.Vdata());
   nsRefPtr<gfxImageSurface> surfY = asurfY.GetAsImage();
   nsRefPtr<gfxImageSurface> surfU = asurfU.GetAsImage();
   nsRefPtr<gfxImageSurface> surfV = asurfV.GetAsImage();
@@ -922,16 +944,18 @@ ShadowImageLayerOGL::RenderLayer(int aPr
       if (img && (img->type() == SharedImage::TYUVImage)) {
         UploadSharedYUVToTexture(img->get_YUVImage());
   
         mImageVersion = imgVersion;
       } else if (img && (img->type() == SharedImage::TYCbCrImage)) {
         ShmemYCbCrImage shmemImage(img->get_YCbCrImage().data(),
                                    img->get_YCbCrImage().offset());
         UploadSharedYCbCrToTexture(shmemImage, img->get_YCbCrImage().picture());
+
+        mImageVersion = imgVersion;
 #ifdef MOZ_WIDGET_GONK
       } else if (img
                  && (img->type() == SharedImage::TSurfaceDescriptor)
                  && (img->get_SurfaceDescriptor().type() == SurfaceDescriptor::TSurfaceDescriptorGralloc)) {
         const SurfaceDescriptorGralloc& desc = img->get_SurfaceDescriptor().get_SurfaceDescriptorGralloc();
         sp<GraphicBuffer> graphicBuffer = GrallocBufferActor::GetFrom(desc);
         mSize = gfxIntSize(graphicBuffer->getWidth(), graphicBuffer->getHeight());
         if (!mExternalBufferTexture.IsAllocated()) {
--- a/gfx/layers/opengl/ImageLayerOGL.h
+++ b/gfx/layers/opengl/ImageLayerOGL.h
@@ -168,16 +168,17 @@ public:
 
   virtual void Disconnect();
 
   // LayerOGL impl
   virtual void Destroy();
   virtual bool LoadAsTexture(GLuint aTextureUnit, gfxIntSize* aSize);
 
   virtual Layer* GetLayer();
+  virtual LayerRenderState GetRenderState() MOZ_OVERRIDE;
 
   virtual void RenderLayer(int aPreviousFrameBuffer,
                            const nsIntPoint& aOffset);
 
   virtual void CleanupResources();
 
 private:
   bool Init(const SharedImage& aFront);
--- a/gfx/layers/opengl/LayerManagerOGL.cpp
+++ b/gfx/layers/opengl/LayerManagerOGL.cpp
@@ -3,16 +3,17 @@
  * 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 "mozilla/layers/PLayers.h"
 
 /* This must occur *after* layers/PLayers.h to avoid typedefs conflicts. */
 #include "mozilla/Util.h"
 
+#include "Composer2D.h"
 #include "LayerManagerOGL.h"
 #include "ThebesLayerOGL.h"
 #include "ContainerLayerOGL.h"
 #include "ImageLayerOGL.h"
 #include "ColorLayerOGL.h"
 #include "CanvasLayerOGL.h"
 #include "TiledThebesLayerOGL.h"
 #include "mozilla/TimeStamp.h"
@@ -578,16 +579,18 @@ LayerManagerOGL::Initialize(nsRefPtr<GLC
         Preferences::AddBoolVarCache(&sDrawFPS, "layers.acceleration.draw-fps");
         Preferences::AddBoolVarCache(&sFrameCounter, "layers.acceleration.frame-counter");
         return NS_OK;
       }
     };
     NS_DispatchToMainThread(new ReadDrawFPSPref());
   }
 
+  mComposer2D = mWidget->GetComposer2D();
+
   reporter.SetSuccessful();
   return true;
 }
 
 void
 LayerManagerOGL::SetClippingRegion(const nsIntRegion& aClippingRegion)
 {
   mClippingRegion = aClippingRegion;
@@ -666,17 +669,41 @@ LayerManagerOGL::EndTransaction(DrawTheb
     // 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(gfx3DMatrix());
 
     mThebesLayerCallback = aCallback;
     mThebesLayerCallbackData = aCallbackData;
     SetCompositingDisabled(aFlags & END_NO_COMPOSITE);
 
-    Render();
+    bool needGLRender = true;
+    if (mComposer2D && mComposer2D->TryRender(mRoot, mWorldMatrix)) {
+      needGLRender = false;
+
+      if (sDrawFPS) {
+        if (!mFPS) {
+          mFPS = new FPSState();
+        }
+        double fps = mFPS->mCompositionFps.AddFrameAndGetFps(TimeStamp::Now());
+        printf_stderr("HWComposer: FPS is %g\n", fps);
+      }
+
+      // This lets us reftest and screenshot content rendered by the
+      // 2d composer.
+      if (mTarget) {
+        MakeCurrent();
+        CopyToTarget(mTarget);
+        mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
+      }
+      MOZ_ASSERT(!needGLRender);
+    }
+
+    if (needGLRender) {
+      Render();
+    }
 
     mThebesLayerCallback = nullptr;
     mThebesLayerCallbackData = nullptr;
   }
 
   mTarget = NULL;
 
 #ifdef MOZ_LAYERS_HAVE_LOG
--- a/gfx/layers/opengl/LayerManagerOGL.h
+++ b/gfx/layers/opengl/LayerManagerOGL.h
@@ -31,16 +31,17 @@ typedef int GLsizei;
 #include "gfxContext.h"
 #include "gfx3DMatrix.h"
 #include "nsIWidget.h"
 #include "GLContext.h"
 
 namespace mozilla {
 namespace layers {
 
+class Composer2D;
 class LayerOGL;
 class ShadowThebesLayer;
 class ShadowContainerLayer;
 class ShadowImageLayer;
 class ShadowCanvasLayer;
 class ShadowColorLayer;
 struct FPSState;
 
@@ -382,16 +383,19 @@ private:
 
   /** 
    * Context target, NULL when drawing directly to our swap chain.
    */
   nsRefPtr<gfxContext> mTarget;
 
   nsRefPtr<GLContext> mGLContext;
 
+  /** Our more efficient but less powerful alter ego, if one is available. */
+  nsRefPtr<Composer2D> mComposer2D;
+
   already_AddRefed<mozilla::gl::GLContext> CreateContext();
 
   /** Backbuffer */
   GLuint mBackBufferFBO;
   GLuint mBackBufferTexture;
   nsIntSize mBackBufferSize;
 
   /** Shader Programs */
@@ -473,16 +477,40 @@ private:
   // next forwarded transaction that re-validates their buffers.
   bool mMaybeInvalidTree;
 #endif
 
   static bool sDrawFPS;
   static bool sFrameCounter;
 };
 
+enum LayerRenderStateFlags {
+  LAYER_RENDER_STATE_Y_FLIPPED = 1 << 0,
+  LAYER_RENDER_STATE_BUFFER_ROTATION = 1 << 1
+};
+
+struct LayerRenderState {
+  LayerRenderState() : mSurface(nullptr), mFlags(0)
+  {}
+
+  LayerRenderState(SurfaceDescriptor* aSurface, uint32_t aFlags = 0)
+    : mSurface(aSurface)
+    , mFlags(aFlags)
+  {}
+
+  bool YFlipped() const
+  { return mFlags & LAYER_RENDER_STATE_Y_FLIPPED; }
+
+  bool BufferRotated() const
+  { return mFlags & LAYER_RENDER_STATE_BUFFER_ROTATION; }
+
+  SurfaceDescriptor* mSurface;
+  uint32_t mFlags;
+};
+
 /**
  * General information and tree management for OGL layers.
  */
 class LayerOGL
 {
 public:
   LayerOGL(LayerManagerOGL *aManager)
     : mOGLManager(aManager), mDestroyed(false)
@@ -496,16 +524,18 @@ public:
 
   /* Do NOT call this from the generic LayerOGL destructor.  Only from the
    * concrete class destructor
    */
   virtual void Destroy() = 0;
 
   virtual Layer* GetLayer() = 0;
 
+  virtual LayerRenderState GetRenderState() { return LayerRenderState(); }
+
   virtual void RenderLayer(int aPreviousFrameBuffer,
                            const nsIntPoint& aOffset) = 0;
 
   typedef mozilla::gl::GLContext GLContext;
 
   LayerManagerOGL* OGLManager() const { return mOGLManager; }
   GLContext *gl() const { return mOGLManager->gl(); }
   virtual void CleanupResources() = 0;
--- a/gfx/layers/opengl/ThebesLayerOGL.cpp
+++ b/gfx/layers/opengl/ThebesLayerOGL.cpp
@@ -943,16 +943,20 @@ public:
   void Upload(gfxASurface* aUpdate, const nsIntRegion& aUpdated,
               const nsIntRect& aRect, const nsIntPoint& aRotation);
 
   already_AddRefed<TextureImage>
   Swap(TextureImage* aNewBackBuffer,
        const nsIntRect& aRect, const nsIntPoint& aRotation,
        nsIntRect* aPrevRect, nsIntPoint* aPrevRotation);
 
+  nsIntPoint Rotation() {
+    return mBufferRotation;
+  }
+
 protected:
   virtual nsIntPoint GetOriginOffset() {
     return mBufferRect.TopLeft() - mBufferRotation;
   }
 
 private:
   nsIntRect mBufferRect;
   nsIntPoint mBufferRotation;
@@ -1144,16 +1148,27 @@ ShadowThebesLayerOGL::Destroy()
 }
 
 Layer*
 ShadowThebesLayerOGL::GetLayer()
 {
   return this;
 }
 
+LayerRenderState
+ShadowThebesLayerOGL::GetRenderState()
+{
+  if (!mBuffer || mDestroyed) {
+    return LayerRenderState();
+  }
+  uint32_t flags = (mBuffer->Rotation() != nsIntPoint()) ?
+                   LAYER_RENDER_STATE_BUFFER_ROTATION : 0;
+  return LayerRenderState(&mBufferDescriptor, flags);
+}
+
 bool
 ShadowThebesLayerOGL::IsEmpty()
 {
   return !mBuffer;
 }
 
 void
 ShadowThebesLayerOGL::RenderLayer(int aPreviousFrameBuffer,
--- a/gfx/layers/opengl/ThebesLayerOGL.h
+++ b/gfx/layers/opengl/ThebesLayerOGL.h
@@ -126,16 +126,17 @@ public:
   virtual void SetValidRegion(const nsIntRegion& aRegion)
   {
     ShadowThebesLayer::SetValidRegion(aRegion);
   }
 
   // LayerOGL impl
   void Destroy();
   Layer* GetLayer();
+  virtual LayerRenderState GetRenderState() MOZ_OVERRIDE;
   virtual bool IsEmpty();
   virtual void RenderLayer(int aPreviousFrameBuffer,
                            const nsIntPoint& aOffset);
   virtual void CleanupResources();
 
 private:
   nsRefPtr<ShadowBufferOGL> mBuffer;
   SurfaceDescriptor mBufferDescriptor;
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -3661,16 +3661,19 @@ pref("layers.acceleration.force-enabled"
 
 pref("layers.acceleration.draw-fps", false);
 
 // Whether to animate simple opacity and transforms on the compositor
 pref("layers.offmainthreadcomposition.animate-opacity", false);
 pref("layers.offmainthreadcomposition.animate-transform", false);
 pref("layers.offmainthreadcomposition.log-animations", false);
 
+// Whether to (try) to use a Composer2D if available on this platform.
+pref("layers.composer2d.enabled", false);
+
 #ifdef MOZ_X11
 #ifdef MOZ_WIDGET_GTK2
 pref("gfx.xrender.enabled",true);
 #endif
 #endif
 
 #ifdef XP_WIN
 // Whether to disable the automatic detection and use of direct2d.
--- a/widget/gonk/HWComposer.h
+++ b/widget/gonk/HWComposer.h
@@ -33,17 +33,17 @@ public:
     HWComposer();
     ~HWComposer();
 
     int init();
 
     // swap buffers using vendor specific implementation
     status_t swapBuffers(hwc_display_t dpy, hwc_surface_t surf) const;
 
-private:
+protected:
     struct cb_context {
         hwc_procs_t procs;
         HWComposer* hwc;
     };
     void invalidate();
 
     hw_module_t const*      mModule;
     hwc_composer_device_t*  mHwc;
--- a/widget/gonk/Makefile.in
+++ b/widget/gonk/Makefile.in
@@ -30,16 +30,17 @@ IS_COMPONENT    = 1
 MODULE_NAME     = nsWidgetGonkModule
 GRE_MODULE      = 1
 LIBXUL_LIBRARY  = 1
 
 
 CPPSRCS	= \
 	Framebuffer.cpp \
 	HWComposer.cpp \
+	HwcComposer2D.cpp \
 	nsAppShell.cpp \
 	nsWidgetFactory.cpp \
 	nsWindow.cpp \
 	nsLookAndFeel.cpp \
 	nsIdleServiceGonk.cpp \
 	OrientationObserver.cpp \
 	EventHub.cpp \
 	GonkMemoryPressureMonitoring.cpp \
--- a/widget/gonk/nsWindow.cpp
+++ b/widget/gonk/nsWindow.cpp
@@ -24,16 +24,17 @@
 #include "mozilla/Hal.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/FileUtils.h"
 #include "Framebuffer.h"
 #include "gfxContext.h"
 #include "gfxPlatform.h"
 #include "gfxUtils.h"
 #include "GLContextProvider.h"
+#include "HwcComposer2D.h"
 #include "LayerManagerOGL.h"
 #include "nsAutoPtr.h"
 #include "nsAppShell.h"
 #include "nsIdleService.h"
 #include "nsScreenManagerGonk.h"
 #include "nsTArray.h"
 #include "nsWindow.h"
 #include "nsIWidgetListener.h"
@@ -61,16 +62,17 @@ static gfxMatrix sRotationMatrix;
 
 static nsRefPtr<GLContext> sGLContext;
 static nsTArray<nsWindow *> sTopWindows;
 static nsWindow *gWindowToRedraw = nullptr;
 static nsWindow *gFocusedWindow = nullptr;
 static android::FramebufferNativeWindow *gNativeWindow = nullptr;
 static bool sFramebufferOpen;
 static bool sUsingOMTC;
+static bool sUsingHwc;
 static bool sScreenInitialized;
 static nsRefPtr<gfxASurface> sOMTCSurface;
 static pthread_t sFramebufferWatchThread;
 
 namespace {
 
 static int
 CancelBufferNoop(ANativeWindow* aWindow, android_native_buffer_t* aBuffer)
@@ -210,16 +212,17 @@ nsWindow::nsWindow()
         // This is a hack to force initialization of the compositor
         // resources, if we're going to use omtc.
         //
         // NB: GetPlatform() will create the gfxPlatform, which wants
         // to know the color depth, which asks our native window.
         // This has to happen after other init has finished.
         gfxPlatform::GetPlatform();
         sUsingOMTC = UseOffMainThreadCompositing();
+        sUsingHwc = Preferences::GetBool("layers.composer2d.enabled", false);
 
         if (sUsingOMTC) {
           sOMTCSurface = new gfxImageSurface(gfxIntSize(1, 1),
                                              gfxASurface::ImageFormatRGB24);
         }
     }
 }
 
@@ -690,16 +693,28 @@ bool
 nsWindow::NeedsPaint()
 {
   if (!mLayerManager) {
     return false;
   }
   return nsIWidget::NeedsPaint();
 }
 
+Composer2D*
+nsWindow::GetComposer2D()
+{
+    if (!sUsingHwc) {
+        return nullptr;
+    }
+    if (HwcComposer2D* hwc = HwcComposer2D::GetInstance()) {
+        return hwc->Initialized() ? hwc : nullptr;
+    }
+    return nullptr;
+}
+
 // nsScreenGonk.cpp
 
 nsScreenGonk::nsScreenGonk(void *nativeScreen)
 {
 }
 
 nsScreenGonk::~nsScreenGonk()
 {
--- a/widget/gonk/nsWindow.h
+++ b/widget/gonk/nsWindow.h
@@ -104,16 +104,18 @@ public:
                                       const InputContextAction& aAction);
     NS_IMETHOD_(InputContext) GetInputContext();
 
     virtual uint32_t GetGLFrameBufferFormat() MOZ_OVERRIDE;
 
     virtual nsIntRect GetNaturalBounds() MOZ_OVERRIDE;
     virtual bool NeedsPaint();
 
+    virtual Composer2D* GetComposer2D() MOZ_OVERRIDE;
+
 protected:
     nsWindow* mParent;
     bool mVisible;
     nsIntRegion mDirtyRegion;
     InputContext mInputContext;
     nsCOMPtr<nsIIdleServiceInternal> mIdleService;
 
     void BringToTop();
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -37,16 +37,17 @@ class   ViewWrapper;
 class   nsIWidgetListener;
 class   nsIntRegion;
 
 namespace mozilla {
 namespace dom {
 class TabChild;
 }
 namespace layers {
+class Composer2D;
 class CompositorChild;
 class LayerManager;
 class PLayersChild;
 }
 }
 
 /**
  * Callback function that processes events.
@@ -86,18 +87,18 @@ typedef nsEventStatus (* EVENT_CALLBACK)
 #ifdef XP_WIN
 #define NS_NATIVE_TSF_THREAD_MGR       100
 #define NS_NATIVE_TSF_CATEGORY_MGR     101
 #define NS_NATIVE_TSF_DISPLAY_ATTR_MGR 102
 #define NS_NATIVE_ICOREWINDOW          103 // winrt specific
 #endif
 
 #define NS_IWIDGET_IID \
-  { 0xb7c60bda, 0xe16c, 0x4e89, \
-    { 0x86, 0x8c, 0xc3, 0x2e, 0x62, 0x40, 0x05, 0xb2 } }
+  { 0xdb9b0931, 0xebf9, 0x4e0d, \
+    { 0xb2, 0x0a, 0xf7, 0x5f, 0xcb, 0x17, 0xe6, 0xe1 } }
 
 /*
  * Window shadow styles
  * Also used for the -moz-window-shadow CSS property
  */
 
 #define NS_STYLE_WINDOW_SHADOW_NONE             0
 #define NS_STYLE_WINDOW_SHADOW_DEFAULT          1
@@ -408,16 +409,17 @@ struct SizeConstraints {
  * The base class for all the widgets. It provides the interface for
  * all basic and necessary functionality.
  */
 class nsIWidget : public nsISupports {
   protected:
     typedef mozilla::dom::TabChild TabChild;
 
   public:
+    typedef mozilla::layers::Composer2D Composer2D;
     typedef mozilla::layers::CompositorChild CompositorChild;
     typedef mozilla::layers::LayerManager LayerManager;
     typedef mozilla::layers::LayersBackend LayersBackend;
     typedef mozilla::layers::PLayersChild PLayersChild;
     typedef mozilla::widget::IMEState IMEState;
     typedef mozilla::widget::InputContext InputContext;
     typedef mozilla::widget::InputContextAction InputContextAction;
     typedef mozilla::widget::SizeConstraints SizeConstraints;
@@ -1668,16 +1670,26 @@ class nsIWidget : public nsISupports {
 
     /**
      * If this isn't directly compositing to its window surface,
      * return the compositor which is doing that on our behalf.
      */
     virtual CompositorChild* GetRemoteRenderer()
     { return nullptr; }
 
+    /**
+     * If this widget has a more efficient composer available for its
+     * native framebuffer, return it.
+     *
+     * This can be called from a non-main thread, but that thread must
+     * hold a strong reference to this.
+     */
+    virtual Composer2D* GetComposer2D()
+    { return nullptr; }
+
 protected:
     /**
      * Like GetDefaultScale, but taking into account only the system settings
      * and ignoring Gecko preferences.
      */
     virtual double GetDefaultScaleInternal() { return 1.0; }
 
     // keep the list of children.  We also keep track of our siblings.