Bug 1465466 Part 2 - Synchronously composite into a graphics shmem buffer when recording/replaying, r=nical.
authorBrian Hackett <bhackett1024@gmail.com>
Sun, 22 Jul 2018 12:02:26 +0000
changeset 427747 7e4f6f3985d0caf63dd852f26dec16156782caa2
parent 427746 a0ee9e803ff24c59245322da5c2f692d803300e1
child 427748 1ac6543017416f0899dbac1c785cc2b48637b900
push id34314
push usercsabou@mozilla.com
push dateMon, 23 Jul 2018 09:31:12 +0000
treeherdermozilla-central@143984185dce [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnical
bugs1465466
milestone63.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 1465466 Part 2 - Synchronously composite into a graphics shmem buffer when recording/replaying, r=nical.
dom/ipc/TabChild.h
gfx/layers/ipc/CompositorBridgeParent.h
gfx/layers/ipc/LayerTransactionParent.cpp
gfx/layers/ipc/ShadowLayers.cpp
toolkit/recordreplay/ipc/ChildIPC.cpp
toolkit/recordreplay/ipc/ChildIPC.h
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -654,16 +654,18 @@ public:
                   const FrameMetrics::ViewID& aViewId,
                   const CSSRect& aRect,
                   const uint32_t& aFlags);
 
   // Request that the docshell be marked as active.
   void PaintWhileInterruptingJS(uint64_t aLayerObserverEpoch,
                                 bool aForceRepaint);
 
+  uint64_t LayerObserverEpoch() const { return mLayerObserverEpoch; }
+
 #if defined(XP_WIN) && defined(ACCESSIBILITY)
   uintptr_t GetNativeWindowHandle() const { return mNativeWindowHandle; }
 #endif
 
   // These methods return `true` if this TabChild is currently awaiting a
   // Large-Allocation header.
   bool StopAwaitingLargeAlloc();
   bool IsAwaitingLargeAlloc();
--- a/gfx/layers/ipc/CompositorBridgeParent.h
+++ b/gfx/layers/ipc/CompositorBridgeParent.h
@@ -166,16 +166,20 @@ public:
                            uint32_t aApzcId) override;
   bool StopSharingMetrics(FrameMetrics::ViewID aScrollId,
                           uint32_t aApzcId) override;
 
   virtual bool IsRemote() const {
     return false;
   }
 
+  virtual void ForceComposeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect = nullptr) {
+    MOZ_CRASH();
+  }
+
 protected:
   ~CompositorBridgeParentBase() override;
 
   bool mCanSend;
 
 private:
   RefPtr<CompositorManagerParent> mCompositorManager;
 };
@@ -441,17 +445,17 @@ public:
 
   /**
    * Used by the profiler to denote when a vsync occured
    */
   static void PostInsertVsyncProfilerMarker(mozilla::TimeStamp aVsyncTimestamp);
 
   widget::CompositorWidget* GetWidget() { return mWidget; }
 
