Bug 1514640 - Allow slow scene building to start a composite a bit late. r=jrmuizel
authorMatt Woodrow <mwoodrow@mozilla.com>
Thu, 20 Dec 2018 22:43:01 +0000
changeset 451603 2a55726134a07bdd99c9ea4b99254750ca393ff9
parent 451602 594b7dcaaf2d4c1024445521f8b2c72a120bdc33
child 451604 46ddda3930924d3a0624aec673c8975de2ac4fb2
push id35247
push userebalazs@mozilla.com
push dateFri, 21 Dec 2018 09:36:28 +0000
treeherdermozilla-central@84931341bac1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1514640
milestone66.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 1514640 - Allow slow scene building to start a composite a bit late. r=jrmuizel Differential Revision: https://phabricator.services.mozilla.com/D15020
gfx/layers/ipc/CompositorBridgeParent.cpp
gfx/layers/ipc/CompositorBridgeParent.h
gfx/layers/ipc/CompositorVsyncScheduler.cpp
gfx/layers/ipc/CompositorVsyncScheduler.h
gfx/layers/wr/WebRenderBridgeParent.cpp
gfx/layers/wr/WebRenderBridgeParent.h
gfx/thebes/gfxPrefs.h
gfx/webrender_bindings/RenderThread.cpp
gfx/webrender_bindings/src/bindings.rs
gfx/webrender_bindings/webrender_ffi_generated.h
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -2006,16 +2006,29 @@ void CompositorBridgeParent::DidComposit
     mRefreshStartTime = TimeStamp();
     mTxnStartTime = TimeStamp();
     mFwdTime = TimeStamp();
 #endif
     mPendingTransaction = TransactionId{0};
   }
 }
 
+void CompositorBridgeParent::NotifyDidSceneBuild() {
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  if (mPaused) {
+    return;
+  }
+
+  if (mWrBridge) {
+    mWrBridge->NotifyDidSceneBuild();
+  } else {
+    mCompositorScheduler->ScheduleComposition();
+  }
+}
+
 void CompositorBridgeParent::NotifyPipelineRendered(
     const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch,
     const VsyncId& aCompositeStartId, TimeStamp& aCompositeStart,
     TimeStamp& aRenderStart, TimeStamp& aCompositeEnd,
     wr::RendererStats* aStats) {
   if (!mWrBridge || !mAsyncImageManager) {
     return;
   }
--- a/gfx/layers/ipc/CompositorBridgeParent.h
+++ b/gfx/layers/ipc/CompositorBridgeParent.h
@@ -310,16 +310,17 @@ class CompositorBridgeParent final : pub
   void NotifyWebRenderError(wr::WebRenderError aError);
   void NotifyWebRenderContextPurge();
   void NotifyPipelineRendered(const wr::PipelineId& aPipelineId,
                               const wr::Epoch& aEpoch,
                               const VsyncId& aCompositeStartId,
                               TimeStamp& aCompositeStart,
                               TimeStamp& aRenderStart, TimeStamp& aCompositeEnd,
                               wr::RendererStats* aStats = nullptr);
+  void NotifyDidSceneBuild();
   RefPtr<AsyncImagePipelineManager> GetAsyncImagePipelineManager() const;
 
   PCompositorWidgetParent* AllocPCompositorWidgetParent(
       const CompositorWidgetInitData& aInitData) override;
   bool DeallocPCompositorWidgetParent(PCompositorWidgetParent* aActor) override;
 
   void ObserveLayersUpdate(LayersId aLayersId, LayersObserverEpoch aEpoch,
                            bool aActive) override {}
--- a/gfx/layers/ipc/CompositorVsyncScheduler.cpp
+++ b/gfx/layers/ipc/CompositorVsyncScheduler.cpp
@@ -61,16 +61,17 @@ void CompositorVsyncScheduler::Observer:
   mOwner = nullptr;
 }
 
 CompositorVsyncScheduler::CompositorVsyncScheduler(
     CompositorVsyncSchedulerOwner* aVsyncSchedulerOwner,
     widget::CompositorWidget* aWidget)
     : mVsyncSchedulerOwner(aVsyncSchedulerOwner),
       mLastCompose(TimeStamp::Now()),
