Bug 1488980 - Implement StartFrameTimeRecording for WebRender. r=mstange
authorMatt Woodrow <mwoodrow@mozilla.com>
Sat, 08 Sep 2018 18:41:34 +0000
changeset 435342 6d3db9b4da32
parent 435341 4df1ba9c741f
child 435343 07c23fd98229
push id68978
push usermwoodrow@mozilla.com
push dateSat, 08 Sep 2018 23:28:46 +0000
treeherderautoland@6d3db9b4da32 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange
bugs1488980
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 1488980 - Implement StartFrameTimeRecording for WebRender. r=mstange MozReview-Commit-ID: Ib5t0e8S6df Differential Revision: https://phabricator.services.mozilla.com/D5103
gfx/layers/Layers.cpp
gfx/layers/Layers.h
gfx/layers/ipc/CompositorBridgeParent.cpp
gfx/layers/ipc/CompositorBridgeParent.h
gfx/layers/wr/WebRenderBridgeParent.h
gfx/layers/wr/WebRenderLayerManager.cpp
gfx/layers/wr/WebRenderLayerManager.h
gfx/webrender_bindings/RenderThread.cpp
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -1478,17 +1478,17 @@ RefLayer::FillSpecificAttributes(Specifi
  *   on each call to StartFrameTimeRecording. If this index gets overwritten,
  *   it means that all earlier start indices obtained via StartFrameTimeRecording
  *   were also overwritten, hence, no point in recording, so pause.
  * - mCurrentRunStartIndex indicates the oldest index of the recording after which
  *   the recording was not paused. If StopFrameTimeRecording is invoked with a start index
  *   older than this, it means that some frames were not recorded, so data is invalid.
  */
 uint32_t
-LayerManager::StartFrameTimeRecording(int32_t aBufferSize)
+FrameRecorder::StartFrameTimeRecording(int32_t aBufferSize)
 {
   if (mRecording.mIsPaused) {
     mRecording.mIsPaused = false;
 
     if (!mRecording.mIntervals.Length()) { // Initialize recording buffers
       mRecording.mIntervals.SetLength(aBufferSize);
     }
 
@@ -1501,17 +1501,17 @@ LayerManager::StartFrameTimeRecording(in
 
   // If we'll overwrite this index, there are no more consumers with aStartIndex
   // for which we're able to provide the full recording, so no point in keep recording.
   mRecording.mLatestStartIndex = mRecording.mNextIndex;
   return mRecording.mNextIndex;
 }
 
 void
-LayerManager::RecordFrame()
+FrameRecorder::RecordFrame()
 {
   if (!mRecording.mIsPaused) {
     TimeStamp now = TimeStamp::Now();
     uint32_t i = mRecording.mNextIndex % mRecording.mIntervals.Length();
     mRecording.mIntervals[i] = static_cast<float>((now - mRecording.mLastFrameTime)
                                                   .ToMilliseconds());
     mRecording.mNextIndex++;
     mRecording.mLastFrameTime = now;
@@ -1519,18 +1519,18 @@ LayerManager::RecordFrame()
     if (mRecording.mNextIndex > (mRecording.mLatestStartIndex + mRecording.mIntervals.Length())) {
       // We've just overwritten the most recent recording start -> pause.
       mRecording.mIsPaused = true;
     }
   }
 }
 
 void
-LayerManager::StopFrameTimeRecording(uint32_t         aStartIndex,
-                                     nsTArray<float>& aFrameIntervals)
+FrameRecorder::StopFrameTimeRecording(uint32_t         aStartIndex,
+                                      nsTArray<float>& aFrameIntervals)
 {
   uint32_t bufferSize = mRecording.mIntervals.Length();
   uint32_t length = mRecording.mNextIndex - aStartIndex;
   if (mRecording.mIsPaused || length > bufferSize || aStartIndex < mRecording.mCurrentRunStartIndex) {
     // aStartIndex is too old. Also if aStartIndex was issued before mRecordingNextIndex overflowed (uint32_t)
     //   and stopped after the overflow (would happen once every 828 days of constant 60fps).
     length = 0;
   }
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -120,16 +120,65 @@ class LayersPacket;
 // Defined in LayerUserData.h; please include that file instead.
 class LayerUserData;
 
 class DidCompositeObserver {
   public:
     virtual void DidComposite() = 0;
 };
 
+class FrameRecorder {
+public:
+  /**
+   * Record (and return) frame-intervals and paint-times for frames which were presented
+   *   between calling StartFrameTimeRecording and StopFrameTimeRecording.
+   *
+   * - Uses a cyclic buffer and serves concurrent consumers, so if Stop is called too late
+   *     (elements were overwritten since Start), result is considered invalid and hence empty.
+   * - Buffer is capable of holding 10 seconds @ 60fps (or more if frames were less frequent).
+   *     Can be changed (up to 1 hour) via pref: toolkit.framesRecording.bufferSize.
+   * - Note: the first frame-interval may be longer than expected because last frame
+   *     might have been presented some time before calling StartFrameTimeRecording.
+   */
+
+  /**
+   * Returns a handle which represents current recording start position.
+   */
+  virtual uint32_t StartFrameTimeRecording(int32_t aBufferSize);
+
+  /**
+   *  Clears, then populates aFrameIntervals with the recorded frame timing
+   *  data. The array will be empty if data was overwritten since
+   *  aStartIndex was obtained.
+   */
+  virtual void StopFrameTimeRecording(uint32_t         aStartIndex,
+                                      nsTArray<float>& aFrameIntervals);
+
+  void RecordFrame();
+private:
+  struct FramesTimingRecording
+  {
+    // Stores state and data for frame intervals and paint times recording.
+    // see LayerManager::StartFrameTimeRecording() at Layers.cpp for more details.
+    FramesTimingRecording()
+      : mNextIndex(0)
+      , mLatestStartIndex(0)
+      , mCurrentRunStartIndex(0)
+      , mIsPaused(true)
+    {}
+    nsTArray<float> mIntervals;
+    TimeStamp mLastFrameTime;
+    uint32_t mNextIndex;
+    uint32_t mLatestStartIndex;
+    uint32_t mCurrentRunStartIndex;
+    bool mIsPaused;
+  };
+  FramesTimingRecording mRecording;
+};
+
 /*
  * Motivation: For truly smooth animation and video playback, we need to
  * be able to compose frames and render them on a dedicated thread (i.e.
  * off the main thread where DOM manipulation, script execution and layout
  * induce difficult-to-bound latency). This requires Gecko to construct
  * some kind of persistent scene structure (graph or tree) that can be
  * safely transmitted across threads. We have other scenarios (e.g. mobile
  * browsing) where retaining some rendered data between paints is desired
@@ -168,17 +217,17 @@ class DidCompositeObserver {
  * order. When the client has finished drawing into the PaintedLayers, it should
  * call EndTransaction to complete the transaction.
  *
  * All layer API calls happen on the main thread.
  *
  * Layers are refcounted. The layer manager holds a reference to the
  * root layer, and each container layer holds a reference to its children.
  */
-class LayerManager {
+class LayerManager : public FrameRecorder {
   NS_INLINE_DECL_REFCOUNTING(LayerManager)
 
 protected:
   typedef mozilla::gfx::DrawTarget DrawTarget;
   typedef mozilla::gfx::IntSize IntSize;
   typedef mozilla::gfx::SurfaceFormat SurfaceFormat;
 
 public:
@@ -612,43 +661,16 @@ public:
    */
   void Log(const char* aPrefix="");
   /**
    * Log information about just this layer manager itself to the NSPR
    * log (if enabled for "Layers").
    */
   void LogSelf(const char* aPrefix="");
 
-  /**
-   * Record (and return) frame-intervals and paint-times for frames which were presented
-   *   between calling StartFrameTimeRecording and StopFrameTimeRecording.
-   *
-   * - Uses a cyclic buffer and serves concurrent consumers, so if Stop is called too late
-   *     (elements were overwritten since Start), result is considered invalid and hence empty.
-   * - Buffer is capable of holding 10 seconds @ 60fps (or more if frames were less frequent).
-   *     Can be changed (up to 1 hour) via pref: toolkit.framesRecording.bufferSize.
-   * - Note: the first frame-interval may be longer than expected because last frame
-   *     might have been presented some time before calling StartFrameTimeRecording.
-   */
-
-  /**
-   * Returns a handle which represents current recording start position.
-   */
-  virtual uint32_t StartFrameTimeRecording(int32_t aBufferSize);
-
-  /**
-   *  Clears, then populates aFrameIntervals with the recorded frame timing
-   *  data. The array will be empty if data was overwritten since
-   *  aStartIndex was obtained.
-   */
-  virtual void StopFrameTimeRecording(uint32_t         aStartIndex,
-                                      nsTArray<float>& aFrameIntervals);
-
-  void RecordFrame();
-
   static bool IsLogEnabled();
   static mozilla::LogModule* GetLog();
 
   bool IsCompositingCheap(LayersBackend aBackend)
   {
     // LayersBackend::LAYERS_NONE is an error state, but in that case we should try to
     // avoid loading the compositor!
     return LayersBackend::LAYERS_BASIC != aBackend && LayersBackend::LAYERS_NONE != aBackend;
@@ -729,35 +751,16 @@ protected:
 
   uint64_t mId;
   bool mInTransaction;
   // The time when painting most recently finished. This is recorded so that
   // we can time any play-pending animations from this point.
   TimeStamp mAnimationReadyTime;
   // The count of pixels that were painted in the current transaction.
   uint32_t mPaintedPixelCount;
-private:
-  struct FramesTimingRecording
-  {
-    // Stores state and data for frame intervals and paint times recording.
-    // see LayerManager::StartFrameTimeRecording() at Layers.cpp for more details.
-    FramesTimingRecording()
-      : mNextIndex(0)
-      , mLatestStartIndex(0)
-      , mCurrentRunStartIndex(0)
-      , mIsPaused(true)
-    {}
-    nsTArray<float> mIntervals;
-    TimeStamp mLastFrameTime;
-    uint32_t mNextIndex;
-    uint32_t mLatestStartIndex;
-    uint32_t mCurrentRunStartIndex;
-    bool mIsPaused;
-  };
-  FramesTimingRecording mRecording;
 
 public:
   /*
    * Methods to store/get/clear a "pending scroll info update" object on a
    * per-scrollid basis. This is used for empty transactions that push over
    * scroll position updates to the APZ code.
    */
   virtual bool SetPendingScrollUpdateForNextTransaction(FrameMetrics::ViewID aScrollId,
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -653,28 +653,32 @@ CompositorBridgeParent::Invalidate()
   }
 }
 
 mozilla::ipc::IPCResult
 CompositorBridgeParent::RecvStartFrameTimeRecording(const int32_t& aBufferSize, uint32_t* aOutStartIndex)
 {
   if (mLayerManager) {
     *aOutStartIndex = mLayerManager->StartFrameTimeRecording(aBufferSize);
+  } else if (mWrBridge) {
+    *aOutStartIndex = mWrBridge->StartFrameTimeRecording(aBufferSize);
   } else {
     *aOutStartIndex = 0;
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 CompositorBridgeParent::RecvStopFrameTimeRecording(const uint32_t& aStartIndex,
                                                    InfallibleTArray<float>* intervals)
 {
   if (mLayerManager) {
     mLayerManager->StopFrameTimeRecording(aStartIndex, *intervals);
+  } else if (mWrBridge) {
+    mWrBridge->StopFrameTimeRecording(aStartIndex, *intervals);
   }
   return IPC_OK();
 }
 
 void
 CompositorBridgeParent::ActorDestroy(ActorDestroyReason why)
 {
   mCanSend = false;
--- a/gfx/layers/ipc/CompositorBridgeParent.h
+++ b/gfx/layers/ipc/CompositorBridgeParent.h
@@ -503,16 +503,18 @@ public:
   static already_AddRefed<IAPZCTreeManager> GetAPZCTreeManager(LayersId aLayersId);
 
 #if defined(MOZ_WIDGET_ANDROID)
   gfx::IntSize GetEGLSurfaceSize() {
     return mEGLSurfaceSize;
   }
 #endif // defined(MOZ_WIDGET_ANDROID)
 
+  WebRenderBridgeParent* GetWrBridge() { return mWrBridge; }
+
 private:
 
   void Initialize();
 
   /**
    * Called during destruction in order to release resources as early as possible.
    */
   void StopAndClearResources();
--- a/gfx/layers/wr/WebRenderBridgeParent.h
+++ b/gfx/layers/wr/WebRenderBridgeParent.h
@@ -41,16 +41,17 @@ class CompositorAnimationStorage;
 class CompositorBridgeParentBase;
 class CompositorVsyncScheduler;
 class AsyncImagePipelineManager;
 class WebRenderImageHost;
 
 class WebRenderBridgeParent final : public PWebRenderBridgeParent
                                   , public CompositorVsyncSchedulerOwner
                                   , public CompositableParentManager
+                                  , public layers::FrameRecorder
 {
 public:
   WebRenderBridgeParent(CompositorBridgeParentBase* aCompositorBridge,
                         const wr::PipelineId& aPipelineId,
                         widget::CompositorWidget* aWidget,
                         CompositorVsyncScheduler* aScheduler,
                         RefPtr<wr::WebRenderAPI>&& aApi,
                         RefPtr<AsyncImagePipelineManager>&& aImageMgr,
--- a/gfx/layers/wr/WebRenderLayerManager.cpp
+++ b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -144,16 +144,38 @@ WebRenderLayerManager::~WebRenderLayerMa
 }
 
 CompositorBridgeChild*
 WebRenderLayerManager::GetCompositorBridgeChild()
 {
   return WrBridge()->GetCompositorBridgeChild();
 }
 
+uint32_t
+WebRenderLayerManager::StartFrameTimeRecording(int32_t aBufferSize)
+{
+  CompositorBridgeChild* renderer = GetCompositorBridgeChild();
+  if (renderer) {
+    uint32_t startIndex;
+    renderer->SendStartFrameTimeRecording(aBufferSize, &startIndex);
+    return startIndex;
+  }
+  return -1;
+}
+
+void
+WebRenderLayerManager::StopFrameTimeRecording(uint32_t         aStartIndex,
+                                              nsTArray<float>& aFrameIntervals)
+{
+  CompositorBridgeChild* renderer = GetCompositorBridgeChild();
+  if (renderer) {
+    renderer->SendStopFrameTimeRecording(aStartIndex, &aFrameIntervals);
+  }
+}
+
 bool
 WebRenderLayerManager::BeginTransactionWithTarget(gfxContext* aTarget)
 {
   mTarget = aTarget;
   return BeginTransaction();
 }
 
 bool
--- a/gfx/layers/wr/WebRenderLayerManager.h
+++ b/gfx/layers/wr/WebRenderLayerManager.h
@@ -158,16 +158,20 @@ public:
   WebRenderScrollData& GetScrollData() { return mScrollData; }
 
   void WrUpdated();
   void WindowOverlayChanged() { mWindowOverlayChanged = true; }
   nsIWidget* GetWidget() { return mWidget; }
 
   dom::TabGroup* GetTabGroup();
 
+  uint32_t StartFrameTimeRecording(int32_t aBufferSize) override;
+  void StopFrameTimeRecording(uint32_t         aStartIndex,
+                              nsTArray<float>& aFrameIntervals) override;
+
 private:
   /**
    * Take a snapshot of the parent context, and copy
    * it into mTarget.
    */
   void MakeSnapshotIfRequired(LayoutDeviceIntSize aSize);
 
 private:
--- a/gfx/webrender_bindings/RenderThread.cpp
+++ b/gfx/webrender_bindings/RenderThread.cpp
@@ -8,16 +8,17 @@
 #include "GeckoProfiler.h"
 #include "RenderThread.h"
 #include "nsThreadUtils.h"
 #include "mtransport/runnable_utils.h"
 #include "mozilla/layers/AsyncImagePipelineManager.h"
 #include "mozilla/gfx/GPUParent.h"
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/WebRenderBridgeParent.h"
 #include "mozilla/layers/SharedSurfacesParent.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/webrender/RendererOGL.h"
 #include "mozilla/webrender/RenderTextureHost.h"
 #include "mozilla/widget/CompositorWidget.h"
 
 #ifdef XP_WIN
@@ -290,16 +291,20 @@ RenderThread::RunEvent(wr::WindowId aWin
 }
 
 static void
 NotifyDidRender(layers::CompositorBridgeParent* aBridge,
                 wr::WrPipelineInfo aInfo,
                 TimeStamp aStart,
                 TimeStamp aEnd)
 {
+  if (aBridge->GetWrBridge()) {
+    aBridge->GetWrBridge()->RecordFrame();
+  }
+
   for (uintptr_t i = 0; i < aInfo.epochs.length; i++) {
     aBridge->NotifyPipelineRendered(
         aInfo.epochs.data[i].pipeline_id,
         aInfo.epochs.data[i].epoch,
         aStart,
         aEnd);
   }