tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

CanvasChild.cpp (26919B)


      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 "CanvasChild.h"
      8 
      9 #include "MainThreadUtils.h"
     10 #include "mozilla/dom/WorkerPrivate.h"
     11 #include "mozilla/dom/WorkerRef.h"
     12 #include "mozilla/dom/WorkerRunnable.h"
     13 #include "mozilla/gfx/CanvasManagerChild.h"
     14 #include "mozilla/gfx/CanvasShutdownManager.h"
     15 #include "mozilla/gfx/DrawTargetRecording.h"
     16 #include "mozilla/gfx/gfxVars.h"
     17 #include "mozilla/gfx/Tools.h"
     18 #include "mozilla/gfx/Rect.h"
     19 #include "mozilla/gfx/Point.h"
     20 #include "mozilla/ipc/Endpoint.h"
     21 #include "mozilla/ipc/ProcessChild.h"
     22 #include "mozilla/ipc/SharedMemoryHandle.h"
     23 #include "mozilla/layers/CanvasDrawEventRecorder.h"
     24 #include "mozilla/layers/ImageDataSerializer.h"
     25 #include "mozilla/layers/SourceSurfaceSharedData.h"
     26 #include "mozilla/AppShutdown.h"
     27 #include "mozilla/Mutex.h"
     28 #include "mozilla/StaticPrefs_gfx.h"
     29 #include "nsIObserverService.h"
     30 #include "nsICanvasRenderingContextInternal.h"
     31 #include "RecordedCanvasEventImpl.h"
     32 
     33 namespace mozilla {
     34 namespace layers {
     35 
     36 class RecorderHelpers final : public CanvasDrawEventRecorder::Helpers {
     37 public:
     38  NS_DECL_OWNINGTHREAD
     39 
     40  explicit RecorderHelpers(const RefPtr<CanvasChild>& aCanvasChild)
     41      : mCanvasChild(aCanvasChild) {}
     42 
     43  ~RecorderHelpers() override = default;
     44 
     45  bool InitTranslator(
     46      TextureType aTextureType, TextureType aWebglTextureType,
     47      gfx::BackendType aBackendType,
     48      ipc::MutableSharedMemoryHandle&& aReadHandle,
     49      nsTArray<ipc::ReadOnlySharedMemoryHandle>&& aBufferHandles,
     50      CrossProcessSemaphoreHandle&& aReaderSem,
     51      CrossProcessSemaphoreHandle&& aWriterSem) override {
     52    NS_ASSERT_OWNINGTHREAD(RecorderHelpers);
     53    if (NS_WARN_IF(!mCanvasChild)) {
     54      return false;
     55    }
     56    return mCanvasChild->SendInitTranslator(
     57        aTextureType, aWebglTextureType, aBackendType, std::move(aReadHandle),
     58        std::move(aBufferHandles), std::move(aReaderSem),
     59        std::move(aWriterSem));
     60  }
     61 
     62  bool AddBuffer(ipc::ReadOnlySharedMemoryHandle&& aBufferHandle) override {
     63    NS_ASSERT_OWNINGTHREAD(RecorderHelpers);
     64    if (!mCanvasChild) {
     65      return false;
     66    }
     67    return mCanvasChild->SendAddBuffer(std::move(aBufferHandle));
     68  }
     69 
     70  bool ReaderClosed() override {
     71    NS_ASSERT_OWNINGTHREAD(RecorderHelpers);
     72    if (!mCanvasChild) {
     73      return false;
     74    }
     75    return !mCanvasChild->CanSend() || AppShutdown::IsShutdownImpending();
     76  }
     77 
     78  bool RestartReader() override {
     79    NS_ASSERT_OWNINGTHREAD(RecorderHelpers);
     80    if (!mCanvasChild) {
     81      return false;
     82    }
     83    return mCanvasChild->SendRestartTranslation();
     84  }
     85 
     86  already_AddRefed<CanvasChild> GetCanvasChild() const override {
     87    RefPtr<CanvasChild> canvasChild(mCanvasChild);
     88    return canvasChild.forget();
     89  }
     90 
     91 private:
     92  const WeakPtr<CanvasChild> mCanvasChild;
     93 };
     94 
     95 // Limit the number of in-flight export surfaces
     96 static Atomic<uint32_t> sCurrentExportSurfaces(0);
     97 // Limit the memory used by in-flight export surfaces
     98 static Atomic<size_t> sCurrentExportSurfaceMemory(0);
     99 
    100 class SourceSurfaceCanvasRecording final : public gfx::SourceSurface {
    101 public:
    102  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceCanvasRecording, final)
    103 
    104  SourceSurfaceCanvasRecording(
    105      const RemoteTextureOwnerId aTextureOwnerId,
    106      const RefPtr<gfx::SourceSurface>& aRecordedSuface,
    107      CanvasChild* aCanvasChild,
    108      const RefPtr<CanvasDrawEventRecorder>& aRecorder)
    109      : mTextureOwnerId(aTextureOwnerId),
    110        mRecordedSurface(aRecordedSuface),
    111        mCanvasChild(aCanvasChild),
    112        mRecorder(aRecorder) {
    113    // It's important that AddStoredObject is called first because that will
    114    // run any pending processing required by recorded objects that have been
    115    // deleted off the main thread.
    116    mRecorder->AddStoredObject(this);
    117    mRecorder->RecordEvent(RecordedAddSurfaceAlias(this, aRecordedSuface));
    118  }
    119 
    120  ~SourceSurfaceCanvasRecording() {
    121    ReferencePtr surfaceAlias = this;
    122    ReferencePtr exportID = mExportID;
    123    if (NS_IsMainThread()) {
    124      ReleaseOnMainThread(std::move(mRecorder), surfaceAlias,
    125                          std::move(mRecordedSurface), std::move(mCanvasChild),
    126                          exportID);
    127      return;
    128    }
    129 
    130    mRecorder->AddPendingDeletion(
    131        [recorder = std::move(mRecorder), surfaceAlias,
    132         aliasedSurface = std::move(mRecordedSurface),
    133         canvasChild = std::move(mCanvasChild), exportID]() mutable -> void {
    134          ReleaseOnMainThread(std::move(recorder), surfaceAlias,
    135                              std::move(aliasedSurface), std::move(canvasChild),
    136                              exportID);
    137        });
    138  }
    139 
    140  gfx::SurfaceType GetType() const final { return mRecordedSurface->GetType(); }
    141 
    142  gfx::IntSize GetSize() const final { return mRecordedSurface->GetSize(); }
    143 
    144  gfx::SurfaceFormat GetFormat() const final {
    145    return mRecordedSurface->GetFormat();
    146  }
    147 
    148  already_AddRefed<gfx::DataSourceSurface> GetDataSurface() final {
    149    EnsureDataSurfaceOnMainThread();
    150    return do_AddRef(mDataSourceSurface);
    151  }
    152 
    153  void AttachSurface() { mDetached = false; }
    154  void DetachSurface() { mDetached = true; }
    155 
    156  void InvalidateDataSurface() {
    157    if (mDataSourceSurface && mMayInvalidate) {
    158      // This must be the only reference to the data left.
    159      MOZ_ASSERT(mDataSourceSurface->hasOneRef());
    160      mDataSourceSurface =
    161          gfx::Factory::CopyDataSourceSurface(mDataSourceSurface);
    162      mMayInvalidate = false;
    163    }
    164  }
    165 
    166  already_AddRefed<gfx::SourceSurface> ExtractSubrect(
    167      const gfx::IntRect& aRect) final {
    168    return mRecordedSurface->ExtractSubrect(aRect);
    169  }
    170 
    171  static size_t GetExportSurfaceSize(gfx::SourceSurface* aSurface) {
    172    return ImageDataSerializer::ComputeRGBBufferSize(aSurface->GetSize(),
    173                                                     aSurface->GetFormat());
    174  }
    175 
    176  bool GetSurfaceDescriptor(SurfaceDescriptor& aDesc) final {
    177    static Atomic<uintptr_t> sNextExportID(0);
    178    if (!mExportID) {
    179      if (++sCurrentExportSurfaces >
    180          StaticPrefs::gfx_canvas_accelerated_max_export_surfaces()) {
    181        --sCurrentExportSurfaces;
    182        return false;
    183      }
    184      size_t bytes = GetExportSurfaceSize(mRecordedSurface);
    185      if ((sCurrentExportSurfaceMemory += bytes) >
    186          StaticPrefs::gfx_canvas_accelerated_max_export_surface_memory()) {
    187        --sCurrentExportSurfaces;
    188        sCurrentExportSurfaceMemory -= bytes;
    189        return false;
    190      }
    191      mExportID = gfx::ReferencePtr(++sNextExportID);
    192      mRecorder->RecordEvent(RecordedAddExportSurface(mExportID, this));
    193    }
    194    aDesc = SurfaceDescriptorCanvasSurface(
    195        static_cast<gfx::CanvasManagerChild*>(mCanvasChild->Manager())->Id(),
    196        mCanvasChild->Id(), uintptr_t(mExportID));
    197    return true;
    198  }
    199 
    200 private:
    201  void EnsureDataSurfaceOnMainThread() {
    202    // The data can only be retrieved on the main thread.
    203    if (!mDataSourceSurface && NS_IsMainThread()) {
    204      mDataSourceSurface = mCanvasChild->GetDataSurface(
    205          mTextureOwnerId, mRecordedSurface, mDetached, mMayInvalidate);
    206    }
    207  }
    208 
    209  // Used to ensure that clean-up that requires it is done on the main thread.
    210  static void ReleaseOnMainThread(RefPtr<CanvasDrawEventRecorder> aRecorder,
    211                                  ReferencePtr aSurfaceAlias,
    212                                  RefPtr<gfx::SourceSurface> aAliasedSurface,
    213                                  RefPtr<CanvasChild> aCanvasChild,
    214                                  ReferencePtr aExportID) {
    215    MOZ_ASSERT(NS_IsMainThread());
    216 
    217    aRecorder->RemoveStoredObject(aSurfaceAlias);
    218    aRecorder->RecordEvent(RecordedRemoveSurfaceAlias(aSurfaceAlias));
    219    if (aExportID) {
    220      aRecorder->RecordEvent(RecordedRemoveExportSurface(aExportID));
    221      --sCurrentExportSurfaces;
    222      size_t bytes = GetExportSurfaceSize(aAliasedSurface);
    223      sCurrentExportSurfaceMemory -= bytes;
    224    }
    225    aAliasedSurface = nullptr;
    226    aCanvasChild = nullptr;
    227    aRecorder = nullptr;
    228  }
    229 
    230  const RemoteTextureOwnerId mTextureOwnerId;
    231  RefPtr<gfx::SourceSurface> mRecordedSurface;
    232  RefPtr<CanvasChild> mCanvasChild;
    233  RefPtr<CanvasDrawEventRecorder> mRecorder;
    234  RefPtr<gfx::DataSourceSurface> mDataSourceSurface;
    235  bool mDetached = false;
    236  bool mMayInvalidate = false;
    237  ReferencePtr mExportID;
    238 };
    239 
    240 class CanvasDataShmemHolder {
    241 public:
    242  CanvasDataShmemHolder(
    243      const std::shared_ptr<ipc::ReadOnlySharedMemoryMapping>& aShmem,
    244      CanvasChild* aCanvasChild)
    245      : mMutex("CanvasChild::DataShmemHolder::mMutex"),
    246        mShmem(aShmem),
    247        mCanvasChild(aCanvasChild) {}
    248 
    249  bool Init(dom::ThreadSafeWorkerRef* aWorkerRef) {
    250    if (!aWorkerRef) {
    251      return true;
    252    }
    253 
    254    RefPtr<dom::StrongWorkerRef> workerRef = dom::StrongWorkerRef::Create(
    255        aWorkerRef->Private(), "CanvasChild::DataShmemHolder",
    256        [this]() { DestroyWorker(); });
    257    if (NS_WARN_IF(!workerRef)) {
    258      return false;
    259    }
    260 
    261    MutexAutoLock lock(mMutex);
    262    mWorkerRef = new dom::ThreadSafeWorkerRef(workerRef);
    263    return true;
    264  }
    265 
    266  void Destroy() {
    267    class DestroyRunnable final : public dom::WorkerThreadRunnable {
    268     public:
    269      explicit DestroyRunnable(CanvasDataShmemHolder* aShmemHolder)
    270          : dom::WorkerThreadRunnable("CanvasDataShmemHolder::Destroy"),
    271            mShmemHolder(aShmemHolder) {}
    272 
    273      bool WorkerRun(JSContext* aCx,
    274                     dom::WorkerPrivate* aWorkerPrivate) override {
    275        mShmemHolder->Destroy();
    276        return true;
    277      }
    278 
    279      void PostRun(JSContext* aCx, dom::WorkerPrivate* aWorkerPrivate,
    280                   bool aRunResult) override {}
    281 
    282      bool PreDispatch(dom::WorkerPrivate* aWorkerPrivate) override {
    283        return true;
    284      }
    285 
    286      void PostDispatch(dom::WorkerPrivate* aWorkerPrivate,
    287                        bool aDispatchResult) override {}
    288 
    289     private:
    290      CanvasDataShmemHolder* mShmemHolder;
    291    };
    292 
    293    mMutex.Lock();
    294 
    295    if (mCanvasChild) {
    296      if (mWorkerRef) {
    297        if (!mWorkerRef->Private()->IsOnCurrentThread()) {
    298          auto task = MakeRefPtr<DestroyRunnable>(this);
    299          dom::WorkerPrivate* worker = mWorkerRef->Private();
    300          mMutex.Unlock();
    301          task->Dispatch(worker);
    302          return;
    303        }
    304      } else if (!NS_IsMainThread()) {
    305        mMutex.Unlock();
    306        NS_DispatchToMainThread(NS_NewRunnableFunction(
    307            "CanvasDataShmemHolder::Destroy", [this]() { Destroy(); }));
    308        return;
    309      }
    310 
    311      mCanvasChild->ReturnDataSurfaceShmem(std::move(mShmem));
    312      mCanvasChild = nullptr;
    313      mWorkerRef = nullptr;
    314    }
    315 
    316    mMutex.Unlock();
    317    delete this;
    318  }
    319 
    320  void DestroyWorker() {
    321    MutexAutoLock lock(mMutex);
    322    mCanvasChild = nullptr;
    323    mWorkerRef = nullptr;
    324  }
    325 
    326 private:
    327  Mutex mMutex;
    328  std::shared_ptr<ipc::ReadOnlySharedMemoryMapping> mShmem;
    329  RefPtr<CanvasChild> mCanvasChild MOZ_GUARDED_BY(mMutex);
    330  RefPtr<dom::ThreadSafeWorkerRef> mWorkerRef MOZ_GUARDED_BY(mMutex);
    331 };
    332 
    333 CanvasChild::CanvasChild(dom::ThreadSafeWorkerRef* aWorkerRef)
    334    : mWorkerRef(aWorkerRef) {}
    335 
    336 CanvasChild::~CanvasChild() { MOZ_ASSERT(!mWorkerRef); }
    337 
    338 static void NotifyCanvasDeviceChanged() {
    339  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
    340  if (obs) {
    341    obs->NotifyObservers(nullptr, "canvas-device-reset", nullptr);
    342  }
    343 }
    344 
    345 ipc::IPCResult CanvasChild::RecvNotifyDeviceReset(
    346    const nsTArray<RemoteTextureOwnerId>& aOwnerIds) {
    347  NS_ASSERT_OWNINGTHREAD(CanvasChild);
    348 
    349  if (auto* manager = gfx::CanvasShutdownManager::MaybeGet()) {
    350    manager->OnRemoteCanvasReset(aOwnerIds);
    351  }
    352 
    353  mRecorder->RecordEvent(RecordedDeviceResetAcknowledged());
    354  return IPC_OK();
    355 }
    356 
    357 /* static */ bool CanvasChild::mDeactivated = false;
    358 
    359 ipc::IPCResult CanvasChild::RecvDeactivate() {
    360  NS_ASSERT_OWNINGTHREAD(CanvasChild);
    361 
    362  RefPtr<CanvasChild> self(this);
    363  mDeactivated = true;
    364  if (auto* cm = gfx::CanvasManagerChild::Get()) {
    365    cm->DeactivateCanvas();
    366  }
    367  NotifyCanvasDeviceChanged();
    368  return IPC_OK();
    369 }
    370 
    371 ipc::IPCResult CanvasChild::RecvBlockCanvas() {
    372  mBlocked = true;
    373  if (auto* cm = gfx::CanvasManagerChild::Get()) {
    374    cm->BlockCanvas();
    375  }
    376  return IPC_OK();
    377 }
    378 
    379 bool CanvasChild::EnsureRecorder(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
    380                                 TextureType aTextureType,
    381                                 TextureType aWebglTextureType) {
    382  NS_ASSERT_OWNINGTHREAD(CanvasChild);
    383 
    384  if (!mRecorder) {
    385    gfx::BackendType backendType =
    386        gfxPlatform::GetPlatform()->GetPreferredCanvasBackend();
    387    auto recorder = MakeRefPtr<CanvasDrawEventRecorder>(mWorkerRef);
    388    if (!recorder->Init(aTextureType, aWebglTextureType, backendType,
    389                        MakeUnique<RecorderHelpers>(this))) {
    390      return false;
    391    }
    392 
    393    mRecorder = recorder.forget();
    394  }
    395 
    396  if (NS_WARN_IF(mRecorder->GetTextureType() != aTextureType)) {
    397    // The recorder has already been initialized with a different type. This can
    398    // happen if there is a device reset or fallback that causes a switch to a
    399    // different unaccelerated texture type (i.e. unknown). In that case, just
    400    // fall back to non-remote rendering.
    401    return false;
    402  }
    403 
    404  EnsureDataSurfaceShmem(aSize, aFormat);
    405 
    406  return true;
    407 }
    408 
    409 void CanvasChild::ActorDestroy(ActorDestroyReason aWhy) {
    410  NS_ASSERT_OWNINGTHREAD(CanvasChild);
    411 
    412  if (mRecorder) {
    413    mRecorder->DetachResources();
    414  }
    415  mTextureInfo.clear();
    416 }
    417 
    418 void CanvasChild::Destroy() {
    419  NS_ASSERT_OWNINGTHREAD(CanvasChild);
    420 
    421  if (CanSend()) {
    422    Send__delete__(this);
    423  }
    424 
    425  mWorkerRef = nullptr;
    426 }
    427 
    428 bool CanvasChild::EnsureBeginTransaction() {
    429  NS_ASSERT_OWNINGTHREAD(CanvasChild);
    430 
    431  if (!mIsInTransaction) {
    432    RecordEvent(RecordedCanvasBeginTransaction());
    433    mIsInTransaction = true;
    434  }
    435 
    436  return true;
    437 }
    438 
    439 void CanvasChild::EndTransaction() {
    440  NS_ASSERT_OWNINGTHREAD(CanvasChild);
    441 
    442  if (mIsInTransaction) {
    443    RecordEvent(RecordedCanvasEndTransaction());
    444    mIsInTransaction = false;
    445    mDormant = false;
    446  } else if (mRecorder) {
    447    // Schedule to drop free buffers if we have no non-empty transactions.
    448    if (!mDormant) {
    449      mDormant = true;
    450      NS_DelayedDispatchToCurrentThread(
    451          NewRunnableMethod("CanvasChild::DropFreeBuffersWhenDormant", this,
    452                            &CanvasChild::DropFreeBuffersWhenDormant),
    453          StaticPrefs::gfx_canvas_remote_drop_buffer_milliseconds());
    454    }
    455  }
    456 
    457  // If we are continuously drawing/recording, then we need to periodically
    458  // flush our external surface/image references, to ensure they actually get
    459  // freed on a timely basis.
    460  if (mRecorder) {
    461    mRecorder->ClearProcessedExternalSurfaces();
    462    mRecorder->ClearProcessedExternalImages();
    463  }
    464 
    465  ++mTransactionsSinceGetDataSurface;
    466 }
    467 
    468 void CanvasChild::DropFreeBuffersWhenDormant() {
    469  NS_ASSERT_OWNINGTHREAD(CanvasChild);
    470  // Drop any free buffers if we have not had any non-empty transactions.
    471  if (mDormant && mRecorder) {
    472    mRecorder->DropFreeBuffers();
    473    // Notify CanvasTranslator it is dormant.
    474    SendDropFreeBuffersWhenDormant();
    475  }
    476 }
    477 
    478 void CanvasChild::ClearCachedResources() {
    479  NS_ASSERT_OWNINGTHREAD(CanvasChild);
    480  if (mRecorder) {
    481    mRecorder->DropFreeBuffers();
    482    // Notify CanvasTranslator it is about to be minimized.
    483    SendClearCachedResources();
    484  }
    485 }
    486 
    487 bool CanvasChild::ShouldBeCleanedUp() const {
    488  NS_ASSERT_OWNINGTHREAD(CanvasChild);
    489 
    490  // Always return true if we've been deactivated.
    491  if (Deactivated()) {
    492    return true;
    493  }
    494 
    495  // We can only be cleaned up if nothing else references our recorder.
    496  return !mRecorder || (mRecorder->hasOneRef() && mTextureInfo.empty());
    497 }
    498 
    499 already_AddRefed<gfx::DrawTargetRecording> CanvasChild::CreateDrawTarget(
    500    const RemoteTextureOwnerId& aTextureOwnerId, gfx::IntSize aSize,
    501    gfx::SurfaceFormat aFormat) {
    502  NS_ASSERT_OWNINGTHREAD(CanvasChild);
    503  MOZ_ASSERT(mTextureInfo.find(aTextureOwnerId) == mTextureInfo.end());
    504 
    505  if (!mRecorder) {
    506    return nullptr;
    507  }
    508 
    509  RefPtr<gfx::DrawTarget> dummyDt = gfx::Factory::CreateDrawTarget(
    510      gfx::BackendType::SKIA, gfx::IntSize(1, 1), aFormat);
    511  RefPtr<gfx::DrawTargetRecording> dt = MakeAndAddRef<gfx::DrawTargetRecording>(
    512      mRecorder, aTextureOwnerId, dummyDt, aSize);
    513  dt->SetOptimizeTransform(true);
    514 
    515  mTextureInfo.insert({aTextureOwnerId, {}});
    516 
    517  return dt.forget();
    518 }
    519 
    520 size_t CanvasChild::SizeOfDataSurfaceShmem(gfx::IntSize aSize,
    521                                           gfx::SurfaceFormat aFormat) {
    522  if (!mRecorder) {
    523    return 0;
    524  }
    525  size_t sizeRequired =
    526      ImageDataSerializer::ComputeRGBBufferSize(aSize, aFormat);
    527  return sizeRequired > 0 ? ipc::shared_memory::PageAlignedSize(sizeRequired)
    528                          : 0;
    529 }
    530 
    531 bool CanvasChild::ShouldGrowDataSurfaceShmem(size_t aSizeRequired) {
    532  return aSizeRequired > 0 && (!mDataSurfaceShmemAvailable ||
    533                               mDataSurfaceShmem->Size() < aSizeRequired);
    534 }
    535 
    536 bool CanvasChild::EnsureDataSurfaceShmem(size_t aSizeRequired) {
    537  NS_ASSERT_OWNINGTHREAD(CanvasChild);
    538 
    539  if (!aSizeRequired) {
    540    return false;
    541  }
    542 
    543  if (ShouldGrowDataSurfaceShmem(aSizeRequired)) {
    544    RecordEvent(RecordedPauseTranslation());
    545    auto shmemHandle = ipc::shared_memory::Create(aSizeRequired);
    546    if (!shmemHandle) {
    547      return false;
    548    }
    549 
    550    auto roMapping = shmemHandle.AsReadOnly().Map();
    551    if (!roMapping) {
    552      return false;
    553    }
    554 
    555    auto id = ++mNextDataSurfaceShmemId;
    556    if (!id) {
    557      // If ids overflow, ensure that zero is reserved.
    558      id = ++mNextDataSurfaceShmemId;
    559    }
    560    if (!SendSetDataSurfaceBuffer(id, std::move(shmemHandle))) {
    561      return false;
    562    }
    563 
    564    mDataSurfaceShmem = std::make_shared<ipc::ReadOnlySharedMemoryMapping>(
    565        std::move(roMapping));
    566    mDataSurfaceShmemAvailable = true;
    567  }
    568 
    569  MOZ_ASSERT(mDataSurfaceShmemAvailable);
    570  return true;
    571 }
    572 
    573 bool CanvasChild::EnsureDataSurfaceShmem(gfx::IntSize aSize,
    574                                         gfx::SurfaceFormat aFormat) {
    575  size_t sizeRequired = SizeOfDataSurfaceShmem(aSize, aFormat);
    576  if (!sizeRequired) {
    577    return false;
    578  }
    579 
    580  return EnsureDataSurfaceShmem(sizeRequired);
    581 }
    582 
    583 void CanvasChild::RecordEvent(const gfx::RecordedEvent& aEvent) {
    584  NS_ASSERT_OWNINGTHREAD(CanvasChild);
    585 
    586  // We drop mRecorder in ActorDestroy to break the reference cycle.
    587  if (!mRecorder) {
    588    return;
    589  }
    590 
    591  mRecorder->RecordEvent(aEvent);
    592 }
    593 
    594 int64_t CanvasChild::CreateCheckpoint() {
    595  NS_ASSERT_OWNINGTHREAD(CanvasChild);
    596  return mRecorder->CreateCheckpoint();
    597 }
    598 
    599 already_AddRefed<gfx::DataSourceSurface> CanvasChild::GetDataSurface(
    600    const RemoteTextureOwnerId aTextureOwnerId,
    601    const gfx::SourceSurface* aSurface, bool aDetached, bool& aMayInvalidate) {
    602  NS_ASSERT_OWNINGTHREAD(CanvasChild);
    603  MOZ_ASSERT(aSurface);
    604 
    605  // mTransactionsSinceGetDataSurface is used to determine if we want to prepare
    606  // a DataSourceSurface in the GPU process up front at the end of the
    607  // transaction, but that only makes sense if the canvas JS is requesting data
    608  // in between transactions.
    609  if (!mIsInTransaction) {
    610    mTransactionsSinceGetDataSurface = 0;
    611  }
    612 
    613  if (!EnsureBeginTransaction()) {
    614    return nullptr;
    615  }
    616 
    617  gfx::IntSize ssSize = aSurface->GetSize();
    618  gfx::SurfaceFormat ssFormat = aSurface->GetFormat();
    619  auto stride = ImageDataSerializer::ComputeRGBStride(ssFormat, ssSize.width);
    620 
    621  // Shmem is only valid if the surface is the latest snapshot (not detached).
    622  if (!aDetached) {
    623    // If there is a shmem associated with this snapshot id, then we want to try
    624    // use that directly without having to allocate a new shmem for retrieval.
    625    auto it = mTextureInfo.find(aTextureOwnerId);
    626    if (it != mTextureInfo.end() && it->second.mSnapshotShmem) {
    627      const auto* shmemPtr = it->second.mSnapshotShmem->DataAs<uint8_t>();
    628      MOZ_ASSERT(shmemPtr);
    629      mRecorder->RecordEvent(RecordedPrepareShmem(aTextureOwnerId));
    630      auto checkpoint = CreateCheckpoint();
    631      if (NS_WARN_IF(!mRecorder->WaitForCheckpoint(checkpoint))) {
    632        return nullptr;
    633      }
    634      auto* closure =
    635          new CanvasDataShmemHolder(it->second.mSnapshotShmem, this);
    636      if (NS_WARN_IF(!closure->Init(mWorkerRef))) {
    637        delete closure;
    638        return nullptr;
    639      }
    640      // We can cast away the const of `shmemPtr` to match the call because the
    641      // DataSourceSurface will not be written to.
    642      RefPtr<gfx::DataSourceSurface> dataSurface =
    643          gfx::Factory::CreateWrappingDataSourceSurface(
    644              const_cast<uint8_t*>(shmemPtr), stride, ssSize, ssFormat,
    645              ReleaseDataShmemHolder, closure);
    646      aMayInvalidate = true;
    647      return dataSurface.forget();
    648    }
    649  }
    650 
    651  size_t sizeRequired = SizeOfDataSurfaceShmem(ssSize, ssFormat);
    652  if (!sizeRequired) {
    653    return nullptr;
    654  }
    655 
    656  RecordEvent(RecordedCacheDataSurface(aSurface));
    657 
    658  if (!EnsureDataSurfaceShmem(sizeRequired)) {
    659    return nullptr;
    660  }
    661 
    662  RecordEvent(RecordedGetDataForSurface(mNextDataSurfaceShmemId, aSurface));
    663  auto checkpoint = CreateCheckpoint();
    664  if (NS_WARN_IF(!mRecorder->WaitForCheckpoint(checkpoint))) {
    665    return nullptr;
    666  }
    667 
    668  auto* closure = new CanvasDataShmemHolder(mDataSurfaceShmem, this);
    669  if (NS_WARN_IF(!closure->Init(mWorkerRef))) {
    670    delete closure;
    671    return nullptr;
    672  }
    673 
    674  mDataSurfaceShmemAvailable = false;
    675 
    676  const auto* data = mDataSurfaceShmem->DataAs<uint8_t>();
    677 
    678  // We can cast away the const of `data` to match the call because the
    679  // DataSourceSurface will not be written to.
    680  RefPtr<gfx::DataSourceSurface> dataSurface =
    681      gfx::Factory::CreateWrappingDataSourceSurface(
    682          const_cast<uint8_t*>(data), stride, ssSize, ssFormat,
    683          ReleaseDataShmemHolder, closure);
    684  aMayInvalidate = false;
    685  return dataSurface.forget();
    686 }
    687 
    688 /* static */ void CanvasChild::ReleaseDataShmemHolder(void* aClosure) {
    689  auto* shmemHolder = static_cast<CanvasDataShmemHolder*>(aClosure);
    690  shmemHolder->Destroy();
    691 }
    692 
    693 already_AddRefed<gfx::SourceSurface> CanvasChild::WrapSurface(
    694    const RefPtr<gfx::SourceSurface>& aSurface,
    695    const RemoteTextureOwnerId aTextureOwnerId) {
    696  NS_ASSERT_OWNINGTHREAD(CanvasChild);
    697 
    698  if (!aSurface) {
    699    return nullptr;
    700  }
    701 
    702  return MakeAndAddRef<SourceSurfaceCanvasRecording>(aTextureOwnerId, aSurface,
    703                                                     this, mRecorder);
    704 }
    705 
    706 void CanvasChild::ReturnDataSurfaceShmem(
    707    std::shared_ptr<ipc::ReadOnlySharedMemoryMapping>&& aDataSurfaceShmem) {
    708  // We can only reuse the latest data surface shmem.
    709  if (aDataSurfaceShmem == mDataSurfaceShmem) {
    710    MOZ_ASSERT(!mDataSurfaceShmemAvailable);
    711    mDataSurfaceShmemAvailable = true;
    712  }
    713 }
    714 
    715 void CanvasChild::AttachSurface(const RefPtr<gfx::SourceSurface>& aSurface) {
    716  if (auto* surface =
    717          static_cast<SourceSurfaceCanvasRecording*>(aSurface.get())) {
    718    surface->AttachSurface();
    719  }
    720 }
    721 
    722 void CanvasChild::DetachSurface(const RefPtr<gfx::SourceSurface>& aSurface,
    723                                bool aInvalidate) {
    724  if (auto* surface =
    725          static_cast<SourceSurfaceCanvasRecording*>(aSurface.get())) {
    726    surface->DetachSurface();
    727    if (aInvalidate) {
    728      surface->InvalidateDataSurface();
    729    }
    730  }
    731 }
    732 
    733 ipc::IPCResult CanvasChild::RecvNotifyRequiresRefresh(
    734    const RemoteTextureOwnerId aTextureOwnerId) {
    735  auto it = mTextureInfo.find(aTextureOwnerId);
    736  if (it != mTextureInfo.end()) {
    737    it->second.mRequiresRefresh = true;
    738  }
    739  return IPC_OK();
    740 }
    741 
    742 bool CanvasChild::RequiresRefresh(
    743    const RemoteTextureOwnerId aTextureOwnerId) const {
    744  if (mBlocked) {
    745    return true;
    746  }
    747  auto it = mTextureInfo.find(aTextureOwnerId);
    748  if (it != mTextureInfo.end()) {
    749    return it->second.mRequiresRefresh;
    750  }
    751  return false;
    752 }
    753 
    754 ipc::IPCResult CanvasChild::RecvSnapshotShmem(
    755    const RemoteTextureOwnerId aTextureOwnerId,
    756    ipc::ReadOnlySharedMemoryHandle&& aShmemHandle,
    757    SnapshotShmemResolver&& aResolve) {
    758  auto it = mTextureInfo.find(aTextureOwnerId);
    759  if (it != mTextureInfo.end()) {
    760    auto shmem = aShmemHandle.Map();
    761    if (NS_WARN_IF(!shmem)) {
    762      shmem = nullptr;
    763    } else {
    764      it->second.mSnapshotShmem =
    765          std::make_shared<ipc::ReadOnlySharedMemoryMapping>(std::move(shmem));
    766    }
    767    aResolve(true);
    768  } else {
    769    aResolve(false);
    770  }
    771  return IPC_OK();
    772 }
    773 
    774 ipc::IPCResult CanvasChild::RecvNotifyTextureDestruction(
    775    const RemoteTextureOwnerId aTextureOwnerId) {
    776  auto it = mTextureInfo.find(aTextureOwnerId);
    777  if (it == mTextureInfo.end()) {
    778    MOZ_ASSERT(!CanSend());
    779    return IPC_OK();
    780  }
    781 
    782  mTextureInfo.erase(aTextureOwnerId);
    783  return IPC_OK();
    784 }
    785 
    786 already_AddRefed<gfx::SourceSurface> CanvasChild::SnapshotExternalCanvas(
    787    gfx::DrawTargetRecording* aTarget,
    788    nsICanvasRenderingContextInternal* aCanvas,
    789    mozilla::ipc::IProtocol* aActor) {
    790  // SnapshotExternalCanvas is only valid to use if using Accelerated Canvas2D
    791  // with the pending events queue enabled. This ensures WebGL and AC2D are
    792  // running under the same thread, and that events can be paused or resumed
    793  // while synchronizing between WebGL and AC2D.
    794  if (!gfx::gfxVars::UseAcceleratedCanvas2D() ||
    795      !StaticPrefs::gfx_canvas_remote_use_canvas_translator_event_AtStartup()) {
    796    return nullptr;
    797  }
    798 
    799  gfx::SurfaceFormat format = aCanvas->GetIsOpaque()
    800                                  ? gfx::SurfaceFormat::B8G8R8X8
    801                                  : gfx::SurfaceFormat::B8G8R8A8;
    802  gfx::IntSize size(aCanvas->GetWidth(), aCanvas->GetHeight());
    803  // Create a source sourface that will be associated with the snapshot.
    804  RefPtr<gfx::SourceSurface> surface =
    805      aTarget->CreateExternalSourceSurface(size, format);
    806  if (!surface) {
    807    return nullptr;
    808  }
    809 
    810  // Pause translation until the sync-id identifying the snapshot is received.
    811  uint64_t syncId = ++mLastSyncId;
    812  mRecorder->RecordEvent(RecordedAwaitTranslationSync(syncId));
    813 
    814  // Flush WebGL to cause any IPDL messages to get sent at this sync point.
    815  aCanvas->SyncSnapshot();
    816 
    817  // Once the IPDL message is sent to generate the snapshot, resolve the sync-id
    818  // to a surface in the recording stream. The AwaitTranslationSync above will
    819  // ensure this event is not translated until the snapshot is generated first.
    820  mRecorder->RecordEvent(aTarget,
    821                         RecordedResolveExternalSnapshot(
    822                             syncId, gfx::ReferencePtr(surface), size, format));
    823 
    824  uint32_t managerId = static_cast<gfx::CanvasManagerChild*>(Manager())->Id();
    825  ActorId canvasId = aActor->Id();
    826 
    827  // Actually send the request via IPDL to snapshot the external WebGL canvas.
    828  SendSnapshotExternalCanvas(syncId, managerId, canvasId);
    829 
    830  return surface.forget();
    831 }
    832 
    833 }  // namespace layers
    834 }  // namespace mozilla