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 idunknown
push userunknown
push dateunknown
reviewersmattwoodrow, mwu, roc, roc
bugs804852
milestone19.0a1
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.