-  void ForceComposeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect = nullptr);
+  virtual void ForceComposeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect = nullptr) override;
 
   PAPZCTreeManagerParent* AllocPAPZCTreeManagerParent(const LayersId& aLayersId) override;
   bool DeallocPAPZCTreeManagerParent(PAPZCTreeManagerParent* aActor) override;
 
   // Helper method so that we don't have to expose mApzcTreeManager to
   // CrossProcessCompositorBridgeParent.
   void AllocateAPZCTreeManagerParent(const MonitorAutoLock& aProofOfLayerTreeStateLock,
                                      const LayersId& aLayersId,
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -157,16 +157,22 @@ LayerTransactionParent::RecvPaintTime(co
 {
   mCompositorBridge->UpdatePaintTime(this, aPaintTime);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 LayerTransactionParent::RecvUpdate(const TransactionInfo& aInfo)
 {
+  auto guard = MakeScopeExit([&] {
+      if (recordreplay::IsRecordingOrReplaying()) {
+        recordreplay::child::NotifyPaintComplete();
+      }
+    });
+
   AUTO_PROFILER_TRACING("Paint", "LayerTransaction");
   AUTO_PROFILER_LABEL("LayerTransactionParent::RecvUpdate", GRAPHICS);
 
   TimeStamp updateStart = TimeStamp::Now();
 
   MOZ_LAYERS_LOG(("[ParentSide] received txn with %zu edits", aInfo.cset().Length()));
 
   UpdateFwdTransactionId(aInfo.fwdTransactionId());
@@ -484,16 +490,21 @@ LayerTransactionParent::RecvUpdate(const
       printf_stderr("LayerTransactionParent::RecvUpdate transaction from process %d took %f ms",
                     OtherPid(),
                     latency.ToMilliseconds());
     }
 
     mLayerManager->RecordUpdateTime((TimeStamp::Now() - updateStart).ToMilliseconds());
   }
 
+  // Compose after every update when recording/replaying.
+  if (recordreplay::IsRecordingOrReplaying()) {
+    mCompositorBridge->ForceComposeToTarget(nullptr);
+  }
+
   return IPC_OK();
 }
 
 bool
 LayerTransactionParent::SetLayerAttributes(const OpSetLayerAttributes& aOp)
 {
   Layer* layer = AsLayer(aOp.layer());
   if (!layer) {
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -762,23 +762,31 @@ ShadowLayerForwarder::EndTransaction(con
     mPaintTiming.serializeMs() = (TimeStamp::Now() - startTime.value()).ToMilliseconds();
     startTime = Some(TimeStamp::Now());
   }
 
   // We delay at the last possible minute, to give the paint thread a chance to
   // finish. If it does we don't have to delay messages at all.
   GetCompositorBridgeChild()->PostponeMessagesIfAsyncPainting();
 
+  if (recordreplay::IsRecordingOrReplaying()) {
+    recordreplay::child::NotifyPaintStart();
+  }
+
   MOZ_LAYERS_LOG(("[LayersForwarder] sending transaction..."));
   RenderTraceScope rendertrace3("Forward Transaction", "000093");
   if (!mShadowManager->SendUpdate(info)) {
     MOZ_LAYERS_LOG(("[LayersForwarder] WARNING: sending transaction failed!"));
     return false;
   }
 
+  if (recordreplay::IsRecordingOrReplaying()) {
+    recordreplay::child::WaitForPaintToComplete();
+  }
+
   if (startTime) {
     mPaintTiming.sendMs() = (TimeStamp::Now() - startTime.value()).ToMilliseconds();
     mShadowManager->SendRecordPaintTimes(mPaintTiming);
   }
 
   *aSent = true;
   mIsFirstPaint = false;
   mFocusTarget = FocusTarget();
--- a/toolkit/recordreplay/ipc/ChildIPC.cpp
+++ b/toolkit/recordreplay/ipc/ChildIPC.cpp
@@ -343,16 +343,116 @@ void
 NotifyAlwaysMarkMajorCheckpoints()
 {
   if (IsActiveChild()) {
     gChannel->SendMessage(AlwaysMarkMajorCheckpointsMessage());
   }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
+// Vsyncs
+///////////////////////////////////////////////////////////////////////////////
+
+static VsyncObserver* gVsyncObserver;
+
+void
+SetVsyncObserver(VsyncObserver* aObserver)
+{
+  MOZ_RELEASE_ASSERT(!gVsyncObserver || !aObserver);
+  gVsyncObserver = aObserver;
+}
+
+void
+NotifyVsyncObserver()
+{
+  if (gVsyncObserver) {
+    gVsyncObserver->NotifyVsync(TimeStamp::Now());
+  }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Painting
+///////////////////////////////////////////////////////////////////////////////
+
+// Graphics memory is only written on the compositor thread and read on the
+// main thread and by the middleman. The gPendingPaint flag is used to
+// synchronize access, so that data is not read until the paint has completed.
+static Maybe<PaintMessage> gPaintMessage;
+static bool gPendingPaint;
+
+// Target buffer for the draw target created by the child process widget.
+static void* gDrawTargetBuffer;
+static size_t gDrawTargetBufferSize;
+
+already_AddRefed<gfx::DrawTarget>
+DrawTargetForRemoteDrawing(LayoutDeviceIntSize aSize)
+{
+  MOZ_RELEASE_ASSERT(!NS_IsMainThread());
+
+  gPaintMessage = Some(PaintMessage(aSize.width, aSize.height));
+
+  gfx::IntSize size(aSize.width, aSize.height);
+  size_t bufferSize = layers::ImageDataSerializer::ComputeRGBBufferSize(size, gSurfaceFormat);
+  MOZ_RELEASE_ASSERT(bufferSize <= parent::GraphicsMemorySize);
+
+  if (bufferSize != gDrawTargetBufferSize) {
+    free(gDrawTargetBuffer);
+    gDrawTargetBuffer = malloc(bufferSize);
+    gDrawTargetBufferSize = bufferSize;
+  }
+
+  size_t stride = layers::ImageDataSerializer::ComputeRGBStride(gSurfaceFormat, aSize.width);
+  RefPtr<gfx::DrawTarget> drawTarget =
+    gfx::Factory::CreateDrawTargetForData(gfx::BackendType::SKIA, (uint8_t*) gDrawTargetBuffer,
+                                          size, stride, gSurfaceFormat,
+                                          /* aUninitialized = */ true);
+  if (!drawTarget) {
+    MOZ_CRASH();
+  }
+
+  return drawTarget.forget();
+}
+
+void
+NotifyPaintStart()
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+  NewCheckpoint(/* aTemporary = */ false);
+
+  gPendingPaint = true;
+}
+
+void
+WaitForPaintToComplete()
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+  MonitorAutoLock lock(*gMonitor);
+  while (gPendingPaint) {
+    gMonitor->Wait();
+  }
+  if (IsActiveChild() && gPaintMessage.isSome()) {
+    memcpy(gGraphicsShmem, gDrawTargetBuffer, gDrawTargetBufferSize);
+    gChannel->SendMessage(gPaintMessage.ref());
+  }
+}
+
+void
+NotifyPaintComplete()
+{
+  MOZ_RELEASE_ASSERT(!NS_IsMainThread());
+
+  MonitorAutoLock lock(*gMonitor);
+  MOZ_RELEASE_ASSERT(gPendingPaint);
+  gPendingPaint = false;
+  gMonitor->Notify();
+}
+
+///////////////////////////////////////////////////////////////////////////////
 // Checkpoint Messages
 ///////////////////////////////////////////////////////////////////////////////
 
 // When recording, the time when the last HitCheckpoint message was sent.
 static double gLastCheckpointTime;
 
 // When recording and we are idle, the time when we became idle.
 static double gIdleTimeStart;
--- a/toolkit/recordreplay/ipc/ChildIPC.h
+++ b/toolkit/recordreplay/ipc/ChildIPC.h
@@ -3,18 +3,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_recordreplay_ChildIPC_h
 #define mozilla_recordreplay_ChildIPC_h
 
 #include "base/process.h"
+#include "mozilla/gfx/2D.h"
+#include "Units.h"
 
 namespace mozilla {
+
+class VsyncObserver;
+
 namespace recordreplay {
 namespace child {
 
 // Naively replaying a child process execution will not perform any IPC. When
 // the replaying process attempts to make system calls that communicate with
 // the parent, function redirections are invoked that simply replay the values
 // which those calls produced in the original recording.
 //
@@ -37,16 +42,26 @@ namespace child {
 void InitRecordingOrReplayingProcess(int* aArgc, char*** aArgv);
 
 // Get the contents of the prefs shmem as conveyed to the middleman process.
 char* PrefsShmemContents(size_t aPrefsLen);
 
 base::ProcessId MiddlemanProcessId();
 base::ProcessId ParentProcessId();
 
+void SetVsyncObserver(VsyncObserver* aObserver);
+void NotifyVsyncObserver();
+
+void NotifyPaint();
+void NotifyPaintStart();
+void NotifyPaintComplete();
+void WaitForPaintToComplete();
+
+already_AddRefed<gfx::DrawTarget> DrawTargetForRemoteDrawing(LayoutDeviceIntSize aSize);
+
 // Notify the middleman that the recording was flushed.
 void NotifyFlushedRecording();
 
 // Notify the middleman about an AlwaysMarkMajorCheckpoints directive.
 void NotifyAlwaysMarkMajorCheckpoints();
 
 // Report a fatal error to the middleman process.
 void ReportFatalError(const char* aFormat, ...);