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