Bug 1479754 - Hook up the force-first-paint codepath with webrender r=kats
authorJamie Nicol <jnicol@mozilla.com>
Fri, 19 Oct 2018 13:48:04 +0000
changeset 500653 fb372f5f22b3a8813b3c687e5a8d4ce3177fbdea
parent 500652 681ffcabae79527e14f5f795aaf360eeb2e85399
child 500654 8b7834399818e8a4d73d0350e493114241531a4a
push id1864
push userffxbld-merge
push dateMon, 03 Dec 2018 15:51:40 +0000
treeherdermozilla-release@f040763d99ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats
bugs1479754
milestone64.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 1479754 - Hook up the force-first-paint codepath with webrender r=kats When a CompositorBridgeParent receives a force first paint message, it sets the flag on the AsyncCompositionManager, which notifies the widget code at the next composite via UiCompositorControllerParent::NotifyFirstPaint(). With webrender, this is crashing as there is no AsyncCompositionManager. And even if it weren't crashing, the widget will never receive the first paint message, so it never uncovers its content. This change ensures the widget receives the first message when webrender is enabled. CompositorBridgeParent will set the flag on its WebRenderBridgeParent, which will set the flag on the next received display list. When the WebRenderBridgeParent flushes the corresponding transaction, it calls UiCompositorcontrollerParent::NotifyFirstPaint, to ensure the widget code gets the message. Differential Revision: https://phabricator.services.mozilla.com/D9250
gfx/layers/ipc/CompositorBridgeParent.cpp
gfx/layers/wr/WebRenderBridgeParent.cpp
gfx/layers/wr/WebRenderBridgeParent.h
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -462,17 +462,21 @@ CompositorBridgeParent::~CompositorBridg
     RefPtr<TextureHost> tex = TextureHost::AsTextureHost(textures[i]);
     tex->DeallocateDeviceData();
   }
 }
 
 void
 CompositorBridgeParent::ForceIsFirstPaint()
 {
-  mCompositionManager->ForceIsFirstPaint();
+  if (mWrBridge) {
+    mWrBridge->ForceIsFirstPaint();
+  } else {
+    mCompositionManager->ForceIsFirstPaint();
+  }
 }
 
 void
 CompositorBridgeParent::StopAndClearResources()
 {
   if (mForceCompositionTask) {
     mForceCompositionTask->Cancel();
     mForceCompositionTask = nullptr;
@@ -2200,21 +2204,24 @@ CompositorBridgeParent::NotifyPipelineRe
                                                const wr::Epoch& aEpoch,
                                                TimeStamp& aCompositeStart,
                                                TimeStamp& aCompositeEnd)
 {
   if (!mWrBridge) {
     return;
   }
 
+  RefPtr<UiCompositorControllerParent> uiController =
+    UiCompositorControllerParent::GetFromRootLayerTreeId(mRootLayerTreeID);
+
   if (mWrBridge->PipelineId() == aPipelineId) {
     mWrBridge->RemoveEpochDataPriorTo(aEpoch);
 
     if (!mPaused) {
-      TransactionId transactionId = mWrBridge->FlushTransactionIdsForEpoch(aEpoch, aCompositeEnd);
+      TransactionId transactionId = mWrBridge->FlushTransactionIdsForEpoch(aEpoch, aCompositeEnd, uiController);
       Unused << SendDidComposite(LayersId{0}, transactionId, aCompositeStart, aCompositeEnd);
 
       nsTArray<ImageCompositeNotificationInfo> notifications;
       mWrBridge->ExtractImageCompositeNotifications(&notifications);
       if (!notifications.IsEmpty()) {
         Unused << ImageBridgeParent::NotifyImageComposites(notifications);
       }
     }
@@ -2226,17 +2233,17 @@ CompositorBridgeParent::NotifyPipelineRe
     if (lts->mCrossProcessParent &&
         lts->mWrBridge &&
         lts->mWrBridge->PipelineId() == aPipelineId) {
 
       lts->mWrBridge->RemoveEpochDataPriorTo(aEpoch);
 
       if (!mPaused) {
         CrossProcessCompositorBridgeParent* cpcp = lts->mCrossProcessParent;
-        TransactionId transactionId = lts->mWrBridge->FlushTransactionIdsForEpoch(aEpoch, aCompositeEnd);
+        TransactionId transactionId = lts->mWrBridge->FlushTransactionIdsForEpoch(aEpoch, aCompositeEnd, uiController);
         Unused << cpcp->SendDidComposite(aLayersId, transactionId, aCompositeStart, aCompositeEnd);
       }
     }
   });
 }
 
 RefPtr<AsyncImagePipelineManager>
 CompositorBridgeParent::GetAsyncImagePipelineManager() const
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -224,16 +224,17 @@ WebRenderBridgeParent::WebRenderBridgePa
   , mVsyncRate(aVsyncRate)
   , mChildLayersObserverEpoch{0}
   , mParentLayersObserverEpoch{0}
   , mWrEpoch{0}
   , mIdNamespace(aApi->GetNamespace())
   , mPaused(false)
   , mDestroyed(false)
   , mReceivedDisplayList(false)
