Bug 1390755 - Ensure PaintThread::EndTransaction runs before IPDL messages are resumed. r?mchang draft
authorRyan Hunt <rhunt@eqrion.net>
Wed, 16 Aug 2017 13:55:57 -0500
changeset 647725 bcb0b4e51f42f165bc3ac6c74e7ab2379feee664
parent 647724 1fe2bce0d05a74868c9f08a02deff04eefb1f8be
child 647726 ac47d49d456a122889f2d0cd4ebbad39cccd6058
push id74530
push userrhunt@eqrion.net
push dateWed, 16 Aug 2017 22:00:00 +0000
reviewersmchang
bugs1390755
milestone57.0a1
Bug 1390755 - Ensure PaintThread::EndTransaction runs before IPDL messages are resumed. r?mchang MozReview-Commit-ID: GdSKTxtqWRA
gfx/layers/PaintThread.cpp
gfx/layers/ipc/CompositorBridgeChild.cpp
gfx/layers/ipc/CompositorBridgeChild.h
--- a/gfx/layers/PaintThread.cpp
+++ b/gfx/layers/PaintThread.cpp
@@ -204,16 +204,17 @@ PaintThread::EndAsyncPaintingLayer()
 void
 PaintThread::FinishedLayerTransaction(SyncObjectClient* aSyncObject)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   RefPtr<CompositorBridgeChild> cbc;
   if (!gfxPrefs::LayersOMTPForceSync()) {
     cbc = CompositorBridgeChild::Get();
+    cbc->NotifyBeginAsyncPaintEndTransaction();
   }
 
   RefPtr<SyncObjectClient> syncObject(aSyncObject);
   RefPtr<PaintThread> self = this;
   RefPtr<Runnable> task = NS_NewRunnableFunction("PaintThread::EndAsyncLayerTransaction",
     [self, cbc, syncObject]() -> void
   {
     self->EndAsyncLayerTransaction(cbc, syncObject);
@@ -232,17 +233,17 @@ PaintThread::EndAsyncLayerTransaction(Co
 {
   MOZ_ASSERT(IsOnPaintThread());
 
   if (aSyncObject) {
     aSyncObject->Synchronize();
   }
 
   if (aBridge) {
-    aBridge->NotifyFinishedAsyncPaintTransaction();
+    aBridge->NotifyFinishedAsyncPaintEndTransaction();
   }
 }
 
 void
 PaintThread::PaintContents(CapturedPaintState* aState,
                            PrepDrawTargetForPaintingCallback aCallback)
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/gfx/layers/ipc/CompositorBridgeChild.cpp
+++ b/gfx/layers/ipc/CompositorBridgeChild.cpp
@@ -87,16 +87,17 @@ CompositorBridgeChild::CompositorBridgeC
   , mActorDestroyed(false)
   , mFwdTransactionId(0)
   , mDeviceResetSequenceNumber(0)
   , mMessageLoop(MessageLoop::current())
   , mProcessToken(0)
   , mSectionAllocator(nullptr)
   , mPaintLock("CompositorBridgeChild.mPaintLock")
   , mOutstandingAsyncPaints(0)
+  , mOutstandingAsyncEndTransaction(false)
   , mIsWaitingForPaint(false)
 {
   MOZ_ASSERT(NS_IsMainThread());
 }
 
 CompositorBridgeChild::~CompositorBridgeChild()
 {
   if (mCanSend) {
@@ -1184,23 +1185,37 @@ CompositorBridgeChild::NotifyFinishedAsy
   aState->mTextureClient = nullptr;
   if (aState->mTextureClientOnWhite) {
     aState->mTextureClientOnWhite->DropPaintThreadRef();
     aState->mTextureClientOnWhite = nullptr;
   }
 }
 
 void
-CompositorBridgeChild::NotifyFinishedAsyncPaintTransaction()
+CompositorBridgeChild::NotifyBeginAsyncPaintEndTransaction()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MonitorAutoLock lock(mPaintLock);
+
+  MOZ_ASSERT(!mOutstandingAsyncEndTransaction);
+  mOutstandingAsyncEndTransaction = true;
+}
+
+void
+CompositorBridgeChild::NotifyFinishedAsyncPaintEndTransaction()
 {
   MOZ_ASSERT(PaintThread::IsOnPaintThread());
   MonitorAutoLock lock(mPaintLock);
+
   // Since this should happen after ALL paints are done and
   // at the end of a transaction, this should always be true.
   MOZ_RELEASE_ASSERT(mOutstandingAsyncPaints == 0);
+  MOZ_ASSERT(mOutstandingAsyncEndTransaction);
+
+  mOutstandingAsyncEndTransaction = false;
 
   // It's possible that we painted so fast that the main thread never reached
   // the code that starts delaying messages. If so, mIsWaitingForPaint will be
   // false, and we can safely return.
   if (mIsWaitingForPaint) {
     ResumeIPCAfterAsyncPaint();
 
     // Notify the main thread in case it's blocking. We do this unconditionally
@@ -1213,17 +1228,17 @@ void
 CompositorBridgeChild::PostponeMessagesIfAsyncPainting()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   MonitorAutoLock lock(mPaintLock);
 
   MOZ_ASSERT(!mIsWaitingForPaint);
 
-  if (mOutstandingAsyncPaints > 0) {
+  if (mOutstandingAsyncPaints > 0 || mOutstandingAsyncEndTransaction) {
     mIsWaitingForPaint = true;
     GetIPCChannel()->BeginPostponingSends();
   }
 }
 
 void
 CompositorBridgeChild::ResumeIPCAfterAsyncPaint()
 {
--- a/gfx/layers/ipc/CompositorBridgeChild.h
+++ b/gfx/layers/ipc/CompositorBridgeChild.h
@@ -235,21 +235,26 @@ public:
   // previous frames have been flushed. The main thread blocks until the
   // operation completes.
   void FlushAsyncPaints();
 
   // Must only be called from the paint thread. Notifies the CompositorBridge
   // that the paint thread has finished an asynchronous paint request.
   void NotifyFinishedAsyncPaint(CapturedPaintState* aState);
 
+  // Must only be called from the main thread. Notifies the CompositorBridge
+  // that the paint thread is going to perform texture synchronization at the
+  // end of async painting, and should postpone messages if needed until
+  // finished.
+  void NotifyBeginAsyncPaintEndTransaction();
+
   // Must only be called from the paint thread. Notifies the CompositorBridge
-  // that the paint thread has finished ALL async requests from a given
-  // transaction. We can resume IPC transactions after ALL
-  // async paints are done.
-  void NotifyFinishedAsyncPaintTransaction();
+  // that the paint thread has finished all async paints and texture syncs from
+  // a given transaction and may resume sending messages.
+  void NotifyFinishedAsyncPaintEndTransaction();
 
 private:
   // Private destructor, to discourage deletion outside of Release():
   virtual ~CompositorBridgeChild();
 
   // Must only be called from the paint thread. If the main thread is delaying
   // IPC messages, this forwards all such delayed IPC messages to the I/O thread
   // and resumes IPC.
@@ -368,16 +373,19 @@ private:
   // state below.
   Monitor mPaintLock;
 
   // Contains the number of outstanding asynchronous paints tied to a
   // PLayerTransaction on this bridge. This is R/W on both the main and paint
   // threads, and must be accessed within the paint lock.
   size_t mOutstandingAsyncPaints;
 
+  // Whether we are waiting for an async paint end transaction
+  bool mOutstandingAsyncEndTransaction;
+
   // True if this CompositorBridge is currently delaying its messages until the
   // paint thread completes. This is R/W on both the main and paint threads, and
   // must be accessed within the paint lock.
   bool mIsWaitingForPaint;
 };
 
 } // namespace layers
 } // namespace mozilla