Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/layers/wr/WebRenderCommandBuilder.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 "WebRenderCommandBuilder.h"
8
9
#include "BasicLayers.h"
10
#include "mozilla/AutoRestore.h"
11
#include "mozilla/gfx/2D.h"
12
#include "mozilla/gfx/Types.h"
13
#include "mozilla/layers/ClipManager.h"
14
#include "mozilla/layers/ImageClient.h"
15
#include "mozilla/layers/WebRenderBridgeChild.h"
16
#include "mozilla/layers/WebRenderLayerManager.h"
17
#include "mozilla/layers/IpcResourceUpdateQueue.h"
18
#include "mozilla/layers/SharedSurfacesChild.h"
19
#include "mozilla/layers/SourceSurfaceSharedData.h"
20
#include "mozilla/layers/StackingContextHelper.h"
21
#include "mozilla/layers/UpdateImageHelper.h"
22
#include "mozilla/layers/WebRenderDrawEventRecorder.h"
23
#include "UnitTransforms.h"
24
#include "gfxEnv.h"
25
#include "nsDisplayListInvalidation.h"
26
#include "WebRenderCanvasRenderer.h"
27
#include "LayersLogging.h"
28
#include "LayerTreeInvalidation.h"
29
30
namespace mozilla {
31
namespace layers {
32
33
using namespace gfx;
34
static bool
35
PaintByLayer(nsDisplayItem* aItem,
36
             nsDisplayListBuilder* aDisplayListBuilder,
37
             const RefPtr<BasicLayerManager>& aManager,
38
             gfxContext* aContext,
39
             const gfx::Size& aScale,
40
             const std::function<void()>& aPaintFunc);
41
static int sIndent;
42
#include <stdarg.h>
43
#include <stdio.h>
44
45
0
static void GP(const char *fmt, ...) {
46
0
    va_list args;
47
0
    va_start(args, fmt);
48
#if 0
49
    for (int i = 0; i < sIndent; i++) { printf(" "); }
50
    vprintf(fmt, args);
51
#endif
52
    va_end(args);
53
0
}
54
55
//XXX: problems:
56
// - How do we deal with scrolling while having only a single invalidation rect?
57
// We can have a valid rect and an invalid rect. As we scroll the valid rect will move
58
// and the invalid rect will be the new area
59
60
struct BlobItemData;
61
static void DestroyBlobGroupDataProperty(nsTArray<BlobItemData*>* aArray);
62
NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(BlobGroupDataProperty,
63
                                    nsTArray<BlobItemData*>,
64
                                    DestroyBlobGroupDataProperty);
65
66
static void
67
SetBlobImageVisibleArea(wr::IpcResourceUpdateQueue& aResources,
68
                        wr::ImageKey aImageKey,
69
                        const LayoutDeviceRect& aImageRect,
70
                        const LayoutDeviceRect& aPaintRect)
71
0
{
72
0
  LayoutDeviceRect visibleRect = aImageRect.Intersect(aPaintRect);
73
0
  // Send the visible rect in normalized coordinates.
74
0
  Rect visibleArea = Rect((visibleRect.x - aImageRect.x) / aImageRect.width,
75
0
                          (visibleRect.y - aImageRect.y) / aImageRect.height,
76
0
                          visibleRect.width / aImageRect.width,
77
0
                          visibleRect.height / aImageRect.height);
78
0
79
0
  aResources.SetImageVisibleArea(aImageKey, visibleArea);
80
0
}
81
82
83
// These are currently manually allocated and ownership is help by the mDisplayItems
84
// hash table in DIGroup
85
struct BlobItemData
86
{
87
  // a weak pointer to the frame for this item.
88
  // DisplayItemData has a mFrameList to deal with merged frames. Hopefully we
89
  // don't need to worry about that.
90
  nsIFrame* mFrame;
91
92
  uint32_t  mDisplayItemKey;
93
  nsTArray<BlobItemData*>* mArray; // a weak pointer to the array that's owned by the frame property
94
95
  IntRect mRect;
96
  // It would be nice to not need this. We need to be able to call ComputeInvalidationRegion.
97
  // ComputeInvalidationRegion will sometimes reach into parent style structs to get information
98
  // that can change the invalidation region
99
  UniquePtr<nsDisplayItemGeometry> mGeometry;
100
  DisplayItemClip mClip;
101
  bool mUsed; // initialized near construction
102
103
  // a weak pointer to the group that owns this item
104
  // we use this to track whether group for a particular item has changed
105
  struct DIGroup* mGroup;
106
107
  // XXX: only used for debugging
108
  bool mInvalid;
109
  bool mEmpty;
110
111
  // properties that are used to emulate layer tree invalidation
112
  Matrix mMatrix; // updated to track the current transform to device space
113
  Matrix4x4Flagged mTransform; // only used with nsDisplayTransform items to detect transform changes
114
  float mOpacity; // only used with nsDisplayOpacity items to detect change to opacity
115
  RefPtr<BasicLayerManager> mLayerManager;
116
117
  IntRect mImageRect;
118
  LayerIntPoint mGroupOffset;
119
120
  BlobItemData(DIGroup* aGroup, nsDisplayItem *aItem)
121
    : mUsed(false)
122
    , mGroup(aGroup)
123
    , mOpacity(0.0)
124
0
  {
125
0
    mInvalid = false;
126
0
    mEmpty = false;
127
0
    mDisplayItemKey = aItem->GetPerFrameKey();
128
0
    AddFrame(aItem->Frame());
129
0
  }
130
131
private:
132
  void AddFrame(nsIFrame* aFrame)
133
0
  {
134
0
    mFrame = aFrame;
135
0
136
0
    nsTArray<BlobItemData*>* array =
137
0
      aFrame->GetProperty(BlobGroupDataProperty());
138
0
    if (!array) {
139
0
      array = new nsTArray<BlobItemData*>();
140
0
      aFrame->SetProperty(BlobGroupDataProperty(), array);
141
0
    }
142
0
    array->AppendElement(this);
143
0
    mArray = array;
144
0
  }
145
146
public:
147
  void ClearFrame()
148
0
  {
149
0
    // Delete the weak pointer to this BlobItemData on the frame
150
0
    MOZ_RELEASE_ASSERT(mFrame);
151
0
    // the property may already be removed if WebRenderUserData got deleted
152
0
    // first so we use our own mArray pointer.
153
0
    mArray->RemoveElement(this);
154
0
155
0
    // drop the entire property if nothing's left in the array
156
0
    if (mArray->IsEmpty()) {
157
0
      // If the frame is in the process of being destroyed this will fail
158
0
      // but that's ok, because the the property will be removed then anyways
159
0
      mFrame->DeleteProperty(BlobGroupDataProperty());
160
0
    }
161
0
    mFrame = nullptr;
162
0
  }
163
164
  ~BlobItemData()
165
0
  {
166
0
    if (mFrame) {
167
0
      ClearFrame();
168
0
    }
169
0
  }
170
};
171
172
static BlobItemData*
173
GetBlobItemData(nsDisplayItem* aItem)
174
0
{
175
0
  nsIFrame* frame = aItem->Frame();
176
0
  uint32_t key = aItem->GetPerFrameKey();
177
0
  const nsTArray<BlobItemData*>* array = frame->GetProperty(BlobGroupDataProperty());
178
0
  if (array) {
179
0
    for (BlobItemData* item : *array) {
180
0
      if (item->mDisplayItemKey == key) {
181
0
        return item;
182
0
      }
183
0
    }
184
0
  }
185
0
  return nullptr;
186
0
}
187
188
// We keep around the BlobItemData so that when we invalidate it get properly included in the rect
189
static void
190
DestroyBlobGroupDataProperty(nsTArray<BlobItemData*>* aArray)
191
0
{
192
0
  for (BlobItemData* item : *aArray) {
193
0
    GP("DestroyBlobGroupDataProperty: %p-%d\n", item->mFrame, item->mDisplayItemKey);
194
0
    item->mFrame = nullptr;
195
0
  }
196
0
  delete aArray;
197
0
}
198
199
static void
200
TakeExternalSurfaces(WebRenderDrawEventRecorder* aRecorder,
201
                     std::vector<RefPtr<SourceSurface>>& aExternalSurfaces,
202
                     WebRenderLayerManager* aManager,
203
                     wr::IpcResourceUpdateQueue& aResources)
204
0
{
205
0
  aRecorder->TakeExternalSurfaces(aExternalSurfaces);
206
0
207
0
  for (auto& surface : aExternalSurfaces) {
208
0
    if (surface->GetType() != SurfaceType::DATA_SHARED) {
209
0
      MOZ_ASSERT_UNREACHABLE("External surface that is not a shared surface!");
210
0
      continue;
211
0
    }
212
0
213
0
    // While we don't use the image key with the surface, because the blob image
214
0
    // renderer doesn't have easy access to the resource set, we still want to
215
0
    // ensure one is generated. That will ensure the surface remains alive until
216
0
    // at least the last epoch which the blob image could be used in.
217
0
    wr::ImageKey key;
218
0
    auto sharedSurface = static_cast<SourceSurfaceSharedData*>(surface.get());
219
0
    SharedSurfacesChild::Share(sharedSurface, aManager, aResources, key);
220
0
  }
221
0
}
222
223
struct DIGroup;
224
struct Grouper
225
{
226
  explicit Grouper(ClipManager& aClipManager)
227
   : mAppUnitsPerDevPixel(0)
228
   , mDisplayListBuilder(nullptr)
229
   , mClipManager(aClipManager)
230
0
  {}
231
232
  int32_t mAppUnitsPerDevPixel;
233
  std::vector<nsDisplayItem*> mItemStack;
234
  nsDisplayListBuilder* mDisplayListBuilder;
235
  ClipManager& mClipManager;
236
  Matrix mTransform;
237
238
  // Paint the list of aChildren display items.
239
  void PaintContainerItem(DIGroup* aGroup, nsDisplayItem* aItem, const IntRect& aItemBounds,
240
                          nsDisplayList* aChildren, gfxContext* aContext,
241
                          WebRenderDrawEventRecorder* aRecorder);
242
243
  // Builds groups of display items split based on 'layer activity'
244
  void ConstructGroups(nsDisplayListBuilder* aDisplayListBuilder,
245
                       WebRenderCommandBuilder* aCommandBuilder,
246
                       wr::DisplayListBuilder& aBuilder,
247
                       wr::IpcResourceUpdateQueue& aResources,
248
                       DIGroup* aGroup, nsDisplayList* aList,
249
                       const StackingContextHelper& aSc);
250
  // Builds a group of display items without promoting anything to active.
251
  void ConstructGroupInsideInactive(WebRenderCommandBuilder* aCommandBuilder,
252
                                       wr::DisplayListBuilder& aBuilder,
253
                                       wr::IpcResourceUpdateQueue& aResources,
254
                                       DIGroup* aGroup, nsDisplayList* aList,
255
                                       const StackingContextHelper& aSc);
256
  // Helper method for processing a single inactive item
257
  void ConstructItemInsideInactive(WebRenderCommandBuilder* aCommandBuilder,
258
                                   wr::DisplayListBuilder& aBuilder,
259
                                   wr::IpcResourceUpdateQueue& aResources,
260
                                   DIGroup* aGroup, nsDisplayItem* aItem,
261
                                   const StackingContextHelper& aSc);
262
0
  ~Grouper() {
263
0
  }
264
};
265
266
// Returns whether this is an item for which complete invalidation was
267
// reliant on LayerTreeInvalidation in the pre-webrender world.
268
static bool
269
IsContainerLayerItem(nsDisplayItem* aItem)
270
{
271
  switch (aItem->GetType()) {
272
    case DisplayItemType::TYPE_WRAP_LIST:
273
    case DisplayItemType::TYPE_TRANSFORM:
274
    case DisplayItemType::TYPE_OPACITY:
275
    case DisplayItemType::TYPE_FILTER:
276
    case DisplayItemType::TYPE_BLEND_CONTAINER:
277
    case DisplayItemType::TYPE_BLEND_MODE:
278
    case DisplayItemType::TYPE_MASK: {
279
      return true;
280
    }
281
    default: {
282
      return false;
283
    }
284
  }
285
}
286
287
#include <sstream>
288
289
bool
290
UpdateContainerLayerPropertiesAndDetectChange(nsDisplayItem* aItem, BlobItemData* aData, nsDisplayItemGeometry& aGeometry)
291
0
{
292
0
  bool changed = false;
293
0
  switch (aItem->GetType()) {
294
0
    case DisplayItemType::TYPE_TRANSFORM: {
295
0
      auto transformItem = static_cast<nsDisplayTransform*>(aItem);
296
0
      Matrix4x4Flagged trans = transformItem->GetTransform();
297
0
      changed = aData->mTransform != trans;
298
0
299
0
      if (changed) {
300
0
        std::stringstream ss;
301
0
        //ss << trans << ' ' << aData->mTransform;
302
0
        //GP("UpdateContainerLayerPropertiesAndDetectChange Matrix %d %s\n", changed, ss.str().c_str());
303
0
      }
304
0
305
0
      aData->mTransform = trans;
306
0
      break;
307
0
    }
308
0
    case DisplayItemType::TYPE_OPACITY: {
309
0
      auto opacityItem = static_cast<nsDisplayOpacity*>(aItem);
310
0
      float opacity = opacityItem->GetOpacity();
311
0
      changed = aData->mOpacity != opacity;
312
0
      aData->mOpacity = opacity;
313
0
      GP("UpdateContainerLayerPropertiesAndDetectChange Opacity\n");
314
0
      break;
315
0
    }
316
0
    case DisplayItemType::TYPE_MASK:
317
0
    case DisplayItemType::TYPE_FILTER: {
318
0
      // These two items go through BasicLayerManager composition which clips to the BuildingRect
319
0
      aGeometry.mBounds = aGeometry.mBounds.Intersect(aItem->GetBuildingRect());
320
0
      break;
321
0
    }
322
0
    default:
323
0
      break;
324
0
  }
325
0
326
0
  return changed || !aGeometry.mBounds.IsEqualEdges(aData->mGeometry->mBounds);
327
0
}
328
329
struct DIGroup
330
{
331
  // XXX: Storing owning pointers to the BlobItemData in a hash table is not
332
  // a good choice. There are two better options:
333
  //
334
  // 1. We should just be using a linked list for this stuff.
335
  //    That we can iterate over only the used items.
336
  //    We remove from the unused list and add to the used list
337
  //    when we see an item.
338
  //
339
  //    we allocate using a free list.
340
  //
341
  // 2. We can use a Vec and use SwapRemove().
342
  //    We'll just need to be careful when iterating.
343
  //    The advantage of a Vec is that everything stays compact
344
  //    and we don't need to heap allocate the BlobItemData's
345
  nsTHashtable<nsPtrHashKey<BlobItemData>> mDisplayItems;
346
347
  nsPoint mAnimatedGeometryRootOrigin;
348
  nsPoint mLastAnimatedGeometryRootOrigin;
349
  IntRect mInvalidRect;
350
  nsRect mGroupBounds;
351
  LayoutDeviceRect mPaintRect;
352
  int32_t mAppUnitsPerDevPixel;
353
  gfx::Size mScale;
354
  FrameMetrics::ViewID mScrollId;
355
  LayerPoint mResidualOffset;
356
  LayerIntRect mLayerBounds;
357
  Maybe<wr::ImageKey> mKey;
358
  std::vector<RefPtr<SourceSurface>> mExternalSurfaces;
359
  std::vector<RefPtr<ScaledFont>> mFonts;
360
361
  DIGroup()
362
    : mAppUnitsPerDevPixel(0)
363
    , mScrollId(FrameMetrics::NULL_SCROLL_ID)
364
0
  {
365
0
  }
366
367
  void InvalidateRect(const IntRect& aRect)
368
0
  {
369
0
    // Empty rects get dropped
370
0
    mInvalidRect = mInvalidRect.Union(aRect);
371
0
  }
372
373
  IntRect ItemBounds(nsDisplayItem* aItem)
374
0
  {
375
0
    BlobItemData* data = GetBlobItemData(aItem);
376
0
    return data->mRect;
377
0
  }
378
379
  void ClearItems()
380
0
  {
381
0
    GP("items: %d\n", mDisplayItems.Count());
382
0
    for (auto iter = mDisplayItems.Iter(); !iter.Done(); iter.Next()) {
383
0
      BlobItemData* data = iter.Get()->GetKey();
384
0
      GP("Deleting %p-%d\n", data->mFrame, data->mDisplayItemKey);
385
0
      iter.Remove();
386
0
      delete data;
387
0
    }
388
0
  }
389
390
  void ClearImageKey(WebRenderLayerManager* aManager, bool aForce = false)
391
0
  {
392
0
    if (mKey) {
393
0
      MOZ_RELEASE_ASSERT(aForce || mInvalidRect.IsEmpty());
394
0
      aManager->AddImageKeyForDiscard(mKey.value());
395
0
      mKey = Nothing();
396
0
    }
397
0
    mFonts.clear();
398
0
  }
399
400
  static IntRect
401
  ToDeviceSpace(nsRect aBounds, Matrix& aMatrix, int32_t aAppUnitsPerDevPixel, LayerIntPoint aOffset)
402
0
  {
403
0
    // RoundedOut can convert empty rectangles to non-empty ones
404
0
    // so special case them here
405
0
    if (aBounds.IsEmpty()) {
406
0
      return IntRect();
407
0
    }
408
0
    return RoundedOut(aMatrix.TransformBounds(ToRect(nsLayoutUtils::RectToGfxRect(aBounds, aAppUnitsPerDevPixel)))) - aOffset.ToUnknownPoint();
409
0
  }
410
411
  void ComputeGeometryChange(nsDisplayItem* aItem, BlobItemData* aData, Matrix& aMatrix, nsDisplayListBuilder* aBuilder)
412
0
  {
413
0
    // If the frame is marked as invalidated, and didn't specify a rect to invalidate then we want to
414
0
    // invalidate both the old and new bounds, otherwise we only want to invalidate the changed areas.
415
0
    // If we do get an invalid rect, then we want to add this on top of the change areas.
416
0
    nsRect invalid;
417
0
    nsRegion combined;
418
0
    const DisplayItemClip& clip = aItem->GetClip();
419
0
420
0
    nsPoint shift = mAnimatedGeometryRootOrigin - mLastAnimatedGeometryRootOrigin;
421
0
422
0
    if (shift.x != 0 || shift.y != 0)
423
0
      GP("shift %d %d\n", shift.x, shift.y);
424
0
    int32_t appUnitsPerDevPixel = aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
425
0
    MOZ_RELEASE_ASSERT(mAppUnitsPerDevPixel == appUnitsPerDevPixel);
426
0
    LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(mGroupBounds, appUnitsPerDevPixel);
427
0
    LayoutDeviceIntPoint offset = RoundedToInt(bounds.TopLeft());
428
0
    GP("\n");
429
0
    GP("CGC offset %d %d\n", offset.x, offset.y);
430
0
    LayerIntSize size = mLayerBounds.Size();
431
0
    IntRect imageRect(0, 0, size.width, size.height);
432
0
    GP("imageSize: %d %d\n", size.width, size.height);
433
0
    /*if (aItem->IsReused() && aData->mGeometry) {
434
0
      return;
435
0
    }*/
436
0
437
0
    GP("pre mInvalidRect: %s %p-%d - inv: %d %d %d %d\n", aItem->Name(), aItem->Frame(), aItem->GetPerFrameKey(),
438
0
       mInvalidRect.x, mInvalidRect.y, mInvalidRect.width, mInvalidRect.height);
439
0
    if (!aData->mGeometry) {
440
0
      // This item is being added for the first time, invalidate its entire area.
441
0
      UniquePtr<nsDisplayItemGeometry> geometry(aItem->AllocateGeometry(aBuilder));
442
0
      combined = clip.ApplyNonRoundedIntersection(geometry->ComputeInvalidationRegion());
443
0
      aData->mGeometry = std::move(geometry);
444
0
      nsRect bounds = combined.GetBounds();
445
0
446
0
      IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mLayerBounds.TopLeft());
447
0
      aData->mRect = transformedRect.Intersect(imageRect);
448
0
      GP("CGC %s %d %d %d %d\n", aItem->Name(), bounds.x, bounds.y, bounds.width, bounds.height);
449
0
      GP("%d %d,  %f %f\n", mLayerBounds.TopLeft().x, mLayerBounds.TopLeft().y, aMatrix._11, aMatrix._22);
450
0
      GP("mRect %d %d %d %d\n", aData->mRect.x, aData->mRect.y, aData->mRect.width, aData->mRect.height);
451
0
      InvalidateRect(aData->mRect);
452
0
      aData->mInvalid = true;
453
0
    } else if (aData->mInvalid || /* XXX: handle image load invalidation */ (aItem->IsInvalid(invalid) && invalid.IsEmpty())) {
454
0
      MOZ_RELEASE_ASSERT(imageRect.IsEqualEdges(aData->mImageRect));
455
0
      MOZ_RELEASE_ASSERT(mLayerBounds.TopLeft() == aData->mGroupOffset);
456
0
      UniquePtr<nsDisplayItemGeometry> geometry(aItem->AllocateGeometry(aBuilder));
457
0
      /* Instead of doing this dance, let's just invalidate the old rect and the
458
0
       * new rect.
459
0
      combined = aData->mClip.ApplyNonRoundedIntersection(aData->mGeometry->ComputeInvalidationRegion());
460
0
      combined.MoveBy(shift);
461
0
      combined.Or(combined, clip.ApplyNonRoundedIntersection(geometry->ComputeInvalidationRegion()));
462
0
      aData->mGeometry = std::move(geometry);
463
0
      */
464
0
      combined = clip.ApplyNonRoundedIntersection(geometry->ComputeInvalidationRegion());
465
0
      aData->mGeometry = std::move(geometry);
466
0
467
0
      GP("matrix: %f %f\n", aMatrix._31, aMatrix._32); 
468
0
      GP("frame invalid invalidate: %s\n", aItem->Name());
469
0
      GP("old rect: %d %d %d %d\n",
470
0
             aData->mRect.x,
471
0
             aData->mRect.y,
472
0
             aData->mRect.width,
473
0
             aData->mRect.height);
474
0
      InvalidateRect(aData->mRect.Intersect(imageRect));
475
0
      // We want to snap to outside pixels. When should we multiply by the matrix?
476
0
      // XXX: TransformBounds is expensive. We should avoid doing it if we have no transform
477
0
      IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mLayerBounds.TopLeft());
478
0
      aData->mRect = transformedRect.Intersect(imageRect);
479
0
      InvalidateRect(aData->mRect);
480
0
      GP("new rect: %d %d %d %d\n",
481
0
             aData->mRect.x,
482
0
             aData->mRect.y,
483
0
             aData->mRect.width,
484
0
             aData->mRect.height);
485
0
      aData->mInvalid = true;
486
0
    } else {
487
0
      MOZ_RELEASE_ASSERT(imageRect.IsEqualEdges(aData->mImageRect));
488
0
      MOZ_RELEASE_ASSERT(mLayerBounds.TopLeft() == aData->mGroupOffset);
489
0
      GP("else invalidate: %s\n", aItem->Name());
490
0
      aData->mGeometry->MoveBy(shift);
491
0
      // this includes situations like reflow changing the position
492
0
      aItem->ComputeInvalidationRegion(aBuilder, aData->mGeometry.get(), &combined);
493
0
      if (!combined.IsEmpty()) {
494
0
        // There might be no point in doing this elaborate tracking here to get
495
0
        // smaller areas
496
0
        InvalidateRect(aData->mRect.Intersect(imageRect)); // invalidate the old area -- in theory combined should take care of this
497
0
        UniquePtr<nsDisplayItemGeometry> geometry(aItem->AllocateGeometry(aBuilder));
498
0
        aData->mClip.AddOffsetAndComputeDifference(shift, aData->mGeometry->ComputeInvalidationRegion(), clip,
499
0
                                                   geometry ? geometry->ComputeInvalidationRegion() :
500
0
                                                   aData->mGeometry->ComputeInvalidationRegion(),
501
0
                                                   &combined);
502
0
        IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mLayerBounds.TopLeft());
503
0
        IntRect invalidRect = transformedRect.Intersect(imageRect);
504
0
        GP("combined not empty: mRect %d %d %d %d\n", invalidRect.x, invalidRect.y, invalidRect.width, invalidRect.height);
505
0
        // invalidate the invalidated area.
506
0
        InvalidateRect(invalidRect);
507
0
508
0
        aData->mGeometry = std::move(geometry);
509
0
510
0
        combined = clip.ApplyNonRoundedIntersection(aData->mGeometry->ComputeInvalidationRegion());
511
0
        transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mLayerBounds.TopLeft());
512
0
        aData->mRect = transformedRect.Intersect(imageRect);
513
0
514
0
        aData->mInvalid = true;
515
0
      } else {
516
0
        if (aData->mClip != clip) {
517
0
          UniquePtr<nsDisplayItemGeometry> geometry(aItem->AllocateGeometry(aBuilder));
518
0
          if (!IsContainerLayerItem(aItem)) {
519
0
            // the bounds of layer items can change on us without ComputeInvalidationRegion
520
0
            // returning any change. Other items shouldn't have any hidden
521
0
            // geometry change.
522
0
            MOZ_RELEASE_ASSERT(geometry->mBounds.IsEqualEdges(aData->mGeometry->mBounds));
523
0
          } else {
524
0
            aData->mGeometry = std::move(geometry);
525
0
          }
526
0
          combined = clip.ApplyNonRoundedIntersection(aData->mGeometry->ComputeInvalidationRegion());
527
0
          IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mLayerBounds.TopLeft());
528
0
          InvalidateRect(aData->mRect.Intersect(imageRect));
529
0
          aData->mRect = transformedRect.Intersect(imageRect);
530
0
          InvalidateRect(aData->mRect);
531
0
532
0
          GP("ClipChange: %s %d %d %d %d\n", aItem->Name(),
533
0
                 aData->mRect.x, aData->mRect.y, aData->mRect.XMost(), aData->mRect.YMost());
534
0
535
0
        } else if (!aMatrix.ExactlyEquals(aData->mMatrix)) {
536
0
          // We haven't detected any changes so far. Unfortunately we don't
537
0
          // currently have a good way of checking if the transform has changed
538
0
          // so we just store it and see if it see if it has changed.
539
0
          // If we want this to go faster, we can probably put a flag on the frame
540
0
          // using the style sytem UpdateTransformLayer hint and check for that.
541
0
542
0
          UniquePtr<nsDisplayItemGeometry> geometry(aItem->AllocateGeometry(aBuilder));
543
0
          if (!IsContainerLayerItem(aItem)) {
544
0
            // the bounds of layer items can change on us
545
0
            // other items shouldn't
546
0
            MOZ_RELEASE_ASSERT(geometry->mBounds.IsEqualEdges(aData->mGeometry->mBounds));
547
0
          } else {
548
0
            aData->mGeometry = std::move(geometry);
549
0
          }
550
0
          combined = clip.ApplyNonRoundedIntersection(aData->mGeometry->ComputeInvalidationRegion());
551
0
          IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mLayerBounds.TopLeft());
552
0
          InvalidateRect(aData->mRect.Intersect(imageRect));
553
0
          aData->mRect = transformedRect.Intersect(imageRect);
554
0
          InvalidateRect(aData->mRect);
555
0
556
0
          GP("TransformChange: %s %d %d %d %d\n", aItem->Name(),
557
0
                 aData->mRect.x, aData->mRect.y, aData->mRect.XMost(), aData->mRect.YMost());
558
0
        } else if (IsContainerLayerItem(aItem)) {
559
0
          UniquePtr<nsDisplayItemGeometry> geometry(aItem->AllocateGeometry(aBuilder));
560
0
          // we need to catch bounds changes of containers so that we continue to have the correct bounds rects in the recording
561
0
          if (UpdateContainerLayerPropertiesAndDetectChange(aItem, aData, *geometry)) {
562
0
            combined = clip.ApplyNonRoundedIntersection(geometry->ComputeInvalidationRegion());
563
0
            aData->mGeometry = std::move(geometry);
564
0
            IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mLayerBounds.TopLeft());
565
0
            InvalidateRect(aData->mRect.Intersect(imageRect));
566
0
            aData->mRect = transformedRect.Intersect(imageRect);
567
0
            InvalidateRect(aData->mRect);
568
0
            GP("UpdateContainerLayerPropertiesAndDetectChange change\n");
569
0
          } else {
570
0
            // XXX: this code can eventually be deleted/made debug only
571
0
            combined = clip.ApplyNonRoundedIntersection(geometry->ComputeInvalidationRegion());
572
0
            IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mLayerBounds.TopLeft());
