Bug 1392453: Allow limiting the blend surface area and the portion of the layer being resolved. r=mchang
authorBas Schouten <bschouten@mozilla.com>
Tue, 12 Sep 2017 19:10:55 +0000
changeset 430053 f84944ac039d2db1cbfcd521634ca12665710074
parent 430052 5ea410f10e0512c7b8c55c7148765f1fa185314b
child 430054 a3b2778fa5971e7159cc16297ff2b1dfcb41aa9d
push id7761
push userjlund@mozilla.com
push dateFri, 15 Sep 2017 00:19:52 +0000
treeherdermozilla-beta@c38455951db4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmchang
bugs1392453
milestone57.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 1392453: Allow limiting the blend surface area and the portion of the layer being resolved. r=mchang This introduces the infrastructure to specify the bounds of drawing commands being executed. These bounds can then be used to limit operations done for blending in order to reduce required fillrate. Currently we only specify the bounds for DrawSurface but it would be easy to extend this for other drawing commands if so desired. MozReview-Commit-ID: BUFJzphfdKc
gfx/2d/DrawTargetD2D1.cpp
gfx/2d/DrawTargetD2D1.h
--- a/gfx/2d/DrawTargetD2D1.cpp
+++ b/gfx/2d/DrawTargetD2D1.cpp
@@ -233,17 +233,18 @@ DrawTargetD2D1::DrawSurface(SourceSurfac
                                                      D2D1_EXTEND_MODE_CLAMP,
                                                      D2D1_EXTEND_MODE_CLAMP,
                                                      D2DInterpolationMode(aSurfOptions.mSamplingFilter)),
                           D2D1::BrushProperties(aOptions.mAlpha, D2DMatrix(transform)),
                           getter_AddRefs(brush));
     mDC->FillRectangle(D2DRect(aDest), brush);
   }
 
-  FinalizeDrawing(aOptions.mCompositionOp, ColorPattern(Color()));
+  Rect destBounds = mTransform.TransformBounds(aDest);
+  FinalizeDrawing(aOptions.mCompositionOp, ColorPattern(Color()), &destBounds);
 }
 
 void
 DrawTargetD2D1::DrawFilter(FilterNode *aNode,
                            const Rect &aSourceRect,
                            const Point &aDestPoint,
                            const DrawOptions &aOptions)
 {
@@ -1339,17 +1340,17 @@ DrawTargetD2D1::PrepareForDrawing(Compos
   if (ShouldClipTemporarySurfaceDrawing(aOp, aPattern, clipIsComplex)) {
     PushClipsToDC(mDC);
   }
 
   FlushTransformToDC();
 }
 
 void
-DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern)
+DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern, const Rect* aAffectedRect /* = nullptr */)
 {
   bool patternSupported = IsPatternSupportedByD2D(aPattern);
 
   if (D2DSupportsPrimitiveBlendMode(aOp) && patternSupported) {
     if (aOp != CompositionOp::OP_OVER)
       mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER);
     return;
   }
@@ -1402,25 +1403,35 @@ DrawTargetD2D1::FinalizeDrawing(Composit
     RefPtr<ID2D1Effect> blendEffect;
     HRESULT hr = mDC->CreateEffect(CLSID_D2D1Blend, getter_AddRefs(blendEffect));
 
     if (FAILED(hr) || !blendEffect) {
       gfxWarning() << "Failed to create blend effect!";
       return;
     }
 
-    // We don't need to preserve the current content of this layer as the output
+    Point outOffset;
+    // We don't need to preserve the current content of this layer if the output
     // of the blend effect should completely replace it.
-    RefPtr<ID2D1Image> tmpImage = GetImageForLayerContent(false);
+    bool shouldPreserveContent = !!aAffectedRect && !aAffectedRect->Contains(Rect(0, 0, mSize.width, mSize.height));
+    RefPtr<ID2D1Image> tmpImage = GetImageForLayerContent(shouldPreserveContent, aAffectedRect, &outOffset);
     if (!tmpImage) {
       return;
     }
 
     blendEffect->SetInput(0, tmpImage);
     blendEffect->SetInput(1, source);
+
+    if (outOffset != Point()) {
+      RefPtr<ID2D1Effect> transformEffect;
+      mDC->CreateEffect(CLSID_D2D12DAffineTransform, getter_AddRefs(transformEffect));
+      transformEffect->SetInput(0, tmpImage);
+      transformEffect->SetValue(D2D1_2DAFFINETRANSFORM_PROP_TRANSFORM_MATRIX, D2D1::Matrix3x2F::Translation(outOffset.x, outOffset.y));
+      blendEffect->SetInputEffect(0, transformEffect);
+    }
     blendEffect->SetValue(D2D1_BLEND_PROP_MODE, D2DBlendMode(aOp));
 
     mDC->DrawImage(blendEffect, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY);
 
     // This may seem a little counter intuitive. If this is false, we go through the regular
     // codepaths and set it to true. When this was true, GetImageForLayerContent will return
     // a bitmap for the current command list and we will no longer have a complex blend
     // with a list for tmpImage. Therefore we can set it to false again.
@@ -1502,20 +1513,24 @@ DrawTargetD2D1::GetDeviceSpaceClipRect(D
     if (!iter->mIsPixelAligned) {
       aIsPixelAligned = false;
     }
   }
   return true;
 }
 
 already_AddRefed<ID2D1Image>
