1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include <stdint.h>                 // for uint32_t
8 #include <stdlib.h>                 // for rand, RAND_MAX
9 #include <sys/types.h>              // for int32_t
10 #include <stack>                    // for stack
11 #include "BasicContainerLayer.h"    // for BasicContainerLayer
12 #include "BasicLayersImpl.h"        // for ToData, BasicReadbackLayer, etc
13 #include "GeckoProfiler.h"          // for AUTO_PROFILER_LABEL
14 #include "ImageContainer.h"         // for ImageFactory
15 #include "Layers.h"                 // for Layer, ContainerLayer, etc
16 #include "ReadbackLayer.h"          // for ReadbackLayer
17 #include "ReadbackProcessor.h"      // for ReadbackProcessor
18 #include "RenderTrace.h"            // for RenderTraceLayers, etc
19 #include "basic/BasicImplData.h"    // for BasicImplData
20 #include "basic/BasicLayers.h"      // for BasicLayerManager, etc
21 #include "gfxASurface.h"            // for gfxASurface, etc
22 #include "gfxContext.h"             // for gfxContext, etc
23 #include "gfxImageSurface.h"        // for gfxImageSurface
24 #include "gfxMatrix.h"              // for gfxMatrix
25 #include "gfxPlatform.h"            // for gfxPlatform
26 #include "gfxPrefs.h"               // for gfxPrefs
27 #include "gfxPoint.h"               // for IntSize, gfxPoint
28 #include "gfxRect.h"                // for gfxRect
29 #include "gfxUtils.h"               // for gfxUtils
30 #include "gfx2DGlue.h"              // for thebes --> moz2d transition
31 #include "mozilla/Assertions.h"     // for MOZ_ASSERT, etc
32 #include "mozilla/WidgetUtils.h"    // for ScreenRotation
33 #include "mozilla/gfx/2D.h"         // for DrawTarget
34 #include "mozilla/gfx/BasePoint.h"  // for BasePoint
35 #include "mozilla/gfx/BaseRect.h"   // for BaseRect
36 #include "mozilla/gfx/Matrix.h"     // for Matrix
37 #include "mozilla/gfx/PathHelpers.h"
38 #include "mozilla/gfx/Rect.h"            // for IntRect, Rect
39 #include "mozilla/layers/LayersTypes.h"  // for BufferMode::BUFFER_NONE, etc
40 #include "mozilla/mozalloc.h"            // for operator new
41 #include "nsCOMPtr.h"                    // for already_AddRefed
42 #include "nsDebug.h"                     // for NS_ASSERTION, etc
43 #include "nsISupportsImpl.h"             // for gfxContext::Release, etc
44 #include "nsPoint.h"                     // for nsIntPoint
45 #include "nsRect.h"                      // for mozilla::gfx::IntRect
46 #include "nsRegion.h"                    // for nsIntRegion, etc
47 #include "nsTArray.h"                    // for AutoTArray
48 #include "TreeTraversal.h"               // for ForEachNode
49 
50 class nsIWidget;
51 
52 namespace mozilla {
53 namespace layers {
54 
55 using namespace mozilla::gfx;
56 
57 /**
58  * Clips to the smallest device-pixel-aligned rectangle containing aRect
59  * in user space.
60  * Returns true if the clip is "perfect", i.e. we actually clipped exactly to
61  * aRect.
62  */
ClipToContain(gfxContext * aContext,const IntRect & aRect)63 static bool ClipToContain(gfxContext* aContext, const IntRect& aRect) {
64   gfxRect userRect(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height());
65   gfxRect deviceRect = aContext->UserToDevice(userRect);
66   deviceRect.RoundOut();
67 
68   Matrix currentMatrix = aContext->CurrentMatrix();
69   aContext->SetMatrix(Matrix());
70   aContext->NewPath();
71   aContext->Rectangle(deviceRect);
72   aContext->Clip();
73   aContext->SetMatrix(currentMatrix);
74 
75   return aContext->DeviceToUser(deviceRect).IsEqualInterior(userRect);
76 }
77 
PushGroupForLayer(gfxContext * aContext,Layer * aLayer,const nsIntRegion & aRegion,PushedGroup & aGroupResult)78 bool BasicLayerManager::PushGroupForLayer(gfxContext* aContext, Layer* aLayer,
79                                           const nsIntRegion& aRegion,
80                                           PushedGroup& aGroupResult) {
81   aGroupResult.mVisibleRegion = aRegion;
82   aGroupResult.mFinalTarget = aContext;
83   aGroupResult.mOperator = GetEffectiveOperator(aLayer);
84   aGroupResult.mOpacity = aLayer->GetEffectiveOpacity();
85 
86   // If we need to call PushGroup, we should clip to the smallest possible
87   // area first to minimize the size of the temporary surface.
88   bool didCompleteClip = ClipToContain(aContext, aRegion.GetBounds());
89 
90   bool canPushGroup =
91       aGroupResult.mOperator == CompositionOp::OP_OVER ||
92       (aGroupResult.mOperator == CompositionOp::OP_SOURCE &&
93        (aLayer->CanUseOpaqueSurface() ||
94         aLayer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA));
95 
96   if (!canPushGroup) {
97     aContext->Save();
98     gfxUtils::ClipToRegion(aGroupResult.mFinalTarget,
99                            aGroupResult.mVisibleRegion);
100 
101     // PushGroup/PopGroup do not support non operator over.
102     gfxRect rect = aContext->GetClipExtents(gfxContext::eDeviceSpace);
103     rect.RoundOut();
104     IntRect surfRect;
105     ToRect(rect).ToIntRect(&surfRect);
106 
107     if (!surfRect.IsEmpty()) {
108       RefPtr<DrawTarget> dt =
109           aContext->GetDrawTarget()->CreateSimilarDrawTarget(
110               surfRect.Size(), SurfaceFormat::B8G8R8A8);
111 
112       RefPtr<gfxContext> ctx =
113           gfxContext::CreateOrNull(dt, ToRect(rect).TopLeft());
114       if (!ctx) {
115         gfxCriticalNote
116             << "BasicLayerManager context problem in PushGroupForLayer "
117             << gfx::hexa(dt);
118         return false;
119       }
120       ctx->SetMatrix(aContext->CurrentMatrix());
121 
122       aGroupResult.mGroupOffset = surfRect.TopLeft();
123       aGroupResult.mGroupTarget = ctx;
124 
125       aGroupResult.mMaskSurface =
126           GetMaskForLayer(aLayer, &aGroupResult.mMaskTransform);
127       return true;
128     }
129     aContext->Restore();
130   }
131 
132   Matrix maskTransform;
133   RefPtr<SourceSurface> maskSurf = GetMaskForLayer(aLayer, &maskTransform);
134 
135   if (maskSurf) {
136     // The returned transform will transform the mask to device space on the
137     // destination. Since the User->Device space transform will be applied
138     // to the mask by PopGroupAndBlend we need to adjust the transform to
139     // transform the mask to user space.
140     Matrix currentTransform = aGroupResult.mFinalTarget->CurrentMatrix();
141     currentTransform.Invert();
142     maskTransform = maskTransform * currentTransform;
143   }
144 
145   if (aLayer->CanUseOpaqueSurface() &&
146       ((didCompleteClip && aRegion.GetNumRects() == 1) ||
147        !aContext->CurrentMatrix().HasNonIntegerTranslation())) {
148     // If the layer is opaque in its visible region we can push a
149     // gfxContentType::COLOR group. We need to make sure that only pixels inside
150     // the layer's visible region are copied back to the destination. Remember
151     // if we've already clipped precisely to the visible region.
152     aGroupResult.mNeedsClipToVisibleRegion =
153         !didCompleteClip || aRegion.GetNumRects() > 1;
154     if (aGroupResult.mNeedsClipToVisibleRegion) {
155       aGroupResult.mFinalTarget->Save();
156       gfxUtils::ClipToRegion(aGroupResult.mFinalTarget,
157                              aGroupResult.mVisibleRegion);
158     }
159 
160     aContext->PushGroupForBlendBack(
161         gfxContentType::COLOR, aGroupResult.mOpacity, maskSurf, maskTransform);
162   } else {
163     if (aLayer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA) {
164       aContext->PushGroupAndCopyBackground(gfxContentType::COLOR_ALPHA,
165                                            aGroupResult.mOpacity, maskSurf,
166                                            maskTransform);
167     } else {
168       aContext->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA,
169                                       aGroupResult.mOpacity, maskSurf,
170                                       maskTransform);
171     }
172   }
173 
174   aGroupResult.mGroupTarget = aGroupResult.mFinalTarget;
175 
176   return true;
177 }
178 
PopGroupForLayer(PushedGroup & group)179 void BasicLayerManager::PopGroupForLayer(PushedGroup& group) {
180   if (group.mFinalTarget == group.mGroupTarget) {
181     group.mFinalTarget->PopGroupAndBlend();
182     if (group.mNeedsClipToVisibleRegion) {
183       group.mFinalTarget->Restore();
184     }
185     return;
186   }
187 
188   DrawTarget* dt = group.mFinalTarget->GetDrawTarget();
189   RefPtr<DrawTarget> sourceDT = group.mGroupTarget->GetDrawTarget();
190   group.mGroupTarget = nullptr;
191 
192   RefPtr<SourceSurface> src = sourceDT->Snapshot();
193 
194   if (group.mMaskSurface) {
195     Point finalOffset = group.mFinalTarget->GetDeviceOffset();
196     dt->SetTransform(group.mMaskTransform * Matrix::Translation(-finalOffset));
197     Matrix surfTransform = group.mMaskTransform;
198     surfTransform.Invert();
199     dt->MaskSurface(SurfacePattern(src, ExtendMode::CLAMP,
200                                    surfTransform * Matrix::Translation(
201                                                        group.mGroupOffset.x,
202                                                        group.mGroupOffset.y)),
203                     group.mMaskSurface, Point(0, 0),
204                     DrawOptions(group.mOpacity, group.mOperator));
205   } else {
206     // For now this is required since our group offset is in device space of the
207     // final target, context but that may still have its own device offset. Once
208     // PushGroup/PopGroup logic is migrated to DrawTargets this can go as
209     // gfxContext::GetDeviceOffset will essentially always become null.
210     dt->SetTransform(
211         Matrix::Translation(-group.mFinalTarget->GetDeviceOffset()));
212     dt->DrawSurface(src,
213                     Rect(group.mGroupOffset.x, group.mGroupOffset.y,
214                          src->GetSize().width, src->GetSize().height),
215                     Rect(0, 0, src->GetSize().width, src->GetSize().height),
216                     DrawSurfaceOptions(SamplingFilter::POINT),
217                     DrawOptions(group.mOpacity, group.mOperator));
218   }
219 
220   if (group.mNeedsClipToVisibleRegion) {
221     dt->PopClip();
222   }
223 
224   group.mFinalTarget->Restore();
225 }
226 
ToInsideIntRect(const gfxRect & aRect)227 static IntRect ToInsideIntRect(const gfxRect& aRect) {
228   return IntRect::RoundIn(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height());
229 }
230 
231 // A context helper for BasicLayerManager::PaintLayer() that holds all the
232 // painting context together in a data structure so it can be easily passed
233 // around. It also uses ensures that the Transform and Opaque rect are restored
234 // to their former state on destruction.
235 
236 class PaintLayerContext {
237  public:
PaintLayerContext(gfxContext * aTarget,Layer * aLayer,LayerManager::DrawPaintedLayerCallback aCallback,void * aCallbackData)238   PaintLayerContext(gfxContext* aTarget, Layer* aLayer,
239                     LayerManager::DrawPaintedLayerCallback aCallback,
240                     void* aCallbackData)
241       : mTarget(aTarget),
242         mTargetMatrixSR(aTarget),
243         mLayer(aLayer),
244         mCallback(aCallback),
245         mCallbackData(aCallbackData),
246         mPushedOpaqueRect(false) {}
247 
~PaintLayerContext()248   ~PaintLayerContext() {
249     // Matrix is restored by mTargetMatrixSR
250     if (mPushedOpaqueRect) {
251       ClearOpaqueRect();
252     }
253   }
254 
255   // Gets the effective transform and returns true if it is a 2D
256   // transform.
Setup2DTransform()257   bool Setup2DTransform() {
258     // Will return an identity matrix for 3d transforms.
259     return mLayer->GetEffectiveTransformForBuffer().CanDraw2D(&mTransform);
260   }
261 
262   // Applies the effective transform if it's 2D. If it's a 3D transform then
263   // it applies an identity.
Apply2DTransform()264   void Apply2DTransform() { mTarget->SetMatrix(mTransform); }
265 
266   // Set the opaque rect to match the bounds of the visible region.
AnnotateOpaqueRect()267   void AnnotateOpaqueRect() {
268     const nsIntRegion visibleRegion =
269         mLayer->GetLocalVisibleRegion().ToUnknownRegion();
270     const IntRect& bounds = visibleRegion.GetBounds();
271 
272     DrawTarget* dt = mTarget->GetDrawTarget();
273     const IntRect& targetOpaqueRect = dt->GetOpaqueRect();
274 
275     // Try to annotate currentSurface with a region of pixels that have been
276     // (or will be) painted opaque, if no such region is currently set.
277     if (targetOpaqueRect.IsEmpty() && visibleRegion.GetNumRects() == 1 &&
278         (mLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) &&
279         !mTransform.HasNonAxisAlignedTransform()) {
280       gfx::Rect opaqueRect = dt->GetTransform().TransformBounds(
281           gfx::Rect(bounds.X(), bounds.Y(), bounds.Width(), bounds.Height()));
282       opaqueRect.RoundIn();
283       IntRect intOpaqueRect;
284       if (opaqueRect.ToIntRect(&intOpaqueRect)) {
285         mTarget->GetDrawTarget()->SetOpaqueRect(intOpaqueRect);
286         mPushedOpaqueRect = true;
287       }
288     }
289   }
290 
291   // Clear the Opaque rect. Although this doesn't really restore it to it's
292   // previous state it will happen on the exit path of the PaintLayer() so when
293   // painting is complete the opaque rect qill be clear.
ClearOpaqueRect()294   void ClearOpaqueRect() { mTarget->GetDrawTarget()->SetOpaqueRect(IntRect()); }
295 
296   gfxContext* mTarget;
297   gfxContextMatrixAutoSaveRestore mTargetMatrixSR;
298   Layer* mLayer;
299   LayerManager::DrawPaintedLayerCallback mCallback;
300   void* mCallbackData;
301   Matrix mTransform;
302   bool mPushedOpaqueRect;
303 };
304 
BasicLayerManager(nsIWidget * aWidget)305 BasicLayerManager::BasicLayerManager(nsIWidget* aWidget)
306     : mPhase(PHASE_NONE),
307       mWidget(aWidget),
308       mDoubleBuffering(BufferMode::BUFFER_NONE),
309       mType(BLM_WIDGET),
310       mUsingDefaultTarget(false),
311       mTransactionIncomplete(false),
312       mCompositorMightResample(false) {
313   MOZ_COUNT_CTOR(BasicLayerManager);
314   NS_ASSERTION(aWidget, "Must provide a widget");
315 }
316 
BasicLayerManager(BasicLayerManagerType aType)317 BasicLayerManager::BasicLayerManager(BasicLayerManagerType aType)
318     : mPhase(PHASE_NONE),
319       mWidget(nullptr),
320       mDoubleBuffering(BufferMode::BUFFER_NONE),
321       mType(aType),
322       mUsingDefaultTarget(false),
323       mTransactionIncomplete(false) {
324   MOZ_COUNT_CTOR(BasicLayerManager);
325   MOZ_ASSERT(mType != BLM_WIDGET);
326 }
327 
~BasicLayerManager()328 BasicLayerManager::~BasicLayerManager() {
329   NS_ASSERTION(!InTransaction(), "Died during transaction?");
330 
331   ClearCachedResources();
332 
333   mRoot = nullptr;
334 
335   MOZ_COUNT_DTOR(BasicLayerManager);
336 }
337 
SetDefaultTarget(gfxContext * aContext)338 void BasicLayerManager::SetDefaultTarget(gfxContext* aContext) {
339   NS_ASSERTION(!InTransaction(), "Must set default target outside transaction");
340   mDefaultTarget = aContext;
341 }
342 
SetDefaultTargetConfiguration(BufferMode aDoubleBuffering,ScreenRotation aRotation)343 void BasicLayerManager::SetDefaultTargetConfiguration(
344     BufferMode aDoubleBuffering, ScreenRotation aRotation) {
345   mDoubleBuffering = aDoubleBuffering;
346 }
347 
BeginTransaction()348 bool BasicLayerManager::BeginTransaction() {
349   mInTransaction = true;
350   mUsingDefaultTarget = true;
351   return BeginTransactionWithTarget(mDefaultTarget);
352 }
353 
BeginTransactionWithTarget(gfxContext * aTarget)354 bool BasicLayerManager::BeginTransactionWithTarget(gfxContext* aTarget) {
355   mInTransaction = true;
356 
357 #ifdef MOZ_LAYERS_HAVE_LOG
358   MOZ_LAYERS_LOG(("[----- BeginTransaction"));
359   Log();
360 #endif
361 
362   NS_ASSERTION(!InTransaction(), "Nested transactions not allowed");
363   mPhase = PHASE_CONSTRUCTION;
364   mTarget = aTarget;
365   return true;
366 }
367 
TransformIntRect(IntRect & aRect,const Matrix & aMatrix,IntRect (* aRoundMethod)(const gfxRect &))368 static void TransformIntRect(IntRect& aRect, const Matrix& aMatrix,
369                              IntRect (*aRoundMethod)(const gfxRect&)) {
370   Rect gr = Rect(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height());
371   gr = aMatrix.TransformBounds(gr);
372   aRect = (*aRoundMethod)(ThebesRect(gr));
373 }
374 
375 /**
376  * This function assumes that GetEffectiveTransform transforms
377  * all layers to the same coordinate system (the "root coordinate system").
378  * It can't be used as is by accelerated layers because of intermediate
379  * surfaces. This must set the hidden flag to true or false on *all* layers in
380  * the subtree. It also sets the operator for all layers to "OVER", and call
381  * SetDrawAtomically(false).
382  * It clears mClipToVisibleRegion on all layers.
383  * @param aClipRect the cliprect, in the root coordinate system. We assume
384  * that any layer drawing is clipped to this rect. It is therefore not
385  * allowed to add to the opaque region outside that rect.
386  * @param aDirtyRect the dirty rect that will be painted, in the root
387  * coordinate system. Layers outside this rect should be hidden.
388  * @param aOpaqueRegion the opaque region covering aLayer, in the
389  * root coordinate system.
390  */
391 enum {
392   ALLOW_OPAQUE = 0x01,
393 };
MarkLayersHidden(Layer * aLayer,const IntRect & aClipRect,const IntRect & aDirtyRect,nsIntRegion & aOpaqueRegion,uint32_t aFlags)394 static void MarkLayersHidden(Layer* aLayer, const IntRect& aClipRect,
395                              const IntRect& aDirtyRect,
396                              nsIntRegion& aOpaqueRegion, uint32_t aFlags) {
397   IntRect newClipRect(aClipRect);
398   uint32_t newFlags = aFlags;
399 
400   // Allow aLayer or aLayer's descendants to cover underlying layers
401   // only if it's opaque.
402   if (aLayer->GetOpacity() != 1.0f) {
403     newFlags &= ~ALLOW_OPAQUE;
404   }
405 
406   {
407     const Maybe<ParentLayerIntRect>& clipRect = aLayer->GetLocalClipRect();
408     if (clipRect) {
409       IntRect cr = clipRect->ToUnknownRect();
410       // clipRect is in the container's coordinate system. Get it into the
411       // global coordinate system.
412       if (aLayer->GetParent()) {
413         Matrix tr;
414         if (aLayer->GetParent()->GetEffectiveTransform().CanDraw2D(&tr)) {
415           // Clip rect is applied after aLayer's transform, i.e., in the
416           // coordinate system of aLayer's parent.
417           TransformIntRect(cr, tr, ToInsideIntRect);
418         } else {
419           cr.SetRect(0, 0, 0, 0);
420         }
421       }
422       newClipRect.IntersectRect(newClipRect, cr);
423     }
424   }
425 
426   BasicImplData* data = ToData(aLayer);
427   data->SetOperator(CompositionOp::OP_OVER);
428   data->SetClipToVisibleRegion(false);
429   data->SetDrawAtomically(false);
430 
431   if (!aLayer->AsContainerLayer()) {
432     Matrix transform;
433     if (!aLayer->GetEffectiveTransform().CanDraw2D(&transform)) {
434       data->SetHidden(false);
435       return;
436     }
437 
438     nsIntRegion region = aLayer->GetLocalVisibleRegion().ToUnknownRegion();
439     IntRect r = region.GetBounds();
440     TransformIntRect(r, transform, ToOutsideIntRect);
441     r.IntersectRect(r, aDirtyRect);
442     data->SetHidden(aOpaqueRegion.Contains(r));
443 
444     // Allow aLayer to cover underlying layers only if aLayer's
445     // content is opaque
446     if ((aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) &&
447         (newFlags & ALLOW_OPAQUE)) {
448       for (auto iter = region.RectIter(); !iter.Done(); iter.Next()) {
449         r = iter.Get();
450         TransformIntRect(r, transform, ToInsideIntRect);
451 
452         r.IntersectRect(r, newClipRect);
453         aOpaqueRegion.Or(aOpaqueRegion, r);
454       }
455     }
456   } else {
457     Layer* child = aLayer->GetLastChild();
458     bool allHidden = true;
459     for (; child; child = child->GetPrevSibling()) {
460       MarkLayersHidden(child, newClipRect, aDirtyRect, aOpaqueRegion, newFlags);
461       if (!ToData(child)->IsHidden()) {
462         allHidden = false;
463       }
464     }
465     data->SetHidden(allHidden);
466   }
467 }
468 
469 /**
470  * This function assumes that GetEffectiveTransform transforms
471  * all layers to the same coordinate system (the "root coordinate system").
472  * MarkLayersHidden must be called before calling this.
473  * @param aVisibleRect the rectangle of aLayer that is visible (i.e. not
474  * clipped and in the dirty rect), in the root coordinate system.
475  */
ApplyDoubleBuffering(Layer * aLayer,const IntRect & aVisibleRect)476 static void ApplyDoubleBuffering(Layer* aLayer, const IntRect& aVisibleRect) {
477   BasicImplData* data = ToData(aLayer);
478   if (data->IsHidden()) return;
479 
480   IntRect newVisibleRect(aVisibleRect);
481 
482   {
483     const Maybe<ParentLayerIntRect>& clipRect = aLayer->GetLocalClipRect();
484     if (clipRect) {
485       IntRect cr = clipRect->ToUnknownRect();
486       // clipRect is in the container's coordinate system. Get it into the
487       // global coordinate system.
488       if (aLayer->GetParent()) {
489         Matrix tr;
490         if (aLayer->GetParent()->GetEffectiveTransform().CanDraw2D(&tr)) {
491           NS_ASSERTION(!ThebesMatrix(tr).HasNonIntegerTranslation(),
492                        "Parent can only have an integer translation");
493           cr += nsIntPoint(int32_t(tr._31), int32_t(tr._32));
494         } else {
495           NS_ERROR("Parent can only have an integer translation");
496         }
497       }
498       newVisibleRect.IntersectRect(newVisibleRect, cr);
499     }
500   }
501 
502   BasicContainerLayer* container =
503       static_cast<BasicContainerLayer*>(aLayer->AsContainerLayer());
504   // Layers that act as their own backbuffers should be drawn to the destination
505   // using OP_SOURCE to ensure that alpha values in a transparent window are
506   // cleared. This can also be faster than OP_OVER.
507   if (!container) {
508     data->SetOperator(CompositionOp::OP_SOURCE);
509     data->SetDrawAtomically(true);
510   } else {
511     if (container->UseIntermediateSurface() ||
512         !container->ChildrenPartitionVisibleRegion(newVisibleRect)) {
513       // We need to double-buffer this container.
514       data->SetOperator(CompositionOp::OP_SOURCE);
515       container->ForceIntermediateSurface();
516     } else {
517       // Tell the children to clip to their visible regions so our assumption
518       // that they don't paint outside their visible regions is valid!
519       for (Layer* child = aLayer->GetFirstChild(); child;
520            child = child->GetNextSibling()) {
521         ToData(child)->SetClipToVisibleRegion(true);
522         ApplyDoubleBuffering(child, newVisibleRect);
523       }
524     }
525   }
526 }
527 
EndTransaction(DrawPaintedLayerCallback aCallback,void * aCallbackData,EndTransactionFlags aFlags)528 void BasicLayerManager::EndTransaction(DrawPaintedLayerCallback aCallback,
529                                        void* aCallbackData,
530                                        EndTransactionFlags aFlags) {
531   mInTransaction = false;
532 
533   EndTransactionInternal(aCallback, aCallbackData, aFlags);
534 }
535 
AbortTransaction()536 void BasicLayerManager::AbortTransaction() {
537   NS_ASSERTION(InConstruction(), "Should be in construction phase");
538   mPhase = PHASE_NONE;
539   mUsingDefaultTarget = false;
540   mInTransaction = false;
541 }
542 
EndTransactionInternal(DrawPaintedLayerCallback aCallback,void * aCallbackData,EndTransactionFlags aFlags)543 bool BasicLayerManager::EndTransactionInternal(
544     DrawPaintedLayerCallback aCallback, void* aCallbackData,
545     EndTransactionFlags aFlags) {
546   AUTO_PROFILER_LABEL("BasicLayerManager::EndTransactionInternal", GRAPHICS);
547 
548 #ifdef MOZ_LAYERS_HAVE_LOG
549   MOZ_LAYERS_LOG(("  ----- (beginning paint)"));
550   Log();
551 #endif
552 
553   NS_ASSERTION(InConstruction(), "Should be in construction phase");
554   mPhase = PHASE_DRAWING;
555 
556   SetCompositionTime(TimeStamp::Now());
557 
558   RenderTraceLayers(mRoot, "FF00");
559 
560   mTransactionIncomplete = false;
561 
562   if (mRoot) {
563     if (aFlags & END_NO_COMPOSITE) {
564       // Apply pending tree updates before recomputing effective
565       // properties.
566       mRoot->ApplyPendingUpdatesToSubtree();
567     }
568 
569     // Need to do this before we call ApplyDoubleBuffering,
570     // which depends on correct effective transforms
571     if (mTarget) {
572       mSnapEffectiveTransforms =
573           !mTarget->GetDrawTarget()->GetUserData(&sDisablePixelSnapping);
574     } else {
575       mSnapEffectiveTransforms = true;
576     }
577     mRoot->ComputeEffectiveTransforms(
578         mTarget ? Matrix4x4::From2D(mTarget->CurrentMatrix()) : Matrix4x4());
579 
580     ToData(mRoot)->Validate(aCallback, aCallbackData, nullptr);
581     if (mRoot->GetMaskLayer()) {
582       ToData(mRoot->GetMaskLayer())
583           ->Validate(aCallback, aCallbackData, nullptr);
584     }
585   }
586 
587   if (mTarget && mRoot && !(aFlags & END_NO_IMMEDIATE_REDRAW) &&
588       !(aFlags & END_NO_COMPOSITE)) {
589     IntRect clipRect =
590         ToOutsideIntRect(mTarget->GetClipExtents(gfxContext::eDeviceSpace));
591 
592     if (IsRetained()) {
593       nsIntRegion region;
594       MarkLayersHidden(mRoot, clipRect, clipRect, region, ALLOW_OPAQUE);
595       if (mUsingDefaultTarget && mDoubleBuffering != BufferMode::BUFFER_NONE) {
596         ApplyDoubleBuffering(mRoot, clipRect);
597       }
598     }
599 
600     PaintLayer(mTarget, mRoot, aCallback, aCallbackData);
601     if (!mRegionToClear.IsEmpty()) {
602       for (auto iter = mRegionToClear.RectIter(); !iter.Done(); iter.Next()) {
603         const IntRect& r = iter.Get();
604         mTarget->GetDrawTarget()->ClearRect(
605             Rect(r.X(), r.Y(), r.Width(), r.Height()));
606       }
607     }
608     if (mWidget) {
609       FlashWidgetUpdateArea(mTarget);
610     }
611     RecordFrame();
612 
613     if (!mTransactionIncomplete) {
614       // Clear out target if we have a complete transaction.
615       mTarget = nullptr;
616     }
617   }
618 
619   if (mRoot) {
620     mAnimationReadyTime = TimeStamp::Now();
621     mRoot->StartPendingAnimations(mAnimationReadyTime);
622   }
623 
624 #ifdef MOZ_LAYERS_HAVE_LOG
625   Log();
626   MOZ_LAYERS_LOG(("]----- EndTransaction"));
627 #endif
628 
629   // Go back to the construction phase if the transaction isn't complete.
630   // Layout will update the layer tree and call EndTransaction().
631   mPhase = mTransactionIncomplete ? PHASE_CONSTRUCTION : PHASE_NONE;
632 
633   if (!mTransactionIncomplete) {
634     // This is still valid if the transaction was incomplete.
635     mUsingDefaultTarget = false;
636   }
637 
638   NS_ASSERTION(!aCallback || !mTransactionIncomplete,
639                "If callback is not null, transaction must be complete");
640 
641   ClearDisplayItemLayers();
642 
643   // XXX - We should probably assert here that for an incomplete transaction
644   // out target is the default target.
645 
646   return !mTransactionIncomplete;
647 }
648 
FlashWidgetUpdateArea(gfxContext * aContext)649 void BasicLayerManager::FlashWidgetUpdateArea(gfxContext* aContext) {
650   if (gfxPrefs::WidgetUpdateFlashing()) {
651     float r = float(rand()) / RAND_MAX;
652     float g = float(rand()) / RAND_MAX;
653     float b = float(rand()) / RAND_MAX;
654     aContext->SetColor(Color(r, g, b, 0.2f));
655     aContext->Paint();
656   }
657 }
658 
EndEmptyTransaction(EndTransactionFlags aFlags)659 bool BasicLayerManager::EndEmptyTransaction(EndTransactionFlags aFlags) {
660   mInTransaction = false;
661 
662   if (!mRoot) {
663     return false;
664   }
665 
666   return EndTransactionInternal(nullptr, nullptr, aFlags);
667 }
668 
SetRoot(Layer * aLayer)669 void BasicLayerManager::SetRoot(Layer* aLayer) {
670   NS_ASSERTION(aLayer, "Root can't be null");
671   NS_ASSERTION(aLayer->Manager() == this, "Wrong manager");
672   NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
673   mRoot = aLayer;
674 }
675 
PaintSelfOrChildren(PaintLayerContext & aPaintContext,gfxContext * aGroupTarget)676 void BasicLayerManager::PaintSelfOrChildren(PaintLayerContext& aPaintContext,
677                                             gfxContext* aGroupTarget) {
678   MOZ_ASSERT(aGroupTarget);
679   BasicImplData* data = ToData(aPaintContext.mLayer);
680 
681   /* Only paint ourself, or our children - This optimization relies on this! */
682   Layer* child = aPaintContext.mLayer->GetFirstChild();
683   if (!child) {
684     if (aPaintContext.mLayer->AsPaintedLayer()) {
685       data->PaintThebes(aGroupTarget, aPaintContext.mLayer->GetMaskLayer(),
686                         aPaintContext.mCallback, aPaintContext.mCallbackData);
687     } else {
688       data->Paint(aGroupTarget->GetDrawTarget(),
689                   aGroupTarget->GetDeviceOffset(),
690                   aPaintContext.mLayer->GetMaskLayer());
691     }
692   } else {
693     ContainerLayer* container =
694         static_cast<ContainerLayer*>(aPaintContext.mLayer);
695 
696     nsTArray<LayerPolygon> children = container->SortChildrenBy3DZOrder(
697         ContainerLayer::SortMode::WITHOUT_GEOMETRY);
698 
699     for (uint32_t i = 0; i < children.Length(); i++) {
700       Layer* layer = children.ElementAt(i).layer;
701       if (layer->IsBackfaceHidden()) {
702         continue;
703       }
704       if (!layer->AsContainerLayer() && !layer->IsVisible()) {
705         continue;
706       }
707 
708       PaintLayer(aGroupTarget, layer, aPaintContext.mCallback,
709                  aPaintContext.mCallbackData);
710       if (mTransactionIncomplete) break;
711     }
712   }
713 }
714 
FlushGroup(PaintLayerContext & aPaintContext,bool aNeedsClipToVisibleRegion)715 void BasicLayerManager::FlushGroup(PaintLayerContext& aPaintContext,
716                                    bool aNeedsClipToVisibleRegion) {
717   // If we're doing our own double-buffering, we need to avoid drawing
718   // the results of an incomplete transaction to the destination surface ---
719   // that could cause flicker. Double-buffering is implemented using a
720   // temporary surface for one or more container layers, so we need to stop
721   // those temporary surfaces from being composited to aTarget.
722   // ApplyDoubleBuffering guarantees that this container layer can't
723   // intersect any other leaf layers, so if the transaction is not yet marked
724   // incomplete, the contents of this container layer are the final contents
725   // for the window.
726   if (!mTransactionIncomplete) {
727     if (aNeedsClipToVisibleRegion) {
728       gfxUtils::ClipToRegion(
729           aPaintContext.mTarget,
730           aPaintContext.mLayer->GetLocalVisibleRegion().ToUnknownRegion());
731     }
732 
733     CompositionOp op = GetEffectiveOperator(aPaintContext.mLayer);
734     AutoSetOperator setOperator(aPaintContext.mTarget, op);
735 
736     PaintWithMask(aPaintContext.mTarget,
737                   aPaintContext.mLayer->GetEffectiveOpacity(),
738                   aPaintContext.mLayer->GetMaskLayer());
739   }
740 }
741 
742 /**
743  * Install the clip applied to the layer on the given gfxContext.  The
744  * given gfxContext is the buffer that the layer will be painted to.
745  */
InstallLayerClipPreserves3D(gfxContext * aTarget,Layer * aLayer)746 static void InstallLayerClipPreserves3D(gfxContext* aTarget, Layer* aLayer) {
747   const Maybe<ParentLayerIntRect>& clipRect = aLayer->GetLocalClipRect();
748 
749   if (!clipRect) {
750     return;
751   }
752   MOZ_ASSERT(
753       !aLayer->Extend3DContext() || !aLayer->Combines3DTransformWithAncestors(),
754       "Layers in a preserve 3D context have no clip"
755       " except leaves and the estabisher!");
756 
757   Layer* parent = aLayer->GetParent();
758   Matrix4x4 transform3d = parent && parent->Extend3DContext()
759                               ? parent->GetEffectiveTransform()
760                               : Matrix4x4();
761   Matrix transform;
762   if (!transform3d.CanDraw2D(&transform)) {
763     gfxDevCrash(LogReason::CannotDraw3D)
764         << "GFX: We should not have a 3D transform that CanDraw2D() is false!";
765   }
766   Matrix oldTransform = aTarget->CurrentMatrix();
767   transform *= oldTransform;
768   aTarget->SetMatrix(transform);
769 
770   aTarget->NewPath();
771   aTarget->SnappedRectangle(gfxRect(clipRect->X(), clipRect->Y(),
772                                     clipRect->Width(), clipRect->Height()));
773   aTarget->Clip();
774 
775   aTarget->SetMatrix(oldTransform);
776 }
777 
PaintLayer(gfxContext * aTarget,Layer * aLayer,DrawPaintedLayerCallback aCallback,void * aCallbackData)778 void BasicLayerManager::PaintLayer(gfxContext* aTarget, Layer* aLayer,
779                                    DrawPaintedLayerCallback aCallback,
780                                    void* aCallbackData) {
781   MOZ_ASSERT(aTarget);
782 
783   AUTO_PROFILER_LABEL("BasicLayerManager::PaintLayer", GRAPHICS);
784 
785   PaintLayerContext paintLayerContext(aTarget, aLayer, aCallback,
786                                       aCallbackData);
787 
788   // Don't attempt to paint layers with a singular transform, cairo will
789   // just throw an error.
790   if (aLayer->GetEffectiveTransform().IsSingular()) {
791     return;
792   }
793 
794   RenderTraceScope trace("BasicLayerManager::PaintLayer", "707070");
795 
796   const Maybe<ParentLayerIntRect>& clipRect = aLayer->GetLocalClipRect();
797   BasicContainerLayer* container =
798       static_cast<BasicContainerLayer*>(aLayer->AsContainerLayer());
799   bool needsGroup = container && container->UseIntermediateSurface();
800   BasicImplData* data = ToData(aLayer);
801   bool needsClipToVisibleRegion =
802       data->GetClipToVisibleRegion() && !aLayer->AsPaintedLayer();
803   NS_ASSERTION(needsGroup || !container ||
804                    container->GetOperator() == CompositionOp::OP_OVER,
805                "non-OVER operator should have forced UseIntermediateSurface");
806   NS_ASSERTION(
807       !container || !aLayer->GetMaskLayer() ||
808           container->UseIntermediateSurface(),
809       "ContainerLayer with mask layer should force UseIntermediateSurface");
810 
811   gfxContextAutoSaveRestore contextSR;
812   gfxMatrix transform;
813   // Will return an identity matrix for 3d transforms, and is handled separately
814   // below.
815   bool is2D = paintLayerContext.Setup2DTransform();
816   MOZ_ASSERT(is2D || needsGroup || !container || container->Extend3DContext() ||
817                  container->Is3DContextLeaf(),
818              "Must PushGroup for 3d transforms!");
819 
820   Layer* parent = aLayer->GetParent();
821   bool inPreserves3DChain = parent && parent->Extend3DContext();
822   bool needsSaveRestore = needsGroup || clipRect || needsClipToVisibleRegion ||
823                           !is2D || inPreserves3DChain;
824   if (needsSaveRestore) {
825     contextSR.SetContext(aTarget);
826 
827     // The clips on ancestors on the preserved3d chain should be
828     // installed on the aTarget before painting the layer.
829     InstallLayerClipPreserves3D(aTarget, aLayer);
830     for (Layer* l = parent; l && l->Extend3DContext(); l = l->GetParent()) {
831       InstallLayerClipPreserves3D(aTarget, l);
832     }
833   }
834 
835   paintLayerContext.Apply2DTransform();
836 
837   nsIntRegion visibleRegion = aLayer->GetLocalVisibleRegion().ToUnknownRegion();
838   // If needsGroup is true, we'll clip to the visible region after we've popped
839   // the group
840   if (needsClipToVisibleRegion && !needsGroup) {
841     gfxUtils::ClipToRegion(aTarget, visibleRegion);
842     // Don't need to clip to visible region again
843     needsClipToVisibleRegion = false;
844   }
845 
846   if (is2D) {
847     paintLayerContext.AnnotateOpaqueRect();
848   }
849 
850   bool clipIsEmpty = aTarget->GetClipExtents().IsEmpty();
851   if (clipIsEmpty) {
852     PaintSelfOrChildren(paintLayerContext, aTarget);
853     return;
854   }
855 
856   if (is2D) {
857     if (needsGroup) {
858       PushedGroup pushedGroup;
859       if (PushGroupForLayer(aTarget, aLayer,
860                             aLayer->GetLocalVisibleRegion().ToUnknownRegion(),
861                             pushedGroup)) {
862         PaintSelfOrChildren(paintLayerContext, pushedGroup.mGroupTarget);
863         PopGroupForLayer(pushedGroup);
864       }
865     } else {
866       PaintSelfOrChildren(paintLayerContext, aTarget);
867     }
868   } else {
869     if (!needsGroup && container) {
870       PaintSelfOrChildren(paintLayerContext, aTarget);
871       return;
872     }
873 
874     IntRect bounds = visibleRegion.GetBounds();
875     // DrawTarget without the 3D transform applied:
876     RefPtr<DrawTarget> untransformedDT =
877         gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
878             IntSize(bounds.Width(), bounds.Height()), SurfaceFormat::B8G8R8A8);
879     if (!untransformedDT || !untransformedDT->IsValid()) {
880       return;
881     }
882     untransformedDT->SetTransform(
883         Matrix::Translation(-Point(bounds.X(), bounds.Y())));
884 
885     RefPtr<gfxContext> groupTarget =
886         gfxContext::CreatePreservingTransformOrNull(untransformedDT);
887     MOZ_ASSERT(groupTarget);  // already checked the target above
888 
889     PaintSelfOrChildren(paintLayerContext, groupTarget);
890 
891     // Temporary fast fix for bug 725886
892     // Revert these changes when 725886 is ready
893 #ifdef DEBUG
894     if (aLayer->GetDebugColorIndex() != 0) {
895       Color color((aLayer->GetDebugColorIndex() & 1) ? 1.f : 0.f,
896                   (aLayer->GetDebugColorIndex() & 2) ? 1.f : 0.f,
897                   (aLayer->GetDebugColorIndex() & 4) ? 1.f : 0.f);
898       untransformedDT->FillRect(Rect(bounds), ColorPattern(color));
899     }
900 #endif
901     Matrix4x4 effectiveTransform = aLayer->GetEffectiveTransform();
902     Rect xformBounds = effectiveTransform.TransformAndClipBounds(
903         Rect(bounds), ToRect(aTarget->GetClipExtents()));
904     xformBounds.RoundOut();
905     effectiveTransform.PostTranslate(-xformBounds.X(), -xformBounds.Y(), 0);
906     effectiveTransform.PreTranslate(bounds.X(), bounds.Y(), 0);
907 
908     RefPtr<SourceSurface> untransformedSurf = untransformedDT->Snapshot();
909     RefPtr<DrawTarget> xformDT = untransformedDT->CreateSimilarDrawTarget(
910         IntSize::Truncate(xformBounds.Width(), xformBounds.Height()),
911         SurfaceFormat::B8G8R8A8);
912     RefPtr<SourceSurface> xformSurf;
913     if (xformDT && untransformedSurf &&
914         xformDT->Draw3DTransformedSurface(untransformedSurf,
915                                           effectiveTransform)) {
916       xformSurf = xformDT->Snapshot();
917     }
918 
919     if (xformSurf) {
920       aTarget->SetPattern(new gfxPattern(
921           xformSurf, Matrix::Translation(xformBounds.TopLeft())));
922 
923       // Azure doesn't support EXTEND_NONE, so to avoid extending the edges
924       // of the source surface out to the current clip region, clip to
925       // the rectangle of the result surface now.
926       aTarget->NewPath();
927       aTarget->SnappedRectangle(ThebesRect(xformBounds));
928       aTarget->Clip();
929       FlushGroup(paintLayerContext, needsClipToVisibleRegion);
930     }
931   }
932 }
933 
ClearCachedResources(Layer * aSubtree)934 void BasicLayerManager::ClearCachedResources(Layer* aSubtree) {
935   MOZ_ASSERT(!aSubtree || aSubtree->Manager() == this);
936   if (aSubtree) {
937     ClearLayer(aSubtree);
938   } else if (mRoot) {
939     ClearLayer(mRoot);
940   }
941 }
ClearLayer(Layer * aLayer)942 void BasicLayerManager::ClearLayer(Layer* aLayer) {
943   ToData(aLayer)->ClearCachedResources();
944   for (Layer* child = aLayer->GetFirstChild(); child;
945        child = child->GetNextSibling()) {
946     ClearLayer(child);
947   }
948 }
949 
CreateReadbackLayer()950 already_AddRefed<ReadbackLayer> BasicLayerManager::CreateReadbackLayer() {
951   NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
952   RefPtr<ReadbackLayer> layer = new BasicReadbackLayer(this);
953   return layer.forget();
954 }
955 
956 }  // namespace layers
957 }  // namespace mozilla
958