Bug 1265824 - Wait on texture handles with IPC r?mattwoodrow draft
authorDoug Thayer <dothayer@mozilla.com>
Sat, 05 May 2018 15:46:26 -0700
changeset 816212 787c3b0b9e52ebc250cfee72e1a29547c0de2731
parent 816211 cbe7962f7da9138172774b54cddae654f8c1944e
child 816213 161e186bc16a99095f2510e47553fbc15fdcff27
push id115776
push userbmo:dothayer@mozilla.com
push dateTue, 10 Jul 2018 18:33:51 +0000
reviewersmattwoodrow
bugs1265824
milestone63.0a1
Bug 1265824 - Wait on texture handles with IPC r?mattwoodrow There's a lot going on here, but it all fits under the idea of being able to communicate about texture locking statuses without spinning on IsReadLocked. This is a bit of a trade - we could just always allocate/grab a texture from the pool, which would put a smaller cap on the amount of time we can possibly spend when a texture is locked. However, this eats up more CPU and memory than waiting on the textures to unlock, and could take longer, especially if there were a large number of textures which we just need to wait for for a short amount of time. In any case, we very rarely hit the case where we actually need to wait on the sync IPC to the compositor - most of the time the textures are already unlocked. There is also an async IPC call in here, which we make before flushing async paints. This just causes the compositor to check whether the GPU is done with its textures or not and unlock them if it is. This helps us avoid the case where we take a long time painting asynchronously, turn IPC back on at the end of that, and then have to wait for the compositor to to get into TiledLayerBufferComposite::UseTiles before getting a response. Specifically this eliminates several talos regressions which use ASAP mode. Lastly, there seem to be no other cases of static Monitors being used. This seems like it falls under similar use cases as StaticMutexes, so I added it in. I can move it into its own file if we think it might be generally useful in the future. MozReview-Commit-ID: IYQLwUqMxg2
gfx/layers/TextureSourceProvider.cpp
gfx/layers/TextureSourceProvider.h
gfx/layers/TextureSync.cpp
gfx/layers/TextureSync.h
gfx/layers/client/ClientLayerManager.cpp
gfx/layers/client/MultiTiledContentClient.cpp
gfx/layers/client/SingleTiledContentClient.cpp
gfx/layers/client/TiledContentClient.cpp
gfx/layers/client/TiledContentClient.h
gfx/layers/composite/TextureHost.cpp
gfx/layers/composite/TextureHost.h
gfx/layers/composite/TiledContentHost.cpp
gfx/layers/ipc/ShadowLayers.cpp
gfx/layers/ipc/ShadowLayers.h
gfx/layers/opengl/CompositorOGL.cpp
gfx/layers/opengl/CompositorOGL.h
gfx/layers/opengl/TextureHostOGL.cpp
gfx/layers/opengl/TextureHostOGL.h
ipc/glue/SharedMemoryBasic_mach.h
ipc/glue/SharedMemoryBasic_mach.mm
--- a/gfx/layers/TextureSourceProvider.cpp
+++ b/gfx/layers/TextureSourceProvider.cpp
@@ -1,32 +1,50 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/layers/TextureSourceProvider.h"
 #include "mozilla/layers/TextureHost.h"
