tor-browser

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

TextureClient.cpp (60869B)


      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 "mozilla/layers/TextureClient.h"
      8 
      9 #include <stdint.h>  // for uint8_t, uint32_t, etc
     10 
     11 #include "BufferTexture.h"
     12 #include "IPDLActor.h"
     13 #include "ImageContainer.h"  // for PlanarYCbCrData, etc
     14 #include "MainThreadUtils.h"
     15 #include "gfx2DGlue.h"
     16 #include "gfxPlatform.h"  // for gfxPlatform
     17 #include "gfxUtils.h"     // for gfxUtils::GetAsLZ4Base64Str
     18 #include "mozilla/Atomics.h"
     19 #include "mozilla/Mutex.h"
     20 #include "mozilla/ProfilerLabels.h"
     21 #include "mozilla/SchedulerGroup.h"
     22 #include "mozilla/StaticPrefs_gfx.h"
     23 #include "mozilla/StaticPrefs_layers.h"
     24 #include "mozilla/gfx/2D.h"
     25 #include "mozilla/gfx/DataSurfaceHelpers.h"  // for CreateDataSourceSurfaceByCloning
     26 #include "mozilla/gfx/Logging.h"             // for gfxDebug
     27 #include "mozilla/gfx/gfxVars.h"
     28 #include "mozilla/ipc/CrossProcessSemaphore.h"
     29 #include "mozilla/layers/CanvasRenderer.h"
     30 #include "mozilla/layers/CompositableForwarder.h"
     31 #include "mozilla/layers/ISurfaceAllocator.h"
     32 #include "mozilla/layers/ImageBridgeChild.h"
     33 #include "mozilla/layers/ImageDataSerializer.h"
     34 #include "mozilla/layers/PTextureChild.h"
     35 #include "mozilla/layers/SynchronousTask.h"
     36 #include "mozilla/layers/TextureClientOGL.h"
     37 #include "mozilla/layers/TextureClientRecycleAllocator.h"
     38 #include "mozilla/layers/TextureRecorded.h"
     39 #include "nsDebug.h"  // for NS_ASSERTION, NS_WARNING, etc
     40 #include "nsISerialEventTarget.h"
     41 #include "nsISupportsImpl.h"  // for MOZ_COUNT_CTOR, etc
     42 #include "nsPrintfCString.h"  // for nsPrintfCString
     43 
     44 #ifdef XP_WIN
     45 #  include "gfx2DGlue.h"
     46 #  include "gfxWindowsPlatform.h"
     47 #  include "mozilla/gfx/DeviceManagerDx.h"
     48 #  include "mozilla/layers/TextureD3D11.h"
     49 #endif
     50 #ifdef MOZ_WIDGET_GTK
     51 #  include <gtk/gtkx.h>
     52 #  include "gfxPlatformGtk.h"
     53 #endif
     54 #ifdef MOZ_WAYLAND
     55 #  include "mozilla/widget/nsWaylandDisplay.h"
     56 #endif
     57 
     58 #ifdef XP_MACOSX
     59 #  include "mozilla/layers/MacIOSurfaceTextureClientOGL.h"
     60 #endif
     61 
     62 #if 0
     63 #  define RECYCLE_LOG(...) printf_stderr(__VA_ARGS__)
     64 #else
     65 #  define RECYCLE_LOG(...) \
     66    do {                   \
     67    } while (0)
     68 #endif
     69 
     70 namespace mozilla::layers {
     71 
     72 using namespace mozilla::ipc;
     73 using namespace mozilla::gl;
     74 using namespace mozilla::gfx;
     75 
     76 struct TextureDeallocParams {
     77  TextureData* data = nullptr;
     78  RefPtr<TextureChild> actor;
     79  RefPtr<TextureReadLock> readLock;
     80  RefPtr<LayersIPCChannel> allocator;
     81  bool clientDeallocation = false;
     82  bool syncDeallocation = false;
     83 
     84  TextureDeallocParams() = default;
     85  TextureDeallocParams(const TextureDeallocParams&) = delete;
     86  TextureDeallocParams& operator=(const TextureDeallocParams&) = delete;
     87 
     88  TextureDeallocParams(TextureDeallocParams&& aOther)
     89      : data(aOther.data),
     90        actor(std::move(aOther.actor)),
     91        readLock(std::move(aOther.readLock)),
     92        allocator(std::move(aOther.allocator)),
     93        clientDeallocation(aOther.clientDeallocation),
     94        syncDeallocation(aOther.syncDeallocation) {
     95    aOther.data = nullptr;
     96  }
     97 
     98  TextureDeallocParams& operator=(TextureDeallocParams&& aOther) {
     99    data = aOther.data;
    100    aOther.data = nullptr;
    101    actor = std::move(aOther.actor);
    102    readLock = std::move(aOther.readLock);
    103    allocator = std::move(aOther.allocator);
    104    clientDeallocation = aOther.clientDeallocation;
    105    syncDeallocation = aOther.syncDeallocation;
    106    return *this;
    107  }
    108 };
    109 
    110 void DeallocateTextureClient(TextureDeallocParams& params);
    111 
    112 /**
    113 * TextureChild is the content-side incarnation of the PTexture IPDL actor.
    114 *
    115 * TextureChild is used to synchronize a texture client and its corresponding
    116 * TextureHost if needed (a TextureClient that is not shared with the compositor
    117 * does not have a TextureChild)
    118 *
    119 * During the deallocation phase, a TextureChild may hold its recently destroyed
    120 * TextureClient's data until the compositor side confirmed that it is safe to
    121 * deallocte or recycle the it.
    122 */
    123 class TextureChild final : PTextureChild {
    124  ~TextureChild() {
    125    // We should have deallocated mTextureData in ActorDestroy
    126    MOZ_ASSERT(!mTextureData);
    127    MOZ_ASSERT_IF(!mOwnerCalledDestroy, !mTextureClient);
    128  }
    129 
    130 public:
    131  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TextureChild)
    132 
    133  TextureChild()
    134      : mCompositableForwarder(nullptr),
    135        mTextureForwarder(nullptr),
    136        mTextureClient(nullptr),
    137        mTextureData(nullptr),
    138        mDestroyed(false),
    139        mIPCOpen(false),
    140        mOwnsTextureData(false),
    141        mOwnerCalledDestroy(false),
    142        mUsesImageBridge(false) {}
    143 
    144  mozilla::ipc::IPCResult Recv__delete__() override { return IPC_OK(); }
    145 
    146  LayersIPCChannel* GetAllocator() { return mTextureForwarder; }
    147 
    148  void ActorDestroy(ActorDestroyReason why) override;
    149 
    150  bool IPCOpen() const { return mIPCOpen; }
    151 
    152  void Lock() const {
    153    if (mUsesImageBridge) {
    154      mLock.Enter();
    155    }
    156  }
    157 
    158  void Unlock() const {
    159    if (mUsesImageBridge) {
    160      mLock.Leave();
    161    }
    162  }
    163 
    164 private:
    165  // AddIPDLReference and ReleaseIPDLReference are only to be called by
    166  // CreateIPDLActor and DestroyIPDLActor, respectively. We intentionally make
    167  // them private to prevent misuse. The purpose of these methods is to be aware
    168  // of when the IPC system around this actor goes down: mIPCOpen is then set to
    169  // false.
    170  void AddIPDLReference() {
    171    MOZ_ASSERT(mIPCOpen == false);
    172    mIPCOpen = true;
    173    AddRef();
    174  }
    175  void ReleaseIPDLReference() {
    176    MOZ_ASSERT(mIPCOpen == false);
    177    Release();
    178  }
    179 
    180  /// The normal way to destroy the actor.
    181  ///
    182  /// This will asynchronously send a Destroy message to the parent actor, whom
    183  /// will send the delete message.
    184  void Destroy(const TextureDeallocParams& aParams);
    185 
    186  // This lock is used order to prevent several threads to access the
    187  // TextureClient's data concurrently. In particular, it prevents shutdown
    188  // code to destroy a texture while another thread is reading or writing into
    189  // it.
    190  // In most places, the lock is held in short and bounded scopes in which we
    191  // don't block on any other resource. There are few exceptions to this, which
    192  // are discussed below.
    193  //
    194  // The locking pattern of TextureClient may in some case upset deadlock
    195  // detection tools such as TSan. Typically our tile rendering code will lock
    196  // all of its tiles, render into them and unlock them all right after that,
    197  // which looks something like:
    198  //
    199  // Lock tile A
    200  // Lock tile B
    201  // Lock tile C
    202  // Apply drawing commands to tiles A, B and C
    203  // Unlock tile A
    204  // Unlock tile B
    205  // Unlock tile C
    206  //
    207  // And later, we may end up rendering a tile buffer that has the same tiles,
    208  // in a different order, for example:
    209  //
    210  // Lock tile B
    211  // Lock tile A
    212  // Lock tile D
    213  // Apply drawing commands to tiles A, B and D
    214  // Unlock tile B
    215  // Unlock tile A
    216  // Unlock tile D
    217  //
    218  // This is because textures being expensive to create, we recycle them as much
    219  // as possible and they may reappear in the tile buffer in a different order.
    220  //
    221  // Unfortunately this is not very friendly to TSan's analysis, which will see
    222  // that B was once locked while A was locked, and then A locked while B was
    223  // locked. TSan identifies this as a potential dead-lock which would be the
    224  // case if this kind of inconsistent and dependent locking order was happening
    225  // concurrently.
    226  // In the case of TextureClient, dependent locking only ever happens on the
    227  // thread that draws into the texture (let's call it the producer thread).
    228  // Other threads may call into a method that can lock the texture in a short
    229  // and bounded scope inside of which it is not allowed to do anything that
    230  // could cause the thread to block. A given texture can only have one producer
    231  // thread.
    232  //
    233  // Another example of TSan-unfriendly locking pattern is when copying a
    234  // texture into another, which also never happens outside of the producer
    235  // thread. Copying A into B looks like this:
    236  //
    237  // Lock texture B
    238  // Lock texture A
    239  // Copy A into B
    240  // Unlock A
    241  // Unlock B
    242  //
    243  // In a given frame we may need to copy A into B and in another frame copy
    244  // B into A. For example A and B can be the Front and Back buffers,
    245  // alternating roles and the copy is needed to avoid the cost of re-drawing
    246  // the valid region.
    247  //
    248  // The important rule is that all of the dependent locking must occur only
    249  // in the texture's producer thread to avoid deadlocks.
    250  mutable gfx::CriticalSection mLock;
    251 
    252  RefPtr<CompositableForwarder> mCompositableForwarder;
    253  RefPtr<TextureForwarder> mTextureForwarder;
    254 
    255  TextureClient* mTextureClient;
    256  TextureData* mTextureData;
    257  Atomic<bool> mDestroyed;
    258  bool mIPCOpen;
    259  bool mOwnsTextureData;
    260  bool mOwnerCalledDestroy;
    261  bool mUsesImageBridge;
    262 
    263  friend class TextureClient;
    264  friend void DeallocateTextureClient(TextureDeallocParams& params);
    265 };
    266 
    267 static inline gfx::BackendType BackendTypeForBackendSelector(
    268    LayersBackend aLayersBackend, BackendSelector aSelector) {
    269  switch (aSelector) {
    270    case BackendSelector::Canvas:
    271      return gfxPlatform::GetPlatform()->GetPreferredCanvasBackend();
    272    case BackendSelector::Content:
    273      return gfxPlatform::GetPlatform()->GetContentBackendFor(aLayersBackend);
    274    default:
    275      MOZ_ASSERT_UNREACHABLE("Unknown backend selector");
    276      return gfx::BackendType::NONE;
    277  }
    278 };
    279 
    280 static TextureType ChooseTextureType(gfx::SurfaceFormat aFormat,
    281                                     gfx::IntSize aSize,
    282                                     KnowsCompositor* aKnowsCompositor,
    283                                     BackendSelector aSelector,
    284                                     TextureAllocationFlags aAllocFlags) {
    285  LayersBackend layersBackend = aKnowsCompositor->GetCompositorBackendType();
    286  gfx::BackendType moz2DBackend =
    287      BackendTypeForBackendSelector(layersBackend, aSelector);
    288  (void)moz2DBackend;
    289 
    290 #ifdef XP_MACOSX
    291  if (StaticPrefs::gfx_use_iosurface_textures_AtStartup() &&
    292      !aKnowsCompositor->UsingSoftwareWebRender()) {
    293    return TextureType::MacIOSurface;
    294  }
    295 #endif
    296 
    297 #ifdef MOZ_WIDGET_ANDROID
    298  if (StaticPrefs::gfx_use_surfacetexture_textures_AtStartup() &&
    299      !aKnowsCompositor->UsingSoftwareWebRender()) {
    300    return TextureType::AndroidNativeWindow;
    301  }
    302 #endif
    303 
    304  return TextureType::Unknown;
    305 }
    306 
    307 TextureType PreferredCanvasTextureType(KnowsCompositor* aKnowsCompositor) {
    308  return ChooseTextureType(gfx::SurfaceFormat::R8G8B8A8, {1, 1},
    309                           aKnowsCompositor, BackendSelector::Canvas,
    310                           TextureAllocationFlags::ALLOC_DEFAULT);
    311 }
    312 
    313 /* static */
    314 TextureData* TextureData::Create(TextureType aTextureType,
    315                                 gfx::SurfaceFormat aFormat,
    316                                 const gfx::IntSize& aSize,
    317                                 TextureAllocationFlags aAllocFlags,
    318                                 gfx::BackendType aBackendType) {
    319  switch (aTextureType) {
    320 #ifdef XP_WIN
    321    case TextureType::D3D11:
    322      return D3D11TextureData::Create(aSize, aFormat, aAllocFlags);
    323 #endif
    324 
    325 #ifdef XP_MACOSX
    326    case TextureType::MacIOSurface:
    327      return MacIOSurfaceTextureData::Create(aSize, aFormat, aBackendType);
    328 #endif
    329 #ifdef MOZ_WIDGET_ANDROID
    330    case TextureType::AndroidNativeWindow:
    331      return AndroidNativeWindowTextureData::Create(aSize, aFormat);
    332 #endif
    333    default:
    334      return nullptr;
    335  }
    336 }
    337 
    338 /* static */
    339 TextureData* TextureData::Create(TextureForwarder* aAllocator,
    340                                 gfx::SurfaceFormat aFormat, gfx::IntSize aSize,
    341                                 KnowsCompositor* aKnowsCompositor,
    342                                 BackendSelector aSelector,
    343                                 TextureFlags aTextureFlags,
    344                                 TextureAllocationFlags aAllocFlags) {
    345  TextureType textureType = ChooseTextureType(aFormat, aSize, aKnowsCompositor,
    346                                              aSelector, aAllocFlags);
    347 
    348  if (aAllocFlags & ALLOC_FORCE_REMOTE) {
    349    RefPtr<CanvasChild> canvasChild = aAllocator->GetCanvasChild();
    350    if (canvasChild) {
    351      TextureType webglTextureType =
    352          TexTypeForWebgl(aKnowsCompositor, /* aIsWebglOop */ true);
    353      if (canvasChild->EnsureRecorder(aSize, aFormat, textureType,
    354                                      webglTextureType)) {
    355        return new RecordedTextureData(canvasChild.forget(), aSize, aFormat,
    356                                       textureType, webglTextureType);
    357      }
    358    }
    359    // If we must be remote, but there is no canvas child, then falling back
    360    // is not possible.
    361    return nullptr;
    362  }
    363 
    364  gfx::BackendType moz2DBackend = gfx::BackendType::NONE;
    365 
    366 #if defined(XP_MACOSX) || defined(MOZ_WIDGET_GTK)
    367  moz2DBackend = BackendTypeForBackendSelector(
    368      aKnowsCompositor->GetCompositorBackendType(), aSelector);
    369 #endif
    370 
    371  return TextureData::Create(textureType, aFormat, aSize, aAllocFlags,
    372                             moz2DBackend);
    373 }
    374 
    375 /* static */
    376 bool TextureData::IsRemote(KnowsCompositor* aKnowsCompositor,
    377                           BackendSelector aSelector,
    378                           gfx::SurfaceFormat aFormat, gfx::IntSize aSize) {
    379  if (aSelector != BackendSelector::Canvas || !gfxPlatform::UseRemoteCanvas()) {
    380    return false;
    381  }
    382 
    383  TextureType textureType =
    384      ChooseTextureType(aFormat, aSize, aKnowsCompositor, aSelector,
    385                        TextureAllocationFlags::ALLOC_DEFAULT);
    386 
    387  switch (textureType) {
    388    case TextureType::D3D11:
    389      return true;
    390    default:
    391      return false;
    392  }
    393 }
    394 
    395 static void DestroyTextureData(TextureData* aTextureData,
    396                               LayersIPCChannel* aAllocator, bool aDeallocate) {
    397  if (!aTextureData) {
    398    return;
    399  }
    400 
    401  if (aDeallocate) {
    402    aTextureData->Deallocate(aAllocator);
    403  } else {
    404    aTextureData->Forget(aAllocator);
    405  }
    406  delete aTextureData;
    407 }
    408 
    409 void TextureChild::ActorDestroy(ActorDestroyReason why) {
    410  AUTO_PROFILER_LABEL("TextureChild::ActorDestroy", GRAPHICS);
    411  MOZ_ASSERT(mIPCOpen);
    412  mIPCOpen = false;
    413 
    414  if (mTextureData) {
    415    DestroyTextureData(mTextureData, GetAllocator(), mOwnsTextureData);
    416    mTextureData = nullptr;
    417  }
    418 }
    419 
    420 void TextureChild::Destroy(const TextureDeallocParams& aParams) {
    421  MOZ_ASSERT(!mOwnerCalledDestroy);
    422  if (mOwnerCalledDestroy) {
    423    return;
    424  }
    425 
    426  mOwnerCalledDestroy = true;
    427 
    428  if (!IPCOpen()) {
    429    DestroyTextureData(aParams.data, aParams.allocator,
    430                       aParams.clientDeallocation);
    431    return;
    432  }
    433 
    434  // DestroyTextureData will be called by TextureChild::ActorDestroy
    435  mTextureData = aParams.data;
    436  mOwnsTextureData = aParams.clientDeallocation;
    437 
    438  if (!mCompositableForwarder ||
    439      !mCompositableForwarder->DestroyInTransaction(this)) {
    440    this->SendDestroy();
    441  }
    442 }
    443 
    444 /* static */
    445 Atomic<uint64_t> TextureClient::sSerialCounter(0);
    446 
    447 /// The logic for synchronizing a TextureClient's deallocation goes here.
    448 ///
    449 /// This funciton takes care of dispatching work to the right thread using
    450 /// a synchronous proxy if needed, and handles client/host deallocation.
    451 void DeallocateTextureClient(TextureDeallocParams& params) {
    452  if (!params.actor && !params.readLock && !params.data) {
    453    // Nothing to do
    454    return;
    455  }
    456 
    457  TextureChild* actor = params.actor;
    458  nsCOMPtr<nsISerialEventTarget> ipdlThread;
    459 
    460  if (params.allocator) {
    461    ipdlThread = params.allocator->GetThread();
    462    if (!ipdlThread) {
    463      // An allocator with no thread means we are too late in the shutdown
    464      // sequence.
    465      gfxCriticalError() << "Texture deallocated too late during shutdown";
    466      return;
    467    }
    468  }
    469 
    470  // First make sure that the work is happening on the IPDL thread.
    471  if (ipdlThread && !ipdlThread->IsOnCurrentThread()) {
    472    if (params.syncDeallocation) {
    473      bool done = false;
    474      ReentrantMonitor barrier MOZ_UNANNOTATED("DeallocateTextureClient");
    475      ReentrantMonitorAutoEnter autoMon(barrier);
    476      ipdlThread->Dispatch(NS_NewRunnableFunction(
    477          "DeallocateTextureClientSyncProxyRunnable", [&]() {
    478            DeallocateTextureClient(params);
    479            ReentrantMonitorAutoEnter autoMonInner(barrier);
    480            done = true;
    481            barrier.NotifyAll();
    482          }));
    483      while (!done) {
    484        barrier.Wait();
    485      }
    486    } else {
    487      ipdlThread->Dispatch(
    488          NS_NewRunnableFunction("DeallocateTextureClientRunnable",
    489                                 [params = std::move(params)]() mutable {
    490                                   DeallocateTextureClient(params);
    491                                 }));
    492    }
    493    // The work has been forwarded to the IPDL thread, we are done.
    494    return;
    495  }
    496 
    497  // Below this line, we are either in the IPDL thread or ther is no IPDL
    498  // thread anymore.
    499 
    500  if (!ipdlThread) {
    501    // If we don't have a thread we can't know for sure that we are in
    502    // the IPDL thread and use the LayersIPCChannel.
    503    // This should ideally not happen outside of gtest, but some shutdown
    504    // raciness could put us in this situation.
    505    params.allocator = nullptr;
    506  }
    507 
    508  if (params.readLock) {
    509    // This should be the last reference to the object, which will destroy it.
    510    params.readLock = nullptr;
    511  }
    512 
    513  if (!actor) {
    514    // We don't have an IPDL actor, probably because we destroyed the
    515    // TextureClient before sharing it with the compositor. It means the data
    516    // cannot be owned by the TextureHost since we never created the
    517    // TextureHost...
    518    DestroyTextureData(params.data, params.allocator, /* aDeallocate */ true);
    519    return;
    520  }
    521 
    522  actor->Destroy(params);
    523 }
    524 
    525 void TextureClient::Destroy() {
    526  // Async paints should have been flushed by now.
    527  MOZ_RELEASE_ASSERT(mPaintThreadRefs == 0);
    528 
    529  if (mActor && !mIsLocked) {
    530    mActor->Lock();
    531  }
    532 
    533  mBorrowedDrawTarget = nullptr;
    534  mBorrowedSnapshot = false;
    535 
    536  RefPtr<TextureChild> actor = std::move(mActor);
    537 
    538  RefPtr<TextureReadLock> readLock;
    539  {
    540    MutexAutoLock lock(mMutex);
    541    readLock = std::move(mReadLock);
    542  }
    543 
    544  if (actor && !actor->mDestroyed.compareExchange(false, true)) {
    545    actor->Unlock();
    546    actor = nullptr;
    547  }
    548 
    549  TextureData* data = mData;
    550  mData = nullptr;
    551 
    552  if (data || actor || readLock) {
    553    TextureDeallocParams params;
    554    params.actor = std::move(actor);
    555    params.readLock = std::move(readLock);
    556    params.allocator = mAllocator;
    557    params.clientDeallocation = !!(mFlags & TextureFlags::DEALLOCATE_CLIENT);
    558    params.data = data;
    559    // At the moment we always deallocate synchronously when deallocating on the
    560    // client side, but having asynchronous deallocate in some of the cases will
    561    // be a worthwhile optimization.
    562    params.syncDeallocation = !!(mFlags & TextureFlags::DEALLOCATE_CLIENT);
    563 
    564    // Release the lock before calling DeallocateTextureClient because the
    565    // latter may wait for the main thread which could create a dead-lock.
    566 
    567    if (params.actor) {
    568      params.actor->Unlock();
    569    }
    570 
    571    DeallocateTextureClient(params);
    572  }
    573 }
    574 
    575 void TextureClient::LockActor() const {
    576  if (mActor) {
    577    mActor->Lock();
    578  }
    579 }
    580 
    581 void TextureClient::UnlockActor() const {
    582  if (mActor) {
    583    mActor->Unlock();
    584  }
    585 }
    586 
    587 void TextureClient::EnsureHasReadLock() {
    588  if (mFlags & TextureFlags::NON_BLOCKING_READ_LOCK) {
    589    MOZ_ASSERT(!(mFlags & TextureFlags::BLOCKING_READ_LOCK));
    590    EnableReadLock();
    591  } else if (mFlags & TextureFlags::BLOCKING_READ_LOCK) {
    592    MOZ_ASSERT(!(mFlags & TextureFlags::NON_BLOCKING_READ_LOCK));
    593    EnableBlockingReadLock();
    594  }
    595 }
    596 
    597 bool TextureClient::IsReadLocked() {
    598  if (!ShouldReadLock()) {
    599    return false;
    600  }
    601 
    602  nsCOMPtr<nsISerialEventTarget> thread;
    603 
    604  {
    605    MutexAutoLock lock(mMutex);
    606    if (mReadLock) {
    607      MOZ_ASSERT(mReadLock->AsNonBlockingLock(),
    608                 "Can only check locked for non-blocking locks!");
    609      return mReadLock->AsNonBlockingLock()->GetReadCount() > 1;
    610    }
    611 
    612    thread = mAllocator->GetThread();
    613    if (!thread) {
    614      // We must be in the process of shutting down.
    615      return false;
    616    }
    617 
    618    if (thread->IsOnCurrentThread()) {
    619      EnsureHasReadLock();
    620      if (NS_WARN_IF(!mReadLock)) {
    621        MOZ_ASSERT(!mAllocator->IPCOpen());
    622        return false;
    623      }
    624      MOZ_ASSERT(mReadLock->AsNonBlockingLock(),
    625                 "Can only check locked for non-blocking locks!");
    626      return mReadLock->AsNonBlockingLock()->GetReadCount() > 1;
    627    }
    628  }
    629 
    630  MOZ_ASSERT(mAllocator->UsesImageBridge());
    631 
    632  bool result = false;
    633  SynchronousTask task("TextureClient::IsReadLocked");
    634  thread->Dispatch(NS_NewRunnableFunction("TextureClient::IsReadLocked", [&]() {
    635    AutoCompleteTask complete(&task);
    636    result = IsReadLocked();
    637  }));
    638  task.Wait();
    639 
    640  return result;
    641 }
    642 
    643 bool TextureClient::TryReadLock() {
    644  if (!ShouldReadLock()) {
    645    return true;
    646  }
    647 
    648  nsCOMPtr<nsISerialEventTarget> thread;
    649 
    650  {
    651    MutexAutoLock lock(mMutex);
    652    if (mIsReadLocked) {
    653      return true;
    654    }
    655 
    656    if (mReadLock) {
    657      if (mReadLock->AsNonBlockingLock() &&
    658          mReadLock->AsNonBlockingLock()->GetReadCount() > 1) {
    659        return false;
    660      }
    661 
    662      if (!mReadLock->TryReadLock(TimeDuration::FromMilliseconds(500))) {
    663        return false;
    664      }
    665 
    666      mIsReadLocked = true;
    667      return true;
    668    }
    669 
    670    thread = mAllocator->GetThread();
    671    if (!thread) {
    672      // We must be in the process of shutting down.
    673      return false;
    674    }
    675 
    676    if (thread->IsOnCurrentThread()) {
    677      EnsureHasReadLock();
    678 
    679      if (NS_WARN_IF(!mReadLock)) {
    680        MOZ_ASSERT(!mAllocator->IPCOpen());
    681        return false;
    682      }
    683 
    684      if (mReadLock->AsNonBlockingLock() &&
    685          mReadLock->AsNonBlockingLock()->GetReadCount() > 1) {
    686        return false;
    687      }
    688 
    689      if (!mReadLock->TryReadLock(TimeDuration::FromMilliseconds(500))) {
    690        return false;
    691      }
    692 
    693      mIsReadLocked = true;
    694      return true;
    695    }
    696  }
    697 
    698  MOZ_ASSERT(mAllocator->UsesImageBridge());
    699 
    700  bool result = false;
    701  SynchronousTask task("TextureClient::TryReadLock");
    702  thread->Dispatch(NS_NewRunnableFunction("TextureClient::TryReadLock", [&]() {
    703    AutoCompleteTask complete(&task);
    704    result = TryReadLock();
    705  }));
    706  task.Wait();
    707 
    708  return result;
    709 }
    710 
    711 void TextureClient::ReadUnlock() {
    712  if (!ShouldReadLock()) {
    713    return;
    714  }
    715 
    716  MutexAutoLock lock(mMutex);
    717 
    718  if (!mIsReadLocked) {
    719    return;
    720  }
    721 
    722  MOZ_ASSERT(mReadLock);
    723  mReadLock->ReadUnlock();
    724  mIsReadLocked = false;
    725 }
    726 
    727 bool TextureClient::Lock(OpenMode aMode) {
    728  if (NS_WARN_IF(!IsValid())) {
    729    return false;
    730  }
    731  if (NS_WARN_IF(mIsLocked)) {
    732    return mOpenMode == aMode;
    733  }
    734 
    735  if ((aMode & OpenMode::OPEN_WRITE || !mInfo.canConcurrentlyReadLock) &&
    736      !TryReadLock()) {
    737    // Only warn if attempting to write. Attempting to read is acceptable usage.
    738    if (aMode & OpenMode::OPEN_WRITE) {
    739      NS_WARNING(
    740          "Attempt to Lock a texture that is being read by the compositor!");
    741    }
    742    return false;
    743  }
    744 
    745  LockActor();
    746 
    747  mIsLocked = mData->Lock(aMode);
    748  mOpenMode = aMode;
    749 
    750  auto format = GetFormat();
    751  if (mIsLocked && CanExposeDrawTarget() &&
    752      (aMode & OpenMode::OPEN_READ_WRITE) == OpenMode::OPEN_READ_WRITE &&
    753      NS_IsMainThread() &&
    754      // the formats that we apparently expect, in the cairo backend. Any other
    755      // format will trigger an assertion in GfxFormatToCairoFormat.
    756      (format == SurfaceFormat::A8R8G8B8_UINT32 ||
    757       format == SurfaceFormat::X8R8G8B8_UINT32 ||
    758       format == SurfaceFormat::A8 || format == SurfaceFormat::R5G6B5_UINT16)) {
    759    if (!BorrowDrawTarget()) {
    760      // Failed to get a DrawTarget, means we won't be able to write into the
    761      // texture, might as well fail now.
    762      Unlock();
    763      return false;
    764    }
    765  }
    766 
    767  if (!mIsLocked) {
    768    UnlockActor();
    769    ReadUnlock();
    770  }
    771 
    772  return mIsLocked;
    773 }
    774 
    775 void TextureClient::Unlock() {
    776  MOZ_ASSERT(IsValid());
    777  MOZ_ASSERT(mIsLocked);
    778  if (!IsValid() || !mIsLocked) {
    779    return;
    780  }
    781 
    782  if (mBorrowedDrawTarget) {
    783    if (mOpenMode & OpenMode::OPEN_WRITE) {
    784      mBorrowedDrawTarget->Flush();
    785    }
    786 
    787    mData->ReturnDrawTarget(mBorrowedDrawTarget.forget());
    788  }
    789  mBorrowedSnapshot = false;
    790 
    791  if (mOpenMode & OpenMode::OPEN_WRITE) {
    792    mUpdated = true;
    793  }
    794 
    795  if (mData) {
    796    mData->Unlock();
    797  }
    798  mIsLocked = false;
    799  mOpenMode = OpenMode::OPEN_NONE;
    800 
    801  UnlockActor();
    802  ReadUnlock();
    803 }
    804 
    805 void TextureClient::EnableReadLock() {
    806  MOZ_ASSERT(ShouldReadLock());
    807  if (!mReadLock && mAllocator->GetTileLockAllocator()) {
    808    mReadLock = NonBlockingTextureReadLock::Create(mAllocator);
    809  }
    810 }
    811 
    812 void TextureClient::OnPrepareForwardToHost() {
    813  if (!ShouldReadLock()) {
    814    return;
    815  }
    816 
    817  MutexAutoLock lock(mMutex);
    818  if (NS_WARN_IF(!mReadLock)) {
    819    MOZ_ASSERT(!mAllocator->IPCOpen(), "Should have created readlock already!");
    820    MOZ_ASSERT(!mIsPendingForwardReadLocked);
    821    return;
    822  }
    823 
    824  if (mIsPendingForwardReadLocked) {
    825    return;
    826  }
    827 
    828  mReadLock->ReadLock();
    829  mIsPendingForwardReadLocked = true;
    830 }
    831 
    832 void TextureClient::OnAbandonForwardToHost() {
    833  if (!ShouldReadLock()) {
    834    return;
    835  }
    836 
    837  MutexAutoLock lock(mMutex);
    838  if (!mReadLock || !mIsPendingForwardReadLocked) {
    839    return;
    840  }
    841 
    842  mReadLock->ReadUnlock();
    843  mIsPendingForwardReadLocked = false;
    844 }
    845 
    846 bool TextureClient::OnForwardedToHost() {
    847  if (mData) {
    848    mData->OnForwardedToHost();
    849  }
    850 
    851  if (!ShouldReadLock()) {
    852    return false;
    853  }
    854 
    855  MutexAutoLock lock(mMutex);
    856  EnsureHasReadLock();
    857 
    858  if (NS_WARN_IF(!mReadLock)) {
    859    MOZ_ASSERT(!mAllocator->IPCOpen());
    860    return false;
    861  }
    862 
    863  if (!mUpdated) {
    864    if (mIsPendingForwardReadLocked) {
    865      mIsPendingForwardReadLocked = false;
    866      mReadLock->ReadUnlock();
    867    }
    868    return false;
    869  }
    870 
    871  mUpdated = false;
    872 
    873  if (mIsPendingForwardReadLocked) {
    874    // We have successfully forwarded, just clear the flag and let the
    875    // TextureHost be responsible for unlocking.
    876    mIsPendingForwardReadLocked = false;
    877  } else {
    878    // Otherwise we did not need to readlock in advance, so do so now. We do
    879    // this on behalf of the TextureHost.
    880    mReadLock->ReadLock();
    881  }
    882 
    883  return true;
    884 }
    885 
    886 TextureClient::~TextureClient() {
    887  // TextureClients should be kept alive while there are references on the
    888  // paint thread.
    889  MOZ_ASSERT(mPaintThreadRefs == 0);
    890  mReadLock = nullptr;
    891  Destroy();
    892 }
    893 
    894 void TextureClient::UpdateFromSurface(gfx::SourceSurface* aSurface) {
    895  MOZ_ASSERT(IsValid());
    896  MOZ_ASSERT(mIsLocked);
    897  MOZ_ASSERT(aSurface);
    898  // If you run into this assertion, make sure the texture was locked write-only
    899  // rather than read-write.
    900  MOZ_ASSERT(!mBorrowedDrawTarget);
    901 
    902  // XXX - It would be better to first try the DrawTarget approach and fallback
    903  // to the backend-specific implementation because the latter will usually do
    904  // an expensive read-back + cpu-side copy if the texture is on the gpu.
    905  // There is a bug with the DrawTarget approach, though specific to reading
    906  // back from WebGL (where R and B channel end up inverted) to figure out
    907  // first.
    908  if (mData->UpdateFromSurface(aSurface)) {
    909    return;
    910  }
    911  if (CanExposeDrawTarget() && NS_IsMainThread()) {
    912    RefPtr<DrawTarget> dt = BorrowDrawTarget();
    913 
    914    MOZ_ASSERT(dt);
    915    if (dt) {
    916      dt->CopySurface(aSurface,
    917                      gfx::IntRect(gfx::IntPoint(0, 0), aSurface->GetSize()),
    918                      gfx::IntPoint(0, 0));
    919      return;
    920    }
    921  }
    922  NS_WARNING("TextureClient::UpdateFromSurface failed");
    923 }
    924 
    925 already_AddRefed<TextureClient> TextureClient::CreateSimilar(
    926    LayersBackend aLayersBackend, TextureFlags aFlags,
    927    TextureAllocationFlags aAllocFlags) const {
    928  MOZ_ASSERT(IsValid());
    929 
    930  MOZ_ASSERT(!mIsLocked);
    931  if (mIsLocked) {
    932    return nullptr;
    933  }
    934 
    935  LockActor();
    936  TextureData* data =
    937      mData->CreateSimilar(mAllocator, aLayersBackend, aFlags, aAllocFlags);
    938  UnlockActor();
    939 
    940  if (!data) {
    941    return nullptr;
    942  }
    943 
    944  return MakeAndAddRef<TextureClient>(data, aFlags, mAllocator);
    945 }
    946 
    947 gfx::DrawTarget* TextureClient::BorrowDrawTarget() {
    948  MOZ_ASSERT(IsValid());
    949  MOZ_ASSERT(mIsLocked);
    950  // TODO- We can't really assert that at the moment because there is code that
    951  // Borrows the DrawTarget, just to get a snapshot, which is legit in term of
    952  // OpenMode but we should have a way to get a SourceSurface directly instead.
    953  // MOZ_ASSERT(mOpenMode & OpenMode::OPEN_WRITE);
    954 
    955  if (!IsValid() || !mIsLocked) {
    956    return nullptr;
    957  }
    958 
    959  if (!mBorrowedDrawTarget) {
    960    mBorrowedDrawTarget = mData->BorrowDrawTarget();
    961  }
    962 
    963  return mBorrowedDrawTarget;
    964 }
    965 
    966 void TextureClient::EndDraw() {
    967  MOZ_ASSERT(mOpenMode & OpenMode::OPEN_READ_WRITE);
    968 
    969  // Because EndDraw is used when we are not unlocking this TextureClient at the
    970  // end of a transaction, we need to Flush and DetachAllSnapshots to ensure any
    971  // dependents are updated.
    972  mBorrowedDrawTarget->Flush();
    973  mData->ReturnDrawTarget(mBorrowedDrawTarget.forget());
    974 
    975  mBorrowedSnapshot = false;
    976  mData->EndDraw();
    977 }
    978 
    979 void TextureData::ReturnDrawTarget(already_AddRefed<gfx::DrawTarget> aDT) {
    980  RefPtr<gfx::DrawTarget> dt(aDT);
    981  dt->DetachAllSnapshots();
    982 }
    983 
    984 already_AddRefed<gfx::SourceSurface> TextureClient::BorrowSnapshot() {
    985  MOZ_ASSERT(mIsLocked);
    986 
    987  RefPtr<gfx::SourceSurface> surface = mData->BorrowSnapshot();
    988  if (surface) {
    989    mBorrowedSnapshot = true;
    990  } else {
    991    RefPtr<gfx::DrawTarget> drawTarget = BorrowDrawTarget();
    992    if (!drawTarget) {
    993      return nullptr;
    994    }
    995    surface = drawTarget->Snapshot();
    996  }
    997 
    998  return surface.forget();
    999 }
   1000 
   1001 void TextureData::ReturnSnapshot(
   1002    already_AddRefed<gfx::SourceSurface> aSnapshot) {
   1003  RefPtr<gfx::SourceSurface> snapshot(aSnapshot);
   1004 }
   1005 
   1006 void TextureClient::ReturnSnapshot(
   1007    already_AddRefed<gfx::SourceSurface> aSnapshot) {
   1008  RefPtr<gfx::SourceSurface> snapshot = aSnapshot;
   1009  if (mBorrowedSnapshot) {
   1010    mData->ReturnSnapshot(snapshot.forget());
   1011    mBorrowedSnapshot = false;
   1012  }
   1013 }
   1014 
   1015 bool TextureClient::BorrowMappedData(MappedTextureData& aMap) {
   1016  MOZ_ASSERT(IsValid());
   1017 
   1018  // TODO - SharedRGBImage just accesses the buffer without properly locking
   1019  // the texture. It's bad.
   1020  // MOZ_ASSERT(mIsLocked);
   1021  // if (!mIsLocked) {
   1022  //  return nullptr;
   1023  //}
   1024 
   1025  return mData ? mData->BorrowMappedData(aMap) : false;
   1026 }
   1027 
   1028 bool TextureClient::BorrowMappedYCbCrData(MappedYCbCrTextureData& aMap) {
   1029  MOZ_ASSERT(IsValid());
   1030 
   1031  return mData ? mData->BorrowMappedYCbCrData(aMap) : false;
   1032 }
   1033 
   1034 bool TextureClient::ToSurfaceDescriptor(SurfaceDescriptor& aOutDescriptor) {
   1035  MOZ_ASSERT(IsValid());
   1036 
   1037  return mData ? mData->Serialize(aOutDescriptor) : false;
   1038 }
   1039 
   1040 // static
   1041 PTextureChild* TextureClient::CreateIPDLActor() {
   1042  TextureChild* c = new TextureChild();
   1043  c->AddIPDLReference();
   1044  return c;
   1045 }
   1046 
   1047 // static
   1048 bool TextureClient::DestroyIPDLActor(PTextureChild* actor) {
   1049  static_cast<TextureChild*>(actor)->ReleaseIPDLReference();
   1050  return true;
   1051 }
   1052 
   1053 // static
   1054 already_AddRefed<TextureClient> TextureClient::AsTextureClient(
   1055    PTextureChild* actor) {
   1056  if (!actor) {
   1057    return nullptr;
   1058  }
   1059 
   1060  TextureChild* tc = static_cast<TextureChild*>(actor);
   1061 
   1062  tc->Lock();
   1063 
   1064  // Since TextureClient may be destroyed asynchronously with respect to its
   1065  // IPDL actor, we must acquire a reference within a lock. The mDestroyed bit
   1066  // tells us whether or not the main thread has disconnected the TextureClient
   1067  // from its actor.
   1068  if (tc->mDestroyed) {
   1069    tc->Unlock();
   1070    return nullptr;
   1071  }
   1072 
   1073  RefPtr<TextureClient> texture = tc->mTextureClient;
   1074  tc->Unlock();
   1075 
   1076  return texture.forget();
   1077 }
   1078 
   1079 bool TextureClient::IsSharedWithCompositor() const {
   1080  return mActor && mActor->IPCOpen();
   1081 }
   1082 
   1083 void TextureClient::AddFlags(TextureFlags aFlags) {
   1084  MOZ_ASSERT(
   1085      !IsSharedWithCompositor() ||
   1086      ((GetFlags() & TextureFlags::RECYCLE) && !IsAddedToCompositableClient()));
   1087  mFlags |= aFlags;
   1088 }
   1089 
   1090 void TextureClient::RemoveFlags(TextureFlags aFlags) {
   1091  MOZ_ASSERT(
   1092      !IsSharedWithCompositor() ||
   1093      ((GetFlags() & TextureFlags::RECYCLE) && !IsAddedToCompositableClient()));
   1094  mFlags &= ~aFlags;
   1095 }
   1096 
   1097 void TextureClient::RecycleTexture(TextureFlags aFlags) {
   1098  MOZ_ASSERT(GetFlags() & TextureFlags::RECYCLE);
   1099  MOZ_ASSERT(!mIsLocked);
   1100 
   1101  mAddedToCompositableClient = false;
   1102  if (mFlags != aFlags) {
   1103    mFlags = aFlags;
   1104  }
   1105 }
   1106 
   1107 void TextureClient::SetAddedToCompositableClient() {
   1108  if (!mAddedToCompositableClient) {
   1109    mAddedToCompositableClient = true;
   1110    if (!(GetFlags() & TextureFlags::RECYCLE)) {
   1111      return;
   1112    }
   1113    MOZ_ASSERT(!mIsLocked);
   1114    LockActor();
   1115    if (IsValid() && mActor && !mActor->mDestroyed && mActor->IPCOpen()) {
   1116      mActor->SendRecycleTexture(mFlags);
   1117    }
   1118    UnlockActor();
   1119  }
   1120 }
   1121 
   1122 static void CancelTextureClientNotifyNotUsed(uint64_t aTextureId,
   1123                                             LayersIPCChannel* aAllocator) {
   1124  if (!aAllocator) {
   1125    return;
   1126  }
   1127  nsCOMPtr<nsISerialEventTarget> thread = aAllocator->GetThread();
   1128  if (!thread) {
   1129    return;
   1130  }
   1131  if (thread->IsOnCurrentThread()) {
   1132    aAllocator->CancelWaitForNotifyNotUsed(aTextureId);
   1133  } else {
   1134    thread->Dispatch(NewRunnableFunction(
   1135        "CancelTextureClientNotifyNotUsedRunnable",
   1136        CancelTextureClientNotifyNotUsed, aTextureId, aAllocator));
   1137  }
   1138 }
   1139 
   1140 void TextureClient::CancelWaitForNotifyNotUsed() {
   1141  if (GetFlags() & TextureFlags::RECYCLE) {
   1142    CancelTextureClientNotifyNotUsed(mSerial, GetAllocator());
   1143    return;
   1144  }
   1145 }
   1146 
   1147 /* static */
   1148 void TextureClient::TextureClientRecycleCallback(TextureClient* aClient,
   1149                                                 void* aClosure) {
   1150  MOZ_ASSERT(aClient->GetRecycleAllocator());
   1151  aClient->GetRecycleAllocator()->RecycleTextureClient(aClient);
   1152 }
   1153 
   1154 void TextureClient::SetRecycleAllocator(
   1155    ITextureClientRecycleAllocator* aAllocator) {
   1156  mRecycleAllocator = aAllocator;
   1157  if (aAllocator) {
   1158    SetRecycleCallback(TextureClientRecycleCallback, nullptr);
   1159  } else {
   1160    ClearRecycleCallback();
   1161  }
   1162 }
   1163 
   1164 bool TextureClient::InitIPDLActor(CompositableForwarder* aForwarder) {
   1165  MOZ_ASSERT(aForwarder && aForwarder->GetTextureForwarder()->GetThread() ==
   1166                               mAllocator->GetThread());
   1167 
   1168  if (mActor && !mActor->IPCOpen()) {
   1169    return false;
   1170  }
   1171 
   1172  if (mActor && !mActor->mDestroyed) {
   1173    CompositableForwarder* currentFwd = mActor->mCompositableForwarder;
   1174    TextureForwarder* currentTexFwd = mActor->mTextureForwarder;
   1175    if (currentFwd != aForwarder) {
   1176      // It's a bit iffy but right now ShadowLayerForwarder inherits
   1177      // TextureForwarder even though it should not.
   1178      // ShadowLayerForwarder::GetTextureForwarder actually returns a pointer to
   1179      // the CompositorBridgeChild. It's Ok for a texture to move from a
   1180      // ShadowLayerForwarder to another, but not form a CompositorBridgeChild
   1181      // to another (they use different channels).
   1182      if (currentTexFwd && currentTexFwd != aForwarder->GetTextureForwarder()) {
   1183        gfxCriticalError()
   1184            << "Attempt to move a texture to a different channel CF.";
   1185        MOZ_ASSERT_UNREACHABLE("unexpected to be called");
   1186        return false;
   1187      }
   1188      if (currentFwd && currentFwd->GetCompositorBackendType() !=
   1189                            aForwarder->GetCompositorBackendType()) {
   1190        gfxCriticalError()
   1191            << "Attempt to move a texture to different compositor backend.";
   1192        MOZ_ASSERT_UNREACHABLE("unexpected to be called");
   1193        return false;
   1194      }
   1195      mActor->mCompositableForwarder = aForwarder;
   1196      mActor->mUsesImageBridge =
   1197          aForwarder->GetTextureForwarder()->UsesImageBridge();
   1198    }
   1199    return true;
   1200  }
   1201  MOZ_ASSERT(!mActor || mActor->mDestroyed,
   1202             "Cannot use a texture on several IPC channels.");
   1203 
   1204  SurfaceDescriptor desc;
   1205  if (!ToSurfaceDescriptor(desc)) {
   1206    return false;
   1207  }
   1208 
   1209  // Try external image id allocation.
   1210  mExternalImageId =
   1211      aForwarder->GetTextureForwarder()->GetNextExternalImageId();
   1212 
   1213  ReadLockDescriptor readLockDescriptor = null_t();
   1214 
   1215  {
   1216    MutexAutoLock lock(mMutex);
   1217    EnsureHasReadLock();
   1218    if (mReadLock) {
   1219      mReadLock->Serialize(readLockDescriptor, GetAllocator()->GetParentPid());
   1220    }
   1221  }
   1222 
   1223  PTextureChild* actor = aForwarder->GetTextureForwarder()->CreateTexture(
   1224      desc, std::move(readLockDescriptor),
   1225      aForwarder->GetCompositorBackendType(), GetFlags(),
   1226      dom::ContentParentId(), mSerial, mExternalImageId);
   1227 
   1228  if (!actor) {
   1229    gfxCriticalNote << static_cast<int32_t>(desc.type()) << ", "
   1230                    << static_cast<int32_t>(
   1231                           aForwarder->GetCompositorBackendType())
   1232                    << ", " << static_cast<uint32_t>(GetFlags()) << ", "
   1233                    << mSerial;
   1234    return false;
   1235  }
   1236 
   1237  mActor = static_cast<TextureChild*>(actor);
   1238  mActor->mCompositableForwarder = aForwarder;
   1239  mActor->mTextureForwarder = aForwarder->GetTextureForwarder();
   1240  mActor->mTextureClient = this;
   1241 
   1242  // If the TextureClient is already locked, we have to lock TextureChild's
   1243  // mutex since it will be unlocked in TextureClient::Unlock.
   1244  if (mIsLocked) {
   1245    LockActor();
   1246  }
   1247 
   1248  return mActor->IPCOpen();
   1249 }
   1250 
   1251 bool TextureClient::InitIPDLActor(KnowsCompositor* aKnowsCompositor,
   1252                                  const dom::ContentParentId& aContentId) {
   1253  MOZ_ASSERT(aKnowsCompositor &&
   1254             aKnowsCompositor->GetTextureForwarder()->GetThread() ==
   1255                 mAllocator->GetThread());
   1256  TextureForwarder* fwd = aKnowsCompositor->GetTextureForwarder();
   1257  if (mActor && !mActor->mDestroyed) {
   1258    CompositableForwarder* currentFwd = mActor->mCompositableForwarder;
   1259    TextureForwarder* currentTexFwd = mActor->mTextureForwarder;
   1260 
   1261    if (currentFwd) {
   1262      gfxCriticalError()
   1263          << "Attempt to remove a texture from a CompositableForwarder.";
   1264      return false;
   1265    }
   1266 
   1267    if (currentTexFwd && currentTexFwd != fwd) {
   1268      gfxCriticalError()
   1269          << "Attempt to move a texture to a different channel TF.";
   1270      return false;
   1271    }
   1272    mActor->mTextureForwarder = fwd;
   1273    return true;
   1274  }
   1275  MOZ_ASSERT(!mActor || mActor->mDestroyed,
   1276             "Cannot use a texture on several IPC channels.");
   1277 
   1278  SurfaceDescriptor desc;
   1279  if (!ToSurfaceDescriptor(desc)) {
   1280    return false;
   1281  }
   1282 
   1283  // Try external image id allocation.
   1284  mExternalImageId =
   1285      aKnowsCompositor->GetTextureForwarder()->GetNextExternalImageId();
   1286 
   1287  ReadLockDescriptor readLockDescriptor = null_t();
   1288  {
   1289    MutexAutoLock lock(mMutex);
   1290    EnsureHasReadLock();
   1291    if (mReadLock) {
   1292      mReadLock->Serialize(readLockDescriptor, GetAllocator()->GetParentPid());
   1293    }
   1294  }
   1295 
   1296  PTextureChild* actor =
   1297      fwd->CreateTexture(desc, std::move(readLockDescriptor),
   1298                         aKnowsCompositor->GetCompositorBackendType(),
   1299                         GetFlags(), aContentId, mSerial, mExternalImageId);
   1300  if (!actor) {
   1301    gfxCriticalNote << static_cast<int32_t>(desc.type()) << ", "
   1302                    << static_cast<int32_t>(
   1303                           aKnowsCompositor->GetCompositorBackendType())
   1304                    << ", " << static_cast<uint32_t>(GetFlags()) << ", "
   1305                    << mSerial;
   1306    return false;
   1307  }
   1308 
   1309  mActor = static_cast<TextureChild*>(actor);
   1310  mActor->mTextureForwarder = fwd;
   1311  mActor->mTextureClient = this;
   1312 
   1313  // If the TextureClient is already locked, we have to lock TextureChild's
   1314  // mutex since it will be unlocked in TextureClient::Unlock.
   1315  if (mIsLocked) {
   1316    LockActor();
   1317  }
   1318 
   1319  return mActor->IPCOpen();
   1320 }
   1321 
   1322 PTextureChild* TextureClient::GetIPDLActor() { return mActor; }
   1323 
   1324 // static
   1325 already_AddRefed<TextureClient> TextureClient::CreateForDrawing(
   1326    KnowsCompositor* aAllocator, gfx::SurfaceFormat aFormat, gfx::IntSize aSize,
   1327    BackendSelector aSelector, TextureFlags aTextureFlags,
   1328    TextureAllocationFlags aAllocFlags) {
   1329  return TextureClient::CreateForDrawing(aAllocator->GetTextureForwarder(),
   1330                                         aFormat, aSize, aAllocator, aSelector,
   1331                                         aTextureFlags, aAllocFlags);
   1332 }
   1333 
   1334 // static
   1335 already_AddRefed<TextureClient> TextureClient::CreateForDrawing(
   1336    TextureForwarder* aAllocator, gfx::SurfaceFormat aFormat,
   1337    gfx::IntSize aSize, KnowsCompositor* aKnowsCompositor,
   1338    BackendSelector aSelector, TextureFlags aTextureFlags,
   1339    TextureAllocationFlags aAllocFlags) {
   1340  LayersBackend layersBackend = aKnowsCompositor->GetCompositorBackendType();
   1341  gfx::BackendType moz2DBackend =
   1342      BackendTypeForBackendSelector(layersBackend, aSelector);
   1343 
   1344  // also test the validity of aAllocator
   1345  if (!aAllocator || !aAllocator->IPCOpen()) {
   1346    return nullptr;
   1347  }
   1348 
   1349  if (!gfx::Factory::AllowedSurfaceSize(aSize)) {
   1350    return nullptr;
   1351  }
   1352 
   1353  TextureData* data =
   1354      TextureData::Create(aAllocator, aFormat, aSize, aKnowsCompositor,
   1355                          aSelector, aTextureFlags, aAllocFlags);
   1356 
   1357  if (data) {
   1358    return MakeAndAddRef<TextureClient>(data, aTextureFlags, aAllocator);
   1359  }
   1360  if (aAllocFlags & ALLOC_FORCE_REMOTE) {
   1361    // If we must be remote, but allocation failed, then don't fall back.
   1362    return nullptr;
   1363  }
   1364 
   1365  // Can't do any better than a buffer texture client.
   1366  return TextureClient::CreateForRawBufferAccess(aAllocator, aFormat, aSize,
   1367                                                 moz2DBackend, layersBackend,
   1368                                                 aTextureFlags, aAllocFlags);
   1369 }
   1370 
   1371 // static
   1372 already_AddRefed<TextureClient> TextureClient::CreateFromSurface(
   1373    KnowsCompositor* aAllocator, gfx::SourceSurface* aSurface,
   1374    BackendSelector aSelector, TextureFlags aTextureFlags,
   1375    TextureAllocationFlags aAllocFlags) {
   1376  // also test the validity of aAllocator
   1377  if (!aAllocator || !aAllocator->GetTextureForwarder()->IPCOpen()) {
   1378    return nullptr;
   1379  }
   1380 
   1381  gfx::IntSize size = aSurface->GetSize();
   1382 
   1383  if (!gfx::Factory::AllowedSurfaceSize(size)) {
   1384    return nullptr;
   1385  }
   1386 
   1387  // Fall back to using UpdateFromSurface
   1388 
   1389  TextureAllocationFlags allocFlags =
   1390      TextureAllocationFlags(aAllocFlags | ALLOC_UPDATE_FROM_SURFACE);
   1391  RefPtr<TextureClient> client =
   1392      CreateForDrawing(aAllocator, aSurface->GetFormat(), size, aSelector,
   1393                       aTextureFlags, allocFlags);
   1394  if (!client) {
   1395    return nullptr;
   1396  }
   1397 
   1398  TextureClientAutoLock autoLock(client, OpenMode::OPEN_WRITE_ONLY);
   1399  if (!autoLock.Succeeded()) {
   1400    return nullptr;
   1401  }
   1402 
   1403  client->UpdateFromSurface(aSurface);
   1404  return client.forget();
   1405 }
   1406 
   1407 // static
   1408 already_AddRefed<TextureClient> TextureClient::CreateForRawBufferAccess(
   1409    KnowsCompositor* aAllocator, gfx::SurfaceFormat aFormat, gfx::IntSize aSize,
   1410    gfx::BackendType aMoz2DBackend, TextureFlags aTextureFlags,
   1411    TextureAllocationFlags aAllocFlags) {
   1412  return CreateForRawBufferAccess(
   1413      aAllocator->GetTextureForwarder(), aFormat, aSize, aMoz2DBackend,
   1414      aAllocator->GetCompositorBackendType(), aTextureFlags, aAllocFlags);
   1415 }
   1416 
   1417 // static
   1418 already_AddRefed<TextureClient> TextureClient::CreateForRawBufferAccess(
   1419    LayersIPCChannel* aAllocator, gfx::SurfaceFormat aFormat,
   1420    gfx::IntSize aSize, gfx::BackendType aMoz2DBackend,
   1421    LayersBackend aLayersBackend, TextureFlags aTextureFlags,
   1422    TextureAllocationFlags aAllocFlags) {
   1423  // also test the validity of aAllocator
   1424  if (!aAllocator || !aAllocator->IPCOpen()) {
   1425    return nullptr;
   1426  }
   1427 
   1428  if (!gfx::Factory::AllowedSurfaceSize(aSize)) {
   1429    return nullptr;
   1430  }
   1431 
   1432  if (aFormat == SurfaceFormat::B8G8R8X8) {
   1433    // Skia doesn't support RGBX, so ensure we clear the buffer for the proper
   1434    // alpha values.
   1435    aAllocFlags = TextureAllocationFlags(aAllocFlags | ALLOC_CLEAR_BUFFER);
   1436  }
   1437 
   1438  // We ignore the backend type if we get here. It should only be Skia.
   1439  // Therefore it is safe to force the buffer to be Skia.
   1440  NS_WARNING_ASSERTION(aMoz2DBackend == gfx::BackendType::SKIA,
   1441                       "Unsupported TextureClient backend type");
   1442 
   1443  // For future changes, check aAllocFlags aAllocFlags & ALLOC_DO_NOT_ACCELERATE
   1444  TextureData* texData = BufferTextureData::Create(
   1445      aSize, aFormat, gfx::BackendType::SKIA, aLayersBackend, aTextureFlags,
   1446      aAllocFlags, aAllocator);
   1447  if (!texData) {
   1448    return nullptr;
   1449  }
   1450 
   1451  return MakeAndAddRef<TextureClient>(texData, aTextureFlags, aAllocator);
   1452 }
   1453 
   1454 // static
   1455 already_AddRefed<TextureClient> TextureClient::CreateForYCbCr(
   1456    KnowsCompositor* aAllocator, const gfx::IntRect& aDisplay,
   1457    const gfx::IntSize& aYSize, uint32_t aYStride,
   1458    const gfx::IntSize& aCbCrSize, uint32_t aCbCrStride, StereoMode aStereoMode,
   1459    gfx::ColorDepth aColorDepth, gfx::YUVColorSpace aYUVColorSpace,
   1460    gfx::ColorRange aColorRange, gfx::ChromaSubsampling aSubsampling,
   1461    TextureFlags aTextureFlags) {
   1462  if (!aAllocator || !aAllocator->GetLayersIPCActor()->IPCOpen()) {
   1463    return nullptr;
   1464  }
   1465 
   1466  if (!gfx::Factory::AllowedSurfaceSize(aYSize)) {
   1467    return nullptr;
   1468  }
   1469 
   1470  TextureData* data = BufferTextureData::CreateForYCbCr(
   1471      aAllocator, aDisplay, aYSize, aYStride, aCbCrSize, aCbCrStride,
   1472      aStereoMode, aColorDepth, aYUVColorSpace, aColorRange, aSubsampling,
   1473      aTextureFlags);
   1474  if (!data) {
   1475    return nullptr;
   1476  }
   1477 
   1478  return MakeAndAddRef<TextureClient>(data, aTextureFlags,
   1479                                      aAllocator->GetTextureForwarder());
   1480 }
   1481 
   1482 TextureClient::TextureClient(TextureData* aData, TextureFlags aFlags,
   1483                             LayersIPCChannel* aAllocator)
   1484    : AtomicRefCountedWithFinalize("TextureClient"),
   1485      mMutex("TextureClient::mMutex"),
   1486      mAllocator(aAllocator),
   1487      mActor(nullptr),
   1488      mData(aData),
   1489      mFlags(aFlags),
   1490      mOpenMode(OpenMode::OPEN_NONE),
   1491      mIsLocked(false),
   1492      mIsReadLocked(false),
   1493      mUpdated(false),
   1494      mAddedToCompositableClient(false),
   1495      mFwdTransactionId(0),
   1496      mSerial(++sSerialCounter) {
   1497  mData->FillInfo(mInfo);
   1498  mFlags |= mData->GetTextureFlags();
   1499 }
   1500 
   1501 bool TextureClient::CopyToTextureClient(TextureClient* aTarget,
   1502                                        const gfx::IntRect* aRect,
   1503                                        const gfx::IntPoint* aPoint) {
   1504  MOZ_ASSERT(IsLocked());
   1505  MOZ_ASSERT(aTarget->IsLocked());
   1506 
   1507  if (!aTarget->CanExposeDrawTarget() || !CanExposeDrawTarget()) {
   1508    return false;
   1509  }
   1510 
   1511  RefPtr<DrawTarget> destinationTarget = aTarget->BorrowDrawTarget();
   1512  if (!destinationTarget) {
   1513    gfxWarning() << "TextureClient::CopyToTextureClient (dest) failed in "
   1514                    "BorrowDrawTarget";
   1515    return false;
   1516  }
   1517 
   1518  RefPtr<DrawTarget> sourceTarget = BorrowDrawTarget();
   1519  if (!sourceTarget) {
   1520    gfxWarning() << "TextureClient::CopyToTextureClient (src) failed in "
   1521                    "BorrowDrawTarget";
   1522    return false;
   1523  }
   1524 
   1525  RefPtr<gfx::SourceSurface> source = sourceTarget->Snapshot();
   1526  destinationTarget->CopySurface(
   1527      source, aRect ? *aRect : gfx::IntRect(gfx::IntPoint(0, 0), GetSize()),
   1528      aPoint ? *aPoint : gfx::IntPoint(0, 0));
   1529  return true;
   1530 }
   1531 
   1532 already_AddRefed<gfx::DataSourceSurface> TextureClient::GetAsSurface() {
   1533  if (!Lock(OpenMode::OPEN_READ)) {
   1534    return nullptr;
   1535  }
   1536  RefPtr<gfx::DataSourceSurface> data;
   1537  {  // scope so that the DrawTarget is destroyed before Unlock()
   1538    RefPtr<gfx::DrawTarget> dt = BorrowDrawTarget();
   1539    if (dt) {
   1540      RefPtr<gfx::SourceSurface> surf = dt->Snapshot();
   1541      if (surf) {
   1542        data = surf->GetDataSurface();
   1543      }
   1544    }
   1545  }
   1546  Unlock();
   1547  return data.forget();
   1548 }
   1549 
   1550 void TextureClient::GetSurfaceDescriptorRemoteDecoder(
   1551    SurfaceDescriptorRemoteDecoder* const aOutDesc) {
   1552  const auto handle = GetSerial();
   1553 
   1554  RemoteDecoderVideoSubDescriptor subDesc = null_t();
   1555  MOZ_RELEASE_ASSERT(mData);
   1556  mData->GetSubDescriptor(&subDesc);
   1557 
   1558  *aOutDesc = SurfaceDescriptorRemoteDecoder(
   1559      handle, std::move(subDesc), Nothing(),
   1560      SurfaceDescriptorRemoteDecoderId::GetNext());
   1561 }
   1562 
   1563 class MemoryTextureReadLock : public NonBlockingTextureReadLock {
   1564 public:
   1565  MemoryTextureReadLock();
   1566 
   1567  virtual ~MemoryTextureReadLock();
   1568 
   1569  bool ReadLock() override;
   1570 
   1571  int32_t ReadUnlock() override;
   1572 
   1573  int32_t GetReadCount() override;
   1574 
   1575  LockType GetType() override { return TYPE_NONBLOCKING_MEMORY; }
   1576 
   1577  bool IsValid() const override { return true; };
   1578 
   1579  bool Serialize(ReadLockDescriptor& aOutput, base::ProcessId aOther) override;
   1580 
   1581  Atomic<int32_t> mReadCount;
   1582 };
   1583 
   1584 // The cross-prcess implementation of TextureReadLock.
   1585 //
   1586 // Since we don't use cross-process reference counting for the ReadLock objects,
   1587 // we use the lock's internal counter as a way to know when to deallocate the
   1588 // underlying shmem section: when the counter is equal to 1, it means that the
   1589 // lock is not "held" (the texture is writable), when the counter is equal to 0
   1590 // it means that we can safely deallocate the shmem section without causing a
   1591 // race condition with the other process.
   1592 class ShmemTextureReadLock : public NonBlockingTextureReadLock {
   1593 public:
   1594  struct ShmReadLockInfo {
   1595    int32_t readCount;
   1596  };
   1597 
   1598  explicit ShmemTextureReadLock(LayersIPCChannel* aAllocator);
   1599 
   1600  virtual ~ShmemTextureReadLock();
   1601 
   1602  bool ReadLock() override;
   1603 
   1604  int32_t ReadUnlock() override;
   1605 
   1606  int32_t GetReadCount() override;
   1607 
   1608  bool IsValid() const override { return mAllocSuccess; };
   1609 
   1610  LockType GetType() override { return TYPE_NONBLOCKING_SHMEM; }
   1611 
   1612  bool Serialize(ReadLockDescriptor& aOutput, base::ProcessId aOther) override;
   1613 
   1614  mozilla::layers::ShmemSection& GetShmemSection() { return mShmemSection; }
   1615 
   1616  explicit ShmemTextureReadLock(
   1617      const mozilla::layers::ShmemSection& aShmemSection)
   1618      : mShmemSection(aShmemSection), mAllocSuccess(true) {
   1619    MOZ_COUNT_CTOR(ShmemTextureReadLock);
   1620  }
   1621 
   1622  ShmReadLockInfo* GetShmReadLockInfoPtr() {
   1623    return reinterpret_cast<ShmReadLockInfo*>(
   1624        mShmemSection.shmem().get<char>() + mShmemSection.offset());
   1625  }
   1626 
   1627  RefPtr<LayersIPCChannel> mClientAllocator;
   1628  mozilla::layers::ShmemSection mShmemSection;
   1629  bool mAllocSuccess;
   1630 };
   1631 
   1632 class CrossProcessSemaphoreReadLock : public TextureReadLock {
   1633 public:
   1634  CrossProcessSemaphoreReadLock()
   1635      : mSemaphore(CrossProcessSemaphore::Create("TextureReadLock", 1)),
   1636        mShared(false) {}
   1637  explicit CrossProcessSemaphoreReadLock(CrossProcessSemaphoreHandle aHandle)
   1638      : mSemaphore(CrossProcessSemaphore::Create(std::move(aHandle))),
   1639        mShared(false) {}
   1640 
   1641  bool ReadLock() override {
   1642    if (!IsValid()) {
   1643      return false;
   1644    }
   1645    return mSemaphore->Wait();
   1646  }
   1647  bool TryReadLock(TimeDuration aTimeout) override {
   1648    if (!IsValid()) {
   1649      return false;
   1650    }
   1651    return mSemaphore->Wait(Some(aTimeout));
   1652  }
   1653  int32_t ReadUnlock() override {
   1654    if (!IsValid()) {
   1655      return 1;
   1656    }
   1657    mSemaphore->Signal();
   1658    return 1;
   1659  }
   1660  bool IsValid() const override { return !!mSemaphore; }
   1661 
   1662  bool Serialize(ReadLockDescriptor& aOutput, base::ProcessId aOther) override;
   1663 
   1664  LockType GetType() override { return TYPE_CROSS_PROCESS_SEMAPHORE; }
   1665 
   1666  UniquePtr<CrossProcessSemaphore> mSemaphore;
   1667  bool mShared;
   1668 };
   1669 
   1670 // static
   1671 already_AddRefed<TextureReadLock> TextureReadLock::Deserialize(
   1672    ReadLockDescriptor&& aDescriptor, ISurfaceAllocator* aAllocator) {
   1673  switch (aDescriptor.type()) {
   1674    case ReadLockDescriptor::TUntrustedShmemSection: {
   1675      const UntrustedShmemSection& untrusted =
   1676          aDescriptor.get_UntrustedShmemSection();
   1677      Maybe<ShmemSection> section = ShmemSection::FromUntrusted(untrusted);
   1678      if (section.isNothing()) {
   1679        return nullptr;
   1680      }
   1681      return MakeAndAddRef<ShmemTextureReadLock>(section.value());
   1682    }
   1683    case ReadLockDescriptor::Tuintptr_t: {
   1684      if (!aAllocator->IsSameProcess()) {
   1685        // Trying to use a memory based lock instead of a shmem based one in
   1686        // the cross-process case is a bad security violation.
   1687        NS_ERROR(
   1688            "A client process may be trying to peek at the host's address "
   1689            "space!");
   1690        return nullptr;
   1691      }
   1692      RefPtr<TextureReadLock> lock =
   1693          reinterpret_cast<MemoryTextureReadLock*>(aDescriptor.get_uintptr_t());
   1694 
   1695      MOZ_ASSERT(lock);
   1696      if (lock) {
   1697        // The corresponding AddRef is in MemoryTextureReadLock::Serialize
   1698        lock.get()->Release();
   1699      }
   1700 
   1701      return lock.forget();
   1702    }
   1703    case ReadLockDescriptor::TCrossProcessSemaphoreDescriptor: {
   1704      return MakeAndAddRef<CrossProcessSemaphoreReadLock>(
   1705          std::move(aDescriptor.get_CrossProcessSemaphoreDescriptor().sem()));
   1706    }
   1707    case ReadLockDescriptor::Tnull_t: {
   1708      return nullptr;
   1709    }
   1710    default: {
   1711      MOZ_DIAGNOSTIC_CRASH(
   1712          "Invalid descriptor in TextureReadLock::Deserialize");
   1713    }
   1714  }
   1715  return nullptr;
   1716 }
   1717 // static
   1718 already_AddRefed<TextureReadLock> NonBlockingTextureReadLock::Create(
   1719    LayersIPCChannel* aAllocator) {
   1720  if (aAllocator->IsSameProcess()) {
   1721    // If our compositor is in the same process, we can save some cycles by not
   1722    // using shared memory.
   1723    return MakeAndAddRef<MemoryTextureReadLock>();
   1724  }
   1725 
   1726  return MakeAndAddRef<ShmemTextureReadLock>(aAllocator);
   1727 }
   1728 
   1729 MemoryTextureReadLock::MemoryTextureReadLock() : mReadCount(1) {
   1730  MOZ_COUNT_CTOR(MemoryTextureReadLock);
   1731 }
   1732 
   1733 MemoryTextureReadLock::~MemoryTextureReadLock() {
   1734  // One read count that is added in constructor.
   1735  MOZ_ASSERT(mReadCount == 1);
   1736  MOZ_COUNT_DTOR(MemoryTextureReadLock);
   1737 }
   1738 
   1739 bool MemoryTextureReadLock::Serialize(ReadLockDescriptor& aOutput,
   1740                                      base::ProcessId aOther) {
   1741  // AddRef here and Release when receiving on the host side to make sure the
   1742  // reference count doesn't go to zero before the host receives the message.
   1743  // see TextureReadLock::Deserialize
   1744  this->AddRef();
   1745  aOutput = ReadLockDescriptor(uintptr_t(this));
   1746  return true;
   1747 }
   1748 
   1749 bool MemoryTextureReadLock::ReadLock() {
   1750  ++mReadCount;
   1751  return true;
   1752 }
   1753 
   1754 int32_t MemoryTextureReadLock::ReadUnlock() {
   1755  int32_t readCount = --mReadCount;
   1756  MOZ_ASSERT(readCount >= 0);
   1757 
   1758  return readCount;
   1759 }
   1760 
   1761 int32_t MemoryTextureReadLock::GetReadCount() { return mReadCount; }
   1762 
   1763 ShmemTextureReadLock::ShmemTextureReadLock(LayersIPCChannel* aAllocator)
   1764    : mClientAllocator(aAllocator), mAllocSuccess(false) {
   1765  MOZ_COUNT_CTOR(ShmemTextureReadLock);
   1766  MOZ_ASSERT(mClientAllocator);
   1767  MOZ_ASSERT(mClientAllocator->GetTileLockAllocator());
   1768 #define MOZ_ALIGN_WORD(x) (((x) + 3) & ~3)
   1769  if (mClientAllocator->GetTileLockAllocator()->AllocShmemSection(
   1770          MOZ_ALIGN_WORD(sizeof(ShmReadLockInfo)), &mShmemSection)) {
   1771    ShmReadLockInfo* info = GetShmReadLockInfoPtr();
   1772    info->readCount = 1;
   1773    mAllocSuccess = true;
   1774  }
   1775 }
   1776 
   1777 ShmemTextureReadLock::~ShmemTextureReadLock() {
   1778  if (mClientAllocator) {
   1779    // Release one read count that is added in constructor.
   1780    // The count is kept for calling GetReadCount() by TextureClientPool.
   1781    ReadUnlock();
   1782  }
   1783  MOZ_COUNT_DTOR(ShmemTextureReadLock);
   1784 }
   1785 
   1786 bool ShmemTextureReadLock::Serialize(ReadLockDescriptor& aOutput,
   1787                                     base::ProcessId aOther) {
   1788  aOutput = ReadLockDescriptor(GetShmemSection().AsUntrusted());
   1789  return true;
   1790 }
   1791 
   1792 bool ShmemTextureReadLock::ReadLock() {
   1793  if (!mAllocSuccess) {
   1794    return false;
   1795  }
   1796  ShmReadLockInfo* info = GetShmReadLockInfoPtr();
   1797  PR_ATOMIC_INCREMENT(&info->readCount);
   1798  return true;
   1799 }
   1800 
   1801 int32_t ShmemTextureReadLock::ReadUnlock() {
   1802  if (!mAllocSuccess) {
   1803    return 0;
   1804  }
   1805  ShmReadLockInfo* info = GetShmReadLockInfoPtr();
   1806  int32_t readCount = PR_ATOMIC_DECREMENT(&info->readCount);
   1807  MOZ_ASSERT(readCount >= 0);
   1808  if (readCount > 0) {
   1809    return readCount;
   1810  }
   1811  if (mClientAllocator) {
   1812    if (nsCOMPtr<nsISerialEventTarget> thread = mClientAllocator->GetThread()) {
   1813      if (thread->IsOnCurrentThread()) {
   1814        if (auto* tileLockAllocator =
   1815                mClientAllocator->GetTileLockAllocator()) {
   1816          tileLockAllocator->DeallocShmemSection(mShmemSection);
   1817          return readCount;
   1818        }
   1819      } else {
   1820        thread->Dispatch(NS_NewRunnableFunction(
   1821            __func__,
   1822            [shmemSection = std::move(mShmemSection),
   1823             clientAllocator = std::move(mClientAllocator)]() mutable {
   1824              if (auto* tileLockAllocator =
   1825                      clientAllocator->GetTileLockAllocator()) {
   1826                tileLockAllocator->DeallocShmemSection(shmemSection);
   1827              } else {
   1828                // we are on the compositor process, or IPC is down.
   1829                FixedSizeSmallShmemSectionAllocator::FreeShmemSection(
   1830                    shmemSection);
   1831              }
   1832            }));
   1833        return readCount;
   1834      }
   1835    }
   1836  }
   1837  // we are on the compositor process, or IPC is down.
   1838  FixedSizeSmallShmemSectionAllocator::FreeShmemSection(mShmemSection);
   1839  return readCount;
   1840 }
   1841 
   1842 int32_t ShmemTextureReadLock::GetReadCount() {
   1843  if (!mAllocSuccess) {
   1844    return 0;
   1845  }
   1846  ShmReadLockInfo* info = GetShmReadLockInfoPtr();
   1847  return info->readCount;
   1848 }
   1849 
   1850 bool CrossProcessSemaphoreReadLock::Serialize(ReadLockDescriptor& aOutput,
   1851                                              base::ProcessId aOther) {
   1852  if (!mShared && IsValid()) {
   1853    aOutput = ReadLockDescriptor(
   1854        CrossProcessSemaphoreDescriptor(mSemaphore->CloneHandle()));
   1855    mSemaphore->CloseHandle();
   1856    mShared = true;
   1857    return true;
   1858  } else {
   1859    return mShared;
   1860  }
   1861 }
   1862 
   1863 void TextureClient::EnableBlockingReadLock() {
   1864  MOZ_ASSERT(ShouldReadLock());
   1865  if (!mReadLock) {
   1866    mReadLock = new CrossProcessSemaphoreReadLock();
   1867  }
   1868 }
   1869 
   1870 bool UpdateYCbCrTextureClient(TextureClient* aTexture,
   1871                              const PlanarYCbCrData& aData) {
   1872  MOZ_ASSERT(aTexture);
   1873  MOZ_ASSERT(aTexture->IsLocked());
   1874  MOZ_ASSERT(aTexture->GetFormat() == gfx::SurfaceFormat::YUV420,
   1875             "This textureClient can only use YCbCr data");
   1876  MOZ_ASSERT(!aTexture->IsImmutable());
   1877  MOZ_ASSERT(aTexture->IsValid());
   1878  MOZ_ASSERT(aData.mCbSkip == aData.mCrSkip);
   1879 
   1880  MappedYCbCrTextureData mapped;
   1881  if (!aTexture->BorrowMappedYCbCrData(mapped)) {
   1882    NS_WARNING("Failed to extract YCbCr info!");
   1883    return false;
   1884  }
   1885 
   1886  uint32_t bytesPerPixel =
   1887      BytesPerPixel(SurfaceFormatForColorDepth(aData.mColorDepth));
   1888  MappedYCbCrTextureData srcData;
   1889  srcData.y.data = aData.mYChannel;
   1890  srcData.y.size = aData.YDataSize();
   1891  srcData.y.stride = aData.mYStride;
   1892  srcData.y.skip = aData.mYSkip;
   1893  srcData.y.bytesPerPixel = bytesPerPixel;
   1894  srcData.cb.data = aData.mCbChannel;
   1895  srcData.cb.size = aData.CbCrDataSize();
   1896  srcData.cb.stride = aData.mCbCrStride;
   1897  srcData.cb.skip = aData.mCbSkip;
   1898  srcData.cb.bytesPerPixel = bytesPerPixel;
   1899  srcData.cr.data = aData.mCrChannel;
   1900  srcData.cr.size = aData.CbCrDataSize();
   1901  srcData.cr.stride = aData.mCbCrStride;
   1902  srcData.cr.skip = aData.mCrSkip;
   1903  srcData.cr.bytesPerPixel = bytesPerPixel;
   1904  srcData.metadata = nullptr;
   1905 
   1906  if (!srcData.CopyInto(mapped)) {
   1907    NS_WARNING("Failed to copy image data!");
   1908    return false;
   1909  }
   1910 
   1911  if (TextureRequiresLocking(aTexture->GetFlags())) {
   1912    // We don't have support for proper locking yet, so we'll
   1913    // have to be immutable instead.
   1914    aTexture->MarkImmutable();
   1915  }
   1916  return true;
   1917 }
   1918 
   1919 already_AddRefed<TextureClient> TextureClient::CreateWithData(
   1920    TextureData* aData, TextureFlags aFlags, LayersIPCChannel* aAllocator) {
   1921  if (!aData) {
   1922    return nullptr;
   1923  }
   1924  return MakeAndAddRef<TextureClient>(aData, aFlags, aAllocator);
   1925 }
   1926 
   1927 template <class PixelDataType>
   1928 static void copyData(PixelDataType* aDst,
   1929                     const MappedYCbCrChannelData& aChannelDst,
   1930                     PixelDataType* aSrc,
   1931                     const MappedYCbCrChannelData& aChannelSrc) {
   1932  uint8_t* srcByte = reinterpret_cast<uint8_t*>(aSrc);
   1933  const int32_t srcSkip = aChannelSrc.skip + 1;
   1934  uint8_t* dstByte = reinterpret_cast<uint8_t*>(aDst);
   1935  const int32_t dstSkip = aChannelDst.skip + 1;
   1936  for (int32_t i = 0; i < aChannelSrc.size.height; ++i) {
   1937    for (int32_t j = 0; j < aChannelSrc.size.width; ++j) {
   1938      *aDst = *aSrc;
   1939      aSrc += srcSkip;
   1940      aDst += dstSkip;
   1941    }
   1942    srcByte += aChannelSrc.stride;
   1943    aSrc = reinterpret_cast<PixelDataType*>(srcByte);
   1944    dstByte += aChannelDst.stride;
   1945    aDst = reinterpret_cast<PixelDataType*>(dstByte);
   1946  }
   1947 }
   1948 
   1949 bool MappedYCbCrChannelData::CopyInto(MappedYCbCrChannelData& aDst) {
   1950  if (!data || !aDst.data || size != aDst.size) {
   1951    return false;
   1952  }
   1953 
   1954  if (stride == aDst.stride && skip == aDst.skip) {
   1955    // fast path!
   1956    // We assume that the padding in the destination is there for alignment
   1957    // purposes and doesn't contain useful data.
   1958    memcpy(aDst.data, data, stride * size.height);
   1959    return true;
   1960  }
   1961 
   1962  if (aDst.skip == 0 && skip == 0) {
   1963    // fast-ish path
   1964    for (int32_t i = 0; i < size.height; ++i) {
   1965      memcpy(aDst.data + i * aDst.stride, data + i * stride,
   1966             size.width * bytesPerPixel);
   1967    }
   1968    return true;
   1969  }
   1970 
   1971  MOZ_ASSERT(bytesPerPixel == 1 || bytesPerPixel == 2);
   1972  // slow path
   1973  if (bytesPerPixel == 1) {
   1974    copyData(aDst.data, aDst, data, *this);
   1975  } else if (bytesPerPixel == 2) {
   1976    copyData(reinterpret_cast<uint16_t*>(aDst.data), aDst,
   1977             reinterpret_cast<uint16_t*>(data), *this);
   1978  }
   1979  return true;
   1980 }
   1981 
   1982 }  // namespace mozilla::layers