Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/layers/wr/AsyncImagePipelineManager.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "AsyncImagePipelineManager.h"
8
9
#include "CompositableHost.h"
10
#include "gfxEnv.h"
11
#include "mozilla/gfx/gfxVars.h"
12
#include "mozilla/layers/CompositorThread.h"
13
#include "mozilla/layers/SharedSurfacesParent.h"
14
#include "mozilla/layers/WebRenderImageHost.h"
15
#include "mozilla/layers/WebRenderTextureHost.h"
16
#include "mozilla/webrender/RenderThread.h"
17
#include "mozilla/webrender/WebRenderAPI.h"
18
#include "mozilla/webrender/WebRenderTypes.h"
19
20
namespace mozilla {
21
namespace layers {
22
23
AsyncImagePipelineManager::AsyncImagePipeline::AsyncImagePipeline()
24
 : mInitialised(false)
25
 , mIsChanged(false)
26
 , mUseExternalImage(false)
27
 , mFilter(wr::ImageRendering::Auto)
28
 , mMixBlendMode(wr::MixBlendMode::Normal)
29
0
{}
30
31
AsyncImagePipelineManager::AsyncImagePipelineManager(already_AddRefed<wr::WebRenderAPI>&& aApi)
32
 : mApi(aApi)
33
 , mIdNamespace(mApi->GetNamespace())
34
 , mResourceId(0)
35
 , mAsyncImageEpoch{0}
36
 , mWillGenerateFrame(false)
37
 , mDestroyed(false)
38
 , mUpdatesLock("UpdatesLock")
39
0
{
40
0
  MOZ_COUNT_CTOR(AsyncImagePipelineManager);
41
0
}
42
43
AsyncImagePipelineManager::~AsyncImagePipelineManager()
44
0
{
45
0
  MOZ_COUNT_DTOR(AsyncImagePipelineManager);
46
0
}
47
48
void
49
AsyncImagePipelineManager::Destroy()
50
0
{
51
0
  MOZ_ASSERT(!mDestroyed);
52
0
  mApi = nullptr;
53
0
  mDestroyed = true;
54
0
}
55
56
void
57
AsyncImagePipelineManager::SetWillGenerateFrame()
58
0
{
59
0
  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
60
0
61
0
  mWillGenerateFrame = true;
62
0
}
63
64
bool
65
AsyncImagePipelineManager::GetAndResetWillGenerateFrame()
66
0
{
67
0
  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
68
0
69
0
  bool ret = mWillGenerateFrame;
70
0
  mWillGenerateFrame = false;
71
0
  return ret;
72
0
}
73
74
wr::ExternalImageId
75
AsyncImagePipelineManager::GetNextExternalImageId()
76
0
{
77
0
  static uint32_t sNextId = 0;
78
0
  ++sNextId;
79
0
  MOZ_RELEASE_ASSERT(sNextId != UINT32_MAX);
80
0
  // gecko allocates external image id as (IdNamespace:32bit + ResourceId:32bit).
81
0
  // And AsyncImagePipelineManager uses IdNamespace = 0.
82
0
  return wr::ToExternalImageId((uint64_t)sNextId);
83
0
}
84
85
void
86
AsyncImagePipelineManager::AddPipeline(const wr::PipelineId& aPipelineId)
87
0
{
88
0
  if (mDestroyed) {
89
0
    return;
90
0
  }
91
0
  uint64_t id = wr::AsUint64(aPipelineId);
92
0
93
0
  PipelineTexturesHolder* holder = mPipelineTexturesHolders.Get(wr::AsUint64(aPipelineId));
94
0
  if(holder) {
95
0
    // This could happen during tab move between different windows.
96
0
    // Previously removed holder could be still alive for waiting destroyed.
97
0
    MOZ_ASSERT(holder->mDestroyedEpoch.isSome());
98
0
    holder->mDestroyedEpoch = Nothing(); // Revive holder
99
0
    return;
100
0
  }
101
0
  holder = new PipelineTexturesHolder();
102
0
  mPipelineTexturesHolders.Put(id, holder);
103
0
}
104
105
void
106
AsyncImagePipelineManager::RemovePipeline(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch)
107
0
{
108
0
  if (mDestroyed) {
109
0
    return;
110
0
  }
111
0
112
0
  PipelineTexturesHolder* holder = mPipelineTexturesHolders.Get(wr::AsUint64(aPipelineId));
113
0
  MOZ_ASSERT(holder);
114
0
  if (!holder) {
115
0
    return;
116
0
  }
117
0
  holder->mDestroyedEpoch = Some(aEpoch);
118
0
}
119
120
void
121
AsyncImagePipelineManager::AddAsyncImagePipeline(const wr::PipelineId& aPipelineId, WebRenderImageHost* aImageHost)
122
0
{
123
0
  if (mDestroyed) {
124
0
    return;
125
0
  }
126
0
  MOZ_ASSERT(aImageHost);
127
0
  uint64_t id = wr::AsUint64(aPipelineId);
128
0
129
0
  MOZ_ASSERT(!mAsyncImagePipelines.Get(id));
130
0
  AsyncImagePipeline* holder = new AsyncImagePipeline();
131
0
  holder->mImageHost = aImageHost;
132
0
  mAsyncImagePipelines.Put(id, holder);
133
0
  AddPipeline(aPipelineId);
134
0
}
135
136
void
137
AsyncImagePipelineManager::RemoveAsyncImagePipeline(const wr::PipelineId& aPipelineId, wr::TransactionBuilder& aTxn)
138
0
{
139
0
  if (mDestroyed) {
140
0
    return;
141
0
  }
142
0
143
0
  uint64_t id = wr::AsUint64(aPipelineId);
144
0
  if (auto entry = mAsyncImagePipelines.Lookup(id)) {
145
0
    AsyncImagePipeline* holder = entry.Data();
146
0
    wr::Epoch epoch = GetNextImageEpoch();
147
0
    aTxn.ClearDisplayList(epoch, aPipelineId);
148
0
    for (wr::ImageKey key : holder->mKeys) {
149
0
      aTxn.DeleteImage(key);
150
0
    }
151
0
    entry.Remove();
152
0
    RemovePipeline(aPipelineId, epoch);
153
0
  }
154
0
}
155
156
void
157
AsyncImagePipelineManager::UpdateAsyncImagePipeline(const wr::PipelineId& aPipelineId,
158
                                                    const LayoutDeviceRect& aScBounds,
159
                                                    const gfx::Matrix4x4& aScTransform,
160
                                                    const gfx::MaybeIntSize& aScaleToSize,
161
                                                    const wr::ImageRendering& aFilter,
162
                                                    const wr::MixBlendMode& aMixBlendMode)
163
0
{
164
0
  if (mDestroyed) {
165
0
    return;
166
0
  }
167
0
  AsyncImagePipeline* pipeline = mAsyncImagePipelines.Get(wr::AsUint64(aPipelineId));
168
0
  if (!pipeline) {
169
0
    return;
170
0
  }
171
0
  pipeline->mInitialised = true;
172
0
  pipeline->Update(aScBounds,
173
0
                   aScTransform,
174
0
                   aScaleToSize,
175
0
                   aFilter,
176
0
                   aMixBlendMode);
177
0
}
178
179
Maybe<TextureHost::ResourceUpdateOp>
180
AsyncImagePipelineManager::UpdateImageKeys(const wr::Epoch& aEpoch,
181
                                           const wr::PipelineId& aPipelineId,
182
                                           AsyncImagePipeline* aPipeline,
183
                                           nsTArray<wr::ImageKey>& aKeys,
184
                                           wr::TransactionBuilder& aSceneBuilderTxn,
185
                                           wr::TransactionBuilder& aMaybeFastTxn)
186
0
{
187
0
  MOZ_ASSERT(aKeys.IsEmpty());
188
0
  MOZ_ASSERT(aPipeline);
189
0
190
0
  TextureHost* texture = aPipeline->mImageHost->GetAsTextureHostForComposite();
191
0
  TextureHost* previousTexture = aPipeline->mCurrentTexture.get();
192
0
193
0
  if (texture == previousTexture) {
194
0
    // The texture has not changed, just reuse previous ImageKeys.
195
0
    aKeys = aPipeline->mKeys;
196
0
    if (aPipeline->mWrTextureWrapper) {
197
0
      HoldExternalImage(aPipelineId, aEpoch, aPipeline->mWrTextureWrapper);
198
0
    }
199
0
    return Nothing();
200
0
  }
201
0
202
0
  if (!texture) {
203
0
    // We don't have a new texture, there isn't much we can do.
204
0
    aKeys = aPipeline->mKeys;
205
0
    if (aPipeline->mWrTextureWrapper) {
206
0
      HoldExternalImage(aPipelineId, aEpoch, aPipeline->mWrTextureWrapper);
207
0
    }
208
0
    return Nothing();
209
0
  }
210
0
211
0
  aPipeline->mCurrentTexture = texture;
212
0
213
0
  WebRenderTextureHost* wrTexture = texture->AsWebRenderTextureHost();
214
0
215
0
  bool useExternalImage = !gfxEnv::EnableWebRenderRecording() && wrTexture;
216
0
  aPipeline->mUseExternalImage = useExternalImage;
217
0
218
0
  // Use WebRenderTextureHostWrapper only for video.
219
0
  // And WebRenderTextureHostWrapper could be used only with WebRenderTextureHost
220
0
  // that supports NativeTexture
221
0
  bool useWrTextureWrapper = aPipeline->mImageHost->GetAsyncRef() &&
222
0
                             useExternalImage &&
223
0
                             wrTexture &&
224
0
                             wrTexture->SupportsWrNativeTexture();
225
0
226
0
  // The non-external image code path falls back to converting the texture into
227
0
  // an rgb image.
228
0
  auto numKeys = useExternalImage ? texture->NumSubTextures() : 1;
229
0
230
0
  // If we already had a texture and the format hasn't changed, better to reuse the image keys
231
0
  // than create new ones.
232
0
  bool canUpdate = !!previousTexture
233
0
                   && previousTexture->GetSize() == texture->GetSize()
234
0
                   && previousTexture->GetFormat() == texture->GetFormat()
235
0
                   && aPipeline->mKeys.Length() == numKeys;
236
0
237
0
  // Check if WebRenderTextureHostWrapper could be reused.
238
0
  if (aPipeline->mWrTextureWrapper &&
239
0
      (!useWrTextureWrapper || !canUpdate)) {
240
0
    aPipeline->mWrTextureWrapper = nullptr;
241
0
    canUpdate = false;
242
0
  }
243
0
244
0
  if (!canUpdate) {
245
0
    for (auto key : aPipeline->mKeys) {
246
0
      // Destroy ImageKeys on transaction of scene builder thread, since DisplayList is
247
0
      // updated on SceneBuilder thread. It prevents too early ImageKey deletion.
248
0
      aSceneBuilderTxn.DeleteImage(key);
249
0
    }
250
0
    aPipeline->mKeys.Clear();
251
0
    for (uint32_t i = 0; i < numKeys; ++i) {
252
0
      aPipeline->mKeys.AppendElement(GenerateImageKey());
253
0
    }
254
0
  }
255
0
256
0
  aKeys = aPipeline->mKeys;
257
0
258
0
  auto op = canUpdate ? TextureHost::UPDATE_IMAGE : TextureHost::ADD_IMAGE;
259
0
260
0
  if (!useExternalImage) {
261
0
    return UpdateWithoutExternalImage(texture, aKeys[0], op, aMaybeFastTxn);
262
0
  }
263
0
264
0
  if (useWrTextureWrapper && aPipeline->mWrTextureWrapper) {
265
0
    MOZ_ASSERT(canUpdate);
266
0
    // Reuse WebRenderTextureHostWrapper. With it, rendered frame could be updated
267
0
    // without batch re-creation.
268
0
    aPipeline->mWrTextureWrapper->UpdateWebRenderTextureHost(wrTexture);
269
0
    // Ensure frame generation.
270
0
    SetWillGenerateFrame();
271
0
  } else {
272
0
    if (useWrTextureWrapper) {
273
0
      aPipeline->mWrTextureWrapper = new WebRenderTextureHostWrapper(this);
274
0
      aPipeline->mWrTextureWrapper->UpdateWebRenderTextureHost(wrTexture);
275
0
    }
276
0
    Range<wr::ImageKey> keys(&aKeys[0], aKeys.Length());
277
0
    auto externalImageKey =
278
0
      aPipeline->mWrTextureWrapper ? aPipeline->mWrTextureWrapper->GetExternalImageKey() : wrTexture->GetExternalImageKey();
279
0
    wrTexture->PushResourceUpdates(aMaybeFastTxn, op, keys, externalImageKey);
280
0
  }
281
0
282
0
  if (aPipeline->mWrTextureWrapper) {
283
0
    HoldExternalImage(aPipelineId, aEpoch, aPipeline->mWrTextureWrapper);
284
0
  }
285
0
286
0
  return Some(op);
287
0
}
288
289
Maybe<TextureHost::ResourceUpdateOp>
290
AsyncImagePipelineManager::UpdateWithoutExternalImage(TextureHost* aTexture,
291
                                                      wr::ImageKey aKey,
292
                                                      TextureHost::ResourceUpdateOp aOp,
293
                                                      wr::TransactionBuilder& aTxn)
294
0
{
295
0
  MOZ_ASSERT(aTexture);
296
0
297
0
  RefPtr<gfx::DataSourceSurface> dSurf = aTexture->GetAsSurface();
298
0
  if (!dSurf) {
299
0
    NS_ERROR("TextureHost does not return DataSourceSurface");
300
0
    return Nothing();
301
0
  }
302
0
  gfx::DataSourceSurface::MappedSurface map;
303
0
  if (!dSurf->Map(gfx::DataSourceSurface::MapType::READ, &map)) {
304
0
    NS_ERROR("DataSourceSurface failed to map");
305
0
    return Nothing();
306
0
  }
307
0
308
0
  gfx::IntSize size = dSurf->GetSize();
309
0
  wr::ImageDescriptor descriptor(size, map.mStride, dSurf->GetFormat());
310
0
311
0
  // Costly copy right here...
312
0
  wr::Vec<uint8_t> bytes;
313
0
  bytes.PushBytes(Range<uint8_t>(map.mData, size.height * map.mStride));
314
0
315
0
  if (aOp == TextureHost::UPDATE_IMAGE) {
316
0
    aTxn.UpdateImageBuffer(aKey, descriptor, bytes);
317
0
  } else {
318
0
    aTxn.AddImage(aKey, descriptor, bytes);
319
0
  }
320
0
321
0
  dSurf->Unmap();
322
0
323
0
  return Some(aOp);
324
0
}
325
326
void
327
AsyncImagePipelineManager::ApplyAsyncImagesOfImageBridge(wr::TransactionBuilder& aSceneBuilderTxn,
328
                                                         wr::TransactionBuilder& aFastTxn)
329
0
{
330
0
  if (mDestroyed || mAsyncImagePipelines.Count() == 0) {
331
0
    return;
332
0
  }
333
0
334
0
  wr::Epoch epoch = GetNextImageEpoch();
335
0
336
0
  // We use a pipeline with a very small display list for each video element.
337
0
  // Update each of them if needed.
338
0
  for (auto iter = mAsyncImagePipelines.Iter(); !iter.Done(); iter.Next()) {
339
0
    wr::PipelineId pipelineId = wr::AsPipelineId(iter.Key());
340
0
    AsyncImagePipeline* pipeline = iter.Data();
341
0
    // If aync image pipeline does not use ImageBridge, do not need to apply.
342
0
    if (!pipeline->mImageHost->GetAsyncRef()) {
343
0
      continue;
344
0
    }
345
0
    ApplyAsyncImageForPipeline(epoch, pipelineId, pipeline, aSceneBuilderTxn, aFastTxn);
346
0
  }
347
0
}
348
349
void
350
AsyncImagePipelineManager::ApplyAsyncImageForPipeline(const wr::Epoch& aEpoch,
351
                                                      const wr::PipelineId& aPipelineId,
352
                                                      AsyncImagePipeline* aPipeline,
353
                                                      wr::TransactionBuilder& aSceneBuilderTxn,
354
                                                      wr::TransactionBuilder& aMaybeFastTxn)
355
0
{
356
0
  nsTArray<wr::ImageKey> keys;
357
0
  auto op = UpdateImageKeys(aEpoch, aPipelineId, aPipeline, keys, aSceneBuilderTxn, aMaybeFastTxn);
358
0
359
0
  bool updateDisplayList = aPipeline->mInitialised &&
360
0
                           (aPipeline->mIsChanged || op == Some(TextureHost::ADD_IMAGE)) &&
361
0
                           !!aPipeline->mCurrentTexture;
362
0
363
0
  if (!updateDisplayList) {
364
0
    // We don't need to update the display list, either because we can't or because
365
0
    // the previous one is still up to date.
366
0
    // We may, however, have updated some resources.
367
0
368
0
    // Use transaction of scene builder thread to notify epoch.
369
0
    // It is for making epoch update consistent.
370
0
    aSceneBuilderTxn.UpdateEpoch(aPipelineId, aEpoch);
371
0
    if (aPipeline->mCurrentTexture) {
372
0
      HoldExternalImage(aPipelineId, aEpoch, aPipeline->mCurrentTexture->AsWebRenderTextureHost());
373
0
    }
374
0
    return;
375
0
  }
376
0
377
0
  aPipeline->mIsChanged = false;
378
0
379
0
  wr::LayoutSize contentSize { aPipeline->mScBounds.Width(), aPipeline->mScBounds.Height() };
380
0
  wr::DisplayListBuilder builder(aPipelineId, contentSize);
381
0
382
0
  float opacity = 1.0f;
383
0
  Maybe<wr::WrClipId> referenceFrameId = builder.PushStackingContext(
384
0
    wr::ToLayoutRect(aPipeline->mScBounds),
385
0
    nullptr,
386
0
    nullptr,
387
0
    &opacity,
388
0
    aPipeline->mScTransform.IsIdentity() ? nullptr : &aPipeline->mScTransform,
389
0
    wr::TransformStyle::Flat,
390
0
    nullptr,
391
0
    aPipeline->mMixBlendMode,
392
0
    nsTArray<wr::WrFilterOp>(),
393
0
    true,
394
0
    // This is fine to do unconditionally because we only push images here.
395
0
    wr::RasterSpace::Screen());
396
0
397
0
  if (aPipeline->mCurrentTexture && !keys.IsEmpty()) {
398
0
    LayoutDeviceRect rect(0, 0, aPipeline->mCurrentTexture->GetSize().width, aPipeline->mCurrentTexture->GetSize().height);
399
0
    if (aPipeline->mScaleToSize.isSome()) {
400
0
      rect = LayoutDeviceRect(0, 0, aPipeline->mScaleToSize.value().width, aPipeline->mScaleToSize.value().height);
401
0
    }
402
0
403
0
    if (aPipeline->mUseExternalImage) {
404
0
      MOZ_ASSERT(aPipeline->mCurrentTexture->AsWebRenderTextureHost());
405
0
      Range<wr::ImageKey> range_keys(&keys[0], keys.Length());
406
0
      aPipeline->mCurrentTexture->PushDisplayItems(builder,
407
0
                                                  wr::ToLayoutRect(rect),
408
0
                                                  wr::ToLayoutRect(rect),
409
0
                                                  aPipeline->mFilter,
410
0
                                                  range_keys);
411
0
      HoldExternalImage(aPipelineId, aEpoch, aPipeline->mCurrentTexture->AsWebRenderTextureHost());
412
0
    } else {
413
0
      MOZ_ASSERT(keys.Length() == 1);
414
0
      builder.PushImage(wr::ToLayoutRect(rect),
415
0
                        wr::ToLayoutRect(rect),
416
0
                        true,
417
0
                        aPipeline->mFilter,
418
0
                        keys[0]);
419
0
    }
420
0
  }
421
0
422
0
  builder.PopStackingContext(referenceFrameId.isSome());
423
0
424
0
  wr::BuiltDisplayList dl;
425
0
  wr::LayoutSize builderContentSize;
426
0
  builder.Finalize(builderContentSize, dl);
427
0
  aSceneBuilderTxn.SetDisplayList(
428
0
    gfx::Color(0.f, 0.f, 0.f, 0.f),
429
0
    aEpoch,
430
0
    LayerSize(aPipeline->mScBounds.Width(), aPipeline->mScBounds.Height()),
431
0
    aPipelineId, builderContentSize,
432
0
    dl.dl_desc, dl.dl);
433
0
}
434
435
void
436
AsyncImagePipelineManager::ApplyAsyncImageForPipeline(const wr::PipelineId& aPipelineId, wr::TransactionBuilder& aSceneBuilderTxn)
437
0
{
438
0
  AsyncImagePipeline* pipeline = mAsyncImagePipelines.Get(wr::AsUint64(aPipelineId));
439
0
  if (!pipeline) {
440
0
    return;
441
0
  }
442
0
  wr::TransactionBuilder fastTxn(/* aUseSceneBuilderThread */ false);
443
0
  wr::AutoTransactionSender sender(mApi, &fastTxn);
444
0
445
0
  // Use transaction of using non scene builder thread when ImageHost uses ImageBridge.
446
0
  // ApplyAsyncImagesOfImageBridge() handles transaction of adding and updating
447
0
  // wr::ImageKeys of ImageHosts that uses ImageBridge. Then AsyncImagePipelineManager
448
0
  // always needs to use non scene builder thread transaction for adding and updating
449
0
  // wr::ImageKeys of ImageHosts that uses ImageBridge. Otherwise, ordering of
450
0
  // wr::ImageKeys updating in webrender becomes inconsistent.
451
0
  auto& txn = pipeline->mImageHost->GetAsyncRef() ? fastTxn : aSceneBuilderTxn;
452
0
453
0
  wr::Epoch epoch = GetNextImageEpoch();
454
0
  ApplyAsyncImageForPipeline(epoch, aPipelineId, pipeline, aSceneBuilderTxn, txn);
455
0
}
456
457
void
458
AsyncImagePipelineManager::SetEmptyDisplayList(const wr::PipelineId& aPipelineId, wr::TransactionBuilder& aTxn)
459
0
{
460
0
  AsyncImagePipeline* pipeline = mAsyncImagePipelines.Get(wr::AsUint64(aPipelineId));
461
0
  if (!pipeline) {
462
0
    return;
463
0
  }
464
0
465
0
  wr::Epoch epoch = GetNextImageEpoch();
466
0
  wr::LayoutSize contentSize { pipeline->mScBounds.Width(), pipeline->mScBounds.Height() };
467
0
  wr::DisplayListBuilder builder(aPipelineId, contentSize);
468
0
469
0
  wr::BuiltDisplayList dl;
470
0
  wr::LayoutSize builderContentSize;
471
0
  builder.Finalize(builderContentSize, dl);
472
0
  aTxn.SetDisplayList(gfx::Color(0.f, 0.f, 0.f, 0.f),
473
0
                      epoch,
474
0
                      LayerSize(pipeline->mScBounds.Width(), pipeline->mScBounds.Height()),
475
0
                      aPipelineId, builderContentSize,
476
0
                      dl.dl_desc, dl.dl);
477
0
}
478
479
void
480
AsyncImagePipelineManager::HoldExternalImage(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch, WebRenderTextureHost* aTexture)
481
0
{
482
0
  if (mDestroyed) {
483
0
    return;
484
0
  }
485
0
  MOZ_ASSERT(aTexture);
486
0
487
0
  PipelineTexturesHolder* holder = mPipelineTexturesHolders.Get(wr::AsUint64(aPipelineId));
488
0
  MOZ_ASSERT(holder);
489
0
  if (!holder) {
490
0
    return;
491
0
  }
492
0
  // Hold WebRenderTextureHost until end of its usage on RenderThread
493
0
  holder->mTextureHosts.push(ForwardingTextureHost(aEpoch, aTexture));
494
0
}
495
496
void
497
AsyncImagePipelineManager::HoldExternalImage(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch, WebRenderTextureHostWrapper* aWrTextureWrapper)
498
0
{
499
0
  if (mDestroyed) {
500
0
    return;
501
0
  }
502
0
  MOZ_ASSERT(aWrTextureWrapper);
503
0
504
0
  PipelineTexturesHolder* holder = mPipelineTexturesHolders.Get(wr::AsUint64(aPipelineId));
505
0
  MOZ_ASSERT(holder);
506
0
  if (!holder) {
507
0
    return;
508
0
  }
509
0
  // Hold WebRenderTextureHostWrapper until end of its usage on RenderThread
510
0
  holder->mTextureHostWrappers.push(ForwardingTextureHostWrapper(aEpoch, aWrTextureWrapper));
511
0
}
512
513
void
514
AsyncImagePipelineManager::HoldExternalImage(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch, const wr::ExternalImageId& aImageId)
515
0
{
516
0
  if (mDestroyed) {
517
0
    SharedSurfacesParent::Release(aImageId);
518
0
    return;
519
0
  }
520
0
521
0
  PipelineTexturesHolder* holder = mPipelineTexturesHolders.Get(wr::AsUint64(aPipelineId));
522
0
  MOZ_ASSERT(holder);
523
0
  if (!holder) {
524
0
    SharedSurfacesParent::Release(aImageId);
525
0
    return;
526
0
  }
527
0
528
0
  holder->mExternalImages.push(ForwardingExternalImage(aEpoch, aImageId));
529
0
}
530
531
void
532
AsyncImagePipelineManager::NotifyPipelinesUpdated(wr::WrPipelineInfo aInfo)
533
0
{
534
0
  // This is called on the render thread, so we just stash the data into
535
0
  // mUpdatesQueue and process it later on the compositor thread.
536
0
  MOZ_ASSERT(wr::RenderThread::IsInRenderThread());
537
0
538
0
  MutexAutoLock lock(mUpdatesLock);
539
0
  for (uintptr_t i = 0; i < aInfo.epochs.length; i++) {
540
0
    mUpdatesQueue.push(std::make_pair(
541
0
        aInfo.epochs.data[i].pipeline_id,
542
0
        Some(aInfo.epochs.data[i].epoch)));
543
0
  }
544
0
  for (uintptr_t i = 0; i < aInfo.removed_pipelines.length; i++) {
545
0
    mUpdatesQueue.push(std::make_pair(
546
0
        aInfo.removed_pipelines.data[i],
547
0
        Nothing()));
548
0
  }
549
0
  // Queue a runnable on the compositor thread to process the queue
550
0
  layers::CompositorThreadHolder::Loop()->PostTask(
551
0
      NewRunnableMethod("ProcessPipelineUpdates",
552
0
                        this,
553
0
                        &AsyncImagePipelineManager::ProcessPipelineUpdates));
554
0
}
555
556
void
557
AsyncImagePipelineManager::ProcessPipelineUpdates()
558
0
{
559
0
  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
560
0
561
0
  if (mDestroyed) {
562
0
    return;
563
0
  }
564
0
565
0
  while (true) {
566
0
    wr::PipelineId pipelineId;
567
0
    Maybe<wr::Epoch> epoch;
568
0
569
0
    { // scope lock to extract one item from the queue
570
0
      MutexAutoLock lock(mUpdatesLock);
571
0
      if (mUpdatesQueue.empty()) {
572
0
        break;
573
0
      }
574
0
      pipelineId = mUpdatesQueue.front().first;
575
0
      epoch = mUpdatesQueue.front().second;
576
0
      mUpdatesQueue.pop();
577
0
    }
578
0
579
0
    if (epoch.isSome()) {
580
0
      ProcessPipelineRendered(pipelineId, *epoch);
581
0
    } else {
582
0
      ProcessPipelineRemoved(pipelineId);
583
0
    }
584
0
  }
585
0
}
586
587
void
588
AsyncImagePipelineManager::ProcessPipelineRendered(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch)
589
0
{
590
0
  if (auto entry = mPipelineTexturesHolders.Lookup(wr::AsUint64(aPipelineId))) {
591
0
    PipelineTexturesHolder* holder = entry.Data();
592
0
    // Release TextureHosts based on Epoch
593
0
    while (!holder->mTextureHosts.empty()) {
594
0
      if (aEpoch <= holder->mTextureHosts.front().mEpoch) {
595
0
        break;
596
0
      }
597
0
      holder->mTextureHosts.pop();
598
0
    }
599
0
    while (!holder->mTextureHostWrappers.empty()) {
600
0
      if (aEpoch <= holder->mTextureHostWrappers.front().mEpoch) {
601
0
        break;
602
0
      }
603
0
      holder->mTextureHostWrappers.pop();
604
0
    }
605
0
    while (!holder->mExternalImages.empty()) {
606
0
      if (aEpoch <= holder->mExternalImages.front().mEpoch) {
607
0
        break;
608
0
      }
609
0
      DebugOnly<bool> released =
610
0
        SharedSurfacesParent::Release(holder->mExternalImages.front().mImageId);
611
0
      MOZ_ASSERT(released);
612
0
      holder->mExternalImages.pop();
613
0
    }
614
0
  }
615
0
}
616
617
void
618
AsyncImagePipelineManager::ProcessPipelineRemoved(const wr::PipelineId& aPipelineId)
619
0
{
620
0
  if (mDestroyed) {
621
0
    return;
622
0
  }
623
0
  if (auto entry = mPipelineTexturesHolders.Lookup(wr::AsUint64(aPipelineId))) {
624
0
    PipelineTexturesHolder* holder = entry.Data();
625
0
    if (holder->mDestroyedEpoch.isSome()) {
626
0
      // Explicitly release all of the shared surfaces.
627
0
      while (!holder->mExternalImages.empty()) {
628
0
        DebugOnly<bool> released =
629
0
          SharedSurfacesParent::Release(holder->mExternalImages.front().mImageId);
630
0
        MOZ_ASSERT(released);
631
0
        holder->mExternalImages.pop();
632
0
      }
633
0
634
0
      // Remove Pipeline
635
0
      entry.Remove();
636
0
    }
637
0
    // If mDestroyedEpoch contains nothing it means we reused the same pipeline id (probably because
638
0
    // we moved the tab to another window). In this case we need to keep the holder.
639
0
  }
640
0
}
641
642
wr::Epoch
643
AsyncImagePipelineManager::GetNextImageEpoch()
644
0
{
645
0
  mAsyncImageEpoch.mHandle++;
646
0
  return mAsyncImageEpoch;
647
0
}
648
649
} // namespace layers
650
} // namespace mozilla