-DrawTargetD2D1::GetImageForLayerContent(bool aShouldPreserveContent)
+DrawTargetD2D1::GetImageForLayerContent(bool aShouldPreserveContent, const Rect* aBounds /* = nullptr */, Point* aOutOffset /* = nullptr */) 
 {
   PopAllClips();
 
+  if (aOutOffset) {
+    *aOutOffset = Point();
+  }
+
   if (!CurrentLayer().mCurrentList) {
     RefPtr<ID2D1Bitmap> tmpBitmap;
     HRESULT hr = mDC->CreateBitmap(D2DIntSize(mSize), D2D1::BitmapProperties(D2DPixelFormat(mFormat)), getter_AddRefs(tmpBitmap));
     if (FAILED(hr)) {
       gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(mSize))) << "[D2D1.1] 6CreateBitmap failure " << mSize << " Code: " << hexa(hr) << " format " << (int)mFormat;
       // If it's a recreate target error, return and handle it elsewhere.
       if (hr == D2DERR_RECREATE_TARGET) {
         mDC->Flush();
@@ -1535,20 +1550,30 @@ DrawTargetD2D1::GetImageForLayerContent(
     list->Close();
 
     RefPtr<ID2D1Bitmap1> tmpBitmap;
     if (mDidComplexBlendWithListInList) {
       D2D1_BITMAP_PROPERTIES1 props =
         D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_TARGET,
                                 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM,
                                                   D2D1_ALPHA_MODE_PREMULTIPLIED));
-      mDC->CreateBitmap(mBitmap->GetPixelSize(), nullptr, 0, &props, getter_AddRefs(tmpBitmap));
+      D2D1_SIZE_U size = mBitmap->GetPixelSize();
+      D2D1_POINT_2F offset = D2D1::Point2F();
+      if (aBounds) {
+        size.width = aBounds->width;
+        size.height = aBounds->height;
+        offset.x = -aBounds->x;
+        offset.y = -aBounds->y;
+        aOutOffset->x = aBounds->x;
+        aOutOffset->y = aBounds->y;
+      }
+      mDC->CreateBitmap(size, nullptr, 0, &props, getter_AddRefs(tmpBitmap));
       mDC->SetTransform(D2D1::IdentityMatrix());
       mDC->SetTarget(tmpBitmap);
-      mDC->DrawImage(list, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY);
+      mDC->DrawImage(list, offset, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY);
       mDC->SetTarget(CurrentTarget());
     }
 
     DCCommandSink sink(mDC);
 
     if (aShouldPreserveContent) {
       list->Stream(&sink);
       PushAllClips();
--- a/gfx/2d/DrawTargetD2D1.h
+++ b/gfx/2d/DrawTargetD2D1.h
@@ -175,27 +175,33 @@ private:
 
   typedef std::unordered_set<DrawTargetD2D1*> TargetSet;
 
   // This function will mark the surface as changing, and make sure any
   // copy-on-write snapshots are notified.
   void MarkChanged();
   bool ShouldClipTemporarySurfaceDrawing(CompositionOp aOp, const Pattern& aPattern, bool aClipIsComplex);
   void PrepareForDrawing(CompositionOp aOp, const Pattern &aPattern);
-  void FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern);
+  // aAffectedRect may be used to supply the bounds of the drawing operations in order to prevent
+  // excessive surface area being operated on
+  void FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern, const Rect* aAffectedRect = nullptr);
   void FlushTransformToDC() {
     if (mTransformDirty) {
       mDC->SetTransform(D2DMatrix(mTransform));
       mTransformDirty = false;
     }
   }
   void AddDependencyOnSource(SourceSurfaceD2D1* aSource);
 
   // Must be called with all clips popped and an identity matrix set.
-  already_AddRefed<ID2D1Image> GetImageForLayerContent(bool aShouldPreserveContent = true);
+  // aBounds can be specified to allow the function to return only part of the layer contents
+  // aOutOffset will return the offset that should be applied to move the ID2D1Image onto the
+  // correct portion of the destination layer. If !aBounds this will always be 0,0 and can be
+  // ignored.
+  already_AddRefed<ID2D1Image> GetImageForLayerContent(bool aShouldPreserveContent = true, const Rect* aBounds = nullptr, Point* aOutOffset = nullptr);
 
   ID2D1Image* CurrentTarget()
   {
     if (CurrentLayer().mCurrentList) {
       return CurrentLayer().mCurrentList;
     }
     return mBitmap;
   }