Bug 1102631 - Create a software vsync timer. r=kats
authorMason Chang <mchang@mozilla.com>
Thu, 08 Jan 2015 15:12:47 -0800
changeset 248648 26c24b7c06aeca5c78a26b520b916740b47c9f0b
parent 248647 95267af607c398fb56878d43f07c7c150dac82d7
child 248649 3941c5858c22c5ffc83c74ad9b4c7afb28c9ed97
push id4489
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 15:17:55 +0000
treeherdermozilla-beta@fd7c3dc24146 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats
bugs1102631
milestone37.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 1102631 - Create a software vsync timer. r=kats
gfx/thebes/SoftwareVsyncSource.cpp
gfx/thebes/SoftwareVsyncSource.h
gfx/thebes/VsyncSource.cpp
gfx/thebes/VsyncSource.h
gfx/thebes/gfxPlatform.cpp
gfx/thebes/gfxPlatform.h
gfx/thebes/moz.build
new file mode 100644
--- /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;
+}
new file mode 100644
--- /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<SoftwareDisplay> mGlobalDisplay;
+};
+
+#endif /* GFX_SOFTWARE_VSYNC_SOURCE_H */
--- a/gfx/thebes/VsyncSource.cpp
+++ b/gfx/thebes/VsyncSource.cpp
@@ -30,17 +30,17 @@ VsyncSource::Display&
 VsyncSource::FindDisplay(CompositorVsyncDispatcher* aCompositorVsyncDispatcher)
 {
   return GetGlobalDisplay();
 }
 
 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);
   }
 }
 
 VsyncSource::Display::Display()
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/gfx/thebes/VsyncSource.h
+++ b/gfx/thebes/VsyncSource.h
@@ -1,13 +1,16 @@
 /* -*- 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_VSYNCSOURCE_H
+#define GFX_VSYNCSOURCE_H
+
 #include "mozilla/RefPtr.h"
 #include "mozilla/TimeStamp.h"
 #include "nsISupportsImpl.h"
 #include "nsTArray.h"
 
 namespace mozilla {
 class CompositorVsyncDispatcher;
 
@@ -21,18 +24,18 @@ class VsyncSource
 public:
   // Controls vsync unique to each display and unique on each platform
   class Display {
     public:
       Display();
       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;
       virtual void DisableVsync() = 0;
       virtual bool IsVsyncEnabled() = 0;
 
     private:
       nsTArray<nsRefPtr<mozilla::CompositorVsyncDispatcher>> mCompositorVsyncDispatchers;
@@ -43,8 +46,10 @@ public:
 
 protected:
   virtual Display& GetGlobalDisplay() = 0; // Works across all displays
   virtual Display& FindDisplay(mozilla::CompositorVsyncDispatcher* aCompositorVsyncDispatcher);
   virtual ~VsyncSource() {}
 }; // VsyncSource
 } // gfx
 } // mozilla
+
+#endif /* GFX_VSYNCSOURCE_H */
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -100,16 +100,17 @@ class mozilla::gl::SkiaGLGlue : public G
 #include "mozilla/Preferences.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Mutex.h"
 
 #include "nsIGfxInfo.h"
 #include "nsIXULRuntime.h"
 #include "VsyncSource.h"
+#include "SoftwareVsyncSource.h"
 
 namespace mozilla {
 namespace layers {
 #ifdef MOZ_WIDGET_GONK
 void InitGralloc();
 #endif
 void ShutdownTileCache();
 }
@@ -2278,8 +2279,16 @@ gfxPlatform::UsesOffMainThreadCompositin
     result &= PR_GetEnv("MOZ_USE_OMTC") || PR_GetEnv("MOZ_OMTC_ENABLED");
 #endif
 #endif
     firstTime = false;
   }
 
   return result;
 }
+
+already_AddRefed<mozilla::gfx::VsyncSource>
+gfxPlatform::CreateHardwareVsyncSource()
+{
+  NS_WARNING("Hardware Vsync support not yet implemented. Falling back to software timers\n");
+  nsRefPtr<mozilla::gfx::VsyncSource> softwareVsync = new SoftwareVsyncSource();
+  return softwareVsync.forget();
+}
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -599,20 +599,17 @@ protected:
     virtual ~gfxPlatform();
 
     void AppendCJKPrefLangs(eFontPrefLang aPrefLangs[], uint32_t &aLen,
                             eFontPrefLang aCharLang, eFontPrefLang aPageLang);
 
     /**
      * Initialized hardware vsync based on each platform.
      */
-    virtual already_AddRefed<mozilla::gfx::VsyncSource> CreateHardwareVsyncSource() {
-      NS_WARNING("Hardware vsync not supported on platform yet");
-      return nullptr;
-    }
+    virtual already_AddRefed<mozilla::gfx::VsyncSource> CreateHardwareVsyncSource();
 
     /**
      * Helper method, creates a draw target for a specific Azure backend.
      * Used by CreateOffscreenDrawTarget.
      */
     mozilla::TemporaryRef<DrawTarget>
       CreateDrawTargetForBackend(mozilla::gfx::BackendType aBackend,
                                  const mozilla::gfx::IntSize& aSize,
--- a/gfx/thebes/moz.build
+++ b/gfx/thebes/moz.build
@@ -47,16 +47,17 @@ EXPORTS += [
     'gfxTeeSurface.h',
     'gfxTextRun.h',
     'gfxTypes.h',
     'gfxUserFontSet.h',
     'gfxUtils.h',
     'gfxVR.h',
     'GraphicsFilter.h',
     'RoundedRect.h',
+    'SoftwareVsyncSource.h',
     'VsyncSource.h',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
     EXPORTS += [
         'gfxAndroidPlatform.h',
         'gfxFT2FontBase.h',
         'gfxFT2Fonts.h',
@@ -240,16 +241,17 @@ UNIFIED_SOURCES += [
     'gfxSkipChars.cpp',
     'gfxSVGGlyphs.cpp',
     'gfxTeeSurface.cpp',
     'gfxTextRun.cpp',
     'gfxUserFontSet.cpp',
     'gfxUtils.cpp',
     'gfxVR.cpp',
     'nsUnicodeRange.cpp',
+    'SoftwareVsyncSource.cpp',
     'VsyncSource.cpp',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
     UNIFIED_SOURCES += [
         'gfxMacPlatformFontList.mm',
     ]