Bug 1102631 - Create a software vsync timer. r=kats
authorMason Chang <mchang@mozilla.com>
Thu, 08 Jan 2015 15:12:47 -0800
changeset 222852 26c24b7c06aeca5c78a26b520b916740b47c9f0b
parent 222851 95267af607c398fb56878d43f07c7c150dac82d7
child 222853 3941c5858c22c5ffc83c74ad9b4c7afb28c9ed97
push id10731
push usercbook@mozilla.com
push dateFri, 09 Jan 2015 14:51:37 +0000
treeherderfx-team@e6756043d930 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats
bugs1102631
milestone37.0a1
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',
     ]