573
0
            auto rect = transformedRect.Intersect(imageRect);
574
0
            GP("Layer NoChange: %s %d %d %d %d\n", aItem->Name(),
575
0
                   aData->mRect.x, aData->mRect.y, aData->mRect.XMost(), aData->mRect.YMost());
576
0
            MOZ_RELEASE_ASSERT(rect.IsEqualEdges(aData->mRect));
577
0
          }
578
0
        } else {
579
0
          // XXX: this code can eventually be deleted/made debug only
580
0
          UniquePtr<nsDisplayItemGeometry> geometry(aItem->AllocateGeometry(aBuilder));
581
0
          combined = clip.ApplyNonRoundedIntersection(geometry->ComputeInvalidationRegion());
582
0
          IntRect transformedRect = ToDeviceSpace(combined.GetBounds(), aMatrix, appUnitsPerDevPixel, mLayerBounds.TopLeft());
583
0
          auto rect = transformedRect.Intersect(imageRect);
584
0
          GP("NoChange: %s %d %d %d %d\n", aItem->Name(),
585
0
                 aData->mRect.x, aData->mRect.y, aData->mRect.XMost(), aData->mRect.YMost());
586
0
          MOZ_RELEASE_ASSERT(rect.IsEqualEdges(aData->mRect));
587
0
        }
