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