diff --git a/gfx/thebes/SoftwareVsyncSource.cpp b/gfx/thebes/SoftwareVsyncSource.cpp new file mode 100644 index 000000000000..d313a4737fad --- /dev/null +++ b/gfx/thebes/SoftwareVsyncSource.cpp @@ -0,0 +1,116 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * 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 "SoftwareVsyncSource.h" +#include "base/task.h" +#include "nsThreadUtils.h" + +SoftwareVsyncSource::SoftwareVsyncSource() +{ + mGlobalDisplay = new SoftwareDisplay(); +} + +SoftwareVsyncSource::~SoftwareVsyncSource() +{ + MOZ_ASSERT(NS_IsMainThread()); + // Ensure we disable vsync on the main thread here + mGlobalDisplay->DisableVsync(); + mGlobalDisplay = nullptr; +} + +SoftwareDisplay::SoftwareDisplay() + : mCurrentTaskMonitor("SoftwareVsyncCurrentTaskMonitor") +{ + // Mimic 60 fps + MOZ_ASSERT(NS_IsMainThread()); + const double rate = 1000 / 60.0; + mVsyncRate = mozilla::TimeDuration::FromMilliseconds(rate); + mVsyncThread = new base::Thread("SoftwareVsyncThread"); + EnableVsync(); +} + +void +SoftwareDisplay::EnableVsync() +{ + MOZ_ASSERT(NS_IsMainThread()); + mozilla::MonitorAutoLock lock(mCurrentTaskMonitor); + mVsyncEnabled = true; + MOZ_ASSERT(!mVsyncThread->IsRunning()); + MOZ_RELEASE_ASSERT(mVsyncThread->Start(), "Could not start software vsync thread"); + mCurrentVsyncTask = NewRunnableMethod(this, + &SoftwareDisplay::NotifyVsync, + mozilla::TimeStamp::Now()); + mVsyncThread->message_loop()->PostTask(FROM_HERE, mCurrentVsyncTask); +} + +void +SoftwareDisplay::DisableVsync() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mVsyncThread->IsRunning()); + { // Scope lock + mozilla::MonitorAutoLock lock(mCurrentTaskMonitor); + mVsyncEnabled = false; + if (mCurrentVsyncTask) { + mCurrentVsyncTask->Cancel(); + mCurrentVsyncTask = nullptr; + } + } + mVsyncThread->Stop(); +} + +bool +SoftwareDisplay::IsVsyncEnabled() +{ + MOZ_ASSERT(NS_IsMainThread()); + mozilla::MonitorAutoLock lock(mCurrentTaskMonitor); + return mVsyncEnabled; +} + +bool +SoftwareDisplay::IsInSoftwareVsyncThread() +{ + return mVsyncThread->thread_id() == PlatformThread::CurrentId(); +} + +void +SoftwareDisplay::NotifyVsync(mozilla::TimeStamp aVsyncTimestamp) +{ + MOZ_ASSERT(IsInSoftwareVsyncThread()); + Display::NotifyVsync(aVsyncTimestamp); + ScheduleNextVsync(aVsyncTimestamp); +} + +void +SoftwareDisplay::ScheduleNextVsync(mozilla::TimeStamp aVsyncTimestamp) +{ + MOZ_ASSERT(IsInSoftwareVsyncThread()); + mozilla::TimeStamp nextVsync = aVsyncTimestamp + mVsyncRate; + mozilla::TimeDuration delay = nextVsync - mozilla::TimeStamp::Now(); + if (delay.ToMilliseconds() < 0) { + delay = mozilla::TimeDuration::FromMilliseconds(0); + } + + mozilla::MonitorAutoLock lock(mCurrentTaskMonitor); + // We could've disabled vsync between this posted task and when it actually + // executes + if (!mVsyncEnabled) { + return; + } + mCurrentVsyncTask = NewRunnableMethod(this, + &SoftwareDisplay::NotifyVsync, + nextVsync); + + mVsyncThread->message_loop()->PostDelayedTask(FROM_HERE, + mCurrentVsyncTask, + delay.ToMilliseconds()); +} + +SoftwareDisplay::~SoftwareDisplay() +{ + MOZ_ASSERT(NS_IsMainThread()); + delete mVsyncThread; +} diff --git a/gfx/thebes/SoftwareVsyncSource.h b/gfx/thebes/SoftwareVsyncSource.h new file mode 100644 index 000000000000..75af0d054b5c --- /dev/null +++ b/gfx/thebes/SoftwareVsyncSource.h @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * 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_SOFTWARE_VSYNC_SOURCE_H +#define GFX_SOFTWARE_VSYNC_SOURCE_H + +#include "mozilla/Monitor.h" +#include "mozilla/RefPtr.h" +#include "mozilla/TimeStamp.h" +#include "base/thread.h" +#include "nsISupportsImpl.h" +#include "VsyncSource.h" + +class CancelableTask; + +class SoftwareDisplay MOZ_FINAL : public mozilla::gfx::VsyncSource::Display +{ + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SoftwareDisplay) + +public: + SoftwareDisplay(); + virtual void EnableVsync() MOZ_OVERRIDE; + virtual void DisableVsync() MOZ_OVERRIDE; + virtual bool IsVsyncEnabled() MOZ_OVERRIDE; + bool IsInSoftwareVsyncThread(); + virtual void NotifyVsync(mozilla::TimeStamp aVsyncTimestamp) MOZ_OVERRIDE; + void ScheduleNextVsync(mozilla::TimeStamp aVsyncTimestamp); + +protected: + ~SoftwareDisplay(); + +private: + mozilla::TimeDuration mVsyncRate; + // Use a chromium thread because nsITimers* fire on the main thread + base::Thread* mVsyncThread; + bool mVsyncEnabled; + CancelableTask* mCurrentVsyncTask; + // Locks against both mCurrentVsyncTask and mVsyncEnabled + mozilla::Monitor mCurrentTaskMonitor; +}; // SoftwareDisplay + +// Fallback option to use a software timer to mimic vsync. Useful for gtests +// To mimic a hardware vsync thread, we create a dedicated software timer +// vsync thread. +class SoftwareVsyncSource : public mozilla::gfx::VsyncSource +{ +public: + SoftwareVsyncSource(); + ~SoftwareVsyncSource(); + + virtual Display& GetGlobalDisplay() MOZ_OVERRIDE + { + MOZ_ASSERT(mGlobalDisplay != nullptr); + return *mGlobalDisplay; + } + +private: + nsRefPtr mGlobalDisplay; +}; + +#endif /* GFX_SOFTWARE_VSYNC_SOURCE_H */ diff --git a/gfx/thebes/VsyncSource.cpp b/gfx/thebes/VsyncSource.cpp index f9bd3bd584c9..091134565671 100644 --- a/gfx/thebes/VsyncSource.cpp +++ b/gfx/thebes/VsyncSource.cpp @@ -35,7 +35,7 @@ VsyncSource::FindDisplay(CompositorVsyncDispatcher* aCompositorVsyncDispatcher) void VsyncSource::Display::NotifyVsync(TimeStamp aVsyncTimestamp) { - // Called on the hardware vsync thread + // Called on the vsync thread for (size_t i = 0; i < mCompositorVsyncDispatchers.Length(); i++) { mCompositorVsyncDispatchers[i]->NotifyVsync(aVsyncTimestamp); } diff --git a/gfx/thebes/VsyncSource.h b/gfx/thebes/VsyncSource.h index f8a1454e5abf..0cecf7408910 100644 --- a/gfx/thebes/VsyncSource.h +++ b/gfx/thebes/VsyncSource.h @@ -3,6 +3,9 @@ * 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_VSYNCSOURCE_H +#define GFX_VSYNCSOURCE_H + #include "mozilla/RefPtr.h" #include "mozilla/TimeStamp.h" #include "nsISupportsImpl.h" @@ -26,8 +29,8 @@ public: virtual ~Display(); void AddCompositorVsyncDispatcher(mozilla::CompositorVsyncDispatcher* aCompositorVsyncDispatcher); void RemoveCompositorVsyncDispatcher(mozilla::CompositorVsyncDispatcher* aCompositorVsyncDispatcher); - // Notified when this display's vsync occurs, on the hardware vsync thread - void NotifyVsync(mozilla::TimeStamp aVsyncTimestamp); + // Notified when this display's vsync occurs, on the vsync thread + virtual void NotifyVsync(mozilla::TimeStamp aVsyncTimestamp); // These should all only be called on the main thread virtual void EnableVsync() = 0; @@ -48,3 +51,5 @@ protected: }; // VsyncSource } // gfx } // mozilla + +#endif /* GFX_VSYNCSOURCE_H */ diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index 548d8914c8af..c8d713b3cd8b 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -105,6 +105,7 @@ class mozilla::gl::SkiaGLGlue : public GenericAtomicRefCounted { #include "nsIGfxInfo.h" #include "nsIXULRuntime.h" #include "VsyncSource.h" +#include "SoftwareVsyncSource.h" namespace mozilla { namespace layers { @@ -2283,3 +2284,11 @@ gfxPlatform::UsesOffMainThreadCompositing() return result; } + +already_AddRefed +gfxPlatform::CreateHardwareVsyncSource() +{ + NS_WARNING("Hardware Vsync support not yet implemented. Falling back to software timers\n"); + nsRefPtr softwareVsync = new SoftwareVsyncSource(); + return softwareVsync.forget(); +} diff --git a/gfx/thebes/gfxPlatform.h b/gfx/thebes/gfxPlatform.h index b6417326bfb5..d99ab3a4f90a 100644 --- a/gfx/thebes/gfxPlatform.h +++ b/gfx/thebes/gfxPlatform.h @@ -604,10 +604,7 @@ protected: /** * Initialized hardware vsync based on each platform. */ - virtual already_AddRefed CreateHardwareVsyncSource() { - NS_WARNING("Hardware vsync not supported on platform yet"); - return nullptr; - } + virtual already_AddRefed CreateHardwareVsyncSource(); /** * Helper method, creates a draw target for a specific Azure backend. diff --git a/gfx/thebes/moz.build b/gfx/thebes/moz.build index 944b5a70abdf..c006c4080089 100644 --- a/gfx/thebes/moz.build +++ b/gfx/thebes/moz.build @@ -52,6 +52,7 @@ EXPORTS += [ 'gfxVR.h', 'GraphicsFilter.h', 'RoundedRect.h', + 'SoftwareVsyncSource.h', 'VsyncSource.h', ] @@ -245,6 +246,7 @@ UNIFIED_SOURCES += [ 'gfxUtils.cpp', 'gfxVR.cpp', 'nsUnicodeRange.cpp', + 'SoftwareVsyncSource.cpp', 'VsyncSource.cpp', ]