588
0
      }
589
0
    }
590
0
    aData->mClip = clip;
591
0
    aData->mMatrix = aMatrix;
592
0
    aData->mGroupOffset = mLayerBounds.TopLeft();
593
0
    aData->mImageRect = imageRect;
594
0
    GP("post mInvalidRect: %d %d %d %d\n", mInvalidRect.x, mInvalidRect.y, mInvalidRect.width, mInvalidRect.height);
595
0
  }
596
597
  void EndGroup(WebRenderLayerManager* aWrManager,
598
                nsDisplayListBuilder* aDisplayListBuilder,
599
                wr::DisplayListBuilder& aBuilder,
600
                wr::IpcResourceUpdateQueue& aResources,
601
                Grouper* aGrouper,
602
                nsDisplayItem* aStartItem,
603
                nsDisplayItem* aEndItem)
604
0
  {
605
0
    mLastAnimatedGeometryRootOrigin = mAnimatedGeometryRootOrigin;
606
0
    GP("\n\n");
607
0
    GP("Begin EndGroup\n");
608
0
609
0
    // Invalidate any unused items
610
0
    GP("mDisplayItems\n");
611
0
    for (auto iter = mDisplayItems.Iter(); !iter.Done(); iter.Next()) {
612
0
      BlobItemData* data = iter.Get()->GetKey();
613
0
      GP("  : %p-%d\n", data->mFrame, data->mDisplayItemKey);
614
0
      if (!data->mUsed) {
615
0
        GP("Invalidate unused: %p-%d\n", data->mFrame, data->mDisplayItemKey);
616
0
        InvalidateRect(data->mRect);
617
0
        iter.Remove();
618
0
        delete data;
619
0
      } else {
620
0
        data->mUsed = false;
621
0
      }
622
0
    }
623
0
624
0
    // Round the bounds out to leave space for unsnapped content
625
0
    LayoutDeviceToLayerScale2D scale(mScale.width, mScale.height);
626
0
    LayerIntRect layerBounds = mLayerBounds;
627
0
    IntSize dtSize = layerBounds.Size().ToUnknownSize();
628
0
    LayoutDeviceRect bounds = (LayerRect(layerBounds) - mResidualOffset) / scale;
629
0
630
0
    if (mInvalidRect.IsEmpty()) {
631
0
      GP("Not repainting group because it's empty\n");
632
0
      GP("End EndGroup\n");
633
0
      if (mKey) {
634
0
        SetBlobImageVisibleArea(aResources, mKey.value(), bounds, mPaintRect);
635
0
        PushImage(aBuilder, bounds);
636
0
      }
637
0
      return;
638
0
    }
639
0
640
0
    gfx::SurfaceFormat format = gfx::SurfaceFormat::B8G8R8A8;
641
0
    std::vector<RefPtr<ScaledFont>> fonts;
642
0
    RefPtr<WebRenderDrawEventRecorder> recorder =
643
0
      MakeAndAddRef<WebRenderDrawEventRecorder>(
644
0
        [&](MemStream& aStream, std::vector<RefPtr<ScaledFont>>& aScaledFonts) {
645
0
          size_t count = aScaledFonts.size();
646
0
          aStream.write((const char*)&count, sizeof(count));
647
0
          for (auto& scaled : aScaledFonts) {
648
0
            BlobFont font = {
649
0
              aWrManager->WrBridge()->GetFontKeyForScaledFont(scaled, &aResources),
650
0
              scaled
651
0
            };
652
0
            aStream.write((const char*)&font, sizeof(font));
653
0
          }
654
0
          fonts = std::move(aScaledFonts);
655
0
        });
656
0
657
0
    RefPtr<gfx::DrawTarget> dummyDt =
658
0
      gfx::Factory::CreateDrawTarget(gfx::BackendType::SKIA, gfx::IntSize(1, 1), format);
659
0
660
0
    RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateRecordingDrawTarget(recorder, dummyDt, dtSize);
661
0
    // Setup the gfxContext
662
0
    RefPtr<gfxContext> context = gfxContext::CreateOrNull(dt);
663
0
    GP("ctx-offset %f %f\n", bounds.x, bounds.y);
664
0
    context->SetMatrix(Matrix::Scaling(mScale.width, mScale.height).PreTranslate(-bounds.x, -bounds.y));
665
0
666
0
    GP("mInvalidRect: %d %d %d %d\n", mInvalidRect.x, mInvalidRect.y, mInvalidRect.width, mInvalidRect.height);
667
0
668
0
    bool empty = aStartItem == aEndItem;
669
0
    if (empty) {
670
0
      ClearImageKey(aWrManager, true);
671
0
      return;
672
0
    }
673
0
674
0
    PaintItemRange(aGrouper, aStartItem, aEndItem, context, recorder);
675
0
676
0
    // XXX: set this correctly perhaps using aItem->GetOpaqueRegion(aDisplayListBuilder, &snapped).Contains(paintBounds);?
677
0
    bool isOpaque = false;
678
0
679
0
    TakeExternalSurfaces(recorder, mExternalSurfaces, aWrManager, aResources);
680
0
    bool hasItems = recorder->Finish();
681
0
    GP("%d Finish\n", hasItems);
682
0
    Range<uint8_t> bytes((uint8_t*)recorder->mOutputStream.mData, recorder->mOutputStream.mLength);
683
0
    if (!mKey) {
684
0
      if (!hasItems) // we don't want to send a new image that doesn't have any items in it
685
0
        return;
686
0
      wr::ImageKey key = aWrManager->WrBridge()->GetNextImageKey();
687
0
      GP("No previous key making new one %d\n", key.mHandle);
688
0
      wr::ImageDescriptor descriptor(dtSize, 0, dt->GetFormat(), isOpaque);
689
0
      MOZ_RELEASE_ASSERT(bytes.length() > sizeof(size_t));
690
0
      if (!aResources.AddBlobImage(key, descriptor, bytes)) {
691
0
        return;
692
0
      }
693
0
      mKey = Some(key);
694
0
    } else {
695
0
      wr::ImageDescriptor descriptor(dtSize, 0, dt->GetFormat(), isOpaque);
696
0
      auto bottomRight = mInvalidRect.BottomRight();
697
0
      GP("check invalid %d %d - %d %d\n", bottomRight.x, bottomRight.y, dtSize.width, dtSize.height);
698
0
      MOZ_RELEASE_ASSERT(bottomRight.x <= dtSize.width && bottomRight.y <= dtSize.height);
699
0
      GP("Update Blob %d %d %d %d\n", mInvalidRect.x, mInvalidRect.y, mInvalidRect.width, mInvalidRect.height);
700
0
      if (!aResources.UpdateBlobImage(mKey.value(), descriptor, bytes, ViewAs<ImagePixel>(mInvalidRect))) {
701
0
        return;
702
0
      }
703
0
    }
704
0
    mFonts = std::move(fonts);
705
0
    mInvalidRect.SetEmpty();
706
0
    SetBlobImageVisibleArea(aResources, mKey.value(), mPaintRect, bounds);
707
0
    PushImage(aBuilder, bounds);
708
0
    GP("End EndGroup\n\n");
709
0
  }
710
711
  void PushImage(wr::DisplayListBuilder& aBuilder, const LayoutDeviceRect& bounds)
712
0
  {
713
0
    wr::LayoutRect dest = wr::ToLayoutRect(bounds);
714
0
    GP("PushImage: %f %f %f %f\n", dest.origin.x, dest.origin.y, dest.size.width, dest.size.height);
715
0
    gfx::SamplingFilter sampleFilter = gfx::SamplingFilter::LINEAR; //nsLayoutUtils::GetSamplingFilterForFrame(aItem->Frame());
716
0
    bool backfaceHidden = false;
717
0
718
0
    // Emit a dispatch-to-content hit test region covering this area
719
0
    auto hitInfo = CompositorHitTestInfo::eVisibleToHitTest |
720
0
                   CompositorHitTestInfo::eDispatchToContent;
721
0
722
0
    // XXX - clipping the item against the paint rect breaks some content.
723
0
    // cf. Bug 1455422.
724
0
    //wr::LayoutRect clip = wr::ToLayoutRect(bounds.Intersect(mPaintRect));
725
0
726
0
    aBuilder.SetHitTestInfo(mScrollId, hitInfo);
727
0
    aBuilder.PushImage(dest, dest, !backfaceHidden,
728
0
                       wr::ToImageRendering(sampleFilter),
729
0
                       mKey.value());
730
0
    aBuilder.ClearHitTestInfo();
731
0
  }
732
733
  void PaintItemRange(Grouper* aGrouper,
734
                      nsDisplayItem* aStartItem,
735
                      nsDisplayItem* aEndItem,
736
                      gfxContext* aContext,
737
0
                      WebRenderDrawEventRecorder* aRecorder) {
738
0
    LayerIntSize size = mLayerBounds.Size();
739
0
    for (nsDisplayItem* item = aStartItem; item != aEndItem; item = item->GetAbove()) {
740
0
      IntRect bounds = ItemBounds(item);
741
0
      auto bottomRight = bounds.BottomRight();
742
0
743
0
      GP("Trying %s %p-%d %d %d %d %d\n", item->Name(), item->Frame(), item->GetPerFrameKey(), bounds.x, bounds.y, bounds.XMost(), bounds.YMost());
744
0
      GP("paint check invalid %d %d - %d %d\n", bottomRight.x, bottomRight.y, size.width, size.height);
745
0
      // skip empty items
746
0
      if (bounds.IsEmpty()) {
747
0
          continue;
748
0
      }
749
0
750
0
      bool dirty = true;
751
0
      if (!mInvalidRect.Contains(bounds)) {
752
0
        GP("Passing\n");
753
0
        dirty = false;
754
0
      }
755
0
756
0
      if (mInvalidRect.Contains(bounds)) {
757
0
        GP("Wholely contained\n");
758
0
        BlobItemData* data = GetBlobItemData(item);
759
0
        data->mInvalid = false;
760
0
      } else {
761
0
        BlobItemData* data = GetBlobItemData(item);
762
0
        // if the item is invalid it needs to be fully contained
763
0
        MOZ_RELEASE_ASSERT(!data->mInvalid);
764
0
      }
765
0
766
0
      nsDisplayList* children = item->GetChildren();
767
0
      if (children) {
768
0
        GP("doing children in EndGroup\n");
769
0
        aGrouper->PaintContainerItem(this, item, bounds, children, aContext, aRecorder);
770
0
      } else {
771
0
        // Hit test items don't have anything to paint so skip them. Ideally we
772
0
        // would drop these items earlier...
773
0
        if (dirty && item->GetType() != DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) {
774
0
          // What should the clip settting strategy be? We can set the full clip everytime.
775
0
          // this is probably easiest for now. An alternative would be to put the push and the pop
776
0
          // into separate items and let invalidation handle it that way.
777
0
          DisplayItemClip currentClip = item->GetClip();
778
0
779
0
          if (currentClip.HasClip()) {
780
0
            aContext->Save();
781
0
            currentClip.ApplyTo(aContext, aGrouper->mAppUnitsPerDevPixel);
782
0
          }
783
0
          aContext->NewPath();
784
0
          GP("painting %s %p-%d\n", item->Name(), item->Frame(), item->GetPerFrameKey());
785
0
          if (aGrouper->mDisplayListBuilder->IsPaintingToWindow()) {
786
0
            item->Frame()->AddStateBits(NS_FRAME_PAINTED_THEBES);
787
0
          }
788
0
          item->Paint(aGrouper->mDisplayListBuilder, aContext);
789
0
          if (currentClip.HasClip()) {
790
0
            aContext->Restore();
791
0
          }
792
0
        }
793
0
        aContext->GetDrawTarget()->FlushItem(bounds);
794
0
      }
795
0
    }
796
0
  }
797
798
  ~DIGroup()
799
0
  {
800
0
    GP("Group destruct\n");
801
0
    for (auto iter = mDisplayItems.Iter(); !iter.Done(); iter.Next()) {
802
0
      BlobItemData* data = iter.Get()->GetKey();
803
0
      GP("Deleting %p-%d\n", data->mFrame, data->mDisplayItemKey);
804
0
      iter.Remove();
805
0
      delete data;
806
0
    }
807
0
  }
