Bug 1392216 - Part 1: Create VR listener thread in GPU process; r?kip, dvander draft
authorDaosheng Mu <daoshengmu@gmail.com>
Thu, 05 Oct 2017 18:12:45 +0800
changeset 679935 2f0fc103f93611aa70272a68da98333f09199cbb
parent 678275 e897e367d3bd489422d86fbdfac54925c18329d2
child 679936 5b09a3271874b6ac88608ae866d3190239018bb5
push id84349
push userbmo:dmu@mozilla.com
push dateFri, 13 Oct 2017 07:51:03 +0000
reviewerskip, dvander
bugs1392216
milestone58.0a1
Bug 1392216 - Part 1: Create VR listener thread in GPU process; r?kip, dvander 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);