Bug 1392216 - Part 1: Create VR listener thread in GPU process; r?kip, dvander
MozReview-Commit-ID: Img0HT9ax90
--- 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);