808
};
809
810
// If we have an item we need to make sure it matches the current group
811
// otherwise it means the item switched groups and we need to invalidate
812
// it and recreate the data.
813
static BlobItemData*
814
GetBlobItemDataForGroup(nsDisplayItem* aItem, DIGroup* aGroup)
815
0
{
816
0
  BlobItemData* data = GetBlobItemData(aItem);
817
0
  if (data) {
818
0
    MOZ_RELEASE_ASSERT(data->mGroup->mDisplayItems.Contains(data));
819
0
    if (data->mGroup != aGroup) {
820
0
      GP("group don't match %p %p\n", data->mGroup, aGroup);
821
0
      data->ClearFrame();
822
0
      // the item is for another group
823
0
      // it should be cleared out as being unused at the end of this paint
824
0
      data = nullptr;
825
0
    }
826
0
  }
827
0
  if (!data) {
828
0
    GP("Allocating blob data\n");
829
0
    data = new BlobItemData(aGroup, aItem);
830
0
    aGroup->mDisplayItems.PutEntry(data);
831
0
  }
832
0
  data->mUsed = true;
833
0
  return data;
834
0
}
835
836
void
837
Grouper::PaintContainerItem(DIGroup* aGroup, nsDisplayItem* aItem, const IntRect& aItemBounds,
838
                            nsDisplayList* aChildren, gfxContext* aContext,
839
                            WebRenderDrawEventRecorder* aRecorder)
840
0
{
841
0
  mItemStack.push_back(aItem);
842
0
  switch (aItem->GetType()) {
843
0
    case DisplayItemType::TYPE_TRANSFORM: {
844
0
      DisplayItemClip currentClip = aItem->GetClip();
845
0
846
0
      gfx::Matrix matrix;
847
0
      if (currentClip.HasClip()) {
848
0
        aContext->Save();
849
0
        currentClip.ApplyTo(aContext, this->mAppUnitsPerDevPixel);
850
0
        aContext->GetDrawTarget()->FlushItem(aItemBounds);
851
0
      } else {
852
0
        matrix = aContext->CurrentMatrix();
853
0
      }
854
0
855
0
      auto transformItem = static_cast<nsDisplayTransform*>(aItem);
856
0
      Matrix4x4Flagged trans = transformItem->GetTransform();
857
0
      Matrix trans2d;
858
0
      if (!trans.Is2D(&trans2d)) {
859
0
        // We don't currently support doing invalidation inside 3d transforms.
860
0
        // For now just paint it as a single item.
861
0
        BlobItemData* data = GetBlobItemDataForGroup(aItem, aGroup);
862
0
        if (data->mLayerManager->GetRoot()) {
863
0
          data->mLayerManager->BeginTransaction();
864
0
          data->mLayerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer, mDisplayListBuilder);
865
0
          aContext->GetDrawTarget()->FlushItem(aItemBounds);
866
0
        }
867
0
      } else {
868
0
        aContext->Multiply(ThebesMatrix(trans2d));
869
0
        aGroup->PaintItemRange(this, aChildren->GetBottom(), nullptr, aContext, aRecorder);
870
0
871
0
        if (currentClip.HasClip()) {
872
0
          aContext->Restore();
873
0
          aContext->GetDrawTarget()->FlushItem(aItemBounds);
874
0
        } else {
875
0
          aContext->SetMatrix(matrix);
876
0
        }
877
0
      }
878
0
      break;
879
0
    }
880
0
    case DisplayItemType::TYPE_OPACITY: {
881
0
      auto opacityItem = static_cast<nsDisplayOpacity*>(aItem);
882
0
      float opacity = opacityItem->GetOpacity();
883
0
      if (opacity == 0.0f) {
884
0
        return;
885
0
      }
886
0
887
0
      aContext->GetDrawTarget()->PushLayer(false, opacityItem->GetOpacity(), nullptr, mozilla::gfx::Matrix(), aItemBounds);
888
0
      GP("beginGroup %s %p-%d\n", aItem->Name(), aItem->Frame(), aItem->GetPerFrameKey());
889
0
      aContext->GetDrawTarget()->FlushItem(aItemBounds);
890
0
      aGroup->PaintItemRange(this, aChildren->GetBottom(), nullptr, aContext, aRecorder);
891
0
      aContext->GetDrawTarget()->PopLayer();
892
0
      GP("endGroup %s %p-%d\n", aItem->Name(), aItem->Frame(), aItem->GetPerFrameKey());
893
0
      aContext->GetDrawTarget()->FlushItem(aItemBounds);
894
0
      break;
895
0
    }
896
0
    case DisplayItemType::TYPE_BLEND_MODE: {
897
0
      auto blendItem = static_cast<nsDisplayBlendMode*>(aItem);
898
0
      auto blendMode = blendItem->BlendMode();
899
0
      aContext->GetDrawTarget()->PushLayerWithBlend(false, 1.0, nullptr, mozilla::gfx::Matrix(), aItemBounds, false, blendMode);
900
0
      GP("beginGroup %s %p-%d\n", aItem->Name(), aItem->Frame(), aItem->GetPerFrameKey());
901
0
      aContext->GetDrawTarget()->FlushItem(aItemBounds);
902
0
      aGroup->PaintItemRange(this, aChildren->GetBottom(), nullptr, aContext, aRecorder);
903
0
      aContext->GetDrawTarget()->PopLayer();
904
0
      GP("endGroup %s %p-%d\n", aItem->Name(), aItem->Frame(), aItem->GetPerFrameKey());
905
0
      aContext->GetDrawTarget()->FlushItem(aItemBounds);
906
0
      break;
907
0
    }
908
0
    case DisplayItemType::TYPE_MASK: {
909
0
      GP("Paint Mask\n");
910
0
      auto maskItem = static_cast<nsDisplayMask*>(aItem);
911
0
      maskItem->SetPaintRect(maskItem->GetClippedBounds(mDisplayListBuilder));
912
0
      if (maskItem->IsValidMask()) {
913
0
        maskItem->PaintWithContentsPaintCallback(mDisplayListBuilder, aContext, [&] {
914
0
          GP("beginGroup %s %p-%d\n", aItem->Name(), aItem->Frame(), aItem->GetPerFrameKey());
915
0
          aContext->GetDrawTarget()->FlushItem(aItemBounds);
916
0
          aGroup->PaintItemRange(this, aChildren->GetBottom(), nullptr, aContext, aRecorder);
917
0
          GP("endGroup %s %p-%d\n", aItem->Name(), aItem->Frame(), aItem->GetPerFrameKey());
918
0
          });
919
0
        aContext->GetDrawTarget()->FlushItem(aItemBounds);
920
0
      }
921
0
      break;
922
0
    }
923
0
    case DisplayItemType::TYPE_FILTER: {
924
0
      GP("Paint Filter\n");
925
0
      // We don't currently support doing invalidation inside nsDisplayFilter
926
0
      // for now just paint it as a single item
927
0
      BlobItemData* data = GetBlobItemDataForGroup(aItem, aGroup);
928
0
      if (data->mLayerManager->GetRoot()) {
929
0
        data->mLayerManager->BeginTransaction();
930
0
        static_cast<nsDisplayFilter*>(aItem)->PaintAsLayer(mDisplayListBuilder,
931
0
                                                           aContext, data->mLayerManager);
932
0
        if (data->mLayerManager->InTransaction()) {
933
0
          data->mLayerManager->AbortTransaction();
934
0
        }
935
0
        aContext->GetDrawTarget()->FlushItem(aItemBounds);
936
0
      }
937
0
      break;
938
0
    }
939
0
940
0
    default:
941
0
      aGroup->PaintItemRange(this, aChildren->GetBottom(), nullptr, aContext, aRecorder);
942
0
      break;
943
0
  }
944
0
}
945
946
class WebRenderGroupData : public WebRenderUserData
947
{
948
public:
949
  explicit WebRenderGroupData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem);
950
  virtual ~WebRenderGroupData();
951
952
0
  virtual WebRenderGroupData* AsGroupData() override { return this; }
953
0
  virtual UserDataType GetType() override { return UserDataType::eGroup; }
954
0
  static UserDataType Type() { return UserDataType::eGroup; }
955
956
  DIGroup mSubGroup;
957
  DIGroup mFollowingGroup;
958
};
959
960
static bool
961
IsItemProbablyActive(nsDisplayItem* aItem, nsDisplayListBuilder* aDisplayListBuilder);
962
963
static bool
964
HasActiveChildren(const nsDisplayList& aList, nsDisplayListBuilder *aDisplayListBuilder)
965
0
{
966
0
  for (nsDisplayItem* i = aList.GetBottom(); i; i = i->GetAbove()) {
967
0
    if (IsItemProbablyActive(i, aDisplayListBuilder)) {
968
0
      return true;
969
0
    }
970
0
  }
971
0
  return false;
972
0
}
973
974
// This function decides whether we want to treat this item as "active", which means
975
// that it's a container item which we will turn into a WebRender StackingContext, or
976
// whether we treat it as "inactive" and include it inside the parent blob image.
977
//
978
// We can't easily use GetLayerState because it wants a bunch of layers related information.
979
static bool
980
IsItemProbablyActive(nsDisplayItem* aItem, nsDisplayListBuilder* aDisplayListBuilder)
981
0
{
982
0
  switch (aItem->GetType()) {
983
0
  case DisplayItemType::TYPE_TRANSFORM: {
984
0
    nsDisplayTransform* transformItem = static_cast<nsDisplayTransform*>(aItem);
985
0
    const Matrix4x4Flagged& t = transformItem->GetTransform();
986
0
    Matrix t2d;
987
0
    bool is2D = t.Is2D(&t2d);
988
0
    GP("active: %d\n", transformItem->MayBeAnimated(aDisplayListBuilder));
989
0
    return transformItem->MayBeAnimated(aDisplayListBuilder) || !is2D || HasActiveChildren(*transformItem->GetChildren(), aDisplayListBuilder);
990
0
  }
991
0
  case DisplayItemType::TYPE_OPACITY: {
992
0
    nsDisplayOpacity* opacityItem = static_cast<nsDisplayOpacity*>(aItem);
993
0
    bool active = opacityItem->NeedsActiveLayer(aDisplayListBuilder, opacityItem->Frame());
994
0
    GP("active: %d\n", active);
995
0
    return active || HasActiveChildren(*opacityItem->GetChildren(), aDisplayListBuilder);
996
0
  }
997
0
  case DisplayItemType::TYPE_FOREIGN_OBJECT: {
998
0
    return true;
999
0
  }
1000
0
  case DisplayItemType::TYPE_WRAP_LIST:
1001
0
  case DisplayItemType::TYPE_PERSPECTIVE: {
1002
0
    if (aItem->GetChildren()) {
1003
0
      return HasActiveChildren(*aItem->GetChildren(), aDisplayListBuilder);
1004
0
    }
1005
0
    return false;
1006
0
  }
1007
0
  default:
1008
0
    // TODO: handle other items?
1009
0
    return false;
1010
0
  }
1011
0
}
1012
1013
// This does a pass over the display lists and will join the display items
1014
// into groups as well as paint them
1015
void
1016
Grouper::ConstructGroups(nsDisplayListBuilder* aDisplayListBuilder,
1017
                         WebRenderCommandBuilder* aCommandBuilder,
1018
                         wr::DisplayListBuilder& aBuilder,
1019
                         wr::IpcResourceUpdateQueue& aResources,
1020
                         DIGroup* aGroup, nsDisplayList* aList,
1021
                         const StackingContextHelper& aSc)
1022
0
{
1023
0
  DIGroup* currentGroup = aGroup;
1024
0
1025
0
  nsDisplayItem* item = aList->GetBottom();
1026
0
  nsDisplayItem* startOfCurrentGroup = item;
1027
0
  while (item) {
1028
0
    if (IsItemProbablyActive(item, mDisplayListBuilder)) {
1029
0
      currentGroup->EndGroup(aCommandBuilder->mManager, aDisplayListBuilder, aBuilder, aResources, this, startOfCurrentGroup, item);
1030
0
      mClipManager.BeginItem(item, aSc);
1031
0
      sIndent++;
1032
0
      // Note: this call to CreateWebRenderCommands can recurse back into
1033
0
      // this function.
1034
0
      bool createdWRCommands =
1035
0
        item->CreateWebRenderCommands(aBuilder, aResources, aSc, aCommandBuilder->mManager,
1036
0
                                      mDisplayListBuilder);
1037
0
      sIndent--;
1038
0
      MOZ_RELEASE_ASSERT(createdWRCommands, "active transforms should always succeed at creating WebRender commands");
1039
0
1040
0
      RefPtr<WebRenderGroupData> groupData =
1041
0
        aCommandBuilder->CreateOrRecycleWebRenderUserData<WebRenderGroupData>(item);
1042
0
1043
0
      // Initialize groupData->mFollowingGroup
1044
0
      // TODO: compute the group bounds post-grouping, so that they can be
1045
0
      // tighter for just the sublist that made it into this group.
1046
0
      // We want to ensure the tight bounds are still clipped by area
1047
0
      // that we're building the display list for.
1048
0
      if (!groupData->mFollowingGroup.mGroupBounds.IsEqualEdges(currentGroup->mGroupBounds) ||
1049
0
          groupData->mFollowingGroup.mScale != currentGroup->mScale ||
1050
0
          groupData->mFollowingGroup.mAppUnitsPerDevPixel != currentGroup->mAppUnitsPerDevPixel ||
1051
0
          groupData->mFollowingGroup.mResidualOffset != currentGroup->mResidualOffset) {
1052
0
        if (groupData->mFollowingGroup.mAppUnitsPerDevPixel != currentGroup->mAppUnitsPerDevPixel) {
1053
0
          GP("app unit change following: %d %d\n", groupData->mFollowingGroup.mAppUnitsPerDevPixel, currentGroup->mAppUnitsPerDevPixel);
1054
0
        }
1055
0
        // The group changed size
1056
0
        GP("Inner group size change\n");
1057
0
        groupData->mFollowingGroup.ClearItems();
1058
0
        groupData->mFollowingGroup.ClearImageKey(aCommandBuilder->mManager);
1059
0
      }
1060
0
      groupData->mFollowingGroup.mGroupBounds = currentGroup->mGroupBounds;
1061
0
      groupData->mFollowingGroup.mAppUnitsPerDevPixel = currentGroup->mAppUnitsPerDevPixel;
1062
0
      groupData->mFollowingGroup.mLayerBounds = currentGroup->mLayerBounds;
1063
0
      groupData->mFollowingGroup.mScale = currentGroup->mScale;
1064
0
      groupData->mFollowingGroup.mResidualOffset = currentGroup->mResidualOffset;
1065
0
      groupData->mFollowingGroup.mPaintRect = currentGroup->mPaintRect;
1066
0
1067
0
      currentGroup = &groupData->mFollowingGroup;
1068
0
1069
0
      startOfCurrentGroup = item->GetAbove();
1070
0
    } else { // inactive item
1071
0
      ConstructItemInsideInactive(aCommandBuilder, aBuilder, aResources,
1072
0
                                  currentGroup, item, aSc);
1073
0
    }
1074
0
1075
0
    item = item->GetAbove();
1076
0
  }
1077
0
1078
0
  currentGroup->EndGroup(aCommandBuilder->mManager, aDisplayListBuilder, aBuilder, aResources, this, startOfCurrentGroup, nullptr);
1079
0
}
1080
1081
// This does a pass over the display lists and will join the display items
1082
// into a single group.
1083
void
1084
Grouper::ConstructGroupInsideInactive(WebRenderCommandBuilder* aCommandBuilder,
1085
                                       wr::DisplayListBuilder& aBuilder,
1086
                                       wr::IpcResourceUpdateQueue& aResources,
1087
                                       DIGroup* aGroup, nsDisplayList* aList,
1088
                                       const StackingContextHelper& aSc)