+  , mIsFirstPaint(true)
 {
   MOZ_ASSERT(mAsyncImageManager);
   MOZ_ASSERT(mAnimStorage);
   mAsyncImageManager->AddPipeline(mPipelineId);
   if (IsRootWebRenderBridgeParent()) {
     MOZ_ASSERT(!mCompositorScheduler);
     mCompositorScheduler = new CompositorVsyncScheduler(this, mWidget);
   }
@@ -244,16 +245,17 @@ WebRenderBridgeParent::WebRenderBridgePa
   , mPipelineId(aPipelineId)
   , mChildLayersObserverEpoch{0}
   , mParentLayersObserverEpoch{0}
   , mWrEpoch{0}
   , mIdNamespace{0}
   , mPaused(false)
   , mDestroyed(true)
   , mReceivedDisplayList(false)
+  , mIsFirstPaint(false)
 {
 }
 
 /* static */ WebRenderBridgeParent*
 WebRenderBridgeParent::CreateDestroyed(const wr::PipelineId& aPipelineId)
 {
   return new WebRenderBridgeParent(aPipelineId);
 }
@@ -814,16 +816,20 @@ WebRenderBridgeParent::RecvSetDisplayLis
   }
 
   if (!UpdateResources(aResourceUpdates, aSmallShmems, aLargeShmems, txn)) {
     return IPC_FAIL(this, "Failed to deserialize resource updates");
   }
 
   mReceivedDisplayList = true;
 
+  if (aScrollData.IsFirstPaint()) {
+    mIsFirstPaint = true;
+  }
+
   // aScrollData is moved into this function but that is not reflected by the
   // function signature due to the way the IPDL generator works. We remove the
   // const so that we can move this structure all the way to the desired
   // destination.
   // Also note that this needs to happen before the display list transaction is
   // sent to WebRender, so that the UpdateHitTestingTree call is guaranteed to
   // be in the updater queue at the time that the scene swap completes.
   UpdateAPZScrollData(wrEpoch, std::move(const_cast<WebRenderScrollData&>(aScrollData)));
