WebRenderCommandBuilder.cpp (120677B)
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 "mozilla/AutoRestore.h" 10 #include "mozilla/DebugOnly.h" 11 #include "mozilla/EffectCompositor.h" 12 #include "mozilla/ProfilerLabels.h" 13 #include "mozilla/StaticPrefs_gfx.h" 14 #include "mozilla/SVGGeometryFrame.h" 15 #include "mozilla/SVGImageFrame.h" 16 #include "mozilla/UniquePtr.h" 17 #include "mozilla/gfx/2D.h" 18 #include "mozilla/gfx/Logging.h" 19 #include "mozilla/gfx/Types.h" 20 #include "mozilla/image/WebRenderImageProvider.h" 21 #include "mozilla/layers/AnimationHelper.h" 22 #include "mozilla/layers/ClipManager.h" 23 #include "mozilla/layers/ImageClient.h" 24 #include "mozilla/layers/RenderRootStateManager.h" 25 #include "mozilla/layers/WebRenderBridgeChild.h" 26 #include "mozilla/layers/WebRenderLayerManager.h" 27 #include "mozilla/layers/IpcResourceUpdateQueue.h" 28 #include "mozilla/layers/SharedSurfacesChild.h" 29 #include "mozilla/layers/SourceSurfaceSharedData.h" 30 #include "mozilla/layers/StackingContextHelper.h" 31 #include "mozilla/layers/WebRenderDrawEventRecorder.h" 32 #include "UnitTransforms.h" 33 #include "gfxEnv.h" 34 #include "MediaInfo.h" 35 #include "nsDisplayListInvalidation.h" 36 #include "nsLayoutUtils.h" 37 #include "nsTHashSet.h" 38 #include "WebRenderCanvasRenderer.h" 39 40 #include <cstdint> 41 42 namespace mozilla::layers { 43 44 using namespace gfx; 45 using namespace image; 46 static int sIndent; 47 #include <stdarg.h> 48 #include <stdio.h> 49 50 static void GP(const char* fmt, ...) { 51 va_list args; 52 va_start(args, fmt); 53 #if 0 54 for (int i = 0; i < sIndent; i++) { printf(" "); } 55 vprintf(fmt, args); 56 #endif 57 va_end(args); 58 } 59 60 bool FitsInt32(const float aVal) { 61 // Although int32_t min and max can't be represented exactly with floats, the 62 // cast truncates towards zero which is what we want here. 63 const float min = static_cast<float>(std::numeric_limits<int32_t>::min()); 64 const float max = static_cast<float>(std::numeric_limits<int32_t>::max()); 65 return aVal > min && aVal < max; 66 } 67 68 // XXX: problems: 69 // - How do we deal with scrolling while having only a single invalidation rect? 70 // We can have a valid rect and an invalid rect. As we scroll the valid rect 71 // will move and the invalid rect will be the new area 72 73 struct BlobItemData; 74 static void DestroyBlobGroupDataProperty(nsTArray<BlobItemData*>* aArray); 75 NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(BlobGroupDataProperty, 76 nsTArray<BlobItemData*>, 77 DestroyBlobGroupDataProperty); 78 79 // These are currently manually allocated and ownership is help by the 80 // mDisplayItems hash table in DIGroup 81 struct BlobItemData { 82 // a weak pointer to the frame for this item. 83 // DisplayItemData has a mFrameList to deal with merged frames. Hopefully we 84 // don't need to worry about that. 85 nsIFrame* mFrame; 86 87 uint32_t mDisplayItemKey; 88 nsTArray<BlobItemData*>* 89 mArray; // a weak pointer to the array that's owned by the frame property 90 91 LayerIntRect mRect; 92 // It would be nice to not need this. We need to be able to call 93 // ComputeInvalidationRegion. ComputeInvalidationRegion will sometimes reach 94 // into parent style structs to get information that can change the 95 // invalidation region 96 UniquePtr<nsDisplayItemGeometry> mGeometry; 97 DisplayItemClip mClip; 98 bool mInvisible; 99 bool mUsed; // initialized near construction 100 // XXX: only used for debugging 101 bool mInvalid; 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 // We need to keep a list of all the external surfaces used by the blob image. 108 // We do this on a per-display item basis so that the lists remains correct 109 // during invalidations. 110 DrawEventRecorderPrivate::ExternalSurfacesHolder mExternalSurfaces; 111 112 BlobItemData(DIGroup* aGroup, nsDisplayItem* aItem) 113 : mInvisible(false), mUsed(false), mGroup(aGroup) { 114 mInvalid = false; 115 mDisplayItemKey = aItem->GetPerFrameKey(); 116 AddFrame(aItem->Frame()); 117 } 118 119 private: 120 void AddFrame(nsIFrame* aFrame) { 121 mFrame = aFrame; 122 123 nsTArray<BlobItemData*>* array = 124 aFrame->GetProperty(BlobGroupDataProperty()); 125 if (!array) { 126 array = new nsTArray<BlobItemData*>(); 127 aFrame->SetProperty(BlobGroupDataProperty(), array); 128 } 129 array->AppendElement(this); 130 mArray = array; 131 } 132 133 public: 134 void ClearFrame() { 135 // Delete the weak pointer to this BlobItemData on the frame 136 MOZ_RELEASE_ASSERT(mFrame); 137 // the property may already be removed if WebRenderUserData got deleted 138 // first so we use our own mArray pointer. 139 mArray->RemoveElement(this); 140 141 // drop the entire property if nothing's left in the array 142 if (mArray->IsEmpty()) { 143 // If the frame is in the process of being destroyed this will fail 144 // but that's ok, because the the property will be removed then anyways 145 mFrame->RemoveProperty(BlobGroupDataProperty()); 146 } 147 mFrame = nullptr; 148 } 149 150 ~BlobItemData() { 151 if (mFrame) { 152 ClearFrame(); 153 } 154 } 155 }; 156 157 static BlobItemData* GetBlobItemData(nsDisplayItem* aItem) { 158 nsIFrame* frame = aItem->Frame(); 159 uint32_t key = aItem->GetPerFrameKey(); 160 const nsTArray<BlobItemData*>* array = 161 frame->GetProperty(BlobGroupDataProperty()); 162 if (array) { 163 for (BlobItemData* item : *array) { 164 if (item->mDisplayItemKey == key) { 165 return item; 166 } 167 } 168 } 169 return nullptr; 170 } 171 172 // We keep around the BlobItemData so that when we invalidate it get properly 173 // included in the rect 174 static void DestroyBlobGroupDataProperty(nsTArray<BlobItemData*>* aArray) { 175 for (BlobItemData* item : *aArray) { 176 GP("DestroyBlobGroupDataProperty: %p-%d\n", item->mFrame, 177 item->mDisplayItemKey); 178 item->mFrame = nullptr; 179 } 180 delete aArray; 181 } 182 183 static void TakeExternalSurfaces( 184 WebRenderDrawEventRecorder* aRecorder, 185 DrawEventRecorderPrivate::ExternalSurfacesHolder& aExternalSurfaces, 186 RenderRootStateManager* aManager, wr::IpcResourceUpdateQueue& aResources) { 187 aRecorder->TakeExternalSurfaces(aExternalSurfaces); 188 189 for (auto& entry : aExternalSurfaces) { 190 // While we don't use the image key with the surface, because the blob image 191 // renderer doesn't have easy access to the resource set, we still want to 192 // ensure one is generated. That will ensure the surface remains alive until 193 // at least the last epoch which the blob image could be used in. 194 wr::ImageKey key; 195 DebugOnly<nsresult> rv = 196 SharedSurfacesChild::Share(entry.mSurface, aManager, aResources, key); 197 MOZ_ASSERT(rv.value != NS_ERROR_NOT_IMPLEMENTED); 198 } 199 } 200 201 struct DIGroup; 202 struct Grouper { 203 explicit Grouper(ClipManager& aClipManager) 204 : mAppUnitsPerDevPixel(0), 205 mDisplayListBuilder(nullptr), 206 mClipManager(aClipManager) {} 207 208 int32_t mAppUnitsPerDevPixel; 209 nsDisplayListBuilder* mDisplayListBuilder; 210 ClipManager& mClipManager; 211 HitTestInfoManager mHitTestInfoManager; 212 Matrix mTransform; 213 214 // Paint the list of aChildren display items. 215 void PaintContainerItem(DIGroup* aGroup, nsDisplayItem* aItem, 216 BlobItemData* aData, const IntRect& aItemBounds, 217 bool aDirty, nsDisplayList* aChildren, 218 gfxContext* aContext, 219 WebRenderDrawEventRecorder* aRecorder, 220 RenderRootStateManager* aRootManager, 221 wr::IpcResourceUpdateQueue& aResources); 222 223 // Builds groups of display items split based on 'layer activity' 224 void ConstructGroups(nsDisplayListBuilder* aDisplayListBuilder, 225 WebRenderCommandBuilder* aCommandBuilder, 226 wr::DisplayListBuilder& aBuilder, 227 wr::IpcResourceUpdateQueue& aResources, DIGroup* aGroup, 228 nsDisplayList* aList, nsDisplayItem* aWrappingItem, 229 const StackingContextHelper& aSc); 230 // Builds a group of display items without promoting anything to active. 231 bool ConstructGroupInsideInactive(WebRenderCommandBuilder* aCommandBuilder, 232 wr::DisplayListBuilder& aBuilder, 233 wr::IpcResourceUpdateQueue& aResources, 234 DIGroup* aGroup, nsDisplayList* aList, 235 const StackingContextHelper& aSc); 236 // Helper method for processing a single inactive item 237 bool ConstructItemInsideInactive(WebRenderCommandBuilder* aCommandBuilder, 238 wr::DisplayListBuilder& aBuilder, 239 wr::IpcResourceUpdateQueue& aResources, 240 DIGroup* aGroup, nsDisplayItem* aItem, 241 const StackingContextHelper& aSc, 242 bool* aOutIsInvisible); 243 ~Grouper() = default; 244 }; 245 246 // Returns whether this is an item for which complete invalidation was 247 // reliant on LayerTreeInvalidation in the pre-webrender world. 248 static bool IsContainerLayerItem(nsDisplayItem* aItem) { 249 switch (aItem->GetType()) { 250 case DisplayItemType::TYPE_WRAP_LIST: 251 case DisplayItemType::TYPE_CONTAINER: 252 case DisplayItemType::TYPE_TRANSFORM: 253 case DisplayItemType::TYPE_OPACITY: 254 case DisplayItemType::TYPE_FILTER: 255 case DisplayItemType::TYPE_BLEND_CONTAINER: 256 case DisplayItemType::TYPE_BLEND_MODE: 257 case DisplayItemType::TYPE_MASK: 258 case DisplayItemType::TYPE_PERSPECTIVE: { 259 return true; 260 } 261 default: { 262 return false; 263 } 264 } 265 } 266 267 #include <sstream> 268 269 static bool DetectContainerLayerPropertiesBoundsChange( 270 nsDisplayItem* aItem, BlobItemData* aData, 271 nsDisplayItemGeometry& aGeometry) { 272 if (aItem->GetType() == DisplayItemType::TYPE_FILTER) { 273 // Filters get clipped to the BuildingRect since they can 274 // have huge bounds outside of the visible area. 275 // This function and similar code in ComputeGeometryChange should be kept in 276 // sync. 277 aGeometry.mBounds = aGeometry.mBounds.Intersect(aItem->GetBuildingRect()); 278 } 279 280 return !aGeometry.mBounds.IsEqualEdges(aData->mGeometry->mBounds); 281 } 282 283 /* A Display Item Group. This represents a set of diplay items that 284 * have been grouped together for rasterization and can be partially 285 * invalidated. It also tracks a number of properties from the environment 286 * that when changed would cause us to repaint like mScale. */ 287 struct DIGroup { 288 // XXX: Storing owning pointers to the BlobItemData in a hash table is not 289 // a good choice. There are two better options: 290 // 291 // 1. We should just be using a linked list for this stuff. 292 // That we can iterate over only the used items. 293 // We remove from the unused list and add to the used list 294 // when we see an item. 295 // 296 // we allocate using a free list. 297 // 298 // 2. We can use a Vec and use SwapRemove(). 299 // We'll just need to be careful when iterating. 300 // The advantage of a Vec is that everything stays compact 301 // and we don't need to heap allocate the BlobItemData's 302 nsTHashSet<BlobItemData*> mDisplayItems; 303 304 LayerIntRect mInvalidRect; 305 LayerIntRect mVisibleRect; 306 // This is the last visible rect sent to WebRender. It's used 307 // to compute the invalid rect and ensure that we send 308 // the appropriate data to WebRender for merging. 309 LayerIntRect mLastVisibleRect; 310 311 // This is the intersection of mVisibleRect and mLastVisibleRect 312 LayerIntRect mPreservedRect; 313 // mHitTestBounds is the same as mActualBounds except for the bounds 314 // of invisible items which are accounted for in the former but not 315 // in the latter. 316 LayerIntRect mHitTestBounds; 317 LayerIntRect mActualBounds; 318 int32_t mAppUnitsPerDevPixel; 319 gfx::MatrixScales mScale; 320 ScrollableLayerGuid::ViewID mScrollId; 321 CompositorHitTestInfo mHitInfo; 322 LayerPoint mResidualOffset; 323 LayerIntRect mLayerBounds; // mGroupBounds converted to Layer space 324 // mLayerBounds clipped to the container/parent of the 325 // current item being processed. 326 LayerIntRect mClippedImageBounds; // mLayerBounds with the clipping of any 327 // containers applied 328 Maybe<wr::BlobImageKey> mKey; 329 std::vector<RefPtr<ScaledFont>> mFonts; 330 331 DIGroup() 332 : mAppUnitsPerDevPixel(0), 333 mScrollId(ScrollableLayerGuid::NULL_SCROLL_ID), 334 mHitInfo(CompositorHitTestInvisibleToHit) {} 335 336 void InvalidateRect(const LayerIntRect& aRect) { 337 mInvalidRect = mInvalidRect.Union(aRect); 338 } 339 340 LayerIntRect ItemBounds(nsDisplayItem* aItem) { 341 BlobItemData* data = GetBlobItemData(aItem); 342 return data->mRect; 343 } 344 345 void ClearItems() { 346 GP("items: %d\n", mDisplayItems.Count()); 347 for (BlobItemData* data : mDisplayItems) { 348 GP("Deleting %p-%d\n", data->mFrame, data->mDisplayItemKey); 349 delete data; 350 } 351 mDisplayItems.Clear(); 352 } 353 354 void ClearImageKey(RenderRootStateManager* aManager, bool aForce = false) { 355 if (mKey) { 356 MOZ_RELEASE_ASSERT(aForce || mInvalidRect.IsEmpty()); 357 aManager->AddBlobImageKeyForDiscard(*mKey); 358 mKey = Nothing(); 359 } 360 mFonts.clear(); 361 } 362 363 static LayerIntRect ToDeviceSpace(const nsRect& aBounds, Matrix& aMatrix, 364 int32_t aAppUnitsPerDevPixel) { 365 // RoundedOut can convert empty rectangles to non-empty ones 366 // so special case them here 367 if (aBounds.IsEmpty()) { 368 return LayerIntRect(); 369 } 370 return LayerIntRect::FromUnknownRect(RoundedOut(aMatrix.TransformBounds( 371 ToRect(nsLayoutUtils::RectToGfxRect(aBounds, aAppUnitsPerDevPixel))))); 372 } 373 374 bool ComputeGeometryChange(nsDisplayItem* aItem, BlobItemData* aData, 375 Matrix& aMatrix, nsDisplayListBuilder* aBuilder) { 376 // If the frame is marked as invalidated, and didn't specify a rect to 377 // invalidate then we want to invalidate both the old and new bounds, 378 // otherwise we only want to invalidate the changed areas. If we do get an 379 // invalid rect, then we want to add this on top of the change areas. 380 nsRect invalid; 381 bool invalidated = false; 382 const DisplayItemClip& clip = aItem->GetClip(); 383 384 int32_t appUnitsPerDevPixel = 385 aItem->Frame()->PresContext()->AppUnitsPerDevPixel(); 386 MOZ_RELEASE_ASSERT(mAppUnitsPerDevPixel == appUnitsPerDevPixel); 387 GP("\n"); 388 GP("clippedImageRect %d %d %d %d\n", mClippedImageBounds.x, 389 mClippedImageBounds.y, mClippedImageBounds.width, 390 mClippedImageBounds.height); 391 LayerIntSize size = mVisibleRect.Size(); 392 GP("imageSize: %d %d\n", size.width, size.height); 393 /*if (aItem->IsReused() && aData->mGeometry) { 394 return; 395 }*/ 396 397 GP("pre mInvalidRect: %s %p-%d - inv: %d %d %d %d\n", aItem->Name(), 398 aItem->Frame(), aItem->GetPerFrameKey(), mInvalidRect.x, mInvalidRect.y, 399 mInvalidRect.width, mInvalidRect.height); 400 if (!aData->mGeometry) { 401 // This item is being added for the first time, invalidate its entire 402 // area. 403 UniquePtr<nsDisplayItemGeometry> geometry( 404 aItem->AllocateGeometry(aBuilder)); 405 nsRect clippedBounds = clip.ApplyNonRoundedIntersection( 406 geometry->ComputeInvalidationRegion()); 407 aData->mGeometry = std::move(geometry); 408 409 LayerIntRect transformedRect = 410 ToDeviceSpace(clippedBounds, aMatrix, appUnitsPerDevPixel); 411 aData->mRect = transformedRect.Intersect(mClippedImageBounds); 412 GP("CGC %s %d %d %d %d\n", aItem->Name(), clippedBounds.x, 413 clippedBounds.y, clippedBounds.width, clippedBounds.height); 414 GP("%d %d, %f %f\n", mVisibleRect.TopLeft().x.value, 415 mVisibleRect.TopLeft().y.value, aMatrix._11, aMatrix._22); 416 GP("mRect %d %d %d %d\n", aData->mRect.x, aData->mRect.y, 417 aData->mRect.width, aData->mRect.height); 418 InvalidateRect(aData->mRect); 419 aData->mInvalid = true; 420 invalidated = true; 421 } else if (aItem->IsInvalid(invalid) && invalid.IsEmpty()) { 422 UniquePtr<nsDisplayItemGeometry> geometry( 423 aItem->AllocateGeometry(aBuilder)); 424 nsRect clippedBounds = clip.ApplyNonRoundedIntersection( 425 geometry->ComputeInvalidationRegion()); 426 aData->mGeometry = std::move(geometry); 427 428 GP("matrix: %f %f\n", aMatrix._31, aMatrix._32); 429 GP("frame invalid invalidate: %s\n", aItem->Name()); 430 GP("old rect: %d %d %d %d\n", aData->mRect.x, aData->mRect.y, 431 aData->mRect.width, aData->mRect.height); 432 InvalidateRect(aData->mRect); 433 // We want to snap to outside pixels. When should we multiply by the 434 // matrix? 435 // XXX: TransformBounds is expensive. We should avoid doing it if we have 436 // no transform 437 LayerIntRect transformedRect = 438 ToDeviceSpace(clippedBounds, aMatrix, appUnitsPerDevPixel); 439 aData->mRect = transformedRect.Intersect(mClippedImageBounds); 440 InvalidateRect(aData->mRect); 441 GP("new rect: %d %d %d %d\n", aData->mRect.x, aData->mRect.y, 442 aData->mRect.width, aData->mRect.height); 443 aData->mInvalid = true; 444 invalidated = true; 445 } else { 446 GP("else invalidate: %s\n", aItem->Name()); 447 nsRegion combined; 448 // this includes situations like reflow changing the position 449 aItem->ComputeInvalidationRegion(aBuilder, aData->mGeometry.get(), 450 &combined); 451 if (!combined.IsEmpty()) { 452 // There might be no point in doing this elaborate tracking here to get 453 // smaller areas 454 InvalidateRect(aData->mRect); // invalidate the old area -- in theory 455 // combined should take care of this 456 UniquePtr<nsDisplayItemGeometry> geometry( 457 aItem->AllocateGeometry(aBuilder)); 458 // invalidate the invalidated area. 459 460 aData->mGeometry = std::move(geometry); 461 462 nsRect clippedBounds = clip.ApplyNonRoundedIntersection( 463 aData->mGeometry->ComputeInvalidationRegion()); 464 LayerIntRect transformedRect = 465 ToDeviceSpace(clippedBounds, aMatrix, appUnitsPerDevPixel); 466 aData->mRect = transformedRect.Intersect(mClippedImageBounds); 467 InvalidateRect(aData->mRect); 468 469 aData->mInvalid = true; 470 invalidated = true; 471 } else { 472 if (aData->mClip != clip) { 473 UniquePtr<nsDisplayItemGeometry> geometry( 474 aItem->AllocateGeometry(aBuilder)); 475 if (!IsContainerLayerItem(aItem)) { 476 // the bounds of layer items can change on us without 477 // ComputeInvalidationRegion returning any change. Other items 478 // shouldn't have any hidden geometry change. 479 MOZ_RELEASE_ASSERT( 480 geometry->mBounds.IsEqualEdges(aData->mGeometry->mBounds)); 481 } else { 482 aData->mGeometry = std::move(geometry); 483 } 484 nsRect clippedBounds = clip.ApplyNonRoundedIntersection( 485 aData->mGeometry->ComputeInvalidationRegion()); 486 LayerIntRect transformedRect = 487 ToDeviceSpace(clippedBounds, aMatrix, appUnitsPerDevPixel); 488 InvalidateRect(aData->mRect); 489 aData->mRect = transformedRect.Intersect(mClippedImageBounds); 490 InvalidateRect(aData->mRect); 491 invalidated = true; 492 493 GP("ClipChange: %s %d %d %d %d\n", aItem->Name(), aData->mRect.x, 494 aData->mRect.y, aData->mRect.XMost(), aData->mRect.YMost()); 495 496 } else if (IsContainerLayerItem(aItem)) { 497 UniquePtr<nsDisplayItemGeometry> geometry( 498 aItem->AllocateGeometry(aBuilder)); 499 // we need to catch bounds changes of containers so that we continue 500 // to have the correct bounds rects in the recording 501 if (DetectContainerLayerPropertiesBoundsChange(aItem, aData, 502 *geometry)) { 503 nsRect clippedBounds = clip.ApplyNonRoundedIntersection( 504 geometry->ComputeInvalidationRegion()); 505 aData->mGeometry = std::move(geometry); 506 LayerIntRect transformedRect = 507 ToDeviceSpace(clippedBounds, aMatrix, appUnitsPerDevPixel); 508 InvalidateRect(aData->mRect); 509 aData->mRect = transformedRect.Intersect(mClippedImageBounds); 510 InvalidateRect(aData->mRect); 511 invalidated = true; 512 GP("DetectContainerLayerPropertiesBoundsChange change\n"); 513 } else { 514 // Handle changes in mClippedImageBounds 515 nsRect clippedBounds = clip.ApplyNonRoundedIntersection( 516 geometry->ComputeInvalidationRegion()); 517 LayerIntRect transformedRect = 518 ToDeviceSpace(clippedBounds, aMatrix, appUnitsPerDevPixel); 519 auto rect = transformedRect.Intersect(mClippedImageBounds); 520 if (!rect.IsEqualEdges(aData->mRect)) { 521 GP("ContainerLayer image rect bounds change\n"); 522 InvalidateRect(aData->mRect); 523 aData->mRect = rect; 524 InvalidateRect(aData->mRect); 525 invalidated = true; 526 } else { 527 GP("Layer NoChange: %s %d %d %d %d\n", aItem->Name(), 528 aData->mRect.x, aData->mRect.y, aData->mRect.XMost(), 529 aData->mRect.YMost()); 530 } 531 } 532 } else { 533 UniquePtr<nsDisplayItemGeometry> geometry( 534 aItem->AllocateGeometry(aBuilder)); 535 nsRect clippedBounds = clip.ApplyNonRoundedIntersection( 536 geometry->ComputeInvalidationRegion()); 537 LayerIntRect transformedRect = 538 ToDeviceSpace(clippedBounds, aMatrix, appUnitsPerDevPixel); 539 auto rect = transformedRect.Intersect(mClippedImageBounds); 540 // Make sure we update mRect for mClippedImageBounds changes 541 if (!rect.IsEqualEdges(aData->mRect)) { 542 GP("ContainerLayer image rect bounds change\n"); 543 InvalidateRect(aData->mRect); 544 aData->mRect = rect; 545 InvalidateRect(aData->mRect); 546 invalidated = true; 547 } else { 548 GP("NoChange: %s %d %d %d %d\n", aItem->Name(), aData->mRect.x, 549 aData->mRect.y, aData->mRect.XMost(), aData->mRect.YMost()); 550 } 551 } 552 } 553 } 554 555 if (aData->mGeometry && aItem->GetType() == DisplayItemType::TYPE_FILTER) { 556 // This hunk DetectContainerLayerPropertiesBoundsChange should be kept in 557 // sync. 558 aData->mGeometry->mBounds = 559 aData->mGeometry->mBounds.Intersect(aItem->GetBuildingRect()); 560 } 561 562 mHitTestBounds.OrWith(aData->mRect); 563 if (!aData->mInvisible) { 564 mActualBounds.OrWith(aData->mRect); 565 } 566 aData->mClip = clip; 567 GP("post mInvalidRect: %d %d %d %d\n", mInvalidRect.x, mInvalidRect.y, 568 mInvalidRect.width, mInvalidRect.height); 569 return invalidated; 570 } 571 572 void EndGroup(WebRenderLayerManager* aWrManager, 573 nsDisplayListBuilder* aDisplayListBuilder, 574 wr::DisplayListBuilder& aBuilder, 575 wr::IpcResourceUpdateQueue& aResources, Grouper* aGrouper, 576 nsDisplayList::iterator aStartItem, 577 nsDisplayList::iterator aEndItem) { 578 GP("\n\n"); 579 GP("Begin EndGroup\n"); 580 581 auto scale = LayoutDeviceToLayerScale2D::FromUnknownScale(mScale); 582 583 auto hitTestRect = mVisibleRect.Intersect(ViewAs<LayerPixel>( 584 mHitTestBounds, PixelCastJustification::LayerIsImage)); 585 if (!hitTestRect.IsEmpty()) { 586 auto deviceHitTestRect = 587 (LayerRect(hitTestRect) - mResidualOffset) / scale; 588 PushHitTest(aBuilder, deviceHitTestRect); 589 } 590 591 mVisibleRect = mVisibleRect.Intersect(ViewAs<LayerPixel>( 592 mActualBounds, PixelCastJustification::LayerIsImage)); 593 594 if (mVisibleRect.IsEmpty()) { 595 return; 596 } 597 598 // Invalidate any unused items 599 GP("mDisplayItems\n"); 600 mDisplayItems.RemoveIf([&](BlobItemData* data) { 601 GP(" : %p-%d\n", data->mFrame, data->mDisplayItemKey); 602 if (!data->mUsed) { 603 GP("Invalidate unused: %p-%d\n", data->mFrame, data->mDisplayItemKey); 604 InvalidateRect(data->mRect); 605 delete data; 606 return true; 607 } 608 609 data->mUsed = false; 610 return false; 611 }); 612 613 IntSize dtSize = mVisibleRect.Size().ToUnknownSize(); 614 // The actual display item's size shouldn't have the scale factored in 615 // Round the bounds out to leave space for unsnapped content 616 LayoutDeviceRect itemBounds = 617 (LayerRect(mVisibleRect) - mResidualOffset) / scale; 618 619 if (mInvalidRect.IsEmpty() && mVisibleRect.IsEqualEdges(mLastVisibleRect)) { 620 GP("Not repainting group because it's empty\n"); 621 GP("End EndGroup\n"); 622 if (mKey) { 623 // Although the contents haven't changed, the visible area *may* have, 624 // so request it be updated unconditionally (wr should be able to easily 625 // detect if this is a no-op on its side, if that matters) 626 aResources.SetBlobImageVisibleArea( 627 *mKey, ViewAs<ImagePixel>(mVisibleRect, 628 PixelCastJustification::LayerIsImage)); 629 mLastVisibleRect = mVisibleRect; 630 PushImage(aBuilder, itemBounds); 631 } 632 return; 633 } 634 635 std::vector<RefPtr<ScaledFont>> fonts; 636 bool validFonts = true; 637 RefPtr<WebRenderDrawEventRecorder> recorder = 638 MakeAndAddRef<WebRenderDrawEventRecorder>( 639 [&](MemStream& aStream, 640 std::vector<RefPtr<ScaledFont>>& aScaledFonts) { 641 size_t count = aScaledFonts.size(); 642 aStream.write((const char*)&count, sizeof(count)); 643 for (auto& scaled : aScaledFonts) { 644 Maybe<wr::FontInstanceKey> key = 645 aWrManager->WrBridge()->GetFontKeyForScaledFont(scaled, 646 aResources); 647 if (key.isNothing()) { 648 validFonts = false; 649 break; 650 } 651 BlobFont font = {key.value(), scaled}; 652 aStream.write((const char*)&font, sizeof(font)); 653 } 654 fonts = std::move(aScaledFonts); 655 }); 656 657 RefPtr<gfx::DrawTarget> dummyDt = 658 gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget(); 659 660 RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateRecordingDrawTarget( 661 recorder, dummyDt, mLayerBounds.ToUnknownRect()); 662 if (!dt || !dt->IsValid()) { 663 gfxCriticalNote << "Failed to create drawTarget for blob image"; 664 return; 665 } 666 667 gfxContext context(dt); 668 context.SetMatrix(Matrix::Scaling(mScale).PostTranslate(mResidualOffset.x, 669 mResidualOffset.y)); 670 671 GP("mInvalidRect: %d %d %d %d\n", mInvalidRect.x, mInvalidRect.y, 672 mInvalidRect.width, mInvalidRect.height); 673 674 RenderRootStateManager* rootManager = 675 aWrManager->GetRenderRootStateManager(); 676 677 bool empty = aStartItem == aEndItem; 678 if (empty) { 679 ClearImageKey(rootManager, true); 680 return; 681 } 682 683 PaintItemRange(aGrouper, aStartItem, aEndItem, &context, recorder, 684 rootManager, aResources); 685 686 // XXX: set this correctly perhaps using 687 // aItem->GetOpaqueRegion(aDisplayListBuilder, &snapped). 688 // Contains(paintBounds);? 689 wr::OpacityType opacity = wr::OpacityType::HasAlphaChannel; 690 691 bool hasItems = recorder->Finish(); 692 GP("%d Finish\n", hasItems); 693 if (!validFonts) { 694 gfxCriticalNote << "Failed serializing fonts for blob image"; 695 return; 696 } 697 Range<uint8_t> bytes((uint8_t*)recorder->mOutputStream.mData, 698 recorder->mOutputStream.mLength); 699 if (!mKey) { 700 // we don't want to send a new image that doesn't have any 701 // items in it 702 if (!hasItems || mVisibleRect.IsEmpty()) { 703 GP("Skipped group with no items\n"); 704 return; 705 } 706 707 wr::BlobImageKey key = 708 wr::BlobImageKey{aWrManager->WrBridge()->GetNextImageKey()}; 709 GP("No previous key making new one %d\n", key._0.mHandle); 710 wr::ImageDescriptor descriptor(dtSize, 0, dt->GetFormat(), opacity); 711 MOZ_RELEASE_ASSERT(bytes.length() > sizeof(size_t)); 712 if (!aResources.AddBlobImage( 713 key, descriptor, bytes, 714 ViewAs<ImagePixel>(mVisibleRect, 715 PixelCastJustification::LayerIsImage))) { 716 return; 717 } 718 mKey = Some(key); 719 } else { 720 MOZ_DIAGNOSTIC_ASSERT( 721 aWrManager->WrBridge()->MatchesNamespace(mKey.ref()), 722 "Stale blob key for group!"); 723 724 wr::ImageDescriptor descriptor(dtSize, 0, dt->GetFormat(), opacity); 725 726 // Convert mInvalidRect to image space by subtracting the corner of the 727 // image bounds 728 auto dirtyRect = ViewAs<ImagePixel>(mInvalidRect, 729 PixelCastJustification::LayerIsImage); 730 731 auto bottomRight = dirtyRect.BottomRight(); 732 GP("check invalid %d %d - %d %d\n", bottomRight.x.value, 733 bottomRight.y.value, dtSize.width, dtSize.height); 734 GP("Update Blob %d %d %d %d\n", mInvalidRect.x, mInvalidRect.y, 735 mInvalidRect.width, mInvalidRect.height); 736 if (!aResources.UpdateBlobImage( 737 *mKey, descriptor, bytes, 738 ViewAs<ImagePixel>(mVisibleRect, 739 PixelCastJustification::LayerIsImage), 740 dirtyRect)) { 741 return; 742 } 743 } 744 mFonts = std::move(fonts); 745 aResources.SetBlobImageVisibleArea( 746 *mKey, 747 ViewAs<ImagePixel>(mVisibleRect, PixelCastJustification::LayerIsImage)); 748 mLastVisibleRect = mVisibleRect; 749 PushImage(aBuilder, itemBounds); 750 GP("End EndGroup\n\n"); 751 } 752 753 void PushImage(wr::DisplayListBuilder& aBuilder, 754 const LayoutDeviceRect& bounds) { 755 wr::LayoutRect dest = wr::ToLayoutRect(bounds); 756 GP("PushImage: %f %f %f %f\n", dest.min.x, dest.min.y, dest.max.x, 757 dest.max.y); 758 // wr::ToImageRendering(aItem->Frame()->UsedImageRendering()); 759 auto rendering = wr::ImageRendering::Auto; 760 bool backfaceHidden = false; 761 762 // XXX - clipping the item against the paint rect breaks some content. 763 // cf. Bug 1455422. 764 // wr::LayoutRect clip = wr::ToLayoutRect(bounds.Intersect(mVisibleRect)); 765 766 aBuilder.PushImage(dest, dest, !backfaceHidden, false, rendering, 767 wr::AsImageKey(*mKey)); 768 } 769 770 void PushHitTest(wr::DisplayListBuilder& aBuilder, 771 const LayoutDeviceRect& bounds) { 772 wr::LayoutRect dest = wr::ToLayoutRect(bounds); 773 GP("PushHitTest: %f %f %f %f\n", dest.min.x, dest.min.y, dest.max.x, 774 dest.max.y); 775 776 // We don't really know the exact shape of this blob because it may contain 777 // SVG shapes. Also mHitInfo may be a combination of hit info flags from 778 // different shapes so generate an irregular-area hit-test region for it. 779 CompositorHitTestInfo hitInfo = mHitInfo; 780 if (hitInfo.contains(CompositorHitTestFlags::eVisibleToHitTest)) { 781 hitInfo += CompositorHitTestFlags::eIrregularArea; 782 } 783 784 bool backfaceHidden = false; 785 aBuilder.PushHitTest(dest, dest, !backfaceHidden, mScrollId, hitInfo, 786 SideBits::eNone); 787 } 788 789 void PaintItemRange(Grouper* aGrouper, nsDisplayList::iterator aStartItem, 790 nsDisplayList::iterator aEndItem, gfxContext* aContext, 791 WebRenderDrawEventRecorder* aRecorder, 792 RenderRootStateManager* aRootManager, 793 wr::IpcResourceUpdateQueue& aResources) { 794 LayerIntSize size = mVisibleRect.Size(); 795 for (auto it = aStartItem; it != aEndItem; ++it) { 796 nsDisplayItem* item = *it; 797 MOZ_ASSERT(item); 798 799 if (item->GetType() == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) { 800 continue; 801 } 802 803 BlobItemData* data = GetBlobItemData(item); 804 if (data->mInvisible) { 805 continue; 806 } 807 808 LayerIntRect bounds = data->mRect; 809 810 // skip empty items 811 if (bounds.IsEmpty()) { 812 continue; 813 } 814 815 GP("Trying %s %p-%d %d %d %d %d\n", item->Name(), item->Frame(), 816 item->GetPerFrameKey(), bounds.x, bounds.y, bounds.XMost(), 817 bounds.YMost()); 818 819 auto bottomRight = bounds.BottomRight(); 820 821 GP("paint check invalid %d %d - %d %d\n", bottomRight.x.value, 822 bottomRight.y.value, size.width, size.height); 823 824 bool dirty = true; 825 auto preservedBounds = bounds.Intersect(mPreservedRect); 826 if (!mInvalidRect.Contains(preservedBounds)) { 827 GP("Passing\n"); 828 dirty = false; 829 if (data->mInvalid) { 830 gfxCriticalError() 831 << "DisplayItem" << item->Name() << "-should be invalid"; 832 } 833 // if the item is invalid it needs to be fully contained 834 MOZ_RELEASE_ASSERT(!data->mInvalid); 835 } 836 837 nsDisplayList* children = item->GetChildren(); 838 if (children) { 839 // If we aren't dirty, we still need to iterate over the children to 840 // ensure the blob index data is recorded the same as before to allow 841 // the merging of the parts inside in the invalid rect. Any items that 842 // are painted as a single item need to avoid repainting in that case. 843 GP("doing children in EndGroup\n"); 844 aGrouper->PaintContainerItem(this, item, data, bounds.ToUnknownRect(), 845 dirty, children, aContext, aRecorder, 846 aRootManager, aResources); 847 continue; 848 } 849 nsPaintedDisplayItem* paintedItem = item->AsPaintedDisplayItem(); 850 if (!paintedItem) { 851 continue; 852 } 853 if (dirty) { 854 // What should the clip settting strategy be? We can set the full 855 // clip everytime. this is probably easiest for now. An alternative 856 // would be to put the push and the pop into separate items and let 857 // invalidation handle it that way. 858 DisplayItemClip currentClip = paintedItem->GetClip(); 859 860 if (currentClip.HasClip()) { 861 aContext->Save(); 862 currentClip.ApplyTo(aContext, aGrouper->mAppUnitsPerDevPixel); 863 } 864 aContext->NewPath(); 865 GP("painting %s %p-%d\n", paintedItem->Name(), paintedItem->Frame(), 866 paintedItem->GetPerFrameKey()); 867 if (aGrouper->mDisplayListBuilder->IsPaintingToWindow()) { 868 paintedItem->Frame()->AddStateBits(NS_FRAME_PAINTED_THEBES); 869 } 870 871 paintedItem->Paint(aGrouper->mDisplayListBuilder, aContext); 872 TakeExternalSurfaces(aRecorder, data->mExternalSurfaces, aRootManager, 873 aResources); 874 875 if (currentClip.HasClip()) { 876 aContext->Restore(); 877 } 878 } 879 aContext->GetDrawTarget()->FlushItem(bounds.ToUnknownRect()); 880 } 881 } 882 883 ~DIGroup() { 884 GP("Group destruct\n"); 885 for (BlobItemData* data : mDisplayItems) { 886 GP("Deleting %p-%d\n", data->mFrame, data->mDisplayItemKey); 887 delete data; 888 } 889 } 890 }; 891 892 // If we have an item we need to make sure it matches the current group 893 // otherwise it means the item switched groups and we need to invalidate 894 // it and recreate the data. 895 static BlobItemData* GetBlobItemDataForGroup(nsDisplayItem* aItem, 896 DIGroup* aGroup) { 897 BlobItemData* data = GetBlobItemData(aItem); 898 if (data) { 899 MOZ_ASSERT(data->mGroup->mDisplayItems.Contains(data)); 900 if (data->mGroup != aGroup) { 901 GP("group don't match %p %p\n", data->mGroup, aGroup); 902 data->ClearFrame(); 903 // the item is for another group 904 // it should be cleared out as being unused at the end of this paint 905 data = nullptr; 906 } 907 } 908 if (!data) { 909 GP("Allocating blob data\n"); 910 data = new BlobItemData(aGroup, aItem); 911 aGroup->mDisplayItems.Insert(data); 912 } 913 data->mUsed = true; 914 return data; 915 } 916 917 void Grouper::PaintContainerItem(DIGroup* aGroup, nsDisplayItem* aItem, 918 BlobItemData* aData, 919 const IntRect& aItemBounds, bool aDirty, 920 nsDisplayList* aChildren, gfxContext* aContext, 921 WebRenderDrawEventRecorder* aRecorder, 922 RenderRootStateManager* aRootManager, 923 wr::IpcResourceUpdateQueue& aResources) { 924 switch (aItem->GetType()) { 925 case DisplayItemType::TYPE_TRANSFORM: { 926 DisplayItemClip currentClip = aItem->GetClip(); 927 928 gfxContextMatrixAutoSaveRestore saveMatrix; 929 if (currentClip.HasClip()) { 930 aContext->Save(); 931 currentClip.ApplyTo(aContext, this->mAppUnitsPerDevPixel); 932 aContext->GetDrawTarget()->FlushItem(aItemBounds); 933 } else { 934 saveMatrix.SetContext(aContext); 935 } 936 937 auto transformItem = static_cast<nsDisplayTransform*>(aItem); 938 Matrix4x4Flagged trans = transformItem->GetTransform(); 939 Matrix trans2d; 940 if (!trans.Is2D(&trans2d)) { 941 // Painting will cause us to include the item's recording in the blob. 942 // We only want to do that if it is dirty, because otherwise the 943 // recording might change (e.g. due to factor of 2 scaling of images 944 // giving different results) and the merging will discard it because it 945 // is outside the invalid rect. 946 if (aDirty) { 947 // We don't currently support doing invalidation inside 3d transforms. 948 // For now just paint it as a single item. 949 aItem->AsPaintedDisplayItem()->Paint(mDisplayListBuilder, aContext); 950 TakeExternalSurfaces(aRecorder, aData->mExternalSurfaces, 951 aRootManager, aResources); 952 } 953 aContext->GetDrawTarget()->FlushItem(aItemBounds); 954 } else if (!trans2d.IsSingular()) { 955 aContext->Multiply(ThebesMatrix(trans2d)); 956 aGroup->PaintItemRange(this, aChildren->begin(), aChildren->end(), 957 aContext, aRecorder, aRootManager, aResources); 958 } 959 960 if (currentClip.HasClip()) { 961 aContext->Restore(); 962 aContext->GetDrawTarget()->FlushItem(aItemBounds); 963 } 964 break; 965 } 966 case DisplayItemType::TYPE_OPACITY: { 967 auto opacityItem = static_cast<nsDisplayOpacity*>(aItem); 968 float opacity = opacityItem->GetOpacity(); 969 if (opacity == 0.0f) { 970 return; 971 } 972 973 aContext->GetDrawTarget()->PushLayer(false, opacityItem->GetOpacity(), 974 nullptr, mozilla::gfx::Matrix(), 975 aItemBounds); 976 GP("beginGroup %s %p-%d\n", aItem->Name(), aItem->Frame(), 977 aItem->GetPerFrameKey()); 978 aContext->GetDrawTarget()->FlushItem(aItemBounds); 979 aGroup->PaintItemRange(this, aChildren->begin(), aChildren->end(), 980 aContext, aRecorder, aRootManager, aResources); 981 aContext->GetDrawTarget()->PopLayer(); 982 GP("endGroup %s %p-%d\n", aItem->Name(), aItem->Frame(), 983 aItem->GetPerFrameKey()); 984 aContext->GetDrawTarget()->FlushItem(aItemBounds); 985 break; 986 } 987 case DisplayItemType::TYPE_BLEND_MODE: { 988 auto blendItem = static_cast<nsDisplayBlendMode*>(aItem); 989 auto blendMode = blendItem->BlendMode(); 990 aContext->GetDrawTarget()->PushLayerWithBlend( 991 false, 1.0, nullptr, mozilla::gfx::Matrix(), aItemBounds, false, 992 blendMode); 993 GP("beginGroup %s %p-%d\n", aItem->Name(), aItem->Frame(), 994 aItem->GetPerFrameKey()); 995 aContext->GetDrawTarget()->FlushItem(aItemBounds); 996 aGroup->PaintItemRange(this, aChildren->begin(), aChildren->end(), 997 aContext, aRecorder, aRootManager, aResources); 998 aContext->GetDrawTarget()->PopLayer(); 999 GP("endGroup %s %p-%d\n", aItem->Name(), aItem->Frame(), 1000 aItem->GetPerFrameKey()); 1001 aContext->GetDrawTarget()->FlushItem(aItemBounds); 1002 break; 1003 } 1004 case DisplayItemType::TYPE_BLEND_CONTAINER: { 1005 auto* bc = static_cast<nsDisplayBlendContainer*>(aItem); 1006 const bool flatten = bc->ShouldFlattenAway(mDisplayListBuilder); 1007 if (!flatten) { 1008 aContext->GetDrawTarget()->PushLayer( 1009 false, 1.0, nullptr, mozilla::gfx::Matrix(), aItemBounds); 1010 GP("beginGroup %s %p-%d\n", aItem->Name(), aItem->Frame(), 1011 aItem->GetPerFrameKey()); 1012 aContext->GetDrawTarget()->FlushItem(aItemBounds); 1013 } 1014 aGroup->PaintItemRange(this, aChildren->begin(), aChildren->end(), 1015 aContext, aRecorder, aRootManager, aResources); 1016 if (!flatten) { 1017 aContext->GetDrawTarget()->PopLayer(); 1018 GP("endGroup %s %p-%d\n", aItem->Name(), aItem->Frame(), 1019 aItem->GetPerFrameKey()); 1020 aContext->GetDrawTarget()->FlushItem(aItemBounds); 1021 } 1022 break; 1023 } 1024 case DisplayItemType::TYPE_MASK: { 1025 GP("Paint Mask\n"); 1026 auto maskItem = static_cast<nsDisplayMasksAndClipPaths*>(aItem); 1027 if (maskItem->IsValidMask()) { 1028 maskItem->PaintWithContentsPaintCallback( 1029 mDisplayListBuilder, aContext, [&] { 1030 GP("beginGroup %s %p-%d\n", aItem->Name(), aItem->Frame(), 1031 aItem->GetPerFrameKey()); 1032 aContext->GetDrawTarget()->FlushItem(aItemBounds); 1033 aGroup->PaintItemRange(this, aChildren->begin(), aChildren->end(), 1034 aContext, aRecorder, aRootManager, 1035 aResources); 1036 GP("endGroup %s %p-%d\n", aItem->Name(), aItem->Frame(), 1037 aItem->GetPerFrameKey()); 1038 }); 1039 TakeExternalSurfaces(aRecorder, aData->mExternalSurfaces, aRootManager, 1040 aResources); 1041 aContext->GetDrawTarget()->FlushItem(aItemBounds); 1042 } 1043 break; 1044 } 1045 case DisplayItemType::TYPE_FILTER: { 1046 GP("Paint Filter\n"); 1047 // Painting will cause us to include the item's recording in the blob. We 1048 // only want to do that if it is dirty, because otherwise the recording 1049 // might change (e.g. due to factor of 2 scaling of images giving 1050 // different results) and the merging will discard it because it is 1051 // outside the invalid rect. 1052 if (aDirty) { 1053 auto filterItem = static_cast<nsDisplayFilters*>(aItem); 1054 filterItem->Paint(mDisplayListBuilder, aContext); 1055 TakeExternalSurfaces(aRecorder, aData->mExternalSurfaces, aRootManager, 1056 aResources); 1057 } 1058 aContext->GetDrawTarget()->FlushItem(aItemBounds); 1059 break; 1060 } 1061 1062 default: 1063 aGroup->PaintItemRange(this, aChildren->begin(), aChildren->end(), 1064 aContext, aRecorder, aRootManager, aResources); 1065 break; 1066 } 1067 } 1068 1069 class WebRenderGroupData : public WebRenderUserData { 1070 public: 1071 WebRenderGroupData(RenderRootStateManager* aWRManager, nsDisplayItem* aItem); 1072 WebRenderGroupData(RenderRootStateManager* aWRManager, 1073 uint32_t aDisplayItemKey, nsIFrame* aFrame); 1074 virtual ~WebRenderGroupData(); 1075 1076 WebRenderGroupData* AsGroupData() override { return this; } 1077 UserDataType GetType() override { return UserDataType::eGroup; } 1078 static UserDataType Type() { return UserDataType::eGroup; } 1079 1080 DIGroup mSubGroup; 1081 DIGroup mFollowingGroup; 1082 }; 1083 1084 enum class ItemActivity : uint8_t { 1085 /// Item must not be active. 1086 No = 0, 1087 /// Could be active if it has no layerization cost. 1088 /// Typically active if first of an item group. 1089 Could = 1, 1090 /// Should be active unless something external makes that less useful. 1091 /// For example if the item is affected by a complex mask, it remains 1092 /// inactive. 1093 Should = 2, 1094 /// Must be active regardless of external factors. 1095 Must = 3, 1096 }; 1097 1098 ItemActivity CombineActivity(ItemActivity a, ItemActivity b) { 1099 return a > b ? a : b; 1100 } 1101 1102 bool ActivityAtLeast(ItemActivity rhs, ItemActivity atLeast) { 1103 return rhs >= atLeast; 1104 } 1105 1106 static ItemActivity IsItemProbablyActive( 1107 nsDisplayItem* aItem, mozilla::wr::DisplayListBuilder& aBuilder, 1108 mozilla::wr::IpcResourceUpdateQueue& aResources, 1109 const mozilla::layers::StackingContextHelper& aSc, 1110 mozilla::layers::RenderRootStateManager* aManager, 1111 nsDisplayListBuilder* aDisplayListBuilder, bool aSiblingActive, 1112 bool aUniformlyScaled); 1113 1114 static ItemActivity HasActiveChildren( 1115 const nsDisplayList& aList, mozilla::wr::DisplayListBuilder& aBuilder, 1116 mozilla::wr::IpcResourceUpdateQueue& aResources, 1117 const mozilla::layers::StackingContextHelper& aSc, 1118 mozilla::layers::RenderRootStateManager* aManager, 1119 nsDisplayListBuilder* aDisplayListBuilder, bool aUniformlyScaled) { 1120 ItemActivity activity = ItemActivity::No; 1121 for (nsDisplayItem* item : aList) { 1122 // Here we only want to know if a child must be active, so we don't specify 1123 // when the item is first or last, which can cause an item that could be 1124 // either decide to be active. This is a bit conservative and avoids some 1125 // extra layers. It's a good tradeoff until we get to the point where most 1126 // items could have been active but none *had* to. Right now this is 1127 // unlikely but as more svg items get webrenderized it will be better to 1128 // make them active more aggressively. 1129 auto childActivity = 1130 IsItemProbablyActive(item, aBuilder, aResources, aSc, aManager, 1131 aDisplayListBuilder, false, aUniformlyScaled); 1132 activity = CombineActivity(activity, childActivity); 1133 if (activity == ItemActivity::Must) { 1134 return activity; 1135 } 1136 } 1137 return activity; 1138 } 1139 1140 static ItemActivity AssessBounds(const StackingContextHelper& aSc, 1141 nsDisplayListBuilder* aDisplayListBuilder, 1142 nsDisplayItem* aItem, 1143 bool aHasActivePrecedingSibling) { 1144 // Arbitrary threshold up for adjustments. What we want to avoid here 1145 // is alternating between active and non active items and create a lot 1146 // of overlapping blobs, so we only make images active if they are 1147 // costly enough that it's worth the risk of having more layers. As we 1148 // move more blob items into wr display items it will become less of a 1149 // concern. 1150 constexpr float largeish = 512; 1151 1152 bool snap = false; 1153 nsRect bounds = aItem->GetBounds(aDisplayListBuilder, &snap); 1154 1155 float appUnitsPerDevPixel = 1156 static_cast<float>(aItem->Frame()->PresContext()->AppUnitsPerDevPixel()); 1157 1158 float width = 1159 static_cast<float>(bounds.width) * aSc.GetInheritedScale().xScale; 1160 float height = 1161 static_cast<float>(bounds.height) * aSc.GetInheritedScale().yScale; 1162 1163 // Webrender doesn't handle primitives smaller than a pixel well, so 1164 // avoid making them active. 1165 if (width >= appUnitsPerDevPixel && height >= appUnitsPerDevPixel) { 1166 if (aHasActivePrecedingSibling || width > largeish || height > largeish) { 1167 return ItemActivity::Should; 1168 } 1169 1170 return ItemActivity::Could; 1171 } 1172 1173 return ItemActivity::No; 1174 } 1175 1176 // This function decides whether we want to treat this item as "active", which 1177 // means that it's a container item which we will turn into a WebRender 1178 // StackingContext, or whether we treat it as "inactive" and include it inside 1179 // the parent blob image. 1180 // 1181 // We can't easily use GetLayerState because it wants a bunch of layers related 1182 // information. 1183 static ItemActivity IsItemProbablyActive( 1184 nsDisplayItem* aItem, mozilla::wr::DisplayListBuilder& aBuilder, 1185 mozilla::wr::IpcResourceUpdateQueue& aResources, 1186 const mozilla::layers::StackingContextHelper& aSc, 1187 mozilla::layers::RenderRootStateManager* aManager, 1188 nsDisplayListBuilder* aDisplayListBuilder, bool aHasActivePrecedingSibling, 1189 bool aUniformlyScaled) { 1190 switch (aItem->GetType()) { 1191 case DisplayItemType::TYPE_TRANSFORM: { 1192 nsDisplayTransform* transformItem = 1193 static_cast<nsDisplayTransform*>(aItem); 1194 const Matrix4x4Flagged& t = transformItem->GetTransform(); 1195 Matrix t2d; 1196 bool is2D = t.Is2D(&t2d); 1197 if (!is2D) { 1198 return ItemActivity::Must; 1199 } 1200 1201 auto activity = HasActiveChildren(*transformItem->GetChildren(), aBuilder, 1202 aResources, aSc, aManager, 1203 aDisplayListBuilder, aUniformlyScaled); 1204 1205 if (transformItem->MayBeAnimated(aDisplayListBuilder)) { 1206 activity = CombineActivity(activity, ItemActivity::Should); 1207 } 1208 1209 return activity; 1210 } 1211 case DisplayItemType::TYPE_OPACITY: { 1212 nsDisplayOpacity* opacityItem = static_cast<nsDisplayOpacity*>(aItem); 1213 if (opacityItem->NeedsActiveLayer(aDisplayListBuilder, 1214 opacityItem->Frame())) { 1215 return ItemActivity::Must; 1216 } 1217 return HasActiveChildren(*opacityItem->GetChildren(), aBuilder, 1218 aResources, aSc, aManager, aDisplayListBuilder, 1219 aUniformlyScaled); 1220 } 1221 case DisplayItemType::TYPE_FOREIGN_OBJECT: { 1222 return ItemActivity::Must; 1223 } 1224 case DisplayItemType::TYPE_SVG_GEOMETRY: { 1225 auto* svgItem = static_cast<DisplaySVGGeometry*>(aItem); 1226 if (StaticPrefs::gfx_webrender_svg_shapes() && aUniformlyScaled && 1227 svgItem->ShouldBeActive(aBuilder, aResources, aSc, aManager, 1228 aDisplayListBuilder)) { 1229 return AssessBounds(aSc, aDisplayListBuilder, aItem, 1230 aHasActivePrecedingSibling); 1231 } 1232 1233 return ItemActivity::No; 1234 } 1235 case DisplayItemType::TYPE_SVG_IMAGE: { 1236 auto* svgItem = static_cast<DisplaySVGImage*>(aItem); 1237 if (StaticPrefs::gfx_webrender_svg_images() && aUniformlyScaled && 1238 svgItem->ShouldBeActive(aBuilder, aResources, aSc, aManager, 1239 aDisplayListBuilder)) { 1240 return AssessBounds(aSc, aDisplayListBuilder, aItem, 1241 aHasActivePrecedingSibling); 1242 } 1243 1244 return ItemActivity::No; 1245 } 1246 case DisplayItemType::TYPE_BLEND_MODE: { 1247 /* BLEND_MODE needs to be active if it might have a previous sibling 1248 * that is active so that it's able to blend with that content. */ 1249 if (aHasActivePrecedingSibling) { 1250 return ItemActivity::Must; 1251 } 1252 1253 return HasActiveChildren(*aItem->GetChildren(), aBuilder, aResources, aSc, 1254 aManager, aDisplayListBuilder, aUniformlyScaled); 1255 } 1256 case DisplayItemType::TYPE_MASK: { 1257 if (aItem->GetChildren()) { 1258 auto activity = 1259 HasActiveChildren(*aItem->GetChildren(), aBuilder, aResources, aSc, 1260 aManager, aDisplayListBuilder, aUniformlyScaled); 1261 // For masked items, don't bother with making children active since we 1262 // are going to have to need to paint and upload a large mask anyway. 1263 if (activity < ItemActivity::Must) { 1264 return ItemActivity::No; 1265 } 1266 return activity; 1267 } 1268 return ItemActivity::No; 1269 } 1270 case DisplayItemType::TYPE_WRAP_LIST: 1271 case DisplayItemType::TYPE_CONTAINER: 1272 case DisplayItemType::TYPE_PERSPECTIVE: { 1273 if (aItem->GetChildren()) { 1274 return HasActiveChildren(*aItem->GetChildren(), aBuilder, aResources, 1275 aSc, aManager, aDisplayListBuilder, 1276 aUniformlyScaled); 1277 } 1278 return ItemActivity::No; 1279 } 1280 case DisplayItemType::TYPE_FILTER: { 1281 nsDisplayFilters* filters = static_cast<nsDisplayFilters*>(aItem); 1282 if (filters->CanCreateWebRenderCommands()) { 1283 // Items are usually expensive enough on the CPU that we want to 1284 // make them active whenever we can. 1285 return ItemActivity::Must; 1286 } 1287 return ItemActivity::No; 1288 } 1289 default: 1290 // TODO: handle other items? 1291 return ItemActivity::No; 1292 } 1293 } 1294 1295 // This does a pass over the display lists and will join the display items 1296 // into groups as well as paint them 1297 void Grouper::ConstructGroups(nsDisplayListBuilder* aDisplayListBuilder, 1298 WebRenderCommandBuilder* aCommandBuilder, 1299 wr::DisplayListBuilder& aBuilder, 1300 wr::IpcResourceUpdateQueue& aResources, 1301 DIGroup* aGroup, nsDisplayList* aList, 1302 nsDisplayItem* aWrappingItem, 1303 const StackingContextHelper& aSc) { 1304 RenderRootStateManager* manager = 1305 aCommandBuilder->mManager->GetRenderRootStateManager(); 1306 1307 nsDisplayList::iterator startOfCurrentGroup = aList->end(); 1308 DIGroup* currentGroup = aGroup; 1309 1310 // We need to track whether we have active siblings for mixed blend mode. 1311 bool encounteredActiveItem = false; 1312 bool isFirstGroup = true; 1313 // Track whether the item is the first (visible) of its group in which case 1314 // making it active won't add extra layers. 1315 bool isFirst = true; 1316 1317 for (auto it = aList->begin(); it != aList->end(); ++it) { 1318 nsDisplayItem* item = *it; 1319 MOZ_ASSERT(item); 1320 1321 if (item->HasHitTestInfo()) { 1322 // Accumulate the hit-test info flags. In cases where there are multiple 1323 // hittest-info display items with different flags, mHitInfo will have 1324 // the union of all those flags. If that is the case, we will 1325 // additionally set eIrregularArea (at the site that we use mHitInfo) 1326 // so that downstream consumers of this (primarily APZ) will know that 1327 // the exact shape of what gets hit with what is unknown. 1328 currentGroup->mHitInfo += item->GetHitTestInfo().Info(); 1329 } 1330 1331 if (startOfCurrentGroup == aList->end()) { 1332 startOfCurrentGroup = it; 1333 if (!isFirstGroup) { 1334 mClipManager.SwitchItem(aDisplayListBuilder, aWrappingItem); 1335 } 1336 } 1337 1338 bool isLast = it.HasNext(); 1339 1340 // WebRender's anti-aliasing approximation is not very good under 1341 // non-uniform scales. 1342 bool uniformlyScaled = 1343 fabs(aGroup->mScale.xScale - aGroup->mScale.yScale) < 0.1; 1344 1345 auto activity = IsItemProbablyActive( 1346 item, aBuilder, aResources, aSc, manager, mDisplayListBuilder, 1347 encounteredActiveItem, uniformlyScaled); 1348 auto threshold = 1349 isFirst || isLast ? ItemActivity::Could : ItemActivity::Should; 1350 1351 if (activity >= threshold) { 1352 encounteredActiveItem = true; 1353 // We're going to be starting a new group. 1354 RefPtr<WebRenderGroupData> groupData = 1355 aCommandBuilder->CreateOrRecycleWebRenderUserData<WebRenderGroupData>( 1356 item); 1357 1358 groupData->mFollowingGroup.mInvalidRect.SetEmpty(); 1359 1360 // Initialize groupData->mFollowingGroup with data from currentGroup. 1361 // We want to copy out this information before calling EndGroup because 1362 // EndGroup will set mLastVisibleRect depending on whether 1363 // we send something to WebRender. 1364 1365 // TODO: compute the group bounds post-grouping, so that they can be 1366 // tighter for just the sublist that made it into this group. 1367 // We want to ensure the tight bounds are still clipped by area 1368 // that we're building the display list for. 1369 if (groupData->mFollowingGroup.mScale != currentGroup->mScale || 1370 groupData->mFollowingGroup.mAppUnitsPerDevPixel != 1371 currentGroup->mAppUnitsPerDevPixel || 1372 groupData->mFollowingGroup.mResidualOffset != 1373 currentGroup->mResidualOffset) { 1374 if (groupData->mFollowingGroup.mAppUnitsPerDevPixel != 1375 currentGroup->mAppUnitsPerDevPixel) { 1376 GP("app unit change following: %d %d\n", 1377 groupData->mFollowingGroup.mAppUnitsPerDevPixel, 1378 currentGroup->mAppUnitsPerDevPixel); 1379 } 1380 // The group changed size 1381 GP("Inner group size change\n"); 1382 groupData->mFollowingGroup.ClearItems(); 1383 groupData->mFollowingGroup.ClearImageKey( 1384 aCommandBuilder->mManager->GetRenderRootStateManager()); 1385 } 1386 groupData->mFollowingGroup.mAppUnitsPerDevPixel = 1387 currentGroup->mAppUnitsPerDevPixel; 1388 groupData->mFollowingGroup.mLayerBounds = currentGroup->mLayerBounds; 1389 groupData->mFollowingGroup.mClippedImageBounds = 1390 currentGroup->mClippedImageBounds; 1391 groupData->mFollowingGroup.mScale = currentGroup->mScale; 1392 groupData->mFollowingGroup.mResidualOffset = 1393 currentGroup->mResidualOffset; 1394 groupData->mFollowingGroup.mVisibleRect = currentGroup->mVisibleRect; 1395 groupData->mFollowingGroup.mPreservedRect = 1396 groupData->mFollowingGroup.mVisibleRect.Intersect( 1397 groupData->mFollowingGroup.mLastVisibleRect); 1398 groupData->mFollowingGroup.mActualBounds = LayerIntRect(); 1399 groupData->mFollowingGroup.mHitTestBounds = LayerIntRect(); 1400 groupData->mFollowingGroup.mHitInfo = currentGroup->mHitInfo; 1401 1402 currentGroup->EndGroup(aCommandBuilder->mManager, aDisplayListBuilder, 1403 aBuilder, aResources, this, startOfCurrentGroup, 1404 it); 1405 1406 { 1407 auto spaceAndClipChain = 1408 mClipManager.SwitchItem(aDisplayListBuilder, item); 1409 wr::SpaceAndClipChainHelper saccHelper(aBuilder, spaceAndClipChain); 1410 bool hasHitTest = mHitTestInfoManager.ProcessItem(item, aBuilder, 1411 aDisplayListBuilder); 1412 // XXX - This is hacky. Some items have hit testing info on them but we 1413 // also have dedicated hit testing items, the flags of which apply to 1414 // the the group that contains them. We don't want layerization to 1415 // affect that so if the item didn't emit any hit testing then we still 1416 // push a hit test item if the previous group had some hit test flags 1417 // set. This is obviously not great. Hit testing should be independent 1418 // from how we layerize. 1419 if (!hasHitTest && 1420 currentGroup->mHitInfo != gfx::CompositorHitTestInvisibleToHit) { 1421 auto hitTestRect = item->GetBuildingRect(); 1422 if (!hitTestRect.IsEmpty()) { 1423 currentGroup->PushHitTest( 1424 aBuilder, LayoutDeviceRect::FromAppUnits( 1425 hitTestRect, currentGroup->mAppUnitsPerDevPixel)); 1426 } 1427 } 1428 1429 sIndent++; 1430 // Note: this call to CreateWebRenderCommands can recurse back into 1431 // this function. 1432 bool createdWRCommands = item->CreateWebRenderCommands( 1433 aBuilder, aResources, aSc, manager, mDisplayListBuilder); 1434 MOZ_RELEASE_ASSERT( 1435 createdWRCommands, 1436 "active transforms should always succeed at creating " 1437 "WebRender commands"); 1438 sIndent--; 1439 } 1440 1441 isFirstGroup = false; 1442 startOfCurrentGroup = aList->end(); 1443 currentGroup = &groupData->mFollowingGroup; 1444 isFirst = true; 1445 } else { // inactive item 1446 bool isInvisible = false; 1447 ConstructItemInsideInactive(aCommandBuilder, aBuilder, aResources, 1448 currentGroup, item, aSc, &isInvisible); 1449 if (!isInvisible) { 1450 // Invisible items don't count. 1451 isFirst = false; 1452 } 1453 } 1454 } 1455 1456 currentGroup->EndGroup(aCommandBuilder->mManager, aDisplayListBuilder, 1457 aBuilder, aResources, this, startOfCurrentGroup, 1458 aList->end()); 1459 } 1460 1461 // This does a pass over the display lists and will join the display items 1462 // into a single group. 1463 bool Grouper::ConstructGroupInsideInactive( 1464 WebRenderCommandBuilder* aCommandBuilder, wr::DisplayListBuilder& aBuilder, 1465 wr::IpcResourceUpdateQueue& aResources, DIGroup* aGroup, 1466 nsDisplayList* aList, const StackingContextHelper& aSc) { 1467 bool invalidated = false; 1468 for (nsDisplayItem* item : *aList) { 1469 if (item->HasHitTestInfo()) { 1470 // Accumulate the hit-test info flags. In cases where there are multiple 1471 // hittest-info display items with different flags, mHitInfo will have 1472 // the union of all those flags. If that is the case, we will 1473 // additionally set eIrregularArea (at the site that we use mHitInfo) 1474 // so that downstream consumers of this (primarily APZ) will know that 1475 // the exact shape of what gets hit with what is unknown. 1476 aGroup->mHitInfo += item->GetHitTestInfo().Info(); 1477 } 1478 1479 bool invisible = false; 1480 invalidated |= ConstructItemInsideInactive( 1481 aCommandBuilder, aBuilder, aResources, aGroup, item, aSc, &invisible); 1482 } 1483 return invalidated; 1484 } 1485 1486 bool Grouper::ConstructItemInsideInactive( 1487 WebRenderCommandBuilder* aCommandBuilder, wr::DisplayListBuilder& aBuilder, 1488 wr::IpcResourceUpdateQueue& aResources, DIGroup* aGroup, 1489 nsDisplayItem* aItem, const StackingContextHelper& aSc, 1490 bool* aOutIsInvisible) { 1491 nsDisplayList* children = aItem->GetChildren(); 1492 BlobItemData* data = GetBlobItemDataForGroup(aItem, aGroup); 1493 1494 /* mInvalid unfortunately persists across paints. Clear it so that if we don't 1495 * set it to 'true' we ensure that we're not using the value from the last 1496 * time that we painted */ 1497 data->mInvalid = false; 1498 data->mInvisible = aItem->IsInvisible(); 1499 *aOutIsInvisible = data->mInvisible; 1500 1501 // we compute the geometry change here because we have the transform around 1502 // still 1503 bool invalidated = aGroup->ComputeGeometryChange(aItem, data, mTransform, 1504 mDisplayListBuilder); 1505 1506 // Temporarily restrict the image bounds to the bounds of the container so 1507 // that clipped children within the container know about the clip. This 1508 // ensures that the bounds passed to FlushItem are contained in the bounds of 1509 // the clip so that we don't include items in the recording without including 1510 // their corresponding clipping items. 1511 auto oldClippedImageBounds = aGroup->mClippedImageBounds; 1512 aGroup->mClippedImageBounds = 1513 aGroup->mClippedImageBounds.Intersect(data->mRect); 1514 1515 if (aItem->GetType() == DisplayItemType::TYPE_FILTER) { 1516 // If ConstructGroupInsideInactive finds any change, we invalidate the 1517 // entire container item. This is needed because blob merging requires the 1518 // entire item to be within the invalid region. 1519 Matrix m = mTransform; 1520 mTransform = Matrix(); 1521 sIndent++; 1522 if (ConstructGroupInsideInactive(aCommandBuilder, aBuilder, aResources, 1523 aGroup, children, aSc)) { 1524 data->mInvalid = true; 1525 aGroup->InvalidateRect(data->mRect); 1526 invalidated = true; 1527 } 1528 sIndent--; 1529 mTransform = m; 1530 } else if (aItem->GetType() == DisplayItemType::TYPE_TRANSFORM) { 1531 Matrix m = mTransform; 1532 nsDisplayTransform* transformItem = static_cast<nsDisplayTransform*>(aItem); 1533 const Matrix4x4Flagged& t = transformItem->GetTransform(); 1534 Matrix t2d; 1535 bool is2D = t.CanDraw2D(&t2d); 1536 if (!is2D) { 1537 // If ConstructGroupInsideInactive finds any change, we invalidate the 1538 // entire container item. This is needed because blob merging requires the 1539 // entire item to be within the invalid region. 1540 mTransform = Matrix(); 1541 sIndent++; 1542 if (ConstructGroupInsideInactive(aCommandBuilder, aBuilder, aResources, 1543 aGroup, children, aSc)) { 1544 data->mInvalid = true; 1545 aGroup->InvalidateRect(data->mRect); 1546 invalidated = true; 1547 } 1548 sIndent--; 1549 } else { 1550 GP("t2d: %f %f\n", t2d._31, t2d._32); 1551 mTransform.PreMultiply(t2d); 1552 GP("mTransform: %f %f\n", mTransform._31, mTransform._32); 1553 sIndent++; 1554 invalidated |= ConstructGroupInsideInactive( 1555 aCommandBuilder, aBuilder, aResources, aGroup, children, aSc); 1556 sIndent--; 1557 } 1558 mTransform = m; 1559 } else if (children) { 1560 sIndent++; 1561 invalidated |= ConstructGroupInsideInactive( 1562 aCommandBuilder, aBuilder, aResources, aGroup, children, aSc); 1563 sIndent--; 1564 } 1565 1566 GP("Including %s of %d\n", aItem->Name(), aGroup->mDisplayItems.Count()); 1567 aGroup->mClippedImageBounds = oldClippedImageBounds; 1568 return invalidated; 1569 } 1570 1571 /* This is just a copy of nsRect::ScaleToOutsidePixels with an offset added in. 1572 * The offset is applied just before the rounding. It's in the scaled space. */ 1573 static mozilla::LayerIntRect ScaleToOutsidePixelsOffset( 1574 nsRect aRect, float aXScale, float aYScale, nscoord aAppUnitsPerPixel, 1575 LayerPoint aOffset) { 1576 mozilla::LayerIntRect rect; 1577 rect.SetNonEmptyBox( 1578 NSToIntFloor(NSAppUnitsToFloatPixels(aRect.x, float(aAppUnitsPerPixel)) * 1579 aXScale + 1580 aOffset.x), 1581 NSToIntFloor(NSAppUnitsToFloatPixels(aRect.y, float(aAppUnitsPerPixel)) * 1582 aYScale + 1583 aOffset.y), 1584 NSToIntCeil( 1585 NSAppUnitsToFloatPixels(aRect.XMost(), float(aAppUnitsPerPixel)) * 1586 aXScale + 1587 aOffset.x), 1588 NSToIntCeil( 1589 NSAppUnitsToFloatPixels(aRect.YMost(), float(aAppUnitsPerPixel)) * 1590 aYScale + 1591 aOffset.y)); 1592 return rect; 1593 } 1594 1595 /* This function is the same as the above except that it rounds to the 1596 * nearest instead of rounding out. We use it for attempting to compute the 1597 * actual pixel bounds of opaque items */ 1598 static mozilla::gfx::IntRect ScaleToNearestPixelsOffset( 1599 nsRect aRect, float aXScale, float aYScale, nscoord aAppUnitsPerPixel, 1600 LayerPoint aOffset) { 1601 mozilla::gfx::IntRect rect; 1602 rect.SetNonEmptyBox( 1603 NSToIntFloor(NSAppUnitsToFloatPixels(aRect.x, float(aAppUnitsPerPixel)) * 1604 aXScale + 1605 aOffset.x + 0.5), 1606 NSToIntFloor(NSAppUnitsToFloatPixels(aRect.y, float(aAppUnitsPerPixel)) * 1607 aYScale + 1608 aOffset.y + 0.5), 1609 NSToIntFloor( 1610 NSAppUnitsToFloatPixels(aRect.XMost(), float(aAppUnitsPerPixel)) * 1611 aXScale + 1612 aOffset.x + 0.5), 1613 NSToIntFloor( 1614 NSAppUnitsToFloatPixels(aRect.YMost(), float(aAppUnitsPerPixel)) * 1615 aYScale + 1616 aOffset.y + 0.5)); 1617 return rect; 1618 } 1619 1620 RenderRootStateManager* WebRenderCommandBuilder::GetRenderRootStateManager() { 1621 return mManager->GetRenderRootStateManager(); 1622 } 1623 1624 void WebRenderCommandBuilder::DoGroupingForDisplayList( 1625 nsDisplayList* aList, nsDisplayItem* aWrappingItem, 1626 nsDisplayListBuilder* aDisplayListBuilder, const StackingContextHelper& aSc, 1627 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources) { 1628 if (!aList->GetBottom()) { 1629 return; 1630 } 1631 1632 GP("DoGroupingForDisplayList\n"); 1633 1634 mClipManager.BeginList(aSc); 1635 mHitTestInfoManager.Reset(); 1636 Grouper g(mClipManager); 1637 1638 int32_t appUnitsPerDevPixel = 1639 aWrappingItem->Frame()->PresContext()->AppUnitsPerDevPixel(); 1640 1641 g.mDisplayListBuilder = aDisplayListBuilder; 1642 RefPtr<WebRenderGroupData> groupData = 1643 CreateOrRecycleWebRenderUserData<WebRenderGroupData>(aWrappingItem); 1644 1645 nsRect groupBounds = 1646 aWrappingItem->GetUntransformedBounds(aDisplayListBuilder); 1647 DIGroup& group = groupData->mSubGroup; 1648 1649 auto scale = aSc.GetInheritedScale(); 1650 GP("Inherited scale %f %f\n", scale.xScale, scale.yScale); 1651 1652 auto trans = 1653 ViewAs<LayerPixel>(aSc.GetSnappingSurfaceTransform().GetTranslation()); 1654 auto snappedTrans = LayerIntPoint::Floor(trans); 1655 LayerPoint residualOffset = trans - snappedTrans; 1656 1657 auto layerBounds = 1658 ScaleToOutsidePixelsOffset(groupBounds, scale.xScale, scale.yScale, 1659 appUnitsPerDevPixel, residualOffset); 1660 1661 const nsRect& untransformedPaintRect = 1662 aWrappingItem->GetUntransformedPaintRect(); 1663 1664 auto visibleRect = ScaleToOutsidePixelsOffset( 1665 untransformedPaintRect, scale.xScale, scale.yScale, 1666 appUnitsPerDevPixel, residualOffset) 1667 .Intersect(layerBounds); 1668 1669 GP("LayerBounds: %d %d %d %d\n", layerBounds.x, layerBounds.y, 1670 layerBounds.width, layerBounds.height); 1671 GP("VisibleRect: %d %d %d %d\n", visibleRect.x, visibleRect.y, 1672 visibleRect.width, visibleRect.height); 1673 1674 GP("Inherited scale %f %f\n", scale.xScale, scale.yScale); 1675 1676 group.mInvalidRect.SetEmpty(); 1677 if (group.mAppUnitsPerDevPixel != appUnitsPerDevPixel || 1678 group.mScale != scale || group.mResidualOffset != residualOffset) { 1679 GP("Property change. Deleting blob\n"); 1680 1681 if (group.mAppUnitsPerDevPixel != appUnitsPerDevPixel) { 1682 GP(" App unit change %d -> %d\n", group.mAppUnitsPerDevPixel, 1683 appUnitsPerDevPixel); 1684 } 1685 1686 if (group.mScale != scale) { 1687 GP(" Scale %f %f -> %f %f\n", group.mScale.xScale, group.mScale.yScale, 1688 scale.xScale, scale.yScale); 1689 } 1690 1691 if (group.mResidualOffset != residualOffset) { 1692 GP(" Residual Offset %f %f -> %f %f\n", group.mResidualOffset.x.value, 1693 group.mResidualOffset.y.value, residualOffset.x.value, 1694 residualOffset.y.value); 1695 } 1696 1697 group.ClearItems(); 1698 group.ClearImageKey(mManager->GetRenderRootStateManager()); 1699 } 1700 1701 ScrollableLayerGuid::ViewID scrollId = ScrollableLayerGuid::NULL_SCROLL_ID; 1702 if (const ActiveScrolledRoot* asr = aWrappingItem->GetActiveScrolledRoot()) { 1703 scrollId = asr->GetNearestScrollASRViewId(); 1704 } 1705 1706 g.mAppUnitsPerDevPixel = appUnitsPerDevPixel; 1707 group.mResidualOffset = residualOffset; 1708 group.mLayerBounds = layerBounds; 1709 group.mVisibleRect = visibleRect; 1710 group.mActualBounds = LayerIntRect(); 1711 group.mHitTestBounds = LayerIntRect(); 1712 group.mPreservedRect = group.mVisibleRect.Intersect(group.mLastVisibleRect); 1713 group.mAppUnitsPerDevPixel = appUnitsPerDevPixel; 1714 group.mClippedImageBounds = layerBounds; 1715 1716 g.mTransform = 1717 Matrix::Scaling(scale).PostTranslate(residualOffset.x, residualOffset.y); 1718 group.mScale = scale; 1719 group.mScrollId = scrollId; 1720 g.ConstructGroups(aDisplayListBuilder, this, aBuilder, aResources, &group, 1721 aList, aWrappingItem, aSc); 1722 mClipManager.EndList(aSc); 1723 } 1724 1725 WebRenderCommandBuilder::WebRenderCommandBuilder( 1726 WebRenderLayerManager* aManager) 1727 : mManager(aManager), 1728 mLastAsr(nullptr), 1729 mBuilderDumpIndex(0), 1730 mDumpIndent(0), 1731 mApzEnabled(true), 1732 mComputingOpaqueRegion(XRE_IsParentProcess()), 1733 mDoGrouping(false), 1734 mContainsSVGGroup(false) {} 1735 1736 void WebRenderCommandBuilder::Destroy() { 1737 mLastCanvasDatas.Clear(); 1738 ClearCachedResources(); 1739 } 1740 1741 void WebRenderCommandBuilder::EmptyTransaction() { 1742 // We need to update canvases that might have changed. 1743 for (RefPtr<WebRenderCanvasData> canvasData : mLastCanvasDatas) { 1744 WebRenderCanvasRendererAsync* canvas = canvasData->GetCanvasRenderer(); 1745 if (canvas) { 1746 canvas->UpdateCompositableClientForEmptyTransaction(); 1747 } 1748 } 1749 } 1750 1751 bool WebRenderCommandBuilder::NeedsEmptyTransaction() { 1752 return !mLastCanvasDatas.IsEmpty(); 1753 } 1754 1755 void WebRenderCommandBuilder::BuildWebRenderCommands( 1756 wr::DisplayListBuilder& aBuilder, 1757 wr::IpcResourceUpdateQueue& aResourceUpdates, nsDisplayList* aDisplayList, 1758 nsDisplayListBuilder* aDisplayListBuilder, WebRenderScrollData& aScrollData, 1759 WrFiltersHolder&& aFilters) { 1760 AUTO_PROFILER_LABEL_CATEGORY_PAIR(GRAPHICS_WRDisplayList); 1761 1762 StackingContextHelper sc; 1763 aScrollData = WebRenderScrollData(mManager, aDisplayListBuilder); 1764 MOZ_ASSERT(mLayerScrollData.empty()); 1765 mClipManager.BeginBuild(mManager, aBuilder); 1766 mHitTestInfoManager.Reset(); 1767 1768 mBuilderDumpIndex = 0; 1769 mLastCanvasDatas.Clear(); 1770 mLastAsr = nullptr; 1771 mContainsSVGGroup = false; 1772 MOZ_ASSERT(mDumpIndent == 0); 1773 1774 { 1775 wr::StackingContextParams params; 1776 params.mRootReferenceFrame = aDisplayListBuilder->RootReferenceFrame(); 1777 params.mFilters = std::move(aFilters.filters); 1778 params.mFilterDatas = std::move(aFilters.filter_datas); 1779 params.clip = 1780 wr::WrStackingContextClip::ClipChain(aBuilder.CurrentClipChainId()); 1781 1782 StackingContextHelper pageRootSc(sc, nullptr, nullptr, nullptr, aBuilder, 1783 params); 1784 if (ShouldDumpDisplayList(aDisplayListBuilder)) { 1785 mBuilderDumpIndex = 1786 aBuilder.Dump(mDumpIndent + 1, Some(mBuilderDumpIndex), Nothing()); 1787 } 1788 CreateWebRenderCommandsFromDisplayList(aDisplayList, nullptr, 1789 aDisplayListBuilder, pageRootSc, 1790 aBuilder, aResourceUpdates); 1791 } 1792 1793 // Make a "root" layer data that has everything else as descendants 1794 mLayerScrollData.emplace_back(); 1795 mLayerScrollData.back().InitializeRoot(mLayerScrollData.size() - 1); 1796 auto callback = 1797 [&aScrollData](ScrollableLayerGuid::ViewID aScrollId) -> bool { 1798 return aScrollData.HasMetadataFor(aScrollId).isSome(); 1799 }; 1800 Maybe<ScrollMetadata> rootMetadata = 1801 nsLayoutUtils::GetRootMetadata(aDisplayListBuilder, mManager, callback); 1802 if (rootMetadata) { 1803 // Put the fallback root metadata on the rootmost layer that is 1804 // a matching async zoom container, or the root layer that we just 1805 // created above. 1806 size_t rootMetadataTarget = mLayerScrollData.size() - 1; 1807 for (size_t i = rootMetadataTarget; i > 0; i--) { 1808 if (auto zoomContainerId = 1809 mLayerScrollData[i - 1].GetAsyncZoomContainerId()) { 1810 if (*zoomContainerId == rootMetadata->GetMetrics().GetScrollId()) { 1811 rootMetadataTarget = i - 1; 1812 break; 1813 } 1814 } 1815 } 1816 mLayerScrollData[rootMetadataTarget].AppendScrollMetadata( 1817 aScrollData, rootMetadata.ref()); 1818 } 1819 1820 // Append the WebRenderLayerScrollData items into WebRenderScrollData 1821 // in reverse order, from topmost to bottommost. This is in keeping with 1822 // the semantics of WebRenderScrollData. 1823 for (auto it = mLayerScrollData.rbegin(); it != mLayerScrollData.rend(); 1824 it++) { 1825 aScrollData.AddLayerData(std::move(*it)); 1826 } 1827 mLayerScrollData.clear(); 1828 mClipManager.EndBuild(); 1829 1830 // Remove the user data those are not displayed on the screen and 1831 // also reset the data to unused for next transaction. 1832 RemoveUnusedAndResetWebRenderUserData(); 1833 } 1834 1835 bool WebRenderCommandBuilder::ShouldDumpDisplayList( 1836 nsDisplayListBuilder* aBuilder) { 1837 return aBuilder && aBuilder->IsInActiveDocShell() && 1838 ((XRE_IsParentProcess() && 1839 StaticPrefs::gfx_webrender_debug_dl_dump_parent()) || 1840 (XRE_IsContentProcess() && 1841 StaticPrefs::gfx_webrender_debug_dl_dump_content())); 1842 } 1843 1844 void WebRenderCommandBuilder::CreateWebRenderCommands( 1845 nsDisplayItem* aItem, mozilla::wr::DisplayListBuilder& aBuilder, 1846 mozilla::wr::IpcResourceUpdateQueue& aResources, 1847 const StackingContextHelper& aSc, 1848 nsDisplayListBuilder* aDisplayListBuilder) { 1849 mHitTestInfoManager.ProcessItem(aItem, aBuilder, aDisplayListBuilder); 1850 if (aItem->GetType() == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) { 1851 // The hit test information was processed above. 1852 return; 1853 } 1854 1855 auto* item = aItem->AsPaintedDisplayItem(); 1856 MOZ_RELEASE_ASSERT(item, "Tried to paint item that cannot be painted"); 1857 1858 if (aBuilder.ReuseItem(item)) { 1859 // No further processing should be needed, since the item was reused. 1860 return; 1861 } 1862 1863 RenderRootStateManager* manager = mManager->GetRenderRootStateManager(); 1864 1865 // Note: this call to CreateWebRenderCommands can recurse back into 1866 // this function if the |item| is a wrapper for a sublist. 1867 const bool createdWRCommands = aItem->CreateWebRenderCommands( 1868 aBuilder, aResources, aSc, manager, aDisplayListBuilder); 1869 1870 if (!createdWRCommands) { 1871 PushItemAsImage(aItem, aBuilder, aResources, aSc, aDisplayListBuilder); 1872 } 1873 } 1874 1875 // A helper struct to store information needed when creating a new 1876 // WebRenderLayerScrollData in CreateWebRenderCommandsFromDisplayList(). 1877 // This information is gathered before the recursion, and then used to 1878 // emit the new layer after the recursion. 1879 struct NewLayerData { 1880 size_t mLayerCountBeforeRecursing = 0; 1881 const ActiveScrolledRoot* mStopAtAsr = nullptr; 1882 1883 // Information pertaining to the deferred transform. 1884 nsDisplayTransform* mDeferredItem = nullptr; 1885 ScrollableLayerGuid::ViewID mDeferredId = ScrollableLayerGuid::NULL_SCROLL_ID; 1886 bool mTransformShouldGetOwnLayer = false; 1887 1888 void ComputeDeferredTransformInfo(const StackingContextHelper& aSc, 1889 nsDisplayItem* aItem) { 1890 // See the comments on StackingContextHelper::mDeferredTransformItem 1891 // for an overview of what deferred transforms are. 1892 // In the case where we deferred a transform, but have a child display 1893 // item with a different ASR than the deferred transform item, we cannot 1894 // put the transform on the WebRenderLayerScrollData item for the child. 1895 // We cannot do this because it will not conform to APZ's expectations 1896 // with respect to how the APZ tree ends up structured. In particular, 1897 // the GetTransformToThis() for the child APZ (which is created for the 1898 // child item's ASR) will not include the transform when we actually do 1899 // want it to. 1900 // When we run into this scenario, we solve it by creating two 1901 // WebRenderLayerScrollData items; one that just holds the transform, 1902 // that we deferred, and a child WebRenderLayerScrollData item that 1903 // holds the scroll metadata for the child's ASR. 1904 mDeferredItem = aSc.GetDeferredTransformItem(); 1905 if (mDeferredItem) { 1906 // It's possible the transform's ASR is not only an ancestor of 1907 // the item's ASR, but an ancestor of stopAtAsr. In such cases, 1908 // don't use the transform at all at this level (it would be 1909 // scrolled by stopAtAsr which is incorrect). The transform will 1910 // instead be emitted as part of the ancestor WebRenderLayerScrollData 1911 // node (the one with stopAtAsr as its item ASR), or one of its 1912 // ancetors in turn. 1913 if (ActiveScrolledRoot::IsProperAncestor( 1914 mDeferredItem->GetActiveScrolledRoot(), mStopAtAsr)) { 1915 mDeferredItem = nullptr; 1916 } 1917 } 1918 if (mDeferredItem) { 1919 if (const auto* asr = mDeferredItem->GetActiveScrolledRoot()) { 1920 mDeferredId = asr->GetNearestScrollASRViewId(); 1921 } 1922 if (mDeferredItem->GetActiveScrolledRoot() != 1923 aItem->GetActiveScrolledRoot()) { 1924 mTransformShouldGetOwnLayer = true; 1925 } else if (aItem->GetType() == DisplayItemType::TYPE_SCROLL_INFO_LAYER) { 1926 // A scroll info layer has its own scroll id that's not reflected 1927 // in item->GetActiveScrolledRoot(), but will be added to the 1928 // WebRenderLayerScrollData node, so it needs to be treated as 1929 // having a distinct ASR from the deferred transform item. 1930 mTransformShouldGetOwnLayer = true; 1931 } 1932 } 1933 } 1934 }; 1935 1936 static Maybe<nsPoint> AllowComputingOpaqueRegionAcross( 1937 nsDisplayItem* aWrappingItem, nsDisplayListBuilder* aBuilder) { 1938 MOZ_ASSERT(aWrappingItem); 1939 if (aWrappingItem->GetType() != DisplayItemType::TYPE_TRANSFORM) { 1940 return {}; 1941 } 1942 auto* transformItem = static_cast<nsDisplayTransform*>(aWrappingItem); 1943 if (transformItem->MayBeAnimated(aBuilder)) { 1944 return {}; 1945 } 1946 const auto& transform = transformItem->GetTransform(); 1947 if (!transform.Is2D()) { 1948 return {}; 1949 } 1950 const auto transform2d = transform.GetMatrix().As2D(); 1951 if (!transform2d.IsTranslation()) { 1952 return {}; 1953 } 1954 return Some(LayoutDevicePoint::ToAppUnits( 1955 LayoutDevicePoint::FromUnknownPoint(transform2d.GetTranslation()), 1956 transformItem->Frame()->PresContext()->AppUnitsPerDevPixel())); 1957 } 1958 1959 struct MOZ_STACK_CLASS WebRenderCommandBuilder::AutoOpaqueRegionStateTracker { 1960 WebRenderCommandBuilder& mBuilder; 1961 const bool mWasComputingOpaqueRegion; 1962 bool mThroughWrapper = false; 1963 1964 AutoOpaqueRegionStateTracker(WebRenderCommandBuilder& aBuilder, 1965 nsDisplayListBuilder* aDlBuilder, 1966 nsDisplayItem* aWrappingItem) 1967 : mBuilder(aBuilder), 1968 mWasComputingOpaqueRegion(aBuilder.mComputingOpaqueRegion) { 1969 if (!mBuilder.mComputingOpaqueRegion || !aWrappingItem) { 1970 return; 1971 } 1972 Maybe<nsPoint> offset = 1973 AllowComputingOpaqueRegionAcross(aWrappingItem, aDlBuilder); 1974 if (!offset) { 1975 aBuilder.mComputingOpaqueRegion = false; 1976 } else { 1977 mThroughWrapper = true; 1978 aBuilder.mOpaqueRegionWrappers.AppendElement( 1979 std::make_pair(aWrappingItem, *offset)); 1980 } 1981 } 1982 1983 ~AutoOpaqueRegionStateTracker() { 1984 if (!mWasComputingOpaqueRegion) { 1985 return; 1986 } 1987 if (mThroughWrapper) { 1988 mBuilder.mOpaqueRegionWrappers.RemoveLastElement(); 1989 } 1990 mBuilder.mComputingOpaqueRegion = mWasComputingOpaqueRegion; 1991 } 1992 }; 1993 1994 void WebRenderCommandBuilder::CreateWebRenderCommandsFromDisplayList( 1995 nsDisplayList* aDisplayList, nsDisplayItem* aWrappingItem, 1996 nsDisplayListBuilder* aDisplayListBuilder, const StackingContextHelper& aSc, 1997 wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, 1998 bool aNewClipList) { 1999 if (mDoGrouping) { 2000 MOZ_RELEASE_ASSERT( 2001 aWrappingItem, 2002 "Only the root list should have a null wrapping item, and mDoGrouping " 2003 "should never be true for the root list."); 2004 GP("actually entering the grouping code\n"); 2005 DoGroupingForDisplayList(aDisplayList, aWrappingItem, aDisplayListBuilder, 2006 aSc, aBuilder, aResources); 2007 return; 2008 } 2009 2010 const bool dumpEnabled = ShouldDumpDisplayList(aDisplayListBuilder); 2011 if (dumpEnabled) { 2012 // If we're inside a nested display list, print the WR DL items from the 2013 // wrapper item before we start processing the nested items. 2014 mBuilderDumpIndex = 2015 aBuilder.Dump(mDumpIndent + 1, Some(mBuilderDumpIndex), Nothing()); 2016 } 2017 2018 FlattenedDisplayListIterator iter(aDisplayListBuilder, aDisplayList); 2019 if (!iter.HasNext()) { 2020 return; 2021 } 2022 2023 mDumpIndent++; 2024 if (aNewClipList) { 2025 mClipManager.BeginList(aSc); 2026 } 2027 2028 AutoOpaqueRegionStateTracker tracker(*this, aDisplayListBuilder, 2029 aWrappingItem); 2030 do { 2031 nsDisplayItem* item = iter.GetNextItem(); 2032 2033 const DisplayItemType itemType = item->GetType(); 2034 2035 // If this is a new (not retained/reused) item, then we need to disable 2036 // the display item cache for descendants, since it's possible that some of 2037 // them got cached with a flattened opacity values., which may no longer be 2038 // applied. 2039 Maybe<AutoDisplayItemCacheSuppressor> cacheSuppressor; 2040 2041 if (itemType == DisplayItemType::TYPE_OPACITY) { 2042 nsDisplayOpacity* opacity = static_cast<nsDisplayOpacity*>(item); 2043 2044 if (!opacity->IsReused()) { 2045 cacheSuppressor.emplace(aBuilder.GetDisplayItemCache()); 2046 } 2047 2048 if (opacity->CanApplyOpacityToChildren( 2049 mManager->GetRenderRootStateManager()->LayerManager(), 2050 aDisplayListBuilder, aBuilder.GetInheritedOpacity())) { 2051 // If all our children support handling the opacity directly, then push 2052 // the opacity and clip onto the builder and skip creating a stacking 2053 // context. 2054 float oldOpacity = aBuilder.GetInheritedOpacity(); 2055 const DisplayItemClipChain* oldClip = aBuilder.GetInheritedClipChain(); 2056 aBuilder.SetInheritedOpacity(oldOpacity * opacity->GetOpacity()); 2057 aBuilder.PushInheritedClipChain(aDisplayListBuilder, 2058 opacity->GetClipChain()); 2059 2060 CreateWebRenderCommandsFromDisplayList(opacity->GetChildren(), item, 2061 aDisplayListBuilder, aSc, 2062 aBuilder, aResources, false); 2063 2064 aBuilder.SetInheritedOpacity(oldOpacity); 2065 aBuilder.SetInheritedClipChain(oldClip); 2066 continue; 2067 } 2068 } 2069 2070 // If this is an unscrolled background item, in the root display list 2071 // for the parent process, consider doing opaque checks. 2072 if (mComputingOpaqueRegion && 2073 (itemType == DisplayItemType::TYPE_BACKGROUND_COLOR || 2074 itemType == DisplayItemType::TYPE_SOLID_COLOR || 2075 itemType == DisplayItemType::TYPE_BACKGROUND) && 2076 !item->GetActiveScrolledRoot()) { 2077 bool snap; 2078 nsRegion opaque = item->GetOpaqueRegion(aDisplayListBuilder, &snap); 2079 if (opaque.GetNumRects() == 1) { 2080 nsRect result = 2081 item->GetClip().ApproximateIntersectInward(opaque.GetBounds()); 2082 if (!result.IsEmpty()) { 2083 for (auto& [item, offset] : Reversed(mOpaqueRegionWrappers)) { 2084 result = 2085 item->GetClip().ApproximateIntersectInward(result + offset); 2086 if (result.IsEmpty()) { 2087 break; 2088 } 2089 } 2090 if (!result.IsEmpty()) { 2091 aDisplayListBuilder->AddWindowOpaqueRegion(item->Frame(), result); 2092 } 2093 } 2094 } 2095 } 2096 2097 AutoRestore<bool> restoreApzEnabled(mApzEnabled); 2098 mApzEnabled = mApzEnabled && mManager->AsyncPanZoomEnabled() && 2099 itemType != DisplayItemType::TYPE_VT_CAPTURE; 2100 2101 Maybe<NewLayerData> newLayerData; 2102 if (mApzEnabled) { 2103 // For some types of display items we want to force a new 2104 // WebRenderLayerScrollData object, to ensure we preserve the APZ-relevant 2105 // data that is in the display item. 2106 if (item->UpdateScrollData(nullptr, nullptr)) { 2107 newLayerData = Some(NewLayerData()); 2108 } 2109 2110 // Anytime the ASR changes we also want to force a new layer data because 2111 // the stack of scroll metadata is going to be different for this 2112 // display item than previously, so we can't squash the display items 2113 // into the same "layer". 2114 const ActiveScrolledRoot* asr = item->GetActiveScrolledRoot(); 2115 if (asr != mLastAsr) { 2116 mLastAsr = asr; 2117 newLayerData = Some(NewLayerData()); 2118 } 2119 2120 // Refer to the comment on StackingContextHelper::mDeferredTransformItem 2121 // for an overview of what this is about. This bit of code applies to the 2122 // case where we are deferring a transform item, and we then need to defer 2123 // another transform with a different ASR. In such a case we cannot just 2124 // merge the deferred transforms, but need to force a new 2125 // WebRenderLayerScrollData item to flush the old deferred transform, so 2126 // that we can then start deferring the new one. 2127 if (!newLayerData && item->CreatesStackingContextHelper() && 2128 aSc.GetDeferredTransformItem() && 2129 aSc.GetDeferredTransformItem()->GetActiveScrolledRoot() != asr) { 2130 newLayerData = Some(NewLayerData()); 2131 } 2132 2133 // If we're going to create a new layer data for this item, stash the 2134 // ASR so that if we recurse into a sublist they will know where to stop 2135 // walking up their ASR chain when building scroll metadata. 2136 if (newLayerData) { 2137 newLayerData->mLayerCountBeforeRecursing = mLayerScrollData.size(); 2138 newLayerData->mStopAtAsr = 2139 mAsrStack.empty() ? nullptr : mAsrStack.back(); 2140 newLayerData->mStopAtAsr = ActiveScrolledRoot::LowestCommonAncestor( 2141 asr, newLayerData->mStopAtAsr); 2142 newLayerData->ComputeDeferredTransformInfo(aSc, item); 2143 2144 // Our children's |stopAtAsr| must not be an ancestor of our 2145 // |stopAtAsr|, otherwise we could get cyclic scroll metadata 2146 // annotations. 2147 MOZ_ASSERT( 2148 ActiveScrolledRoot::IsAncestor(newLayerData->mStopAtAsr, asr)); 2149 const ActiveScrolledRoot* stopAtAsrForChildren = asr; 2150 // Additionally, while unusual and probably indicative of a poorly 2151 // behaved display list, it's possible to have a deferred transform item 2152 // which we will emit as its own layer on the way out of the recursion, 2153 // whose ASR (let's call it T) is a *descendant* of the current item's 2154 // ASR. In such cases, make sure our children have stopAtAsr=T, 2155 // otherwise ASRs in the range [T, asr) may be emitted in duplicate, 2156 // leading again to cylic scroll metadata annotations. 2157 if (newLayerData->mTransformShouldGetOwnLayer) { 2158 stopAtAsrForChildren = ActiveScrolledRoot::PickDescendant( 2159 stopAtAsrForChildren, 2160 newLayerData->mDeferredItem->GetActiveScrolledRoot()); 2161 } 2162 mAsrStack.push_back(stopAtAsrForChildren); 2163 2164 // If we're going to emit a deferred transform onto this layer, 2165 // clear the deferred transform from the StackingContextHelper 2166 // while we are building the subtree of descendant layers. 2167 // This ensures that the deferred transform is not applied in 2168 // duplicate to any of our descendant layers. 2169 if (newLayerData->mDeferredItem) { 2170 aSc.ClearDeferredTransformItem(); 2171 } 2172 } 2173 } 2174 2175 // This is where we emulate the clip/scroll stack that was previously 2176 // implemented on the WR display list side. 2177 auto spaceAndClipChain = mClipManager.SwitchItem(aDisplayListBuilder, item); 2178 wr::SpaceAndClipChainHelper saccHelper(aBuilder, spaceAndClipChain); 2179 2180 { // scope restoreDoGrouping 2181 AutoRestore<bool> restoreDoGrouping(mDoGrouping); 2182 if (itemType == DisplayItemType::TYPE_SVG_WRAPPER) { 2183 // Inside an <svg>, all display items that are not LAYER_ACTIVE wrapper 2184 // display items (like animated transforms / opacity) share the same 2185 // animated geometry root, so we can combine subsequent items of that 2186 // type into the same image. 2187 mContainsSVGGroup = mDoGrouping = true; 2188 GP("attempting to enter the grouping code\n"); 2189 } 2190 2191 if (dumpEnabled) { 2192 std::stringstream ss; 2193 nsIFrame::PrintDisplayItem(aDisplayListBuilder, item, ss, 2194 static_cast<uint32_t>(mDumpIndent)); 2195 printf_stderr("%s", ss.str().c_str()); 2196 } 2197 2198 CreateWebRenderCommands(item, aBuilder, aResources, aSc, 2199 aDisplayListBuilder); 2200 2201 if (dumpEnabled) { 2202 mBuilderDumpIndex = 2203 aBuilder.Dump(mDumpIndent + 1, Some(mBuilderDumpIndex), Nothing()); 2204 } 2205 } 2206 2207 if (newLayerData) { 2208 // Pop the thing we pushed before the recursion, so the topmost item on 2209 // the stack is enclosing display item's ASR (or the stack is empty) 2210 mAsrStack.pop_back(); 2211 2212 if (newLayerData->mDeferredItem) { 2213 aSc.RestoreDeferredTransformItem(newLayerData->mDeferredItem); 2214 } 2215 2216 const ActiveScrolledRoot* stopAtAsr = newLayerData->mStopAtAsr; 2217 2218 int32_t descendants = 2219 mLayerScrollData.size() - newLayerData->mLayerCountBeforeRecursing; 2220 2221 nsDisplayTransform* deferred = newLayerData->mDeferredItem; 2222 ScrollableLayerGuid::ViewID deferredId = newLayerData->mDeferredId; 2223 2224 if (newLayerData->mTransformShouldGetOwnLayer) { 2225 // This creates the child WebRenderLayerScrollData for |item|, but 2226 // omits the transform (hence the Nothing() as the last argument to 2227 // Initialize(...)). We also need to make sure that the ASR from 2228 // the deferred transform item is not on this node, so we use that 2229 // ASR as the "stop at" ASR for this WebRenderLayerScrollData. 2230 mLayerScrollData.emplace_back(); 2231 mLayerScrollData.back().Initialize( 2232 mManager->GetScrollData(), item, descendants, 2233 deferred->GetActiveScrolledRoot(), Nothing(), 2234 ScrollableLayerGuid::NULL_SCROLL_ID); 2235 2236 // The above WebRenderLayerScrollData will also be a descendant of 2237 // the transform-holding WebRenderLayerScrollData we create below. 2238 descendants++; 2239 2240 // This creates the WebRenderLayerScrollData for the deferred 2241 // transform item. This holds the transform matrix and the remaining 2242 // ASRs needed to complete the ASR chain (i.e. the ones from the 2243 // stopAtAsr down to the deferred transform item's ASR, which must be 2244 // "between" stopAtAsr and |item|'s ASR in the ASR tree). 2245 mLayerScrollData.emplace_back(); 2246 mLayerScrollData.back().Initialize( 2247 mManager->GetScrollData(), deferred, descendants, stopAtAsr, 2248 aSc.GetDeferredTransformMatrix(), deferredId); 2249 } else { 2250 // This is the "simple" case where we don't need to create two 2251 // WebRenderLayerScrollData items; we can just create one that also 2252 // holds the deferred transform matrix, if any. 2253 mLayerScrollData.emplace_back(); 2254 mLayerScrollData.back().Initialize( 2255 mManager->GetScrollData(), item, descendants, stopAtAsr, 2256 deferred ? aSc.GetDeferredTransformMatrix() : Nothing(), 2257 deferredId); 2258 } 2259 } 2260 } while (iter.HasNext()); 2261 2262 mDumpIndent--; 2263 if (aNewClipList) { 2264 mClipManager.EndList(aSc); 2265 } 2266 } 2267 2268 void WebRenderCommandBuilder::PushOverrideForASR( 2269 const ActiveScrolledRoot* aASR, const wr::WrSpatialId& aSpatialId) { 2270 mClipManager.PushOverrideForASR(aASR, aSpatialId); 2271 } 2272 2273 void WebRenderCommandBuilder::PopOverrideForASR( 2274 const ActiveScrolledRoot* aASR) { 2275 mClipManager.PopOverrideForASR(aASR); 2276 } 2277 2278 static wr::WrRotation ToWrRotation(VideoRotation aRotation) { 2279 switch (aRotation) { 2280 case VideoRotation::kDegree_0: 2281 return wr::WrRotation::Degree0; 2282 case VideoRotation::kDegree_90: 2283 return wr::WrRotation::Degree90; 2284 case VideoRotation::kDegree_180: 2285 return wr::WrRotation::Degree180; 2286 case VideoRotation::kDegree_270: 2287 return wr::WrRotation::Degree270; 2288 } 2289 return wr::WrRotation::Degree0; 2290 } 2291 2292 Maybe<wr::ImageKey> WebRenderCommandBuilder::CreateImageKey( 2293 nsDisplayItem* aItem, ImageContainer* aContainer, 2294 mozilla::wr::DisplayListBuilder& aBuilder, 2295 mozilla::wr::IpcResourceUpdateQueue& aResources, 2296 mozilla::wr::ImageRendering aRendering, const StackingContextHelper& aSc, 2297 gfx::IntSize& aSize, const Maybe<LayoutDeviceRect>& aAsyncImageBounds) { 2298 RefPtr<WebRenderImageData> imageData = 2299 CreateOrRecycleWebRenderUserData<WebRenderImageData>(aItem); 2300 MOZ_ASSERT(imageData); 2301 2302 if (aContainer->IsAsync()) { 2303 MOZ_ASSERT(aAsyncImageBounds); 2304 2305 LayoutDeviceRect rect = aAsyncImageBounds.value(); 2306 LayoutDeviceRect scBounds(LayoutDevicePoint(0, 0), rect.Size()); 2307 // TODO! 2308 // We appear to be using the image bridge for a lot (most/all?) of 2309 // layers-free image handling and that breaks frame consistency. 2310 imageData->CreateAsyncImageWebRenderCommands( 2311 aBuilder, aContainer, aSc, rect, scBounds, 2312 ToWrRotation(aContainer->GetRotation()), aRendering, 2313 wr::MixBlendMode::Normal, !aItem->BackfaceIsHidden()); 2314 return Nothing(); 2315 } 2316 2317 AutoLockImage autoLock(aContainer); 2318 if (!autoLock.HasImage()) { 2319 return Nothing(); 2320 } 2321 mozilla::layers::Image* image = autoLock.GetImage(); 2322 aSize = image->GetSize(); 2323 2324 return imageData->UpdateImageKey(aContainer, aResources); 2325 } 2326 2327 bool WebRenderCommandBuilder::PushImage( 2328 nsDisplayItem* aItem, ImageContainer* aContainer, 2329 mozilla::wr::DisplayListBuilder& aBuilder, 2330 mozilla::wr::IpcResourceUpdateQueue& aResources, 2331 const StackingContextHelper& aSc, const LayoutDeviceRect& aRect, 2332 const LayoutDeviceRect& aClip) { 2333 auto rendering = wr::ToImageRendering(aItem->Frame()->UsedImageRendering()); 2334 gfx::IntSize size; 2335 Maybe<wr::ImageKey> key = 2336 CreateImageKey(aItem, aContainer, aBuilder, aResources, rendering, aSc, 2337 size, Some(aRect)); 2338 if (aContainer->IsAsync()) { 2339 // Async ImageContainer does not create ImageKey, instead it uses Pipeline. 2340 MOZ_ASSERT(key.isNothing()); 2341 return true; 2342 } 2343 if (!key) { 2344 return false; 2345 } 2346 2347 auto r = wr::ToLayoutRect(aRect); 2348 auto c = wr::ToLayoutRect(aClip); 2349 aBuilder.PushImage(r, c, !aItem->BackfaceIsHidden(), false, rendering, 2350 key.value()); 2351 2352 return true; 2353 } 2354 2355 Maybe<wr::ImageKey> WebRenderCommandBuilder::CreateImageProviderKey( 2356 nsDisplayItem* aItem, image::WebRenderImageProvider* aProvider, 2357 image::ImgDrawResult aDrawResult, 2358 mozilla::wr::IpcResourceUpdateQueue& aResources) { 2359 RefPtr<WebRenderImageProviderData> imageData = 2360 CreateOrRecycleWebRenderUserData<WebRenderImageProviderData>(aItem); 2361 MOZ_ASSERT(imageData); 2362 return imageData->UpdateImageKey(aProvider, aDrawResult, aResources); 2363 } 2364 2365 bool WebRenderCommandBuilder::PushImageProvider( 2366 nsDisplayItem* aItem, image::WebRenderImageProvider* aProvider, 2367 image::ImgDrawResult aDrawResult, mozilla::wr::DisplayListBuilder& aBuilder, 2368 mozilla::wr::IpcResourceUpdateQueue& aResources, 2369 const LayoutDeviceRect& aRect, const LayoutDeviceRect& aClip) { 2370 Maybe<wr::ImageKey> key = 2371 CreateImageProviderKey(aItem, aProvider, aDrawResult, aResources); 2372 if (!key) { 2373 return false; 2374 } 2375 2376 bool antialiased = aItem->GetType() == DisplayItemType::TYPE_SVG_GEOMETRY; 2377 2378 auto rendering = wr::ToImageRendering(aItem->Frame()->UsedImageRendering()); 2379 auto r = wr::ToLayoutRect(aRect); 2380 auto c = wr::ToLayoutRect(aClip); 2381 aBuilder.PushImage(r, c, !aItem->BackfaceIsHidden(), antialiased, rendering, 2382 key.value()); 2383 2384 return true; 2385 } 2386 2387 static void PaintItemByDrawTarget(nsDisplayItem* aItem, gfx::DrawTarget* aDT, 2388 const LayoutDevicePoint& aOffset, 2389 const IntRect& visibleRect, 2390 nsDisplayListBuilder* aDisplayListBuilder, 2391 const gfx::MatrixScales& aScale, 2392 Maybe<gfx::DeviceColor>& aHighlight) { 2393 MOZ_ASSERT(aDT && aDT->IsValid()); 2394 2395 // XXX Why is this ClearRect() needed? 2396 aDT->ClearRect(Rect(visibleRect)); 2397 gfxContext context(aDT); 2398 2399 switch (aItem->GetType()) { 2400 case DisplayItemType::TYPE_SVG_WRAPPER: 2401 case DisplayItemType::TYPE_MASK: { 2402 // These items should be handled by other code paths 2403 MOZ_RELEASE_ASSERT(0); 2404 break; 2405 } 2406 default: 2407 if (!aItem->AsPaintedDisplayItem()) { 2408 break; 2409 } 2410 2411 context.SetMatrix(context.CurrentMatrix().PreScale(aScale).PreTranslate( 2412 -aOffset.x, -aOffset.y)); 2413 if (aDisplayListBuilder->IsPaintingToWindow()) { 2414 aItem->Frame()->AddStateBits(NS_FRAME_PAINTED_THEBES); 2415 } 2416 aItem->AsPaintedDisplayItem()->Paint(aDisplayListBuilder, &context); 2417 break; 2418 } 2419 2420 if (aHighlight && aItem->GetType() != DisplayItemType::TYPE_MASK) { 2421 // Apply highlight fills, if the appropriate prefs are set. 2422 // We don't do this for masks because we'd be filling the A8 mask surface, 2423 // which isn't very useful. 2424 aDT->SetTransform(gfx::Matrix()); 2425 aDT->FillRect(Rect(visibleRect), gfx::ColorPattern(aHighlight.value())); 2426 } 2427 } 2428 2429 bool WebRenderCommandBuilder::ComputeInvalidationForDisplayItem( 2430 nsDisplayListBuilder* aBuilder, const nsPoint& aShift, 2431 nsDisplayItem* aItem) { 2432 RefPtr<WebRenderFallbackData> fallbackData = 2433 CreateOrRecycleWebRenderUserData<WebRenderFallbackData>(aItem); 2434 2435 nsRect invalid; 2436 if (!fallbackData->mGeometry || aItem->IsInvalid(invalid)) { 2437 fallbackData->mGeometry = WrapUnique(aItem->AllocateGeometry(aBuilder)); 2438 return true; 2439 } 2440 2441 fallbackData->mGeometry->MoveBy(aShift); 2442 nsRegion combined; 2443 aItem->ComputeInvalidationRegion(aBuilder, fallbackData->mGeometry.get(), 2444 &combined); 2445 2446 UniquePtr<nsDisplayItemGeometry> geometry; 2447 if (!combined.IsEmpty() || aItem->NeedsGeometryUpdates()) { 2448 geometry = WrapUnique(aItem->AllocateGeometry(aBuilder)); 2449 } 2450 2451 fallbackData->mClip.AddOffsetAndComputeDifference( 2452 aShift, fallbackData->mGeometry->ComputeInvalidationRegion(), 2453 aItem->GetClip(), 2454 geometry ? geometry->ComputeInvalidationRegion() 2455 : fallbackData->mGeometry->ComputeInvalidationRegion(), 2456 &combined); 2457 2458 if (geometry) { 2459 fallbackData->mGeometry = std::move(geometry); 2460 } 2461 fallbackData->mClip = aItem->GetClip(); 2462 2463 if (!combined.IsEmpty()) { 2464 return true; 2465 } else if (aItem->GetChildren()) { 2466 return ComputeInvalidationForDisplayList(aBuilder, aShift, 2467 aItem->GetChildren()); 2468 } 2469 return false; 2470 } 2471 2472 bool WebRenderCommandBuilder::ComputeInvalidationForDisplayList( 2473 nsDisplayListBuilder* aBuilder, const nsPoint& aShift, 2474 nsDisplayList* aList) { 2475 FlattenedDisplayListIterator iter(aBuilder, aList); 2476 while (iter.HasNext()) { 2477 if (ComputeInvalidationForDisplayItem(aBuilder, aShift, 2478 iter.GetNextItem())) { 2479 return true; 2480 } 2481 } 2482 return false; 2483 } 2484 2485 // When drawing fallback images we create either 2486 // a real image or a blob image that will contain the display item. 2487 // In the case of a blob image we paint the item at 0,0 instead 2488 // of trying to keep at aItem->GetBounds().TopLeft() like we do 2489 // with SVG. We do this because there's not necessarily a reference frame 2490 // between us and the rest of the world so the the coordinates 2491 // that we get for the bounds are not necessarily stable across scrolling 2492 // or other movement. 2493 already_AddRefed<WebRenderFallbackData> 2494 WebRenderCommandBuilder::GenerateFallbackData( 2495 nsDisplayItem* aItem, wr::DisplayListBuilder& aBuilder, 2496 wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc, 2497 nsDisplayListBuilder* aDisplayListBuilder, LayoutDeviceRect& aImageRect) { 2498 Maybe<gfx::DeviceColor> highlight; 2499 if (StaticPrefs::gfx_webrender_debug_highlight_painted_layers()) { 2500 highlight.emplace(gfx::DeviceColor(1.0, 0.0, 0.0, 0.5)); 2501 } 2502 2503 RefPtr<WebRenderFallbackData> fallbackData = 2504 CreateOrRecycleWebRenderUserData<WebRenderFallbackData>(aItem); 2505 2506 // Blob images will only draw the visible area of the blob so we don't need to 2507 // clip them here and can just rely on the webrender clipping. 2508 // TODO We also don't clip native themed widget to avoid over-invalidation 2509 // during scrolling. It would be better to support a sort of streaming/tiling 2510 // scheme for large ones but the hope is that we should not have large native 2511 // themed items. 2512 bool snap; 2513 nsRect paintBounds = aItem->GetBounds(aDisplayListBuilder, &snap); 2514 nsRect buildingRect = aItem->GetBuildingRect(); 2515 2516 const int32_t appUnitsPerDevPixel = 2517 aItem->Frame()->PresContext()->AppUnitsPerDevPixel(); 2518 auto bounds = 2519 LayoutDeviceRect::FromAppUnits(paintBounds, appUnitsPerDevPixel); 2520 if (bounds.IsEmpty()) { 2521 return nullptr; 2522 } 2523 2524 MatrixScales scale = aSc.GetInheritedScale(); 2525 MatrixScales oldScale = fallbackData->mScale; 2526 // We tolerate slight changes in scale so that we don't, for example, 2527 // rerasterize on MotionMark 2528 bool differentScale = gfx::FuzzyEqual(scale.xScale, oldScale.xScale, 1e-6f) && 2529 gfx::FuzzyEqual(scale.yScale, oldScale.yScale, 1e-6f); 2530 2531 auto layerScale = LayoutDeviceToLayerScale2D::FromUnknownScale(scale); 2532 2533 auto trans = 2534 ViewAs<LayerPixel>(aSc.GetSnappingSurfaceTransform().GetTranslation()); 2535 2536 if (!FitsInt32(trans.X()) || !FitsInt32(trans.Y())) { 2537 // The translation overflowed int32_t. 2538 return nullptr; 2539 } 2540 2541 auto snappedTrans = LayerIntPoint::Floor(trans); 2542 LayerPoint residualOffset = trans - snappedTrans; 2543 2544 nsRegion opaqueRegion = aItem->GetOpaqueRegion(aDisplayListBuilder, &snap); 2545 wr::OpacityType opacity = opaqueRegion.Contains(paintBounds) 2546 ? wr::OpacityType::Opaque 2547 : wr::OpacityType::HasAlphaChannel; 2548 2549 LayerIntRect dtRect, visibleRect; 2550 // If we think the item is opaque we round the bounds 2551 // to the nearest pixel instead of rounding them out. If we rounded 2552 // out we'd potentially introduce transparent pixels. 2553 // 2554 // Ideally we'd be able to ask an item its bounds in pixels and whether 2555 // they're all opaque. Unfortunately no such API exists so we currently 2556 // just hope that we get it right. 2557 if (aBuilder.GetInheritedOpacity() == 1.0f && 2558 opacity == wr::OpacityType::Opaque && snap) { 2559 dtRect = LayerIntRect::FromUnknownRect( 2560 ScaleToNearestPixelsOffset(paintBounds, scale.xScale, scale.yScale, 2561 appUnitsPerDevPixel, residualOffset)); 2562 2563 visibleRect = 2564 LayerIntRect::FromUnknownRect( 2565 ScaleToNearestPixelsOffset(buildingRect, scale.xScale, scale.yScale, 2566 appUnitsPerDevPixel, residualOffset)) 2567 .Intersect(dtRect); 2568 } else { 2569 dtRect = ScaleToOutsidePixelsOffset(paintBounds, scale.xScale, scale.yScale, 2570 appUnitsPerDevPixel, residualOffset); 2571 2572 visibleRect = 2573 ScaleToOutsidePixelsOffset(buildingRect, scale.xScale, scale.yScale, 2574 appUnitsPerDevPixel, residualOffset) 2575 .Intersect(dtRect); 2576 } 2577 2578 auto visibleSize = visibleRect.Size(); 2579 // these rectangles can overflow from scaling so try to 2580 // catch that with IsEmpty() checks. See bug 1622126. 2581 if (visibleSize.IsEmpty() || dtRect.IsEmpty()) { 2582 return nullptr; 2583 } 2584 2585 // Display item bounds should be unscaled 2586 aImageRect = visibleRect / layerScale; 2587 2588 // We always paint items at 0,0 so the visibleRect that we use inside the blob 2589 // is needs to be adjusted by the display item bounds top left. 2590 visibleRect -= dtRect.TopLeft(); 2591 2592 nsDisplayItemGeometry* geometry = fallbackData->mGeometry.get(); 2593 2594 bool needPaint = true; 2595 2596 MOZ_RELEASE_ASSERT(aItem->GetType() != DisplayItemType::TYPE_SVG_WRAPPER); 2597 if (geometry && !fallbackData->IsInvalid() && 2598 aItem->GetType() != DisplayItemType::TYPE_SVG_WRAPPER && differentScale) { 2599 nsRect invalid; 2600 if (!aItem->IsInvalid(invalid)) { 2601 nsPoint shift = paintBounds.TopLeft() - geometry->mBounds.TopLeft(); 2602 geometry->MoveBy(shift); 2603 2604 nsRegion invalidRegion; 2605 aItem->ComputeInvalidationRegion(aDisplayListBuilder, geometry, 2606 &invalidRegion); 2607 2608 nsRect lastBounds = fallbackData->mBounds; 2609 lastBounds.MoveBy(shift); 2610 2611 if (lastBounds.IsEqualInterior(paintBounds) && invalidRegion.IsEmpty() && 2612 aBuilder.GetInheritedOpacity() == fallbackData->mOpacity) { 2613 if (aItem->GetType() == DisplayItemType::TYPE_FILTER) { 2614 needPaint = ComputeInvalidationForDisplayList( 2615 aDisplayListBuilder, shift, aItem->GetChildren()); 2616 if (!buildingRect.IsEqualInterior(fallbackData->mBuildingRect)) { 2617 needPaint = true; 2618 } 2619 } else { 2620 needPaint = false; 2621 } 2622 } 2623 } 2624 } 2625 2626 if (needPaint || !fallbackData->GetImageKey()) { 2627 fallbackData->mGeometry = 2628 WrapUnique(aItem->AllocateGeometry(aDisplayListBuilder)); 2629 2630 gfx::SurfaceFormat format = aItem->GetType() == DisplayItemType::TYPE_MASK 2631 ? gfx::SurfaceFormat::A8 2632 : (opacity == wr::OpacityType::Opaque 2633 ? gfx::SurfaceFormat::B8G8R8X8 2634 : gfx::SurfaceFormat::B8G8R8A8); 2635 MOZ_ASSERT(!opaqueRegion.IsComplex()); 2636 2637 std::vector<RefPtr<ScaledFont>> fonts; 2638 bool validFonts = true; 2639 RefPtr<WebRenderDrawEventRecorder> recorder = 2640 MakeAndAddRef<WebRenderDrawEventRecorder>( 2641 [&](MemStream& aStream, 2642 std::vector<RefPtr<ScaledFont>>& aScaledFonts) { 2643 size_t count = aScaledFonts.size(); 2644 aStream.write((const char*)&count, sizeof(count)); 2645 for (auto& scaled : aScaledFonts) { 2646 Maybe<wr::FontInstanceKey> key = 2647 mManager->WrBridge()->GetFontKeyForScaledFont(scaled, 2648 aResources); 2649 if (key.isNothing()) { 2650 validFonts = false; 2651 break; 2652 } 2653 BlobFont font = {key.value(), scaled}; 2654 aStream.write((const char*)&font, sizeof(font)); 2655 } 2656 fonts = std::move(aScaledFonts); 2657 }); 2658 RefPtr<gfx::DrawTarget> dummyDt = gfx::Factory::CreateDrawTarget( 2659 gfx::BackendType::SKIA, gfx::IntSize(1, 1), format); 2660 RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateRecordingDrawTarget( 2661 recorder, dummyDt, (dtRect - dtRect.TopLeft()).ToUnknownRect()); 2662 if (aBuilder.GetInheritedOpacity() != 1.0f) { 2663 dt->PushLayer(false, aBuilder.GetInheritedOpacity(), nullptr, 2664 gfx::Matrix()); 2665 } 2666 PaintItemByDrawTarget(aItem, dt, (dtRect / layerScale).TopLeft(), 2667 /*aVisibleRect: */ dt->GetRect(), aDisplayListBuilder, 2668 scale, highlight); 2669 if (aBuilder.GetInheritedOpacity() != 1.0f) { 2670 dt->PopLayer(); 2671 } 2672 2673 // the item bounds are relative to the blob origin which is 2674 // dtRect.TopLeft() 2675 recorder->FlushItem((dtRect - dtRect.TopLeft()).ToUnknownRect()); 2676 recorder->Finish(); 2677 2678 if (!validFonts) { 2679 gfxCriticalNote << "Failed serializing fonts for blob image"; 2680 return nullptr; 2681 } 2682 2683 Range<uint8_t> bytes((uint8_t*)recorder->mOutputStream.mData, 2684 recorder->mOutputStream.mLength); 2685 wr::BlobImageKey key = 2686 wr::BlobImageKey{mManager->WrBridge()->GetNextImageKey()}; 2687 wr::ImageDescriptor descriptor(visibleSize.ToUnknownSize(), 0, 2688 dt->GetFormat(), opacity); 2689 if (!aResources.AddBlobImage( 2690 key, descriptor, bytes, 2691 ViewAs<ImagePixel>(visibleRect, 2692 PixelCastJustification::LayerIsImage))) { 2693 return nullptr; 2694 } 2695 TakeExternalSurfaces(recorder, fallbackData->mExternalSurfaces, 2696 mManager->GetRenderRootStateManager(), aResources); 2697 fallbackData->SetBlobImageKey(key); 2698 fallbackData->SetFonts(fonts); 2699 2700 fallbackData->mScale = scale; 2701 fallbackData->mOpacity = aBuilder.GetInheritedOpacity(); 2702 fallbackData->SetInvalid(false); 2703 } 2704 2705 MOZ_DIAGNOSTIC_ASSERT(mManager->WrBridge()->MatchesNamespace( 2706 fallbackData->GetBlobImageKey().ref()), 2707 "Stale blob key for fallback!"); 2708 2709 aResources.SetBlobImageVisibleArea( 2710 fallbackData->GetBlobImageKey().value(), 2711 ViewAs<ImagePixel>(visibleRect, PixelCastJustification::LayerIsImage)); 2712 2713 // Update current bounds to fallback data 2714 fallbackData->mBounds = paintBounds; 2715 fallbackData->mBuildingRect = buildingRect; 2716 2717 MOZ_ASSERT(fallbackData->GetImageKey()); 2718 2719 return fallbackData.forget(); 2720 } 2721 2722 void WebRenderMaskData::ClearImageKey() { 2723 if (mBlobKey) { 2724 mManager->AddBlobImageKeyForDiscard(mBlobKey.value()); 2725 } 2726 mBlobKey.reset(); 2727 } 2728 2729 void WebRenderMaskData::Invalidate() { 2730 mMaskStyle = nsStyleImageLayers(nsStyleImageLayers::LayerType::Mask); 2731 } 2732 2733 Maybe<wr::ImageMask> WebRenderCommandBuilder::BuildWrMaskImage( 2734 nsDisplayMasksAndClipPaths* aMaskItem, wr::DisplayListBuilder& aBuilder, 2735 wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc, 2736 nsDisplayListBuilder* aDisplayListBuilder, 2737 const LayoutDeviceRect& aBounds) { 2738 RefPtr<WebRenderMaskData> maskData = 2739 CreateOrRecycleWebRenderUserData<WebRenderMaskData>(aMaskItem); 2740 2741 if (!maskData) { 2742 return Nothing(); 2743 } 2744 2745 bool snap; 2746 nsRect bounds = aMaskItem->GetBounds(aDisplayListBuilder, &snap); 2747 2748 const int32_t appUnitsPerDevPixel = 2749 aMaskItem->Frame()->PresContext()->AppUnitsPerDevPixel(); 2750 2751 MatrixScales scale = aSc.GetInheritedScale(); 2752 MatrixScales oldScale = maskData->mScale; 2753 // This scale determination should probably be done using 2754 // ChooseScaleAndSetTransform but for now we just fake it. 2755 // We tolerate slight changes in scale so that we don't, for example, 2756 // rerasterize on MotionMark 2757 bool sameScale = FuzzyEqual(scale.xScale, oldScale.xScale, 1e-6f) && 2758 FuzzyEqual(scale.yScale, oldScale.yScale, 1e-6f); 2759 2760 LayerIntRect itemRect = 2761 LayerIntRect::FromUnknownRect(bounds.ScaleToOutsidePixels( 2762 scale.xScale, scale.yScale, appUnitsPerDevPixel)); 2763 2764 LayerIntRect visibleRect = 2765 LayerIntRect::FromUnknownRect( 2766 aMaskItem->GetBuildingRect().ScaleToOutsidePixels( 2767 scale.xScale, scale.yScale, appUnitsPerDevPixel)) 2768 .SafeIntersect(itemRect); 2769 2770 if (visibleRect.IsEmpty()) { 2771 return Nothing(); 2772 } 2773 2774 LayoutDeviceToLayerScale2D layerScale(scale.xScale, scale.yScale); 2775 LayoutDeviceRect imageRect = LayerRect(visibleRect) / layerScale; 2776 2777 nsPoint maskOffset = aMaskItem->ToReferenceFrame() - bounds.TopLeft(); 2778 2779 bool shouldHandleOpacity = aBuilder.GetInheritedOpacity() != 1.0f; 2780 2781 nsRect dirtyRect; 2782 // If this mask item is being painted for the first time, some members of 2783 // WebRenderMaskData are still default initialized. This is intentional. 2784 if (aMaskItem->IsInvalid(dirtyRect) || 2785 !itemRect.IsEqualInterior(maskData->mItemRect) || 2786 !(aMaskItem->Frame()->StyleSVGReset()->mMask == maskData->mMaskStyle) || 2787 maskOffset != maskData->mMaskOffset || !sameScale || 2788 shouldHandleOpacity != maskData->mShouldHandleOpacity) { 2789 IntSize size = itemRect.Size().ToUnknownSize(); 2790 2791 if (!Factory::AllowedSurfaceSize(size)) { 2792 return Nothing(); 2793 } 2794 2795 std::vector<RefPtr<ScaledFont>> fonts; 2796 bool validFonts = true; 2797 RefPtr<WebRenderDrawEventRecorder> recorder = 2798 MakeAndAddRef<WebRenderDrawEventRecorder>( 2799 [&](MemStream& aStream, 2800 std::vector<RefPtr<ScaledFont>>& aScaledFonts) { 2801 size_t count = aScaledFonts.size(); 2802 aStream.write((const char*)&count, sizeof(count)); 2803 2804 for (auto& scaled : aScaledFonts) { 2805 Maybe<wr::FontInstanceKey> key = 2806 mManager->WrBridge()->GetFontKeyForScaledFont(scaled, 2807 aResources); 2808 if (key.isNothing()) { 2809 validFonts = false; 2810 break; 2811 } 2812 BlobFont font = {key.value(), scaled}; 2813 aStream.write((const char*)&font, sizeof(font)); 2814 } 2815 2816 fonts = std::move(aScaledFonts); 2817 }); 2818 2819 RefPtr<DrawTarget> dummyDt = Factory::CreateDrawTarget( 2820 BackendType::SKIA, IntSize(1, 1), SurfaceFormat::A8); 2821 RefPtr<DrawTarget> dt = Factory::CreateRecordingDrawTarget( 2822 recorder, dummyDt, IntRect(IntPoint(0, 0), size)); 2823 if (!dt || !dt->IsValid()) { 2824 gfxCriticalNote << "Failed to create drawTarget for blob mask image"; 2825 return Nothing(); 2826 } 2827 2828 gfxContext context(dt); 2829 context.SetMatrix(context.CurrentMatrix() 2830 .PreTranslate(-itemRect.x, -itemRect.y) 2831 .PreScale(scale)); 2832 2833 bool maskPainted = false; 2834 bool maskIsComplete = aMaskItem->PaintMask( 2835 aDisplayListBuilder, &context, shouldHandleOpacity, &maskPainted); 2836 if (!maskPainted) { 2837 return Nothing(); 2838 } 2839 2840 // If a mask is incomplete or missing (e.g. it's display: none) the proper 2841 // behaviour depends on the masked frame being html or svg. 2842 // 2843 // For an HTML frame: 2844 // According to css-masking spec, always create a mask surface when 2845 // we have any item in maskFrame even if all of those items are 2846 // non-resolvable <mask-sources> or <images> so continue with the 2847 // painting code. Note that in a common case of no layer of the mask being 2848 // complete or even partially complete then the mask surface will be 2849 // transparent black so this results in hiding the frame. 2850 // For an SVG frame: 2851 // SVG 1.1 say that if we fail to resolve a mask, we should draw the 2852 // object unmasked so return Nothing(). 2853 if (!maskIsComplete && 2854 aMaskItem->Frame()->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) { 2855 return Nothing(); 2856 } 2857 2858 recorder->FlushItem(IntRect(0, 0, size.width, size.height)); 2859 recorder->Finish(); 2860 2861 if (!validFonts) { 2862 gfxCriticalNote << "Failed serializing fonts for blob mask image"; 2863 return Nothing(); 2864 } 2865 2866 Range<uint8_t> bytes((uint8_t*)recorder->mOutputStream.mData, 2867 recorder->mOutputStream.mLength); 2868 wr::BlobImageKey key = 2869 wr::BlobImageKey{mManager->WrBridge()->GetNextImageKey()}; 2870 wr::ImageDescriptor descriptor(size, 0, dt->GetFormat(), 2871 wr::OpacityType::HasAlphaChannel); 2872 if (!aResources.AddBlobImage(key, descriptor, bytes, 2873 ImageIntRect(0, 0, size.width, size.height))) { 2874 return Nothing(); 2875 } 2876 maskData->ClearImageKey(); 2877 maskData->mBlobKey = Some(key); 2878 maskData->mFonts = fonts; 2879 TakeExternalSurfaces(recorder, maskData->mExternalSurfaces, 2880 mManager->GetRenderRootStateManager(), aResources); 2881 if (maskIsComplete) { 2882 maskData->mItemRect = itemRect; 2883 maskData->mMaskOffset = maskOffset; 2884 maskData->mScale = scale; 2885 maskData->mMaskStyle = aMaskItem->Frame()->StyleSVGReset()->mMask; 2886 maskData->mShouldHandleOpacity = shouldHandleOpacity; 2887 } 2888 } 2889 2890 aResources.SetBlobImageVisibleArea( 2891 maskData->mBlobKey.value(), 2892 ViewAs<ImagePixel>(visibleRect - itemRect.TopLeft(), 2893 PixelCastJustification::LayerIsImage)); 2894 2895 MOZ_DIAGNOSTIC_ASSERT( 2896 mManager->WrBridge()->MatchesNamespace(maskData->mBlobKey.ref()), 2897 "Stale blob key for mask!"); 2898 2899 wr::ImageMask imageMask; 2900 imageMask.image = wr::AsImageKey(maskData->mBlobKey.value()); 2901 imageMask.rect = wr::ToLayoutRect(imageRect); 2902 return Some(imageMask); 2903 } 2904 2905 bool WebRenderCommandBuilder::PushItemAsImage( 2906 nsDisplayItem* aItem, wr::DisplayListBuilder& aBuilder, 2907 wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc, 2908 nsDisplayListBuilder* aDisplayListBuilder) { 2909 LayoutDeviceRect imageRect; 2910 RefPtr<WebRenderFallbackData> fallbackData = GenerateFallbackData( 2911 aItem, aBuilder, aResources, aSc, aDisplayListBuilder, imageRect); 2912 if (!fallbackData) { 2913 return false; 2914 } 2915 2916 wr::LayoutRect dest = wr::ToLayoutRect(imageRect); 2917 auto rendering = wr::ToImageRendering(aItem->Frame()->UsedImageRendering()); 2918 mHitTestInfoManager.ProcessItemAsImage(aItem, dest, aBuilder, 2919 aDisplayListBuilder); 2920 aBuilder.PushImage(dest, dest, !aItem->BackfaceIsHidden(), false, rendering, 2921 fallbackData->GetImageKey().value()); 2922 return true; 2923 } 2924 2925 void WebRenderCommandBuilder::RemoveUnusedAndResetWebRenderUserData() { 2926 mWebRenderUserDatas.RemoveIf([&](WebRenderUserData* data) { 2927 if (!data->IsUsed()) { 2928 nsIFrame* frame = data->GetFrame(); 2929 2930 MOZ_ASSERT(frame->HasProperty(WebRenderUserDataProperty::Key())); 2931 2932 WebRenderUserDataTable* userDataTable = 2933 frame->GetProperty(WebRenderUserDataProperty::Key()); 2934 2935 MOZ_ASSERT(userDataTable->Count()); 2936 2937 userDataTable->Remove( 2938 WebRenderUserDataKey(data->GetDisplayItemKey(), data->GetType())); 2939 2940 if (!userDataTable->Count()) { 2941 frame->RemoveProperty(WebRenderUserDataProperty::Key()); 2942 userDataTable = nullptr; 2943 } 2944 2945 switch (data->GetType()) { 2946 case WebRenderUserData::UserDataType::eCanvas: 2947 mLastCanvasDatas.Remove(data->AsCanvasData()); 2948 break; 2949 case WebRenderUserData::UserDataType::eAnimation: 2950 EffectCompositor::ClearIsRunningOnCompositor( 2951 frame, GetDisplayItemTypeFromKey(data->GetDisplayItemKey())); 2952 break; 2953 default: 2954 break; 2955 } 2956 2957 return true; 2958 } 2959 2960 data->SetUsed(false); 2961 return false; 2962 }); 2963 } 2964 2965 void WebRenderCommandBuilder::ClearCachedResources() { 2966 RemoveUnusedAndResetWebRenderUserData(); 2967 // UserDatas should only be in the used state during a call to 2968 // WebRenderCommandBuilder::BuildWebRenderCommands The should always be false 2969 // upon return from BuildWebRenderCommands(). 2970 MOZ_RELEASE_ASSERT(mWebRenderUserDatas.Count() == 0); 2971 } 2972 2973 WebRenderGroupData::WebRenderGroupData( 2974 RenderRootStateManager* aRenderRootStateManager, nsDisplayItem* aItem) 2975 : WebRenderGroupData(aRenderRootStateManager, aItem->GetPerFrameKey(), 2976 aItem->Frame()) {} 2977 2978 WebRenderGroupData::WebRenderGroupData( 2979 RenderRootStateManager* aRenderRootStateManager, uint32_t aDisplayItemKey, 2980 nsIFrame* aFrame) 2981 : WebRenderUserData(aRenderRootStateManager, aDisplayItemKey, aFrame) { 2982 MOZ_COUNT_CTOR(WebRenderGroupData); 2983 } 2984 2985 WebRenderGroupData::~WebRenderGroupData() { 2986 MOZ_COUNT_DTOR(WebRenderGroupData); 2987 GP("Group data destruct\n"); 2988 mSubGroup.ClearImageKey(mManager, true); 2989 mFollowingGroup.ClearImageKey(mManager, true); 2990 } 2991 2992 } // namespace mozilla::layers