1089
0
{
1090
0
  nsDisplayItem* item = aList->GetBottom();
1091
0
  while (item) {
1092
0
    ConstructItemInsideInactive(aCommandBuilder, aBuilder, aResources,
1093
0
                                aGroup, item, aSc);
1094
0
    item = item->GetAbove();
1095
0
  }
1096
0
}
1097
1098
bool BuildLayer(nsDisplayItem* aItem, BlobItemData* aData,
1099
                nsDisplayListBuilder* aDisplayListBuilder,
1100
                const gfx::Size& aScale);
1101
1102
void
1103
Grouper::ConstructItemInsideInactive(WebRenderCommandBuilder* aCommandBuilder,
1104
                                     wr::DisplayListBuilder& aBuilder,
1105
                                     wr::IpcResourceUpdateQueue& aResources,
1106
                                     DIGroup* aGroup, nsDisplayItem* aItem,
1107
                                     const StackingContextHelper& aSc)
1108
0
{
1109
0
  nsDisplayList* children = aItem->GetChildren();
1110
0
  BlobItemData* data = GetBlobItemDataForGroup(aItem, aGroup);
1111
0
1112
0
  if (aItem->GetType() == DisplayItemType::TYPE_FILTER) {
1113
0
    gfx::Size scale(1, 1);
1114
0
    // If ComputeDifferences finds any change, we invalidate the entire container item.
1115
0
    // This is needed because blob merging requires the entire item to be within the invalid region.
1116
0
    data->mInvalid = BuildLayer(aItem, data, mDisplayListBuilder, scale);
1117
0
  } else if (aItem->GetType() == DisplayItemType::TYPE_TRANSFORM) {
1118
0
    nsDisplayTransform* transformItem = static_cast<nsDisplayTransform*>(aItem);
1119
0
    const Matrix4x4Flagged& t = transformItem->GetTransform();
1120
0
    Matrix t2d;
1121
0
    bool is2D = t.Is2D(&t2d);
1122
0
    if (!is2D) {
1123
0
      // We'll use BasicLayerManager to handle 3d transforms.
1124
0
      gfx::Size scale(1, 1);
1125
0
      // If ComputeDifferences finds any change, we invalidate the entire container item.
1126
0
      // This is needed because blob merging requires the entire item to be within the invalid region.
1127
0
      data->mInvalid = BuildLayer(aItem, data, mDisplayListBuilder, scale);
1128
0
    } else {
1129
0
      Matrix m = mTransform;
1130
0
1131
0
      GP("t2d: %f %f\n", t2d._31, t2d._32);
1132
0
      mTransform.PreMultiply(t2d);
1133
0
      GP("mTransform: %f %f\n", mTransform._31, mTransform._32);
1134
0
      ConstructGroupInsideInactive(aCommandBuilder, aBuilder, aResources, aGroup, children, aSc);
1135
0
1136
0
      mTransform = m;
1137
0
    }
1138
0
  } else if (children) {
1139
0
    sIndent++;
1140
0
    ConstructGroupInsideInactive(aCommandBuilder, aBuilder, aResources, aGroup, children, aSc);
1141
0
    sIndent--;
1142
0
  }
1143
0
1144
0
  GP("Including %s of %d\n", aItem->Name(), aGroup->mDisplayItems.Count());
1145
0
1146
0
  aGroup->ComputeGeometryChange(aItem, data, mTransform, mDisplayListBuilder); // we compute the geometry change here because we have the transform around still
1147
0
}
1148
1149
/* This is just a copy of nsRect::ScaleToOutsidePixels with an offset added in.
1150
 * The offset is applied just before the rounding. It's in the scaled space. */
1151
static mozilla::gfx::IntRect
1152
ScaleToOutsidePixelsOffset(nsRect aRect, float aXScale, float aYScale,
1153
                           nscoord aAppUnitsPerPixel, LayerPoint aOffset)
1154
0
{
1155
0
  mozilla::gfx::IntRect rect;
1156
0
  rect.SetNonEmptyBox(NSToIntFloor(NSAppUnitsToFloatPixels(aRect.x,
1157
0
                                                           float(aAppUnitsPerPixel)) * aXScale + aOffset.x),
1158
0
                      NSToIntFloor(NSAppUnitsToFloatPixels(aRect.y,
1159
0
                                                           float(aAppUnitsPerPixel)) * aYScale + aOffset.y),
1160
0
                      NSToIntCeil(NSAppUnitsToFloatPixels(aRect.XMost(),
1161
0
                                                          float(aAppUnitsPerPixel)) * aXScale + aOffset.x),
1162
0
                      NSToIntCeil(NSAppUnitsToFloatPixels(aRect.YMost(),
1163
0
                                                          float(aAppUnitsPerPixel)) * aYScale + aOffset.y));
1164
0
  return rect;
1165
0
}
1166
1167
void
1168
WebRenderCommandBuilder::DoGroupingForDisplayList(nsDisplayList* aList,
1169
                                                  nsDisplayItem* aWrappingItem,
1170
                                                  nsDisplayListBuilder* aDisplayListBuilder,
1171
                                                  const StackingContextHelper& aSc,
1172
                                                  wr::DisplayListBuilder& aBuilder,
1173
                                                  wr::IpcResourceUpdateQueue& aResources)
1174
0
{
1175
0
  if (!aList->GetBottom()) {
1176
0
    return;
1177
0
  }
1178
0
1179
0
  mClipManager.BeginList(aSc);
1180
0
  Grouper g(mClipManager);
1181
0
  int32_t appUnitsPerDevPixel = aWrappingItem->Frame()->PresContext()->AppUnitsPerDevPixel();
1182
0
  GP("DoGroupingForDisplayList\n");
1183
0
1184
0
  g.mDisplayListBuilder = aDisplayListBuilder;
1185
0
  RefPtr<WebRenderGroupData> groupData = CreateOrRecycleWebRenderUserData<WebRenderGroupData>(aWrappingItem);
1186
0
  bool snapped;
1187
0
  nsRect groupBounds = aWrappingItem->GetBounds(aDisplayListBuilder, &snapped);
1188
0
  DIGroup& group = groupData->mSubGroup;
1189
0
  auto p = group.mGroupBounds;
1190
0
  auto q = groupBounds;
1191
0
  gfx::Size scale = aSc.GetInheritedScale();
1192
0
  auto trans = ViewAs<LayerPixel>(aSc.GetSnappingSurfaceTransform().GetTranslation());
1193
0
  auto snappedTrans = LayerIntPoint::Floor(trans);
1194
0
  LayerPoint residualOffset = trans - snappedTrans;
1195
0
1196
0
  GP("Inherrited scale %f %f\n", scale.width, scale.height);
1197
0
  GP("Bounds: %d %d %d %d vs %d %d %d %d\n", p.x, p.y, p.width, p.height, q.x, q.y, q.width, q.height);
1198
0
  if (!group.mGroupBounds.IsEqualEdges(groupBounds) ||
1199
0
      group.mAppUnitsPerDevPixel != appUnitsPerDevPixel ||
1200
0
      group.mScale != scale ||
1201
0
      group.mResidualOffset != residualOffset) {
1202
0
    GP("Property change. Deleting blob\n");
1203
0
1204
0
    if (group.mAppUnitsPerDevPixel != appUnitsPerDevPixel) {
1205
0
      GP(" App unit change %d -> %d\n", group.mAppUnitsPerDevPixel, appUnitsPerDevPixel);
1206
0
    }
1207
0
    // The bounds have changed so we need to discard the old image and add all
1208
0
    // the commands again.
1209
0
    auto p = group.mGroupBounds;
1210
0
    auto q = groupBounds;
1211
0
    if (!group.mGroupBounds.IsEqualEdges(groupBounds)) {
1212
0
      GP(" Bounds change: %d %d %d %d -> %d %d %d %d\n", p.x, p.y, p.width, p.height, q.x, q.y, q.width, q.height);
1213
0
    }
1214
0
1215
0
    if (group.mScale != scale) {
1216
0
      GP(" Scale %f %f -> %f %f\n", group.mScale.width, group.mScale.height, scale.width, scale.height);
1217
0
    }
1218
0
1219
0
    if (group.mResidualOffset != residualOffset) {
1220
0
      GP(" Residual Offset %f %f -> %f %f\n", group.mResidualOffset.x, group.mResidualOffset.y, residualOffset.x, residualOffset.y);
1221
0
    }
1222
0
1223
0
    group.ClearItems();
1224
0
    group.ClearImageKey(mManager);
1225
0
  }
1226
0
1227
0
  FrameMetrics::ViewID scrollId = FrameMetrics::NULL_SCROLL_ID;
1228
0
  if (const ActiveScrolledRoot* asr = aWrappingItem->GetActiveScrolledRoot()) {
1229
0
    scrollId = asr->GetViewId();
1230
0
  }
1231
0
1232
0
  g.mAppUnitsPerDevPixel = appUnitsPerDevPixel;
1233
0
  group.mResidualOffset = residualOffset;
1234
0
  group.mGroupBounds = groupBounds;
1235
0
  group.mPaintRect = LayoutDeviceRect::FromAppUnits(
1236
0
    aWrappingItem->GetPaintRect(),
1237
0
    appUnitsPerDevPixel
1238
0
  );
1239
0
  group.mAppUnitsPerDevPixel = appUnitsPerDevPixel;
1240
0
  group.mLayerBounds = LayerIntRect::FromUnknownRect(ScaleToOutsidePixelsOffset(group.mGroupBounds,
1241
0
                                                                                scale.width,
1242
0
                                                                                scale.height,
1243
0
                                                                                group.mAppUnitsPerDevPixel,
1244
0
                                                                                residualOffset));
1245
0
  g.mTransform = Matrix::Scaling(scale.width, scale.height)
1246
0
                                .PostTranslate(residualOffset.x, residualOffset.y);
1247
0
  group.mScale = scale;
1248
0
  group.mScrollId = scrollId;
1249
0
  group.mAnimatedGeometryRootOrigin = group.mGroupBounds.TopLeft();
1250
0
  g.ConstructGroups(aDisplayListBuilder, this, aBuilder, aResources, &group, aList, aSc);
1251
0
  mClipManager.EndList(aSc);
1252
0
}
1253
1254
void
1255
WebRenderCommandBuilder::Destroy()
1256
0
{
1257
0
  mLastCanvasDatas.Clear();
1258
0
  ClearCachedResources();
1259
0
}
1260
1261
void
1262
WebRenderCommandBuilder::EmptyTransaction()
1263
0
{
1264
0
  // We need to update canvases that might have changed.
1265
0
  for (auto iter = mLastCanvasDatas.Iter(); !iter.Done(); iter.Next()) {
1266
0
    RefPtr<WebRenderCanvasData> canvasData = iter.Get()->GetKey();
1267
0
    WebRenderCanvasRendererAsync* canvas = canvasData->GetCanvasRenderer();
1268
0
    if (canvas) {
1269
0
      canvas->UpdateCompositableClientForEmptyTransaction();
1270
0
    }
1271
0
  }
1272
0
}
1273
1274
bool
1275
WebRenderCommandBuilder::NeedsEmptyTransaction()
1276
0
{
1277
0
  return !mLastCanvasDatas.IsEmpty();
1278
0
}
1279
1280
void
1281
WebRenderCommandBuilder::BuildWebRenderCommands(wr::DisplayListBuilder& aBuilder,
1282
                                                wr::IpcResourceUpdateQueue& aResourceUpdates,
1283
                                                nsDisplayList* aDisplayList,
1284
                                                nsDisplayListBuilder* aDisplayListBuilder,
1285
                                                WebRenderScrollData& aScrollData,
1286
                                                wr::LayoutSize& aContentSize,
1287
                                                const nsTArray<wr::WrFilterOp>& aFilters)
