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