diff --git a/gfx/ipc/GPUParent.cpp b/gfx/ipc/GPUParent.cpp index 3ad96ae72880..1a60cd23adbe 100644 --- a/gfx/ipc/GPUParent.cpp +++ b/gfx/ipc/GPUParent.cpp @@ -37,6 +37,7 @@ #include "ProcessUtils.h" #include "VRManager.h" #include "VRManagerParent.h" +#include "VRThread.h" #include "VsyncBridgeParent.h" #if defined(XP_WIN) # include "mozilla/gfx/DeviceManagerDx.h" @@ -120,6 +121,8 @@ GPUParent::Init(base::ProcessId aParentPid, } CompositorThreadHolder::Start(); + // TODO: Bug 1406327, Start VRListenerThreadHolder when loading VR content. + VRListenerThreadHolder::Start(); APZThreadUtils::SetControllerThread(CompositorThreadHolder::Loop()); APZCTreeManager::InitializeGlobalState(); LayerTreeOwnerTracker::Initialize(); @@ -431,6 +434,7 @@ GPUParent::ActorDestroy(ActorDestroyReason aWhy) } dom::VideoDecoderManagerParent::ShutdownVideoBridge(); CompositorThreadHolder::Shutdown(); + VRListenerThreadHolder::Shutdown(); if (gfxVars::UseWebRender()) { wr::RenderThread::ShutDown(); } diff --git a/gfx/layers/ipc/CompositorVsyncScheduler.cpp b/gfx/layers/ipc/CompositorVsyncScheduler.cpp index 829087efd1ee..6ab1abd511f1 100644 --- a/gfx/layers/ipc/CompositorVsyncScheduler.cpp +++ b/gfx/layers/ipc/CompositorVsyncScheduler.cpp @@ -35,6 +35,7 @@ #endif #include "mozilla/widget/CompositorWidget.h" #include "VRManager.h" +#include "VRThread.h" namespace mozilla { @@ -83,6 +84,8 @@ CompositorVsyncScheduler::CompositorVsyncScheduler(CompositorVsyncSchedulerOwner , mCurrentCompositeTask(nullptr) , mSetNeedsCompositeMonitor("SetNeedsCompositeMonitor") , mSetNeedsCompositeTask(nullptr) + , mCurrentVRListenerTaskMonitor("CurrentVRTaskMonitor") + , mCurrentVRListenerTask(nullptr) { mVsyncObserver = new Observer(this); @@ -130,6 +133,16 @@ CompositorVsyncScheduler::PostCompositeTask(TimeStamp aCompositeTimestamp) mCurrentCompositeTask = task; ScheduleTask(task.forget(), 0); } + if (mCurrentVRListenerTask == nullptr && VRListenerThreadHolder::Loop()) { + RefPtr task = NewCancelableRunnableMethod( + "layers::CompositorVsyncScheduler::DispatchVREvents", + this, + &CompositorVsyncScheduler::DispatchVREvents, + aCompositeTimestamp); + mCurrentVRListenerTask = task; + MOZ_ASSERT(VRListenerThreadHolder::Loop()); + VRListenerThreadHolder::Loop()->PostDelayedTask(Move(task.forget()), 0); + } } void @@ -254,7 +267,6 @@ CompositorVsyncScheduler::Composite(TimeStamp aVsyncTimestamp) } DispatchTouchEvents(aVsyncTimestamp); - DispatchVREvents(aVsyncTimestamp); if (mNeedsComposite || mAsapScheduling) { mNeedsComposite = 0; @@ -327,7 +339,11 @@ 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); diff --git a/gfx/layers/ipc/CompositorVsyncScheduler.h b/gfx/layers/ipc/CompositorVsyncScheduler.h index 2354cce7db06..0d45a1ef3d40 100644 --- a/gfx/layers/ipc/CompositorVsyncScheduler.h +++ b/gfx/layers/ipc/CompositorVsyncScheduler.h @@ -117,6 +117,9 @@ private: mozilla::Monitor mSetNeedsCompositeMonitor; RefPtr mSetNeedsCompositeTask; + + mozilla::Monitor mCurrentVRListenerTaskMonitor; + RefPtr mCurrentVRListenerTask; }; } // namespace layers diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index 8f450f2e3602..2496550dade1 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -28,6 +28,7 @@ #include "gfxUserFontSet.h" #include "gfxConfig.h" #include "MediaPrefs.h" +#include "VRThread.h" #ifdef XP_WIN #include @@ -1038,6 +1039,7 @@ gfxPlatform::InitLayersIPC() } layers::CompositorThreadHolder::Start(); + gfx::VRListenerThreadHolder::Start(); } } @@ -1066,6 +1068,7 @@ gfxPlatform::ShutdownLayersIPC() 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(); diff --git a/gfx/vr/VRThread.cpp b/gfx/vr/VRThread.cpp new file mode 100644 index 000000000000..63dad4c85d17 --- /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 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 diff --git a/gfx/vr/VRThread.h b/gfx/vr/VRThread.h new file mode 100644 index 000000000000..9faff13f033c --- /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 diff --git a/gfx/vr/moz.build b/gfx/vr/moz.build index 5fb120f210af..a973f0c60344 100644 --- a/gfx/vr/moz.build +++ b/gfx/vr/moz.build @@ -13,6 +13,7 @@ EXPORTS += [ 'VRDisplayClient.h', 'VRDisplayPresentation.h', 'VRManager.h', + 'VRThread.h', ] LOCAL_INCLUDES += [ @@ -31,6 +32,7 @@ UNIFIED_SOURCES += [ 'VRDisplayClient.cpp', 'VRDisplayPresentation.cpp', 'VRManager.cpp', + 'VRThread.cpp', ] # VRDisplayHost includes MacIOSurface.h which includes Mac headers diff --git a/xpcom/threads/nsThreadUtils.h b/xpcom/threads/nsThreadUtils.h index b47ac3652e77..0236ee388cab 100644 --- a/xpcom/threads/nsThreadUtils.h +++ b/xpcom/threads/nsThreadUtils.h @@ -344,6 +344,8 @@ SpinEventLoopUntil(Pred&& aPredicate, nsIThread* aThread = nullptr) */ extern bool NS_IsInCompositorThread(); +extern bool NS_IsInVRThread(); + //----------------------------------------------------------------------------- // Helpers that work with nsCOMPtr: