Bug 1392216 - Part 1: Create VR listener thread in GPU process; r=dvander,kip
authorDaosheng Mu <daoshengmu@gmail.com>
Thu, 05 Oct 2017 18:12:45 +0800
changeset 386492 2db53725248967c97567665705ff0eca53b9ea6d
parent 386491 76176850979d07028e80014ecbf6c51358c29b1b
child 386493 6c06218d7c7bfd85fdd1871c948903ae08c185f6
push id53420
push userdmu@mozilla.com
push dateTue, 17 Oct 2017 01:24:23 +0000
treeherderautoland@bf730db86e26 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdvander, kip
bugs1392216
milestone58.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 1392216 - Part 1: Create VR listener thread in GPU process; r=dvander,kip MozReview-Commit-ID: Img0HT9ax90
gfx/ipc/GPUParent.cpp
gfx/layers/ipc/CompositorVsyncScheduler.cpp
gfx/layers/ipc/CompositorVsyncScheduler.h
gfx/thebes/gfxPlatform.cpp
gfx/vr/VRThread.cpp
gfx/vr/VRThread.h
gfx/vr/moz.build
xpcom/threads/nsThreadUtils.h
--- a/gfx/ipc/GPUParent.cpp
+++ b/gfx/ipc/GPUParent.cpp
@@ -32,16 +32,17 @@
 #include "mozilla/layers/MemoryReportingMLGPU.h"
 #include "mozilla/webrender/RenderThread.h"
 #include "nsDebugImpl.h"
 #include "nsThreadManager.h"
 #include "prenv.h"
 #include "ProcessUtils.h"
 #include "VRManager.h"
 #include "VRManagerParent.h"
+#include "VRThread.h"
 #include "VsyncBridgeParent.h"
 #if defined(XP_WIN)
 # include "mozilla/gfx/DeviceManagerDx.h"
 # include <process.h>
 # include <dwrite.h>
 #endif
 #ifdef MOZ_WIDGET_GTK
 # include <gtk/gtk.h>
