Bug 1417784 - Properly implement SyncWithCompositor for WebRender. r?sotaro draft
authorKartikaya Gupta <kgupta@mozilla.com>
Mon, 28 May 2018 11:29:52 -0400
changeset 800594 0b1178b29bb4ab37853ed46c8b35a17b746302ae
parent 800593 087cf27350b4d3e0c72309bb66783ea7832963e0
push id111414
push userkgupta@mozilla.com
push dateMon, 28 May 2018 15:30:18 +0000
reviewerssotaro
bugs1417784
milestone62.0a1
Bug 1417784 - Properly implement SyncWithCompositor for WebRender. r?sotaro This defines three flushing functions that flush different parts of the WR pipeline. Using all three guarantees that everything sent to WR will have been flushed. This is what we need to do in SyncWithCompositor to ensure that it meets the API contract. In addition, this patch updates the existing FlushRendering function to use the new functions (no functional changes intended here). MozReview-Commit-ID: GzxwpF4JT04
gfx/layers/ipc/PWebRenderBridge.ipdl
gfx/layers/wr/WebRenderBridgeChild.cpp
gfx/layers/wr/WebRenderBridgeParent.cpp
gfx/layers/wr/WebRenderBridgeParent.h
ipc/ipdl/sync-messages.ini
--- a/gfx/layers/ipc/PWebRenderBridge.ipdl
+++ b/gfx/layers/ipc/PWebRenderBridge.ipdl
@@ -52,16 +52,21 @@ parent:
   sync GetSnapshot(PTexture texture);
   async SetLayerObserverEpoch(uint64_t layerObserverEpoch);
   async ClearCachedResources();
   // Schedule a composite if one isn't already scheduled.
   async ScheduleComposite();
   // Save the frame capture to disk
   async Capture();
 
+  // Replacement for PCompositorBridge::SyncWithCompositor, but for WR. We need
+  // it on PWebRenderBridge because it associated with a particular top-level
+  // window, and PCompositorBridge doesn't allow doing that in a secure manner.
+  sync SyncWithCompositor();
+
   // These correspond exactly to the equivalent APIs in PLayerTransaction -
   // see those for documentation.
   async SetConfirmedTargetAPZC(uint64_t aInputBlockId, ScrollableLayerGuid[] aTargets);
   // More copied from PLayerTransaction, but these are only used for testing.
   sync SetTestSampleTime(TimeStamp sampleTime);
   sync LeaveTestMode();
   sync GetAnimationOpacity(uint64_t aCompositorAnimationsId) returns (float opacity,
                                                   bool hasAnimationOpacity);
--- a/gfx/layers/wr/WebRenderBridgeChild.cpp
+++ b/gfx/layers/wr/WebRenderBridgeChild.cpp
@@ -390,20 +390,20 @@ LayersIPCActor*
 WebRenderBridgeChild::GetLayersIPCActor()
 {
   return static_cast<LayersIPCActor*>(GetCompositorBridgeChild());
 }
 
 void
 WebRenderBridgeChild::SyncWithCompositor()
 {
-  auto compositorBridge = GetCompositorBridgeChild();
-  if (compositorBridge && compositorBridge->IPCOpen()) {
-    compositorBridge->SendSyncWithCompositor();
+  if (!IPCOpen()) {
+    return;
   }
+  SendSyncWithCompositor();
 }
 
 void
 WebRenderBridgeChild::Connect(CompositableClient* aCompositable,
                               ImageContainer* aImageContainer)
 {
   MOZ_ASSERT(!mDestroyed);
   MOZ_ASSERT(aCompositable);
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -847,16 +847,72 @@ WebRenderBridgeParent::ProcessWebRenderP
       default: {
         // other commands are handle on the child
         break;
       }
     }
   }
 }
 
