Bug 739679 - Part 3: BasicTiledThebesLayer. r=mwoodrow,roc
authorBenoit Girard <b56girard@gmail.com>
Mon, 16 Apr 2012 19:02:45 -0400
changeset 95494 ea4c7f9fc725509ca6f4ad68f07249d313ea966c
parent 95493 b43c36197d4f80d9d8e0ae3fd13bbacd4d3c40ad
child 95495 502de391e55b5efd3bb2ab62188cca6696bcf9df
push id886
push userlsblakk@mozilla.com
push dateMon, 04 Jun 2012 19:57:52 +0000
treeherdermozilla-beta@bbd8d5efd6d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmwoodrow, roc
bugs739679
milestone14.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 739679 - Part 3: BasicTiledThebesLayer. r=mwoodrow,roc
gfx/layers/Makefile.in
gfx/layers/basic/BasicLayers.cpp
gfx/layers/basic/BasicTiledThebesLayer.cpp
gfx/layers/basic/BasicTiledThebesLayer.h
--- a/gfx/layers/Makefile.in
+++ b/gfx/layers/Makefile.in
@@ -67,16 +67,17 @@ EXPORTS = \
         LayerManagerOGLProgram.h \
         ReadbackLayer.h \
         LayerSorter.h \
         $(NULL)
 
 CPPSRCS = \
         BasicImages.cpp \
         BasicLayers.cpp \
+        BasicTiledThebesLayer.cpp \
         Layers.cpp \
         RenderTrace.cpp \
         ReadbackProcessor.cpp \
         ThebesLayerBuffer.cpp \
         CanvasLayerOGL.cpp \
         ColorLayerOGL.cpp \
         ContainerLayerOGL.cpp \
         ImageLayerOGL.cpp \
--- a/gfx/layers/basic/BasicLayers.cpp
+++ b/gfx/layers/basic/BasicLayers.cpp
@@ -44,16 +44,17 @@
 #include "mozilla/layers/PLayersChild.h"
 #include "mozilla/layers/PLayersParent.h"
 #include "mozilla/gfx/2D.h"
 
 #include "ipc/ShadowLayerChild.h"
 
 #include "BasicLayers.h"
 #include "BasicImplData.h"
+#include "BasicTiledThebesLayer.h"
 #include "ImageLayers.h"
 #include "RenderTrace.h"
 
 #include "prprf.h"
 #include "nsTArray.h"
 #include "nsGUIEvent.h"
 #include "gfxContext.h"
 #include "gfxImageSurface.h"