@@ -115,16 +116,18 @@ GPUParent::Init(base::ProcessId aParentP
   DeviceManagerDx::Init();
 #endif
 
   if (NS_FAILED(NS_InitMinimalXPCOM())) {
     return false;
   }
 
   CompositorThreadHolder::Start();
+  // TODO: Bug 1406327, Start VRListenerThreadHolder when loading VR content.
+  VRListenerThreadHolder::Start();
   APZThreadUtils::SetControllerThread(CompositorThreadHolder::Loop());
   APZCTreeManager::InitializeGlobalState();
   LayerTreeOwnerTracker::Initialize();
   mozilla::ipc::SetThisProcessName("GPU Process");
 #ifdef XP_WIN
   wmf::MFStartup();
 #endif
   return true;
@@ -426,16 +429,17 @@ GPUParent::ActorDestroy(ActorDestroyReas
 #endif
 
   if (mVsyncBridge) {
     mVsyncBridge->Shutdown();
     mVsyncBridge = nullptr;
   }
   dom::VideoDecoderManagerParent::ShutdownVideoBridge();
   CompositorThreadHolder::Shutdown();
+  VRListenerThreadHolder::Shutdown();
   if (gfxVars::UseWebRender()) {
     wr::RenderThread::ShutDown();
   }
   Factory::ShutDown();
 #if defined(XP_WIN)
   DeviceManagerDx::Shutdown();
 #endif
   LayerTreeOwnerTracker::Shutdown();
--- a/gfx/layers/ipc/CompositorVsyncScheduler.cpp
+++ b/gfx/layers/ipc/CompositorVsyncScheduler.cpp
@@ -30,16 +30,17 @@
 #include "nsThreadUtils.h"              // for NS_IsMainThread
 #include "mozilla/Telemetry.h"
 #include "mozilla/VsyncDispatcher.h"
 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
 #include "VsyncSource.h"
 #endif
 #include "mozilla/widget/CompositorWidget.h"
 #include "VRManager.h"
+#include "VRThread.h"
 
 namespace mozilla {
 
 namespace layers {
 
 using namespace mozilla::gfx;
 using namespace std;
 
@@ -78,16 +79,18 @@ CompositorVsyncScheduler::CompositorVsyn
   , mIsObservingVsync(false)
   , mNeedsComposite(0)
   , mVsyncNotificationsSkipped(0)
   , mWidget(aWidget)
   , mCurrentCompositeTaskMonitor("CurrentCompositeTaskMonitor")
   , mCurrentCompositeTask(nullptr)
   , mSetNeedsCompositeMonitor("SetNeedsCompositeMonitor")
   , mSetNeedsCompositeTask(nullptr)
+  , mCurrentVRListenerTaskMonitor("CurrentVRTaskMonitor")
+  , mCurrentVRListenerTask(nullptr)
 {
   mVsyncObserver = new Observer(this);
 
   // mAsapScheduling is set on the main thread during init,
   // but is only accessed after on the compositor thread.
   mAsapScheduling = gfxPrefs::LayersCompositionFrameRate() == 0 ||
                     gfxPlatform::IsInLayoutAsapMode();
 }
@@ -125,16 +128,26 @@ CompositorVsyncScheduler::PostCompositeT
     RefPtr<CancelableRunnable> task = NewCancelableRunnableMethod<TimeStamp>(
       "layers::CompositorVsyncScheduler::Composite",
       this,
       &CompositorVsyncScheduler::Composite,
       aCompositeTimestamp);
     mCurrentCompositeTask = task;
     ScheduleTask(task.forget(), 0);
   }
+  if (mCurrentVRListenerTask == nullptr && VRListenerThreadHolder::Loop()) {
+    RefPtr<CancelableRunnable> task = NewCancelableRunnableMethod<TimeStamp>(
+      "layers::CompositorVsyncScheduler::DispatchVREvents",
+      this,
+      &CompositorVsyncScheduler::DispatchVREvents,
+      aCompositeTimestamp);
+    mCurrentVRListenerTask = task;
+    MOZ_ASSERT(VRListenerThreadHolder::Loop());
+    VRListenerThreadHolder::Loop()->PostDelayedTask(Move(task.forget()), 0);
+  }
 }
 
 void
 CompositorVsyncScheduler::ScheduleComposition()
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   if (!mVsyncObserver) {
     // Destroy was already called on this object.
@@ -249,17 +262,16 @@ CompositorVsyncScheduler::Composite(Time
   if (!mAsapScheduling && mVsyncSchedulerOwner->IsPendingComposite()) {
     // If previous composite is still on going, finish it and does a next
     // composite in a next vsync.
     mVsyncSchedulerOwner->FinishPendingComposite();
     return;
   }
 
   DispatchTouchEvents(aVsyncTimestamp);
-  DispatchVREvents(aVsyncTimestamp);
 
   if (mNeedsComposite || mAsapScheduling) {
     mNeedsComposite = 0;
     mLastCompose = aVsyncTimestamp;
     ComposeToTarget(nullptr);
     mVsyncNotificationsSkipped = 0;
 
     TimeDuration compositeFrameTotal = TimeStamp::Now() - aVsyncTimestamp;
@@ -322,17 +334,21 @@ CompositorVsyncScheduler::UnobserveVsync
 void
 CompositorVsyncScheduler::DispatchTouchEvents(TimeStamp aVsyncTimestamp)
 {
 }
 
 void
 CompositorVsyncScheduler::DispatchVREvents(TimeStamp aVsyncTimestamp)
 {
-  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  MOZ_ASSERT(VRListenerThreadHolder::IsInVRListenerThread());
+  {
+    MonitorAutoLock lock(mCurrentVRListenerTaskMonitor);
+    mCurrentVRListenerTask = nullptr;
+  }
 
   VRManager* vm = VRManager::Get();
   vm->NotifyVsync(aVsyncTimestamp);
 }
 
 void
 CompositorVsyncScheduler::ScheduleTask(already_AddRefed<CancelableRunnable> aTask,
                                        int aTime)
--- a/gfx/layers/ipc/CompositorVsyncScheduler.h
+++ b/gfx/layers/ipc/CompositorVsyncScheduler.h
@@ -112,14 +112,17 @@ private:
   widget::CompositorWidget* mWidget;
   RefPtr<CompositorVsyncScheduler::Observer> mVsyncObserver;
 
   mozilla::Monitor mCurrentCompositeTaskMonitor;
   RefPtr<CancelableRunnable> mCurrentCompositeTask;
 
   mozilla::Monitor mSetNeedsCompositeMonitor;
   RefPtr<CancelableRunnable> mSetNeedsCompositeTask;
+
+  mozilla::Monitor mCurrentVRListenerTaskMonitor;
+  RefPtr<CancelableRunnable> mCurrentVRListenerTask;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // mozilla_layers_CompositorVsyncScheduler_h
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -23,16 +23,17 @@
 #include "gfxCrashReporterUtils.h"
 #include "gfxPlatform.h"
 #include "gfxPrefs.h"
 #include "gfxEnv.h"
 #include "gfxTextRun.h"
 #include "gfxUserFontSet.h"
 #include "gfxConfig.h"
 #include "MediaPrefs.h"
+#include "VRThread.h"
 
 #ifdef XP_WIN
 #include <process.h>
 #define getpid _getpid
 #else
 #include <unistd.h>
 #endif
 
@@ -1033,16 +1034,17 @@ gfxPlatform::InitLayersIPC()
       layers::PaintThread::Start();
     }
   } else if (XRE_IsParentProcess()) {
     if (gfxVars::UseWebRender()) {
       wr::RenderThread::Start();
     }
 
     layers::CompositorThreadHolder::Start();
+    gfx::VRListenerThreadHolder::Start();
   }
 }
 
 /* static */ void
 gfxPlatform::ShutdownLayersIPC()
 {
     if (!sLayersIPCIsUp) {
       return;
@@ -1061,16 +1063,17 @@ gfxPlatform::ShutdownLayersIPC()
           layers::PaintThread::Shutdown();
         }
     } else if (XRE_IsParentProcess()) {
         gfx::VRManagerChild::ShutDown();
         layers::CompositorManagerChild::Shutdown();
         layers::ImageBridgeChild::ShutDown();
         // This has to happen after shutting down the child protocols.
         layers::CompositorThreadHolder::Shutdown();
+        gfx::VRListenerThreadHolder::Shutdown();
         if (gfxVars::UseWebRender()) {
           wr::RenderThread::ShutDown();
 
           Preferences::UnregisterCallback(WebRenderDebugPrefChangeCallback, WR_DEBUG_PREF);
         }
 
     } else {
       // TODO: There are other kind of processes and we should make sure gfx
new file mode 100644
--- /dev/null
+++ b/gfx/vr/VRThread.cpp
@@ -0,0 +1,123 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * 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/. */
+
+
+#include "VRThread.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+
+namespace gfx {
+
+static StaticRefPtr<VRListenerThreadHolder> sVRListenerThreadHolder;
+static bool sFinishedVRListenerShutDown = false;
+
+VRListenerThreadHolder* GetVRListenerThreadHolder()
+{
+  return sVRListenerThreadHolder;
+}
+
+base::Thread*
+VRListenerThread()
+{
+  return sVRListenerThreadHolder
+         ? sVRListenerThreadHolder->GetThread()
+         : nullptr;
+}
+
+/* static */ MessageLoop*
+VRListenerThreadHolder::Loop()
+{
+  return VRListenerThread() ? VRListenerThread()->message_loop() : nullptr;
+}
+
+VRListenerThreadHolder*
+VRListenerThreadHolder::GetSingleton()
+{
+  return sVRListenerThreadHolder;
+}
+
+VRListenerThreadHolder::VRListenerThreadHolder()
+ : mThread(CreateThread())
+{
+  MOZ_ASSERT(NS_IsMainThread());
+}
+
+VRListenerThreadHolder::~VRListenerThreadHolder()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  DestroyThread(mThread);
+}
+
+/* static */ void
+VRListenerThreadHolder::DestroyThread(base::Thread* aThread)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!sVRListenerThreadHolder,
+             "We shouldn't be destroying the VR listener thread yet.");
+  delete aThread;
+  sFinishedVRListenerShutDown = true;
+}
+
+/* static */ base::Thread*
+VRListenerThreadHolder::CreateThread()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!sVRListenerThreadHolder, "The VR listener thread has already been started!");
+
+  base::Thread* vrThread = new base::Thread("VRListener");
+  base::Thread::Options options;
+  /* Timeout values are powers-of-two to enable us get better data.
+     128ms is chosen for transient hangs because 8Hz should be the minimally
+     acceptable goal for Compositor responsiveness (normal goal is 60Hz). */
+  options.transient_hang_timeout = 128; // milliseconds
+  /* 2048ms is chosen for permanent hangs because it's longer than most
+   * Compositor hangs seen in the wild, but is short enough to not miss getting
+   * native hang stacks. */
+  options.permanent_hang_timeout = 2048; // milliseconds
+
+  if (!vrThread->StartWithOptions(options)) {
+    delete vrThread;
+    return nullptr;
+  }
+
+  return vrThread;
+}
+
+void
+VRListenerThreadHolder::Start()
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Should be on the main thread!");
+  MOZ_ASSERT(!sVRListenerThreadHolder, "The VR listener thread has already been started!");
+
+  sVRListenerThreadHolder = new VRListenerThreadHolder();
+}
+
+void
+VRListenerThreadHolder::Shutdown()
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Should be on the main thread!");
+  MOZ_ASSERT(sVRListenerThreadHolder, "The VR listener thread has already been shut down!");
+
+  sVRListenerThreadHolder = nullptr;
+
+  SpinEventLoopUntil([&]() { return sFinishedVRListenerShutDown; });
+}
+
+/* static */ bool
+VRListenerThreadHolder::IsInVRListenerThread()
+{
+  return VRListenerThread() &&
+		 VRListenerThread()->thread_id() == PlatformThread::CurrentId();
+}
+
+} // namespace gfx
+} // namespace mozilla
+
+bool
+NS_IsInVRListenerThread()
+{
+  return mozilla::gfx::VRListenerThreadHolder::IsInVRListenerThread();
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/gfx/vr/VRThread.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * 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 GFX_VR_THREAD_H
+ #define GFX_VR_THREAD_H
+
+#include "ThreadSafeRefcountingWithMainThreadDestruction.h"
+#include "base/thread.h"                // for Thread
+
+namespace mozilla {
+namespace gfx {
+
+class VRListenerThreadHolder final
+{
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(VRListenerThreadHolder)
+
+public:
+  VRListenerThreadHolder();
+
+  base::Thread* GetThread() const {
+    return mThread;
+  }
+
+  static VRListenerThreadHolder* GetSingleton();
+
+  static bool IsActive() {
+    return !!GetSingleton();
+  }
+
+  static void Start();
+  static void Shutdown();
+  static MessageLoop* Loop();
+  static bool IsInVRListenerThread();
+
+private:
+  ~VRListenerThreadHolder();
+
+  base::Thread* const mThread;
+
+  static base::Thread* CreateThread();
+  static void DestroyThread(base::Thread* aThread);
+};
+
+base::Thread* VRListenerThread();
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif // GFX_VR_THREAD_H
\ No newline at end of file
--- a/gfx/vr/moz.build
+++ b/gfx/vr/moz.build
@@ -8,16 +8,17 @@ EXPORTS += [
     'gfxVR.h',
     'ipc/VRLayerChild.h',
     'ipc/VRManagerChild.h',
     'ipc/VRManagerParent.h',
     'ipc/VRMessageUtils.h',
     'VRDisplayClient.h',
     'VRDisplayPresentation.h',
     'VRManager.h',
+    'VRThread.h',
 ]
 
 LOCAL_INCLUDES += [
     '/dom/base',
     '/gfx/layers/d3d11',
     '/gfx/thebes',
 ]
 
@@ -26,16 +27,17 @@ UNIFIED_SOURCES += [
     'gfxVROSVR.cpp',
     'ipc/VRLayerChild.cpp',
     'ipc/VRLayerParent.cpp',
     'ipc/VRManagerChild.cpp',
     'ipc/VRManagerParent.cpp',
     'VRDisplayClient.cpp',
     'VRDisplayPresentation.cpp',
     'VRManager.cpp',
+    'VRThread.cpp',
 ]
 
 # VRDisplayHost includes MacIOSurface.h which includes Mac headers
 # which define Size and Points types in the root namespace that
 # often conflict with our own types.
 SOURCES += [
     'gfxVRPuppet.cpp',
     'VRDisplayHost.cpp',
--- a/xpcom/threads/nsThreadUtils.h
+++ b/xpcom/threads/nsThreadUtils.h
@@ -339,16 +339,18 @@ SpinEventLoopUntil(Pred&& aPredicate, ns
  * Returns true if we're in the compositor thread.
  *
  * We declare this here because the headers required to invoke
  * CompositorThreadHolder::IsInCompositorThread() also pull in a bunch of system
  * headers that #define various tokens in a way that can break the build.
  */
 extern bool NS_IsInCompositorThread();
 
+extern bool NS_IsInVRThread();
+
 //-----------------------------------------------------------------------------
 // Helpers that work with nsCOMPtr:
 
 inline already_AddRefed<nsIThread>
 do_GetCurrentThread()
 {
   nsIThread* thread = nullptr;
   NS_GetCurrentThread(&thread);