+void
+WebRenderBridgeParent::FlushSceneBuilds()
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+
+  if (gfxPrefs::WebRenderAsyncSceneBuild()) {
+    // If we are sending transactions through the scene builder thread, we need
+    // to block until all the inflight transactions have been processed. This
+    // flush message blocks until all previously sent scenes have been built
+    // and received by the render backend thread.
+    mApi->FlushSceneBuilder();
+    // The post-swap hook for async-scene-building calls the
+    // ScheduleRenderOnCompositorThread function from the scene builder thread,
+    // which then triggers a call to ScheduleGenerateFrame() on the compositor
+    // thread. But since *this* function is running on the compositor thread,
+    // that scheduling will not happen until this call stack unwinds (or we
+    // could spin a nested event loop, but that's more messy). Instead, we
+    // simulate it ourselves by calling ScheduleGenerateFrame() directly.
+    // In the case where async scene building is disabled, the
+    // ScheduleGenerateFrame() call in RecvSetDisplayList() serves this purpose.
+    // Note also that the post-swap hook will run and do another
+    // ScheduleGenerateFrame() after we unwind here, so we will end up with an
+    // extra render/composite that is probably avoidable, but in practice we
+    // shouldn't be calling this function all that much in production so this
+    // is probably fine. If it becomes an issue we can add more state tracking
+    // machinery to optimize it away.
+    ScheduleGenerateFrame();
+  }
+}
+
+void
+WebRenderBridgeParent::FlushFrameGeneration()
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  MOZ_ASSERT(mWidget); // This function is only useful on the root WRBP
+
+  // This forces a new GenerateFrame transaction to be sent to the render
+  // backend thread, if one is pending. This doesn't block on any other threads.
+  mForceRendering = true;
+  mCompositorScheduler->FlushPendingComposite();
+  mForceRendering = false;
+}
+
+void
+WebRenderBridgeParent::FlushFramePresentation()
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+
+  // This sends a message to the render backend thread to send a message
+  // to the renderer thread, and waits for that message to be processed. So
+  // this effectively blocks on the render backend and renderer threads,
+  // following the same codepath that WebRender takes to render and composite
+  // a frame.
+  mApi->WaitFlushed();
+}
+
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvGetSnapshot(PTextureParent* aTexture)
 {
   if (mDestroyed) {
     return IPC_OK();
   }
   MOZ_ASSERT(!mPaused);
 
@@ -1115,16 +1171,33 @@ WebRenderBridgeParent::RecvCapture()
 {
   if (!mDestroyed) {
     mApi->Capture();
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
+WebRenderBridgeParent::RecvSyncWithCompositor()
+{
+  FlushSceneBuilds();
+  if (RefPtr<WebRenderBridgeParent> root = GetRootWebRenderBridgeParent()) {
+    root->FlushFrameGeneration();
+  }
+  FlushFramePresentation();
+  // Finally, we force the AsyncImagePipelineManager to handle all the
+  // pipeline updates produced in the last step, so that it frees any
+  // unneeded textures. Then we can return from this sync IPC call knowing
+  // that we've done everything we can to flush stuff on the compositor.
+  mAsyncImageManager->ProcessPipelineUpdates();
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvSetConfirmedTargetAPZC(const uint64_t& aBlockId,
                                                   nsTArray<ScrollableLayerGuid>&& aTargets)
 {
   if (mDestroyed) {
     return IPC_OK();
   }
   mCompositorBridge->SetConfirmedTargetAPZC(GetLayersId(), aBlockId, aTargets);
   return IPC_OK();
@@ -1451,21 +1524,19 @@ WebRenderBridgeParent::ScheduleGenerateF
 
 void
 WebRenderBridgeParent::FlushRendering()
 {
   if (mDestroyed) {
     return;
   }
 
-  mForceRendering = true;
-  mCompositorScheduler->FlushPendingComposite();
-  // Always meed to wait flushed, since WebRender might have ongoing tasks.
-  mApi->WaitFlushed();
-  mForceRendering = false;
+  // XXX: do we need to flush any scene building here?
+  FlushFrameGeneration();
+  FlushFramePresentation();
 }
 
 void
 WebRenderBridgeParent::FlushRenderingAsync()
 {
   if (mDestroyed) {
     return;
   }
--- a/gfx/layers/wr/WebRenderBridgeParent.h
+++ b/gfx/layers/wr/WebRenderBridgeParent.h
@@ -103,16 +103,17 @@ public:
   mozilla::ipc::IPCResult RecvParentCommands(nsTArray<WebRenderParentCommand>&& commands) override;
   mozilla::ipc::IPCResult RecvGetSnapshot(PTextureParent* aTexture) override;
 
   mozilla::ipc::IPCResult RecvSetLayerObserverEpoch(const uint64_t& aLayerObserverEpoch) override;
 
   mozilla::ipc::IPCResult RecvClearCachedResources() override;
   mozilla::ipc::IPCResult RecvScheduleComposite() override;
   mozilla::ipc::IPCResult RecvCapture() override;
+  mozilla::ipc::IPCResult RecvSyncWithCompositor() override;
 
   mozilla::ipc::IPCResult RecvSetConfirmedTargetAPZC(const uint64_t& aBlockId,
                                                      nsTArray<ScrollableLayerGuid>&& aTargets) override;
 
   mozilla::ipc::IPCResult RecvSetTestSampleTime(const TimeStamp& aTime) override;
   mozilla::ipc::IPCResult RecvLeaveTestMode() override;
   mozilla::ipc::IPCResult RecvGetAnimationOpacity(const uint64_t& aCompositorAnimationsId,
                                                   float* aOpacity,
@@ -232,16 +233,20 @@ private:
 
   RefPtr<WebRenderBridgeParent> GetRootWebRenderBridgeParent() const;
 
   // Tell APZ what the subsequent sampling's timestamp should be.
   void SetAPZSampleTime();
 
   wr::Epoch GetNextWrEpoch();
 
+  void FlushSceneBuilds();
+  void FlushFrameGeneration();
+  void FlushFramePresentation();
+
 private:
   struct PendingTransactionId {
     PendingTransactionId(const wr::Epoch& aEpoch, TransactionId aId, const TimeStamp& aTxnStartTime, const TimeStamp& aFwdTime)
       : mEpoch(aEpoch)
       , mId(aId)
       , mTxnStartTime(aTxnStartTime)
       , mFwdTime(aFwdTime)
     {}
--- a/ipc/ipdl/sync-messages.ini
+++ b/ipc/ipdl/sync-messages.ini
@@ -1031,16 +1031,18 @@ description = test only
 [PWebRenderBridge::SetAsyncScrollOffset]
 description = test only
 [PWebRenderBridge::SetAsyncZoom]
 description = test only
 [PWebRenderBridge::GetAPZTestData]
 description = test only
 [PWebRenderBridge::ShutdownSync]
 description = bug 1377024
+[PWebRenderBridge::SyncWithCompositor]
+description = WebRender equivalent for PCompositorBridge::SyncWithCompositor
 [PHal::GetCurrentBatteryInformation]
 description =
 [PHal::GetCurrentNetworkInformation]
 description =
 [PHal::GetWakeLockInfo]
 description =
 [PHal::LockScreenOrientation]
 description =