Bug 1405824 - Recycle a Shmem for IpcResourceUpdateQueue to avoid allocation overhead. r=jrmuizel
authorNicolas Silva <nsilva@mozilla.com>
Fri, 12 Jan 2018 15:11:32 +0100
changeset 399063 8c8d66d7b4640ad3d1031221e60b1eb43666e68a
parent 399062 62401d22f92a45c4a50040424b77915fdea687bc
child 399064 0d1e7b9d2caaf53af551428274b16ef0277d48cf
push id33240
push userncsoregi@mozilla.com
push dateFri, 12 Jan 2018 21:59:33 +0000
treeherdermozilla-central@f5b4481c9fd5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1405824
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1405824 - Recycle a Shmem for IpcResourceUpdateQueue to avoid allocation overhead. r=jrmuizel
gfx/layers/wr/IpcResourceUpdateQueue.cpp
gfx/layers/wr/IpcResourceUpdateQueue.h
gfx/layers/wr/WebRenderBridgeChild.cpp
gfx/layers/wr/WebRenderBridgeChild.h
gfx/layers/wr/WebRenderBridgeParent.cpp
gfx/layers/wr/WebRenderBridgeParent.h
--- a/gfx/layers/wr/IpcResourceUpdateQueue.cpp
+++ b/gfx/layers/wr/IpcResourceUpdateQueue.cpp
@@ -87,21 +87,22 @@ ShmSegmentsWriter::Write(Range<uint8_t> 
 
   return layers::OffsetRange(0, start, length);
 }
 
 bool
 ShmSegmentsWriter::AllocChunk()
 {
   RefCountedShmem shm;
-  if (!RefCountedShm::Alloc(mShmAllocator, mChunkSize, shm)) {
+  if (!mShmAllocator->AllocResourceShmem(mChunkSize, shm)) {
     gfxCriticalNote << "ShmSegmentsWriter failed to allocate chunk #" << mSmallAllocs.Length();
     MOZ_ASSERT(false, "ShmSegmentsWriter fails to allocate chunk");
     return false;
   }
+  RefCountedShm::AddRef(shm);
   mSmallAllocs.AppendElement(shm);
   return true;
 }
 
 layers::OffsetRange
 ShmSegmentsWriter::AllocLargeChunk(size_t aSize)
 {
   ipc::Shmem shm;
@@ -114,35 +115,29 @@ ShmSegmentsWriter::AllocLargeChunk(size_
   mLargeAllocs.AppendElement(shm);
 
   return layers::OffsetRange(mLargeAllocs.Length(), 0, aSize);
 }
 
 void
 ShmSegmentsWriter::Flush(nsTArray<RefCountedShmem>& aSmallAllocs, nsTArray<ipc::Shmem>& aLargeAllocs)
 {
-  aSmallAllocs.Clear();
-  aLargeAllocs.Clear();
+  MOZ_ASSERT(aSmallAllocs.IsEmpty());
+  MOZ_ASSERT(aLargeAllocs.IsEmpty());
   mSmallAllocs.SwapElements(aSmallAllocs);
   mLargeAllocs.SwapElements(aLargeAllocs);
 }
 
 void
 ShmSegmentsWriter::Clear()
 {
   if (mShmAllocator) {
-    for (auto& shm : mSmallAllocs) {
-      RefCountedShm::Dealloc(mShmAllocator, shm);
-    }
-    for (auto& shm : mLargeAllocs) {
-      mShmAllocator->DeallocShmem(shm);
-    }
+    IpcResourceUpdateQueue::ReleaseShmems(mShmAllocator, mSmallAllocs);
+    IpcResourceUpdateQueue::ReleaseShmems(mShmAllocator, mLargeAllocs);
   }
-  mSmallAllocs.Clear();
-  mLargeAllocs.Clear();
   mCursor = 0;
 }
 
 ShmSegmentsReader::ShmSegmentsReader(const nsTArray<RefCountedShmem>& aSmallShmems,
                                      const nsTArray<ipc::Shmem>& aLargeShmems)
 : mSmallAllocs(aSmallShmems)
 , mLargeAllocs(aLargeShmems)
 , mChunkSize(0)
@@ -349,25 +344,47 @@ IpcResourceUpdateQueue::AddFontInstance(
 void
 IpcResourceUpdateQueue::DeleteFontInstance(wr::FontInstanceKey aKey)
 {
   mUpdates.AppendElement(layers::OpDeleteFontInstance(aKey));
 }
 
 void
 IpcResourceUpdateQueue::Flush(nsTArray<layers::OpUpdateResource>& aUpdates,
-                              nsTArray<RefCountedShmem>& aSmallAllocs,
+                              nsTArray<layers::RefCountedShmem>& aSmallAllocs,
                               nsTArray<ipc::Shmem>& aLargeAllocs)
 {
   aUpdates.Clear();
   mUpdates.SwapElements(aUpdates);
   mWriter.Flush(aSmallAllocs, aLargeAllocs);
 }
 
 void
 IpcResourceUpdateQueue::Clear()
 {
   mWriter.Clear();
   mUpdates.Clear();
 }
 
+//static
+void
+IpcResourceUpdateQueue::ReleaseShmems(ipc::IProtocol* aShmAllocator, nsTArray<layers::RefCountedShmem>& aShms)
+{
+  for (auto& shm : aShms) {
+    if (RefCountedShm::IsValid(shm) && RefCountedShm::Release(shm) == 0) {
+      RefCountedShm::Dealloc(aShmAllocator, shm);
+    }
+  }
+  aShms.Clear();
+}
+
+//static
+void
+IpcResourceUpdateQueue::ReleaseShmems(ipc::IProtocol* aShmAllocator, nsTArray<ipc::Shmem>& aShms)
+{
+  for (auto& shm : aShms) {
+    aShmAllocator->DeallocShmem(shm);
+  }
+  aShms.Clear();
+}
+
 } // namespace
 } // namespace
--- a/gfx/layers/wr/IpcResourceUpdateQueue.h
+++ b/gfx/layers/wr/IpcResourceUpdateQueue.h
@@ -113,16 +113,18 @@ public:
   void DeleteFontInstance(wr::FontInstanceKey aKey);
 
   void Clear();
 
   void Flush(nsTArray<layers::OpUpdateResource>& aUpdates,
              nsTArray<layers::RefCountedShmem>& aSmallAllocs,
              nsTArray<ipc::Shmem>& aLargeAllocs);
 
+  static void ReleaseShmems(ipc::IProtocol*, nsTArray<layers::RefCountedShmem>& aShmems);
+  static void ReleaseShmems(ipc::IProtocol*, nsTArray<ipc::Shmem>& aShmems);
 protected:
   ShmSegmentsWriter mWriter;
   nsTArray<layers::OpUpdateResource> mUpdates;
 };
 
 } // namespace
 } // namespace
 