@@ -3121,20 +3122,33 @@ MaybeCreateShadowFor(BasicShadowableLaye
 #define MAYBE_CREATE_SHADOW(_type)                                      \
   MaybeCreateShadowFor(layer, this,                                     \
                        &ShadowLayerForwarder::Created ## _type ## Layer)
 
 already_AddRefed<ThebesLayer>
 BasicShadowLayerManager::CreateThebesLayer()
 {
   NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
-  nsRefPtr<BasicShadowableThebesLayer> layer =
-    new BasicShadowableThebesLayer(this);
-  MAYBE_CREATE_SHADOW(Thebes);
-  return layer.forget();
+#ifdef FORCE_BASICTILEDTHEBESLAYER
+  if (HasShadowManager()) {
+    // BasicTiledThebesLayer doesn't support main
+    // thread compositing so only return this layer
+    // type if we have a shadow manager.
+    nsRefPtr<BasicTiledThebesLayer> layer =
+      new BasicTiledThebesLayer(this);
+    MAYBE_CREATE_SHADOW(Thebes);
+    return layer.forget();
+  } else
+#endif
+  {
+    nsRefPtr<BasicShadowableThebesLayer> layer =
+      new BasicShadowableThebesLayer(this);
+    MAYBE_CREATE_SHADOW(Thebes);
+    return layer.forget();
+  }
 }
 
 already_AddRefed<ContainerLayer>
 BasicShadowLayerManager::CreateContainerLayer()
 {
   NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
   nsRefPtr<BasicShadowableContainerLayer> layer =
     new BasicShadowableContainerLayer(this);
new file mode 100644
--- /dev/null
+++ b/gfx/layers/basic/BasicTiledThebesLayer.cpp
@@ -0,0 +1,227 @@
+/* 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/. */
+
+#include "mozilla/layers/PLayersChild.h"
+#include "BasicTiledThebesLayer.h"
+#include "gfxImageSurface.h"
+#include "sampler.h"
+
+#ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
+#include "cairo.h"
+#include <sstream>
+using mozilla::layers::Layer;
+static void DrawDebugOverlay(gfxImageSurface* imgSurf, int x, int y)
+{
+  gfxContext c(imgSurf);
+
+  // Draw border
+  c.NewPath();
+  c.SetDeviceColor(gfxRGBA(0.0, 0.0, 0.0, 1.0));
+  c.Rectangle(gfxRect(gfxPoint(0,0),imgSurf->GetSize()));
+  c.Stroke();
+
+  // Build tile description
+  std::stringstream ss;
+  ss << x << ", " << y;
+
+  // Draw text using cairo toy text API
+  cairo_t* cr = c.GetCairo();
+  cairo_set_font_size(cr, 10);
+  cairo_text_extents_t extents;
+  cairo_text_extents(cr, ss.str().c_str(), &extents);
+
+  int textWidth = extents.width + 6;
+
+  c.NewPath();
+  c.SetDeviceColor(gfxRGBA(0.0, 0.0, 0.0, 1.0));
+  c.Rectangle(gfxRect(gfxPoint(2,2),gfxSize(textWidth, 15)));
+  c.Fill();
+
+  c.NewPath();
+  c.SetDeviceColor(gfxRGBA(1.0, 0.0, 0.0, 1.0));
+  c.Rectangle(gfxRect(gfxPoint(2,2),gfxSize(textWidth, 15)));
+  c.Stroke();
+
+  c.NewPath();
+  cairo_move_to(cr, 4, 13);
+  cairo_show_text(cr, ss.str().c_str());
+
+}
+
+#endif
+
+namespace mozilla {
+namespace layers {
+
+void
+BasicTiledLayerBuffer::PaintThebes(BasicTiledThebesLayer* aLayer,
+                                   const nsIntRegion& aNewValidRegion,
+                                   const nsIntRegion& aPaintRegion,
+                                   LayerManager::DrawThebesLayerCallback aCallback,
+                                   void* aCallbackData)
+{
+  mThebesLayer = aLayer;
+  mCallback = aCallback;
+  mCallbackData = aCallbackData;
+
+#ifdef GFX_TILEDLAYER_PREF_WARNINGS
+  long start = PR_IntervalNow();
+#endif
+  if (UseSinglePaintBuffer()) {
+    SAMPLE_LABEL("BasicTiledLayerBuffer", "PaintThebesSingleBuffer");
+    const nsIntRect bounds = aPaintRegion.GetBounds();
+    mSinglePaintBuffer = new gfxImageSurface(gfxIntSize(bounds.width, bounds.height), gfxASurface::ImageFormatRGB16_565);
+    mSinglePaintBufferOffset = nsIntPoint(bounds.x, bounds.y);
+    nsRefPtr<gfxContext> ctxt = new gfxContext(mSinglePaintBuffer);
+    ctxt->NewPath();
+    ctxt->Translate(gfxPoint(-bounds.x, -bounds.y));
+#ifdef GFX_TILEDLAYER_PREF_WARNINGS
+    if (PR_IntervalNow() - start > 3) {
+      printf_stderr("Slow alloc %i\n", PR_IntervalNow() - start);
+    }
+    start = PR_IntervalNow();
+#endif
+    mCallback(mThebesLayer, ctxt, aPaintRegion, aPaintRegion, mCallbackData);
+  }
+
+#ifdef GFX_TILEDLAYER_PREF_WARNINGS
+  if (PR_IntervalNow() - start > 30) {
+    const nsIntRect bounds = aPaintRegion.GetBounds();
+    printf_stderr("Time to draw %i: %i, %i, %i, %i\n", PR_IntervalNow() - start, bounds.x, bounds.y, bounds.width, bounds.height);
+    if (aPaintRegion.IsComplex()) {
+      printf_stderr("Complex region\n");
+      nsIntRegionRectIterator it(aPaintRegion);
+      for (const nsIntRect* rect = it.Next(); rect != nsnull; rect = it.Next()) {
+        printf_stderr(" rect %i, %i, %i, %i\n", rect->x, rect->y, rect->width, rect->height);
+      }
+    }
+  }
+  start = PR_IntervalNow();
+#endif
+
+  SAMPLE_LABEL("BasicTiledLayerBuffer", "PaintThebesUpdate");
+  Update(aNewValidRegion, aPaintRegion);
+
+#ifdef GFX_TILEDLAYER_PREF_WARNINGS
+  if (PR_IntervalNow() - start > 10) {
+    const nsIntRect bounds = aPaintRegion.GetBounds();
+    printf_stderr("Time to tile %i: %i, %i, %i, %i\n", PR_IntervalNow() - start, bounds.x, bounds.y, bounds.width, bounds.height);
+  }
+#endif
+
+  mThebesLayer = nsnull;
+  mCallback = nsnull;
+  mCallbackData = nsnull;
+  mSinglePaintBuffer = nsnull;
+}
+
+BasicTiledLayerTile
+BasicTiledLayerBuffer::ValidateTileInternal(BasicTiledLayerTile aTile,
+                                            const nsIntPoint& aTileOrigin,
+                                            const nsIntRect& aDirtyRect)
+{
+  if (aTile == GetPlaceholderTile()) {
+    gfxImageSurface* tmpTile = new gfxImageSurface(gfxIntSize(GetTileLength(), GetTileLength()), gfxASurface::ImageFormatRGB16_565);
+    aTile = BasicTiledLayerTile(tmpTile);
+  }
+
+  gfxRect drawRect(aDirtyRect.x - aTileOrigin.x, aDirtyRect.y - aTileOrigin.y,
+                   aDirtyRect.width, aDirtyRect.height);
+
+  // Use the gfxReusableSurfaceWrapper, which will reuse the surface
+  // if the compositor no longer has a read lock, otherwise the surface
+  // will be copied into a new writable surface.
+  gfxImageSurface* writableSurface;
+  aTile.mSurface = aTile.mSurface->GetWritable(&writableSurface);
+
+  // Bug 742100, this gfxContext really should live on the stack.
+  nsRefPtr<gfxContext> ctxt = new gfxContext(writableSurface);
+  ctxt->NewPath();
+  ctxt->SetOperator(gfxContext::OPERATOR_CLEAR);
+  ctxt->Rectangle(drawRect, true);
+  ctxt->Fill();
+  ctxt->SetOperator(gfxContext::OPERATOR_OVER);
+  if (mSinglePaintBuffer) {
+    ctxt->NewPath();
+    ctxt->SetSource(mSinglePaintBuffer.get(),
+                    gfxPoint(mSinglePaintBufferOffset.x - aDirtyRect.x + drawRect.x,
+                             mSinglePaintBufferOffset.y - aDirtyRect.y + drawRect.y));
+    ctxt->Rectangle(drawRect, true);
+    ctxt->Fill();
+  } else {
+    ctxt->NewPath();
+    ctxt->Translate(gfxPoint(-aTileOrigin.x, -aTileOrigin.y));
+    nsIntPoint a = aTileOrigin;
+    mCallback(mThebesLayer, ctxt, nsIntRegion(nsIntRect(a, nsIntSize(GetTileLength(), GetTileLength()))), aDirtyRect, mCallbackData);
+  }
+
+#ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
+  DrawDebugOverlay(writableSurface, aTileOrigin.x, aTileOrigin.y);
+  //aTile->DumpAsDataURL();
+#endif
+
+  return aTile;
+}
+
+BasicTiledLayerTile
+BasicTiledLayerBuffer::ValidateTile(BasicTiledLayerTile aTile,
+                                    const nsIntPoint& aTileOrigin,
+                                    const nsIntRegion& aDirtyRegion)
+{
+
+  SAMPLE_LABEL("BasicTiledLayerBuffer", "ValidateTile");
+
+#ifdef GFX_TILEDLAYER_PREF_WARNINGS
+  if (aDirtyRegion.IsComplex()) {
+    printf_stderr("Complex region\n");
+  }
+#endif
+
+  nsIntRegionRectIterator it(aDirtyRegion);
+  for (const nsIntRect* rect = it.Next(); rect != nsnull; rect = it.Next()) {
+#ifdef GFX_TILEDLAYER_PREF_WARNINGS
+    printf_stderr(" break into subrect %i, %i, %i, %i\n", rect->x, rect->y, rect->width, rect->height);
+#endif
+    aTile = ValidateTileInternal(aTile, aTileOrigin, *rect);
+  }
+
+  return aTile;
+}
+
+void
+BasicTiledThebesLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
+{
+  aAttrs = ThebesLayerAttributes(GetValidRegion());
+}
+
+void
+BasicTiledThebesLayer::PaintThebes(gfxContext* aContext,
+                                   LayerManager::DrawThebesLayerCallback aCallback,
+                                   void* aCallbackData,
+                                   ReadbackProcessor* aReadback)
+{
+  if (!aCallback) {
+    BasicManager()->SetTransactionIncomplete();
+    return;
+  }
+
+  if (!HasShadow()) {
+    NS_ASSERTION(false, "Shadow requested for painting\n");
+    return;
+  }
+
+  nsIntRegion regionToPaint = mVisibleRegion;
+  regionToPaint.Sub(regionToPaint, mValidRegion);
+  if (regionToPaint.IsEmpty())
+    return;
+
+  mTiledBuffer.PaintThebes(this, mVisibleRegion, regionToPaint, aCallback, aCallbackData);
+  mTiledBuffer.ReadLock();
+  mValidRegion = mVisibleRegion;
+
+  BasicManager()->PaintedTiledLayerBuffer(BasicManager()->Hold(this), &mTiledBuffer);
+}
+
+} // mozilla
+} // layers
new file mode 100644
--- /dev/null
+++ b/gfx/layers/basic/BasicTiledThebesLayer.h
@@ -0,0 +1,211 @@
+/* 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 GFX_BASICTILEDTHEBESLAYER_H
+#define GFX_BASICTILEDTHEBESLAYER_H
+
+#include "TiledLayerBuffer.h"
+#include "gfxReusableSurfaceWrapper.h"
+#include "mozilla/layers/ShadowLayers.h"
+#include "BasicLayers.h"
+#include "BasicImplData.h"
+#include <algorithm>
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * Represent a single tile in tiled buffer. It's backed
+ * by a gfxReusableSurfaceWrapper that implements a
+ * copy-on-write mechanism while locked. The tile should be
+ * locked before being sent to the compositor and unlocked
+ * as soon as it is uploaded to prevent a copy.
+ * Ideal place to store per tile debug information.
+ */
+struct BasicTiledLayerTile {
+  nsRefPtr<gfxReusableSurfaceWrapper> mSurface;
+#ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
+  TimeStamp        mLastUpdate;
+#endif
+
+  // Placeholder
+  BasicTiledLayerTile()
+    : mSurface(NULL)
+  {}
+  explicit BasicTiledLayerTile(gfxImageSurface* aSurface)
+    : mSurface(new gfxReusableSurfaceWrapper(aSurface))
+  {
+  }
+  BasicTiledLayerTile(const BasicTiledLayerTile& o) {
+    mSurface = o.mSurface;
+#ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
+    mLastUpdate = o.mLastUpdate;
+#endif
+  }
+  BasicTiledLayerTile& operator=(const BasicTiledLayerTile& o) {
+    if (this == &o) return *this;
+    mSurface = o.mSurface;
+#ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
+    mLastUpdate = o.mLastUpdate;
+#endif
+    return *this;
+  }
+  bool operator== (const BasicTiledLayerTile& o) const {
+    return mSurface == o.mSurface;
+  }
+  bool operator!= (const BasicTiledLayerTile& o) const {
+    return mSurface != o.mSurface;
+  }
+  void ReadUnlock() {
+    mSurface->ReadUnlock();
+  }
+  void ReadLock() {
+    mSurface->ReadLock();
+  }
+};
+
+class BasicTiledThebesLayer;
+
+/**
+ * Provide an instance of TiledLayerBuffer backed by image surfaces.
+ * This buffer provides an implementation to ValidateTile using a
+ * thebes callback and can support painting using a single paint buffer
+ * which is much faster then painting directly into the tiles.
+ */
+
+class BasicTiledLayerBuffer : public TiledLayerBuffer<BasicTiledLayerBuffer, BasicTiledLayerTile>
+{
+  friend class TiledLayerBuffer<BasicTiledLayerBuffer, BasicTiledLayerTile>;
+
+public:
+  void PaintThebes(BasicTiledThebesLayer* aLayer,
+                   const nsIntRegion& aNewValidRegion,
+                   const nsIntRegion& aPaintRegion,
+                   LayerManager::DrawThebesLayerCallback aCallback,
+                   void* aCallbackData);
+
+  BasicTiledLayerTile GetPlaceholderTile() const {
+    return mPlaceholder;
+  }
+
+  void ReadUnlock() {
+    for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
+      if (mRetainedTiles[i] == GetPlaceholderTile()) continue;
+      mRetainedTiles[i].ReadUnlock();
+    }
+  }
+
+  void ReadLock() {
+    for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
+      if (mRetainedTiles[i] == GetPlaceholderTile()) continue;
+      mRetainedTiles[i].ReadLock();
+    }
+  }
+
+protected:
+  BasicTiledLayerTile ValidateTile(BasicTiledLayerTile aTile,
+                                   const nsIntPoint& aTileRect,
+                                   const nsIntRegion& dirtyRect);
+
+  // If this returns true, we perform the paint operation into a single large
+  // buffer and copy it out to the tiles instead of calling PaintThebes() on
+  // each tile individually. Somewhat surprisingly, this turns out to be faster
+  // on Android.
+  bool UseSinglePaintBuffer() { return true; }
+
+  void ReleaseTile(BasicTiledLayerTile aTile) { /* No-op. */ }
+
+  void SwapTiles(BasicTiledLayerTile& aTileA, BasicTiledLayerTile& aTileB) {
+    std::swap(aTileA, aTileB);
+  }
+
+private:
+  BasicTiledThebesLayer* mThebesLayer;
+  LayerManager::DrawThebesLayerCallback mCallback;
+  void* mCallbackData;
+
+  // The buffer we use when UseSinglePaintBuffer() above is true.
+  nsRefPtr<gfxImageSurface>     mSinglePaintBuffer;
+  nsIntPoint                    mSinglePaintBufferOffset;
+
+  BasicTiledLayerTile           mPlaceholder;
+
+  BasicTiledLayerTile ValidateTileInternal(BasicTiledLayerTile aTile,
+                                           const nsIntPoint& aTileOrigin,
+                                           const nsIntRect& aDirtyRect);
+};
+
+/**
+ * An implementation of ThebesLayer that ONLY supports remote
+ * composition that is backed by tiles. This thebes layer implementation
+ * is better suited to mobile hardware to work around slow implementation
+ * of glTexImage2D (for OGL compositors), and restrait memory bandwidth.
+ */
+class BasicTiledThebesLayer : public ThebesLayer,
+                              public BasicImplData,
+                              public BasicShadowableLayer
+{
+  typedef ThebesLayer Base;
+
+public:
+  BasicTiledThebesLayer(BasicShadowLayerManager* const aManager)
+    : ThebesLayer(aManager, static_cast<BasicImplData*>(this))
+  {
+    MOZ_COUNT_CTOR(BasicTiledThebesLayer);
+  }
+
+  ~BasicTiledThebesLayer()
+  {
+    MOZ_COUNT_DTOR(BasicTiledThebesLayer);
+  }
+
+
+  // Thebes Layer
+  virtual Layer* AsLayer() { return this; }
+  virtual void InvalidateRegion(const nsIntRegion& aRegion) {
+    mValidRegion.Sub(mValidRegion, aRegion);
+  }
+
+  // BasicImplData
+  virtual bool MustRetainContent() { return HasShadow(); }
+
+  // Shadow methods
+  virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs);
+  virtual ShadowableLayer* AsShadowableLayer() { return this; }
+
+  virtual void Disconnect()
+  {
+    BasicShadowableLayer::Disconnect();
+  }
+
+  virtual void PaintThebes(gfxContext* aContext,
+                           LayerManager::DrawThebesLayerCallback aCallback,
+                           void* aCallbackData,
+                           ReadbackProcessor* aReadback);
+
+private:
+  BasicShadowLayerManager* BasicManager()
+  {
+    return static_cast<BasicShadowLayerManager*>(mManager);
+  }
+
+  // BasicImplData
+  virtual void
+  PaintBuffer(gfxContext* aContext,
+              const nsIntRegion& aRegionToDraw,
+              const nsIntRegion& aExtendedRegionToDraw,
+              const nsIntRegion& aRegionToInvalidate,
+              bool aDidSelfCopy,
+              LayerManager::DrawThebesLayerCallback aCallback,
+              void* aCallbackData)
+  { NS_RUNTIMEABORT("Not reached."); }
+
+  // Members
+  BasicTiledLayerBuffer mTiledBuffer;
+};
+
+} // layers
+} // mozilla
+
+#endif