1288
0
{
1289
0
  StackingContextHelper sc;
1290
0
  aScrollData = WebRenderScrollData(mManager);
1291
0
  MOZ_ASSERT(mLayerScrollData.empty());
1292
0
  mLastCanvasDatas.Clear();
1293
0
  mLastAsr = nullptr;
1294
0
  mBuilderDumpIndex = 0;
1295
0
  mContainsSVGGroup = false;
1296
0
  MOZ_ASSERT(mDumpIndent == 0);
1297
0
  mClipManager.BeginBuild(mManager, aBuilder);
1298
0
1299
0
  {
1300
0
    StackingContextHelper pageRootSc(sc, aBuilder, aFilters);
1301
0
    if (ShouldDumpDisplayList()) {
1302
0
      mBuilderDumpIndex = aBuilder.Dump(mDumpIndent + 1, Some(mBuilderDumpIndex), Nothing());
1303
0
    }
1304
0
    CreateWebRenderCommandsFromDisplayList(aDisplayList, nullptr, aDisplayListBuilder,
1305
0
                                           pageRootSc, aBuilder, aResourceUpdates);
1306
0
  }
1307
0
1308
0
  // Make a "root" layer data that has everything else as descendants
1309
0
  mLayerScrollData.emplace_back();
1310
0
  mLayerScrollData.back().InitializeRoot(mLayerScrollData.size() - 1);
1311
0
  auto callback = [&aScrollData](FrameMetrics::ViewID aScrollId) -> bool {
1312
0
    return aScrollData.HasMetadataFor(aScrollId).isSome();
1313
0
  };
1314
0
  if (Maybe<ScrollMetadata> rootMetadata = nsLayoutUtils::GetRootMetadata(
1315
0
        aDisplayListBuilder, mManager, ContainerLayerParameters(), callback)) {
1316
0
    mLayerScrollData.back().AppendScrollMetadata(aScrollData, rootMetadata.ref());
1317
0
  }
1318
0
  // Append the WebRenderLayerScrollData items into WebRenderScrollData
1319
0
  // in reverse order, from topmost to bottommost. This is in keeping with
1320
0
  // the semantics of WebRenderScrollData.
1321
0
  for (auto i = mLayerScrollData.crbegin(); i != mLayerScrollData.crend(); i++) {
1322
0
    aScrollData.AddLayerData(*i);
1323
0
  }
1324
0
  mLayerScrollData.clear();
1325
0
  mClipManager.EndBuild();
1326
0
1327
0
  // Remove the user data those are not displayed on the screen and
1328
0
  // also reset the data to unused for next transaction.
1329
0
  RemoveUnusedAndResetWebRenderUserData();
1330
0
}
1331
1332
bool
1333
WebRenderCommandBuilder::ShouldDumpDisplayList()
1334
0
{
1335
0
  return (XRE_IsParentProcess() && gfxPrefs::WebRenderDLDumpParent()) ||
1336
0
         (XRE_IsContentProcess() && gfxPrefs::WebRenderDLDumpContent());
1337
0
}
1338
1339
void
1340
WebRenderCommandBuilder::CreateWebRenderCommandsFromDisplayList(nsDisplayList* aDisplayList,
1341
                                                                nsDisplayItem* aWrappingItem,
1342
                                                                nsDisplayListBuilder* aDisplayListBuilder,
1343
                                                                const StackingContextHelper& aSc,
1344
                                                                wr::DisplayListBuilder& aBuilder,
1345
                                                                wr::IpcResourceUpdateQueue& aResources)
1346
0
{
1347
0
  if (mDoGrouping) {
1348
0
    MOZ_RELEASE_ASSERT(aWrappingItem, "Only the root list should have a null wrapping item, and mDoGrouping should never be true for the root list.");
1349
0
    GP("actually entering the grouping code\n");
1350
0
    DoGroupingForDisplayList(aDisplayList, aWrappingItem, aDisplayListBuilder, aSc, aBuilder, aResources);
1351
0
    return;
1352
0
  }
1353
0
1354
0
  bool dumpEnabled = ShouldDumpDisplayList();
1355
0
  if (dumpEnabled) {
1356
0
    // If we're inside a nested display list, print the WR DL items from the
1357
0
    // wrapper item before we start processing the nested items.
1358
0
    mBuilderDumpIndex = aBuilder.Dump(mDumpIndent + 1, Some(mBuilderDumpIndex), Nothing());
1359
0
  }
1360
0
1361
0
  mDumpIndent++;
1362
0
  mClipManager.BeginList(aSc);
1363
0
1364
0
  bool apzEnabled = mManager->AsyncPanZoomEnabled();
1365
0
1366
0
  FlattenedDisplayItemIterator iter(aDisplayListBuilder, aDisplayList);
1367
0
  while (nsDisplayItem* i = iter.GetNext()) {
1368
0
    nsDisplayItem* item = i;
1369
0
    DisplayItemType itemType = item->GetType();
1370
0
1371
0
    // Peek ahead to the next item and try merging with it or swapping with it
1372
0
    // if necessary.
1373
0
    AutoTArray<nsDisplayItem*, 1> mergedItems;
1374
0
    mergedItems.AppendElement(item);
1375
0
    while (nsDisplayItem* peek = iter.PeekNext()) {
1376
0
      if (!item->CanMerge(peek)) {
1377
0
        break;
1378
0
      }
1379
0
1380
0
      mergedItems.AppendElement(peek);
1381
0
1382
0
      // Move the iterator forward since we will merge this item.
1383
0
      i = iter.GetNext();
1384
0
    }
1385
0
1386
0
    if (mergedItems.Length() > 1) {
1387
0
      item = aDisplayListBuilder->MergeItems(mergedItems);
1388
0
      MOZ_ASSERT(item && itemType == item->GetType());
1389
0
    }
1390
0
1391
0
    bool forceNewLayerData = false;
1392
0
    size_t layerCountBeforeRecursing = mLayerScrollData.size();
1393
0
    if (apzEnabled) {
1394
0
      // For some types of display items we want to force a new
1395
0
      // WebRenderLayerScrollData object, to ensure we preserve the APZ-relevant
1396
0
      // data that is in the display item.
1397
0
      forceNewLayerData = item->UpdateScrollData(nullptr, nullptr);
1398
0
1399
0
      // Anytime the ASR changes we also want to force a new layer data because
1400
0
      // the stack of scroll metadata is going to be different for this
1401
0
      // display item than previously, so we can't squash the display items
1402
0
      // into the same "layer".
1403
0
      const ActiveScrolledRoot* asr = item->GetActiveScrolledRoot();
1404
0
      if (asr != mLastAsr) {
1405
0
        mLastAsr = asr;
1406
0
        forceNewLayerData = true;
1407
0
      }
1408
0
1409
0
      // If we're going to create a new layer data for this item, stash the
1410
0
      // ASR so that if we recurse into a sublist they will know where to stop
1411
0
      // walking up their ASR chain when building scroll metadata.
1412
0
      if (forceNewLayerData) {
1413
0
        mAsrStack.push_back(asr);
1414
0
      }
1415
0
    }
1416
0
1417
0
    mClipManager.BeginItem(item, aSc);
1418
0
1419
0
    { // scope restoreDoGrouping
1420
0
      AutoRestore<bool> restoreDoGrouping(mDoGrouping);
1421
0
      if (itemType == DisplayItemType::TYPE_SVG_WRAPPER) {
1422
0
        // Inside an <svg>, all display items that are not LAYER_ACTIVE wrapper
1423
0
        // display items (like animated transforms / opacity) share the same
1424
0
        // animated geometry root, so we can combine subsequent items of that
1425
0
        // type into the same image.
1426
0
        mContainsSVGGroup = mDoGrouping = true;
1427
0
        GP("attempting to enter the grouping code\n");
1428
0
      }
1429
0
1430
0
      if (dumpEnabled) {
1431
0
        std::stringstream ss;
1432
0
        nsFrame::PrintDisplayItem(aDisplayListBuilder, item, ss, static_cast<uint32_t>(mDumpIndent));
1433
0
        printf_stderr("%s", ss.str().c_str());
1434
0
      }
1435
0
1436
0
      // Note: this call to CreateWebRenderCommands can recurse back into
1437
0
      // this function if the |item| is a wrapper for a sublist.
1438
0
      item->SetPaintRect(item->GetBuildingRect());
1439
0
      bool createdWRCommands =
1440
0
        item->CreateWebRenderCommands(aBuilder, aResources, aSc, mManager,
1441
0
                                      aDisplayListBuilder);
1442
0
      if (!createdWRCommands) {
1443
0
        PushItemAsImage(item, aBuilder, aResources, aSc, aDisplayListBuilder);
1444
0
      }
1445
0
1446
0
      if (dumpEnabled) {
1447
0
        mBuilderDumpIndex = aBuilder.Dump(mDumpIndent + 1, Some(mBuilderDumpIndex), Nothing());
1448
0
      }
1449
0
    }
1450
0
1451
0
    if (apzEnabled) {
1452
0
      if (forceNewLayerData) {
1453
0
        // Pop the thing we pushed before the recursion, so the topmost item on
1454
0
        // the stack is enclosing display item's ASR (or the stack is empty)
1455
0
        mAsrStack.pop_back();
1456
0
        const ActiveScrolledRoot* stopAtAsr =
1457
0
            mAsrStack.empty() ? nullptr : mAsrStack.back();
1458
0
1459
0
        int32_t descendants = mLayerScrollData.size() - layerCountBeforeRecursing;
1460
0
1461
0
        // A deferred transform item is a nsDisplayTransform for which we did
1462
0
        // not create a dedicated WebRenderLayerScrollData item at the point
1463
0
        // that we encountered the item. Instead, we "deferred" the transform
1464
0
        // from that item to combine it into the WebRenderLayerScrollData produced
1465
0
        // by child display items. However, in the case where we have a child
1466
0
        // display item with a different ASR than the nsDisplayTransform item,
1467
0
        // we cannot do this, because it will not conform to APZ's expectations
1468
0
        // with respect to how the APZ tree ends up structured. In particular,
1469
0
        // the GetTransformToThis() for the child APZ (which is created for the
1470
0
        // child item's ASR) will not include the transform when we actually do
1471
0
        // want it to.
1472
0
        // When we run into this scenario, we solve it by creating two
1473
0
        // WebRenderLayerScrollData items; one that just holds the transform,
1474
0
        // that we deferred, and a child WebRenderLayerScrollData item that
1475
0
        // holds the scroll metadata for the child's ASR.
1476
0
        Maybe<nsDisplayTransform*> deferred = aSc.GetDeferredTransformItem();
1477
0
        if (deferred && (*deferred)->GetActiveScrolledRoot() != item->GetActiveScrolledRoot()) {
1478
0
          // This creates the child WebRenderLayerScrollData for |item|, but
1479
0
          // omits the transform (hence the Nothing() as the last argument to
1480
0
          // Initialize(...)). We also need to make sure that the ASR from
1481
0
          // the deferred transform item is not on this node, so we use that
1482
0
          // ASR as the "stop at" ASR for this WebRenderLayerScrollData.
1483
0
          mLayerScrollData.emplace_back();
1484
0
          mLayerScrollData.back().Initialize(mManager->GetScrollData(), item,
1485
0
              descendants, (*deferred)->GetActiveScrolledRoot(), Nothing());
1486
0
1487
0
          // The above WebRenderLayerScrollData will also be a descendant of
1488
0
          // the transform-holding WebRenderLayerScrollData we create below.
1489
0
          descendants++;
1490
0
1491
0
          // This creates the WebRenderLayerScrollData for the deferred transform
1492
0
          // item. This holds the transform matrix. and the remaining ASRs
1493
0
          // needed to complete the ASR chain (i.e. the ones from the stopAtAsr
1494
0
          // down to the deferred transform item's ASR, which must be "between"
1495
0
          // stopAtAsr and |item|'s ASR in the ASR tree.
1496
0
          mLayerScrollData.emplace_back();
1497
0
          mLayerScrollData.back().Initialize(mManager->GetScrollData(), *deferred,
1498
0
              descendants, stopAtAsr, Some((*deferred)->GetTransform().GetMatrix()));
1499
0
        } else {
1500
0
          // This is the "simple" case where we don't need to create two
1501
0
          // WebRenderLayerScrollData items; we can just create one that also
1502
0
          // holds the deferred transform matrix, if any.
1503
0
          mLayerScrollData.emplace_back();
1504
0
          mLayerScrollData.back().Initialize(mManager->GetScrollData(), item,
1505
0
              descendants, stopAtAsr, deferred ? Some((*deferred)->GetTransform().GetMatrix()) : Nothing());
1506
0
        }
1507
0
      }
1508
0
    }
1509
0
  }
1510
0
1511
0
  mDumpIndent--;
1512
0
  mClipManager.EndList(aSc);
1513
0
}
1514
1515
void
1516
WebRenderCommandBuilder::PushOverrideForASR(const ActiveScrolledRoot* aASR,
1517
                                            const Maybe<wr::WrClipId>& aClipId)
1518
0
{
1519
0
  mClipManager.PushOverrideForASR(aASR, aClipId);
1520
0
}
1521
1522
void
1523
WebRenderCommandBuilder::PopOverrideForASR(const ActiveScrolledRoot* aASR)
1524
0
{
1525
0
  mClipManager.PopOverrideForASR(aASR);
1526
0
}
1527
1528
Maybe<wr::ImageKey>
1529
WebRenderCommandBuilder::CreateImageKey(nsDisplayItem* aItem,
1530
                                        ImageContainer* aContainer,
1531
                                        mozilla::wr::DisplayListBuilder& aBuilder,
1532
                                        mozilla::wr::IpcResourceUpdateQueue& aResources,
1533
                                        mozilla::wr::ImageRendering aRendering,
1534
                                        const StackingContextHelper& aSc,
1535
                                        gfx::IntSize& aSize,
1536
                                        const Maybe<LayoutDeviceRect>& aAsyncImageBounds)
1537
0
{
1538
0
  RefPtr<WebRenderImageData> imageData = CreateOrRecycleWebRenderUserData<WebRenderImageData>(aItem);
1539
0
  MOZ_ASSERT(imageData);
1540
0
1541
0
  if (aContainer->IsAsync()) {
1542
0
    MOZ_ASSERT(aAsyncImageBounds);
1543
0
1544
0
    LayoutDeviceRect rect = aAsyncImageBounds.value();
1545
0
    LayoutDeviceRect scBounds(LayoutDevicePoint(0, 0), rect.Size());
1546
0
    gfx::MaybeIntSize scaleToSize;
1547
0
    if (!aContainer->GetScaleHint().IsEmpty()) {
1548
0
      scaleToSize = Some(aContainer->GetScaleHint());
1549
0
    }
1550
0
    gfx::Matrix4x4 transform = gfx::Matrix4x4::From2D(aContainer->GetTransformHint());
1551
0
    // TODO!
1552
0
    // We appear to be using the image bridge for a lot (most/all?) of
1553
0
    // layers-free image handling and that breaks frame consistency.
1554
0
    imageData->CreateAsyncImageWebRenderCommands(aBuilder,
1555
0
                                                 aContainer,
1556
0
                                                 aSc,
1557
0
                                                 rect,
1558
0
                                                 scBounds,
1559
0
                                                 transform,
1560
0
                                                 scaleToSize,
1561
0
                                                 aRendering,
1562
0
                                                 wr::MixBlendMode::Normal,
1563
0
                                                 !aItem->BackfaceIsHidden());
1564
0
    return Nothing();
1565
0
  }
1566
0
1567
0
  AutoLockImage autoLock(aContainer);
1568
0
  if (!autoLock.HasImage()) {
1569
0
    return Nothing();
1570
0
  }
1571
0
  mozilla::layers::Image* image = autoLock.GetImage();
1572
0
  aSize = image->GetSize();
1573
0
1574
0
  return imageData->UpdateImageKey(aContainer, aResources);
1575
0
}
1576
1577
bool
1578
WebRenderCommandBuilder::PushImage(nsDisplayItem* aItem,
1579
                                   ImageContainer* aContainer,
1580
                                   mozilla::wr::DisplayListBuilder& aBuilder,
1581
                                   mozilla::wr::IpcResourceUpdateQueue& aResources,
1582
                                   const StackingContextHelper& aSc,
1583
                                   const LayoutDeviceRect& aRect)