+#include "mozilla/layers/PTextureParent.h"
 
 namespace mozilla {
 namespace layers {
 
 TextureSourceProvider::~TextureSourceProvider()
 {
   ReadUnlockTextures();
 }
 
 void
 TextureSourceProvider::ReadUnlockTextures()
 {
+  nsClassHashtable<nsUint32HashKey, nsTArray<uint64_t>> texturesIdsToUnlockByPid;
   for (auto& texture : mUnlockAfterComposition) {
-    texture->ReadUnlock();
+    auto bufferTexture = texture->AsBufferTextureHost();
+    if (bufferTexture && bufferTexture->IsDirectMap()) {
+      texture->ReadUnlock();
+      auto actor = texture->GetIPDLActor();
+      if (actor) {
+        base::ProcessId pid = actor->OtherPid();
+        nsTArray<uint64_t>* textureIds = texturesIdsToUnlockByPid.LookupOrAdd(pid);
+        textureIds->AppendElement(TextureHost::GetTextureSerial(actor));
+      }
+    } else {
+      texture->ReadUnlock();
+    }
   }
   mUnlockAfterComposition.Clear();
+#ifdef XP_DARWIN
+  for (auto it = texturesIdsToUnlockByPid.ConstIter(); !it.Done(); it.Next()) {
+    ipc::SharedMemoryBasic::SetTexturesUnlocked(it.Key(), *it.UserData());
+  }
+#endif
 }
 
 void
 TextureSourceProvider::UnlockAfterComposition(TextureHost* aTexture)
 {
   mUnlockAfterComposition.AppendElement(aTexture);
 }
 
--- a/gfx/layers/TextureSourceProvider.h
+++ b/gfx/layers/TextureSourceProvider.h
@@ -69,16 +69,19 @@ public:
   /// wait for the end of the next composition before NotifyNotUsed() call.
   /// This function provides a convenient way to do this delayed NotifyNotUsed()
   /// call, if the texture itself requires it.
   /// See bug 1260611 and bug 1252835
   ///
   /// Returns true if notified, false otherwise.
   virtual bool NotifyNotUsedAfterComposition(TextureHost* aTextureHost);
 
+  virtual void MaybeUnlockBeforeNextComposition(TextureHost* aTextureHost) {}
+  virtual void TryUnlockTextures() {}
+
   // If overridden, make sure to call the base function.
   virtual void Destroy();
 
   void FlushPendingNotifyNotUsed();
 
   // If this provider is also a Compositor, return the compositor. Otherwise return
   // null.
   virtual Compositor* AsCompositor() {
new file mode 100644
--- /dev/null
+++ b/gfx/layers/TextureSync.cpp
@@ -0,0 +1,276 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "TextureSync.h"
+
+#include <unordered_set>
+
+#include "chrome/common/mach_ipc_mac.h"
+#include "mozilla/ipc/SharedMemoryBasic.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/StaticMonitor.h"
+#include "mozilla/StaticPtr.h"
+
+#ifdef DEBUG
+#define LOG_ERROR(str, args...)                                   \
+  PR_BEGIN_MACRO                                                  \
+  mozilla::SmprintfPointer msg = mozilla::Smprintf(str, ## args); \
+  NS_WARNING(msg.get());                                          \
+  PR_END_MACRO
+#else
+#define LOG_ERROR(str, args...) do { /* nothing */ } while(0)
+#endif
+
+#define CHECK_MACH_ERROR(kr, msg)                               \
+  PR_BEGIN_MACRO                                                \
+  if (kr != KERN_SUCCESS) {                                     \
+    LOG_ERROR("%s %s (%x)\n", msg, mach_error_string(kr), kr);  \
+    return false;                                               \
+  }                                                             \
+  PR_END_MACRO
+
+namespace mozilla {
+
+namespace layers {
+
+static nsTArray<RefPtr<TextureSourceProvider>> gTextureSourceProviders;
+
+static std::map<pid_t, std::unordered_set<uint64_t>> gProcessTextureIds;
+static StaticMonitor gTextureLockMonitor;
+
+const int kTimeout = 1000;
+const int kLongTimeout = 60 * kTimeout;
+const int kTextureLockTimeout = 32; // We really don't want to wait more than
+                                    // two frames for a texture to unlock. This
+                                    // will in any case be very uncommon.
+
+struct WaitForTexturesReply
+{
+  bool success;
+};
+
+struct WaitForTexturesRequest
+{
+  pid_t pid;
+};
+
+std::unordered_set<uint64_t>*
+GetLockedTextureIdsForProcess(pid_t pid)
+{
+  gTextureLockMonitor.AssertCurrentThreadOwns();
+
+  if (gProcessTextureIds.find(pid) == gProcessTextureIds.end()) {
+    gProcessTextureIds[pid] = std::unordered_set<uint64_t>();
+  }
+
+  return &gProcessTextureIds.at(pid);
+}
+
+bool
+WaitForTextureIdsToUnlock(pid_t pid, uint64_t* textureIds, uint32_t textureIdsLength)
+{
+  {
+    StaticMonitorAutoLock lock(gTextureLockMonitor);
+    std::unordered_set<uint64_t>* freedTextureIds = GetLockedTextureIdsForProcess(pid);
+
+    while (true) {
+      bool allCleared = true;
+      for (uint32_t i = 0; i < textureIdsLength; ++i) {
+        if (freedTextureIds->find(textureIds[i]) != freedTextureIds->end()) {
+          allCleared = false;
+        }
+      }
+
+      if (allCleared) {
+        return true;
+      }
+
+      if (lock.Wait(TimeDuration::FromMilliseconds(kTextureLockTimeout)) == CVStatus::Timeout) {
+        return false;
+      }
+    }
+  }
+}
+
+void
+CheckTexturesForUnlock()
+{
+  for (auto& textureSourceProvider : gTextureSourceProviders) {
+    textureSourceProvider->TryUnlockTextures();
+  }
+}
+
+void
+TextureSync::DispatchCheckTexturesForUnlock()
+{
+  RefPtr<Runnable> task = NS_NewRunnableFunction(
+    "CheckTexturesForUnlock",
+    &CheckTexturesForUnlock);
+  CompositorThreadHolder::Loop()->PostTask(task.forget());
+}
+
+void
+TextureSync::HandleWaitForTexturesMessage(MachReceiveMessage* rmsg, ipc::MemoryPorts* ports)
+{
+  WaitForTexturesRequest* req = reinterpret_cast<WaitForTexturesRequest*>(rmsg->GetData());
+  uint64_t* textureIds = (uint64_t*)(req + 1);
+  uint32_t textureIdsLength = (rmsg->GetDataLength() - sizeof(WaitForTexturesRequest)) / sizeof(uint64_t);
+
+  bool success = WaitForTextureIdsToUnlock(req->pid, textureIds, textureIdsLength);
+
+  if (!success) {
+    LOG_ERROR("Waiting for textures to unlock failed.\n");
+  }
+
+  MachSendMessage msg(ipc::kReturnWaitForTexturesMsg);
+  WaitForTexturesReply replydata;
+  replydata.success = success;
+  msg.SetData(&replydata, sizeof(WaitForTexturesReply));
+  kern_return_t err = ports->mSender->SendMessage(msg, kTimeout);
+  if (KERN_SUCCESS != err) {
+    LOG_ERROR("SendMessage failed 0x%x %s\n", err, mach_error_string(err));
+  }
+}
+
+void
+TextureSync::RegisterTextureSourceProvider(TextureSourceProvider* textureSourceProvider)
+{
+  RefPtr<TextureSourceProvider> ref(textureSourceProvider);
+  MOZ_RELEASE_ASSERT(!gTextureSourceProviders.Contains(ref));
+
+  gTextureSourceProviders.AppendElement(ref.forget());
+}
+
+void
+TextureSync::SetTexturesLocked(pid_t pid, const nsTArray<uint64_t>& textureIds)
+{
+  StaticMonitorAutoLock mal(gTextureLockMonitor);
+  std::unordered_set<uint64_t>* lockedTextureIds = GetLockedTextureIdsForProcess(pid);
+  for (uint64_t textureId : textureIds) {
+    lockedTextureIds->insert(textureId);
+  }
+}
+
+void
+TextureSync::SetTexturesUnlocked(pid_t pid, const nsTArray<uint64_t>& textureIds)
+{
+  bool oneErased = false;
+  {
+    StaticMonitorAutoLock mal(gTextureLockMonitor);
+    std::unordered_set<uint64_t>* lockedTextureIds = GetLockedTextureIdsForProcess(pid);
+    for (uint64_t textureId : textureIds) {
+      if (lockedTextureIds->erase(textureId)) {
+        oneErased = true;
+      }
+    }
+  }
+  if (oneErased) {
+    gTextureLockMonitor.NotifyAll();
+  }
+}
+
+void
+TextureSync::Shutdown()
+{
+  gTextureSourceProviders.Clear();
+
+  {
+    StaticMonitorAutoLock lock(gTextureLockMonitor);
+
+    for (auto& lockedTextureIds : gProcessTextureIds) {
+      lockedTextureIds.second.clear(); 
+    } 
+  }
+
+  gTextureLockMonitor.NotifyAll();
+
+  {
+    StaticMonitorAutoLock lock(gTextureLockMonitor);
+    gProcessTextureIds.clear();
+  }
+}
+
+void
+TextureSync::UpdateTextureLocks(base::ProcessId aProcessId)
+{
+  if (aProcessId == getpid()) {
+    DispatchCheckTexturesForUnlock();
+    return;
+  }
+
+  MachSendMessage smsg(ipc::kUpdateTextureLocksMsg);
+  smsg.SetData(&aProcessId, sizeof(aProcessId));
+  ipc::SharedMemoryBasic::SendMachMessage(aProcessId, smsg, NULL);
+}
+
+bool
+TextureSync::WaitForTextures(base::ProcessId aProcessId, const nsTArray<uint64_t>& textureIds)
+{
+  if (aProcessId == getpid()) {
+    UniquePtr<uint64_t[]> reqTextureIds = MakeUnique<uint64_t[]>(textureIds.Length());
+    for (uint32_t i = 0; i < textureIds.Length(); ++i) {
+      reqTextureIds[i] = textureIds[i];
+    }
+    bool success = WaitForTextureIdsToUnlock(aProcessId, reqTextureIds.get(), textureIds.Length());
+    if (!success) {
+      LOG_ERROR("Failed waiting for textures to unlock.\n");
+    }
+
+    return success;
+  }
+
+  MachSendMessage smsg(ipc::kWaitForTexturesMsg);
+  size_t messageSize = sizeof(WaitForTexturesRequest) + textureIds.Length() * sizeof(uint64_t);
+  UniquePtr<uint8_t[]> messageData = MakeUnique<uint8_t[]>(messageSize);
+  WaitForTexturesRequest* req = (WaitForTexturesRequest*)messageData.get();
+  uint64_t* reqTextureIds = (uint64_t*)(req + 1);
+
+  for (uint32_t i = 0; i < textureIds.Length(); ++i) {
+    reqTextureIds[i] = textureIds[i];
+  }
+
+  req->pid = getpid();
+  bool dataWasSet = smsg.SetData(req, messageSize);
+
+  if (!dataWasSet) {
+    LOG_ERROR("Data was too large: %zu\n", messageSize);
+    return false;
+  }
+
+  MachReceiveMessage msg;
+  bool success = ipc::SharedMemoryBasic::SendMachMessage(aProcessId, smsg, &msg);
+  if (!success) {
+    return false;
+  }
+
+  if (msg.GetDataLength() != sizeof(WaitForTexturesReply)) {
+    LOG_ERROR("Improperly formatted reply\n");
+    return false;
+  }
+
+  WaitForTexturesReply* msg_data = reinterpret_cast<WaitForTexturesReply*>(msg.GetData());
+  if (!msg_data->success) {
+    LOG_ERROR("Failed waiting for textures to unlock.\n");
+    return false;
+  }
+
+  return true;
+}
+
+void
+TextureSync::CleanupForPid(base::ProcessId aProcessId)
+{
+  {
+    StaticMonitorAutoLock lock(gTextureLockMonitor);
+    std::unordered_set<uint64_t>* lockedTextureIds = GetLockedTextureIdsForProcess(aProcessId);
+    lockedTextureIds->clear();
+  }
+  gTextureLockMonitor.NotifyAll();
+}
+
+} // namespace layers
+
+} // namespace mozilla
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/gfx/layers/TextureSync.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MOZILLA_LAYERS_TEXTURESYNC_H
+#define MOZILLA_LAYERS_TEXTURESYNC_H
+
+#include "base/process.h"
+
+#include "nsTArray.h"
+#include "chrome/common/mach_ipc_mac.h"
+
+#include "SharedMemory.h"
+
+namespace mozilla {
+namespace ipc {
+  struct MemoryPorts;
+} // namespace ipc
+
+namespace layers {
+
+class TextureSync
+{
+public:
+  static void RegisterTextureSourceProvider(layers::TextureSourceProvider* aTextureSourceProvider);
+  static void DispatchCheckTexturesForUnlock();
+  static void HandleWaitForTexturesMessage(MachReceiveMessage* rmsg, ipc::MemoryPorts* ports);
+  static void UpdateTextureLocks(base::ProcessId aProcessId);
+  static bool WaitForTextures(base::ProcessId aProcessId, const nsTArray<uint64_t>& aTextureIds);
+  static void SetTexturesLocked(base::ProcessId aProcessId, const nsTArray<uint64_t>& aTextureIds);
+  static void SetTexturesUnlocked(base::ProcessId aProcessId, const nsTArray<uint64_t>& aTextureIds);
+  static void Shutdown();
+  static void CleanupForPid(base::ProcessId aProcessId);
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
\ No newline at end of file
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -312,16 +312,20 @@ ClientLayerManager::BeginTransaction()
   return BeginTransactionWithTarget(nullptr);
 }
 
 bool
 ClientLayerManager::EndTransactionInternal(DrawPaintedLayerCallback aCallback,
                                            void* aCallbackData,
                                            EndTransactionFlags)
 {
+  if (mForwarder) {
+    mForwarder->UpdateTextureLocks();
+  }
+
   // Wait for any previous async paints to complete before starting to paint again.
   // Do this outside the profiler and telemetry block so this doesn't count as time
   // spent rasterizing.
   {
     PaintTelemetry::AutoRecord record(PaintTelemetry::Metric::FlushRasterization);
     FlushAsyncPaints();
   }
 
--- a/gfx/layers/client/MultiTiledContentClient.cpp
+++ b/gfx/layers/client/MultiTiledContentClient.cpp
@@ -255,16 +255,48 @@ void ClientMultiTiledLayerBuffer::Update
       oldRetainedTiles[oldIndex].DiscardBuffers();
     }
   }
 
   oldRetainedTiles.Clear();
 
   nsIntRegion paintRegion = aPaintRegion;
   nsIntRegion dirtyRegion = aDirtyRegion;
+
+  AutoTArray<uint64_t, 10> syncTextureSerials;
+  SurfaceMode mode;
+  Unused << GetContentType(&mode);
+
+  if (mManager->AsShadowForwarder()->SupportsTextureDirectMapping()) {
+    // Pre-pass through the tiles (mirroring the filter logic below) to gather
+    // texture IDs that we need to ensure are unused by the GPU before we
+    // continue.
+    if (!paintRegion.IsEmpty()) {
+      MOZ_ASSERT(mPaintStates.size() == 0);
+      for (size_t i = 0; i < newTileCount; ++i) {
+        const TileCoordIntPoint tileCoord = newTiles.TileCoord(i);
+
+        IntPoint tileOffset = GetTileOffset(tileCoord);
+        nsIntRegion tileDrawRegion = IntRect(tileOffset, scaledTileSize);
+        tileDrawRegion.AndWith(paintRegion);
+
+        if (tileDrawRegion.IsEmpty()) {
+          continue;
+        }
+
+        TileClient& tile = mRetainedTiles[i];
+        tile.GetSyncTextureSerials(mode, syncTextureSerials);
+      }
+    }
+
+    if (syncTextureSerials.Length() > 0) {
+      mManager->AsShadowForwarder()->SyncTextures(syncTextureSerials);
+    }
+  }
+
   if (!paintRegion.IsEmpty()) {
     MOZ_ASSERT(mPaintStates.size() == 0);
     for (size_t i = 0; i < newTileCount; ++i) {
       const TileCoordIntPoint tileCoord = newTiles.TileCoord(i);
 
       IntPoint tileOffset = GetTileOffset(tileCoord);
       nsIntRegion tileDrawRegion = IntRect(tileOffset, scaledTileSize);
       tileDrawRegion.AndWith(paintRegion);
--- a/gfx/layers/client/SingleTiledContentClient.cpp
+++ b/gfx/layers/client/SingleTiledContentClient.cpp
@@ -135,16 +135,25 @@ ClientSingleTiledLayerBuffer::PaintThebe
   SurfaceMode mode;
   gfxContentType content = GetContentType(&mode);
   mFormat = gfxPlatform::GetPlatform()->OptimalFormatForContent(content);
 
   if (mTile.IsPlaceholderTile()) {
     mTile.SetTextureAllocator(this);
   }
 
+
+  if (mManager->AsShadowForwarder()->SupportsTextureDirectMapping()) {
+    AutoTArray<uint64_t, 2> syncTextureSerials;
+    mTile.GetSyncTextureSerials(mode, syncTextureSerials);
+    if (syncTextureSerials.Length() > 0) {
+      mManager->AsShadowForwarder()->SyncTextures(syncTextureSerials);
+    }
+  }
+
   // The dirty region relative to the top-left of the tile.
   nsIntRegion tileVisibleRegion = aNewValidRegion.MovedBy(-mTilingOrigin);
   nsIntRegion tileDirtyRegion = paintRegion.MovedBy(-mTilingOrigin);
 
   std::vector<RefPtr<TextureClient>> paintClients;
   std::vector<CapturedTiledPaintState::Copy> paintCopies;
   std::vector<CapturedTiledPaintState::Clear> paintClears;
 
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -659,16 +659,42 @@ CreateBackBufferTexture(TextureClient* a
   if (!aCompositable.AddTextureClient(texture)) {
     gfxCriticalError() << "[Tiling:Client] Failed to connect a TextureClient";
     return nullptr;
   }
 
   return texture.forget();
 }
 
+void
+TileClient::GetSyncTextureSerials(SurfaceMode aMode, nsTArray<uint64_t>& aSerials)
+{
+  if (mFrontBuffer &&
+      mFrontBuffer->HasIntermediateBuffer() &&
+      !mFrontBuffer->IsReadLocked() &&
+      (aMode != SurfaceMode::SURFACE_COMPONENT_ALPHA || (
+        mFrontBufferOnWhite && !mFrontBufferOnWhite->IsReadLocked())))
+  {
+    return;
+  }
+
+  if (mBackBuffer &&
+      !mBackBuffer->HasIntermediateBuffer() &&
+      mBackBuffer->IsReadLocked()) {
+    aSerials.AppendElement(mBackBuffer->GetSerial());
+  }
+
+  if (aMode == SurfaceMode::SURFACE_COMPONENT_ALPHA &&
+      mBackBufferOnWhite &&
+      !mBackBufferOnWhite->HasIntermediateBuffer() &&
+      mBackBufferOnWhite->IsReadLocked()) {
+    aSerials.AppendElement(mBackBufferOnWhite->GetSerial());
+  }
+}
+
 TextureClient*
 TileClient::GetBackBuffer(CompositableClient& aCompositable,
                           const nsIntRegion& aDirtyRegion,
                           const nsIntRegion& aVisibleRegion,
                           gfxContentType aContent,
                           SurfaceMode aMode,
                           nsIntRegion& aAddPaintedRegion,
                           TilePaintFlags aFlags,
@@ -717,27 +743,28 @@ TileClient::GetBackBuffer(CompositableCl
       if (!mBackBuffer) {
         DiscardBackBuffer();
         DiscardFrontBuffer();
         return nullptr;
       }
       mInvalidBack = IntRect(IntPoint(), mBackBuffer->GetSize());
     }
 
-    if (aMode == SurfaceMode::SURFACE_COMPONENT_ALPHA
-        && (!mBackBufferOnWhite || mBackBufferOnWhite->IsReadLocked())) {
-      mBackBufferOnWhite = CreateBackBufferTexture(
-        mBackBufferOnWhite, aCompositable, mAllocator
-      );
-      if (!mBackBufferOnWhite) {
-        DiscardBackBuffer();
-        DiscardFrontBuffer();
-        return nullptr;
+    if (aMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
+      if (!mBackBufferOnWhite || mBackBufferOnWhite->IsReadLocked()) {
+        mBackBufferOnWhite = CreateBackBufferTexture(
+          mBackBufferOnWhite, aCompositable, mAllocator
+        );
+        if (!mBackBufferOnWhite) {
+          DiscardBackBuffer();
+          DiscardFrontBuffer();
+          return nullptr;
+        }
+        mInvalidBack = IntRect(IntPoint(), mBackBufferOnWhite->GetSize());
       }
-      mInvalidBack = IntRect(IntPoint(), mBackBufferOnWhite->GetSize());
     }
 
     ValidateBackBufferFromFront(aDirtyRegion, aVisibleRegion, aAddPaintedRegion, aFlags, aCopies, aClients);
   }
 
   OpenMode lockMode = aFlags & TilePaintFlags::Async ? OpenMode::OPEN_READ_WRITE_ASYNC
                                                      : OpenMode::OPEN_READ_WRITE;
 
--- a/gfx/layers/client/TiledContentClient.h
+++ b/gfx/layers/client/TiledContentClient.h
@@ -109,16 +109,18 @@ struct TileClient
   */
   void Flip();
 
   void DumpTexture(std::stringstream& aStream, TextureDumpMode aCompress) {
     // TODO We should combine the OnWhite/OnBlack here an just output a single image.
     CompositableClient::DumpTextureClient(aStream, mFrontBuffer, aCompress);
   }
 
+  void GetSyncTextureSerials(SurfaceMode aMode, nsTArray<uint64_t>& aSerials);
+
   /**
   * Returns an unlocked TextureClient that can be used for writing new
   * data to the tile. This may flip the front-buffer to the back-buffer if
   * the front-buffer is still locked by the host, or does not have an
   * internal buffer (and so will always be locked).
   *
   * If getting the back buffer required copying pixels from the front buffer
   * then the copied region is stored in aAddPaintedRegion so the host side
@@ -320,16 +322,19 @@ public:
   {}
 
   virtual void PaintThebes(const nsIntRegion& aNewValidRegion,
                    const nsIntRegion& aPaintRegion,
                    const nsIntRegion& aDirtyRegion,
                    LayerManager::DrawPaintedLayerCallback aCallback,
                    void* aCallbackData,
                    TilePaintFlags aFlags) = 0;
+  virtual void GetSyncTextureSerials(const nsIntRegion& aPaintRegion,
+                                     const nsIntRegion& aDirtyRegion,
+                                     nsTArray<uint64_t>& aSerials) { return; }
 
   virtual bool SupportsProgressiveUpdate() = 0;
   virtual bool ProgressiveUpdate(const nsIntRegion& aValidRegion,
                          const nsIntRegion& aInvalidRegion,
                          const nsIntRegion& aOldValidRegion,
                          nsIntRegion& aOutDrawnRegion,
                          BasicTiledLayerPaintData* aPaintData,
                          LayerManager::DrawPaintedLayerCallback aCallback,
--- a/gfx/layers/composite/TextureHost.cpp
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -365,21 +365,24 @@ TextureHost::TextureHost(TextureFlags aF
     , mCompositableCount(0)
     , mFwdTransactionId(0)
     , mReadLocked(false)
 {
 }
 
 TextureHost::~TextureHost()
 {
-  // If we still have a ReadLock, unlock it. At this point we don't care about
-  // the texture client being written into on the other side since it should be
-  // destroyed by now. But we will hit assertions if we don't ReadUnlock before
-  // destroying the lock itself.
-  ReadUnlock();
+  if (mReadLocked) {
+    // If we still have a ReadLock, unlock it. At this point we don't care about
+    // the texture client being written into on the other side since it should be
+    // destroyed by now. But we will hit assertions if we don't ReadUnlock before
+    // destroying the lock itself.
+    ReadUnlock();
+    MaybeNotifyUnlocked();
+  }
 }
 
 void TextureHost::Finalize()
 {
   if (!(GetFlags() & TextureFlags::DEALLOCATE_CLIENT)) {
     DeallocateSharedData();
     DeallocateDeviceData();
   }
@@ -395,16 +398,17 @@ TextureHost::UnbindTextureSource()
     // composition before calling ReadUnlock. We ask the compositor to take care
     // of that for us.
     if (mProvider) {
       mProvider->UnlockAfterComposition(this);
     } else {
       // GetCompositor returned null which means no compositor can be using this
       // texture. We can ReadUnlock right away.
       ReadUnlock();
+      MaybeNotifyUnlocked();
     }
   }
 }
 
 void
 TextureHost::RecycleTexture(TextureFlags aFlags)
 {
   MOZ_ASSERT(GetFlags() & TextureFlags::RECYCLE);
@@ -696,16 +700,19 @@ TextureHost::SetReadLocked()
 {
   if (!mReadLock) {
     return;
   }
   // If mReadLocked is true it means we haven't read unlocked yet and the content
   // side should not have been able to write into this texture and read lock again!
   MOZ_ASSERT(!mReadLocked);
   mReadLocked = true;
+  if (mProvider) {
+    mProvider->MaybeUnlockBeforeNextComposition(this);
+  }
 }
 
 void
 TextureHost::ReadUnlock()
 {
   if (mReadLock && mReadLocked) {
     mReadLock->ReadUnlock();
     mReadLocked = false;
@@ -885,37 +892,51 @@ BufferTextureHost::AcquireTextureSource(
   aTexture = mFirstSource;
   return !!mFirstSource;
 }
 
 void
 BufferTextureHost::ReadUnlock()
 {
   if (mFirstSource) {
-    mFirstSource->Sync();
+    mFirstSource->Sync(true);
   }
 
   TextureHost::ReadUnlock();
 }
 
 void
+BufferTextureHost::MaybeNotifyUnlocked()
+{
+#ifdef XP_DARWIN
+  auto actor = GetIPDLActor();
+  if (actor) {
+    AutoTArray<uint64_t, 1> serials;
+    serials.AppendElement(TextureHost::GetTextureSerial(actor));
+    ipc::SharedMemoryBasic::SetTexturesUnlocked(actor->OtherPid(), serials); 
+  }
+#endif
+}
+
+void
 BufferTextureHost::UnbindTextureSource()
 {
   if (mFirstSource && mFirstSource->IsOwnedBy(this)) {
     mFirstSource->Unbind();
   }
 
   // This texture is not used by any layer anymore.
   // If the texture doesn't have an intermediate buffer, it means we are
   // compositing synchronously on the CPU, so we don't need to wait until
   // the end of the next composition to ReadUnlock (which other textures do
   // by default).
   // If the texture has an intermediate buffer we don't care either because
   // texture uploads are also performed synchronously for BufferTextureHost.
   ReadUnlock();
+  MaybeNotifyUnlocked();
 }
 
 gfx::SurfaceFormat
 BufferTextureHost::GetFormat() const
 {
   // mFormat is the format of the data that we share with the content process.
   // GetFormat, on the other hand, expects the format that we present to the
   // Compositor (it is used to choose the effect type).
@@ -972,16 +993,17 @@ BufferTextureHost::MaybeUpload(nsIntRegi
   if (!Upload(aRegion)) {
     return false;
   }
 
   if (mHasIntermediateBuffer) {
     // We just did the texture upload, the content side can now freely write
     // into the shared buffer.
     ReadUnlock();
+    MaybeNotifyUnlocked();
   }
 
   // We no longer have an invalid region.
   mNeedsFullUpdate = false;
   mMaybeUpdatedRegion.SetEmpty();
 
   // If upload returns true we know mFirstSource is not null
   mFirstSource->SetUpdateSerial(mUpdateSerial);
@@ -1285,19 +1307,22 @@ TextureParent::Init(const SurfaceDescrip
 
 void
 TextureParent::Destroy()
 {
   if (!mTextureHost) {
     return;
   }
 
-  // ReadUnlock here to make sure the ReadLock's shmem does not outlive the
-  // protocol that created it.
-  mTextureHost->ReadUnlock();
+  if (mTextureHost->mReadLocked) {
+    // ReadUnlock here to make sure the ReadLock's shmem does not outlive the
+    // protocol that created it.
+    mTextureHost->ReadUnlock();
+    mTextureHost->MaybeNotifyUnlocked();
+  }
 
   if (mTextureHost->GetFlags() & TextureFlags::DEALLOCATE_CLIENT) {
     mTextureHost->ForgetSharedData();
   }
 
   mTextureHost->mActor = nullptr;
   mTextureHost = nullptr;
 }
--- a/gfx/layers/composite/TextureHost.h
+++ b/gfx/layers/composite/TextureHost.h
@@ -184,17 +184,17 @@ public:
   int NumCompositableRefs() const { return mCompositableCount; }
 
   // Some texture sources could wrap the cpu buffer to gpu directly. Then,
   // we could get better performance of texture uploading.
   virtual bool IsDirectMap() { return false; }
   // The direct-map cpu buffer should be alive when gpu uses it. And it
   // should not be updated while gpu reads it. This Sync() function
   // implements this synchronized behavior.
-  virtual void Sync() { }
+  virtual bool Sync(bool aBlocking) { return true; }
 
 protected:
 
   RefPtr<TextureSource> mNextSibling;
   int mCompositableCount;
 };
 
 /// Equivalent of a RefPtr<TextureSource>, that calls AddCompositableRef and
@@ -665,21 +665,25 @@ public:
     MOZ_ASSERT_UNREACHABLE("No PushDisplayItems() implementation for this TextureHost type.");
   }
 
   /**
    * Some API's can use the cross-process IOSurface directly, such as OpenVR
    */
   virtual MacIOSurface* GetMacIOSurface() { return nullptr; }
 
+  virtual bool IsDirectMap() { return false; }
+
 protected:
   virtual void ReadUnlock();
 
   void RecycleTexture(TextureFlags aFlags);
 
+  virtual void MaybeNotifyUnlocked() {}
+
   virtual void UpdatedInternal(const nsIntRegion *Region) {}
 
   /**
    * Called when mCompositableCount becomes 0.
    */
   virtual void NotifyNotUsed();
 
   // for Compositor.
@@ -772,24 +776,29 @@ public:
 
   virtual void PushDisplayItems(wr::DisplayListBuilder& aBuilder,
                                 const wr::LayoutRect& aBounds,
                                 const wr::LayoutRect& aClip,
                                 wr::ImageRendering aFilter,
                                 const Range<wr::ImageKey>& aImageKeys) override;
 
   virtual void ReadUnlock() override;
+  virtual bool IsDirectMap() override { return mFirstSource && mFirstSource->IsDirectMap(); };
+
+  bool CanUnlock() { return !mFirstSource || mFirstSource->Sync(false); }
 
 protected:
   bool Upload(nsIntRegion *aRegion = nullptr);
   bool UploadIfNeeded();
   bool MaybeUpload(nsIntRegion *aRegion);
   bool EnsureWrappingTextureSource();
 
   virtual void UpdatedInternal(const nsIntRegion* aRegion = nullptr) override;
+  virtual void MaybeNotifyUnlocked() override;
+
 
   BufferDescriptor mDescriptor;
   RefPtr<Compositor> mCompositor;
   RefPtr<DataTextureSource> mFirstSource;
   nsIntRegion mMaybeUpdatedRegion;
   gfx::IntSize mSize;
   gfx::SurfaceFormat mFormat;
   uint32_t mUpdateSerial;
--- a/gfx/layers/composite/TiledContentHost.cpp
+++ b/gfx/layers/composite/TiledContentHost.cpp
@@ -293,16 +293,19 @@ TiledLayerBufferComposite::UseTiles(cons
   TilesPlacement newTiles(aTiles.firstTileX(), aTiles.firstTileY(),
                           aTiles.retainedWidth(), aTiles.retainedHeight());
 
   const InfallibleTArray<TileDescriptor>& tileDescriptors = aTiles.tiles();
 
   TextureSourceRecycler oldRetainedTiles(std::move(mRetainedTiles));
   mRetainedTiles.SetLength(tileDescriptors.Length());
 
+  AutoTArray<uint64_t, 10> lockedTextureSerials;
+  base::ProcessId lockedTexturePid = 0;
+
   // Step 1, deserialize the incoming set of tiles into mRetainedTiles, and attempt
   // to recycle the TextureSource for any repeated tiles.
   //
   // Since we don't have any retained 'tile' object, we have to search for instances
   // of the same TextureHost in the old tile set. The cost of binding a TextureHost
   // to a TextureSource for gralloc (binding EGLImage to GL texture) can be really
   // high, so we avoid this whenever possible.
   for (size_t i = 0; i < tileDescriptors.Length(); i++) {
@@ -317,24 +320,37 @@ TiledLayerBufferComposite::UseTiles(cons
       continue;
     }
 
     const TexturedTileDescriptor& texturedDesc = tileDesc.get_TexturedTileDescriptor();
 
     tile.mTextureHost = TextureHost::AsTextureHost(texturedDesc.textureParent());
     if (texturedDesc.readLocked()) {
       tile.mTextureHost->SetReadLocked();
+      auto actor = tile.mTextureHost->GetIPDLActor();
+      if (actor && tile.mTextureHost->IsDirectMap()) {
+        lockedTextureSerials.AppendElement(TextureHost::GetTextureSerial(actor));
+
+        if (lockedTexturePid) {
+          MOZ_ASSERT(lockedTexturePid == actor->OtherPid());
+        }
+        lockedTexturePid = actor->OtherPid();
+      }
     }
 
     if (texturedDesc.textureOnWhite().type() == MaybeTexture::TPTextureParent) {
       tile.mTextureHostOnWhite = TextureHost::AsTextureHost(
         texturedDesc.textureOnWhite().get_PTextureParent()
       );
       if (texturedDesc.readLockedOnWhite()) {
         tile.mTextureHostOnWhite->SetReadLocked();
+        auto actor = tile.mTextureHostOnWhite->GetIPDLActor();
+        if (actor && tile.mTextureHostOnWhite->IsDirectMap()) {
+          lockedTextureSerials.AppendElement(TextureHost::GetTextureSerial(actor));
+        }
       }
     }
 
     tile.mTileCoord = newTiles.TileCoord(i);
 
     // If this same tile texture existed in the old tile set then this will move the texture
     // source into our new tile.
     oldRetainedTiles.RecycleTextureSourceForTile(tile);
@@ -349,16 +365,22 @@ TiledLayerBufferComposite::UseTiles(cons
       // We need to begin fading it in (if enabled via layers.tiles.fade-in.enabled)
       tile.mFadeStart = TimeStamp::Now();
 
       aLayerManager->CompositeUntil(
         tile.mFadeStart + TimeDuration::FromMilliseconds(gfxPrefs::LayerTileFadeInDuration()));
     }
   }
 
+  #ifdef XP_DARWIN
+  if (lockedTextureSerials.Length() > 0) {
+    ipc::SharedMemoryBasic::SetTexturesLocked(lockedTexturePid, lockedTextureSerials);
+  }
+  #endif
+
   // Step 2, attempt to recycle unused texture sources from the old tile set into new tiles.
   //
   // For gralloc, binding a new TextureHost to the existing TextureSource is the fastest way
   // to ensure that any implicit locking on the old gralloc image is released.
   for (TileHost& tile : mRetainedTiles) {
     if (!tile.mTextureHost || tile.mTextureSource) {
       continue;
     }
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -801,16 +801,48 @@ ShadowLayerForwarder::SetLayerObserverEp
 {
   if (!IPCOpen()) {
     return;
   }
   Unused << mShadowManager->SendSetLayerObserverEpoch(aLayerObserverEpoch);
 }
 
 void
+ShadowLayerForwarder::UpdateTextureLocks()
+{
+#ifdef XP_DARWIN
+  if (!IPCOpen()) {
+    return;
+  }
+
+  auto compositorBridge = GetCompositorBridgeChild();
+  if (compositorBridge) {
+    auto pid = compositorBridge->OtherPid();
+    ipc::SharedMemoryBasic::UpdateTextureLocks(pid);
+  }
+#endif
+}
+
+void
+ShadowLayerForwarder::SyncTextures(const nsTArray<uint64_t>& aSerials)
+{
+#ifdef XP_DARWIN
+  if (!IPCOpen()) {
+    return;
+  }
+
+  auto compositorBridge = GetCompositorBridgeChild();
+  if (compositorBridge) {
+    auto pid = compositorBridge->OtherPid();
+    ipc::SharedMemoryBasic::WaitForTextures(pid, aSerials);
+  }
+#endif
+}
+
+void
 ShadowLayerForwarder::ReleaseLayer(const LayerHandle& aHandle)
 {
   if (!IPCOpen()) {
     return;
   }
   Unused << mShadowManager->SendReleaseLayer(aHandle);
 }
 
--- a/gfx/layers/ipc/ShadowLayers.h
+++ b/gfx/layers/ipc/ShadowLayers.h
@@ -116,16 +116,17 @@ class Transaction;
  * compositables and textures can be manipulated, and does not always originate
  * from the content thread. (See CompositableForwarder.h and ImageBridgeChild.h)
  */
 
 class ShadowLayerForwarder final : public LayersIPCActor
                                  , public CompositableForwarder
                                  , public LegacySurfaceDescriptorAllocator
 {
+  typedef mozilla::ipc::SharedMemoryBasic SharedMemoryBasic;
   friend class ClientLayerManager;
 
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ShadowLayerForwarder, override);
 
   /**
    * Setup the IPDL actor for aCompositable to be part of layers
    * transactions.
@@ -356,16 +357,19 @@ public:
                                               uint32_t aCaps,
                                               SurfaceDescriptor* aBuffer) override;
 
   virtual void DestroySurfaceDescriptor(SurfaceDescriptor* aSurface) override;
 
   virtual void UpdateFwdTransactionId() override;
   virtual uint64_t GetFwdTransactionId() override;
 
+  void UpdateTextureLocks();
+  void SyncTextures(const nsTArray<uint64_t>& aSerials);
+
   void ReleaseLayer(const LayerHandle& aHandle);
 
   bool InForwarderThread() override {
     return NS_IsMainThread();
   }
 
   PaintTiming& GetPaintTiming() {
     return mPaintTiming;
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -26,16 +26,17 @@
 #include "mozilla/gfx/Triangle.h"       // for Triangle
 #include "mozilla/gfx/gfxVars.h"        // for gfxVars
 #include "mozilla/layers/ImageDataSerializer.h"
 #include "mozilla/layers/LayerManagerComposite.h"  // for LayerComposite, etc
 #include "mozilla/layers/CompositingRenderTargetOGL.h"
 #include "mozilla/layers/Effects.h"     // for EffectChain, TexturedEffect, etc
 #include "mozilla/layers/TextureHost.h"  // for TextureSource, etc
 #include "mozilla/layers/TextureHostOGL.h"  // for TextureSourceOGL, etc
+#include "mozilla/layers/PTextureParent.h" // for OtherPid() on PTextureParent
 #include "mozilla/mozalloc.h"           // for operator delete, etc
 #include "nsAppRunner.h"
 #include "nsAString.h"
 #include "nsIConsoleService.h"          // for nsIConsoleService, etc
 #include "nsIWidget.h"                  // for nsIWidget
 #include "nsLiteralString.h"            // for NS_LITERAL_STRING
 #include "nsMathUtils.h"                // for NS_roundf
 #include "nsRect.h"                     // for mozilla::gfx::IntRect
@@ -178,16 +179,17 @@ CompositorOGL::CompositorOGL(CompositorB
   , mSurfaceSize(aSurfaceWidth, aSurfaceHeight)
   , mHasBGRA(0)
   , mUseExternalSurfaceSize(aUseExternalSurfaceSize)
   , mFrameInProgress(false)
   , mDestroyed(false)
   , mViewportSize(0, 0)
   , mCurrentProgram(nullptr)
 {
+  ipc::SharedMemoryBasic::RegisterTextureSourceProvider(this);
   MOZ_COUNT_CTOR(CompositorOGL);
 }
 
 CompositorOGL::~CompositorOGL()
 {
   MOZ_COUNT_DTOR(CompositorOGL);
   Destroy();
 }
@@ -1916,16 +1918,48 @@ CompositorOGL::CreateDataTextureSourceAr
   RefPtr<DirectMapTextureSource> srcV = new DirectMapTextureSource(this, tempCr);
 
   srcY->SetNextSibling(srcU);
   srcU->SetNextSibling(srcV);
 
   return srcY.forget();
 }
 
+#ifdef XP_DARWIN
+void
+CompositorOGL::MaybeUnlockBeforeNextComposition(TextureHost* aTextureHost)
+{
+  auto bufferTexture = aTextureHost->AsBufferTextureHost();
+  if (bufferTexture) {
+    mMaybeUnlockBeforeNextComposition.AppendElement(bufferTexture);
+  }
+}
+
+void
+CompositorOGL::TryUnlockTextures()
+{
+  nsClassHashtable<nsUint32HashKey, nsTArray<uint64_t>> texturesIdsToUnlockByPid;
+  for (auto& texture : mMaybeUnlockBeforeNextComposition) {
+    if (texture->IsDirectMap() && texture->CanUnlock()) {
+      texture->ReadUnlock();
+      auto actor = texture->GetIPDLActor();
+      if (actor) {
+        base::ProcessId pid = actor->OtherPid();
+        nsTArray<uint64_t>* textureIds = texturesIdsToUnlockByPid.LookupOrAdd(pid);
+        textureIds->AppendElement(TextureHost::GetTextureSerial(actor));
+      }
+    }
+  }
+  mMaybeUnlockBeforeNextComposition.Clear();
+  for (auto it = texturesIdsToUnlockByPid.ConstIter(); !it.Done(); it.Next()) {
+    ipc::SharedMemoryBasic::SetTexturesUnlocked(it.Key(), *it.UserData());
+  }
+}
+#endif
+
 already_AddRefed<DataTextureSource>
 CompositorOGL::CreateDataTextureSourceAround(gfx::DataSourceSurface* aSurface)
 {
   return MakeAndAddRef<DirectMapTextureSource>(this, aSurface);
 }
 
 bool
 CompositorOGL::SupportsPartialTextureUpdate()
--- a/gfx/layers/opengl/CompositorOGL.h
+++ b/gfx/layers/opengl/CompositorOGL.h
@@ -39,16 +39,17 @@ namespace mozilla {
 
 namespace layers {
 
 class CompositingRenderTarget;
 class CompositingRenderTargetOGL;
 class DataTextureSource;
 class GLManagerCompositor;
 class TextureSource;
+class BufferTextureHost;
 struct Effect;
 struct EffectChain;
 class GLBlitTextureImageHelper;
 
 /**
  * Interface for pools of temporary gl textures for the compositor.
  * The textures are fully owned by the pool, so the latter is responsible
  * calling fDeleteTextures accordingly.
@@ -230,16 +231,21 @@ public:
   }
 
   virtual void Pause() override;
   virtual bool Resume() override;
 
   GLContext* gl() const { return mGLContext; }
   GLContext* GetGLContext() const override { return mGLContext; }
 
+#ifdef XP_DARWIN
+  virtual void MaybeUnlockBeforeNextComposition(TextureHost* aTextureHost) override;
+  virtual void TryUnlockTextures() override;
+#endif
+
   /**
    * Clear the program state. This must be called
    * before operating on the GLContext directly. */
   void ResetProgram();
 
   gfx::SurfaceFormat GetFBOFormat() const {
     return gfx::SurfaceFormat::R8G8B8A8;
   }
@@ -283,16 +289,20 @@ private:
   bool SupportsTextureDirectMapping();
 
   /** Widget associated with this compositor */
   LayoutDeviceIntSize mWidgetSize;
   RefPtr<GLContext> mGLContext;
   UniquePtr<GLBlitTextureImageHelper> mBlitTextureImageHelper;
   gfx::Matrix4x4 mProjMatrix;
 
+#ifdef XP_DARWIN
+  nsTArray<RefPtr<BufferTextureHost>> mMaybeUnlockBeforeNextComposition;
+#endif
+
   /** The size of the surface we are rendering to */
   gfx::IntSize mSurfaceSize;
 
   ScreenPoint mRenderOffset;
 
   already_AddRefed<mozilla::gl::GLContext> CreateContext();
 
   /** Texture target to use for FBOs */
--- a/gfx/layers/opengl/TextureHostOGL.cpp
+++ b/gfx/layers/opengl/TextureHostOGL.cpp
@@ -332,23 +332,28 @@ DirectMapTextureSource::Update(gfx::Data
 {
   if (!aSurface) {
     return false;
   }
 
   return UpdateInternal(aSurface, aDestRegion, aSrcOffset, false);
 }
 
-void
-DirectMapTextureSource::Sync()
+bool
+DirectMapTextureSource::Sync(bool aBlocking)
 {
   gl()->MakeCurrent();
   if (!gl()->IsDestroyed()) {
-    gl()->fFinishObjectAPPLE(LOCAL_GL_TEXTURE, mTextureHandle);
+    if (aBlocking) {
+      gl()->fFinishObjectAPPLE(LOCAL_GL_TEXTURE, mTextureHandle);
+    } else {
+      return gl()->fTestObjectAPPLE(LOCAL_GL_TEXTURE, mTextureHandle);
+    }
   }
+  return true;
 }
 
 bool
 DirectMapTextureSource::UpdateInternal(gfx::DataSourceSurface* aSurface,
                                        nsIntRegion* aDestRegion,
                                        gfx::IntPoint* aSrcOffset,
                                        bool aInit)
 {
--- a/gfx/layers/opengl/TextureHostOGL.h
+++ b/gfx/layers/opengl/TextureHostOGL.h
@@ -299,18 +299,20 @@ public:
                          gfx::DataSourceSurface* aSurface);
 
   virtual bool Update(gfx::DataSourceSurface* aSurface,
                       nsIntRegion* aDestRegion = nullptr,
                       gfx::IntPoint* aSrcOffset = nullptr) override;
 
   virtual bool IsDirectMap() override { return true; }
 
-  // Wait until this texture source is not used by the compositor.
-  virtual void Sync() override;
+  // If aBlocking is false, check if this texture is no longer being used
+  // by the GPU - if aBlocking is true, this will block until the GPU is
+  // done with it.
+  virtual bool Sync(bool aBlocking) override;
 
 private:
   bool UpdateInternal(gfx::DataSourceSurface* aSurface,
                       nsIntRegion* aDestRegion,
                       gfx::IntPoint* aSrcOffset,
                       bool aInit);
 };
 
--- a/ipc/glue/SharedMemoryBasic_mach.h
+++ b/ipc/glue/SharedMemoryBasic_mach.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_ipc_SharedMemoryBasic_mach_h
 #define mozilla_ipc_SharedMemoryBasic_mach_h
 
 #include "base/file_descriptor_posix.h"
 #include "base/process.h"
+#include "nsTArray.h"
 
 #include "SharedMemory.h"
 #include <mach/port.h>
 
 #ifdef FUZZING
 #include "SharedMemoryFuzzer.h"
 #endif
 
@@ -21,32 +22,42 @@
 // This is a low-level wrapper around platform shared memory.  Don't
 // use it directly; use Shmem allocated through IPDL interfaces.
 //
 
 class MachPortSender;
 class ReceivePort;
 
 namespace mozilla {
+namespace layers {
+  class TextureSourceProvider;
+}
+
 namespace ipc {
 
 class SharedMemoryBasic final : public SharedMemoryCommon<mach_port_t>
 {
 public:
   static void SetupMachMemory(pid_t pid,
                               ReceivePort* listen_port,
                               MachPortSender* listen_port_ack,
                               MachPortSender* send_port,
                               ReceivePort* send_port_ack,
                               bool pidIsParent);
 
   static void CleanupForPid(pid_t pid);
 
   static void Shutdown();
 
+  static void RegisterTextureSourceProvider(layers::TextureSourceProvider* textureSourceProvider);
+  static void UpdateTextureLocks(base::ProcessId aProcessId);
+  static bool WaitForTextures(base::ProcessId aProcessId, const nsTArray<uint64_t>& aTextureIds);
+  static void SetTexturesLocked(pid_t pid, const nsTArray<uint64_t>& textureIds);
+  static void SetTexturesUnlocked(pid_t pid, const nsTArray<uint64_t>& textureIds);
+
   SharedMemoryBasic();
 
   virtual bool SetHandle(const Handle& aHandle, OpenRights aRights) override;
 
   virtual bool Create(size_t aNbytes) override;
 
   virtual bool Map(size_t nBytes) override;
 
--- a/ipc/glue/SharedMemoryBasic_mach.mm
+++ b/ipc/glue/SharedMemoryBasic_mach.mm
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: sw=2 ts=8 et :
  */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <map>
+#include <unordered_set>
 
 #include <mach/vm_map.h>
 #include <mach/mach_port.h>
 #if defined(XP_IOS)
 #include <mach/vm_map.h>
 #define mach_vm_address_t vm_address_t
 #define mach_vm_map vm_map
 #define mach_vm_read vm_read
@@ -19,19 +20,26 @@
 #else
 #include <mach/mach_vm.h>
 #endif
 #include <pthread.h>
 #include <unistd.h>
 #include "SharedMemoryBasic.h"
 #include "chrome/common/mach_ipc_mac.h"
 
+#include "mozilla/ClearOnShutdown.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/Printf.h"
+#include "mozilla/StaticMonitor.h"
 #include "mozilla/StaticMutex.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/TextureSourceProvider.h"
+#include "nsThreadUtils.h"
 
 #ifdef DEBUG
 #define LOG_ERROR(str, args...)                                   \
   PR_BEGIN_MACRO                                                  \
   mozilla::SmprintfPointer msg = mozilla::Smprintf(str, ## args); \
   NS_WARNING(msg.get());                                          \
   PR_END_MACRO
 #else
@@ -86,30 +94,40 @@ struct MemoryPorts {
   MachPortSender* mSender;
   ReceivePort* mReceiver;
 
   MemoryPorts() = default;
   MemoryPorts(MachPortSender* sender, ReceivePort* receiver)
    : mSender(sender), mReceiver(receiver) {}
 };
 
+static StaticRefPtr<layers::TextureSourceProvider> gTextureSourceProvider;
+
+static std::map<pid_t, std::unordered_set<uint64_t>> gProcessTextureIds;
+static StaticMonitor gTextureLockMonitor;
+
 // Protects gMemoryCommPorts and gThreads.
 static StaticMutex gMutex;
-
 static std::map<pid_t, MemoryPorts> gMemoryCommPorts;
 
 enum {
   kGetPortsMsg = 1,
   kSharePortsMsg,
+  kWaitForTexturesMsg,
+  kUpdateTextureLocksMsg,
   kReturnIdMsg,
+  kReturnWaitForTexturesMsg,
   kReturnPortsMsg,
   kShutdownMsg,
   kCleanupMsg,
 };
 
+const int kTextureLockTimeout = 32; // We really don't want to wait more than
+                                    // two frames for a texture to unlock. This
+                                    // in any case be very uncommon.
 const int kTimeout = 1000;
 const int kLongTimeout = 60 * kTimeout;
 
 pid_t gParentPid = 0;
 
 struct PIDPair {
   pid_t mRequester;
   pid_t mRequested;
@@ -127,18 +145,37 @@ struct ListeningThread {
    : mThread(thread), mPorts(ports) {}
 };
 
 struct SharePortsReply {
   uint64_t serial;
   mach_port_t port;
 };
 
+struct WaitForTexturesReply {
+  bool success;
+};
+
+struct WaitForTexturesRequest {
+  pid_t pid;
+};
+
 std::map<pid_t, ListeningThread> gThreads;
 
+std::unordered_set<uint64_t>* GetLockedTextureIdsForProcess(pid_t pid)
+{
+  gTextureLockMonitor.AssertCurrentThreadOwns();
+
+  if (gProcessTextureIds.find(pid) == gProcessTextureIds.end()) {
+    gProcessTextureIds[pid] = std::unordered_set<uint64_t>();
+  }
+
+  return &gProcessTextureIds.at(pid);
+}
+
 static void *
 PortServerThread(void *argument);
 
 
 static void
 SetupMachMemory(pid_t pid,
                 ReceivePort* listen_port,
                 MachPortSender* listen_port_ack,
@@ -149,16 +186,17 @@ SetupMachMemory(pid_t pid,
   if (pidIsParent) {
     gParentPid = pid;
   }
   auto* listen_ports = new MemoryPorts(listen_port_ack, listen_port);
   pthread_t thread;
   pthread_attr_t attr;
   pthread_attr_init(&attr);
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
   int err = pthread_create(&thread, &attr, PortServerThread, listen_ports);
   if (err) {
     LOG_ERROR("pthread_create failed with %x\n", err);
     return;
   }
 
   gMutex.AssertCurrentThreadOwns();
   gThreads[pid] = ListeningThread(thread, listen_ports);
@@ -235,17 +273,17 @@ GetMemoryPortsForPid(pid_t pid)
 {
   gMutex.AssertCurrentThreadOwns();
 
   if (gMemoryCommPorts.find(pid) == gMemoryCommPorts.end()) {
     // We don't have the ports open to communicate with that pid, so we're going to
     // ask our parent process over IPC to set them up for us.
     if (gParentPid == 0) {
       // If we're the top level parent process, we have no parent to ask.
-      LOG_ERROR("request for ports for pid %d, but we're the chrome process\n", pid);
+      // LOG_ERROR("request for ports for pid %d, but we're the chrome process\n", pid);
       return nullptr;
     }
     const MemoryPorts& parent = gMemoryCommPorts[gParentPid];
 
     // Create two receiving ports in this process to send to the parent. One will be used for
     // for listening for incoming memory to be shared, the other for getting the Handle of
     // memory we share to the other process.
     auto* ports_in_receiver = new ReceivePort();
@@ -270,16 +308,57 @@ GetMemoryPortsForPid(pid_t pid)
                     ports_out_sender,
                     ports_out_receiver,
                     false);
     MOZ_ASSERT(gMemoryCommPorts.find(pid) != gMemoryCommPorts.end());
   }
   return &gMemoryCommPorts.at(pid);
 }
 
+bool
+WaitForTextureIdsToUnlock(pid_t pid, uint64_t* textureIds, uint32_t textureIdsLength)
+{
+  {
+    StaticMonitorAutoLock lock(gTextureLockMonitor);
+    std::unordered_set<uint64_t>* freedTextureIds = GetLockedTextureIdsForProcess(pid);
+
+    while (true) {
+      bool allCleared = true;
+      for (uint32_t i = 0; i < textureIdsLength; ++i) {
+        if (freedTextureIds->find(textureIds[i]) != freedTextureIds->end()) {
+          allCleared = false;
+        }
+      }
+
+      if (allCleared) {
+        return true;
+      }
+
+      if (lock.Wait(TimeDuration::FromMilliseconds(kTextureLockTimeout)) == CVStatus::Timeout) {
+        return false;
+      }
+    }
+  }
+}
+
+void CheckTexturesForUnlock()
+{
+  if (gTextureSourceProvider) {
+    gTextureSourceProvider->TryUnlockTextures();
+  }
+}
+
+void DispatchCheckTexturesForUnlock()
+{
+  RefPtr<Runnable> task = NS_NewRunnableFunction(
+    "CheckTexturesForUnlock",
+    &CheckTexturesForUnlock);
+  layers::CompositorThreadHolder::Loop()->PostTask(task.forget());
+}
+
 // We just received a port representing a region of shared memory, reply to
 // the process that set it with the mach_port_t that represents it in this process.
 // That will be the Handle to be shared over normal IPC
 void
 HandleSharePortsMessage(MachReceiveMessage* rmsg, MemoryPorts* ports)
 {
   mach_port_t port = rmsg->GetTranslatedPort(0);
   uint64_t* serial = reinterpret_cast<uint64_t*>(rmsg->GetData());
@@ -290,16 +369,39 @@ HandleSharePortsMessage(MachReceiveMessa
   replydata.serial = *serial;
   msg.SetData(&replydata, sizeof(SharePortsReply));
   kern_return_t err = ports->mSender->SendMessage(msg, kTimeout);
   if (KERN_SUCCESS != err) {
     LOG_ERROR("SendMessage failed 0x%x %s\n", err, mach_error_string(err));
   }
 }
 
+void
+HandleWaitForTexturesMessage(MachReceiveMessage* rmsg, MemoryPorts* ports)
+{
+  WaitForTexturesRequest* req = reinterpret_cast<WaitForTexturesRequest*>(rmsg->GetData());
+  uint64_t* textureIds = (uint64_t*)(req + 1);
+  uint32_t textureIdsLength = (rmsg->GetDataLength() - sizeof(WaitForTexturesRequest)) / sizeof(uint64_t);
+
+  bool success = WaitForTextureIdsToUnlock(req->pid, textureIds, textureIdsLength);
+
+  if (!success) {
+    LOG_ERROR("Waiting for textures to unlock failed.\n");
+  }
+
+  MachSendMessage msg(kReturnWaitForTexturesMsg);
+  WaitForTexturesReply replydata;
+  replydata.success = success;
+  msg.SetData(&replydata, sizeof(WaitForTexturesReply));
+  kern_return_t err = ports->mSender->SendMessage(msg, kTimeout);
+  if (KERN_SUCCESS != err) {
+    LOG_ERROR("SendMessage failed 0x%x %s\n", err, mach_error_string(err));
+  }
+}
+
 // We were asked by another process to get communications ports to some process. Return
 // those ports via an IPC message.
 bool
 SendReturnPortsMsg(MachPortSender* sender,
                    mach_port_t raw_ports_in_sender,
                    mach_port_t raw_ports_out_sender)
 {
   MachSendMessage getPortsMsg(kReturnPortsMsg);
@@ -400,61 +502,118 @@ PortServerThread(void *argument)
       continue;
     }
     if (rmsg.GetMessageID() == kShutdownMsg) {
       delete ports->mSender;
       delete ports->mReceiver;
       delete ports;
       return nullptr;
     }
-    StaticMutexAutoLock smal(gMutex);
-    switch (rmsg.GetMessageID()) {
-    case kSharePortsMsg:
-      HandleSharePortsMessage(&rmsg, ports);
-      break;
-    case kGetPortsMsg:
-      HandleGetPortsMessage(&rmsg, ports);
-      break;
-    case kCleanupMsg:
-     if (gParentPid == 0) {
-       LOG_ERROR("Cleanup message not valid for parent process");
-       continue;
-     }
+    if (rmsg.GetMessageID() == kWaitForTexturesMsg) {
+      HandleWaitForTexturesMessage(&rmsg, ports);
+    } else if (rmsg.GetMessageID() == kUpdateTextureLocksMsg) {
+      DispatchCheckTexturesForUnlock();
+    } else {
+      StaticMutexAutoLock smal(gMutex);
+      switch (rmsg.GetMessageID()) {
+      case kSharePortsMsg:
+        HandleSharePortsMessage(&rmsg, ports);
+        break;
+      case kGetPortsMsg:
+        HandleGetPortsMessage(&rmsg, ports);
+        break;
+      case kCleanupMsg:
+       if (gParentPid == 0) {
+         LOG_ERROR("Cleanup message not valid for parent process");
+         continue;
+       }
 
-      pid_t* pid;
-      if (rmsg.GetDataLength() != sizeof(pid_t)) {
-        LOG_ERROR("Improperly formatted message\n");
-        continue;
+        pid_t* pid;
+        if (rmsg.GetDataLength() != sizeof(pid_t)) {
+          LOG_ERROR("Improperly formatted message\n");
+          continue;
+        }
+        pid = reinterpret_cast<pid_t*>(rmsg.GetData());
+        SharedMemoryBasic::CleanupForPid(*pid);
+        break;
+      default:
+        LOG_ERROR("Unknown message\n");
       }
-      pid = reinterpret_cast<pid_t*>(rmsg.GetData());
-      SharedMemoryBasic::CleanupForPid(*pid);
-      break;
-    default:
-      LOG_ERROR("Unknown message\n");
     }
   }
 }
 
 void
+SharedMemoryBasic::RegisterTextureSourceProvider(layers::TextureSourceProvider* textureSourceProvider)
+{
+  gTextureSourceProvider = textureSourceProvider;
+}
+
+void
+SharedMemoryBasic::SetTexturesLocked(pid_t pid, const nsTArray<uint64_t>& textureIds)
+{
+  StaticMonitorAutoLock mal(gTextureLockMonitor);
+  std::unordered_set<uint64_t>* lockedTextureIds = GetLockedTextureIdsForProcess(pid);
+  for (uint64_t textureId : textureIds) {
+    lockedTextureIds->insert(textureId);
+  }
+}
+
+void
+SharedMemoryBasic::SetTexturesUnlocked(pid_t pid, const nsTArray<uint64_t>& textureIds)
+{
+  bool oneErased = false;
+  {
+    StaticMonitorAutoLock mal(gTextureLockMonitor);
+    std::unordered_set<uint64_t>* lockedTextureIds = GetLockedTextureIdsForProcess(pid);
+    for (uint64_t textureId : textureIds) {
+      if (lockedTextureIds->erase(textureId)) {
+        oneErased = true;
+      }
+    }
+  }
+  if (oneErased) {
+    gTextureLockMonitor.NotifyAll();
+  }
+}
+
+void
 SharedMemoryBasic::SetupMachMemory(pid_t pid,
                                    ReceivePort* listen_port,
                                    MachPortSender* listen_port_ack,
                                    MachPortSender* send_port,
                                    ReceivePort* send_port_ack,
                                    bool pidIsParent)
 {
   StaticMutexAutoLock smal(gMutex);
   mozilla::ipc::SetupMachMemory(pid, listen_port, listen_port_ack, send_port, send_port_ack, pidIsParent);
 }
 
 void
 SharedMemoryBasic::Shutdown()
 {
   StaticMutexAutoLock smal(gMutex);
 
+  gTextureSourceProvider = nullptr;
+
+  {
+    StaticMonitorAutoLock lock(gTextureLockMonitor);
+
+    for (auto& lockedTextureIds : gProcessTextureIds) {
+      lockedTextureIds.second.clear(); 
+    } 
+  }
+
+  gTextureLockMonitor.NotifyAll();
+
+  {
+    StaticMonitorAutoLock lock(gTextureLockMonitor);
+    gProcessTextureIds.clear();
+  }
+
   for (auto& thread : gThreads) {
     MachSendMessage shutdownMsg(kShutdownMsg);
     thread.second.mPorts->mReceiver->SendMessageToSelf(shutdownMsg, kTimeout);
   }
   gThreads.clear();
 
   for (auto& memoryCommPort : gMemoryCommPorts) {
     delete memoryCommPort.second.mSender;
@@ -464,16 +623,24 @@ SharedMemoryBasic::Shutdown()
 }
 
 void
 SharedMemoryBasic::CleanupForPid(pid_t pid)
 {
   if (gThreads.find(pid) == gThreads.end()) {
     return;
   }
+
+  {
+    StaticMonitorAutoLock lock(gTextureLockMonitor);
+    std::unordered_set<uint64_t>* lockedTextureIds = GetLockedTextureIdsForProcess(pid);
+    lockedTextureIds->clear();
+  }
+  gTextureLockMonitor.NotifyAll();
+
   const ListeningThread& listeningThread = gThreads[pid];
   MachSendMessage shutdownMsg(kShutdownMsg);
   kern_return_t ret = listeningThread.mPorts->mReceiver->SendMessageToSelf(shutdownMsg, kTimeout);
   if (ret != KERN_SUCCESS) {
     LOG_ERROR("sending shutdown msg failed %s %x\n", mach_error_string(ret), ret);
   }
   gThreads.erase(pid);
 
@@ -633,16 +800,105 @@ SharedMemoryBasic::ShareToProcess(base::
     LOG_ERROR("Serials do not match up: %" PRIu64 " vs %" PRIu64 "", serial_check, my_serial);
     return false;
   }
   *aNewHandle = id;
   return true;
 }
 
 void
+SharedMemoryBasic::UpdateTextureLocks(base::ProcessId pid)
+{
+  if (pid == getpid()) {
+    DispatchCheckTexturesForUnlock();
+    return;
+  }
+
+  StaticMutexAutoLock smal(gMutex);
+  MemoryPorts* ports = GetMemoryPortsForPid(pid);
+  if (!ports) {
+    LOG_ERROR("Unable to get ports for process.\n");
+    return;
+  }
+  MachSendMessage smsg(kUpdateTextureLocksMsg);
+  smsg.SetData(&pid, sizeof(pid));
+
+  kern_return_t err = ports->mSender->SendMessage(smsg, kTimeout);
+  if (err != KERN_SUCCESS) {
+    LOG_ERROR("Failed updating texture locks.\n");
+  }
+}
+
+bool
+SharedMemoryBasic::WaitForTextures(base::ProcessId pid, const nsTArray<uint64_t>& textureIds)
+{
+  if (pid == getpid()) {
+    UniquePtr<uint64_t[]> reqTextureIds = MakeUnique<uint64_t[]>(textureIds.Length());
+    for (uint32_t i = 0; i < textureIds.Length(); ++i) {
+      reqTextureIds[i] = textureIds[i];
+    }
+    bool success = WaitForTextureIdsToUnlock(pid, reqTextureIds.get(), textureIds.Length());
+    if (!success) {
+      LOG_ERROR("Failed waiting for textures to unlock.\n");
+    }
+
+    return success;
+  }
+  StaticMutexAutoLock smal(gMutex);
+
+  MemoryPorts* ports = GetMemoryPortsForPid(pid);
+  if (!ports) {
+    LOG_ERROR("Unable to get ports for process.\n");
+    return false;
+  }
+  MachSendMessage smsg(kWaitForTexturesMsg);
+  size_t messageSize = sizeof(WaitForTexturesRequest) + textureIds.Length() * sizeof(uint64_t);
+  UniquePtr<uint8_t[]> messageData = MakeUnique<uint8_t[]>(messageSize);
+  WaitForTexturesRequest* req = (WaitForTexturesRequest*)messageData.get();
+  uint64_t* reqTextureIds = (uint64_t*)(req + 1);
+
+  for (uint32_t i = 0; i < textureIds.Length(); ++i) {
+    reqTextureIds[i] = textureIds[i];
+  }
+
+  req->pid = getpid();
+  bool dataWasSet = smsg.SetData(req, messageSize);
+
+  if (!dataWasSet) {
+    LOG_ERROR("Data was too large: %zu\n", messageSize);
+    return false;
+  }
+
+  kern_return_t err = ports->mSender->SendMessage(smsg, kTimeout);
+  MachReceiveMessage msg;
+  err = ports->mReceiver->WaitForMessage(&msg, kTimeout);
+  if (err != KERN_SUCCESS) {
+    LOG_ERROR("short timeout didn't get an id %s %x\n", mach_error_string(err), err);
+    err = ports->mReceiver->WaitForMessage(&msg, kLongTimeout);
+
+    if (err != KERN_SUCCESS) {
+      LOG_ERROR("long timeout didn't get an id %s %x\n", mach_error_string(err), err);
+      return false;
+    }
+  }
+  if (msg.GetDataLength() != sizeof(WaitForTexturesReply)) {
+    LOG_ERROR("Improperly formatted reply\n");
+    return false;
+  }
+
+  WaitForTexturesReply* msg_data = reinterpret_cast<WaitForTexturesReply*>(msg.GetData());
+  if (!msg_data->success) {
+    LOG_ERROR("Failed waiting for textures to unlock.\n");
+    return false;
+  }
+
+  return true;
+}
+
+void
 SharedMemoryBasic::Unmap()
 {
   if (!mMemory) {
     return;
   }
   vm_address_t address = toVMAddress(mMemory);
   kern_return_t kr = vm_deallocate(mach_task_self(), address, round_page(mMappedSize));
   if (kr != KERN_SUCCESS) {