Bug 1527441 - Separate WebRenderImageData and WebRenderFallbackData. r=jrmuizel
authorNicolas Silva <nsilva@mozilla.com>
Wed, 13 Feb 2019 09:21:11 +0000
changeset 458877 96b6da57e237722e313af1da3fd97f0e4439a80a
parent 458876 1a88323932cbbd7af5e37ccfd5c5e7ab34393f0a
child 458878 a11b90e9e698ac9d82bf754037dad5ad0f559d72
push id78075
push usernsilva@mozilla.com
push dateWed, 13 Feb 2019 09:22:34 +0000
treeherderautoland@96b6da57e237 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1527441
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1527441 - Separate WebRenderImageData and WebRenderFallbackData. r=jrmuizel This patch is an attempt to separate the two and avoid accidentally getting into an inconsistent state. WebRenderImageData manages resources for images that are shared with webrender, while WebRenderFallbackData manages state to render content, in most case in a blob image. if the fallback data needs to render into a TextureClient, it creates a WebRenderImageData to hold on to the shared texture, but does not inherit from it anymore. Differential Revision: https://phabricator.services.mozilla.com/D19565
gfx/layers/wr/WebRenderCommandBuilder.cpp
gfx/layers/wr/WebRenderUserData.cpp
gfx/layers/wr/WebRenderUserData.h
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -2043,17 +2043,17 @@ WebRenderCommandBuilder::GenerateFallbac
       aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
   auto bounds =
       LayoutDeviceRect::FromAppUnits(paintBounds, appUnitsPerDevPixel);
   if (bounds.IsEmpty()) {
     return nullptr;
   }
 
   gfx::Size scale = aSc.GetInheritedScale();
-  gfx::Size oldScale = fallbackData->GetScale();
+  gfx::Size oldScale = fallbackData->mScale;
   // We tolerate slight changes in scale so that we don't, for example,
   // rerasterize on MotionMark
   bool differentScale = gfx::FuzzyEqual(scale.width, oldScale.width, 1e-6f) &&
                         gfx::FuzzyEqual(scale.height, oldScale.height, 1e-6f);
 
   LayoutDeviceToLayerScale2D layerScale(scale.width, scale.height);
 
   auto trans =
@@ -2077,17 +2077,17 @@ WebRenderCommandBuilder::GenerateFallbac
   if (dtSize.IsEmpty()) {
     return nullptr;
   }
 
   aImageRect = dtRect / layerScale;
 
   auto offset = aImageRect.TopLeft();
 
