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 "WebRenderUserData.h"
8 
9 #include "mozilla/image/WebRenderImageProvider.h"
10 #include "mozilla/layers/AnimationHelper.h"
11 #include "mozilla/layers/CompositorBridgeChild.h"
12 #include "mozilla/layers/ImageClient.h"
13 #include "mozilla/layers/WebRenderBridgeChild.h"
14 #include "mozilla/layers/RenderRootStateManager.h"
15 #include "mozilla/layers/WebRenderMessages.h"
16 #include "mozilla/layers/IpcResourceUpdateQueue.h"
17 #include "mozilla/layers/SharedSurfacesChild.h"
18 #include "mozilla/webgpu/WebGPUChild.h"
19 #include "nsDisplayListInvalidation.h"
20 #include "nsIFrame.h"
21 #include "WebRenderCanvasRenderer.h"
22 
23 using namespace mozilla::image;
24 
25 namespace mozilla {
26 namespace layers {
27 
AddWebRenderCommands(wr::DisplayListBuilder & aBuilder)28 void WebRenderBackgroundData::AddWebRenderCommands(
29     wr::DisplayListBuilder& aBuilder) {
30   aBuilder.PushRect(mBounds, mBounds, true, true, mColor);
31 }
32 
33 /* static */
SupportsAsyncUpdate(nsIFrame * aFrame)34 bool WebRenderUserData::SupportsAsyncUpdate(nsIFrame* aFrame) {
35   if (!aFrame) {
36     return false;
37   }
38   RefPtr<WebRenderImageData> data = GetWebRenderUserData<WebRenderImageData>(
39       aFrame, static_cast<uint32_t>(DisplayItemType::TYPE_VIDEO));
40   if (data) {
41     return data->IsAsync();
42   }
43 
44   return false;
45 }
46 
47 /* static */
ProcessInvalidateForImage(nsIFrame * aFrame,DisplayItemType aType,ImageProviderId aProviderId)48 bool WebRenderUserData::ProcessInvalidateForImage(nsIFrame* aFrame,
49                                                   DisplayItemType aType,
50                                                   ImageProviderId aProviderId) {
51   MOZ_ASSERT(aFrame);
52 
53   if (!aFrame->HasProperty(WebRenderUserDataProperty::Key())) {
54     aFrame->SchedulePaint();
55     return false;
56   }
57 
58   auto type = static_cast<uint32_t>(aType);
59   RefPtr<WebRenderFallbackData> fallback =
60       GetWebRenderUserData<WebRenderFallbackData>(aFrame, type);
61   if (fallback) {
62     fallback->SetInvalid(true);
63     aFrame->SchedulePaint();
64     return true;
65   }
66 
67   RefPtr<WebRenderImageProviderData> image =
68       GetWebRenderUserData<WebRenderImageProviderData>(aFrame, type);
69   if (image && image->Invalidate(aProviderId)) {
70     return true;
71   }
72 
73   aFrame->SchedulePaint();
74   return false;
75 }
76 
WebRenderUserData(RenderRootStateManager * aManager,uint32_t aDisplayItemKey,nsIFrame * aFrame)77 WebRenderUserData::WebRenderUserData(RenderRootStateManager* aManager,
78                                      uint32_t aDisplayItemKey, nsIFrame* aFrame)
79     : mManager(aManager),
80       mFrame(aFrame),
81       mDisplayItemKey(aDisplayItemKey),
82       mTable(aManager->GetWebRenderUserDataTable()),
83       mUsed(false) {}
84 
WebRenderUserData(RenderRootStateManager * aManager,nsDisplayItem * aItem)85 WebRenderUserData::WebRenderUserData(RenderRootStateManager* aManager,
86                                      nsDisplayItem* aItem)
87     : mManager(aManager),
88       mFrame(aItem->Frame()),
89       mDisplayItemKey(aItem->GetPerFrameKey()),
90       mTable(aManager->GetWebRenderUserDataTable()),
91       mUsed(false) {}
92 
93 WebRenderUserData::~WebRenderUserData() = default;
94 
RemoveFromTable()95 void WebRenderUserData::RemoveFromTable() { mTable->Remove(this); }
96 
WrBridge() const97 WebRenderBridgeChild* WebRenderUserData::WrBridge() const {
98   return mManager->WrBridge();
99 }
100 
WebRenderImageData(RenderRootStateManager * aManager,nsDisplayItem * aItem)101 WebRenderImageData::WebRenderImageData(RenderRootStateManager* aManager,
102                                        nsDisplayItem* aItem)
103     : WebRenderUserData(aManager, aItem) {}
104 
WebRenderImageData(RenderRootStateManager * aManager,uint32_t aDisplayItemKey,nsIFrame * aFrame)105 WebRenderImageData::WebRenderImageData(RenderRootStateManager* aManager,
106                                        uint32_t aDisplayItemKey,
107                                        nsIFrame* aFrame)
108     : WebRenderUserData(aManager, aDisplayItemKey, aFrame) {}
109 
~WebRenderImageData()110 WebRenderImageData::~WebRenderImageData() {
111   ClearImageKey();
112 
113   if (mPipelineId) {
114     mManager->RemovePipelineIdForCompositable(mPipelineId.ref());
115   }
116 }
117 
ClearImageKey()118 void WebRenderImageData::ClearImageKey() {
119   if (mKey) {
120     mManager->AddImageKeyForDiscard(mKey.value());
121     if (mTextureOfImage) {
122       WrBridge()->ReleaseTextureOfImage(mKey.value());
123       mTextureOfImage = nullptr;
124     }
125     mKey.reset();
126   }
127   MOZ_ASSERT(!mTextureOfImage);
128 }
129 
UpdateImageKey(ImageContainer * aContainer,wr::IpcResourceUpdateQueue & aResources,bool aFallback)130 Maybe<wr::ImageKey> WebRenderImageData::UpdateImageKey(
131     ImageContainer* aContainer, wr::IpcResourceUpdateQueue& aResources,
132     bool aFallback) {
133   MOZ_ASSERT(aContainer);
134 
135   if (mContainer != aContainer) {
136     mContainer = aContainer;
137   }
138 
139   CreateImageClientIfNeeded();
140   if (!mImageClient) {
141     return Nothing();
142   }
143 
144   MOZ_ASSERT(mImageClient->AsImageClientSingle());
145 
146   ImageClientSingle* imageClient = mImageClient->AsImageClientSingle();
147   uint32_t oldCounter = imageClient->GetLastUpdateGenerationCounter();
148 
149   bool ret = imageClient->UpdateImage(aContainer, /* unused */ 0);
150   RefPtr<TextureClient> currentTexture = imageClient->GetForwardedTexture();
151   if (!ret || !currentTexture) {
152     // Delete old key
153     ClearImageKey();
154     return Nothing();
155   }
156 
157   // Reuse old key if generation is not updated.
158   if (!aFallback &&
159       oldCounter == imageClient->GetLastUpdateGenerationCounter() && mKey) {
160     return mKey;
161   }
162 
163   // If we already had a texture and the format hasn't changed, better to reuse
164   // the image keys than create new ones.
165   bool useUpdate = mKey.isSome() && !!mTextureOfImage && !!currentTexture &&
166                    mTextureOfImage->GetSize() == currentTexture->GetSize() &&
167                    mTextureOfImage->GetFormat() == currentTexture->GetFormat();
168 
169   wr::MaybeExternalImageId extId = currentTexture->GetExternalImageKey();
170   MOZ_RELEASE_ASSERT(extId.isSome());
171 
172   if (useUpdate) {
173     MOZ_ASSERT(mKey.isSome());
174     MOZ_ASSERT(mTextureOfImage);
175     aResources.PushExternalImageForTexture(
176         extId.ref(), mKey.ref(), currentTexture, /* aIsUpdate */ true);
177   } else {
178     ClearImageKey();
179     wr::WrImageKey key = WrBridge()->GetNextImageKey();
180     aResources.PushExternalImageForTexture(extId.ref(), key, currentTexture,
181                                            /* aIsUpdate */ false);
182     mKey = Some(key);
183   }
184 
185   mTextureOfImage = currentTexture;
186   return mKey;
187 }
188 
GetImageClient()189 already_AddRefed<ImageClient> WebRenderImageData::GetImageClient() {
190   RefPtr<ImageClient> imageClient = mImageClient;
191   return imageClient.forget();
192 }
193 
CreateAsyncImageWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,ImageContainer * aContainer,const StackingContextHelper & aSc,const LayoutDeviceRect & aBounds,const LayoutDeviceRect & aSCBounds,VideoInfo::Rotation aRotation,const wr::ImageRendering & aFilter,const wr::MixBlendMode & aMixBlendMode,bool aIsBackfaceVisible)194 void WebRenderImageData::CreateAsyncImageWebRenderCommands(
195     mozilla::wr::DisplayListBuilder& aBuilder, ImageContainer* aContainer,
196     const StackingContextHelper& aSc, const LayoutDeviceRect& aBounds,
197     const LayoutDeviceRect& aSCBounds, VideoInfo::Rotation aRotation,
198     const wr::ImageRendering& aFilter, const wr::MixBlendMode& aMixBlendMode,
199     bool aIsBackfaceVisible) {
200   MOZ_ASSERT(aContainer->IsAsync());
201 
202   if (mPipelineId.isSome() && mContainer != aContainer) {
203     // In this case, we need to remove the existed pipeline and create new one
204     // because the ImageContainer is changed.
205     WrBridge()->RemovePipelineIdForCompositable(mPipelineId.ref());
206     mPipelineId.reset();
207   }
208 
209   if (!mPipelineId) {
210     // Alloc async image pipeline id.
211     mPipelineId =
212         Some(WrBridge()->GetCompositorBridgeChild()->GetNextPipelineId());
213     WrBridge()->AddPipelineIdForCompositable(
214         mPipelineId.ref(), aContainer->GetAsyncContainerHandle(),
215         CompositableHandleOwner::ImageBridge);
216     mContainer = aContainer;
217   }
218   MOZ_ASSERT(!mImageClient);
219 
220   // Push IFrame for async image pipeline.
221   //
222   // We don't push a stacking context for this async image pipeline here.
223   // Instead, we do it inside the iframe that hosts the image. As a result,
224   // a bunch of the calculations normally done as part of that stacking
225   // context need to be done manually and pushed over to the parent side,
226   // where it will be done when we build the display list for the iframe.
227   // That happens in AsyncImagePipelineManager.
228   wr::LayoutRect r = wr::ToLayoutRect(aBounds);
229   aBuilder.PushIFrame(r, aIsBackfaceVisible, mPipelineId.ref(),
230                       /*ignoreMissingPipelines*/ false);
231 
232   WrBridge()->AddWebRenderParentCommand(OpUpdateAsyncImagePipeline(
233       mPipelineId.value(), aSCBounds, aRotation, aFilter, aMixBlendMode));
234 }
235 
CreateImageClientIfNeeded()236 void WebRenderImageData::CreateImageClientIfNeeded() {
237   if (!mImageClient) {
238     mImageClient = ImageClient::CreateImageClient(
239         CompositableType::IMAGE, WrBridge(), TextureFlags::DEFAULT);
240     if (!mImageClient) {
241       return;
242     }
243 
244     mImageClient->Connect();
245   }
246 }
247 
WebRenderImageProviderData(RenderRootStateManager * aManager,nsDisplayItem * aItem)248 WebRenderImageProviderData::WebRenderImageProviderData(
249     RenderRootStateManager* aManager, nsDisplayItem* aItem)
250     : WebRenderUserData(aManager, aItem) {}
251 
WebRenderImageProviderData(RenderRootStateManager * aManager,uint32_t aDisplayItemKey,nsIFrame * aFrame)252 WebRenderImageProviderData::WebRenderImageProviderData(
253     RenderRootStateManager* aManager, uint32_t aDisplayItemKey,
254     nsIFrame* aFrame)
255     : WebRenderUserData(aManager, aDisplayItemKey, aFrame),
256       mDrawResult(ImgDrawResult::NOT_READY) {}
257 
258 WebRenderImageProviderData::~WebRenderImageProviderData() = default;
259 
UpdateImageKey(WebRenderImageProvider * aProvider,ImgDrawResult aDrawResult,wr::IpcResourceUpdateQueue & aResources)260 Maybe<wr::ImageKey> WebRenderImageProviderData::UpdateImageKey(
261     WebRenderImageProvider* aProvider, ImgDrawResult aDrawResult,
262     wr::IpcResourceUpdateQueue& aResources) {
263   if (mProvider != aProvider) {
264     mProvider = aProvider;
265   }
266 
267   wr::ImageKey key = {};
268   nsresult rv = mProvider ? mProvider->UpdateKey(mManager, aResources, key)
269                           : NS_ERROR_FAILURE;
270   mKey = NS_SUCCEEDED(rv) ? Some(key) : Nothing();
271   mDrawResult = aDrawResult;
272   return mKey;
273 }
274 
Invalidate(ImageProviderId aProviderId) const275 bool WebRenderImageProviderData::Invalidate(ImageProviderId aProviderId) const {
276   if (!aProviderId || !mProvider || mProvider->GetProviderId() != aProviderId ||
277       !mKey) {
278     return false;
279   }
280 
281   if (mDrawResult != ImgDrawResult::SUCCESS &&
282       mDrawResult != ImgDrawResult::BAD_IMAGE) {
283     return false;
284   }
285 
286   wr::ImageKey key = {};
287   nsresult rv =
288       mProvider->UpdateKey(mManager, mManager->AsyncResourceUpdates(), key);
289   return NS_SUCCEEDED(rv) && mKey.ref() == key;
290 }
291 
WebRenderInProcessImageData(RenderRootStateManager * aManager,nsDisplayItem * aItem)292 WebRenderInProcessImageData::WebRenderInProcessImageData(
293     RenderRootStateManager* aManager, nsDisplayItem* aItem)
294     : WebRenderUserData(aManager, aItem) {}
295 
WebRenderInProcessImageData(RenderRootStateManager * aManager,uint32_t aDisplayItemKey,nsIFrame * aFrame)296 WebRenderInProcessImageData::WebRenderInProcessImageData(
297     RenderRootStateManager* aManager, uint32_t aDisplayItemKey,
298     nsIFrame* aFrame)
299     : WebRenderUserData(aManager, aDisplayItemKey, aFrame) {}
300 
~WebRenderInProcessImageData()301 WebRenderInProcessImageData::~WebRenderInProcessImageData() {
302   if (mPipelineId) {
303     mManager->RemovePipelineIdForCompositable(mPipelineId.ref());
304   }
305 }
306 
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,const CompositableHandle & aHandle,const StackingContextHelper & aSc,const LayoutDeviceRect & aBounds,const LayoutDeviceRect & aSCBounds,VideoInfo::Rotation aRotation,const wr::ImageRendering & aFilter,const wr::MixBlendMode & aMixBlendMode,bool aIsBackfaceVisible)307 void WebRenderInProcessImageData::CreateWebRenderCommands(
308     mozilla::wr::DisplayListBuilder& aBuilder,
309     const CompositableHandle& aHandle, const StackingContextHelper& aSc,
310     const LayoutDeviceRect& aBounds, const LayoutDeviceRect& aSCBounds,
311     VideoInfo::Rotation aRotation, const wr::ImageRendering& aFilter,
312     const wr::MixBlendMode& aMixBlendMode, bool aIsBackfaceVisible) {
313   MOZ_ASSERT(aHandle);
314 
315   if (mPipelineId.isSome() && !(mHandle == aHandle)) {
316     // In this case, we need to remove the existed pipeline and create new one
317     // because the CompositableHandle has changed.
318     WrBridge()->RemovePipelineIdForCompositable(mPipelineId.ref());
319     mPipelineId.reset();
320   }
321 
322   if (!mPipelineId) {
323     // Alloc in process image pipeline id.
324     mPipelineId =
325         Some(WrBridge()->GetCompositorBridgeChild()->GetNextPipelineId());
326     WrBridge()->AddPipelineIdForCompositable(
327         mPipelineId.ref(), aHandle, CompositableHandleOwner::InProcessManager);
328     mHandle = aHandle;
329   }
330 
331   // Push IFrame for in process image pipeline.
332   //
333   // We don't push a stacking context for this in process image pipeline here.
334   // Instead, we do it inside the iframe that hosts the image. As a result,
335   // a bunch of the calculations normally done as part of that stacking
336   // context need to be done manually and pushed over to the parent side,
337   // where it will be done when we build the display list for the iframe.
338   // That happens in AsyncImagePipelineManager.
339   wr::LayoutRect r = wr::ToLayoutRect(aBounds);
340   aBuilder.PushIFrame(r, aIsBackfaceVisible, mPipelineId.ref(),
341                       /*ignoreMissingPipelines*/ false);
342 
343   WrBridge()->AddWebRenderParentCommand(OpUpdateAsyncImagePipeline(
344       mPipelineId.value(), aSCBounds, aRotation, aFilter, aMixBlendMode));
345 }
346 
WebRenderFallbackData(RenderRootStateManager * aManager,nsDisplayItem * aItem)347 WebRenderFallbackData::WebRenderFallbackData(RenderRootStateManager* aManager,
348                                              nsDisplayItem* aItem)
349     : WebRenderUserData(aManager, aItem), mOpacity(1.0f), mInvalid(false) {}
350 
~WebRenderFallbackData()351 WebRenderFallbackData::~WebRenderFallbackData() { ClearImageKey(); }
352 
SetBlobImageKey(const wr::BlobImageKey & aKey)353 void WebRenderFallbackData::SetBlobImageKey(const wr::BlobImageKey& aKey) {
354   ClearImageKey();
355   mBlobKey = Some(aKey);
356 }
357 
GetImageKey()358 Maybe<wr::ImageKey> WebRenderFallbackData::GetImageKey() {
359   if (mBlobKey) {
360     return Some(wr::AsImageKey(mBlobKey.value()));
361   }
362 
363   if (mImageData) {
364     return mImageData->GetImageKey();
365   }
366 
367   return Nothing();
368 }
369 
ClearImageKey()370 void WebRenderFallbackData::ClearImageKey() {
371   if (mImageData) {
372     mImageData->ClearImageKey();
373     mImageData = nullptr;
374   }
375 
376   if (mBlobKey) {
377     mManager->AddBlobImageKeyForDiscard(mBlobKey.value());
378     mBlobKey.reset();
379   }
380 }
381 
PaintIntoImage()382 WebRenderImageData* WebRenderFallbackData::PaintIntoImage() {
383   if (mBlobKey) {
384     mManager->AddBlobImageKeyForDiscard(mBlobKey.value());
385     mBlobKey.reset();
386   }
387 
388   if (mImageData) {
389     return mImageData.get();
390   }
391 
392   mImageData = MakeAndAddRef<WebRenderImageData>(mManager.get(),
393                                                  mDisplayItemKey, mFrame);
394 
395   return mImageData.get();
396 }
397 
WebRenderAPZAnimationData(RenderRootStateManager * aManager,nsDisplayItem * aItem)398 WebRenderAPZAnimationData::WebRenderAPZAnimationData(
399     RenderRootStateManager* aManager, nsDisplayItem* aItem)
400     : WebRenderUserData(aManager, aItem),
401       mAnimationId(AnimationHelper::GetNextCompositorAnimationsId()) {}
402 
WebRenderAnimationData(RenderRootStateManager * aManager,nsDisplayItem * aItem)403 WebRenderAnimationData::WebRenderAnimationData(RenderRootStateManager* aManager,
404                                                nsDisplayItem* aItem)
405     : WebRenderUserData(aManager, aItem) {}
406 
~WebRenderAnimationData()407 WebRenderAnimationData::~WebRenderAnimationData() {
408   // It may be the case that nsDisplayItem that created this WebRenderUserData
409   // gets destroyed without getting a chance to discard the compositor animation
410   // id, so we should do it as part of cleanup here.
411   uint64_t animationId = mAnimationInfo.GetCompositorAnimationsId();
412   // animationId might be 0 if mAnimationInfo never held any active animations.
413   if (animationId) {
414     mManager->AddCompositorAnimationsIdForDiscard(animationId);
415   }
416 }
417 
WebRenderCanvasData(RenderRootStateManager * aManager,nsDisplayItem * aItem)418 WebRenderCanvasData::WebRenderCanvasData(RenderRootStateManager* aManager,
419                                          nsDisplayItem* aItem)
420     : WebRenderUserData(aManager, aItem) {}
421 
~WebRenderCanvasData()422 WebRenderCanvasData::~WebRenderCanvasData() {
423   if (mCanvasRenderer) {
424     mCanvasRenderer->ClearCachedResources();
425   }
426 }
427 
ClearCanvasRenderer()428 void WebRenderCanvasData::ClearCanvasRenderer() { mCanvasRenderer = nullptr; }
429 
GetCanvasRenderer()430 WebRenderCanvasRendererAsync* WebRenderCanvasData::GetCanvasRenderer() {
431   return mCanvasRenderer.get();
432 }
433 
CreateCanvasRenderer()434 WebRenderCanvasRendererAsync* WebRenderCanvasData::CreateCanvasRenderer() {
435   mCanvasRenderer = new WebRenderCanvasRendererAsync(mManager);
436   return mCanvasRenderer.get();
437 }
438 
SetImageContainer(ImageContainer * aImageContainer)439 void WebRenderCanvasData::SetImageContainer(ImageContainer* aImageContainer) {
440   mContainer = aImageContainer;
441 }
442 
GetImageContainer()443 ImageContainer* WebRenderCanvasData::GetImageContainer() {
444   if (!mContainer) {
445     mContainer = MakeAndAddRef<ImageContainer>();
446   }
447   return mContainer;
448 }
449 
ClearImageContainer()450 void WebRenderCanvasData::ClearImageContainer() { mContainer = nullptr; }
451 
WebRenderRemoteData(RenderRootStateManager * aManager,nsDisplayItem * aItem)452 WebRenderRemoteData::WebRenderRemoteData(RenderRootStateManager* aManager,
453                                          nsDisplayItem* aItem)
454     : WebRenderUserData(aManager, aItem) {}
455 
~WebRenderRemoteData()456 WebRenderRemoteData::~WebRenderRemoteData() {
457   if (mRemoteBrowser) {
458     mRemoteBrowser->UpdateEffects(mozilla::dom::EffectsInfo::FullyHidden());
459   }
460 }
461 
DestroyWebRenderUserDataTable(WebRenderUserDataTable * aTable)462 void DestroyWebRenderUserDataTable(WebRenderUserDataTable* aTable) {
463   for (const auto& value : aTable->Values()) {
464     value->RemoveFromTable();
465   }
466   delete aTable;
467 }
468 
469 }  // namespace layers
470 }  // namespace mozilla
471