Bug 1220629 - Part 4: Allow gfxContext to use the native pushlayer implementations based on a pref. r=jrmuizel draft
authorBas Schouten <bschouten@mozilla.com>
Wed, 06 Jan 2016 00:04:42 +0100
changeset 319145 78f439fd806576f9c4cd55ea49f12a2994e46fc3
parent 319144 058ed10659456f59761535f7538ced6df5425aec
child 319146 ec545e1bde6741e9bc461b1a6ab966e7b6c9236a
push id8986
push userbschouten@mozilla.com
push dateTue, 05 Jan 2016 23:23:40 +0000
reviewersjrmuizel
bugs1220629
milestone46.0a1
Bug 1220629 - Part 4: Allow gfxContext to use the native pushlayer implementations based on a pref. r=jrmuizel
gfx/layers/Layers.cpp
gfx/thebes/gfxContext.cpp
gfx/thebes/gfxPrefs.h
gfx/thebes/gfxWindowsNativeDrawing.cpp
modules/libpref/init/all.js
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -2426,17 +2426,17 @@ PrintInfo(std::stringstream& aStream, La
     AppendToString(aStream, aLayerComposite->GetShadowVisibleRegion().ToUnknownRegion(), " [shadow-visible=", "]");
   }
 }
 
 void
 SetAntialiasingFlags(Layer* aLayer, DrawTarget* aTarget)
 {
   bool permitSubpixelAA = !(aLayer->GetContentFlags() & Layer::CONTENT_DISABLE_SUBPIXEL_AA);
-  if (aTarget->GetFormat() != SurfaceFormat::B8G8R8A8) {
+  if (aTarget->IsCurrentGroupOpaque()) {
     aTarget->SetPermitSubpixelAA(permitSubpixelAA);
     return;
   }
 
   const IntRect& bounds = aLayer->GetVisibleRegion().ToUnknownRegion().GetBounds();
   gfx::Rect transformedBounds = aTarget->GetTransform().TransformBounds(gfx::Rect(Float(bounds.x), Float(bounds.y),
                                                                                   Float(bounds.width), Float(bounds.height)));
   transformedBounds.RoundOut();
--- a/gfx/thebes/gfxContext.cpp
+++ b/gfx/thebes/gfxContext.cpp
@@ -13,16 +13,17 @@
 #include "gfxContext.h"
 
 #include "gfxMatrix.h"
 #include "gfxUtils.h"
 #include "gfxASurface.h"
 #include "gfxPattern.h"
 #include "gfxPlatform.h"
 #include "gfxTeeSurface.h"
+#include "gfxPrefs.h"
 #include "GeckoProfiler.h"
 #include "gfx2DGlue.h"
 #include "mozilla/gfx/PathHelpers.h"
 #include "mozilla/gfx/DrawTargetTiled.h"
 #include <algorithm>
 
 #if XP_WIN
 #include "gfxWindowsPlatform.h"
@@ -803,29 +804,34 @@ gfxContext::Paint(gfxFloat alpha)
 
   mDT->FillRect(paintRect, PatternFromState(this),
                 DrawOptions(Float(alpha), GetOp()));
 }
 
 void
 gfxContext::PushGroupForBlendBack(gfxContentType content, Float aOpacity, SourceSurface* aMask, const Matrix& aMaskTransform)
 {
-  DrawTarget* oldDT = mDT;
+  if (gfxPrefs::UseNativePushLayer()) {
+    Save();
+    mDT->PushLayer(content == gfxContentType::COLOR, aOpacity, aMask, aMaskTransform);
+  } else {
+    DrawTarget* oldDT = mDT;
+
+    PushNewDT(content);
 
-  PushNewDT(content);
+    if (oldDT != mDT) {
+      PushClipsToDT(mDT);
+    }
+    mDT->SetTransform(GetDTTransform());
 
-  if (oldDT != mDT) {
-    PushClipsToDT(mDT);
+    CurrentState().mBlendOpacity = aOpacity;
+    CurrentState().mBlendMask = aMask;
+    CurrentState().mWasPushedForBlendBack = true;
+    CurrentState().mBlendMaskTransform = aMaskTransform;
   }
-  mDT->SetTransform(GetDTTransform());
-
-  CurrentState().mBlendOpacity = aOpacity;
-  CurrentState().mBlendMask = aMask;
-  CurrentState().mWasPushedForBlendBack = true;
-  CurrentState().mBlendMaskTransform = aMaskTransform;
 }
 
 static gfxRect
 GetRoundOutDeviceClipExtents(gfxContext* aCtx)
 {
   gfxContextMatrixAutoSaveRestore save(aCtx);
   aCtx->SetMatrix(gfxMatrix());
   gfxRect r = aCtx->GetClipExtents();
@@ -836,121 +842,138 @@ GetRoundOutDeviceClipExtents(gfxContext*
 void
 gfxContext::PushGroupAndCopyBackground(gfxContentType content, Float aOpacity, SourceSurface* aMask, const Matrix& aMaskTransform)
 {
   IntRect clipExtents;
   if (mDT->GetFormat() != SurfaceFormat::B8G8R8X8) {
     gfxRect clipRect = GetRoundOutDeviceClipExtents(this);
     clipExtents = IntRect(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
   }
-  if ((mDT->GetFormat() == SurfaceFormat::B8G8R8X8 ||
-       mDT->GetOpaqueRect().Contains(clipExtents)) &&
-      !mDT->GetUserData(&sDontUseAsSourceKey)) {
-    DrawTarget *oldDT = mDT;
-    RefPtr<SourceSurface> source = mDT->Snapshot();
-    Point oldDeviceOffset = CurrentState().deviceOffset;
+  bool pushOpaqueWithCopiedBG = (mDT->GetFormat() == SurfaceFormat::B8G8R8X8 ||
+                                 mDT->GetOpaqueRect().Contains(clipExtents)) &&
+                                !mDT->GetUserData(&sDontUseAsSourceKey);
+
+  if (gfxPrefs::UseNativePushLayer()) {
+    Save();
+
+    if (pushOpaqueWithCopiedBG) {
+      mDT->PushLayer(true, aOpacity, aMask, aMaskTransform, IntRect(), true);
+    } else {
+      mDT->PushLayer(content == gfxContentType::COLOR, aOpacity, aMask, aMaskTransform, IntRect(), false);
+    }
+  } else {
+    if (pushOpaqueWithCopiedBG) {
+      DrawTarget *oldDT = mDT;
+      RefPtr<SourceSurface> source = mDT->Snapshot();
+      Point oldDeviceOffset = CurrentState().deviceOffset;
+
+      PushNewDT(gfxContentType::COLOR);
+
+      if (oldDT == mDT) {
+        // Creating new DT failed.
+        return;
+      }
+
+      CurrentState().mBlendOpacity = aOpacity;
+      CurrentState().mBlendMask = aMask;
+      CurrentState().mWasPushedForBlendBack = true;
+      CurrentState().mBlendMaskTransform = aMaskTransform;
 
-    PushNewDT(gfxContentType::COLOR);
+      Point offset = CurrentState().deviceOffset - oldDeviceOffset;
+      Rect surfRect(0, 0, Float(mDT->GetSize().width), Float(mDT->GetSize().height));
+      Rect sourceRect = surfRect + offset;
+
+      mDT->SetTransform(Matrix());
 
-    if (oldDT == mDT) {
-      // Creating new DT failed.
+      // XXX: It's really sad that we have to do this (for performance).
+      // Once DrawTarget gets a PushLayer API we can implement this within
+      // DrawTargetTiled.
+      if (source->GetType() == SurfaceType::TILED) {
+        SnapshotTiled *sourceTiled = static_cast<SnapshotTiled*>(source.get());
+        for (uint32_t i = 0; i < sourceTiled->mSnapshots.size(); i++) {
+          Rect tileSourceRect = sourceRect.Intersect(Rect(sourceTiled->mOrigins[i].x,
+                                                          sourceTiled->mOrigins[i].y,
+                                                          sourceTiled->mSnapshots[i]->GetSize().width,
+                                                          sourceTiled->mSnapshots[i]->GetSize().height));
+
+          if (tileSourceRect.IsEmpty()) {
+            continue;
+          }
+          Rect tileDestRect = tileSourceRect - offset;
+          tileSourceRect -= sourceTiled->mOrigins[i];
+
+          mDT->DrawSurface(sourceTiled->mSnapshots[i], tileDestRect, tileSourceRect);
+        }
+      } else {
+        mDT->DrawSurface(source, surfRect, sourceRect);
+      }
+      mDT->SetOpaqueRect(oldDT->GetOpaqueRect());
+
+      PushClipsToDT(mDT);
+      mDT->SetTransform(GetDTTransform());
       return;
     }
+    DrawTarget* oldDT = mDT;
 
+    PushNewDT(content);
+
+    if (oldDT != mDT) {
+      PushClipsToDT(mDT);
+    }
+
+    mDT->SetTransform(GetDTTransform());
     CurrentState().mBlendOpacity = aOpacity;
     CurrentState().mBlendMask = aMask;
     CurrentState().mWasPushedForBlendBack = true;
     CurrentState().mBlendMaskTransform = aMaskTransform;
-
-    Point offset = CurrentState().deviceOffset - oldDeviceOffset;
-    Rect surfRect(0, 0, Float(mDT->GetSize().width), Float(mDT->GetSize().height));
-    Rect sourceRect = surfRect + offset;
-
-    mDT->SetTransform(Matrix());
-
-    // XXX: It's really sad that we have to do this (for performance).
-    // Once DrawTarget gets a PushLayer API we can implement this within
-    // DrawTargetTiled.
-    if (source->GetType() == SurfaceType::TILED) {
-      SnapshotTiled *sourceTiled = static_cast<SnapshotTiled*>(source.get());
-      for (uint32_t i = 0; i < sourceTiled->mSnapshots.size(); i++) {
-        Rect tileSourceRect = sourceRect.Intersect(Rect(sourceTiled->mOrigins[i].x,
-                                                        sourceTiled->mOrigins[i].y,
-                                                        sourceTiled->mSnapshots[i]->GetSize().width,
-                                                        sourceTiled->mSnapshots[i]->GetSize().height));
-
-        if (tileSourceRect.IsEmpty()) {
-          continue;
-        }
-        Rect tileDestRect = tileSourceRect - offset;
-        tileSourceRect -= sourceTiled->mOrigins[i];
-
-        mDT->DrawSurface(sourceTiled->mSnapshots[i], tileDestRect, tileSourceRect);
-      }
-    } else {
-      mDT->DrawSurface(source, surfRect, sourceRect);
-    }
-    mDT->SetOpaqueRect(oldDT->GetOpaqueRect());
-
-    PushClipsToDT(mDT);
-    mDT->SetTransform(GetDTTransform());
-    return;
   }
-  DrawTarget* oldDT = mDT;
-
-  PushNewDT(content);
-
-  if (oldDT != mDT) {
-    PushClipsToDT(mDT);
-  }
-
-  mDT->SetTransform(GetDTTransform());
-  CurrentState().mBlendOpacity = aOpacity;
-  CurrentState().mBlendMask = aMask;
-  CurrentState().mWasPushedForBlendBack = true;
-  CurrentState().mBlendMaskTransform = aMaskTransform;
 }
 
 void
 gfxContext::PopGroupAndBlend()
 {
-  MOZ_ASSERT(CurrentState().mWasPushedForBlendBack);
-  Float opacity = CurrentState().mBlendOpacity;
-  RefPtr<SourceSurface> mask = CurrentState().mBlendMask;
-  Matrix maskTransform = CurrentState().mBlendMaskTransform;
+  if (gfxPrefs::UseNativePushLayer()) {
+    mDT->PopLayer();
+    Restore();
+  } else {
+    MOZ_ASSERT(CurrentState().mWasPushedForBlendBack);
+    Float opacity = CurrentState().mBlendOpacity;
+    RefPtr<SourceSurface> mask = CurrentState().mBlendMask;
+    Matrix maskTransform = CurrentState().mBlendMaskTransform;
 
-  RefPtr<SourceSurface> src = mDT->Snapshot();
-  Point deviceOffset = CurrentState().deviceOffset;
-  Restore();
-  CurrentState().sourceSurfCairo = nullptr;
-  CurrentState().sourceSurface = src;
-  CurrentState().sourceSurfaceDeviceOffset = deviceOffset;
-  CurrentState().pattern = nullptr;
-  CurrentState().patternTransformChanged = false;
+    RefPtr<SourceSurface> src = mDT->Snapshot();
+    Point deviceOffset = CurrentState().deviceOffset;
+    Restore();
+    CurrentState().sourceSurfCairo = nullptr;
+    CurrentState().sourceSurface = src;
+    CurrentState().sourceSurfaceDeviceOffset = deviceOffset;
+    CurrentState().pattern = nullptr;
+    CurrentState().patternTransformChanged = false;
 
-  Matrix mat = mTransform;
-  mat.Invert();
-  mat.PreTranslate(deviceOffset.x, deviceOffset.y); // device offset translation
+    Matrix mat = mTransform;
+    mat.Invert();
+    mat.PreTranslate(deviceOffset.x, deviceOffset.y); // device offset translation
 
-  CurrentState().surfTransform = mat;
+    CurrentState().surfTransform = mat;
 
-  CompositionOp oldOp = GetOp();
-  SetOp(CompositionOp::OP_OVER);
+    CompositionOp oldOp = GetOp();
+    SetOp(CompositionOp::OP_OVER);
 
-  if (mask) {
-    if (!maskTransform.HasNonTranslation()) {
-      Mask(mask, opacity, Point(maskTransform._31, maskTransform._32));
+    if (mask) {
+      if (!maskTransform.HasNonTranslation()) {
+        Mask(mask, opacity, Point(maskTransform._31, maskTransform._32));
+      } else {
+        Mask(mask, opacity, maskTransform);
+      }
     } else {
-      Mask(mask, opacity, maskTransform);
+      Paint(opacity);
     }
-  } else {
-    Paint(opacity);
+
+    SetOp(oldOp);
   }
-
-  SetOp(oldOp);
 }
 
 #ifdef MOZ_DUMP_PAINTING
 void
 gfxContext::WriteAsPNG(const char* aFile)
 {
   gfxUtils::WriteAsPNG(mDT, aFile);
 }
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -252,16 +252,18 @@ private:
   // The maximums here are quite conservative, we can tighten them if problems show up.
   DECL_GFX_PREF(Once, "gfx.max-alloc-size",                    MaxAllocSize, int32_t, (int32_t)500000000);
   DECL_GFX_PREF(Once, "gfx.max-texture-size",                  MaxTextureSize, int32_t, (int32_t)32767);
   DECL_GFX_PREF(Live, "gfx.perf-warnings.enabled",             PerfWarnings, bool, false);
   DECL_GFX_PREF(Live, "gfx.SurfaceTexture.detach.enabled",     SurfaceTextureDetachEnabled, bool, true);
   DECL_GFX_PREF(Live, "gfx.testing.device-reset",              DeviceResetForTesting, int32_t, 0);
   DECL_GFX_PREF(Live, "gfx.testing.device-fail",               DeviceFailForTesting, bool, false);
 
+  DECL_GFX_PREF(Live, "gfx.content.use-native-pushlayer",      UseNativePushLayer, bool, false);
+
   // These times should be in milliseconds
   DECL_GFX_PREF(Once, "gfx.touch.resample.delay-threshold",    TouchResampleVsyncDelayThreshold, int32_t, 20);
   DECL_GFX_PREF(Once, "gfx.touch.resample.max-predict",        TouchResampleMaxPredict, int32_t, 8);
   DECL_GFX_PREF(Once, "gfx.touch.resample.min-delta",          TouchResampleMinDelta, int32_t, 2);
   DECL_GFX_PREF(Once, "gfx.touch.resample.old-touch-threshold",TouchResampleOldTouchThreshold, int32_t, 17);
   DECL_GFX_PREF(Once, "gfx.touch.resample.vsync-adjust",       TouchVsyncSampleAdjust, int32_t, 5);
 
   DECL_GFX_PREF(Once, "gfx.vr.mirror-textures",                VRMirrorTextures, bool, false);
--- a/gfx/thebes/gfxWindowsNativeDrawing.cpp
+++ b/gfx/thebes/gfxWindowsNativeDrawing.cpp
@@ -48,19 +48,23 @@ gfxWindowsNativeDrawing::BeginNativeDraw
     if (mRenderState == RENDER_STATE_INIT) {
         RefPtr<gfxASurface> surf;
         DrawTarget* drawTarget = mContext->GetDrawTarget();
         cairo_t* cairo = nullptr;
         if (drawTarget->GetBackendType() == BackendType::CAIRO) {
             cairo = static_cast<cairo_t*>
                 (drawTarget->GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT));
             if (cairo) {
-                cairo_surface_t* s = cairo_get_target(cairo);
+                cairo_surface_t* s = cairo_get_group_target(cairo);
                 if (s) {
                     mDeviceOffset = mContext->GetDeviceOffset();
+                    double sdx, sdy;
+                    cairo_surface_get_device_offset(s, &sdx, &sdy);
+                    mDeviceOffset.x -= sdx;
+                    mDeviceOffset.y -= sdy;
                     surf = gfxASurface::Wrap(s);
                 }
             }
         }
 
         if (surf && surf->CairoStatus() != 0)
             return nullptr;
 
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4336,16 +4336,19 @@ pref("layers.offmainthreadcomposition.as
 
 // Whether to log information about off main thread animations to stderr
 pref("layers.offmainthreadcomposition.log-animations", false);
 
 pref("layers.bufferrotation.enabled", true);
 
 pref("layers.componentalpha.enabled", true);
 
+// Use the DT-backend implemented PushLayer
+pref("gfx.content.use-native-pushlayer", false);
+
 #ifdef ANDROID
 pref("gfx.apitrace.enabled",false);
 #endif
 
 #ifdef MOZ_X11
 #ifdef MOZ_WIDGET_GTK
 pref("gfx.xrender.enabled",true);
 #endif