tor-browser

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

CanvasTranslator.cpp (62903B)


      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 https://mozilla.org/MPL/2.0/. */
      6 
      7 #include "CanvasTranslator.h"
      8 
      9 #include "gfxGradientCache.h"
     10 #include "mozilla/DataMutex.h"
     11 #include "mozilla/gfx/2D.h"
     12 #include "mozilla/gfx/CanvasManagerParent.h"
     13 #include "mozilla/gfx/CanvasRenderThread.h"
     14 #include "mozilla/gfx/DataSourceSurfaceWrapper.h"
     15 #include "mozilla/gfx/DrawTargetWebgl.h"
     16 #include "mozilla/gfx/gfxVars.h"
     17 #include "mozilla/gfx/GPUParent.h"
     18 #include "mozilla/gfx/GPUProcessManager.h"
     19 #include "mozilla/gfx/Logging.h"
     20 #include "mozilla/gfx/Swizzle.h"
     21 #include "mozilla/ipc/Endpoint.h"
     22 #include "mozilla/ipc/SharedMemoryHandle.h"
     23 #include "mozilla/layers/BufferTexture.h"
     24 #include "mozilla/layers/CanvasTranslator.h"
     25 #include "mozilla/layers/ImageDataSerializer.h"
     26 #include "mozilla/layers/SharedSurfacesParent.h"
     27 #include "mozilla/layers/TextureClient.h"
     28 #include "mozilla/layers/VideoBridgeParent.h"
     29 #include "mozilla/StaticPrefs_gfx.h"
     30 #include "mozilla/SyncRunnable.h"
     31 #include "mozilla/TaskQueue.h"
     32 #include "GLContext.h"
     33 #include "HostWebGLContext.h"
     34 #include "SharedSurface.h"
     35 #include "WebGLParent.h"
     36 #include "RecordedCanvasEventImpl.h"
     37 
     38 #if defined(XP_WIN)
     39 #  include "mozilla/gfx/DeviceManagerDx.h"
     40 #  include "mozilla/layers/TextureD3D11.h"
     41 #endif
     42 
     43 namespace mozilla {
     44 namespace layers {
     45 
     46 UniquePtr<TextureData> CanvasTranslator::CreateTextureData(
     47    const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat, bool aClear) {
     48  TextureData* textureData = nullptr;
     49  TextureAllocationFlags allocFlags =
     50      aClear ? ALLOC_CLEAR_BUFFER : ALLOC_DEFAULT;
     51  switch (mTextureType) {
     52    case TextureType::Unknown:
     53      textureData = BufferTextureData::Create(
     54          aSize, aFormat, gfx::BackendType::SKIA, LayersBackend::LAYERS_WR,
     55          TextureFlags::DEALLOCATE_CLIENT | TextureFlags::REMOTE_TEXTURE,
     56          allocFlags, nullptr);
     57      break;
     58    default:
     59      textureData = TextureData::Create(mTextureType, aFormat, aSize,
     60                                        allocFlags, mBackendType);
     61      break;
     62  }
     63 
     64  return WrapUnique(textureData);
     65 }
     66 
     67 CanvasTranslator::CanvasTranslator(
     68    layers::SharedSurfacesHolder* aSharedSurfacesHolder,
     69    const dom::ContentParentId& aContentId, uint32_t aManagerId)
     70    : mSharedSurfacesHolder(aSharedSurfacesHolder),
     71      mMaxSpinCount(StaticPrefs::gfx_canvas_remote_max_spin_count()),
     72      mContentId(aContentId),
     73      mManagerId(aManagerId),
     74      mCanvasTranslatorEventsLock(
     75          "CanvasTranslator::mCanvasTranslatorEventsLock") {
     76  mNextEventTimeout = TimeDuration::FromMilliseconds(
     77      StaticPrefs::gfx_canvas_remote_event_timeout_ms());
     78 }
     79 
     80 CanvasTranslator::~CanvasTranslator() = default;
     81 
     82 void CanvasTranslator::DispatchToTaskQueue(
     83    already_AddRefed<nsIRunnable> aRunnable) {
     84  gfx::CanvasRenderThread::Dispatch(std::move(aRunnable));
     85 }
     86 
     87 bool CanvasTranslator::IsInTaskQueue() const {
     88  return gfx::CanvasRenderThread::IsInCanvasRenderThread();
     89 }
     90 
     91 StaticRefPtr<gfx::SharedContextWebgl> CanvasTranslator::sSharedContext;
     92 
     93 bool CanvasTranslator::EnsureSharedContextWebgl() {
     94  if (!mSharedContext || mSharedContext->IsContextLost()) {
     95    if (mSharedContext) {
     96      ForceDrawTargetWebglFallback();
     97      if (mRemoteTextureOwner) {
     98        // Ensure any shared surfaces referring to the old context go away.
     99        mRemoteTextureOwner->ClearRecycledTextures();
    100      }
    101    }
    102    // Check if the global shared context is still valid. If not, instantiate
    103    // a new one before we try to use it.
    104    if (!sSharedContext || sSharedContext->IsContextLost()) {
    105      sSharedContext = gfx::SharedContextWebgl::Create();
    106    }
    107    mSharedContext = sSharedContext;
    108    // If we can't get a new context, then the only thing left to do is block
    109    // new canvases.
    110    if (!mSharedContext || mSharedContext->IsContextLost()) {
    111      mSharedContext = nullptr;
    112      BlockCanvas();
    113      return false;
    114    }
    115  }
    116  return true;
    117 }
    118 
    119 void CanvasTranslator::Shutdown() {
    120  if (sSharedContext) {
    121    gfx::CanvasRenderThread::Dispatch(NS_NewRunnableFunction(
    122        "CanvasTranslator::Shutdown", []() { sSharedContext = nullptr; }));
    123  }
    124 }
    125 
    126 mozilla::ipc::IPCResult CanvasTranslator::RecvInitTranslator(
    127    TextureType aTextureType, TextureType aWebglTextureType,
    128    gfx::BackendType aBackendType, ipc::MutableSharedMemoryHandle&& aReadHandle,
    129    nsTArray<ipc::ReadOnlySharedMemoryHandle>&& aBufferHandles,
    130    CrossProcessSemaphoreHandle&& aReaderSem,
    131    CrossProcessSemaphoreHandle&& aWriterSem) {
    132  if (mHeaderShmem) {
    133    return IPC_FAIL(this, "RecvInitTranslator called twice.");
    134  }
    135 
    136  mTextureType = aTextureType;
    137  mWebglTextureType = aWebglTextureType;
    138  mBackendType = aBackendType;
    139  mOtherPid = OtherPid();
    140 
    141  mHeaderShmem = aReadHandle.Map();
    142  if (!mHeaderShmem) {
    143    Deactivate();
    144    return IPC_FAIL(this, "Failed to map canvas header shared memory.");
    145  }
    146 
    147  mHeader = mHeaderShmem.DataAs<Header>();
    148 
    149  mWriterSemaphore.reset(CrossProcessSemaphore::Create(std::move(aWriterSem)));
    150  mWriterSemaphore->CloseHandle();
    151 
    152  mReaderSemaphore.reset(CrossProcessSemaphore::Create(std::move(aReaderSem)));
    153  mReaderSemaphore->CloseHandle();
    154 
    155  if (!CreateReferenceTexture()) {
    156    gfxCriticalNote << "GFX: CanvasTranslator failed to get device";
    157    Deactivate();
    158    return IPC_OK();
    159  }
    160 
    161  if (gfx::gfxVars::UseAcceleratedCanvas2D() && !EnsureSharedContextWebgl()) {
    162    gfxCriticalNote
    163        << "GFX: CanvasTranslator failed creating WebGL shared context";
    164  }
    165 
    166  // Use the first buffer as our current buffer.
    167  if (aBufferHandles.IsEmpty()) {
    168    Deactivate();
    169    return IPC_FAIL(this, "No canvas buffer shared memory supplied.");
    170  }
    171  mDefaultBufferSize = aBufferHandles[0].Size();
    172  auto handleIter = aBufferHandles.begin();
    173  mCurrentShmem.shmem = std::move(*handleIter).Map();
    174  if (!mCurrentShmem.shmem) {
    175    Deactivate();
    176    return IPC_FAIL(this, "Failed to map canvas buffer shared memory.");
    177  }
    178  mCurrentMemReader = mCurrentShmem.CreateMemReader();
    179 
    180  // Add all other buffers to our recycled CanvasShmems.
    181  for (handleIter++; handleIter < aBufferHandles.end(); handleIter++) {
    182    CanvasShmem newShmem;
    183    newShmem.shmem = std::move(*handleIter).Map();
    184    if (!newShmem.shmem) {
    185      Deactivate();
    186      return IPC_FAIL(this, "Failed to map canvas buffer shared memory.");
    187    }
    188    mCanvasShmems.emplace(std::move(newShmem));
    189  }
    190 
    191  if (UsePendingCanvasTranslatorEvents()) {
    192    MutexAutoLock lock(mCanvasTranslatorEventsLock);
    193    mPendingCanvasTranslatorEvents.push_back(
    194        CanvasTranslatorEvent::TranslateRecording());
    195    PostCanvasTranslatorEvents(lock);
    196  } else {
    197    DispatchToTaskQueue(
    198        NewRunnableMethod("CanvasTranslator::TranslateRecording", this,
    199                          &CanvasTranslator::TranslateRecording));
    200  }
    201  return IPC_OK();
    202 }
    203 
    204 ipc::IPCResult CanvasTranslator::RecvRestartTranslation() {
    205  if (mDeactivated) {
    206    // The other side might have sent a message before we deactivated.
    207    return IPC_OK();
    208  }
    209 
    210  if (UsePendingCanvasTranslatorEvents()) {
    211    MutexAutoLock lock(mCanvasTranslatorEventsLock);
    212    mPendingCanvasTranslatorEvents.push_back(
    213        CanvasTranslatorEvent::TranslateRecording());
    214    PostCanvasTranslatorEvents(lock);
    215  } else {
    216    DispatchToTaskQueue(
    217        NewRunnableMethod("CanvasTranslator::TranslateRecording", this,
    218                          &CanvasTranslator::TranslateRecording));
    219  }
    220 
    221  return IPC_OK();
    222 }
    223 
    224 ipc::IPCResult CanvasTranslator::RecvAddBuffer(
    225    ipc::ReadOnlySharedMemoryHandle&& aBufferHandle) {
    226  if (mDeactivated) {
    227    // The other side might have sent a resume message before we deactivated.
    228    return IPC_OK();
    229  }
    230 
    231  if (UsePendingCanvasTranslatorEvents()) {
    232    MutexAutoLock lock(mCanvasTranslatorEventsLock);
    233    mPendingCanvasTranslatorEvents.push_back(
    234        CanvasTranslatorEvent::AddBuffer(std::move(aBufferHandle)));
    235    PostCanvasTranslatorEvents(lock);
    236  } else {
    237    DispatchToTaskQueue(NewRunnableMethod<ipc::ReadOnlySharedMemoryHandle&&>(
    238        "CanvasTranslator::AddBuffer", this, &CanvasTranslator::AddBuffer,
    239        std::move(aBufferHandle)));
    240  }
    241 
    242  return IPC_OK();
    243 }
    244 
    245 bool CanvasTranslator::AddBuffer(
    246    ipc::ReadOnlySharedMemoryHandle&& aBufferHandle) {
    247  MOZ_ASSERT(IsInTaskQueue());
    248  if (mHeader->readerState == State::Failed) {
    249    // We failed before we got to the pause event.
    250    return false;
    251  }
    252 
    253  if (mHeader->readerState != State::Paused) {
    254    gfxCriticalNote << "CanvasTranslator::AddBuffer bad state "
    255                    << uint32_t(State(mHeader->readerState));
    256 #ifndef FUZZING_SNAPSHOT
    257    MOZ_DIAGNOSTIC_CRASH("mHeader->readerState == State::Paused");
    258 #endif
    259    Deactivate();
    260    return false;
    261  }
    262 
    263  MOZ_ASSERT(mDefaultBufferSize != 0);
    264 
    265  // Check and signal the writer when we finish with a buffer, because it
    266  // might have hit the buffer count limit and be waiting to use our old one.
    267  CheckAndSignalWriter();
    268 
    269  // Default sized buffers will have been queued for recycling.
    270  if (mCurrentShmem.IsValid() && mCurrentShmem.Size() == mDefaultBufferSize) {
    271    mCanvasShmems.emplace(std::move(mCurrentShmem));
    272  }
    273 
    274  CanvasShmem newShmem;
    275  newShmem.shmem = aBufferHandle.Map();
    276  if (!newShmem.shmem) {
    277    return false;
    278  }
    279 
    280  mCurrentShmem = std::move(newShmem);
    281  mCurrentMemReader = mCurrentShmem.CreateMemReader();
    282 
    283  return TranslateRecording();
    284 }
    285 
    286 ipc::IPCResult CanvasTranslator::RecvSetDataSurfaceBuffer(
    287    uint32_t aId, ipc::MutableSharedMemoryHandle&& aBufferHandle) {
    288  if (mDeactivated) {
    289    // The other side might have sent a resume message before we deactivated.
    290    return IPC_OK();
    291  }
    292 
    293  if (UsePendingCanvasTranslatorEvents()) {
    294    MutexAutoLock lock(mCanvasTranslatorEventsLock);
    295    mPendingCanvasTranslatorEvents.push_back(
    296        CanvasTranslatorEvent::SetDataSurfaceBuffer(aId,
    297                                                    std::move(aBufferHandle)));
    298    PostCanvasTranslatorEvents(lock);
    299  } else {
    300    DispatchToTaskQueue(
    301        NewRunnableMethod<uint32_t, ipc::MutableSharedMemoryHandle&&>(
    302            "CanvasTranslator::SetDataSurfaceBuffer", this,
    303            &CanvasTranslator::SetDataSurfaceBuffer, aId,
    304            std::move(aBufferHandle)));
    305  }
    306 
    307  return IPC_OK();
    308 }
    309 
    310 void CanvasTranslator::UnlinkDataSurfaceShmemOwner(
    311    const RefPtr<gfx::DataSourceSurface>& aSurface) {
    312  if (!aSurface) {
    313    return;
    314  }
    315  // Remove the associated id key
    316  aSurface->RemoveUserData(&mDataSurfaceShmemIdKey);
    317  // Force copy-on-write of contained shmem if applicable
    318  gfx::DataSourceSurface::ScopedMap map(
    319      aSurface, gfx::DataSourceSurface::MapType::READ_WRITE);
    320 }
    321 
    322 void CanvasTranslator::DataSurfaceBufferWillChange(uint32_t aId,
    323                                                   bool aKeepAlive,
    324                                                   size_t aLimit) {
    325  if (aId) {
    326    // If a specific id is changing, then attempt to copy it.
    327    auto it = mDataSurfaceShmems.find(aId);
    328    if (it != mDataSurfaceShmems.end()) {
    329      RefPtr<gfx::DataSourceSurface> owner(it->second.mOwner);
    330      if (owner) {
    331        UnlinkDataSurfaceShmemOwner(owner);
    332        it->second.mOwner = nullptr;
    333      }
    334      // Erase the shmem if it's not the last used id.
    335      if (!aKeepAlive || aId != mLastDataSurfaceShmemId) {
    336        mDataSurfaceShmems.erase(it);
    337      }
    338    }
    339  } else {
    340    // If no limit is requested, clear everything.
    341    if (!aLimit) {
    342      aLimit = mDataSurfaceShmems.size();
    343    }
    344    // Ensure all shmems still alive are copied.
    345    DataSurfaceShmem lastShmem;
    346    auto it = mDataSurfaceShmems.begin();
    347    for (; aLimit > 0 && it != mDataSurfaceShmems.end(); ++it, --aLimit) {
    348      RefPtr<gfx::DataSourceSurface> owner(it->second.mOwner);
    349      if (owner) {
    350        UnlinkDataSurfaceShmemOwner(owner);
    351        it->second.mOwner = nullptr;
    352      }
    353      // If the last shmem id needs to be kept alive, move it before clearing
    354      // the hashtable.
    355      if (aKeepAlive && it->first == mLastDataSurfaceShmemId) {
    356        lastShmem = std::move(it->second);
    357      }
    358    }
    359    // Clear all iterated shmem mappings.
    360    if (it == mDataSurfaceShmems.end()) {
    361      mDataSurfaceShmems.clear();
    362    } else if (it != mDataSurfaceShmems.begin()) {
    363      mDataSurfaceShmems.erase(mDataSurfaceShmems.begin(), it);
    364    }
    365    // Restore the last shmem id if necessary.
    366    if (lastShmem.mShmem.IsValid()) {
    367      mDataSurfaceShmems[mLastDataSurfaceShmemId] = std::move(lastShmem);
    368    }
    369  }
    370 }
    371 
    372 bool CanvasTranslator::SetDataSurfaceBuffer(
    373    uint32_t aId, ipc::MutableSharedMemoryHandle&& aBufferHandle) {
    374  MOZ_ASSERT(IsInTaskQueue());
    375  if (mHeader->readerState == State::Failed) {
    376    // We failed before we got to the pause event.
    377    return false;
    378  }
    379 
    380  if (mHeader->readerState != State::Paused) {
    381    gfxCriticalNote << "CanvasTranslator::SetDataSurfaceBuffer bad state "
    382                    << uint32_t(State(mHeader->readerState));
    383 #ifndef FUZZING_SNAPSHOT
    384    MOZ_DIAGNOSTIC_CRASH("mHeader->readerState == State::Paused");
    385 #endif
    386    Deactivate();
    387    return false;
    388  }
    389 
    390  if (!aId) {
    391    return false;
    392  }
    393 
    394  if (aId < mLastDataSurfaceShmemId) {
    395    // The id space has overflowed, so clear out all existing mapping.
    396    DataSurfaceBufferWillChange(0, false);
    397  } else if (mLastDataSurfaceShmemId != aId) {
    398    // The last shmem id will be kept alive, even if there is no owner. The last
    399    // shmem id is about to change, so if the current last id has no owner, it
    400    // needs to be erased.
    401    auto it = mDataSurfaceShmems.find(mLastDataSurfaceShmemId);
    402    if (it != mDataSurfaceShmems.end() && it->second.mOwner.IsDead()) {
    403      mDataSurfaceShmems.erase(it);
    404    }
    405    // Ensure the current shmems don't exceed the maximum allowed.
    406    size_t maxShmems = StaticPrefs::gfx_canvas_accelerated_max_data_shmems();
    407    if (maxShmems > 0 && mDataSurfaceShmems.size() >= maxShmems) {
    408      DataSurfaceBufferWillChange(0, false,
    409                                  (mDataSurfaceShmems.size() - maxShmems) + 1);
    410    }
    411  }
    412  mLastDataSurfaceShmemId = aId;
    413 
    414  // If the id is being reused, then ensure the old contents is copied before
    415  // the buffer is changed.
    416  DataSurfaceBufferWillChange(aId);
    417 
    418  // Finally, change the shmem mapping.
    419  {
    420    auto& dataSurfaceShmem = mDataSurfaceShmems[aId];
    421    dataSurfaceShmem.mShmem = aBufferHandle.Map();
    422    if (!dataSurfaceShmem.mShmem) {
    423      // Try clearing out old mappings to see if resource limits were reached.
    424      DataSurfaceBufferWillChange(0, false);
    425      // Try mapping one last time.
    426      dataSurfaceShmem.mShmem = aBufferHandle.Map();
    427      if (!dataSurfaceShmem.mShmem) {
    428        return false;
    429      }
    430    }
    431  }
    432 
    433  return TranslateRecording();
    434 }
    435 
    436 void CanvasTranslator::GetDataSurface(uint32_t aId, uint64_t aSurfaceRef) {
    437  MOZ_ASSERT(IsInTaskQueue());
    438 
    439  ReferencePtr surfaceRef = reinterpret_cast<void*>(aSurfaceRef);
    440  RefPtr<gfx::DataSourceSurface> dataSurface = LookupDataSurface(surfaceRef);
    441  if (!dataSurface) {
    442    gfx::SourceSurface* surface = LookupSourceSurface(surfaceRef);
    443    if (!surface) {
    444      return;
    445    }
    446    dataSurface = surface->GetDataSurface();
    447    if (!dataSurface) {
    448      return;
    449    }
    450  }
    451  auto dstSize = dataSurface->GetSize();
    452  gfx::SurfaceFormat format = dataSurface->GetFormat();
    453  int32_t dstStride =
    454      ImageDataSerializer::ComputeRGBStride(format, dstSize.width);
    455  auto requiredSize =
    456      ImageDataSerializer::ComputeRGBBufferSize(dstSize, format);
    457  if (requiredSize <= 0) {
    458    return;
    459  }
    460 
    461  // Ensure any old references to the shmem are copied before modification.
    462  DataSurfaceBufferWillChange(aId);
    463 
    464  // Ensure a shmem exists with the given id.
    465  auto it = mDataSurfaceShmems.find(aId);
    466  if (it == mDataSurfaceShmems.end()) {
    467    return;
    468  }
    469 
    470  // Try directly reading the data surface into shmem to avoid further copies.
    471  if (size_t(requiredSize) > it->second.mShmem.Size()) {
    472    return;
    473  }
    474 
    475  uint8_t* dst = it->second.mShmem.DataAs<uint8_t>();
    476  if (dataSurface->ReadDataInto(dst, dstStride)) {
    477    // If reading directly into the shmem, then mark the data surface as the
    478    // shmem's owner.
    479    it->second.mOwner = dataSurface;
    480    dataSurface->AddUserData(&mDataSurfaceShmemIdKey,
    481                             reinterpret_cast<void*>(aId), nullptr);
    482    return;
    483  }
    484 
    485  // Otherwise, map the data surface and do an explicit copy.
    486  gfx::DataSourceSurface::ScopedMap map(dataSurface,
    487                                        gfx::DataSourceSurface::MapType::READ);
    488  if (!map.IsMapped()) {
    489    return;
    490  }
    491 
    492  gfx::SwizzleData(map.GetData(), map.GetStride(), format, dst, dstStride,
    493                   format, dstSize);
    494 }
    495 
    496 already_AddRefed<gfx::SourceSurface> CanvasTranslator::WaitForSurface(
    497    uintptr_t aId, Maybe<layers::SurfaceDescriptor>* aDesc) {
    498  // If it's not safe to flush the event queue, then don't try to wait.
    499  if (!gfx::gfxVars::UseAcceleratedCanvas2D() ||
    500      !UsePendingCanvasTranslatorEvents() || !IsInTaskQueue()) {
    501    return nullptr;
    502  }
    503  ReferencePtr idRef(aId);
    504  ExportSurface* surf = LookupExportSurface(idRef);
    505  if (!surf || !surf->mData) {
    506    if (!HasPendingEvent()) {
    507      return nullptr;
    508    }
    509 
    510    // If the surface doesn't exist yet, that may be because the events
    511    // that produce it still need to be processed. Flush out any events
    512    // currently in the queue, that by now should have been placed in
    513    // the queue but for which processing has not yet occurred..
    514    mFlushCheckpoint = mHeader->eventCount;
    515    HandleCanvasTranslatorEvents();
    516    mFlushCheckpoint = 0;
    517    // If there is still no surface, then it is unlikely to be produced
    518    // now, so give up.
    519    surf = LookupExportSurface(idRef);
    520    if (!surf || !surf->mData) {
    521      return nullptr;
    522    }
    523  }
    524  // If we need to export a surface descriptor, then ensure we can export
    525  // to an accelerated type from the WebGL context.
    526  if (aDesc && mWebglTextureType != TextureType::Unknown && mSharedContext &&
    527      !mSharedContext->IsContextLost()) {
    528    surf->mSharedSurface =
    529        mSharedContext->ExportSharedSurface(mWebglTextureType, surf->mData);
    530    if (surf->mSharedSurface) {
    531      surf->mSharedSurface->BeginRead();
    532      *aDesc = surf->mSharedSurface->ToSurfaceDescriptor();
    533      surf->mSharedSurface->EndRead();
    534    }
    535  }
    536  return do_AddRef(surf->mData);
    537 }
    538 
    539 void CanvasTranslator::RemoveExportSurface(gfx::ReferencePtr aRefPtr) {
    540  auto it = mExportSurfaces.find(aRefPtr);
    541  if (it != mExportSurfaces.end()) {
    542    mExportSurfaces.erase(it);
    543  }
    544 }
    545 
    546 void CanvasTranslator::RecycleBuffer() {
    547  if (!mCurrentShmem.IsValid()) {
    548    return;
    549  }
    550 
    551  mCanvasShmems.emplace(std::move(mCurrentShmem));
    552  NextBuffer();
    553 }
    554 
    555 void CanvasTranslator::NextBuffer() {
    556  if (mCanvasShmems.empty()) {
    557    return;
    558  }
    559 
    560  // Check and signal the writer when we finish with a buffer, because it
    561  // might have hit the buffer count limit and be waiting to use our old one.
    562  CheckAndSignalWriter();
    563 
    564  mCurrentShmem = std::move(mCanvasShmems.front());
    565  mCanvasShmems.pop();
    566  mCurrentMemReader = mCurrentShmem.CreateMemReader();
    567 }
    568 
    569 void CanvasTranslator::ActorDestroy(ActorDestroyReason why) {
    570  MOZ_ASSERT(gfx::CanvasRenderThread::IsInCanvasRenderThread());
    571 
    572  // Since we might need to access the actor status off the owning IPDL thread,
    573  // we need to cache it here.
    574  mIPDLClosed = true;
    575 
    576  {
    577    MutexAutoLock lock(mCanvasTranslatorEventsLock);
    578    mPendingCanvasTranslatorEvents.clear();
    579  }
    580 
    581  DispatchToTaskQueue(NewRunnableMethod("CanvasTranslator::ClearTextureInfo",
    582                                        this,
    583                                        &CanvasTranslator::ClearTextureInfo));
    584 }
    585 
    586 void CanvasTranslator::Deactivate() {
    587  if (mDeactivated) {
    588    return;
    589  }
    590  mDeactivated = true;
    591  if (mHeader) {
    592    mHeader->readerState = State::Failed;
    593  }
    594 
    595  // We need to tell the other side to deactivate. Make sure the stream is
    596  // marked as bad so that the writing side won't wait for space to write.
    597  gfx::CanvasRenderThread::Dispatch(
    598      NewRunnableMethod("CanvasTranslator::SendDeactivate", this,
    599                        &CanvasTranslator::SendDeactivate));
    600 
    601  // Disable remote canvas for all.
    602  gfx::CanvasManagerParent::DisableRemoteCanvas();
    603 }
    604 
    605 inline gfx::DrawTargetWebgl* CanvasTranslator::TextureInfo::GetDrawTargetWebgl(
    606    bool aCheckForFallback) const {
    607  if ((!mTextureData || !aCheckForFallback) && mDrawTarget &&
    608      mDrawTarget->GetBackendType() == gfx::BackendType::WEBGL) {
    609    return static_cast<gfx::DrawTargetWebgl*>(mDrawTarget.get());
    610  }
    611  return nullptr;
    612 }
    613 
    614 bool CanvasTranslator::TryDrawTargetWebglFallback(
    615    const RemoteTextureOwnerId aTextureOwnerId, gfx::DrawTargetWebgl* aWebgl) {
    616  NotifyRequiresRefresh(aTextureOwnerId);
    617 
    618  const auto& info = mTextureInfo[aTextureOwnerId];
    619  if (info.mTextureData) {
    620    return true;
    621  }
    622  if (RefPtr<gfx::DrawTarget> dt =
    623          CreateFallbackDrawTarget(info.mRefPtr, aTextureOwnerId,
    624                                   aWebgl->GetSize(), aWebgl->GetFormat())) {
    625    bool success = aWebgl->CopyToFallback(dt);
    626    if (info.mRefPtr) {
    627      AddDrawTarget(info.mRefPtr, dt);
    628    }
    629    return success;
    630  }
    631  return false;
    632 }
    633 
    634 void CanvasTranslator::ForceDrawTargetWebglFallback() {
    635  // This looks for any DrawTargetWebgls that have a cached data snapshot that
    636  // can be used to recover a fallback TextureData in the event of a context
    637  // loss.
    638  RemoteTextureOwnerIdSet lost;
    639  for (const auto& entry : mTextureInfo) {
    640    const auto& ownerId = entry.first;
    641    const auto& info = entry.second;
    642    if (gfx::DrawTargetWebgl* webgl = info.GetDrawTargetWebgl()) {
    643      if (!TryDrawTargetWebglFallback(entry.first, webgl)) {
    644        // No fallback could be created, so we need to notify the compositor the
    645        // texture won't be pushed.
    646        if (mRemoteTextureOwner && mRemoteTextureOwner->IsRegistered(ownerId)) {
    647          lost.insert(ownerId);
    648        }
    649      }
    650    }
    651  }
    652  if (!lost.empty()) {
    653    NotifyDeviceReset(lost);
    654  }
    655 }
    656 
    657 void CanvasTranslator::BlockCanvas() {
    658  if (mDeactivated || mBlocked) {
    659    return;
    660  }
    661  mBlocked = true;
    662  gfx::CanvasRenderThread::Dispatch(
    663      NewRunnableMethod("CanvasTranslator::SendBlockCanvas", this,
    664                        &CanvasTranslator::SendBlockCanvas));
    665 }
    666 
    667 void CanvasTranslator::CheckAndSignalWriter() {
    668  do {
    669    switch (mHeader->writerState) {
    670      case State::Processing:
    671      case State::Failed:
    672        return;
    673      case State::AboutToWait:
    674        // The writer is making a decision about whether to wait. So, we must
    675        // wait until it has decided to avoid races. Check if the writer is
    676        // closed to avoid hangs.
    677        if (mIPDLClosed) {
    678          return;
    679        }
    680        continue;
    681      case State::Waiting:
    682        if (mHeader->processedCount >= mHeader->writerWaitCount) {
    683          mHeader->writerState = State::Processing;
    684          mWriterSemaphore->Signal();
    685        }
    686        return;
    687      default:
    688        MOZ_ASSERT_UNREACHABLE("Invalid waiting state.");
    689        return;
    690    }
    691  } while (true);
    692 }
    693 
    694 bool CanvasTranslator::HasPendingEvent() {
    695  return mHeader->processedCount < mHeader->eventCount;
    696 }
    697 
    698 bool CanvasTranslator::ReadPendingEvent(EventType& aEventType) {
    699  ReadElementConstrained(mCurrentMemReader, aEventType,
    700                         EventType::DRAWTARGETCREATION, LAST_CANVAS_EVENT_TYPE);
    701  if (!mCurrentMemReader.good()) {
    702    mHeader->readerState = State::Failed;
    703    return false;
    704  }
    705 
    706  return true;
    707 }
    708 
    709 bool CanvasTranslator::ReadNextEvent(EventType& aEventType) {
    710  MOZ_DIAGNOSTIC_ASSERT(mHeader->readerState == State::Processing);
    711 
    712  uint32_t spinCount = mMaxSpinCount;
    713  do {
    714    if (HasPendingEvent()) {
    715      return ReadPendingEvent(aEventType);
    716    }
    717  } while (--spinCount != 0);
    718 
    719  Flush();
    720  mHeader->readerState = State::AboutToWait;
    721  if (HasPendingEvent()) {
    722    mHeader->readerState = State::Processing;
    723    return ReadPendingEvent(aEventType);
    724  }
    725 
    726  if (!mIsInTransaction) {
    727    mHeader->readerState = State::Stopped;
    728    return false;
    729  }
    730 
    731  // When in a transaction we wait for a short time because we're expecting more
    732  // events from the content process. We don't want to wait for too long in case
    733  // other content processes are waiting for events to process.
    734  mHeader->readerState = State::Waiting;
    735 
    736  if (mReaderSemaphore->Wait(Some(mNextEventTimeout))) {
    737    MOZ_RELEASE_ASSERT(HasPendingEvent());
    738    MOZ_RELEASE_ASSERT(mHeader->readerState == State::Processing);
    739    return ReadPendingEvent(aEventType);
    740  }
    741 
    742  // We have to use compareExchange here because the writer can change our
    743  // state if we are waiting.
    744  if (!mHeader->readerState.compareExchange(State::Waiting, State::Stopped)) {
    745    MOZ_RELEASE_ASSERT(HasPendingEvent());
    746    MOZ_RELEASE_ASSERT(mHeader->readerState == State::Processing);
    747    // The writer has just signaled us, so consume it before returning
    748    MOZ_ALWAYS_TRUE(mReaderSemaphore->Wait());
    749    return ReadPendingEvent(aEventType);
    750  }
    751 
    752  return false;
    753 }
    754 
    755 bool CanvasTranslator::TranslateRecording() {
    756  MOZ_ASSERT(IsInTaskQueue());
    757  MOZ_DIAGNOSTIC_ASSERT_IF(mFlushCheckpoint, HasPendingEvent());
    758 
    759  if (mHeader->readerState == State::Failed) {
    760    return false;
    761  }
    762 
    763  if (mSharedContext && EnsureSharedContextWebgl()) {
    764    mSharedContext->EnterTlsScope();
    765  }
    766  auto exitTlsScope = MakeScopeExit([&] {
    767    if (mSharedContext) {
    768      mSharedContext->ExitTlsScope();
    769    }
    770  });
    771 
    772  auto start = TimeStamp::Now();
    773  mHeader->readerState = State::Processing;
    774  EventType eventType = EventType::INVALID;
    775  while (ReadNextEvent(eventType)) {
    776    bool success = RecordedEvent::DoWithEventFromReader(
    777        mCurrentMemReader, eventType,
    778        [&](RecordedEvent* recordedEvent) -> bool {
    779          // Make sure that the whole event was read from the stream.
    780          if (!mCurrentMemReader.good()) {
    781            if (mIPDLClosed) {
    782              // The other side has closed only warn about read failure.
    783              gfxWarning() << "Failed to read event type: "
    784                           << recordedEvent->GetType();
    785            } else {
    786              gfxCriticalNote << "Failed to read event type: "
    787                              << recordedEvent->GetType();
    788            }
    789            return false;
    790          }
    791 
    792          return recordedEvent->PlayEvent(this);
    793        });
    794 
    795    // Check the stream is good here or we will log the issue twice.
    796    if (!mCurrentMemReader.good()) {
    797      mHeader->readerState = State::Failed;
    798      return false;
    799    }
    800 
    801    if (!success && !HandleExtensionEvent(eventType)) {
    802      gfxCriticalNote << "Failed to play canvas event type: " << eventType;
    803 
    804      if (!mCurrentMemReader.good()) {
    805        mHeader->readerState = State::Failed;
    806        return false;
    807      }
    808    }
    809 
    810    mHeader->processedCount++;
    811 
    812    if (mHeader->readerState == State::Paused || PauseUntilSync()) {
    813      // We're waiting for an IPDL message return false, because we will resume
    814      // translation after it is received.
    815      Flush();
    816      return false;
    817    }
    818 
    819    if (mFlushCheckpoint) {
    820      // If we processed past the checkpoint return true to ensure translation
    821      // after the checkpoint resumes later.
    822      if (mHeader->processedCount >= mFlushCheckpoint) {
    823        return true;
    824      }
    825    } else {
    826      if (UsePendingCanvasTranslatorEvents()) {
    827        const auto maxDurationMs = 100;
    828        const auto now = TimeStamp::Now();
    829        const auto waitDurationMs =
    830            static_cast<uint32_t>((now - start).ToMilliseconds());
    831        if (waitDurationMs > maxDurationMs) {
    832          return true;
    833        }
    834      }
    835    }
    836  }
    837 
    838  return false;
    839 }
    840 
    841 bool CanvasTranslator::UsePendingCanvasTranslatorEvents() {
    842  return StaticPrefs::gfx_canvas_remote_use_canvas_translator_event_AtStartup();
    843 }
    844 
    845 void CanvasTranslator::PostCanvasTranslatorEvents(
    846    const MutexAutoLock& aProofOfLock) {
    847  if (mIPDLClosed) {
    848    return;
    849  }
    850 
    851  // Runnable has already been triggered.
    852  if (mCanvasTranslatorEventsRunnable) {
    853    return;
    854  }
    855 
    856  RefPtr<nsIRunnable> runnable =
    857      NewRunnableMethod("CanvasTranslator::HandleCanvasTranslatorEvents", this,
    858                        &CanvasTranslator::HandleCanvasTranslatorEvents);
    859  mCanvasTranslatorEventsRunnable = runnable;
    860 
    861  // Runnable has not been triggered yet.
    862  DispatchToTaskQueue(runnable.forget());
    863 }
    864 
    865 void CanvasTranslator::HandleCanvasTranslatorEvents() {
    866  MOZ_ASSERT(IsInTaskQueue());
    867 
    868  UniquePtr<CanvasTranslatorEvent> event;
    869  {
    870    MutexAutoLock lock(mCanvasTranslatorEventsLock);
    871    MOZ_ASSERT_IF(mIPDLClosed, mPendingCanvasTranslatorEvents.empty());
    872    if (mPendingCanvasTranslatorEvents.empty() || PauseUntilSync()) {
    873      mCanvasTranslatorEventsRunnable = nullptr;
    874      return;
    875    }
    876    auto& front = mPendingCanvasTranslatorEvents.front();
    877    event = std::move(front);
    878    mPendingCanvasTranslatorEvents.pop_front();
    879  }
    880 
    881  MOZ_RELEASE_ASSERT(event.get());
    882 
    883  bool dispatchTranslate = false;
    884  while (!dispatchTranslate && event) {
    885    switch (event->mTag) {
    886      case CanvasTranslatorEvent::Tag::TranslateRecording:
    887        dispatchTranslate = TranslateRecording();
    888        break;
    889      case CanvasTranslatorEvent::Tag::AddBuffer:
    890        dispatchTranslate = AddBuffer(event->TakeBufferHandle());
    891        break;
    892      case CanvasTranslatorEvent::Tag::SetDataSurfaceBuffer:
    893        dispatchTranslate = SetDataSurfaceBuffer(
    894            event->mId, event->TakeDataSurfaceBufferHandle());
    895        break;
    896      case CanvasTranslatorEvent::Tag::ClearCachedResources:
    897        ClearCachedResources();
    898        break;
    899      case CanvasTranslatorEvent::Tag::DropFreeBuffersWhenDormant:
    900        DropFreeBuffersWhenDormant();
    901        break;
    902    }
    903 
    904    event.reset(nullptr);
    905 
    906    {
    907      MutexAutoLock lock(mCanvasTranslatorEventsLock);
    908      MOZ_ASSERT_IF(mIPDLClosed, mPendingCanvasTranslatorEvents.empty());
    909      if (mIPDLClosed) {
    910        return;
    911      }
    912      if (PauseUntilSync()) {
    913        mCanvasTranslatorEventsRunnable = nullptr;
    914        mPendingCanvasTranslatorEvents.push_front(
    915            CanvasTranslatorEvent::TranslateRecording());
    916        return;
    917      }
    918      if (!mIPDLClosed && !dispatchTranslate &&
    919          !mPendingCanvasTranslatorEvents.empty()) {
    920        auto& front = mPendingCanvasTranslatorEvents.front();
    921        event = std::move(front);
    922        mPendingCanvasTranslatorEvents.pop_front();
    923      }
    924    }
    925  }
    926 
    927  MOZ_ASSERT(!event);
    928 
    929  {
    930    MutexAutoLock lock(mCanvasTranslatorEventsLock);
    931    mCanvasTranslatorEventsRunnable = nullptr;
    932 
    933    MOZ_ASSERT_IF(mIPDLClosed, mPendingCanvasTranslatorEvents.empty());
    934    if (mIPDLClosed) {
    935      return;
    936    }
    937 
    938    if (dispatchTranslate) {
    939      // Handle TranslateRecording at first in next
    940      // HandleCanvasTranslatorEvents().
    941      mPendingCanvasTranslatorEvents.push_front(
    942          CanvasTranslatorEvent::TranslateRecording());
    943    }
    944 
    945    if (!mPendingCanvasTranslatorEvents.empty()) {
    946      PostCanvasTranslatorEvents(lock);
    947    }
    948  }
    949 }
    950 
    951 #define READ_AND_PLAY_CANVAS_EVENT_TYPE(_typeenum, _class)             \
    952  case _typeenum: {                                                    \
    953    auto e = _class(mCurrentMemReader);                                \
    954    if (!mCurrentMemReader.good()) {                                   \
    955      if (mIPDLClosed) {                                               \
    956        /* The other side has closed only warn about read failure. */  \
    957        gfxWarning() << "Failed to read event type: " << _typeenum;    \
    958      } else {                                                         \
    959        gfxCriticalNote << "Failed to read event type: " << _typeenum; \
    960      }                                                                \
    961      return false;                                                    \
    962    }                                                                  \
    963    return e.PlayCanvasEvent(this);                                    \
    964  }
    965 
    966 bool CanvasTranslator::HandleExtensionEvent(int32_t aType) {
    967  // This is where we handle extensions to the Moz2D Recording events to handle
    968  // canvas specific things.
    969  switch (aType) {
    970    FOR_EACH_CANVAS_EVENT(READ_AND_PLAY_CANVAS_EVENT_TYPE)
    971    default:
    972      return false;
    973  }
    974 }
    975 
    976 void CanvasTranslator::BeginTransaction() {
    977  PROFILER_MARKER_TEXT("CanvasTranslator", GRAPHICS, {},
    978                       "CanvasTranslator::BeginTransaction"_ns);
    979  mIsInTransaction = true;
    980 }
    981 
    982 void CanvasTranslator::Flush() {}
    983 
    984 void CanvasTranslator::EndTransaction() {
    985  Flush();
    986  mIsInTransaction = false;
    987 }
    988 
    989 void CanvasTranslator::DeviceResetAcknowledged() {
    990  if (mRemoteTextureOwner) {
    991    mRemoteTextureOwner->NotifyContextRestored();
    992  }
    993 }
    994 
    995 bool CanvasTranslator::CreateReferenceTexture() {
    996  if (mReferenceTextureData) {
    997    if (mBaseDT) {
    998      mReferenceTextureData->ReturnDrawTarget(mBaseDT.forget());
    999    }
   1000    mReferenceTextureData->Unlock();
   1001  }
   1002 
   1003  mReferenceTextureData =
   1004      CreateTextureData(gfx::IntSize(1, 1), gfx::SurfaceFormat::B8G8R8A8, true);
   1005  if (!mReferenceTextureData) {
   1006    return false;
   1007  }
   1008 
   1009  if (NS_WARN_IF(!mReferenceTextureData->Lock(OpenMode::OPEN_READ_WRITE))) {
   1010    gfxCriticalNote << "CanvasTranslator::CreateReferenceTexture lock failed";
   1011    mReferenceTextureData.reset();
   1012    return false;
   1013  }
   1014 
   1015  mBaseDT = mReferenceTextureData->BorrowDrawTarget();
   1016  if (!mBaseDT) {
   1017    return false;
   1018  }
   1019 
   1020  return true;
   1021 }
   1022 
   1023 void CanvasTranslator::NotifyDeviceReset(const RemoteTextureOwnerIdSet& aIds) {
   1024  if (aIds.empty()) {
   1025    return;
   1026  }
   1027  if (mRemoteTextureOwner) {
   1028    mRemoteTextureOwner->NotifyContextLost(&aIds);
   1029  }
   1030  nsTArray<RemoteTextureOwnerId> idArray(aIds.size());
   1031  for (const auto& id : aIds) {
   1032    idArray.AppendElement(id);
   1033  }
   1034  gfx::CanvasRenderThread::Dispatch(
   1035      NewRunnableMethod<nsTArray<RemoteTextureOwnerId>&&>(
   1036          "CanvasTranslator::SendNotifyDeviceReset", this,
   1037          &CanvasTranslator::SendNotifyDeviceReset, std::move(idArray)));
   1038 }
   1039 
   1040 gfx::DrawTargetWebgl* CanvasTranslator::GetDrawTargetWebgl(
   1041    const RemoteTextureOwnerId aTextureOwnerId, bool aCheckForFallback) const {
   1042  auto result = mTextureInfo.find(aTextureOwnerId);
   1043  if (result != mTextureInfo.end()) {
   1044    return result->second.GetDrawTargetWebgl(aCheckForFallback);
   1045  }
   1046  return nullptr;
   1047 }
   1048 
   1049 void CanvasTranslator::NotifyRequiresRefresh(
   1050    const RemoteTextureOwnerId aTextureOwnerId, bool aDispatch) {
   1051  if (aDispatch) {
   1052    auto& info = mTextureInfo[aTextureOwnerId];
   1053    if (!info.mNotifiedRequiresRefresh) {
   1054      info.mNotifiedRequiresRefresh = true;
   1055      DispatchToTaskQueue(NewRunnableMethod<RemoteTextureOwnerId, bool>(
   1056          "CanvasTranslator::NotifyRequiresRefresh", this,
   1057          &CanvasTranslator::NotifyRequiresRefresh, aTextureOwnerId, false));
   1058    }
   1059    return;
   1060  }
   1061 
   1062  if (mTextureInfo.find(aTextureOwnerId) != mTextureInfo.end()) {
   1063    (void)SendNotifyRequiresRefresh(aTextureOwnerId);
   1064  }
   1065 }
   1066 
   1067 void CanvasTranslator::CacheSnapshotShmem(
   1068    const RemoteTextureOwnerId aTextureOwnerId, bool aDispatch) {
   1069  if (aDispatch) {
   1070    DispatchToTaskQueue(NewRunnableMethod<RemoteTextureOwnerId, bool>(
   1071        "CanvasTranslator::CacheSnapshotShmem", this,
   1072        &CanvasTranslator::CacheSnapshotShmem, aTextureOwnerId, false));
   1073    return;
   1074  }
   1075 
   1076  if (gfx::DrawTargetWebgl* webgl = GetDrawTargetWebgl(aTextureOwnerId)) {
   1077    if (auto shmemHandle = webgl->TakeShmemHandle()) {
   1078      // Lock the DT so that it doesn't get removed while shmem is in transit.
   1079      AddTextureKeepAlive(aTextureOwnerId);
   1080      nsCOMPtr<nsIThread> thread =
   1081          gfx::CanvasRenderThread::GetCanvasRenderThread();
   1082      RefPtr<CanvasTranslator> translator = this;
   1083      SendSnapshotShmem(aTextureOwnerId, std::move(shmemHandle))
   1084          ->Then(
   1085              thread, __func__,
   1086              [=](bool) {
   1087                translator->RemoveTextureKeepAlive(aTextureOwnerId);
   1088              },
   1089              [=](ipc::ResponseRejectReason) {
   1090                translator->RemoveTextureKeepAlive(aTextureOwnerId);
   1091              });
   1092    }
   1093  }
   1094 }
   1095 
   1096 void CanvasTranslator::PrepareShmem(
   1097    const RemoteTextureOwnerId aTextureOwnerId) {
   1098  if (gfx::DrawTargetWebgl* webgl =
   1099          GetDrawTargetWebgl(aTextureOwnerId, false)) {
   1100    if (RefPtr<gfx::DrawTarget> dt =
   1101            mTextureInfo[aTextureOwnerId].mFallbackDrawTarget) {
   1102      // If there was a fallback, copy the fallback to the software framebuffer
   1103      // shmem for reading.
   1104      if (RefPtr<gfx::SourceSurface> snapshot = dt->Snapshot()) {
   1105        webgl->CopySurface(snapshot, snapshot->GetRect(), gfx::IntPoint(0, 0));
   1106      }
   1107    } else {
   1108      // Otherwise, just ensure the software framebuffer is up to date.
   1109      webgl->PrepareShmem();
   1110    }
   1111  }
   1112 }
   1113 
   1114 void CanvasTranslator::CacheDataSnapshots() {
   1115  DataSurfaceBufferWillChange();
   1116 
   1117  if (mSharedContext) {
   1118    // If there are any DrawTargetWebgls, then try to cache their framebuffers
   1119    // in software surfaces, just in case the GL context is lost. So long as
   1120    // there is a software copy of the framebuffer, it can be copied into a
   1121    // fallback TextureData later even if the GL context goes away.
   1122    for (auto const& entry : mTextureInfo) {
   1123      if (gfx::DrawTargetWebgl* webgl = entry.second.GetDrawTargetWebgl()) {
   1124        webgl->EnsureDataSnapshot();
   1125      }
   1126    }
   1127  }
   1128 }
   1129 
   1130 void CanvasTranslator::ClearCachedResources() {
   1131  mUsedDataSurfaceForSurfaceDescriptor = nullptr;
   1132  mUsedWrapperForSurfaceDescriptor = nullptr;
   1133  mUsedSurfaceDescriptorForSurfaceDescriptor = Nothing();
   1134 
   1135  if (mSharedContext) {
   1136    mSharedContext->OnMemoryPressure();
   1137  }
   1138 
   1139  CacheDataSnapshots();
   1140 }
   1141 
   1142 ipc::IPCResult CanvasTranslator::RecvClearCachedResources() {
   1143  if (mDeactivated) {
   1144    // The other side might have sent a message before we deactivated.
   1145    return IPC_OK();
   1146  }
   1147 
   1148  if (UsePendingCanvasTranslatorEvents()) {
   1149    MutexAutoLock lock(mCanvasTranslatorEventsLock);
   1150    mPendingCanvasTranslatorEvents.emplace_back(
   1151        CanvasTranslatorEvent::ClearCachedResources());
   1152    PostCanvasTranslatorEvents(lock);
   1153  } else {
   1154    DispatchToTaskQueue(
   1155        NewRunnableMethod("CanvasTranslator::ClearCachedResources", this,
   1156                          &CanvasTranslator::ClearCachedResources));
   1157  }
   1158  return IPC_OK();
   1159 }
   1160 
   1161 void CanvasTranslator::DropFreeBuffersWhenDormant() { CacheDataSnapshots(); }
   1162 
   1163 ipc::IPCResult CanvasTranslator::RecvDropFreeBuffersWhenDormant() {
   1164  if (mDeactivated) {
   1165    // The other side might have sent a message before we deactivated.
   1166    return IPC_OK();
   1167  }
   1168 
   1169  if (UsePendingCanvasTranslatorEvents()) {
   1170    MutexAutoLock lock(mCanvasTranslatorEventsLock);
   1171    mPendingCanvasTranslatorEvents.emplace_back(
   1172        CanvasTranslatorEvent::DropFreeBuffersWhenDormant());
   1173    PostCanvasTranslatorEvents(lock);
   1174  } else {
   1175    DispatchToTaskQueue(
   1176        NewRunnableMethod("CanvasTranslator::DropFreeBuffersWhenDormant", this,
   1177                          &CanvasTranslator::DropFreeBuffersWhenDormant));
   1178  }
   1179  return IPC_OK();
   1180 }
   1181 
   1182 static const OpenMode kInitMode = OpenMode::OPEN_READ_WRITE;
   1183 
   1184 already_AddRefed<gfx::DrawTarget> CanvasTranslator::CreateFallbackDrawTarget(
   1185    gfx::ReferencePtr aRefPtr, const RemoteTextureOwnerId aTextureOwnerId,
   1186    const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat) {
   1187  UniquePtr<TextureData> textureData =
   1188      CreateOrRecycleTextureData(aSize, aFormat);
   1189  if (NS_WARN_IF(!textureData)) {
   1190    return nullptr;
   1191  }
   1192 
   1193  if (NS_WARN_IF(!textureData->Lock(kInitMode))) {
   1194    gfxCriticalNote << "CanvasTranslator::CreateDrawTarget lock failed";
   1195    return nullptr;
   1196  }
   1197 
   1198  RefPtr<gfx::DrawTarget> dt = textureData->BorrowDrawTarget();
   1199  if (NS_WARN_IF(!dt)) {
   1200    textureData->Unlock();
   1201    return nullptr;
   1202  }
   1203  // Recycled buffer contents may be uninitialized.
   1204  dt->ClearRect(gfx::Rect(dt->GetRect()));
   1205 
   1206  TextureInfo& info = mTextureInfo[aTextureOwnerId];
   1207  info.mRefPtr = aRefPtr;
   1208  info.mFallbackDrawTarget = dt;
   1209  info.mTextureData = std::move(textureData);
   1210  info.mTextureLockMode = kInitMode;
   1211 
   1212  return dt.forget();
   1213 }
   1214 
   1215 already_AddRefed<gfx::DrawTarget> CanvasTranslator::CreateDrawTarget(
   1216    gfx::ReferencePtr aRefPtr, const RemoteTextureOwnerId aTextureOwnerId,
   1217    const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat) {
   1218  if (!aTextureOwnerId.IsValid()) {
   1219 #ifndef FUZZING_SNAPSHOT
   1220    MOZ_DIAGNOSTIC_CRASH("No texture owner set");
   1221 #endif
   1222    return nullptr;
   1223  }
   1224 
   1225  {
   1226    auto result = mTextureInfo.find(aTextureOwnerId);
   1227    if (result != mTextureInfo.end()) {
   1228      const TextureInfo& info = result->second;
   1229      if (info.mTextureData || info.mDrawTarget) {
   1230 #ifndef FUZZING_SNAPSHOT
   1231        MOZ_DIAGNOSTIC_CRASH("DrawTarget already exists");
   1232 #endif
   1233        return nullptr;
   1234      }
   1235    }
   1236  }
   1237 
   1238  RefPtr<gfx::DrawTarget> dt;
   1239  if (gfx::gfxVars::UseAcceleratedCanvas2D()) {
   1240    if (EnsureSharedContextWebgl()) {
   1241      mSharedContext->EnterTlsScope();
   1242    }
   1243    if (RefPtr<gfx::DrawTargetWebgl> webgl =
   1244            gfx::DrawTargetWebgl::Create(aSize, aFormat, mSharedContext)) {
   1245      webgl->BeginFrame(true);
   1246      dt = webgl.forget().downcast<gfx::DrawTarget>();
   1247      if (dt) {
   1248        TextureInfo& info = mTextureInfo[aTextureOwnerId];
   1249        info.mRefPtr = aRefPtr;
   1250        info.mDrawTarget = dt;
   1251        info.mTextureLockMode = kInitMode;
   1252        CacheSnapshotShmem(aTextureOwnerId);
   1253      }
   1254    }
   1255    if (!dt) {
   1256      NotifyRequiresRefresh(aTextureOwnerId);
   1257    }
   1258  }
   1259 
   1260  if (!dt) {
   1261    dt = CreateFallbackDrawTarget(aRefPtr, aTextureOwnerId, aSize, aFormat);
   1262  }
   1263 
   1264  if (dt && aRefPtr) {
   1265    AddDrawTarget(aRefPtr, dt);
   1266  }
   1267  return dt.forget();
   1268 }
   1269 
   1270 already_AddRefed<gfx::DrawTarget> CanvasTranslator::CreateDrawTarget(
   1271    gfx::ReferencePtr aRefPtr, const gfx::IntSize& aSize,
   1272    gfx::SurfaceFormat aFormat) {
   1273 #ifndef FUZZING_SNAPSHOT
   1274  MOZ_DIAGNOSTIC_CRASH("Unexpected CreateDrawTarget call!");
   1275 #endif
   1276  return nullptr;
   1277 }
   1278 
   1279 void CanvasTranslator::NotifyTextureDestruction(
   1280    const RemoteTextureOwnerId aTextureOwnerId) {
   1281  MOZ_ASSERT(gfx::CanvasRenderThread::IsInCanvasRenderThread());
   1282 
   1283  if (mIPDLClosed) {
   1284    return;
   1285  }
   1286  (void)SendNotifyTextureDestruction(aTextureOwnerId);
   1287 }
   1288 
   1289 void CanvasTranslator::AddTextureKeepAlive(const RemoteTextureOwnerId& aId) {
   1290  auto result = mTextureInfo.find(aId);
   1291  if (result == mTextureInfo.end()) {
   1292    return;
   1293  }
   1294  auto& info = result->second;
   1295  ++info.mKeepAlive;
   1296 }
   1297 
   1298 void CanvasTranslator::RemoveTextureKeepAlive(const RemoteTextureOwnerId& aId) {
   1299  RemoveTexture(aId, 0, 0, false);
   1300 }
   1301 
   1302 void CanvasTranslator::RemoveTexture(const RemoteTextureOwnerId aTextureOwnerId,
   1303                                     RemoteTextureTxnType aTxnType,
   1304                                     RemoteTextureTxnId aTxnId,
   1305                                     bool aFinalize) {
   1306  // Don't erase the texture if still in use
   1307  auto result = mTextureInfo.find(aTextureOwnerId);
   1308  if (result == mTextureInfo.end()) {
   1309    return;
   1310  }
   1311  auto& info = result->second;
   1312  if (mRemoteTextureOwner && aTxnType && aTxnId) {
   1313    mRemoteTextureOwner->WaitForTxn(aTextureOwnerId, aTxnType, aTxnId);
   1314  }
   1315  // Remove the DrawTarget only if this is being called from a recorded event
   1316  // or if there are no remaining keepalives. If this is being called only to
   1317  // remove a keepalive without forcing removal, then the DrawTarget is still
   1318  // being used by the recording.
   1319  if ((aFinalize || info.mKeepAlive <= 1) && info.mRefPtr) {
   1320    RemoveDrawTarget(info.mRefPtr);
   1321    info.mRefPtr = ReferencePtr();
   1322  }
   1323  if (--info.mKeepAlive > 0) {
   1324    return;
   1325  }
   1326  if (info.mTextureData) {
   1327    if (info.mFallbackDrawTarget) {
   1328      info.mTextureData->ReturnDrawTarget(info.mFallbackDrawTarget.forget());
   1329    }
   1330    info.mTextureData->Unlock();
   1331  }
   1332  if (mRemoteTextureOwner) {
   1333    // If this texture id was manually registered as a remote texture owner,
   1334    // unregister it so it does not stick around after the texture id goes away.
   1335    if (aTextureOwnerId.IsValid()) {
   1336      mRemoteTextureOwner->UnregisterTextureOwner(aTextureOwnerId);
   1337    }
   1338  }
   1339 
   1340  gfx::CanvasRenderThread::Dispatch(NewRunnableMethod<RemoteTextureOwnerId>(
   1341      "CanvasTranslator::NotifyTextureDestruction", this,
   1342      &CanvasTranslator::NotifyTextureDestruction, aTextureOwnerId));
   1343 
   1344  mTextureInfo.erase(result);
   1345 }
   1346 
   1347 bool CanvasTranslator::LockTexture(const RemoteTextureOwnerId aTextureOwnerId,
   1348                                   OpenMode aMode, bool aInvalidContents) {
   1349  if (aMode == OpenMode::OPEN_NONE) {
   1350    return false;
   1351  }
   1352  auto result = mTextureInfo.find(aTextureOwnerId);
   1353  if (result == mTextureInfo.end()) {
   1354    return false;
   1355  }
   1356  auto& info = result->second;
   1357  if (info.mTextureLockMode != OpenMode::OPEN_NONE) {
   1358    return (info.mTextureLockMode & aMode) == aMode;
   1359  }
   1360  if (gfx::DrawTargetWebgl* webgl = info.GetDrawTargetWebgl()) {
   1361    if (aMode & OpenMode::OPEN_WRITE) {
   1362      webgl->BeginFrame(aInvalidContents);
   1363    }
   1364  }
   1365  info.mTextureLockMode = aMode;
   1366  return true;
   1367 }
   1368 
   1369 bool CanvasTranslator::UnlockTexture(
   1370    const RemoteTextureOwnerId aTextureOwnerId) {
   1371  auto result = mTextureInfo.find(aTextureOwnerId);
   1372  if (result == mTextureInfo.end()) {
   1373    return false;
   1374  }
   1375  auto& info = result->second;
   1376  if (info.mTextureLockMode == OpenMode::OPEN_NONE) {
   1377    return false;
   1378  }
   1379 
   1380  if (gfx::DrawTargetWebgl* webgl = info.GetDrawTargetWebgl()) {
   1381    if (info.mTextureLockMode & OpenMode::OPEN_WRITE) {
   1382      webgl->EndFrame();
   1383      if (webgl->RequiresRefresh()) {
   1384        NotifyRequiresRefresh(aTextureOwnerId);
   1385      }
   1386    }
   1387  }
   1388  info.mTextureLockMode = OpenMode::OPEN_NONE;
   1389  return true;
   1390 }
   1391 
   1392 bool CanvasTranslator::PresentTexture(
   1393    const RemoteTextureOwnerId aTextureOwnerId, RemoteTextureId aId) {
   1394  AUTO_PROFILER_MARKER_TEXT("CanvasTranslator", GRAPHICS, {},
   1395                            "CanvasTranslator::PresentTexture"_ns);
   1396  auto result = mTextureInfo.find(aTextureOwnerId);
   1397  if (result == mTextureInfo.end()) {
   1398    return false;
   1399  }
   1400  auto& info = result->second;
   1401  if (gfx::DrawTargetWebgl* webgl = info.GetDrawTargetWebgl()) {
   1402    EnsureRemoteTextureOwner(aTextureOwnerId);
   1403    if (webgl->CopyToSwapChain(mWebglTextureType, aId, aTextureOwnerId,
   1404                               mRemoteTextureOwner)) {
   1405      return true;
   1406    }
   1407    if (mSharedContext && mSharedContext->IsContextLost()) {
   1408      // If the context was lost, try to create a fallback to push instead.
   1409      EnsureSharedContextWebgl();
   1410    } else {
   1411      // CopyToSwapChain failed for an unknown reason other than context loss.
   1412      // Try to read into fallback data if possible to recover, otherwise force
   1413      // the loss of the individual texture.
   1414      webgl->EnsureDataSnapshot();
   1415      if (!TryDrawTargetWebglFallback(aTextureOwnerId, webgl)) {
   1416        RemoteTextureOwnerIdSet lost = {aTextureOwnerId};
   1417        NotifyDeviceReset(lost);
   1418      }
   1419    }
   1420  }
   1421  if (TextureData* data = info.mTextureData.get()) {
   1422    PushRemoteTexture(aTextureOwnerId, data, aId, aTextureOwnerId);
   1423  }
   1424  return true;
   1425 }
   1426 
   1427 void CanvasTranslator::EnsureRemoteTextureOwner(RemoteTextureOwnerId aOwnerId) {
   1428  if (!mRemoteTextureOwner) {
   1429    mRemoteTextureOwner = new RemoteTextureOwnerClient(mOtherPid);
   1430  }
   1431  if (aOwnerId.IsValid() && !mRemoteTextureOwner->IsRegistered(aOwnerId)) {
   1432    mRemoteTextureOwner->RegisterTextureOwner(aOwnerId,
   1433                                              /* aSharedRecycling */ true);
   1434  }
   1435 }
   1436 
   1437 UniquePtr<TextureData> CanvasTranslator::CreateOrRecycleTextureData(
   1438    const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat) {
   1439  if (mRemoteTextureOwner) {
   1440    if (mTextureType == TextureType::Unknown) {
   1441      return mRemoteTextureOwner->CreateOrRecycleBufferTextureData(aSize,
   1442                                                                   aFormat);
   1443    }
   1444    if (UniquePtr<TextureData> data =
   1445            mRemoteTextureOwner->GetRecycledTextureData(aSize, aFormat,
   1446                                                        mTextureType)) {
   1447      return data;
   1448    }
   1449  }
   1450  return CreateTextureData(aSize, aFormat, false);
   1451 }
   1452 
   1453 bool CanvasTranslator::PushRemoteTexture(
   1454    const RemoteTextureOwnerId aTextureOwnerId, TextureData* aData,
   1455    RemoteTextureId aId, RemoteTextureOwnerId aOwnerId) {
   1456  EnsureRemoteTextureOwner(aOwnerId);
   1457  TextureData::Info info;
   1458  aData->FillInfo(info);
   1459  UniquePtr<TextureData> dstData =
   1460      CreateOrRecycleTextureData(info.size, info.format);
   1461  bool success = false;
   1462  // Source data is already locked.
   1463  if (dstData) {
   1464    if (dstData->Lock(OpenMode::OPEN_WRITE)) {
   1465      if (RefPtr<gfx::DrawTarget> dstDT = dstData->BorrowDrawTarget()) {
   1466        if (RefPtr<gfx::DrawTarget> srcDT = aData->BorrowDrawTarget()) {
   1467          if (RefPtr<gfx::SourceSurface> snapshot = srcDT->Snapshot()) {
   1468            dstDT->CopySurface(snapshot, snapshot->GetRect(),
   1469                               gfx::IntPoint(0, 0));
   1470            dstDT->Flush();
   1471            success = true;
   1472          }
   1473        }
   1474      }
   1475      dstData->Unlock();
   1476    } else {
   1477      gfxCriticalNote << "CanvasTranslator::PushRemoteTexture dst lock failed";
   1478    }
   1479  }
   1480  if (success) {
   1481    mRemoteTextureOwner->PushTexture(aId, aOwnerId, std::move(dstData));
   1482  } else {
   1483    mRemoteTextureOwner->PushDummyTexture(aId, aOwnerId);
   1484  }
   1485  return success;
   1486 }
   1487 
   1488 void CanvasTranslator::ClearTextureInfo() {
   1489  MOZ_ASSERT(mIPDLClosed);
   1490 
   1491  DataSurfaceBufferWillChange(0, false);
   1492 
   1493  mUsedDataSurfaceForSurfaceDescriptor = nullptr;
   1494  mUsedWrapperForSurfaceDescriptor = nullptr;
   1495  mUsedSurfaceDescriptorForSurfaceDescriptor = Nothing();
   1496 
   1497  for (auto& entry : mTextureInfo) {
   1498    auto& info = entry.second;
   1499    if (info.mTextureData) {
   1500      if (info.mFallbackDrawTarget) {
   1501        info.mTextureData->ReturnDrawTarget(info.mFallbackDrawTarget.forget());
   1502      }
   1503      info.mTextureData->Unlock();
   1504    }
   1505  }
   1506  mTextureInfo.clear();
   1507  mDrawTargets.Clear();
   1508  mSharedContext = nullptr;
   1509  // If the global shared context's ref is the last ref left, then clear out
   1510  // any internal caches and textures from the context, but still keep it
   1511  // alive. This saves on startup costs while not contributing significantly
   1512  // to memory usage.
   1513  if (sSharedContext && sSharedContext->hasOneRef()) {
   1514    sSharedContext->ClearCaches();
   1515  }
   1516  if (mReferenceTextureData) {
   1517    if (mBaseDT) {
   1518      mReferenceTextureData->ReturnDrawTarget(mBaseDT.forget());
   1519    }
   1520    mReferenceTextureData->Unlock();
   1521  }
   1522  if (mRemoteTextureOwner) {
   1523    mRemoteTextureOwner->UnregisterAllTextureOwners();
   1524    mRemoteTextureOwner = nullptr;
   1525  }
   1526 }
   1527 
   1528 already_AddRefed<gfx::SourceSurface> CanvasTranslator::LookupExternalSurface(
   1529    uint64_t aKey) {
   1530  return mSharedSurfacesHolder->Get(wr::ToExternalImageId(aKey));
   1531 }
   1532 
   1533 // Check if the surface descriptor describes a GPUVideo texture for which we
   1534 // only have an opaque source/handle from SurfaceDescriptorRemoteDecoder to
   1535 // derive the actual texture from.
   1536 static bool SDIsSupportedRemoteDecoder(const SurfaceDescriptor& sd) {
   1537  if (sd.type() != SurfaceDescriptor::TSurfaceDescriptorGPUVideo) {
   1538    return false;
   1539  }
   1540 
   1541  const auto& sdv = sd.get_SurfaceDescriptorGPUVideo();
   1542  const auto& sdvType = sdv.type();
   1543  if (sdvType != SurfaceDescriptorGPUVideo::TSurfaceDescriptorRemoteDecoder) {
   1544    return false;
   1545  }
   1546 
   1547  const auto& sdrd = sdv.get_SurfaceDescriptorRemoteDecoder();
   1548  const auto& subdesc = sdrd.subdesc();
   1549  const auto& subdescType = subdesc.type();
   1550 
   1551  if (subdescType == RemoteDecoderVideoSubDescriptor::Tnull_t ||
   1552      subdescType ==
   1553          RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorMacIOSurface ||
   1554      subdescType == RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorD3D10) {
   1555    return true;
   1556  }
   1557 
   1558  return false;
   1559 }
   1560 
   1561 already_AddRefed<gfx::DataSourceSurface>
   1562 CanvasTranslator::MaybeRecycleDataSurfaceForSurfaceDescriptor(
   1563    TextureHost* aTextureHost,
   1564    const SurfaceDescriptorRemoteDecoder& aSurfaceDescriptor) {
   1565  if (!StaticPrefs::gfx_canvas_remote_recycle_used_data_surface()) {
   1566    return nullptr;
   1567  }
   1568 
   1569  auto& usedSurf = mUsedDataSurfaceForSurfaceDescriptor;
   1570  auto& usedWrapper = mUsedWrapperForSurfaceDescriptor;
   1571  auto& usedDescriptor = mUsedSurfaceDescriptorForSurfaceDescriptor;
   1572 
   1573  if (usedDescriptor.isSome() && usedDescriptor.ref() == aSurfaceDescriptor) {
   1574    MOZ_ASSERT(usedSurf);
   1575    MOZ_ASSERT(usedWrapper);
   1576    MOZ_ASSERT(aTextureHost->GetSize() == usedSurf->GetSize());
   1577 
   1578    // Since the data is the same as before, the DataSourceSurfaceWrapper can be
   1579    // reused.
   1580    return do_AddRef(usedWrapper);
   1581  }
   1582 
   1583  bool isYuvVideo = false;
   1584  if (aTextureHost->AsMacIOSurfaceTextureHost()) {
   1585    if (aTextureHost->GetFormat() == SurfaceFormat::NV12 ||
   1586        aTextureHost->GetFormat() == SurfaceFormat::YUY2) {
   1587      isYuvVideo = true;
   1588    }
   1589  } else if (aTextureHost->GetFormat() == gfx::SurfaceFormat::YUV420) {
   1590    isYuvVideo = true;
   1591  }
   1592 
   1593  // Reuse previously used DataSourceSurface if it is not used and same
   1594  // size/format.
   1595  bool reuseSurface = isYuvVideo && usedSurf && usedSurf->refCount() == 1 &&
   1596                      usedSurf->GetFormat() == gfx::SurfaceFormat::B8G8R8X8 &&
   1597                      aTextureHost->GetSize() == usedSurf->GetSize();
   1598  usedSurf =
   1599      aTextureHost->GetAsSurface(reuseSurface ? usedSurf.get() : nullptr);
   1600  if (NS_WARN_IF(!usedSurf)) {
   1601    usedWrapper = nullptr;
   1602    usedDescriptor = Nothing();
   1603    return nullptr;
   1604  }
   1605  // Wrap DataSourceSurface with DataSourceSurfaceWrapper to force upload in
   1606  // DrawTargetWebgl::DrawSurface().
   1607  usedDescriptor = Some(aSurfaceDescriptor);
   1608  usedWrapper = new gfx::DataSourceSurfaceWrapper(usedSurf);
   1609  return do_AddRef(usedWrapper);
   1610 }
   1611 
   1612 already_AddRefed<gfx::SourceSurface>
   1613 CanvasTranslator::LookupSourceSurfaceFromSurfaceDescriptor(
   1614    const SurfaceDescriptor& aDesc) {
   1615  if (!SDIsSupportedRemoteDecoder(aDesc)) {
   1616    return nullptr;
   1617  }
   1618 
   1619  const auto& sdrd = aDesc.get_SurfaceDescriptorGPUVideo()
   1620                         .get_SurfaceDescriptorRemoteDecoder();
   1621  const auto& subdesc = sdrd.subdesc();
   1622  const auto& subdescType = subdesc.type();
   1623 
   1624  RefPtr<VideoBridgeParent> parent =
   1625      VideoBridgeParent::GetSingleton(sdrd.source());
   1626  if (!parent) {
   1627    MOZ_ASSERT_UNREACHABLE("unexpected to be called");
   1628    gfxCriticalNote << "TexUnpackSurface failed to get VideoBridgeParent";
   1629    return nullptr;
   1630  }
   1631  RefPtr<TextureHost> texture =
   1632      parent->LookupTexture(mContentId, sdrd.handle());
   1633  if (!texture) {
   1634    MOZ_ASSERT_UNREACHABLE("unexpected to be called");
   1635    gfxCriticalNote << "TexUnpackSurface failed to get TextureHost";
   1636    return nullptr;
   1637  }
   1638 
   1639 #if defined(XP_WIN)
   1640  if (subdescType == RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorD3D10) {
   1641    auto* textureHostD3D11 = texture->AsDXGITextureHostD3D11();
   1642    if (!textureHostD3D11) {
   1643      MOZ_ASSERT_UNREACHABLE("unexpected to be called");
   1644      return nullptr;
   1645    }
   1646    auto& usedSurf = mUsedDataSurfaceForSurfaceDescriptor;
   1647    auto& usedDescriptor = mUsedSurfaceDescriptorForSurfaceDescriptor;
   1648 
   1649    // TODO reuse DataSourceSurface if no update.
   1650 
   1651    if (RefPtr<ID3D11Device> device =
   1652            gfx::DeviceManagerDx::Get()->GetCanvasDevice()) {
   1653      usedSurf = textureHostD3D11->GetAsSurfaceWithDevice(device);
   1654    } else {
   1655      usedSurf = nullptr;
   1656    }
   1657    if (!usedSurf) {
   1658      MOZ_ASSERT_UNREACHABLE("unexpected to be called");
   1659      usedDescriptor = Nothing();
   1660      return nullptr;
   1661    }
   1662    usedDescriptor = Some(sdrd);
   1663 
   1664    return do_AddRef(usedSurf);
   1665  }
   1666 #endif
   1667 
   1668  if (subdescType ==
   1669      RemoteDecoderVideoSubDescriptor::TSurfaceDescriptorMacIOSurface) {
   1670    MOZ_ASSERT(texture->AsMacIOSurfaceTextureHost());
   1671 
   1672    RefPtr<gfx::DataSourceSurface> surf =
   1673        MaybeRecycleDataSurfaceForSurfaceDescriptor(texture, sdrd);
   1674    return surf.forget();
   1675  }
   1676 
   1677  if (subdescType == RemoteDecoderVideoSubDescriptor::Tnull_t) {
   1678    RefPtr<gfx::DataSourceSurface> surf =
   1679        MaybeRecycleDataSurfaceForSurfaceDescriptor(texture, sdrd);
   1680    return surf.forget();
   1681  }
   1682 
   1683  MOZ_ASSERT_UNREACHABLE("unexpected to be called");
   1684  return nullptr;
   1685 }
   1686 
   1687 void CanvasTranslator::CheckpointReached() { CheckAndSignalWriter(); }
   1688 
   1689 void CanvasTranslator::PauseTranslation() {
   1690  mHeader->readerState = State::Paused;
   1691 }
   1692 
   1693 void CanvasTranslator::AwaitTranslationSync(uint64_t aSyncId) {
   1694  if (NS_WARN_IF(!UsePendingCanvasTranslatorEvents()) ||
   1695      NS_WARN_IF(!IsInTaskQueue()) || NS_WARN_IF(mAwaitSyncId >= aSyncId)) {
   1696    return;
   1697  }
   1698 
   1699  mAwaitSyncId = aSyncId;
   1700 }
   1701 
   1702 void CanvasTranslator::SyncTranslation(uint64_t aSyncId) {
   1703  if (NS_WARN_IF(!IsInTaskQueue()) || NS_WARN_IF(aSyncId <= mLastSyncId)) {
   1704    return;
   1705  }
   1706 
   1707  bool wasPaused = PauseUntilSync();
   1708  mLastSyncId = aSyncId;
   1709  // If translation was previously paused waiting on a sync-id, check if sync-id
   1710  // encountered requires restarting translation.
   1711  if (wasPaused && !PauseUntilSync()) {
   1712    HandleCanvasTranslatorEvents();
   1713  }
   1714 }
   1715 
   1716 mozilla::ipc::IPCResult CanvasTranslator::RecvSnapshotExternalCanvas(
   1717    uint64_t aSyncId, uint32_t aManagerId, ActorId aCanvasId) {
   1718  if (NS_WARN_IF(!IsInTaskQueue())) {
   1719    return IPC_FAIL(this,
   1720                    "RecvSnapshotExternalCanvas used outside of task queue.");
   1721  }
   1722 
   1723  // Verify that snapshot requests are not received out of order order.
   1724  if (NS_WARN_IF(aSyncId <= mLastSyncId)) {
   1725    return IPC_FAIL(this, "RecvSnapShotExternalCanvas received too late.");
   1726  }
   1727 
   1728  // Attempt to snapshot an external canvas that is associated with the same
   1729  // content process as this canvas. On success, associate it with the sync-id.
   1730  ExternalSnapshot snapshot;
   1731  if (auto* actor = gfx::CanvasManagerParent::GetCanvasActor(
   1732          mContentId, aManagerId, aCanvasId)) {
   1733    switch (actor->GetProtocolId()) {
   1734      case ProtocolId::PWebGLMsgStart:
   1735        if (auto* hostContext =
   1736                static_cast<dom::WebGLParent*>(actor)->GetHostWebGLContext()) {
   1737          if (auto* webgl = hostContext->GetWebGLContext()) {
   1738            if (mWebglTextureType != TextureType::Unknown) {
   1739              snapshot.mSharedSurface =
   1740                  webgl->GetBackBufferSnapshotSharedSurface(mWebglTextureType,
   1741                                                            true, true, true);
   1742              if (snapshot.mSharedSurface) {
   1743                snapshot.mWebgl = webgl;
   1744                snapshot.mDescriptor =
   1745                    snapshot.mSharedSurface->ToSurfaceDescriptor();
   1746              }
   1747            }
   1748            if (!snapshot.mDescriptor) {
   1749              snapshot.mData = webgl->GetBackBufferSnapshot(true);
   1750            }
   1751          }
   1752        }
   1753        break;
   1754      default:
   1755        MOZ_ASSERT_UNREACHABLE("Unsupported protocol");
   1756        break;
   1757    }
   1758  }
   1759 
   1760  if (!snapshot.mDescriptor && !snapshot.mData) {
   1761    // No available surface, but sync translation so it may resume after
   1762    // attempting snapshot.
   1763    SyncTranslation(aSyncId);
   1764    return IPC_FAIL(this, "SnapshotExternalCanvas failed to get surface.");
   1765  }
   1766 
   1767  mExternalSnapshots.insert({aSyncId, std::move(snapshot)});
   1768 
   1769  // Sync translation so it may resume with the snapshot.
   1770  SyncTranslation(aSyncId);
   1771  return IPC_OK();
   1772 }
   1773 
   1774 bool CanvasTranslator::ResolveExternalSnapshot(uint64_t aSyncId,
   1775                                               ReferencePtr aRefPtr,
   1776                                               const IntSize& aSize,
   1777                                               SurfaceFormat aFormat,
   1778                                               DrawTarget* aDT) {
   1779  MOZ_ASSERT(IsInTaskQueue());
   1780  uint64_t prevSyncId = mLastSyncId;
   1781  if (NS_WARN_IF(aSyncId > mLastSyncId)) {
   1782    // If arriving here, a previous SnapshotExternalCanvas IPDL message never
   1783    // arrived for some reason. Sync translation here to avoid locking up.
   1784    SyncTranslation(aSyncId);
   1785  }
   1786 
   1787  // Check if the snapshot was added. This should only ever be called once per
   1788  // snapshot, as it is removed from the table when resolved.
   1789  auto it = mExternalSnapshots.find(aSyncId);
   1790  if (it == mExternalSnapshots.end()) {
   1791    // There was no snapshot available, which can happen if this was called
   1792    // before or without a corresponding SnapshotExternalCanvas, or if called
   1793    // multiple times.
   1794    if (aSyncId > prevSyncId) {
   1795      gfxCriticalNoteOnce
   1796          << "External canvas snapshot resolved before creation.";
   1797    } else {
   1798      gfxCriticalNoteOnce << "Exernal canvas snapshot already resolved.";
   1799    }
   1800    return false;
   1801  }
   1802 
   1803  ExternalSnapshot snapshot = std::move(it->second);
   1804  mExternalSnapshots.erase(it);
   1805 
   1806  RefPtr<gfx::SourceSurface> resolved;
   1807  if (snapshot.mSharedSurface) {
   1808    snapshot.mSharedSurface->BeginRead();
   1809  }
   1810  if (snapshot.mDescriptor) {
   1811    if (aDT) {
   1812      resolved =
   1813          aDT->ImportSurfaceDescriptor(*snapshot.mDescriptor, aSize, aFormat);
   1814    }
   1815    if (!resolved && gfx::gfxVars::UseAcceleratedCanvas2D() &&
   1816        EnsureSharedContextWebgl()) {
   1817      // If we can't import the surface using the DT, then try using the global
   1818      // shared context to allow for a readback.
   1819      resolved = mSharedContext->ImportSurfaceDescriptor(*snapshot.mDescriptor,
   1820                                                         aSize, aFormat);
   1821    }
   1822  }
   1823  if (snapshot.mSharedSurface) {
   1824    snapshot.mSharedSurface->EndRead();
   1825    if (snapshot.mWebgl) {
   1826      snapshot.mWebgl->RecycleSnapshotSharedSurface(snapshot.mSharedSurface);
   1827    }
   1828  }
   1829  if (!resolved) {
   1830    // There was no descriptor, but check if there is at least a data surface.
   1831    resolved = snapshot.mData;
   1832  }
   1833  if (resolved) {
   1834    AddSourceSurface(aRefPtr, resolved);
   1835    return true;
   1836  }
   1837  return false;
   1838 }
   1839 
   1840 already_AddRefed<gfx::GradientStops> CanvasTranslator::GetOrCreateGradientStops(
   1841    gfx::DrawTarget* aDrawTarget, gfx::GradientStop* aRawStops,
   1842    uint32_t aNumStops, gfx::ExtendMode aExtendMode) {
   1843  MOZ_ASSERT(aDrawTarget);
   1844  nsTArray<gfx::GradientStop> rawStopArray(aRawStops, aNumStops);
   1845  return gfx::gfxGradientCache::GetOrCreateGradientStops(
   1846      aDrawTarget, rawStopArray, aExtendMode);
   1847 }
   1848 
   1849 gfx::DataSourceSurface* CanvasTranslator::LookupDataSurface(
   1850    gfx::ReferencePtr aRefPtr) {
   1851  return mDataSurfaces.GetWeak(aRefPtr);
   1852 }
   1853 
   1854 void CanvasTranslator::AddDataSurface(
   1855    gfx::ReferencePtr aRefPtr, RefPtr<gfx::DataSourceSurface>&& aSurface) {
   1856  mDataSurfaces.InsertOrUpdate(aRefPtr, std::move(aSurface));
   1857 }
   1858 
   1859 void CanvasTranslator::RemoveDataSurface(gfx::ReferencePtr aRefPtr) {
   1860  RefPtr<gfx::DataSourceSurface> surface;
   1861  if (mDataSurfaces.Remove(aRefPtr, getter_AddRefs(surface))) {
   1862    // If the data surface is the assigned owner of a shmem, and if the shmem
   1863    // is not the last shmem id used, then erase the shmem.
   1864    if (auto id = reinterpret_cast<uintptr_t>(
   1865            surface->GetUserData(&mDataSurfaceShmemIdKey))) {
   1866      if (id != mLastDataSurfaceShmemId) {
   1867        auto it = mDataSurfaceShmems.find(id);
   1868        if (it != mDataSurfaceShmems.end()) {
   1869          // If this is not the last reference to the surface, then the shmem
   1870          // contents needs to be copied.
   1871          if (!surface->hasOneRef()) {
   1872            UnlinkDataSurfaceShmemOwner(surface);
   1873          }
   1874          mDataSurfaceShmems.erase(it);
   1875        }
   1876      }
   1877    }
   1878  }
   1879 }
   1880 
   1881 }  // namespace layers
   1882 }  // namespace mozilla