@@ -859,17 +865,18 @@ WebRenderBridgeParent::RecvSetDisplayLis
 
     // We will schedule generating a frame after the scene
     // build is done, so we don't need to do it here.
   } else if (observeLayersUpdate) {
     mCompositorBridge->ObserveLayersUpdate(GetLayersId(), mChildLayersObserverEpoch, true);
   }
 
   HoldPendingTransactionId(wrEpoch, aTransactionId, aContainsSVGGroup,
-                           aRefreshStartTime, aTxnStartTime, aFwdTime);
+                           aRefreshStartTime, aTxnStartTime, aFwdTime, mIsFirstPaint);
+  mIsFirstPaint = false;
 
   if (!validTransaction) {
     // Pretend we composited since someone is wating for this event,
     // though DisplayList was not pushed to webrender.
     if (CompositorBridgeParent* cbp = GetRootCompositorBridgeParent()) {
       TimeStamp now = TimeStamp::Now();
       cbp->NotifyPipelineRendered(mPipelineId, wrEpoch, now, now);
     }
@@ -959,16 +966,17 @@ WebRenderBridgeParent::RecvEmptyTransact
   // Only register a value for CONTENT_FRAME_TIME telemetry if we actually drew
   // something. It is for consistency with disabling WebRender.
   HoldPendingTransactionId(mWrEpoch,
                            aTransactionId,
                            false,
                            aRefreshStartTime,
                            aTxnStartTime,
                            aFwdTime,
+                           /* aIsFirstPaint */false,
                            /* aUseForTelemetry */scheduleComposite);
 
   if (scheduleComposite) {
     ScheduleGenerateFrame();
   } else if (sendDidComposite) {
     // The only thing in the pending transaction id queue should be the entry
     // we just added, and now we're going to pretend we rendered it
     MOZ_ASSERT(mPendingTransactionIds.size() == 1);
@@ -1683,40 +1691,43 @@ WebRenderBridgeParent::MaybeGenerateFram
 
 void
 WebRenderBridgeParent::HoldPendingTransactionId(const wr::Epoch& aWrEpoch,
                                                 TransactionId aTransactionId,
                                                 bool aContainsSVGGroup,
                                                 const TimeStamp& aRefreshStartTime,
                                                 const TimeStamp& aTxnStartTime,
                                                 const TimeStamp& aFwdTime,
+                                                const bool aIsFirstPaint,
                                                 const bool aUseForTelemetry)
 {
   MOZ_ASSERT(aTransactionId > LastPendingTransactionId());
   mPendingTransactionIds.push(PendingTransactionId(aWrEpoch,
                                                    aTransactionId,
                                                    aContainsSVGGroup,
                                                    aRefreshStartTime,
                                                    aTxnStartTime,
                                                    aFwdTime,
+                                                   aIsFirstPaint,
                                                    aUseForTelemetry));
 }
 
 TransactionId
 WebRenderBridgeParent::LastPendingTransactionId()
 {
   TransactionId id{0};
   if (!mPendingTransactionIds.empty()) {
     id = mPendingTransactionIds.back().mId;
   }
   return id;
 }
 
 TransactionId
-WebRenderBridgeParent::FlushTransactionIdsForEpoch(const wr::Epoch& aEpoch, const TimeStamp& aEndTime)
+WebRenderBridgeParent::FlushTransactionIdsForEpoch(const wr::Epoch& aEpoch, const TimeStamp& aEndTime,
+                                                   UiCompositorControllerParent* aUiController)
 {
   TransactionId id{0};
   while (!mPendingTransactionIds.empty()) {
     const auto& transactionId = mPendingTransactionIds.front();
 
     if (aEpoch.mHandle < transactionId.mEpoch.mHandle) {
       break;
     }
@@ -1736,16 +1747,21 @@ WebRenderBridgeParent::FlushTransactionI
       int32_t latencyMs = lround((aEndTime - transactionId.mRefreshStartTime).ToMilliseconds());
       printf_stderr("From transaction start to end of generate frame latencyMs %d this %p\n", latencyMs, this);
     }
     if (transactionId.mFwdTime) {
       int32_t latencyMs = lround((aEndTime - transactionId.mFwdTime).ToMilliseconds());
       printf_stderr("From forwarding transaction to end of generate frame latencyMs %d this %p\n", latencyMs, this);
     }
 #endif
+
+    if (aUiController && transactionId.mIsFirstPaint) {
+      aUiController->NotifyFirstPaint();
+    }
+
     id = transactionId.mId;
     mPendingTransactionIds.pop();
   }
   return id;
 }
 
 LayersId
 WebRenderBridgeParent::GetLayersId() const
--- a/gfx/layers/wr/WebRenderBridgeParent.h
+++ b/gfx/layers/wr/WebRenderBridgeParent.h
@@ -10,16 +10,17 @@
 #include <unordered_map>
 #include <unordered_set>
 
 #include "CompositableHost.h"           // for CompositableHost, ImageCompositeNotificationInfo
 #include "GLContextProvider.h"
 #include "mozilla/layers/CompositableTransactionParent.h"
 #include "mozilla/layers/CompositorVsyncSchedulerOwner.h"
 #include "mozilla/layers/PWebRenderBridgeParent.h"
+#include "mozilla/layers/UiCompositorControllerParent.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 #include "mozilla/webrender/WebRenderAPI.h"
 #include "nsTArrayForwardDeclare.h"
 
 namespace mozilla {
 
 namespace gl {
@@ -154,19 +155,21 @@ public:
   void SetAboutToSendAsyncMessages() override;
 
   void HoldPendingTransactionId(const wr::Epoch& aWrEpoch,
                                 TransactionId aTransactionId,
                                 bool aContainsSVGGroup,
                                 const TimeStamp& aRefreshStartTime,
                                 const TimeStamp& aTxnStartTime,
                                 const TimeStamp& aFwdTime,
+                                const bool aIsFirstPaint,
                                 const bool aUseForTelemetry = true);
   TransactionId LastPendingTransactionId();
-  TransactionId FlushTransactionIdsForEpoch(const wr::Epoch& aEpoch, const TimeStamp& aEndTime);
+  TransactionId FlushTransactionIdsForEpoch(const wr::Epoch& aEpoch, const TimeStamp& aEndTime,
+                                            UiCompositorControllerParent* aUiController);
 
   TextureFactoryIdentifier GetTextureFactoryIdentifier();
 
   void ExtractImageCompositeNotifications(nsTArray<ImageCompositeNotificationInfo>* aNotifications);
 
   wr::IdNamespace GetIdNamespace()
   {
     return mIdNamespace;
@@ -198,16 +201,26 @@ public:
   wr::Epoch UpdateWebRender(CompositorVsyncScheduler* aScheduler,
                             wr::WebRenderAPI* aApi,
                             AsyncImagePipelineManager* aImageMgr,
                             CompositorAnimationStorage* aAnimStorage,
                             const TextureFactoryIdentifier& aTextureFactoryIdentifier);
 
   void RemoveEpochDataPriorTo(const wr::Epoch& aRenderedEpoch);
 
+  /**
+   * This sets the is-first-paint flag to true for the next received
+   * display list. This is intended to be called by the widget code when it
+   * loses its viewport information (or for whatever reason wants to refresh
+   * the viewport information). The message will sent back to the widget code
+   * via UiCompositorControllerParent::NotifyFirstPaint() when the corresponding
+   * transaction is flushed.
+   */
+  void ForceIsFirstPaint() { mIsFirstPaint = true; }
+
 private:
   explicit WebRenderBridgeParent(const wr::PipelineId& aPipelineId);
   virtual ~WebRenderBridgeParent();
 
   void UpdateAPZFocusState(const FocusTarget& aFocus);
   void UpdateAPZScrollData(const wr::Epoch& aEpoch, WebRenderScrollData&& aData);
   void UpdateAPZScrollOffsets(ScrollUpdatesMap&& aUpdates,
                               uint32_t aPaintSequenceNumber);
@@ -273,31 +286,34 @@ private:
 private:
   struct PendingTransactionId {
     PendingTransactionId(const wr::Epoch& aEpoch,
                          TransactionId aId,
                          bool aContainsSVGGroup,
                          const TimeStamp& aRefreshStartTime,
                          const TimeStamp& aTxnStartTime,
                          const TimeStamp& aFwdTime,
+                         const bool aIsFirstPaint,
                          const bool aUseForTelemetry)
       : mEpoch(aEpoch)
       , mId(aId)
       , mRefreshStartTime(aRefreshStartTime)
       , mTxnStartTime(aTxnStartTime)
       , mFwdTime(aFwdTime)
       , mContainsSVGGroup(aContainsSVGGroup)
+      , mIsFirstPaint(aIsFirstPaint)
       , mUseForTelemetry(aUseForTelemetry)
     {}
     wr::Epoch mEpoch;
     TransactionId mId;
     TimeStamp mRefreshStartTime;
     TimeStamp mTxnStartTime;
     TimeStamp mFwdTime;
     bool mContainsSVGGroup;
+    bool mIsFirstPaint;
     bool mUseForTelemetry;
   };
 
   struct CompositorAnimationIdsForEpoch {
     CompositorAnimationIdsForEpoch(const wr::Epoch& aEpoch, InfallibleTArray<uint64_t>&& aIds)
       : mEpoch(aEpoch)
       , mIds(std::move(aIds))
     {
@@ -333,14 +349,15 @@ private:
   std::queue<PendingTransactionId> mPendingTransactionIds;
   std::queue<CompositorAnimationIdsForEpoch> mCompositorAnimationsToDelete;
   wr::Epoch mWrEpoch;
   wr::IdNamespace mIdNamespace;
 
   bool mPaused;
   bool mDestroyed;
   bool mReceivedDisplayList;
+  bool mIsFirstPaint;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // mozilla_layers_WebRenderBridgeParent_h