Bug 781731 - Speed up shadows by using tee surface to avoid doing a read back; r=roc
authorAnthony Jones <ajones@mozilla.com>
Mon, 27 Aug 2012 11:34:07 +0200
changeset 105584 aec0729913fee9e86cf3d7aff9315382f963b5ec
parent 105583 4041007e49cd37f4fa1018bbda066c8cc158e2a9
child 105585 3b2797ed0518b438b468bac4d1a76f08afc02be7
push id55
push usershu@rfrn.org
push dateThu, 30 Aug 2012 01:33:09 +0000
reviewersroc
bugs781731
milestone17.0a1
Bug 781731 - Speed up shadows by using tee surface to avoid doing a read back; r=roc
content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
gfx/2d/2D.h
gfx/2d/DrawTargetCairo.cpp
gfx/2d/DrawTargetCairo.h
--- a/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
@@ -306,17 +306,17 @@ public:
     }
 
     mTempRect.ScaleRoundOut(1.0f);
 
     transform._31 -= mTempRect.x;
     transform._32 -= mTempRect.y;
       
     mTarget =
-      mCtx->mTarget->CreateSimilarDrawTarget(IntSize(int32_t(mTempRect.width), int32_t(mTempRect.height)),
+      mCtx->mTarget->CreateShadowDrawTarget(IntSize(int32_t(mTempRect.width), int32_t(mTempRect.height)),
                                              FORMAT_B8G8R8A8);
 
     if (!mTarget) {
       // XXX - Deal with the situation where our temp size is too big to
       // fit in a texture.
       mTarget = ctx->mTarget;
       mCtx = nullptr;
     } else {
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -723,16 +723,25 @@ public:
 
   /*
    * Create a DrawTarget whose snapshot is optimized for use with this DrawTarget.
    */
   virtual TemporaryRef<DrawTarget>
     CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const = 0;
 
   /*
+   * Create a draw target optimized for drawing a shadow.
+   */
+  virtual TemporaryRef<DrawTarget>
+    CreateShadowDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
+  {
+    return CreateSimilarDrawTarget(aSize, aFormat);
+  }
+
+  /*
    * Create a path builder with the specified fillmode.
    *
    * We need the fill mode up front because of Direct2D.
    * ID2D1SimplifiedGeometrySink requires the fill mode
    * to be set before calling BeginFigure().
    */
   virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FILL_WINDING) const = 0;
 
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -6,16 +6,17 @@
 #include "DrawTargetCairo.h"
 
 #include "SourceSurfaceCairo.h"
 #include "PathCairo.h"
 #include "HelpersCairo.h"
 #include "ScaledFontBase.h"
 
 #include "cairo.h"
+#include "cairo-tee.h"
 #include <string.h>
 
 #include "Blur.h"
 #include "Logging.h"
 #include "Tools.h"
 
 #ifdef CAIRO_HAS_QUARTZ_SURFACE
 #include "cairo-quartz.h"
@@ -386,29 +387,21 @@ DrawTargetCairo::DrawSurfaceWithShadow(S
   if (aSurface->GetType() != SURFACE_CAIRO) {
     return;
   }
 
   Float width = aSurface->GetSize().width;
   Float height = aSurface->GetSize().height;
  
   SourceSurfaceCairo* source = static_cast<SourceSurfaceCairo*>(aSurface);
-  cairo_surface_t* surf = source->GetSurface();
+  cairo_surface_t* sourcesurf = source->GetSurface();
+  cairo_surface_t* blursurf = cairo_tee_surface_index(sourcesurf, 0);
+  cairo_surface_t* surf = cairo_tee_surface_index(sourcesurf, 1);
  
-  cairo_surface_t* blursurf = cairo_image_surface_create(CAIRO_FORMAT_A8,
-                                                         width,
-                                                         height);
-
-  cairo_t* ctx = cairo_create(blursurf);
-  cairo_set_source_surface(ctx, surf, 0, 0);
-  cairo_new_path(ctx);
-  cairo_rectangle(ctx, 0, 0, width, height);
-  cairo_fill(ctx);
-  cairo_destroy(ctx);
- 
+  MOZ_ASSERT(cairo_surface_get_type(blursurf) == CAIRO_SURFACE_TYPE_IMAGE);
   Rect extents(0, 0, width, height);
   AlphaBoxBlur blur(cairo_image_surface_get_data(blursurf),
                     extents,
                     cairo_image_surface_get_stride(blursurf),
                     aSigma);
   blur.Blur();
 
   WillChange();
@@ -439,17 +432,16 @@ DrawTargetCairo::DrawSurfaceWithShadow(S
     // Now that the shadow has been drawn, we can draw the surface on top.
     cairo_set_source_surface(mContext, surf, 0, 0);
     cairo_new_path(mContext);
     cairo_rectangle(mContext, 0, 0, width, height);
     cairo_fill(mContext);
   }
 
   cairo_restore(mContext);
-  cairo_surface_destroy(blursurf);
 }
 
 void
 DrawTargetCairo::DrawPattern(const Pattern& aPattern,
                              const StrokeOptions& aStrokeOptions,
                              const DrawOptions& aOptions,
                              DrawPatternType aDrawType)
 {
@@ -794,16 +786,50 @@ DrawTargetCairo::InitAlreadyReferenced(c
   mContext = cairo_create(aSurface);
   mSurface = aSurface;
   mSize = aSize;
   mFormat = CairoContentToGfxFormat(cairo_surface_get_content(aSurface));
 
   return true;
 }
 
+TemporaryRef<DrawTarget>
+DrawTargetCairo::CreateShadowDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
+{
+  cairo_surface_t* similar = cairo_surface_create_similar(cairo_get_target(mContext),
+                                                          GfxFormatToCairoContent(aFormat),
+                                                          aSize.width, aSize.height);
+
+  if (cairo_surface_status(similar)) {
+    return nullptr;
+  }
+
+  cairo_surface_t* blursurf = cairo_image_surface_create(CAIRO_FORMAT_A8,
+                                                         aSize.width,
+                                                         aSize.height);
+
+  if (cairo_surface_status(blursurf)) {
+    return nullptr;
+  }
+
+  cairo_surface_t* tee = cairo_tee_surface_create(blursurf);
+  cairo_surface_destroy(blursurf);
+  if (cairo_surface_status(tee)) {
+    cairo_surface_destroy(similar);
+    return nullptr;
+  }
+
+  cairo_tee_surface_add(tee, similar);
+  cairo_surface_destroy(similar);
+
+  RefPtr<DrawTargetCairo> target = new DrawTargetCairo();
+  target->InitAlreadyReferenced(tee, aSize);
+  return target;
+}
+
 bool
 DrawTargetCairo::Init(cairo_surface_t* aSurface, const IntSize& aSize)
 {
   cairo_surface_reference(aSurface);
   return InitAlreadyReferenced(aSurface, aSize);
 }
 
 void *
--- a/gfx/2d/DrawTargetCairo.h
+++ b/gfx/2d/DrawTargetCairo.h
@@ -110,16 +110,18 @@ public:
                                                             const IntSize &aSize,
                                                             int32_t aStride,
                                                             SurfaceFormat aFormat) const;
   virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const;
   virtual TemporaryRef<SourceSurface>
     CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const;
   virtual TemporaryRef<DrawTarget>
     CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const;
+  virtual TemporaryRef<DrawTarget>
+    CreateShadowDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const;
 
   virtual TemporaryRef<GradientStops>
     CreateGradientStops(GradientStop *aStops,
                         uint32_t aNumStops,
                         ExtendMode aExtendMode = EXTEND_CLAMP) const;
 
   virtual void *GetNativeSurface(NativeSurfaceType aType);