Bug 1503730 - Add visual indicator for when WebRender frames record a CONTENT_FRAME_TIME of >200. r=sotaro
authorMatt Woodrow <mwoodrow@mozilla.com>
Sun, 04 Nov 2018 20:35:36 +0000
changeset 444352 d7cb17082a13ebeb1c4ba9b5907bc0bf18cc209d
parent 444351 3f3db4de78594960a7bae823a2fbe49e2ec614a9
child 444353 9412ce1e2e93f1961bbe9e2a59a0ef144320c692
push id109568
push userebalazs@mozilla.com
push dateMon, 05 Nov 2018 09:53:45 +0000
treeherdermozilla-inbound@4a54c96fade9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssotaro
bugs1503730
milestone65.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 1503730 - Add visual indicator for when WebRender frames record a CONTENT_FRAME_TIME of >200. r=sotaro MozReview-Commit-ID: KdRf3WClD6M Differential Revision: https://phabricator.services.mozilla.com/D10442
gfx/layers/wr/WebRenderBridgeParent.cpp
gfx/thebes/gfxPlatform.cpp
gfx/webrender_bindings/RenderThread.cpp
gfx/webrender_bindings/RenderThread.h
gfx/webrender_bindings/RendererOGL.cpp
gfx/webrender_bindings/RendererOGL.h
gfx/webrender_bindings/WebRenderAPI.cpp
gfx/webrender_bindings/src/bindings.rs
gfx/webrender_bindings/webrender_ffi_generated.h
modules/libpref/init/all.js
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -1803,16 +1803,19 @@ WebRenderBridgeParent::FlushTransactionI
       break;
     }
 
     if (!IsRootWebRenderBridgeParent() && !mVsyncRate.IsZero() && transactionId.mUseForTelemetry) {
       double latencyMs = (aEndTime - transactionId.mTxnStartTime).ToMilliseconds();
       double latencyNorm = latencyMs / mVsyncRate.ToMilliseconds();
       int32_t fracLatencyNorm = lround(latencyNorm * 100.0);
       Telemetry::Accumulate(Telemetry::CONTENT_FRAME_TIME, fracLatencyNorm);
+      if (fracLatencyNorm > 200) {
+        wr::RenderThread::Get()->NotifySlowFrame(mApi->GetId());
+      }
       if (transactionId.mContainsSVGGroup) {
         Telemetry::Accumulate(Telemetry::CONTENT_FRAME_TIME_WITH_SVG, fracLatencyNorm);
       }
     }
 
 #if defined(ENABLE_FRAME_LATENCY_LOG)
     if (transactionId.mRefreshStartTime) {
       int32_t latencyMs = lround((aEndTime - transactionId.mRefreshStartTime).ToMilliseconds());
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -600,16 +600,17 @@ WebRenderDebugPrefChangeCallback(const c
   GFX_WEBRENDER_DEBUG(".gpu-sample-queries", 1 << 4)
   GFX_WEBRENDER_DEBUG(".disable-batching",   1 << 5)
   GFX_WEBRENDER_DEBUG(".epochs",             1 << 6)
   GFX_WEBRENDER_DEBUG(".compact-profiler",   1 << 7)
   GFX_WEBRENDER_DEBUG(".echo-driver-messages", 1 << 8)
   GFX_WEBRENDER_DEBUG(".new-frame-indicator", 1 << 9)
   GFX_WEBRENDER_DEBUG(".new-scene-indicator", 1 << 10)
   GFX_WEBRENDER_DEBUG(".show-overdraw", 1 << 11)
+  GFX_WEBRENDER_DEBUG(".slow-frame-indicator", 1 << 13)
 #undef GFX_WEBRENDER_DEBUG
 
   gfx::gfxVars::SetWebRenderDebugFlags(flags);
 }
 
 
 #if defined(USE_SKIA)
 static uint32_t GetSkiaGlyphCacheSize()
--- a/gfx/webrender_bindings/RenderThread.cpp
+++ b/gfx/webrender_bindings/RenderThread.cpp
@@ -264,26 +264,29 @@ RenderThread::HandleFrame(wr::WindowId a
   }
 
   if (mHandlingDeviceReset) {
     return;
   }
 
   TimeStamp startTime;
 
+  bool hadSlowFrame;
   { // scope lock
     MutexAutoLock lock(mFrameCountMapLock);
     auto it = mWindowInfos.find(AsUint64(aWindowId));
     MOZ_ASSERT(it != mWindowInfos.end());
     WindowInfo* info = it->second;
     MOZ_ASSERT(info->mPendingCount > 0);
     startTime = info->mStartTimes.front();
+    hadSlowFrame = info->mHadSlowFrame;
+    info->mHadSlowFrame = false;
   }
 
-  UpdateAndRender(aWindowId, startTime, aRender, /* aReadbackSize */ Nothing(), /* aReadbackBuffer */ Nothing());
+  UpdateAndRender(aWindowId, startTime, aRender, /* aReadbackSize */ Nothing(), /* aReadbackBuffer */ Nothing(), hadSlowFrame);
   FrameRenderingComplete(aWindowId);
 }
 
 void
 RenderThread::WakeUp(wr::WindowId aWindowId)
 {
   if (mHasShutdown) {
     return;
@@ -356,32 +359,33 @@ NotifyDidRender(layers::CompositorBridge
   wr_pipeline_info_delete(aInfo);
 }
 
 void
 RenderThread::UpdateAndRender(wr::WindowId aWindowId,
                               const TimeStamp& aStartTime,
                               bool aRender,
                               const Maybe<gfx::IntSize>& aReadbackSize,
-                              const Maybe<Range<uint8_t>>& aReadbackBuffer)
+                              const Maybe<Range<uint8_t>>& aReadbackBuffer,
+                              bool aHadSlowFrame)
 {
   AUTO_PROFILER_TRACING("Paint", "Composite");
   MOZ_ASSERT(IsInRenderThread());
   MOZ_ASSERT(aRender || aReadbackBuffer.isNothing());
 
   auto it = mRenderers.find(aWindowId);
   MOZ_ASSERT(it != mRenderers.end());
   if (it == mRenderers.end()) {
     return;
   }
 
   auto& renderer = it->second;
 
   if (aRender) {
-    renderer->UpdateAndRender(aReadbackSize, aReadbackBuffer);
+    renderer->UpdateAndRender(aReadbackSize, aReadbackBuffer, aHadSlowFrame);
   } else {
     renderer->Update();
   }
   // Check graphics reset status even when rendering is skipped.
   renderer->CheckGraphicsResetStatus();
 
   TimeStamp end = TimeStamp::Now();
 
@@ -552,16 +556,29 @@ RenderThread::FrameRenderingComplete(wr:
   // point until now (when the frame is finally pushed to the screen) is
   // equivalent to the COMPOSITE_TIME metric in the non-WR codepath.
   mozilla::Telemetry::AccumulateTimeDelta(mozilla::Telemetry::COMPOSITE_TIME,
                                           info->mStartTimes.front());
   info->mStartTimes.pop();
 }
 
 void
+RenderThread::NotifySlowFrame(wr::WindowId aWindowId)
+{
+  MutexAutoLock lock(mFrameCountMapLock);
+  auto it = mWindowInfos.find(AsUint64(aWindowId));
+  if (it == mWindowInfos.end()) {
+    MOZ_ASSERT(false);
+    return;
+  }
+  WindowInfo* info = it->second;
+  info->mHadSlowFrame = true;
+}
+
+void
 RenderThread::RegisterExternalImage(uint64_t aExternalImageId, already_AddRefed<RenderTextureHost> aTexture)
 {
   MutexAutoLock lock(mRenderTextureMapLock);
 
   if (mHasShutdown) {
     return;
   }
   MOZ_ASSERT(mRenderTextures.find(aExternalImageId) == mRenderTextures.end());
--- a/gfx/webrender_bindings/RenderThread.h
+++ b/gfx/webrender_bindings/RenderThread.h
@@ -147,17 +147,22 @@ public:
 
   /// Automatically forwarded to the render thread.
   void PipelineSizeChanged(wr::WindowId aWindowId, uint64_t aPipelineId, float aWidth, float aHeight);
 
   /// Automatically forwarded to the render thread.
   void RunEvent(wr::WindowId aWindowId, UniquePtr<RendererEvent> aCallBack);
 
   /// Can only be called from the render thread.
-  void UpdateAndRender(wr::WindowId aWindowId, const TimeStamp& aStartTime, bool aRender, const Maybe<gfx::IntSize>& aReadbackSize, const Maybe<Range<uint8_t>>& aReadbackBuffer);
+  void UpdateAndRender(wr::WindowId aWindowId,
+                       const TimeStamp& aStartTime,
+                       bool aRender,
+                       const Maybe<gfx::IntSize>& aReadbackSize,
+                       const Maybe<Range<uint8_t>>& aReadbackBuffer,
+                       bool aHadSlowFrame);
 
   void Pause(wr::WindowId aWindowId);
   bool Resume(wr::WindowId aWindowId);
 
   /// Can be called from any thread.
   void RegisterExternalImage(uint64_t aExternalImageId, already_AddRefed<RenderTextureHost> aTexture);
 
   /// Can be called from any thread.
@@ -182,16 +187,18 @@ public:
   void IncPendingFrameCount(wr::WindowId aWindowId, const TimeStamp& aStartTime);
   /// Can be called from any thread.
   void DecPendingFrameCount(wr::WindowId aWindowId);
   /// Can be called from any thread.
   void IncRenderingFrameCount(wr::WindowId aWindowId);
   /// Can be called from any thread.
   void FrameRenderingComplete(wr::WindowId aWindowId);
 
+  void NotifySlowFrame(wr::WindowId aWindowId);
+
   /// Can be called from any thread.
   WebRenderThreadPool& ThreadPool() { return mThreadPool; }
 
   /// Can only be called from the render thread.
   WebRenderProgramCache* ProgramCache();
 
   /// Can only be called from the render thread.
   WebRenderShaders* Shaders() { return mShaders.get(); }
@@ -236,16 +243,17 @@ private:
 
   struct WindowInfo {
     bool mIsDestroyed = false;
     int64_t mPendingCount = 0;
     int64_t mRenderingCount = 0;
     // One entry in this queue for each pending frame, so the length
     // should always equal mPendingCount
     std::queue<TimeStamp> mStartTimes;
+    bool mHadSlowFrame = false;
   };
 
   Mutex mFrameCountMapLock;
   std::unordered_map<uint64_t, WindowInfo*> mWindowInfos;
 
   Mutex mRenderTextureMapLock;
   std::unordered_map<uint64_t, RefPtr<RenderTextureHost>> mRenderTextures;
   // Used to remove all RenderTextureHost that are going to be removed by
--- a/gfx/webrender_bindings/RendererOGL.cpp
+++ b/gfx/webrender_bindings/RendererOGL.cpp
@@ -101,17 +101,17 @@ RendererOGL::Update()
 
 static void
 DoNotifyWebRenderContextPurge(layers::CompositorBridgeParent* aBridge)
 {
   aBridge->NotifyWebRenderContextPurge();
 }
 
 bool
-RendererOGL::UpdateAndRender(const Maybe<gfx::IntSize>& aReadbackSize, const Maybe<Range<uint8_t>>& aReadbackBuffer)
+RendererOGL::UpdateAndRender(const Maybe<gfx::IntSize>& aReadbackSize, const Maybe<Range<uint8_t>>& aReadbackBuffer, bool aHadSlowFrame)
 {
   uint32_t flags = gfx::gfxVars::WebRenderDebugFlags();
   // Disable debug flags during readback
   if (aReadbackBuffer.isSome()) {
     flags = 0;
   }
 
   if (mDebugFlags.mBits != flags) {
@@ -138,17 +138,17 @@ RendererOGL::UpdateAndRender(const Maybe
   if (!mCompositor->BeginFrame()) {
     return false;
   }
 
   wr_renderer_update(mRenderer);
 
   auto size = mCompositor->GetBufferSize();
 
-  if (!wr_renderer_render(mRenderer, size.width, size.height)) {
+  if (!wr_renderer_render(mRenderer, size.width, size.height, aHadSlowFrame)) {
     NotifyWebRenderError(WebRenderError::RENDER);
   }
 
   if (aReadbackBuffer.isSome()) {
     MOZ_ASSERT(aReadbackSize.isSome());
     wr_renderer_readback(mRenderer,
                          aReadbackSize.ref().width, aReadbackSize.ref().height,
                          &aReadbackBuffer.ref()[0],
--- a/gfx/webrender_bindings/RendererOGL.h
+++ b/gfx/webrender_bindings/RendererOGL.h
@@ -52,17 +52,17 @@ class RendererOGL
 
 public:
   wr::WrExternalImageHandler GetExternalImageHandler();
 
   /// This can be called on the render thread only.
   void Update();
 
   /// This can be called on the render thread only.
-  bool UpdateAndRender(const Maybe<gfx::IntSize>& aReadbackSize, const Maybe<Range<uint8_t>>& aReadbackBuffer);
+  bool UpdateAndRender(const Maybe<gfx::IntSize>& aReadbackSize, const Maybe<Range<uint8_t>>& aReadbackBuffer, bool aHadSlowFrame);
 
   /// This can be called on the render thread only.
   bool RenderToTarget(gfx::DrawTarget& aTarget);
 
   /// This can be called on the render thread only.
   void SetProfilerEnabled(bool aEnabled);
 
   /// This can be called on the render thread only.
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -420,17 +420,17 @@ WebRenderAPI::Readback(const TimeStamp& 
 
             ~Readback()
             {
                 MOZ_COUNT_DTOR(Readback);
             }
 
             virtual void Run(RenderThread& aRenderThread, WindowId aWindowId) override
             {
-                aRenderThread.UpdateAndRender(aWindowId, mStartTime, /* aRender */ true, Some(mSize), Some(mBuffer));
+                aRenderThread.UpdateAndRender(aWindowId, mStartTime, /* aRender */ true, Some(mSize), Some(mBuffer), false);
                 layers::AutoCompleteTask complete(mTask);
             }
 
             layers::SynchronousTask* mTask;
             TimeStamp mStartTime;
             gfx::IntSize mSize;
             const Range<uint8_t>& mBuffer;
     };
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -592,17 +592,21 @@ pub extern "C" fn wr_renderer_set_extern
 #[no_mangle]
 pub extern "C" fn wr_renderer_update(renderer: &mut Renderer) {
     renderer.update();
 }
 
 #[no_mangle]
 pub extern "C" fn wr_renderer_render(renderer: &mut Renderer,
                                      width: u32,
-                                     height: u32) -> bool {
+                                     height: u32,
+                                     had_slow_frame: bool) -> bool {
+    if had_slow_frame {
+      renderer.notify_slow_frame();
+    }
     match renderer.render(DeviceUintSize::new(width, height)) {
         Ok(_) => true,
         Err(errors) => {
             for e in errors {
                 warn!(" Failed to render: {:?}", e);
                 let msg = CString::new(format!("wr_renderer_render: {:?}", e)).unwrap();
                 unsafe {
                     gfx_critical_note(msg.as_ptr());
--- a/gfx/webrender_bindings/webrender_ffi_generated.h
+++ b/gfx/webrender_bindings/webrender_ffi_generated.h
@@ -1626,17 +1626,18 @@ void wr_renderer_readback(Renderer *aRen
                           uint32_t aHeight,
                           uint8_t *aDstBuffer,
                           uintptr_t aBufferSize)
 WR_FUNC;
 
 WR_INLINE
 bool wr_renderer_render(Renderer *aRenderer,
                         uint32_t aWidth,
-                        uint32_t aHeight)
+                        uint32_t aHeight,
+                        bool aHadSlowFrame)
 WR_FUNC;
 
 WR_INLINE
 void wr_renderer_set_debug_flags(Renderer *aRenderer,
                                  WrDebugFlags aFlags)
 WR_FUNC;
 
 WR_INLINE
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -934,16 +934,17 @@ pref("gfx.webrender.debug.gpu-time-queri
 pref("gfx.webrender.debug.gpu-sample-queries", false);
 pref("gfx.webrender.debug.disable-batching", false);
 pref("gfx.webrender.debug.epochs", false);
 pref("gfx.webrender.debug.compact-profiler", false);
 pref("gfx.webrender.debug.echo-driver-messages", false);
 pref("gfx.webrender.debug.new-frame-indicator", false);
 pref("gfx.webrender.debug.new-scene-indicator", false);
 pref("gfx.webrender.debug.show-overdraw", false);
+pref("gfx.webrender.debug.slow-frame-indicator", false);
 pref("gfx.webrender.dl.dump-parent", false);
 pref("gfx.webrender.dl.dump-content", false);
 
 pref("accessibility.browsewithcaret", false);
 pref("accessibility.warn_on_browsewithcaret", true);
 
 pref("accessibility.browsewithcaret_shortcut.enabled", true);