Coverage Report

Created: 2018-09-25 14:53

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