-  nsDisplayItemGeometry* geometry = fallbackData->GetGeometry();
+  nsDisplayItemGeometry* geometry = fallbackData->mGeometry;
 
   bool needPaint = true;
 
   // nsDisplayFilters is rendered via BasicLayerManager which means the
   // invalidate region is unknown until we traverse the displaylist contained by
   // it.
   if (geometry && !fallbackData->IsInvalid() &&
       aItem->GetType() != DisplayItemType::TYPE_FILTER &&
@@ -2098,31 +2098,31 @@ WebRenderCommandBuilder::GenerateFallbac
     if (aItem->IsInvalid(invalid)) {
       invalidRegion.OrWith(paintBounds);
     } else {
       nsPoint shift = itemBounds.TopLeft() - geometry->mBounds.TopLeft();
       geometry->MoveBy(shift);
       aItem->ComputeInvalidationRegion(aDisplayListBuilder, geometry,
                                        &invalidRegion);
 
-      nsRect lastBounds = fallbackData->GetBounds();
+      nsRect lastBounds = fallbackData->mBounds;
       lastBounds.MoveBy(shift);
 
       if (!lastBounds.IsEqualInterior(paintBounds)) {
         invalidRegion.OrWith(lastBounds);
         invalidRegion.OrWith(paintBounds);
       }
     }
     needPaint = !invalidRegion.IsEmpty();
   }
 
   if (needPaint || !fallbackData->GetImageKey()) {
     nsAutoPtr<nsDisplayItemGeometry> newGeometry;
     newGeometry = aItem->AllocateGeometry(aDisplayListBuilder);
-    fallbackData->SetGeometry(std::move(newGeometry));
+    fallbackData->mGeometry = std::move(newGeometry);
 
     gfx::SurfaceFormat format = aItem->GetType() == DisplayItemType::TYPE_MASK
                                     ? gfx::SurfaceFormat::A8
                                     : gfx::SurfaceFormat::B8G8R8A8;
     if (useBlobImage) {
       bool snapped;
       wr::OpacityType opacity =
           aItem->GetOpaqueRegion(aDisplayListBuilder, &snapped)
@@ -2190,18 +2190,20 @@ WebRenderCommandBuilder::GenerateFallbac
           return nullptr;
         }
       }
       aResources.SetBlobImageVisibleArea(
           fallbackData->GetBlobImageKey().value(),
           ViewAs<ImagePixel>(visibleRect,
                              PixelCastJustification::LayerIsImage));
     } else {
-      fallbackData->CreateImageClientIfNeeded();
-      RefPtr<ImageClient> imageClient = fallbackData->GetImageClient();
+      WebRenderImageData* imageData = fallbackData->PaintIntoImage();
+
+      imageData->CreateImageClientIfNeeded();
+      RefPtr<ImageClient> imageClient = imageData->GetImageClient();
       RefPtr<ImageContainer> imageContainer =
           LayerManager::CreateImageContainer();
       bool isInvalidated = false;
 
       {
         UpdateImageHelper helper(imageContainer, imageClient,
                                  dtSize.ToUnknownSize(), format);
         {
@@ -2221,38 +2223,38 @@ WebRenderCommandBuilder::GenerateFallbac
         if (isInvalidated) {
           // Update image if there it's invalidated.
           if (!helper.UpdateImage()) {
             return nullptr;
           }
         } else {
           // If there is no invalidation region and we don't have a image key,
           // it means we don't need to push image for the item.
-          if (!fallbackData->GetImageKey().isSome()) {
+          if (!imageData->GetImageKey().isSome()) {
             return nullptr;
           }
         }
       }
 
       // Force update the key in fallback data since we repaint the image in
       // this path. If not force update, fallbackData may reuse the original key
       // because it doesn't know UpdateImageHelper already updated the image
       // container.
       if (isInvalidated &&
-          !fallbackData->UpdateImageKey(imageContainer, aResources, true)) {
+          !imageData->UpdateImageKey(imageContainer, aResources, true)) {
         return nullptr;
       }
     }
 
-    fallbackData->SetScale(scale);
+    fallbackData->mScale = scale;
     fallbackData->SetInvalid(false);
   }
 
   // Update current bounds to fallback data
-  fallbackData->SetBounds(paintBounds);
+  fallbackData->mBounds = paintBounds;
 
   MOZ_ASSERT(fallbackData->GetImageKey());
 
   return fallbackData.forget();
 }
 
 class WebRenderMaskData : public WebRenderUserData {
  public:
--- a/gfx/layers/wr/WebRenderUserData.cpp
+++ b/gfx/layers/wr/WebRenderUserData.cpp
@@ -61,16 +61,25 @@ void WebRenderBackgroundData::AddWebRend
     return true;
   }
 
   aFrame->SchedulePaint();
   return false;
 }
 
 WebRenderUserData::WebRenderUserData(RenderRootStateManager* aManager,
+                                     uint32_t aDisplayItemKey,
+                                     nsIFrame* aFrame)
+    : mManager(aManager),
+      mFrame(aFrame),
+      mDisplayItemKey(aDisplayItemKey),
+      mTable(aManager->GetWebRenderUserDataTable()),
+      mUsed(false) {}
+
+WebRenderUserData::WebRenderUserData(RenderRootStateManager* aManager,
                                      nsDisplayItem* aItem)
     : mManager(aManager),
       mFrame(aItem->Frame()),
       mDisplayItemKey(aItem->GetPerFrameKey()),
       mTable(aManager->GetWebRenderUserDataTable()),
       mUsed(false) {}
 
 WebRenderUserData::~WebRenderUserData() {}
@@ -80,16 +89,21 @@ void WebRenderUserData::RemoveFromTable(
 WebRenderBridgeChild* WebRenderUserData::WrBridge() const {
   return mManager->WrBridge();
 }
 
 WebRenderImageData::WebRenderImageData(RenderRootStateManager* aManager,
                                        nsDisplayItem* aItem)
     : WebRenderUserData(aManager, aItem), mOwnsKey(false) {}
 
+WebRenderImageData::WebRenderImageData(RenderRootStateManager* aManager,
+                                       uint32_t aDisplayItemKey,
+                                       nsIFrame* aFrame)
+    : WebRenderUserData(aManager, aDisplayItemKey, aFrame), mOwnsKey(false) {}
+
 WebRenderImageData::~WebRenderImageData() {
   ClearImageKey();
 
   if (mPipelineId) {
     mManager->RemovePipelineIdForCompositable(mPipelineId.ref());
   }
 }
 
@@ -261,52 +275,65 @@ void WebRenderImageData::CreateImageClie
     }
 
     mImageClient->Connect();
   }
 }
 
 WebRenderFallbackData::WebRenderFallbackData(RenderRootStateManager* aManager,
                                              nsDisplayItem* aItem)
-    : WebRenderImageData(aManager, aItem), mInvalid(false) {}
+    : WebRenderUserData(aManager, aItem), mInvalid(false) {}
 
 WebRenderFallbackData::~WebRenderFallbackData() { ClearImageKey(); }
 