+      mLastVsync(TimeStamp::Now()),
       mIsObservingVsync(false),
       mVsyncNotificationsSkipped(0),
       mWidget(aWidget),
       mCurrentCompositeTaskMonitor("CurrentCompositeTaskMonitor"),
       mCurrentCompositeTask(nullptr),
       mCurrentVRTaskMonitor("CurrentVRTaskMonitor"),
       mCurrentVRTask(nullptr) {
   mVsyncObserver = new Observer(this);
@@ -195,16 +196,19 @@ void CompositorVsyncScheduler::Composite
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   MOZ_ASSERT(mVsyncSchedulerOwner);
 
   {  // scope lock
     MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
     mCurrentCompositeTask = nullptr;
   }
 
+  mLastVsync = aVsyncTimestamp;
+  mLastVsyncId = aId;
+
   if (!mAsapScheduling) {
     // Some early exit conditions if we're not in ASAP mode
     if (aVsyncTimestamp < mLastCompose) {
       // We can sometimes get vsync timestamps that are in the past
       // compared to the last compose with force composites.
       // In those cases, wait until the next vsync;
       return;
     }
@@ -311,15 +315,25 @@ void CompositorVsyncScheduler::ScheduleT
   CompositorThreadHolder::Loop()->PostDelayedTask(std::move(aTask), 0);
 }
 
 const TimeStamp& CompositorVsyncScheduler::GetLastComposeTime() const {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   return mLastCompose;
 }
 
+const TimeStamp& CompositorVsyncScheduler::GetLastVsyncTime() const {
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  return mLastVsync;
+}
+
+const VsyncId& CompositorVsyncScheduler::GetLastVsyncId() const {
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  return mLastVsyncId;
+}
+
 void CompositorVsyncScheduler::UpdateLastComposeTime() {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   mLastCompose = TimeStamp::Now();
 }
 
 }  // namespace layers
 }  // namespace mozilla
--- a/gfx/layers/ipc/CompositorVsyncScheduler.h
+++ b/gfx/layers/ipc/CompositorVsyncScheduler.h
@@ -89,16 +89,23 @@ class CompositorVsyncScheduler {
 
   /**
    * Return the vsync timestamp of the last or ongoing composite. Must be called
    * on the compositor thread.
    */
   const TimeStamp& GetLastComposeTime() const;
 
   /**
+   * Return the vsync timestamp and id of the most recently received
+   * vsync event. Must be called on the compositor thread.
+   */
+  const TimeStamp& GetLastVsyncTime() const;
+  const VsyncId& GetLastVsyncId() const;
+
+  /**
    * Update LastCompose TimeStamp to current timestamp.
    * The function is typically used when composition is handled outside the
    * CompositorVsyncScheduler.
    */
   void UpdateLastComposeTime();
 
  private:
   virtual ~CompositorVsyncScheduler();
@@ -134,16 +141,18 @@ class CompositorVsyncScheduler {
 
     Mutex mMutex;
     // Hold raw pointer to avoid mutual reference.
     CompositorVsyncScheduler* mOwner;
   };
 
   CompositorVsyncSchedulerOwner* mVsyncSchedulerOwner;
   TimeStamp mLastCompose;
+  TimeStamp mLastVsync;
+  VsyncId mLastVsyncId;
 
   bool mAsapScheduling;
   bool mIsObservingVsync;
   TimeStamp mCompositeRequestedAt;
   int32_t mVsyncNotificationsSkipped;
   widget::CompositorWidget* mWidget;
   RefPtr<CompositorVsyncScheduler::Observer> mVsyncObserver;
 
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -1810,16 +1810,17 @@ void WebRenderBridgeParent::MaybeGenerat
 #if defined(ENABLE_FRAME_LATENCY_LOG)
   auto startTime = TimeStamp::Now();
   mApi->SetFrameStartTime(startTime);
 #endif
 
   fastTxn.GenerateFrame();
 
   mApi->SendTransaction(fastTxn);
+  mMostRecentComposite = TimeStamp::Now();
 }
 
 void WebRenderBridgeParent::HoldPendingTransactionId(
     const wr::Epoch& aWrEpoch, TransactionId aTransactionId,
     bool aContainsSVGGroup, const VsyncId& aVsyncId,
     const TimeStamp& aRefreshStartTime, const TimeStamp& aTxnStartTime,
     const nsCString& aTxnURL, const TimeStamp& aFwdTime,
     const bool aIsFirstPaint, const bool aUseForTelemetry) {
@@ -1842,16 +1843,36 @@ void WebRenderBridgeParent::NotifySceneB
   for (auto& id : mPendingTransactionIds) {
     if (id.mEpoch.mHandle == aEpoch.mHandle) {
       id.mSceneBuiltTime = aEndTime;
       break;
     }
   }
 }
 
