Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/layers/ipc/SharedSurfacesChild.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 "SharedSurfacesChild.h"
8
#include "SharedSurfacesParent.h"
9
#include "CompositorManagerChild.h"
10
#include "mozilla/gfx/gfxVars.h"
11
#include "mozilla/layers/IpcResourceUpdateQueue.h"
12
#include "mozilla/layers/SourceSurfaceSharedData.h"
13
#include "mozilla/layers/WebRenderBridgeChild.h"
14
#include "mozilla/layers/WebRenderLayerManager.h"
15
#include "mozilla/SystemGroup.h"        // for SystemGroup
16
17
namespace mozilla {
18
namespace layers {
19
20
using namespace mozilla::gfx;
21
22
class SharedSurfacesChild::ImageKeyData final
23
{
24
public:
25
  ImageKeyData(WebRenderLayerManager* aManager,
26
               const wr::ImageKey& aImageKey)
27
    : mManager(aManager)
28
    , mImageKey(aImageKey)
29
0
  { }
30
31
  ImageKeyData(ImageKeyData&& aOther)
32
    : mManager(std::move(aOther.mManager))
33
    , mDirtyRect(std::move(aOther.mDirtyRect))
34
    , mImageKey(aOther.mImageKey)
35
0
  { }
36
37
  ImageKeyData& operator=(ImageKeyData&& aOther)
38
0
  {
39
0
    mManager = std::move(aOther.mManager);
40
0
    mDirtyRect = std::move(aOther.mDirtyRect);
41
0
    mImageKey = aOther.mImageKey;
42
0
    return *this;
43
0
  }
44
45
  void MergeDirtyRect(const Maybe<IntRect>& aDirtyRect)
46
0
  {
47
0
    if (mDirtyRect) {
48
0
      if (aDirtyRect) {
49
0
        mDirtyRect->UnionRect(mDirtyRect.ref(), aDirtyRect.ref());
50
0
      }
51
0
    } else {
52
0
      mDirtyRect = aDirtyRect;
53
0
    }
54
0
  }
55
56
  Maybe<IntRect> TakeDirtyRect()
57
0
  {
58
0
    return std::move(mDirtyRect);
59
0
  }
60
61
  ImageKeyData(const ImageKeyData&) = delete;
62
  ImageKeyData& operator=(const ImageKeyData&) = delete;
63
64
  RefPtr<WebRenderLayerManager> mManager;
65
  Maybe<IntRect> mDirtyRect;
66
  wr::ImageKey mImageKey;
67
};
68
69
class SharedSurfacesChild::SharedUserData final
70
{
71
public:
72
  explicit SharedUserData(const wr::ExternalImageId& aId)
73
    : mId(aId)
74
    , mShared(false)
75
0
  { }
76
77
  ~SharedUserData()
78
0
  {
79
0
    if (mShared) {
80
0
      mShared = false;
81
0
      if (NS_IsMainThread()) {
82
0
        SharedSurfacesChild::Unshare(mId, mKeys);
83
0
      } else {
84
0
        class DestroyRunnable final : public Runnable
85
0
        {
86
0
        public:
87
0
          DestroyRunnable(const wr::ExternalImageId& aId,
88
0
                          nsTArray<ImageKeyData>&& aKeys)
89
0
            : Runnable("SharedSurfacesChild::SharedUserData::DestroyRunnable")
90
0
            , mId(aId)
91
0
            , mKeys(std::move(aKeys))
92
0
          { }
93
0
94
0
          NS_IMETHOD Run() override
95
0
          {
96
0
            SharedSurfacesChild::Unshare(mId, mKeys);
97
0
            return NS_OK;
98
0
          }
99
0
100
0
        private:
101
0
          wr::ExternalImageId mId;
102
0
          AutoTArray<ImageKeyData, 1> mKeys;
103
0
        };
104
0
105
0
        nsCOMPtr<nsIRunnable> task = new DestroyRunnable(mId, std::move(mKeys));
106
0
        SystemGroup::Dispatch(TaskCategory::Other, task.forget());
107
0
      }
108
0
    }
109
0
  }
110
111
  const wr::ExternalImageId& Id() const
112
0
  {
113
0
    return mId;
114
0
  }
115
116
  void SetId(const wr::ExternalImageId& aId)
117
0
  {
118
0
    mId = aId;
119
0
    mKeys.Clear();
120
0
    mShared = false;
121
0
  }
122
123
  bool IsShared() const
124
0
  {
125
0
    return mShared;
126
0
  }
127
128
  void MarkShared()
129
0
  {
130
0
    MOZ_ASSERT(!mShared);
131
0
    mShared = true;
132
0
  }
133
134
  wr::ImageKey UpdateKey(WebRenderLayerManager* aManager,
135
                         wr::IpcResourceUpdateQueue& aResources,
136
                         const Maybe<IntRect>& aDirtyRect)
137
0
  {
138
0
    MOZ_ASSERT(aManager);
139
0
    MOZ_ASSERT(!aManager->IsDestroyed());
140
0
141
0
    // We iterate through all of the items to ensure we clean up the old
142
0
    // WebRenderLayerManager references. Most of the time there will be few
143
0
    // entries and this should not be particularly expensive compared to the
144
0
    // cost of duplicating image keys. In an ideal world, we would generate a
145
0
    // single key for the surface, and it would be usable on all of the
146
0
    // renderer instances. For now, we must allocate a key for each WR bridge.
147
0
    wr::ImageKey key;
148
0
    bool found = false;
149
0
    auto i = mKeys.Length();
150
0
    while (i > 0) {
151
0
      --i;
152
0
      ImageKeyData& entry = mKeys[i];
153
0
      if (entry.mManager->IsDestroyed()) {
154
0
        mKeys.RemoveElementAt(i);
155
0
      } else if (entry.mManager == aManager) {
156
0
        WebRenderBridgeChild* wrBridge = aManager->WrBridge();
157
0
        MOZ_ASSERT(wrBridge);
158
0
159
0
        // Even if the manager is the same, its underlying WebRenderBridgeChild
160
0
        // can change state. If our namespace differs, then our old key has
161
0
        // already been discarded.
162
0
        bool ownsKey = wrBridge->GetNamespace() == entry.mImageKey.mNamespace;
163
0
        if (!ownsKey) {
164
0
          entry.mImageKey = wrBridge->GetNextImageKey();
165
0
          entry.TakeDirtyRect();
166
0
          aResources.AddExternalImage(mId, entry.mImageKey);
167
0
        } else {
168
0
          entry.MergeDirtyRect(aDirtyRect);
169
0
          Maybe<IntRect> dirtyRect = entry.TakeDirtyRect();
170
0
          if (dirtyRect) {
171
0
            aResources.UpdateExternalImage(mId, entry.mImageKey,
172
0
                                           ViewAs<ImagePixel>(dirtyRect.ref()));
173
0
          }
174
0
        }
175
0
176
0
        key = entry.mImageKey;
177
0
        found = true;
178
0
      } else {
179
0
        // We don't have the resource update queue for this manager, so just
180
0
        // accumulate the dirty rects until it is requested.
181
0
        entry.MergeDirtyRect(aDirtyRect);
182
0
      }
183
0
    }
184
0
185
0
    if (!found) {
186
0
      key = aManager->WrBridge()->GetNextImageKey();
187
0
      ImageKeyData data(aManager, key);
188
0
      mKeys.AppendElement(std::move(data));
189
0
      aResources.AddExternalImage(mId, key);
190
0
    }
191
0
192
0
    return key;
193
0
  }
194
195
private:
196
  AutoTArray<ImageKeyData, 1> mKeys;
197
  wr::ExternalImageId mId;
198
  bool mShared : 1;
199
};
200
201
/* static */ void
202
SharedSurfacesChild::DestroySharedUserData(void* aClosure)
203
0
{
204
0
  MOZ_ASSERT(aClosure);
205
0
  auto data = static_cast<SharedUserData*>(aClosure);
206
0
  delete data;
207
0
}
208
209
/* static */ nsresult
210
SharedSurfacesChild::ShareInternal(SourceSurfaceSharedData* aSurface,
211
                                   SharedUserData** aUserData)
212
0
{
213
0
  MOZ_ASSERT(NS_IsMainThread());
214
0
  MOZ_ASSERT(aSurface);
215
0
  MOZ_ASSERT(aUserData);
216
0
217
0
  CompositorManagerChild* manager = CompositorManagerChild::GetInstance();
218
0
  if (NS_WARN_IF(!manager || !manager->CanSend() || !gfxVars::UseWebRender())) {
219
0
    // We cannot try to share the surface, most likely because the GPU process
220
0
    // crashed. Ideally, we would retry when it is ready, but the handles may be
221
0
    // a scarce resource, which can cause much more serious problems if we run
222
0
    // out. Better to copy into a fresh buffer later.
223
0
    aSurface->FinishedSharing();
224
0
    return NS_ERROR_NOT_INITIALIZED;
225
0
  }
226
0
227
0
  static UserDataKey sSharedKey;
228
0
  SharedUserData* data =
229
0
    static_cast<SharedUserData*>(aSurface->GetUserData(&sSharedKey));
230
0
  if (!data) {
231
0
    data = new SharedUserData(manager->GetNextExternalImageId());
232
0
    aSurface->AddUserData(&sSharedKey, data, DestroySharedUserData);
233
0
  } else if (!manager->OwnsExternalImageId(data->Id())) {
234
0
    // If the id isn't owned by us, that means the bridge was reinitialized, due
235
0
    // to the GPU process crashing. All previous mappings have been released.
236
0
    data->SetId(manager->GetNextExternalImageId());
237
0
  } else if (data->IsShared()) {
238
0
    // It has already been shared with the GPU process.
239
0
    *aUserData = data;
240
0
    return NS_OK;
241
0
  }
242
0
243
0
  // Ensure that the handle doesn't get released until after we have finished
244
0
  // sending the buffer to the GPU process and/or reallocating it.
245
0
  // FinishedSharing is not a sufficient condition because another thread may
246
0
  // decide we are done while we are in the processing of sharing our newly
247
0
  // reallocated handle. Once it goes out of scope, it may release the handle.
248
0
  SourceSurfaceSharedData::HandleLock lock(aSurface);
249
0
250
0
  // If we live in the same process, then it is a simple matter of directly
251
0
  // asking the parent instance to store a pointer to the same data, no need
252
0
  // to map the data into our memory space twice.
253
0
  auto pid = manager->OtherPid();
254
0
  if (pid == base::GetCurrentProcId()) {
255
0
    SharedSurfacesParent::AddSameProcess(data->Id(), aSurface);
256
0
    data->MarkShared();
257
0
    *aUserData = data;
258
0
    return NS_OK;
259
0
  }
260
0
261
0
  // Attempt to share a handle with the GPU process. The handle may or may not
262
0
  // be available -- it will only be available if it is either not yet finalized
263
0
  // and/or if it has been finalized but never used for drawing in process.
264
0
  ipc::SharedMemoryBasic::Handle handle = ipc::SharedMemoryBasic::NULLHandle();
265
0
  nsresult rv = aSurface->ShareToProcess(pid, handle);
266
0
  if (rv == NS_ERROR_NOT_AVAILABLE) {
267
0
    // It is at least as expensive to copy the image to the GPU process if we
268
0
    // have already closed the handle necessary to share, but if we reallocate
269
0
    // the shared buffer to get a new handle, we can save some memory.
270
0
    if (NS_WARN_IF(!aSurface->ReallocHandle())) {
271
0
      return NS_ERROR_OUT_OF_MEMORY;
272
0
    }
273
0
274
0
    // Reattempt the sharing of the handle to the GPU process.
275
0
    rv = aSurface->ShareToProcess(pid, handle);
276
0
  }
277
0
278
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
279
0
    MOZ_ASSERT(rv != NS_ERROR_NOT_AVAILABLE);
280
0
    return rv;
281
0
  }
282
0
283
0
  SurfaceFormat format = aSurface->GetFormat();
284
0
  MOZ_RELEASE_ASSERT(format == SurfaceFormat::B8G8R8X8 ||
285
0
                     format == SurfaceFormat::B8G8R8A8, "bad format");
286
0
287
0
  data->MarkShared();
288
0
  manager->SendAddSharedSurface(data->Id(),
289
0
                                SurfaceDescriptorShared(aSurface->GetSize(),
290
0
                                                        aSurface->Stride(),
291
0
                                                        format, handle));
292
0
  *aUserData = data;
293
0
  return NS_OK;
294
0
}
295
296
/* static */ void
297
SharedSurfacesChild::Share(SourceSurfaceSharedData* aSurface)
298
0
{
299
0
  MOZ_ASSERT(aSurface);
300
0
301
0
  // The IPDL actor to do sharing can only be accessed on the main thread so we
302
0
  // need to dispatch if off the main thread. However there is no real danger if
303
0
  // we end up racing because if it is already shared, this method will do
304
0
  // nothing.
305
0
  if (!NS_IsMainThread()) {
306
0
    class ShareRunnable final : public Runnable
307
0
    {
308
0
    public:
309
0
      explicit ShareRunnable(SourceSurfaceSharedData* aSurface)
310
0
        : Runnable("SharedSurfacesChild::Share")
311
0
        , mSurface(aSurface)
312
0
      { }
313
0
314
0
      NS_IMETHOD Run() override
315
0
      {
316
0
        SharedUserData* unused = nullptr;
317
0
        SharedSurfacesChild::ShareInternal(mSurface, &unused);
318
0
        return NS_OK;
319
0
      }
320
0
321
0
    private:
322
0
      RefPtr<SourceSurfaceSharedData> mSurface;
323
0
    };
324
0
325
0
    SystemGroup::Dispatch(TaskCategory::Other,
326
0
                          MakeAndAddRef<ShareRunnable>(aSurface));
327
0
    return;
328
0
  }
329
0
330
0
  SharedUserData* unused = nullptr;
331
0
  SharedSurfacesChild::ShareInternal(aSurface, &unused);
332
0
}
333
334
/* static */ nsresult
335
SharedSurfacesChild::Share(SourceSurfaceSharedData* aSurface,
336
                           WebRenderLayerManager* aManager,
337
                           wr::IpcResourceUpdateQueue& aResources,
338
                           wr::ImageKey& aKey)
339
0
{
340
0
  MOZ_ASSERT(NS_IsMainThread());
341
0
  MOZ_ASSERT(aSurface);
342
0
  MOZ_ASSERT(aManager);
343
0
344
0
  // Each time the surface changes, the producers of SourceSurfaceSharedData
345
0
  // surfaces promise to increment the invalidation counter each time the
346
0
  // surface has changed. We can use this counter to determine whether or not
347
0
  // we should update our paired ImageKey.
348
0
  Maybe<IntRect> dirtyRect = aSurface->TakeDirtyRect();
349
0
  SharedUserData* data = nullptr;
350
0
  nsresult rv = SharedSurfacesChild::ShareInternal(aSurface, &data);
351
0
  if (NS_SUCCEEDED(rv)) {
352
0
    MOZ_ASSERT(data);
353
0
    aKey = data->UpdateKey(aManager, aResources, dirtyRect);
354
0
  }
355
0
356
0
  return rv;
357
0
}
358
359
/* static */ nsresult
360
SharedSurfacesChild::Share(ImageContainer* aContainer,
361
                           WebRenderLayerManager* aManager,
362
                           wr::IpcResourceUpdateQueue& aResources,
363
                           wr::ImageKey& aKey)
364
0
{
365
0
  MOZ_ASSERT(NS_IsMainThread());
366
0
  MOZ_ASSERT(aContainer);
367
0
  MOZ_ASSERT(aManager);
368
0
369
0
  if (aContainer->IsAsync()) {
370
0
    return NS_ERROR_NOT_IMPLEMENTED;
371
0
  }
372
0
373
0
  AutoTArray<ImageContainer::OwningImage,4> images;
374
0
  aContainer->GetCurrentImages(&images);
375
0
  if (images.IsEmpty()) {
376
0
    return NS_ERROR_NOT_AVAILABLE;
377
0
  }
378
0
379
0
  RefPtr<gfx::SourceSurface> surface = images[0].mImage->GetAsSourceSurface();
380
0
  if (!surface) {
381
0
    return NS_ERROR_NOT_IMPLEMENTED;
382
0
  }
383
0
384
0
  if (surface->GetType() != SurfaceType::DATA_SHARED) {
385
0
    return NS_ERROR_NOT_IMPLEMENTED;
386
0
  }
387
0
388
0
  auto sharedSurface = static_cast<SourceSurfaceSharedData*>(surface.get());
389
0
  return Share(sharedSurface, aManager, aResources, aKey);
390
0
}
391
392
/* static */ nsresult
393
SharedSurfacesChild::Share(SourceSurface* aSurface,
394
                           wr::ExternalImageId& aId)
395
0
{
396
0
  MOZ_ASSERT(NS_IsMainThread());
397
0
  MOZ_ASSERT(aSurface);
398
0
399
0
  if (aSurface->GetType() != SurfaceType::DATA_SHARED) {
400
0
    return NS_ERROR_NOT_IMPLEMENTED;
401
0
  }
402
0
403
0
  // The external image ID does not change with the invalidation counter. The
404
0
  // caller of this should be aware of the invalidations of the surface through
405
0
  // another mechanism (e.g. imgRequestProxy listener notifications).
406
0
  auto sharedSurface = static_cast<SourceSurfaceSharedData*>(aSurface);
407
0
  SharedUserData* data = nullptr;
408
0
  nsresult rv = ShareInternal(sharedSurface, &data);
409
0
  if (NS_SUCCEEDED(rv)) {
410
0
    MOZ_ASSERT(data);
411
0
    aId = data->Id();
412
0
  }
413
0
414
0
  return rv;
415
0
}
416
417
/* static */ void
418
SharedSurfacesChild::Unshare(const wr::ExternalImageId& aId,
419
                             nsTArray<ImageKeyData>& aKeys)
420
0
{
421
0
  MOZ_ASSERT(NS_IsMainThread());
422
0
423
0
  for (const auto& entry : aKeys) {
424
0
    if (entry.mManager->IsDestroyed()) {
425
0
      continue;
426
0
    }
427
0
428
0
    entry.mManager->AddImageKeyForDiscard(entry.mImageKey);
429
0
    WebRenderBridgeChild* wrBridge = entry.mManager->WrBridge();
430
0
    if (wrBridge) {
431
0
      wrBridge->DeallocExternalImageId(aId);
432
0
    }
433
0
  }
434
0
435
0
  CompositorManagerChild* manager = CompositorManagerChild::GetInstance();
436
0
  if (MOZ_UNLIKELY(!manager || !manager->CanSend())) {
437
0
    return;
438
0
  }
439
0
440
0
  if (manager->OtherPid() == base::GetCurrentProcId()) {
441
0
    // We are in the combined UI/GPU process. Call directly to it to remove its
442
0
    // wrapper surface to free the underlying buffer, but only if the external
443
0
    // image ID is owned by the manager. It can be different if the surface was
444
0
    // last shared with the GPU process, which crashed several times, and its
445
0
    // job was moved into the parent process.
446
0
    if (manager->OwnsExternalImageId(aId)) {
447
0
      SharedSurfacesParent::RemoveSameProcess(aId);
448
0
    }
449
0
  } else if (manager->OwnsExternalImageId(aId)) {
450
0
    // Only attempt to release current mappings in the GPU process. It is
451
0
    // possible we had a surface that was previously shared, the GPU process
452
0
    // crashed / was restarted, and then we freed the surface. In that case
453
0
    // we know the mapping has already been freed.
454
0
    manager->SendRemoveSharedSurface(aId);
455
0
  }
456
0
}
457
458
} // namespace layers
459
} // namespace mozilla