-nsDisplayItemGeometry* WebRenderFallbackData::GetGeometry() {
-  return mGeometry.get();
-}
-
-void WebRenderFallbackData::SetGeometry(
-    nsAutoPtr<nsDisplayItemGeometry> aGeometry) {
-  mGeometry = aGeometry;
-}
-
 void WebRenderFallbackData::SetBlobImageKey(const wr::BlobImageKey& aKey) {
   ClearImageKey();
   mBlobKey = Some(aKey);
-  mOwnsKey = true;
 }
 
 Maybe<wr::ImageKey> WebRenderFallbackData::GetImageKey() {
   if (mBlobKey) {
     return Some(wr::AsImageKey(mBlobKey.value()));
   }
 
-  return mKey;
+  if (mImageData) {
+    return mImageData->GetImageKey();
+  }
+
+  return Nothing();
 }
 
 void WebRenderFallbackData::ClearImageKey() {
-  if (mBlobKey && mOwnsKey) {
+  if (mImageData) {
+    mImageData->ClearImageKey();
+    mImageData = nullptr;
+  }
+
+  if (mBlobKey) {
     mManager->AddBlobImageKeyForDiscard(mBlobKey.value());
+    mBlobKey.reset();
   }
-  mBlobKey.reset();
+}
+
+WebRenderImageData* WebRenderFallbackData::PaintIntoImage() {
+  if (mBlobKey) {
+    mManager->AddBlobImageKeyForDiscard(mBlobKey.value());
+    mBlobKey.reset();
+  }
 
-  WebRenderImageData::ClearImageKey();
+  if (mImageData) {
+    return mImageData.get();
+  }
+
+  mImageData = MakeAndAddRef<WebRenderImageData>(mManager.get(), mDisplayItemKey, mFrame);
+
+  return mImageData.get();
 }
 
+
 WebRenderAnimationData::WebRenderAnimationData(RenderRootStateManager* aManager,
                                                nsDisplayItem* aItem)
     : WebRenderUserData(aManager, aItem) {}
 
 WebRenderAnimationData::~WebRenderAnimationData() {
   // It may be the case that nsDisplayItem that created this WebRenderUserData
   // gets destroyed without getting a chance to discard the compositor animation
   // id, so we should do it as part of cleanup here.
--- a/gfx/layers/wr/WebRenderUserData.h
+++ b/gfx/layers/wr/WebRenderUserData.h
@@ -43,29 +43,32 @@ class WebRenderBackgroundData {
       : mBounds(aBounds), mColor(aColor) {}
   void AddWebRenderCommands(wr::DisplayListBuilder& aBuilder);
 
  protected:
   wr::LayoutRect mBounds;
   wr::ColorF mColor;
 };
 
+/// Parent class for arbitrary WebRender-specific data that can be associated
+/// to an nsFrame.
 class WebRenderUserData {
  public:
   typedef nsTHashtable<nsRefPtrHashKey<WebRenderUserData>>
       WebRenderUserDataRefTable;
 
   static bool SupportsAsyncUpdate(nsIFrame* aFrame);
 
   static bool ProcessInvalidateForImage(nsIFrame* aFrame,
                                         DisplayItemType aType);
 
   NS_INLINE_DECL_REFCOUNTING(WebRenderUserData)
 
   WebRenderUserData(RenderRootStateManager* aManager, nsDisplayItem* aItem);
+  WebRenderUserData(RenderRootStateManager* aManager, uint32_t mDisplayItemKey, nsIFrame* aFrame);
 
   virtual WebRenderImageData* AsImageData() { return nullptr; }
   virtual WebRenderFallbackData* AsFallbackData() { return nullptr; }
   virtual WebRenderCanvasData* AsCanvasData() { return nullptr; }
   virtual WebRenderGroupData* AsGroupData() { return nullptr; }
 
   enum class UserDataType {
     eImage,
@@ -114,26 +117,27 @@ struct WebRenderUserDataKey {
   WebRenderUserData::UserDataType mType;
 };
 
 typedef nsRefPtrHashtable<
     nsGenericHashKey<mozilla::layers::WebRenderUserDataKey>, WebRenderUserData>
     WebRenderUserDataTable;
 
 /// Holds some data used to share TextureClient/ImageClient with the parent
-/// process except if used with blob images (watch your step).
+/// process.
 class WebRenderImageData : public WebRenderUserData {
  public:
   WebRenderImageData(RenderRootStateManager* aManager, nsDisplayItem* aItem);
+  WebRenderImageData(RenderRootStateManager* aManager, uint32_t aDisplayItemKey, nsIFrame* aFrame);
   virtual ~WebRenderImageData();
 
   virtual WebRenderImageData* AsImageData() override { return this; }
   virtual UserDataType GetType() override { return UserDataType::eImage; }
   static UserDataType Type() { return UserDataType::eImage; }
-  virtual Maybe<wr::ImageKey> GetImageKey() { return mKey; }
+  Maybe<wr::ImageKey> GetImageKey() { return mKey; }
   void SetImageKey(const wr::ImageKey& aKey);
   already_AddRefed<ImageClient> GetImageClient();
 
   Maybe<wr::ImageKey> UpdateImageKey(ImageContainer* aContainer,
                                      wr::IpcResourceUpdateQueue& aResources,
                                      bool aFallback = false);
 
   void CreateAsyncImageWebRenderCommands(
@@ -144,71 +148,71 @@ class WebRenderImageData : public WebRen
       const wr::MixBlendMode& aMixBlendMode, bool aIsBackfaceVisible);
 
   void CreateImageClientIfNeeded();
 
   bool IsAsync() { return mPipelineId.isSome(); }
 
   bool UsingSharedSurface() const;
 
- protected:
-  virtual void ClearImageKey();
+  void ClearImageKey();
 
+ protected:
+  Maybe<wr::ImageKey> mKey;
   RefPtr<TextureClient> mTextureOfImage;
-  Maybe<wr::ImageKey> mKey;
   RefPtr<ImageClient> mImageClient;
   Maybe<wr::PipelineId> mPipelineId;
   RefPtr<ImageContainer> mContainer;
+  // The key can be owned by a shared surface that is used by several elements.
+  // when this is the case the shared surface is responsible for managing the
+  // destruction of the key.
+  // TODO: we surely can come up with a simpler/safer way to model this.
   bool mOwnsKey;
 };
 
 /// Used for fallback rendering.
 ///
 /// In most cases this uses blob images but it can also render on the content
 /// side directly into a texture.
-///
-/// TODO(nical) It would be much better to separate the two use cases into
-/// separate classes and not have the blob image related code inherit from
-/// WebRenderImageData (the current code only works if we carefully use a subset
-/// of the parent code).
-class WebRenderFallbackData : public WebRenderImageData {
+class WebRenderFallbackData : public WebRenderUserData {
  public:
   WebRenderFallbackData(RenderRootStateManager* aManager, nsDisplayItem* aItem);
   virtual ~WebRenderFallbackData();
 
   virtual WebRenderFallbackData* AsFallbackData() override { return this; }
   virtual UserDataType GetType() override { return UserDataType::eFallback; }
   static UserDataType Type() { return UserDataType::eFallback; }
-  nsDisplayItemGeometry* GetGeometry() override;
-  void SetGeometry(nsAutoPtr<nsDisplayItemGeometry> aGeometry);
-  nsRect GetBounds() { return mBounds; }
-  void SetBounds(const nsRect& aRect) { mBounds = aRect; }
+
   void SetInvalid(bool aInvalid) { mInvalid = aInvalid; }
-  void SetScale(gfx::Size aScale) { mScale = aScale; }
-  gfx::Size GetScale() { return mScale; }
   bool IsInvalid() { return mInvalid; }
   void SetFonts(const std::vector<RefPtr<gfx::ScaledFont>>& aFonts) {
     mFonts = aFonts;
   }
   Maybe<wr::BlobImageKey> GetBlobImageKey() { return mBlobKey; }
-  virtual Maybe<wr::ImageKey> GetImageKey() override;
   void SetBlobImageKey(const wr::BlobImageKey& aKey);
+  Maybe<wr::ImageKey> GetImageKey();
+
+  /// Create a WebRenderImageData to manage the image we are about to render into.
+  WebRenderImageData* PaintIntoImage();
 
+  std::vector<RefPtr<gfx::SourceSurface>> mExternalSurfaces;
   RefPtr<BasicLayerManager> mBasicLayerManager;
-  std::vector<RefPtr<gfx::SourceSurface>> mExternalSurfaces;
+  nsAutoPtr<nsDisplayItemGeometry> mGeometry;
+  nsRect mBounds;
+  gfx::Size mScale;
 
  protected:
-  virtual void ClearImageKey() override;
+  void ClearImageKey();
 
+  std::vector<RefPtr<gfx::ScaledFont>> mFonts;
   Maybe<wr::BlobImageKey> mBlobKey;
-  nsAutoPtr<nsDisplayItemGeometry> mGeometry;
-  nsRect mBounds;
+  // When rendering into a blob image, mImageData is null. It is non-null only when
+  // we render directly into a texture on the content side.
+  RefPtr<WebRenderImageData> mImageData;
   bool mInvalid;
-  gfx::Size mScale;
-  std::vector<RefPtr<gfx::ScaledFont>> mFonts;
 };
 
 class WebRenderAnimationData : public WebRenderUserData {
  public:
   WebRenderAnimationData(RenderRootStateManager* aManager,
                          nsDisplayItem* aItem);
   virtual ~WebRenderAnimationData();