1584
0
{
1585
0
  mozilla::wr::ImageRendering rendering = wr::ToImageRendering(
1586
0
    nsLayoutUtils::GetSamplingFilterForFrame(aItem->Frame()));
1587
0
  gfx::IntSize size;
1588
0
  Maybe<wr::ImageKey> key = CreateImageKey(
1589
0
    aItem, aContainer, aBuilder, aResources, rendering, aSc, size, Some(aRect));
1590
0
  if (aContainer->IsAsync()) {
1591
0
    // Async ImageContainer does not create ImageKey, instead it uses Pipeline.
1592
0
    MOZ_ASSERT(key.isNothing());
1593
0
    return true;
1594
0
  }
1595
0
  if (!key) {
1596
0
    return false;
1597
0
  }
1598
0
1599
0
  auto r = wr::ToRoundedLayoutRect(aRect);
1600
0
  aBuilder.PushImage(r, r, !aItem->BackfaceIsHidden(), rendering, key.value());
1601
0
1602
0
  return true;
1603
0
}
1604
1605
bool
1606
BuildLayer(nsDisplayItem* aItem,
1607
           BlobItemData* aData,
1608
           nsDisplayListBuilder* aDisplayListBuilder,
1609
           const gfx::Size& aScale)
1610
0
{
1611
0
  if (!aData->mLayerManager) {
1612
0
    aData->mLayerManager = new BasicLayerManager(BasicLayerManager::BLM_INACTIVE);
1613
0
  }
1614
0
  RefPtr<BasicLayerManager> blm = aData->mLayerManager;
1615
0
  UniquePtr<LayerProperties> props;
1616
0
  if (blm->GetRoot()) {
1617
0
    props = LayerProperties::CloneFrom(blm->GetRoot());
1618
0
  }
1619
0
  FrameLayerBuilder* layerBuilder = new FrameLayerBuilder();
1620
0
  layerBuilder->Init(aDisplayListBuilder, blm, nullptr, true);
1621
0
  layerBuilder->DidBeginRetainedLayerTransaction(blm);
1622
0
1623
0
  blm->BeginTransaction();
1624
0
  bool isInvalidated = false;
1625
0
1626
0
  ContainerLayerParameters param(aScale.width, aScale.height);
1627
0
  RefPtr<Layer> root = aItem->BuildLayer(aDisplayListBuilder, blm, param);
1628
0
1629
0
  if (root) {
1630
0
    blm->SetRoot(root);
1631
0
    layerBuilder->WillEndTransaction();
1632
0
1633
0
    // Check if there is any invalidation region.
1634
0
    nsIntRegion invalid;
1635
0
    if (props) {
1636
0
      props->ComputeDifferences(root, invalid, nullptr);
1637
0
      if (!invalid.IsEmpty()) {
1638
0
        isInvalidated = true;
1639
0
      }
1640
0
    } else {
1641
0
      isInvalidated = true;
1642
0
    }
1643
0
  }
1644
0
  blm->AbortTransaction();
1645
0
1646
0
  return isInvalidated;
1647
0
}
1648
1649
static bool
1650
PaintByLayer(nsDisplayItem* aItem,
1651
             nsDisplayListBuilder* aDisplayListBuilder,
1652
             const RefPtr<BasicLayerManager>& aManager,
1653
             gfxContext* aContext,
1654
             const gfx::Size& aScale,
1655
             const std::function<void()>& aPaintFunc)
1656
0
{
1657
0
  UniquePtr<LayerProperties> props;
1658
0
  if (aManager->GetRoot()) {
1659
0
    props = LayerProperties::CloneFrom(aManager->GetRoot());
1660
0
  }
1661
0
  FrameLayerBuilder* layerBuilder = new FrameLayerBuilder();
1662
0
  layerBuilder->Init(aDisplayListBuilder, aManager, nullptr, true);
1663
0
  layerBuilder->DidBeginRetainedLayerTransaction(aManager);
1664
0
1665
0
  aManager->SetDefaultTarget(aContext);
1666
0
  aManager->BeginTransactionWithTarget(aContext);
1667
0
  bool isInvalidated = false;
1668
0
1669
0
  ContainerLayerParameters param(aScale.width, aScale.height);
1670
0
  RefPtr<Layer> root = aItem->BuildLayer(aDisplayListBuilder, aManager, param);
1671
0
1672
0
  if (root) {
1673
0
    aManager->SetRoot(root);
1674
0
    layerBuilder->WillEndTransaction();
1675
0
1676
0
    aPaintFunc();
1677
0
1678
0
    // Check if there is any invalidation region.
1679
0
    nsIntRegion invalid;
1680
0
    if (props) {
1681
0
      props->ComputeDifferences(root, invalid, nullptr);
1682
0
      if (!invalid.IsEmpty()) {
1683
0
        isInvalidated = true;
1684
0
      }
1685
0
    } else {
1686
0
      isInvalidated = true;
1687
0
    }
1688
0
  }
1689
0
1690
#ifdef MOZ_DUMP_PAINTING
1691
  if (gfxUtils::DumpDisplayList() || gfxEnv::DumpPaint()) {
1692
    fprintf_stderr(gfxUtils::sDumpPaintFile, "Basic layer tree for painting contents of display item %s(%p):\n", aItem->Name(), aItem->Frame());
1693
    std::stringstream stream;
1694
    aManager->Dump(stream, "", gfxEnv::DumpPaintToFile());
1695
    fprint_stderr(gfxUtils::sDumpPaintFile, stream);  // not a typo, fprint_stderr declared in LayersLogging.h
1696
  }
1697
#endif
1698
1699
0
  if (aManager->InTransaction()) {
1700
0
    aManager->AbortTransaction();
1701
0
  }
1702
0
1703
0
  aManager->SetTarget(nullptr);
1704
0
  aManager->SetDefaultTarget(nullptr);
1705
0
1706
0
  return isInvalidated;
1707
0
}
1708
1709
static bool
1710
PaintItemByDrawTarget(nsDisplayItem* aItem,
1711
                      gfx::DrawTarget* aDT,
1712
                      const LayerRect& aImageRect,
1713
                      const LayoutDevicePoint& aOffset,
1714
                      nsDisplayListBuilder* aDisplayListBuilder,
1715
                      const RefPtr<BasicLayerManager>& aManager,
1716
                      const gfx::Size& aScale,
1717
                      Maybe<gfx::Color>& aHighlight)
1718
0
{
1719
0
  MOZ_ASSERT(aDT);
1720
0
1721
0
  bool isInvalidated = false;
1722
0
  aDT->ClearRect(aImageRect.ToUnknownRect());
1723
0
  RefPtr<gfxContext> context = gfxContext::CreateOrNull(aDT);
1724
0
  MOZ_ASSERT(context);
1725
0
1726
0
  switch (aItem->GetType()) {
1727
0
  case DisplayItemType::TYPE_MASK:
1728
0
    context->SetMatrix(context->CurrentMatrix().PreScale(aScale.width, aScale.height).PreTranslate(-aOffset.x, -aOffset.y));
1729
0
    static_cast<nsDisplayMask*>(aItem)->PaintMask(aDisplayListBuilder, context, &isInvalidated);
1730
0
    break;
1731
0
  case DisplayItemType::TYPE_SVG_WRAPPER:
1732
0
    {
1733
0
      context->SetMatrix(context->CurrentMatrix().PreTranslate(-aOffset.x, -aOffset.y));
1734
0
      isInvalidated = PaintByLayer(aItem, aDisplayListBuilder, aManager, context, aScale, [&]() {
1735
0
        aManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer, aDisplayListBuilder);
1736
0
      });
1737
0
      break;
1738
0
    }
1739
0
1740
0
  case DisplayItemType::TYPE_FILTER:
1741
0
    {
1742
0
      context->SetMatrix(context->CurrentMatrix().PreTranslate(-aOffset.x, -aOffset.y));
1743
0
      isInvalidated = PaintByLayer(aItem, aDisplayListBuilder, aManager, context, aScale, [&]() {
1744
0
        static_cast<nsDisplayFilter*>(aItem)->PaintAsLayer(aDisplayListBuilder,
1745
0
                                                           context, aManager);
1746
0
      });
1747
0
      break;
1748
0
    }
1749
0
1750
0
  default:
1751
0
    context->SetMatrix(context->CurrentMatrix().PreScale(aScale.width, aScale.height).PreTranslate(-aOffset.x, -aOffset.y));
1752
0
    if (aDisplayListBuilder->IsPaintingToWindow()) {
1753
0
      aItem->Frame()->AddStateBits(NS_FRAME_PAINTED_THEBES);
1754
0
    }
1755
0
    aItem->Paint(aDisplayListBuilder, context);
1756
0
    isInvalidated = true;
1757
0
    break;
1758
0
  }
1759
0
1760
0
  if (aItem->GetType() != DisplayItemType::TYPE_MASK) {
1761
0
    // Apply highlight fills, if the appropriate prefs are set.
1762
0
    // We don't do this for masks because we'd be filling the A8 mask surface,
1763
0
    // which isn't very useful.
1764
0
    if (aHighlight) {
1765
0
      aDT->SetTransform(gfx::Matrix());
1766
0
      aDT->FillRect(gfx::Rect(0, 0, aImageRect.Width(), aImageRect.Height()), gfx::ColorPattern(aHighlight.value()));
1767
0
    }
1768
0
    if (aItem->Frame()->PresContext()->GetPaintFlashing() && isInvalidated) {
1769
0
      aDT->SetTransform(gfx::Matrix());
1770
0
      float r = float(rand()) / RAND_MAX;
1771
0
      float g = float(rand()) / RAND_MAX;
1772
0
      float b = float(rand()) / RAND_MAX;
1773
0
      aDT->FillRect(gfx::Rect(0, 0, aImageRect.Width(), aImageRect.Height()), gfx::ColorPattern(gfx::Color(r, g, b, 0.5)));
1774
0
    }
1775
0
  }
1776
0
1777
0
  return isInvalidated;
1778
0
}
1779
1780
already_AddRefed<WebRenderFallbackData>
1781
WebRenderCommandBuilder::GenerateFallbackData(nsDisplayItem* aItem,
1782
                                              wr::DisplayListBuilder& aBuilder,
1783
                                              wr::IpcResourceUpdateQueue& aResources,
1784
                                              const StackingContextHelper& aSc,
1785
                                              nsDisplayListBuilder* aDisplayListBuilder,
1786
                                              LayoutDeviceRect& aImageRect)