--- a/gfx/layers/wr/WebRenderBridgeChild.cpp
+++ b/gfx/layers/wr/WebRenderBridgeChild.cpp
@@ -63,16 +63,21 @@ void
 WebRenderBridgeChild::ActorDestroy(ActorDestroyReason why)
 {
   DoDestroy();
 }
 
 void
 WebRenderBridgeChild::DoDestroy()
 {
+  if (RefCountedShm::IsValid(mResourceShm) && RefCountedShm::Release(mResourceShm) == 0) {
+    RefCountedShm::Dealloc(this, mResourceShm);
+    mResourceShm = RefCountedShmem();
+  }
+
   // mDestroyed is used to prevent calling Send__delete__() twice.
   // When this function is called from CompositorBridgeChild::Destroy().
   // mActiveResourceTracker is not cleared here, since it is
   // used by PersistentBufferProviderShared.
   mDestroyed = true;
   mManager = nullptr;
 }
 
@@ -632,10 +637,54 @@ WebRenderBridgeChild::GetShmemAllocator(
 
 RefPtr<KnowsCompositor>
 WebRenderBridgeChild::GetForMedia()
 {
   MOZ_ASSERT(NS_IsMainThread());
   return MakeAndAddRef<KnowsCompositorMediaProxy>(GetTextureFactoryIdentifier());
 }
 
+bool
+WebRenderBridgeChild::AllocResourceShmem(size_t aSize, RefCountedShmem& aShm)
+{
+  // We keep a single shmem around to reuse later if it is reference count has
+  // dropped back to 1 (the reference held by the WebRenderBridgeChild).
+
+  // If the cached shmem exists, has the correct size and isn't held by anything
+  // other than us, recycle it.
+  bool alreadyAllocated = RefCountedShm::IsValid(mResourceShm);
+  if (alreadyAllocated) {
+    if (RefCountedShm::GetSize(mResourceShm) == aSize
+        && RefCountedShm::GetReferenceCount(mResourceShm) <= 1) {
+      MOZ_ASSERT(RefCountedShm::GetReferenceCount(mResourceShm) == 1);
+      aShm = mResourceShm;
+      return true;
+    }
+  }
+
+  // If there was no cached shmem or we couldn't recycle it, alloc a new one.
+  if (!RefCountedShm::Alloc(this, aSize, aShm)) {
+    return false;
+  }
+
+  // Now that we have a valid shmem, put it in the cache if we don't have one
+  // yet.
+  if (!alreadyAllocated) {
+    mResourceShm = aShm;
+    RefCountedShm::AddRef(aShm);
+  }
+
+  return true;
+}
+
+void
+WebRenderBridgeChild::DeallocResourceShmem(RefCountedShmem& aShm)
+{
+  if (!RefCountedShm::IsValid(aShm)) {
+    return;
+  }
+  MOZ_ASSERT(RefCountedShm::GetReferenceCount(aShm) == 0);
+
+  RefCountedShm::Dealloc(this, aShm);
+}
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderBridgeChild.h
+++ b/gfx/layers/wr/WebRenderBridgeChild.h
@@ -149,16 +149,27 @@ public:
   void SetWebRenderLayerManager(WebRenderLayerManager* aManager);
 
   ipc::IShmemAllocator* GetShmemAllocator();
 
   virtual bool IsThreadSafe() const override { return false; }
 
   virtual RefPtr<KnowsCompositor> GetForMedia() override;
 
+  /// Alloc a specific type of shmem that is intended for use in
+  /// IpcResourceUpdateQueue only, and cache at most one of them,
+  /// when called multiple times.
+  ///
+  /// Do not use this for anything else.
+  bool AllocResourceShmem(size_t aSize, RefCountedShmem& aShm);
+  /// Dealloc shared memory that was allocated with AllocResourceShmem.
+  /// 
+  /// Do not use this for anything else.
+  void DeallocResourceShmem(RefCountedShmem& aShm);
+
 private:
   friend class CompositorBridgeChild;
 
   ~WebRenderBridgeChild();
 
   wr::ExternalImageId GetNextExternalImageId();
 
   // CompositableForwarder
@@ -219,14 +230,16 @@ private:
 
   uint32_t mFontKeysDeleted;
   nsDataHashtable<UnscaledFontHashKey, wr::FontKey> mFontKeys;
 
   uint32_t mFontInstanceKeysDeleted;
   nsDataHashtable<ScaledFontHashKey, wr::FontInstanceKey> mFontInstanceKeys;
 
   UniquePtr<ActiveResourceTracker> mActiveResourceTracker;
+
+  RefCountedShmem mResourceShm;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // mozilla_layers_WebRenderBridgeChild_h
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -261,38 +261,16 @@ WebRenderBridgeParent::Destroy()
 {
   if (mDestroyed) {
     return;
   }
   mDestroyed = true;
   ClearResources();
 }
 
-void
-WebRenderBridgeParent::DeallocShmems(nsTArray<ipc::Shmem>& aShmems)
-{
-  if (IPCOpen()) {
-    for (auto& shm : aShmems) {
-      DeallocShmem(shm);
-    }
-  }
-  aShmems.Clear();
-}
-
-void
-WebRenderBridgeParent::DeallocShmems(nsTArray<RefCountedShmem>& aShmems)
-{
-  if (IPCOpen()) {
-    for (auto& shm : aShmems) {
-       RefCountedShm::Dealloc(this, shm);
-    }
-  }
-  aShmems.Clear();
-}
-
 bool
 WebRenderBridgeParent::UpdateResources(const nsTArray<OpUpdateResource>& aResourceUpdates,
                                        const nsTArray<RefCountedShmem>& aSmallShmems,
                                        const nsTArray<ipc::Shmem>& aLargeShmems,
                                        wr::ResourceUpdateQueue& aUpdates)
 {
   wr::ShmSegmentsReader reader(aSmallShmems, aLargeShmems);
 
@@ -460,35 +438,33 @@ WebRenderBridgeParent::AddExternalImage(
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvUpdateResources(nsTArray<OpUpdateResource>&& aResourceUpdates,
                                            nsTArray<RefCountedShmem>&& aSmallShmems,
                                            nsTArray<ipc::Shmem>&& aLargeShmems)
 {
   if (mDestroyed) {
-    DeallocShmems(aSmallShmems);
-    DeallocShmems(aLargeShmems);
     return IPC_OK();
   }
 
   wr::ResourceUpdateQueue updates;
 
   if (!UpdateResources(aResourceUpdates, aSmallShmems, aLargeShmems, updates)) {
-    DeallocShmems(aSmallShmems);
-    DeallocShmems(aLargeShmems);
+    wr::IpcResourceUpdateQueue::ReleaseShmems(this, aSmallShmems);
+    wr::IpcResourceUpdateQueue::ReleaseShmems(this, aLargeShmems);
     IPC_FAIL(this, "Invalid WebRender resource data shmem or address.");
   }
 
   wr::TransactionBuilder txn;
   txn.UpdateResources(updates);
   mApi->SendTransaction(txn);
 
-  DeallocShmems(aSmallShmems);
-  DeallocShmems(aLargeShmems);
+  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aSmallShmems);
+  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aLargeShmems);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvDeleteCompositorAnimations(InfallibleTArray<uint64_t>&& aIds)
 {
   if (mDestroyed) {
     return IPC_OK();
@@ -596,18 +572,16 @@ WebRenderBridgeParent::RecvSetDisplayLis
                                           const wr::IdNamespace& aIdNamespace,
                                           const TimeStamp& aTxnStartTime,
                                           const TimeStamp& aFwdTime)
 {
   if (mDestroyed) {
     for (const auto& op : aToDestroy) {
       DestroyActor(op);
     }
-    DeallocShmems(aSmallShmems);
-    DeallocShmems(aLargeShmems);
     return IPC_OK();
   }
 
   AUTO_PROFILER_TRACING("Paint", "SetDisplayList");
   UpdateFwdTransactionId(aFwdTransactionId);
   AutoClearReadLocks clearLocks(mReadLocks);
 
   // This ensures that destroy operations are always processed. It is not safe
@@ -659,18 +633,18 @@ WebRenderBridgeParent::RecvSetDisplayLis
 
   if (mIdNamespace != aIdNamespace) {
     // Pretend we composited since someone is wating for this event,
     // though DisplayList was not pushed to webrender.
     TimeStamp now = TimeStamp::Now();
     mCompositorBridge->DidComposite(wr::AsUint64(mPipelineId), now, now);
   }
 
-  DeallocShmems(aSmallShmems);
-  DeallocShmems(aLargeShmems);
+  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aSmallShmems);
+  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aLargeShmems);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvEmptyTransaction(const FocusTarget& aFocusTarget,
                                             InfallibleTArray<WebRenderParentCommand>&& aCommands,
                                             InfallibleTArray<OpDestroy>&& aToDestroy,
                                             const uint64_t& aFwdTransactionId,
--- a/gfx/layers/wr/WebRenderBridgeParent.h
+++ b/gfx/layers/wr/WebRenderBridgeParent.h
@@ -187,19 +187,16 @@ public:
   void ScheduleGenerateFrame();
 
   void UpdateWebRender(CompositorVsyncScheduler* aScheduler,
                        wr::WebRenderAPI* aApi,
                        AsyncImagePipelineManager* aImageMgr,
                        CompositorAnimationStorage* aAnimStorage);
 
 private:
-  void DeallocShmems(nsTArray<ipc::Shmem>& aShmems);
-  void DeallocShmems(nsTArray<RefCountedShmem>& aShmems);
-
   explicit WebRenderBridgeParent(const wr::PipelineId& aPipelineId);
   virtual ~WebRenderBridgeParent();
 
   bool UpdateResources(const nsTArray<OpUpdateResource>& aResourceUpdates,
                        const nsTArray<RefCountedShmem>& aSmallShmems,
                        const nsTArray<ipc::Shmem>& aLargeShmems,
                        wr::ResourceUpdateQueue& aUpdates);
   bool AddExternalImage(wr::ExternalImageId aExtId, wr::ImageKey aKey,