+void WebRenderBridgeParent::NotifyDidSceneBuild() {
+  if (!mCompositorScheduler) {
+    return;
+  }
+
+  mAsyncImageManager->SetWillGenerateFrame();
+
+  // If the scheduler has a composite more recent than our last composite (which
+  // we missed), and we're within the threshold ms of the last vsync, then
+  // kick of a late composite.
+  TimeStamp lastVsync = mCompositorScheduler->GetLastVsyncTime();
+  if (mMostRecentComposite && mMostRecentComposite < lastVsync &&
+      ((TimeStamp::Now() - lastVsync).ToMilliseconds() <
+       gfxPrefs::WebRenderLateSceneBuildThreshold())) {
+    CompositeToTarget(mCompositorScheduler->GetLastVsyncId(), nullptr, nullptr);
+  } else {
+    mCompositorScheduler->ScheduleComposition();
+  }
+}
+
 TransactionId WebRenderBridgeParent::FlushTransactionIdsForEpoch(
     const wr::Epoch& aEpoch, const VsyncId& aCompositeStartId,
     const TimeStamp& aCompositeStartTime, const TimeStamp& aRenderStartTime,
     const TimeStamp& aEndTime, UiCompositorControllerParent* aUiController,
     wr::RendererStats* aStats, nsTArray<FrameStats>* aOutputStats) {
   TransactionId id{0};
   while (!mPendingTransactionIds.empty()) {
     const auto& transactionId = mPendingTransactionIds.front();
--- a/gfx/layers/wr/WebRenderBridgeParent.h
+++ b/gfx/layers/wr/WebRenderBridgeParent.h
@@ -220,16 +220,18 @@ class WebRenderBridgeParent final : publ
   /**
    * Schedule forced frame rendering at next composite timing.
    *
    * WebRender could skip frame rendering if there is no update.
    * This function is used to force rendering even when there is not update.
    */
   void ScheduleForcedGenerateFrame();
 
+  void NotifyDidSceneBuild();
+
   wr::Epoch UpdateWebRender(
       CompositorVsyncScheduler* aScheduler, wr::WebRenderAPI* aApi,
       AsyncImagePipelineManager* aImageMgr,
       CompositorAnimationStorage* aAnimStorage,
       const TextureFactoryIdentifier& aTextureFactoryIdentifier);
 
   void RemoveEpochDataPriorTo(const wr::Epoch& aRenderedEpoch);
 
@@ -388,16 +390,17 @@ class WebRenderBridgeParent final : publ
   LayersObserverEpoch mParentLayersObserverEpoch;
 
   std::deque<PendingTransactionId> mPendingTransactionIds;
   std::queue<CompositorAnimationIdsForEpoch> mCompositorAnimationsToDelete;
   wr::Epoch mWrEpoch;
   wr::IdNamespace mIdNamespace;
 
   VsyncId mSkippedCompositeId;
+  TimeStamp mMostRecentComposite;
 
   bool mPaused;
   bool mDestroyed;
   bool mReceivedDisplayList;
   bool mIsFirstPaint;
   bool mSkippedComposite;
 };
 
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -546,16 +546,17 @@ class gfxPrefs final {
   DECL_GFX_PREF(Live, "gfx.webrender.blob-images",             WebRenderBlobImages, bool, true);
   DECL_GFX_PREF(Live, "gfx.webrender.blob.invalidation",       WebRenderBlobInvalidation, bool, false);
   DECL_GFX_PREF(Live, "gfx.webrender.blob.paint-flashing",     WebRenderBlobPaintFlashing, bool, false);
   DECL_GFX_PREF(Live, "gfx.webrender.dl.dump-parent",          WebRenderDLDumpParent, bool, false);
   DECL_GFX_PREF(Live, "gfx.webrender.dl.dump-content",         WebRenderDLDumpContent, bool, false);
   DECL_GFX_PREF(Once, "gfx.webrender.enabled",                 WebRenderEnabledDoNotUseDirectly, bool, false);
   DECL_GFX_PREF(Once, "gfx.webrender.force-disabled",          WebRenderForceDisabled, bool, false);
   DECL_GFX_PREF(Live, "gfx.webrender.highlight-painted-layers",WebRenderHighlightPaintedLayers, bool, false);
+  DECL_GFX_PREF(Live, "gfx.webrender.late-scenebuild-threshold", WebRenderLateSceneBuildThreshold, int32_t, 4);
   DECL_GFX_PREF(Live, "gfx.webrender.picture-caching",         WebRenderPictureCaching, bool, false);
 
   // Use vsync events generated by hardware
   DECL_GFX_PREF(Once, "gfx.work-around-driver-bugs",           WorkAroundDriverBugs, bool, true);
 
   DECL_GFX_PREF(Live, "gl.ignore-dx-interop2-blacklist",       IgnoreDXInterop2Blacklist, bool, false);
   DECL_GFX_PREF(Live, "gl.msaa-level",                         MSAALevel, uint32_t, 2);
 #if defined(XP_MACOSX)
--- a/gfx/webrender_bindings/RenderThread.cpp
+++ b/gfx/webrender_bindings/RenderThread.cpp
@@ -850,17 +850,23 @@ void wr_notifier_nop_frame_done(mozilla:
 void wr_notifier_external_event(mozilla::wr::WrWindowId aWindowId,
                                 size_t aRawEvent) {
   mozilla::UniquePtr<mozilla::wr::RendererEvent> evt(
       reinterpret_cast<mozilla::wr::RendererEvent*>(aRawEvent));
   mozilla::wr::RenderThread::Get()->RunEvent(mozilla::wr::WindowId(aWindowId),
                                              std::move(evt));
 }
 
-void wr_schedule_render(mozilla::wr::WrWindowId aWindowId) {
+static void NotifyDidSceneBuild(
+    RefPtr<layers::CompositorBridgeParent> aBridge) {
+  aBridge->NotifyDidSceneBuild();
+}
+
+void wr_finished_scene_build(mozilla::wr::WrWindowId aWindowId) {
   RefPtr<mozilla::layers::CompositorBridgeParent> cbp = mozilla::layers::
       CompositorBridgeParent::GetCompositorBridgeParentFromWindowId(aWindowId);
   if (cbp) {
-    cbp->ScheduleRenderOnCompositorThread();
+    layers::CompositorThreadHolder::Loop()->PostTask(
+        NewRunnableFunction("NotifyDidSceneBuild", &NotifyDidSceneBuild, cbp));
   }
 }
 
 }  // extern C
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -537,17 +537,17 @@ struct CppNotifier {
 unsafe impl Send for CppNotifier {}
 
 extern "C" {
     fn wr_notifier_wake_up(window_id: WrWindowId);
     fn wr_notifier_new_frame_ready(window_id: WrWindowId);
     fn wr_notifier_nop_frame_done(window_id: WrWindowId);
     fn wr_notifier_external_event(window_id: WrWindowId,
                                   raw_event: usize);
-    fn wr_schedule_render(window_id: WrWindowId);
+    fn wr_finished_scene_build(window_id: WrWindowId);
 
     fn wr_transaction_notification_notified(handler: usize, when: Checkpoint);
 }
 
 impl RenderNotifier for CppNotifier {
     fn clone(&self) -> Box<RenderNotifier> {
         Box::new(CppNotifier {
             window_id: self.window_id,
@@ -787,22 +787,22 @@ impl SceneBuilderHooks for APZCallbacks 
         unsafe {
             record_telemetry_time(TelemetryProbe::SceneSwapTime, sceneswap_time);
             apz_post_scene_swap(self.window_id, info);
         }
 
         // After a scene swap we should schedule a render for the next vsync,
         // otherwise there's no guarantee that the new scene will get rendered
         // anytime soon
-        unsafe { wr_schedule_render(self.window_id) }
+        unsafe { wr_finished_scene_build(self.window_id) }
         unsafe { gecko_profiler_end_marker(b"SceneBuilding\0".as_ptr() as *const c_char); }
     }
 
     fn post_resource_update(&self) {
-        unsafe { wr_schedule_render(self.window_id) }
+        unsafe { wr_finished_scene_build(self.window_id) }
         unsafe { gecko_profiler_end_marker(b"SceneBuilding\0".as_ptr() as *const c_char); }
     }
 
     fn post_empty_scene_build(&self) {
         unsafe { gecko_profiler_end_marker(b"SceneBuilding\0".as_ptr() as *const c_char); }
     }
 
     fn poke(&self) {
--- a/gfx/webrender_bindings/webrender_ffi_generated.h
+++ b/gfx/webrender_bindings/webrender_ffi_generated.h
@@ -1620,16 +1620,18 @@ WR_FUNC;
 
 WR_INLINE
 uintptr_t wr_dump_display_list(WrState *aState,
                                uintptr_t aIndent,
                                const uintptr_t *aStart,
                                const uintptr_t *aEnd)
 WR_FUNC;
 
+extern void wr_finished_scene_build(WrWindowId aWindowId);
+
 extern bool wr_moz2d_render_cb(ByteSlice aBlob,
                                int32_t aWidth,
                                int32_t aHeight,
                                ImageFormat aFormat,
                                const uint16_t *aTileSize,
                                const TileOffset *aTileOffset,
                                const LayoutIntRect *aDirtyRect,
                                MutByteSlice aOutput);