1787
0
{
1788
0
  bool useBlobImage = gfxPrefs::WebRenderBlobImages() && !aItem->MustPaintOnContentSide();
1789
0
  Maybe<gfx::Color> highlight = Nothing();
1790
0
  if (gfxPrefs::WebRenderHighlightPaintedLayers()) {
1791
0
    highlight = Some(useBlobImage ? gfx::Color(1.0, 0.0, 0.0, 0.5)
1792
0
                                  : gfx::Color(1.0, 1.0, 0.0, 0.5));
1793
0
  }
1794
0
1795
0
  RefPtr<WebRenderFallbackData> fallbackData = CreateOrRecycleWebRenderUserData<WebRenderFallbackData>(aItem);
1796
0
1797
0
  bool snap;
1798
0
  nsRect itemBounds = aItem->GetBounds(aDisplayListBuilder, &snap);
1799
0
1800
0
  // Blob images will only draw the visible area of the blob so we don't need to clip
1801
0
  // them here and can just rely on the webrender clipping.
1802
0
  // TODO We also don't clip native themed widget to avoid over-invalidation during scrolling.
1803
0
  // it would be better to support a sort of straming/tiling scheme for large ones but the hope
1804
0
  // is that we should not have large native themed items.
1805
0
  nsRect paintBounds = itemBounds;
1806
0
  if (useBlobImage || aItem->MustPaintOnContentSide()) {
1807
0
    paintBounds = itemBounds;
1808
0
  } else {
1809
0
    paintBounds = aItem->GetClippedBounds(aDisplayListBuilder);
1810
0
  }
1811
0
1812
0
  // nsDisplayItem::Paint() may refer the variables that come from ComputeVisibility().
1813
0
  // So we should call ComputeVisibility() before painting. e.g.: nsDisplayBoxShadowInner
1814
0
  // uses mPaintRect in Paint() and mPaintRect is computed in
1815
0
  // nsDisplayBoxShadowInner::ComputeVisibility().
1816
0
  nsRegion visibleRegion(paintBounds);
1817
0
  aItem->SetPaintRect(paintBounds);
1818
0
  aItem->ComputeVisibility(aDisplayListBuilder, &visibleRegion);
1819
0
1820
0
  const int32_t appUnitsPerDevPixel = aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
1821
0
  LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(paintBounds, appUnitsPerDevPixel);
1822
0
1823
0
  gfx::Size scale = aSc.GetInheritedScale();
1824
0
  gfx::Size oldScale = fallbackData->GetScale();
1825
0
  // This scale determination should probably be done using
1826
0
  // ChooseScaleAndSetTransform but for now we just fake it.
1827
0
  // We tolerate slight changes in scale so that we don't, for example,
1828
0
  // rerasterize on MotionMark
1829
0
  bool differentScale = gfx::FuzzyEqual(scale.width, oldScale.width, 1e-6f) &&
1830
0
                        gfx::FuzzyEqual(scale.height, oldScale.height, 1e-6f);
1831
0
1832
0
  // XXX not sure if paintSize should be in layer or layoutdevice pixels, it
1833
0
  // has some sort of scaling applied.
1834
0
  LayerIntSize paintSize = RoundedToInt(LayerSize(bounds.Width() * scale.width, bounds.Height() * scale.height));
1835
0
  if (paintSize.width == 0 || paintSize.height == 0) {
1836
0
    return nullptr;
1837
0
  }
1838
0
1839
0
  // Some display item may draw exceed the paintSize, we need prepare a larger
1840
0
  // draw target to contain the result.
1841
0
  auto scaledBounds = bounds * LayoutDeviceToLayerScale(1);
1842
0
  scaledBounds.Scale(scale.width, scale.height);
1843
0
  LayerIntSize dtSize = RoundedToInt(scaledBounds).Size();
1844
0
1845
0
  // TODO Rounding a rect to integers and then taking the size gives a different behavior than
1846
0
  // just rounding the size of the rect to integers. This can cause a crash, but fixing the
1847
0
  // difference causes some test failures so this is a quick fix
1848
0
  if (dtSize.width <= 0 || dtSize.height <= 0) {
1849
0
    return nullptr;
1850
0
  }
1851
0
1852
0
  bool needPaint = true;
1853
0
  LayoutDeviceIntPoint offset = RoundedToInt(bounds.TopLeft());
1854
0
  aImageRect = LayoutDeviceRect(offset, LayoutDeviceSize(RoundedToInt(bounds).Size()));
1855
0
  LayerRect paintRect = LayerRect(LayerPoint(0, 0), LayerSize(paintSize));
1856
0
  nsDisplayItemGeometry* geometry = fallbackData->GetGeometry();
1857
0
1858
0
  // nsDisplayFilter is rendered via BasicLayerManager which means the invalidate
1859
0
  // region is unknown until we traverse the displaylist contained by it.
1860
0
  if (geometry && !fallbackData->IsInvalid() &&
1861
0
      aItem->GetType() != DisplayItemType::TYPE_FILTER &&
1862
0
      aItem->GetType() != DisplayItemType::TYPE_SVG_WRAPPER &&
1863
0
      differentScale) {
1864
0
    nsRect invalid;
1865
0
    nsRegion invalidRegion;
1866
0
1867
0
    if (aItem->IsInvalid(invalid)) {
1868
0
      invalidRegion.OrWith(paintBounds);
1869
0
    } else {
1870
0
      nsPoint shift = itemBounds.TopLeft() - geometry->mBounds.TopLeft();
1871
0
      geometry->MoveBy(shift);
1872
0
      aItem->ComputeInvalidationRegion(aDisplayListBuilder, geometry, &invalidRegion);
1873
0
1874
0
      nsRect lastBounds = fallbackData->GetBounds();
1875
0
      lastBounds.MoveBy(shift);
1876
0
1877
0
      if (!lastBounds.IsEqualInterior(paintBounds)) {
1878
0
        invalidRegion.OrWith(lastBounds);
1879
0
        invalidRegion.OrWith(paintBounds);
1880
0
      }
1881
0
    }
1882
0
    needPaint = !invalidRegion.IsEmpty();
1883
0
  }
1884
0
1885
0
  if (needPaint || !fallbackData->GetKey()) {
1886
0
    nsAutoPtr<nsDisplayItemGeometry> newGeometry;
1887
0
    newGeometry = aItem->AllocateGeometry(aDisplayListBuilder);
1888
0
    fallbackData->SetGeometry(std::move(newGeometry));
1889
0
1890
0
    gfx::SurfaceFormat format = aItem->GetType() == DisplayItemType::TYPE_MASK ?
1891
0
                                                      gfx::SurfaceFormat::A8 : gfx::SurfaceFormat::B8G8R8A8;
1892
0
    if (useBlobImage) {
1893
0
      bool snapped;
1894
0
      bool isOpaque = aItem->GetOpaqueRegion(aDisplayListBuilder, &snapped).Contains(paintBounds);
1895
0
      std::vector<RefPtr<ScaledFont>> fonts;
1896
0
1897
0
      RefPtr<WebRenderDrawEventRecorder> recorder =
1898
0
        MakeAndAddRef<WebRenderDrawEventRecorder>([&] (MemStream &aStream, std::vector<RefPtr<ScaledFont>> &aScaledFonts) {
1899
0
          size_t count = aScaledFonts.size();
1900
0
          aStream.write((const char*)&count, sizeof(count));
1901
0
          for (auto& scaled : aScaledFonts) {
1902
0
            BlobFont font = {
1903
0
              mManager->WrBridge()->GetFontKeyForScaledFont(scaled, &aResources),
1904
0
              scaled
1905
0
            };
1906
0
            aStream.write((const char*)&font, sizeof(font));
1907
0
          }
1908
0
          fonts = std::move(aScaledFonts);
1909
0
        });
1910
0
      RefPtr<gfx::DrawTarget> dummyDt =
1911
0
        gfx::Factory::CreateDrawTarget(gfx::BackendType::SKIA, gfx::IntSize(1, 1), format);
1912
0
      RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateRecordingDrawTarget(recorder, dummyDt, dtSize.ToUnknownSize());
1913
0
      if (!fallbackData->mBasicLayerManager) {
1914
0
        fallbackData->mBasicLayerManager = new BasicLayerManager(BasicLayerManager::BLM_INACTIVE);
1915
0
      }
1916
0
      bool isInvalidated = PaintItemByDrawTarget(aItem, dt, paintRect, offset, aDisplayListBuilder,
1917
0
                                                 fallbackData->mBasicLayerManager, scale, highlight);
1918
0
      recorder->FlushItem(IntRect(0, 0, paintSize.width, paintSize.height));
1919
0
      TakeExternalSurfaces(recorder, fallbackData->mExternalSurfaces, mManager, aResources);
1920
0
      recorder->Finish();
1921
0
1922
0
      if (isInvalidated) {
1923
0
        Range<uint8_t> bytes((uint8_t *)recorder->mOutputStream.mData, recorder->mOutputStream.mLength);
1924
0
        wr::ImageKey key = mManager->WrBridge()->GetNextImageKey();
1925
0
        wr::ImageDescriptor descriptor(dtSize.ToUnknownSize(), 0, dt->GetFormat(), isOpaque);
1926
0
        if (!aResources.AddBlobImage(key, descriptor, bytes)) {
1927
0
          return nullptr;
1928
0
        }
1929
0
        fallbackData->SetKey(key);
1930
0
        fallbackData->SetFonts(fonts);
1931
0
      } else {
1932
0
        // If there is no invalidation region and we don't have a image key,
1933
0
        // it means we don't need to push image for the item.
1934
0
        if (!fallbackData->GetKey().isSome()) {
1935
0
          return nullptr;
1936
0
        }
1937
0
      }
1938
0
    } else {
1939
0
      fallbackData->CreateImageClientIfNeeded();
1940
0
      RefPtr<ImageClient> imageClient = fallbackData->GetImageClient();
1941
0
      RefPtr<ImageContainer> imageContainer = LayerManager::CreateImageContainer();
1942
0
      bool isInvalidated = false;
1943
0
1944
0
      {
1945
0
        UpdateImageHelper helper(imageContainer, imageClient, dtSize.ToUnknownSize(), format);
1946
0
        {
1947
0
          RefPtr<gfx::DrawTarget> dt = helper.GetDrawTarget();
1948
0
          if (!dt) {
1949
0
            return nullptr;
1950
0
          }
1951
0
          if (!fallbackData->mBasicLayerManager) {
1952
0
            fallbackData->mBasicLayerManager = new BasicLayerManager(mManager->GetWidget());
1953
0
          }
1954
0
          isInvalidated = PaintItemByDrawTarget(aItem, dt, paintRect, offset,
1955
0
                                                aDisplayListBuilder,
1956
0
                                                fallbackData->mBasicLayerManager, scale,
1957
0
                                                highlight);
1958
0
        }
1959
0
1960
0
        if (isInvalidated) {
1961
0
          // Update image if there it's invalidated.
1962
0
          if (!helper.UpdateImage()) {
1963
0
            return nullptr;
1964
0
          }
1965
0
        } else {
1966
0
          // If there is no invalidation region and we don't have a image key,
1967
0
          // it means we don't need to push image for the item.
1968
0
          if (!fallbackData->GetKey().isSome()) {
1969
0
            return nullptr;
1970
0
          }
1971
0
        }
1972
0
      }
1973
0
1974
0
      // Force update the key in fallback data since we repaint the image in this path.
1975
0
      // If not force update, fallbackData may reuse the original key because it
1976
0
      // doesn't know UpdateImageHelper already updated the image container.
1977
0
      if (isInvalidated && !fallbackData->UpdateImageKey(imageContainer, aResources, true)) {
1978
0
        return nullptr;
1979
0
      }
1980
0
    }
1981
0
1982
0
    fallbackData->SetScale(scale);
1983
0
    fallbackData->SetInvalid(false);
1984
0
  }
1985
0
1986
0
  // Update current bounds to fallback data
1987
0
  fallbackData->SetBounds(paintBounds);
1988
0
1989
0
  MOZ_ASSERT(fallbackData->GetKey());
1990
0
1991
0
  return fallbackData.forget();
1992
0
}
1993
1994
Maybe<wr::WrImageMask>
1995
WebRenderCommandBuilder::BuildWrMaskImage(nsDisplayItem* aItem,
1996
                                          wr::DisplayListBuilder& aBuilder,
1997
                                          wr::IpcResourceUpdateQueue& aResources,
1998
                                          const StackingContextHelper& aSc,
1999
                                          nsDisplayListBuilder* aDisplayListBuilder,
2000
                                          const LayoutDeviceRect& aBounds)
2001
0
{
2002
0
  LayoutDeviceRect imageRect;
2003
0
  RefPtr<WebRenderFallbackData> fallbackData = GenerateFallbackData(aItem, aBuilder, aResources,
2004
0
                                                                    aSc, aDisplayListBuilder,
2005
0
                                                                    imageRect);
2006
0
  if (!fallbackData) {
2007
0
    return Nothing();
2008
0
  }
2009
0
2010
0
  wr::WrImageMask imageMask;
2011
0
  imageMask.image = fallbackData->GetKey().value();
2012
0
  imageMask.rect = wr::ToRoundedLayoutRect(aBounds);
2013
0
  imageMask.repeat = false;
2014
0
  return Some(imageMask);
2015
0
}
2016
2017
bool
2018
WebRenderCommandBuilder::PushItemAsImage(nsDisplayItem* aItem,
2019
                                         wr::DisplayListBuilder& aBuilder,
2020
                                         wr::IpcResourceUpdateQueue& aResources,
2021
                                         const StackingContextHelper& aSc,
2022
                                         nsDisplayListBuilder* aDisplayListBuilder)
2023
0
{
2024
0
  LayoutDeviceRect imageRect;
2025
0
  RefPtr<WebRenderFallbackData> fallbackData = GenerateFallbackData(aItem, aBuilder, aResources,
2026
0
                                                                    aSc, aDisplayListBuilder,
2027
0
                                                                    imageRect);
2028
0
  if (!fallbackData) {
2029
0
    return false;
2030
0
  }
2031
0
2032
0
  wr::LayoutRect dest = wr::ToRoundedLayoutRect(imageRect);
2033
0
  gfx::SamplingFilter sampleFilter = nsLayoutUtils::GetSamplingFilterForFrame(aItem->Frame());
2034
0
  aBuilder.PushImage(dest,
2035
0
                     dest,
2036
0
                     !aItem->BackfaceIsHidden(),
2037
0
                     wr::ToImageRendering(sampleFilter),
2038
0
                     fallbackData->GetKey().value());
2039
0
  return true;
2040
0
}
2041
2042
void
2043
WebRenderCommandBuilder::RemoveUnusedAndResetWebRenderUserData()
2044
0
{
2045
0
  for (auto iter = mWebRenderUserDatas.Iter(); !iter.Done(); iter.Next()) {
2046
0
    WebRenderUserData* data = iter.Get()->GetKey();
2047
0
    if (!data->IsUsed()) {
2048
0
      nsIFrame* frame = data->GetFrame();
2049
0
2050
0
      MOZ_ASSERT(frame->HasProperty(WebRenderUserDataProperty::Key()));
2051
0
2052
0
      WebRenderUserDataTable* userDataTable =
2053
0
        frame->GetProperty(WebRenderUserDataProperty::Key());
2054
0
2055
0
      MOZ_ASSERT(userDataTable->Count());
2056
0
2057
0
      userDataTable->Remove(WebRenderUserDataKey(data->GetDisplayItemKey(), data->GetType()));
2058
0
2059
0
      if (!userDataTable->Count()) {
2060
0
        frame->RemoveProperty(WebRenderUserDataProperty::Key());
2061
0
        delete userDataTable;
2062
0
      }
2063
0
2064
0
      if (data->GetType() == WebRenderUserData::UserDataType::eCanvas) {
2065
0
        mLastCanvasDatas.RemoveEntry(data->AsCanvasData());
2066
0
      }
2067
0
2068
0
      iter.Remove();
2069
0
      continue;
2070
0
    }
2071
0
2072
0
    data->SetUsed(false);
2073
0
  }
2074
0
}
2075
2076
void
2077
WebRenderCommandBuilder::ClearCachedResources()
2078
0
{
2079
0
  RemoveUnusedAndResetWebRenderUserData();
2080
0
  // UserDatas should only be in the used state during a call to WebRenderCommandBuilder::BuildWebRenderCommands
2081
0
  // The should always be false upon return from BuildWebRenderCommands().
2082
0
  MOZ_RELEASE_ASSERT(mWebRenderUserDatas.Count() == 0);
2083
0
}
2084
2085
2086
2087
WebRenderGroupData::WebRenderGroupData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem)
2088
  : WebRenderUserData(aWRManager, aItem)
2089
0
{
2090
0
  MOZ_COUNT_CTOR(WebRenderGroupData);
2091
0
}
2092
2093
WebRenderGroupData::~WebRenderGroupData()
2094
0
{
2095
0
  MOZ_COUNT_DTOR(WebRenderGroupData);
2096
0
  GP("Group data destruct\n");
2097
0
  mSubGroup.ClearImageKey(mWRManager, true);
2098
0
  mFollowingGroup.ClearImageKey(mWRManager, true);
2099
0
}
2100
2101
} // namespace layers
2102
} // namespace mozilla