--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -39,17 +39,17 @@
#include "gfxPrefs.h"
#if defined(MOZ_WIDGET_ANDROID)
# include <android/log.h>
# include "AndroidBridge.h"
#endif
#include "GeckoProfiler.h"
#include "FrameUniformityData.h"
#include "TreeTraversal.h"
-#include "VsyncSource.h"
+#include "gfxVsync.h"
struct nsCSSValueSharedList;
namespace mozilla {
namespace layers {
using namespace mozilla::gfx;
@@ -1420,17 +1420,18 @@ AsyncCompositionManager::TransformShadow
TransformScrollableLayer(scrollableLayers[i]);
}
}
}
// Advance APZ animations to the next expected vsync timestamp, if we can
// get it.
TimeStamp nextFrame = aCurrentFrame;
- TimeDuration vsyncrate = gfxPlatform::GetPlatform()->GetHardwareVsync()->GetGlobalDisplay().GetVsyncRate();
+ RefPtr<VsyncSource> vsyncSource = gfxPlatform::GetPlatform()->GetHardwareVsync()->GetGlobalDisplaySource();
+ TimeDuration vsyncrate = vsyncSource->GetVsyncInterval();
if (vsyncrate != TimeDuration::Forever()) {
nextFrame += vsyncrate;
}
wantNextFrame |= SampleAPZAnimations(LayerMetricsWrapper(root), nextFrame);
}
LayerComposite* rootComposite = root->AsLayerComposite();
--- a/gfx/thebes/gfxAndroidPlatform.cpp
+++ b/gfx/thebes/gfxAndroidPlatform.cpp
@@ -18,17 +18,17 @@
#include "mozilla/dom/ContentChild.h"
#include "nsXULAppAPI.h"
#include "nsIScreen.h"
#include "nsIScreenManager.h"
#include "nsILocaleService.h"
#include "nsServiceManagerUtils.h"
#include "gfxPrefs.h"
#include "cairo.h"
-#include "VsyncSource.h"
+#include "gfxVsync.h"
#ifdef MOZ_WIDGET_ANDROID
#include "AndroidBridge.h"
#endif
#ifdef MOZ_WIDGET_GONK
#include <cutils/properties.h>
#include "mozilla/layers/CompositorBridgeParent.h"
@@ -357,90 +357,76 @@ gfxAndroidPlatform::RequiresLinearZoom()
ContentChild::GetSingleton()->IsForBrowser();
#endif
NS_NOTREACHED("oops, what platform is this?");
return gfxPlatform::RequiresLinearZoom();
}
#ifdef MOZ_WIDGET_GONK
-class GonkVsyncSource final : public VsyncSource
+class HWCVsyncSource final : public VsyncSource
{
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(HWCVsyncSource, override)
public:
- GonkVsyncSource()
+ HWCVsyncSource(const nsID& aSourceID)
+ : VsyncSource(aSourceID)
+ , mVsyncEnabled(false)
{
}
- virtual Display& GetGlobalDisplay() override
+ virtual void EnableVsync() override
{
- return mGlobalDisplay;
+ MOZ_ASSERT(NS_IsMainThread());
+ if (IsVsyncEnabled()) {
+ return;
+ }
+ mVsyncEnabled = HwcComposer2D::GetInstance()->EnableVsync(true);
+ }
+
+ virtual void DisableVsync() override
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!IsVsyncEnabled()) {
+ return;
+ }
+ mVsyncEnabled = HwcComposer2D::GetInstance()->EnableVsync(false);
}
- class GonkDisplay final : public VsyncSource::Display
+ virtual bool IsVsyncEnabled() override
{
- public:
- GonkDisplay() : mVsyncEnabled(false)
- {
- }
-
- ~GonkDisplay()
- {
- DisableVsync();
- }
-
- virtual void EnableVsync() override
- {
- MOZ_ASSERT(NS_IsMainThread());
- if (IsVsyncEnabled()) {
- return;
- }
- mVsyncEnabled = HwcComposer2D::GetInstance()->EnableVsync(true);
- }
-
- virtual void DisableVsync() override
- {
- MOZ_ASSERT(NS_IsMainThread());
- if (!IsVsyncEnabled()) {
- return;
- }
- mVsyncEnabled = HwcComposer2D::GetInstance()->EnableVsync(false);
- }
-
- virtual bool IsVsyncEnabled() override
- {
- MOZ_ASSERT(NS_IsMainThread());
- return mVsyncEnabled;
- }
- private:
- bool mVsyncEnabled;
- }; // GonkDisplay
-
-private:
- virtual ~GonkVsyncSource()
- {
+ MOZ_ASSERT(NS_IsMainThread());
+ return mVsyncEnabled;
}
- GonkDisplay mGlobalDisplay;
-}; // GonkVsyncSource
+protected:
+ ~HWCVsyncSource()
+ {
+ DisableVsync();
+ }
+
+ bool mVsyncEnabled;
+}; // HWCVsyncSource
#endif
-already_AddRefed<mozilla::gfx::VsyncSource>
-gfxAndroidPlatform::CreateHardwareVsyncSource()
+already_AddRefed<mozilla::gfx::VsyncManager>
+gfxAndroidPlatform::CreateHardwareVsyncManager()
{
// Only enable true hardware vsync on kit-kat and L device. Jelly Bean has
// inaccurate hardware vsync so disable on JB. Android pre-JB doesn't have
// hardware vsync.
// L is android version 21, L-MR1 is 22, kit-kat is 19, 20 is kit-kat for
// wearables.
#if defined(MOZ_WIDGET_GONK) && (ANDROID_VERSION == 19 || ANDROID_VERSION >= 21)
- RefPtr<GonkVsyncSource> vsyncSource = new GonkVsyncSource();
- VsyncSource::Display& display = vsyncSource->GetGlobalDisplay();
- display.EnableVsync();
- if (!display.IsVsyncEnabled()) {
+ RefPtr<VsyncSource> globalDisplay = new HWCVsyncSource(VsyncManager::kGlobalDisplaySourceID);
+ globalDisplay->EnableVsync();
+ if (!globalDisplay->IsVsyncEnabled()) {
NS_WARNING("Error enabling gonk vsync. Falling back to software vsync");
- return gfxPlatform::CreateHardwareVsyncSource();
+ return gfxPlatform::CreateSoftwareVsyncManager();
}
- display.DisableVsync();
- return vsyncSource.forget();
+ globalDisplay->DisableVsync();
+
+ RefPtr<VsyncManager> vsyncManager = new VsyncManager();
+ vsyncManager->RegisterSource(globalDisplay);
+ return vsyncManager.forget();
#else
- return gfxPlatform::CreateHardwareVsyncSource();
+ return gfxPlatform::CreateHardwareVsyncManager();
#endif
}
--- a/gfx/thebes/gfxAndroidPlatform.h
+++ b/gfx/thebes/gfxAndroidPlatform.h
@@ -61,17 +61,17 @@ public:
virtual bool RequiresLinearZoom() override;
FT_Library GetFTLibrary();
virtual bool CanRenderContentToDataSurface() const override {
return true;
}
- virtual already_AddRefed<mozilla::gfx::VsyncSource> CreateHardwareVsyncSource() override;
+ virtual already_AddRefed<mozilla::gfx::VsyncManager> CreateHardwareVsyncManager() override;
#ifdef MOZ_WIDGET_GONK
virtual bool IsInGonkEmulator() const { return mIsInGonkEmulator; }
#endif
virtual bool SupportsApzTouchInput() const override {
return true;
}
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -127,18 +127,17 @@ class mozilla::gl::SkiaGLGlue : public G
#include "mozilla/Preferences.h"
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/Mutex.h"
#include "nsAlgorithm.h"
#include "nsIGfxInfo.h"
#include "nsIXULRuntime.h"
-#include "VsyncSource.h"
-#include "SoftwareVsyncSource.h"
+#include "gfxVsync.h"
#include "nscore.h" // for NS_FREE_PERMANENT_DATA
#include "mozilla/dom/ContentChild.h"
#include "gfxVR.h"
#include "VRManagerChild.h"
namespace mozilla {
namespace layers {
#ifdef MOZ_WIDGET_GONK
@@ -741,19 +740,19 @@ gfxPlatform::Init()
if (!imgTools) {
NS_RUNTIMEABORT("Could not initialize ImageLib");
}
RegisterStrongMemoryReporter(new GfxMemoryImageReporter());
if (XRE_IsParentProcess()) {
if (gfxPlatform::ForceSoftwareVsync()) {
- gPlatform->mVsyncSource = (gPlatform)->gfxPlatform::CreateHardwareVsyncSource();
+ gPlatform->mVsyncManager = (gPlatform)->gfxPlatform::CreateHardwareVsyncManager();
} else {
- gPlatform->mVsyncSource = gPlatform->CreateHardwareVsyncSource();
+ gPlatform->mVsyncManager = gPlatform->CreateHardwareVsyncManager();
}
}
#ifdef USE_SKIA
uint32_t skiaCacheSize = GetSkiaGlyphCacheSize();
if (skiaCacheSize != kDefaultGlyphCacheSize) {
SkGraphics::SetFontCacheLimit(skiaCacheSize);
}
@@ -802,17 +801,21 @@ gfxPlatform::Shutdown()
NS_ASSERTION(gPlatform->mMemoryPressureObserver, "mMemoryPressureObserver has already gone");
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->RemoveObserver(gPlatform->mMemoryPressureObserver, "memory-pressure");
}
gPlatform->mMemoryPressureObserver = nullptr;
gPlatform->mSkiaGlue = nullptr;
- gPlatform->mVsyncSource = nullptr;
+
+ // we should have always created one
+ MOZ_ASSERT(gPlatform->mVsyncManager);
+ gPlatform->mVsyncManager->Shutdown();
+ gPlatform->mVsyncManager = nullptr;
}
#ifdef MOZ_WIDGET_ANDROID
// Shut down the texture pool
TexturePoolOGL::Shutdown();
#endif
// Shut down the default GL context provider.
@@ -2237,65 +2240,84 @@ gfxPlatform::UsesOffMainThreadCompositin
#endif
firstTime = false;
}
return result;
}
+already_AddRefed<VsyncManager>
+gfxPlatform::CreateHardwareVsyncManager()
+{
+ NS_WARNING("Hardware Vsync support not yet implemented. Falling back to software timers");
+ return CreateSoftwareVsyncManager();
+}
+
+already_AddRefed<VsyncManager>
+gfxPlatform::CreateSoftwareVsyncManager()
+{
+ RefPtr<SoftwareVsyncSource> display = new SoftwareVsyncSource(VsyncManager::kGlobalDisplaySourceID,
+ GetSoftwareVsyncInterval().ToMilliseconds());
+ RefPtr<VsyncManager> vsyncManager = new VsyncManager();
+ vsyncManager->RegisterSource(display);
+ return vsyncManager.forget();
+}
+
/***
* The preference "layout.frame_rate" has 3 meanings depending on the value:
*
* -1 = Auto (default), use hardware vsync or software vsync @ 60 hz if hw vsync fails.
* 0 = ASAP mode - used during talos testing.
* X = Software vsync at a rate of X times per second.
*/
-already_AddRefed<mozilla::gfx::VsyncSource>
-gfxPlatform::CreateHardwareVsyncSource()
-{
- NS_WARNING("Hardware Vsync support not yet implemented. Falling back to software timers");
- RefPtr<mozilla::gfx::VsyncSource> softwareVsync = new SoftwareVsyncSource();
- return softwareVsync.forget();
-}
/* static */ bool
gfxPlatform::IsInLayoutAsapMode()
{
// There are 2 modes of ASAP mode.
// 1 is that the refresh driver and compositor are in lock step
// the second is that the compositor goes ASAP and the refresh driver
// goes at whatever the configurated rate is. This only checks the version
// talos uses, which is the refresh driver and compositor are in lockstep.
- return Preferences::GetInt("layout.frame_rate", -1) == 0;
+ return gfxPrefs::LayoutFrameRate() == 0;
}
/* static */ bool
gfxPlatform::ForceSoftwareVsync()
{
- return Preferences::GetInt("layout.frame_rate", -1) > 0;
+ return gfxPrefs::LayoutFrameRate() >= 0;
+}
+
+/* static */ int32_t
+gfxPlatform::GetVsyncRate()
+{
+ if (gPlatform && gPlatform->mVsyncManager) {
+ RefPtr<VsyncSource> screen = gPlatform->mVsyncManager->GetGlobalDisplaySource();
+ TimeDuration td = screen->GetVsyncInterval();
+ if (td != TimeDuration::Forever()) {
+ return NSToIntRound(1000.0 / td.ToMilliseconds());
+ }
+ }
+ return GetSoftwareVsyncRate();
}
/* static */ int
gfxPlatform::GetSoftwareVsyncRate()
{
- int preferenceRate = Preferences::GetInt("layout.frame_rate",
- gfxPlatform::GetDefaultFrameRate());
- if (preferenceRate <= 0) {
- return gfxPlatform::GetDefaultFrameRate();
+ int preferenceRate = gfxPrefs::LayoutFrameRate();
+ if (preferenceRate == 0) {
+ return 10000; // ASAP mode
+ }
+ if (preferenceRate < 0) {
+ return 60; // "use platform rate", but this is software
}
return preferenceRate;
}
-/* static */ int
-gfxPlatform::GetDefaultFrameRate()
-{
- return 60;
-}
-
void
gfxPlatform::GetApzSupportInfo(mozilla::widget::InfoObject& aObj)
{
if (!gfxPlatform::AsyncPanZoomEnabled()) {
return;
}
if (SupportsApzWheelInput()) {
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -2,16 +2,17 @@
* 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_PLATFORM_H
#define GFX_PLATFORM_H
#include "mozilla/Logging.h"
+#include "mozilla/TimeStamp.h"
#include "mozilla/gfx/Types.h"
#include "nsTArray.h"
#include "nsString.h"
#include "nsCOMPtr.h"
#include "nsUnicodeScriptCodes.h"
#include "gfxTypes.h"
#include "gfxFontFamilyList.h"
@@ -44,17 +45,17 @@ namespace gl {
class SkiaGLGlue;
} // namespace gl
namespace gfx {
class DrawTarget;
class SourceSurface;
class DataSourceSurface;
class ScaledFont;
class DrawEventRecorder;
-class VsyncSource;
+class VsyncManager;
class DeviceInitData;
inline uint32_t
BackendTypeBit(BackendType b)
{
return 1 << uint8_t(b);
}
@@ -567,45 +568,57 @@ public:
static bool UsesOffMainThreadCompositing();
bool HasEnoughTotalSystemMemoryForSkiaGL();
/**
* Get the hardware vsync source for each platform.
* Should only exist and be valid on the parent process
*/
- virtual mozilla::gfx::VsyncSource* GetHardwareVsync() {
- MOZ_ASSERT(mVsyncSource != nullptr);
+ virtual mozilla::gfx::VsyncManager* GetHardwareVsync() {
+ MOZ_ASSERT(mVsyncManager != nullptr);
MOZ_ASSERT(XRE_IsParentProcess());
- return mVsyncSource;
+ return mVsyncManager;
+ }
+
+ /**
+ * Returns the current Vsync rate/interval in use for the default display,
+ * whether hardware or software.
+ */
+ static int32_t GetVsyncRate();
+ static mozilla::TimeDuration GetVsyncInterval() {
+ return mozilla::TimeDuration::FromMilliseconds(1000.0 / double(GetVsyncRate()));
}
/**
* True if layout rendering should use ASAP mode, which means
* the refresh driver and compositor should render ASAP.
* Used for talos testing purposes
*/
static bool IsInLayoutAsapMode();
/**
- * Returns the software vsync rate to use.
+ * Returns the software vsync rate to use. Reads layout.frame_rate.
+ * If pref > 0, returns it directly.
+ * If pref == 0, this is "ASAP" mode; it returns 10000, in an effort to
+ * composite as fast as possible.
+ * If pref < 0, this is "use the platform default rate". As this is
+ * software, it returns 60.
*/
static int GetSoftwareVsyncRate();
+ static mozilla::TimeDuration GetSoftwareVsyncInterval() {
+ return mozilla::TimeDuration::FromMilliseconds(1000.0 / double(GetSoftwareVsyncRate()));
+ }
/**
* Returns whether or not a custom vsync rate is set.
*/
static bool ForceSoftwareVsync();
/**
- * Returns the default frame rate for the refresh driver / software vsync.
- */
- static int GetDefaultFrameRate();
-
- /**
* Used to test which input types are handled via APZ.
*/
virtual bool SupportsApzWheelInput() const {
return false;
}
virtual bool SupportsApzTouchInput() const {
return false;
}
@@ -663,25 +676,31 @@ public:
protected:
gfxPlatform();
virtual ~gfxPlatform();
/**
* Initialized hardware vsync based on each platform.
*/
- virtual already_AddRefed<mozilla::gfx::VsyncSource> CreateHardwareVsyncSource();
+ virtual already_AddRefed<mozilla::gfx::VsyncManager> CreateHardwareVsyncManager();
// Returns whether or not layers should be accelerated by default on this platform.
virtual bool AccelerateLayersByDefault();
// Returns a prioritized list of available compositor backends for acceleration.
virtual void GetAcceleratedCompositorBackends(nsTArray<mozilla::layers::LayersBackend>& aBackends);
/**
+ * Initialize software vsync; can be overriden by the platform, but a generic
+ * timer-based impl exists.
+ */
+ virtual already_AddRefed<mozilla::gfx::VsyncManager> CreateSoftwareVsyncManager();
+
+ /**
* Initialise the preferred and fallback canvas backends
* aBackendBitmask specifies the backends which are acceptable to the caller.
* The backend used is determined by aBackendBitmask and the order specified
* by the gfx.canvas.azure.backends pref.
*/
void InitBackendPrefs(uint32_t aCanvasBitmask, mozilla::gfx::BackendType aCanvasDefault,
uint32_t aContentBitmask, mozilla::gfx::BackendType aContentDefault);
@@ -743,17 +762,17 @@ protected:
int32_t mWordCacheCharLimit;
// max number of entries in word cache
int32_t mWordCacheMaxEntries;
uint32_t mTotalSystemMemory;
// Hardware vsync source. Only valid on parent process
- RefPtr<mozilla::gfx::VsyncSource> mVsyncSource;
+ RefPtr<mozilla::gfx::VsyncManager> mVsyncManager;
RefPtr<mozilla::gfx::DrawTarget> mScreenReferenceDrawTarget;
private:
/**
* Start up Thebes.
*/
static void Init();
--- a/gfx/thebes/gfxPlatformMac.cpp
+++ b/gfx/thebes/gfxPlatformMac.cpp
@@ -13,26 +13,25 @@
#include "gfxMacPlatformFontList.h"
#include "gfxMacFont.h"
#include "gfxCoreTextShaper.h"
#include "gfxTextRun.h"
#include "gfxUserFontSet.h"
#include "nsTArray.h"
#include "mozilla/Preferences.h"
-#include "mozilla/VsyncDispatcher.h"
#include "qcms.h"
#include "gfx2DGlue.h"
#include <dlfcn.h>
#include <CoreVideo/CoreVideo.h>
#include "nsCocoaFeatures.h"
#include "mozilla/layers/CompositorBridgeParent.h"
-#include "VsyncSource.h"
+#include "gfxVsync.h"
using namespace mozilla;
using namespace mozilla::gfx;
// cribbed from CTFontManager.h
enum {
kAutoActivationDisabled = 1
};
@@ -394,206 +393,194 @@ static CVReturn VsyncCallback(CVDisplayL
const CVTimeStamp* aNow,
const CVTimeStamp* aOutputTime,
CVOptionFlags aFlagsIn,
CVOptionFlags* aFlagsOut,
void* aDisplayLinkContext);
class OSXVsyncSource final : public VsyncSource
{
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OSXVsyncSource, override)
public:
- OSXVsyncSource()
+ explicit OSXVsyncSource(const nsID& aID)
+ : VsyncSource(aID)
+ , mDisplayLink(nullptr)
+ , mVsyncInterval(TimeDuration::Forever())
{
+ MOZ_ASSERT(NS_IsMainThread());
+ mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
}
- virtual Display& GetGlobalDisplay() override
+ static void RetryEnableVsync(nsITimer* aTimer, void* aOsxDisplay)
{
- return mGlobalDisplay;
+ MOZ_ASSERT(NS_IsMainThread());
+ OSXVsyncSource* osxDisplay = static_cast<OSXVsyncSource*>(aOsxDisplay);
+ MOZ_ASSERT(osxDisplay);
+ osxDisplay->EnableVsync();
}
- class OSXDisplay final : public VsyncSource::Display
+ virtual void EnableVsync() override
{
- public:
- OSXDisplay()
- : mDisplayLink(nullptr)
- {
- MOZ_ASSERT(NS_IsMainThread());
- mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+ MOZ_ASSERT(NS_IsMainThread());
+ if (IsVsyncEnabled()) {
+ return;
}
- ~OSXDisplay()
- {
- MOZ_ASSERT(NS_IsMainThread());
- mTimer->Cancel();
- mTimer = nullptr;
- DisableVsync();
+ // Create a display link capable of being used with all active displays
+ // TODO: See if we need to create an active DisplayLink for each monitor in multi-monitor
+ // situations. According to the docs, it is compatible with all displays running on the computer
+ // But if we have different monitors at different display rates, we may hit issues.
+ if (CVDisplayLinkCreateWithActiveCGDisplays(&mDisplayLink) != kCVReturnSuccess) {
+ NS_WARNING("Could not create a display link with all active displays. Retrying");
+ CVDisplayLinkRelease(mDisplayLink);
+ mDisplayLink = nullptr;
+
+ // bug 1142708 - When coming back from sleep,
+ // or when changing displays, active displays may not be ready yet,
+ // even if listening for the kIOMessageSystemHasPoweredOn event
+ // from OS X sleep notifications.
+ // Active displays are those that are drawable.
+ // bug 1144638 - When changing display configurations and getting
+ // notifications from CGDisplayReconfigurationCallBack, the
+ // callback gets called twice for each active display
+ // so it's difficult to know when all displays are active.
+ // Instead, try again soon. The delay is arbitrary. 100ms chosen
+ // because on a late 2013 15" retina, it takes about that
+ // long to come back up from sleep.
+ uint32_t delay = 100;
+ mTimer->InitWithFuncCallback(RetryEnableVsync, this, delay, nsITimer::TYPE_ONE_SHOT);
+ return;
}
- static void RetryEnableVsync(nsITimer* aTimer, void* aOsxDisplay)
- {
- MOZ_ASSERT(NS_IsMainThread());
- OSXDisplay* osxDisplay = static_cast<OSXDisplay*>(aOsxDisplay);
- MOZ_ASSERT(osxDisplay);
- osxDisplay->EnableVsync();
+ if (CVDisplayLinkSetOutputCallback(mDisplayLink, &VsyncCallback, this) != kCVReturnSuccess) {
+ NS_WARNING("Could not set displaylink output callback");
+ CVDisplayLinkRelease(mDisplayLink);
+ mDisplayLink = nullptr;
+ return;
}
- virtual void EnableVsync() override
- {
- MOZ_ASSERT(NS_IsMainThread());
- if (IsVsyncEnabled()) {
- return;
- }
-
- // Create a display link capable of being used with all active displays
- // TODO: See if we need to create an active DisplayLink for each monitor in multi-monitor
- // situations. According to the docs, it is compatible with all displays running on the computer
- // But if we have different monitors at different display rates, we may hit issues.
- if (CVDisplayLinkCreateWithActiveCGDisplays(&mDisplayLink) != kCVReturnSuccess) {
- NS_WARNING("Could not create a display link with all active displays. Retrying");
- CVDisplayLinkRelease(mDisplayLink);
- mDisplayLink = nullptr;
-
- // bug 1142708 - When coming back from sleep,
- // or when changing displays, active displays may not be ready yet,
- // even if listening for the kIOMessageSystemHasPoweredOn event
- // from OS X sleep notifications.
- // Active displays are those that are drawable.
- // bug 1144638 - When changing display configurations and getting
- // notifications from CGDisplayReconfigurationCallBack, the
- // callback gets called twice for each active display
- // so it's difficult to know when all displays are active.
- // Instead, try again soon. The delay is arbitrary. 100ms chosen
- // because on a late 2013 15" retina, it takes about that
- // long to come back up from sleep.
- uint32_t delay = 100;
- mTimer->InitWithFuncCallback(RetryEnableVsync, this, delay, nsITimer::TYPE_ONE_SHOT);
- return;
- }
-
- if (CVDisplayLinkSetOutputCallback(mDisplayLink, &VsyncCallback, this) != kCVReturnSuccess) {
- NS_WARNING("Could not set displaylink output callback");
- CVDisplayLinkRelease(mDisplayLink);
- mDisplayLink = nullptr;
- return;
- }
-
- mPreviousTimestamp = TimeStamp::Now();
- if (CVDisplayLinkStart(mDisplayLink) != kCVReturnSuccess) {
- NS_WARNING("Could not activate the display link");
- CVDisplayLinkRelease(mDisplayLink);
- mDisplayLink = nullptr;
- }
-
- CVTime vsyncRate = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(mDisplayLink);
- if (vsyncRate.flags & kCVTimeIsIndefinite) {
- NS_WARNING("Could not get vsync rate, setting to 60.");
- mVsyncRate = TimeDuration::FromMilliseconds(1000.0 / 60.0);
- } else {
- int64_t timeValue = vsyncRate.timeValue;
- int64_t timeScale = vsyncRate.timeScale;
- const int milliseconds = 1000;
- float rateInMs = ((double) timeValue / (double) timeScale) * milliseconds;
- mVsyncRate = TimeDuration::FromMilliseconds(rateInMs);
- }
+ mPreviousTimestamp = TimeStamp::Now();
+ if (CVDisplayLinkStart(mDisplayLink) != kCVReturnSuccess) {
+ NS_WARNING("Could not activate the display link");
+ CVDisplayLinkRelease(mDisplayLink);
+ mDisplayLink = nullptr;
}
- virtual void DisableVsync() override
- {
- MOZ_ASSERT(NS_IsMainThread());
- if (!IsVsyncEnabled()) {
- return;
- }
-
- // Release the display link
- if (mDisplayLink) {
- CVDisplayLinkRelease(mDisplayLink);
- mDisplayLink = nullptr;
- }
- }
-
- virtual bool IsVsyncEnabled() override
- {
- MOZ_ASSERT(NS_IsMainThread());
- return mDisplayLink != nullptr;
+ CVTime vsyncInterval = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(mDisplayLink);
+ if (vsyncInterval.flags & kCVTimeIsIndefinite) {
+ NS_WARNING("Could not get vsync rate, setting to 60.");
+ mVsyncInterval = TimeDuration::FromMilliseconds(1000.0 / 60.0);
+ } else {
+ int64_t timeValue = vsyncInterval.timeValue;
+ int64_t timeScale = vsyncInterval.timeScale;
+ const int milliseconds = 1000;
+ float rateInMs = ((double) timeValue / (double) timeScale) * milliseconds;
+ mVsyncInterval = TimeDuration::FromMilliseconds(rateInMs);
}
+ }
- virtual TimeDuration GetVsyncRate() override
- {
- return mVsyncRate;
+ virtual void DisableVsync() override
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // Make sure the timer is cancelled, because it might
+ // enable vsync itself after it triggers.
+ mTimer->Cancel();
+
+ // Release the display link, if one is set up
+ if (mDisplayLink) {
+ CVDisplayLinkRelease(mDisplayLink);
+ mDisplayLink = nullptr;
}
+ }
- // The vsync timestamps given by the CVDisplayLinkCallback are
- // in the future for the NEXT frame. Large parts of Gecko, such
- // as animations assume a timestamp at either now or in the past.
- // Normalize the timestamps given to the VsyncDispatchers to the vsync
- // that just occured, not the vsync that is upcoming.
- TimeStamp mPreviousTimestamp;
+ virtual bool IsVsyncEnabled() override
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ return mDisplayLink != nullptr;
+ }
- private:
- // Manages the display link render thread
- CVDisplayLinkRef mDisplayLink;
- RefPtr<nsITimer> mTimer;
- TimeDuration mVsyncRate;
- }; // OSXDisplay
+ virtual TimeDuration GetVsyncInterval() override
+ {
+ return mVsyncInterval;
+ }
+
+ // The vsync timestamps given by the CVDisplayLinkCallback are
+ // in the future for the NEXT frame. Large parts of Gecko, such
+ // as animations assume a timestamp at either now or in the past.
+ // Normalize the timestamps given to the VsyncDispatchers to the vsync
+ // that just occured, not the vsync that is upcoming.
+ TimeStamp mPreviousTimestamp;
private:
- virtual ~OSXVsyncSource()
+ ~OSXVsyncSource()
{
+ MOZ_ASSERT(NS_IsMainThread());
+ // should have been shut down
+ MOZ_ASSERT(!IsVsyncEnabled());
}
- OSXDisplay mGlobalDisplay;
+ void Shutdown() override {
+ DisableVsync();
+ VsyncSource::Shutdown();
+ }
+
+ // Manages the display link render thread
+ CVDisplayLinkRef mDisplayLink;
+ RefPtr<nsITimer> mTimer;
+ TimeDuration mVsyncInterval;
}; // OSXVsyncSource
static CVReturn VsyncCallback(CVDisplayLinkRef aDisplayLink,
const CVTimeStamp* aNow,
const CVTimeStamp* aOutputTime,
CVOptionFlags aFlagsIn,
CVOptionFlags* aFlagsOut,
void* aDisplayLinkContext)
{
// Executed on OS X hardware vsync thread
- OSXVsyncSource::OSXDisplay* display = (OSXVsyncSource::OSXDisplay*) aDisplayLinkContext;
+ OSXVsyncSource* display = (OSXVsyncSource*) aDisplayLinkContext;
int64_t nextVsyncTimestamp = aOutputTime->hostTime;
mozilla::TimeStamp nextVsync = mozilla::TimeStamp::FromSystemTime(nextVsyncTimestamp);
mozilla::TimeStamp previousVsync = display->mPreviousTimestamp;
mozilla::TimeStamp now = TimeStamp::Now();
// Snow leopard sometimes sends vsync timestamps very far in the past.
// Normalize the vsync timestamps to now.
if (nextVsync <= previousVsync) {
nextVsync = now;
previousVsync = now;
} else if (now < previousVsync) {
// Bug 1158321 - The VsyncCallback can sometimes execute before the reported
// vsync time. In those cases, normalize the timestamp to Now() as sending
// timestamps in the future has undefined behavior. See the comment above
- // OSXDisplay::mPreviousTimestamp
+ // OSXVsyncSource::mPreviousTimestamp
previousVsync = now;
}
display->mPreviousTimestamp = nextVsync;
- display->NotifyVsync(previousVsync);
+ display->OnVsync(previousVsync);
return kCVReturnSuccess;
}
-already_AddRefed<mozilla::gfx::VsyncSource>
-gfxPlatformMac::CreateHardwareVsyncSource()
+already_AddRefed<mozilla::gfx::VsyncManager>
+gfxPlatformMac::CreateHardwareVsyncManager()
{
- RefPtr<VsyncSource> osxVsyncSource = new OSXVsyncSource();
- VsyncSource::Display& primaryDisplay = osxVsyncSource->GetGlobalDisplay();
- primaryDisplay.EnableVsync();
- if (!primaryDisplay.IsVsyncEnabled()) {
+ RefPtr<OSXVsyncSource> primaryDisplay = new OSXVsyncSource(VsyncManager::kGlobalDisplaySourceID);
+ primaryDisplay->EnableVsync();
+ if (!primaryDisplay->IsVsyncEnabled()) {
NS_WARNING("OS X Vsync source not enabled. Falling back to software vsync.");
- return gfxPlatform::CreateHardwareVsyncSource();
+ return gfxPlatform::CreateSoftwareVsyncManager();
}
- primaryDisplay.DisableVsync();
- return osxVsyncSource.forget();
+ RefPtr<VsyncManager> vsyncManager = new VsyncManager();
+ vsyncManager->RegisterSource(primaryDisplay);
+ return vsyncManager.forget();
}
void
gfxPlatformMac::GetPlatformCMSOutputProfile(void* &mem, size_t &size)
{
mem = nullptr;
size = 0;
--- a/gfx/thebes/gfxPlatformMac.h
+++ b/gfx/thebes/gfxPlatformMac.h
@@ -8,17 +8,17 @@
#include "nsTArrayForwardDeclare.h"
#include "gfxPlatform.h"
#include "mozilla/LookAndFeel.h"
namespace mozilla {
namespace gfx {
class DrawTarget;
-class VsyncSource;
+class VsyncManager;
} // namespace gfx
} // namespace mozilla
class gfxPlatformMac : public gfxPlatform {
public:
gfxPlatformMac();
virtual ~gfxPlatformMac();
@@ -72,17 +72,17 @@ public:
bool RequiresAcceleratedGLContextForCompositorOGL() const override {
// On OS X in a VM, unaccelerated CompositorOGL shows black flashes, so we
// require accelerated GL for CompositorOGL but allow unaccelerated GL for
// BasicCompositor.
return true;
}
virtual bool UseProgressivePaint() override;
- virtual already_AddRefed<mozilla::gfx::VsyncSource> CreateHardwareVsyncSource() override;
+ virtual already_AddRefed<mozilla::gfx::VsyncManager> CreateHardwareVsyncManager() override;
// lower threshold on font anti-aliasing
uint32_t GetAntiAliasingThreshold() { return mFontAntiAliasingThreshold; }
protected:
bool AccelerateLayersByDefault() override;
private:
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -64,18 +64,19 @@
#include <d3d11.h>
#include "nsIMemoryReporter.h"
#include <winternl.h>
#include "d3dkmtQueryStatistics.h"
#include "SurfaceCache.h"
#include "gfxPrefs.h"
-
-#include "VsyncSource.h"
+#include "gfxUtils.h"
+
+#include "gfxVsync.h"
#include "DriverCrashGuard.h"
#include "mozilla/dom/ContentParent.h"
using namespace mozilla;
using namespace mozilla::gfx;
using namespace mozilla::layers;
using namespace mozilla::widget;
using namespace mozilla::image;
@@ -2607,274 +2608,267 @@ gfxWindowsPlatform::DwmCompositionEnable
if (FAILED(WinUtils::dwmIsCompositionEnabledPtr(&dwmEnabled))) {
return false;
}
return dwmEnabled;
}
-class D3DVsyncSource final : public VsyncSource
+class DWMVsyncSource final : public VsyncSource
{
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DWMVsyncSource, override)
public:
-
- class D3DVsyncDisplay final : public VsyncSource::Display
- {
- NS_INLINE_DECL_THREADSAFE_REFCOUNTING(D3DVsyncDisplay)
- public:
- D3DVsyncDisplay()
- : mPrevVsync(TimeStamp::Now())
- , mVsyncEnabledLock("D3DVsyncEnabledLock")
- , mVsyncEnabled(false)
- {
- mVsyncThread = new base::Thread("WindowsVsyncThread");
- const double rate = 1000 / 60.0;
- mSoftwareVsyncRate = TimeDuration::FromMilliseconds(rate);
- MOZ_RELEASE_ASSERT(mVsyncThread->Start(), "Could not start Windows vsync thread");
- SetVsyncRate();
+ DWMVsyncSource(const nsID& aSourceID)
+ : VsyncSource(aSourceID)
+ , mVsyncEnabledLock("D3DVsyncEnabledLock")
+ , mPrevVsync(TimeStamp::Now())
+ , mVsyncEnabled(false)
+ , mVsyncInterval(TimeDuration::Forever())
+ {
+ mVsyncThread = new base::Thread("WindowsVsyncThread");
+ SetVsyncInterval();
+ MOZ_RELEASE_ASSERT(mVsyncThread->Start(), "Could not start Windows vsync thread");
+ }
+
+ static bool DwmCompositionEnabled() {
+ return gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled();
+ }
+
+ void SetVsyncInterval()
+ {
+ if (!DwmCompositionEnabled()) {
+ mVsyncInterval = gfxPlatform::GetSoftwareVsyncInterval();
+ return;
}
- void SetVsyncRate()
- {
- if (!gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) {
- mVsyncRate = TimeDuration::FromMilliseconds(1000.0 / 60.0);
+ DWM_TIMING_INFO vblankTime;
+ // Make sure to init the cbSize, otherwise GetCompositionTiming will fail
+ vblankTime.cbSize = sizeof(DWM_TIMING_INFO);
+ HRESULT hr = WinUtils::dwmGetCompositionTimingInfoPtr(0, &vblankTime);
+ if (SUCCEEDED(hr)) {
+ UNSIGNED_RATIO refreshRate = vblankTime.rateRefresh;
+ // We get the rate in hertz / time, but we want the rate in ms.
+ float rate = ((float) refreshRate.uiDenominator
+ / (float) refreshRate.uiNumerator) * 1000;
+ mVsyncInterval = TimeDuration::FromMilliseconds(rate);
+ } else {
+ mVsyncInterval = gfxPlatform::GetSoftwareVsyncInterval();
+ }
+ }
+
+ virtual bool IsVsyncEnabled() override
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ MonitorAutoLock lock(mVsyncEnabledLock);
+ return mVsyncEnabled;
+ }
+
+ virtual void EnableVsync() override
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mVsyncThread->IsRunning());
+ { // scope lock
+ MonitorAutoLock lock(mVsyncEnabledLock);
+ if (mVsyncEnabled) {
return;
}
-
- DWM_TIMING_INFO vblankTime;
- // Make sure to init the cbSize, otherwise GetCompositionTiming will fail
- vblankTime.cbSize = sizeof(DWM_TIMING_INFO);
- HRESULT hr = WinUtils::dwmGetCompositionTimingInfoPtr(0, &vblankTime);
- if (SUCCEEDED(hr)) {
- UNSIGNED_RATIO refreshRate = vblankTime.rateRefresh;
- // We get the rate in hertz / time, but we want the rate in ms.
- float rate = ((float) refreshRate.uiDenominator
- / (float) refreshRate.uiNumerator) * 1000;
- mVsyncRate = TimeDuration::FromMilliseconds(rate);
- } else {
- mVsyncRate = TimeDuration::FromMilliseconds(1000.0 / 60.0);
- }
+ mVsyncEnabled = true;
}
- virtual void EnableVsync() override
+ CancelableTask* vsyncTask = NewRunnableMethod(this, &DWMVsyncSource::VBlankLoop);
+ mVsyncThread->message_loop()->PostTask(FROM_HERE, vsyncTask);
+ }
+
+ virtual void DisableVsync() override
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mVsyncThread->IsRunning());
{
- MOZ_ASSERT(NS_IsMainThread());
- MOZ_ASSERT(mVsyncThread->IsRunning());
- { // scope lock
- MonitorAutoLock lock(mVsyncEnabledLock);
- if (mVsyncEnabled) {
- return;
- }
- mVsyncEnabled = true;
- }
-
- CancelableTask* vsyncStart = NewRunnableMethod(this,
- &D3DVsyncDisplay::VBlankLoop);
- mVsyncThread->message_loop()->PostTask(FROM_HERE, vsyncStart);
- }
-
- virtual void DisableVsync() override
- {
- MOZ_ASSERT(NS_IsMainThread());
- MOZ_ASSERT(mVsyncThread->IsRunning());
MonitorAutoLock lock(mVsyncEnabledLock);
if (!mVsyncEnabled) {
return;
}
mVsyncEnabled = false;
}
-
- virtual bool IsVsyncEnabled() override
- {
- MOZ_ASSERT(NS_IsMainThread());
- MonitorAutoLock lock(mVsyncEnabledLock);
- return mVsyncEnabled;
- }
-
- virtual TimeDuration GetVsyncRate() override
- {
- return mVsyncRate;
- }
-
- void ScheduleSoftwareVsync(TimeStamp aVsyncTimestamp)
- {
- MOZ_ASSERT(IsInVsyncThread());
- NS_WARNING("DwmComposition dynamically disabled, falling back to software timers");
-
- TimeStamp nextVsync = aVsyncTimestamp + mSoftwareVsyncRate;
- TimeDuration delay = nextVsync - TimeStamp::Now();
- if (delay.ToMilliseconds() < 0) {
- delay = mozilla::TimeDuration::FromMilliseconds(0);
- }
-
- mVsyncThread->message_loop()->PostDelayedTask(FROM_HERE,
- NewRunnableMethod(this, &D3DVsyncDisplay::VBlankLoop),
- delay.ToMilliseconds());
+ }
+
+ virtual TimeDuration GetVsyncInterval() override
+ {
+ return mVsyncInterval;
+ }
+
+ void ScheduleSoftwareVsync(TimeStamp aVsyncTimestamp)
+ {
+ MOZ_ASSERT(IsInVsyncThread());
+ NS_WARNING("DwmComposition dynamically disabled, falling back to software timers");
+
+ TimeStamp nextVsync = aVsyncTimestamp + mVsyncInterval;
+ TimeDuration delay = nextVsync - TimeStamp::Now();
+ if (delay.ToMilliseconds() < 0) {
+ delay = mozilla::TimeDuration::FromMilliseconds(0);
}
- TimeStamp GetAdjustedVsyncTimeStamp(LARGE_INTEGER& aFrequency,
- QPC_TIME& aQpcVblankTime)
- {
- TimeStamp vsync = TimeStamp::Now();
- LARGE_INTEGER qpcNow;
- QueryPerformanceCounter(&qpcNow);
-
- const int microseconds = 1000000;
- int64_t adjust = qpcNow.QuadPart - aQpcVblankTime;
- int64_t usAdjust = (adjust * microseconds) / aFrequency.QuadPart;
- vsync -= TimeDuration::FromMicroseconds((double) usAdjust);
-
- if (IsWin10OrLater()) {
- // On Windows 10 and on, DWMGetCompositionTimingInfo, mostly
- // reports the upcoming vsync time, which is in the future.
- // It can also sometimes report a vblank time in the past.
- // Since large parts of Gecko assume TimeStamps can't be in future,
- // use the previous vsync.
-
- // Windows 10 and Intel HD vsync timestamps are messy and
- // all over the place once in a while. Most of the time,
- // it reports the upcoming vsync. Sometimes, that upcoming
- // vsync is in the past. Sometimes that upcoming vsync is before
- // the previously seen vsync. Sometimes, the previous vsync
- // is still in the future. In these error cases,
- // we try to normalize to Now().
- TimeStamp upcomingVsync = vsync;
- if (upcomingVsync < mPrevVsync) {
- // Windows can report a vsync that's before
- // the previous one. So update it to sometime in the future.
- upcomingVsync = TimeStamp::Now() + TimeDuration::FromMilliseconds(1);
- }
-
- vsync = mPrevVsync;
- mPrevVsync = upcomingVsync;
+ mVsyncThread->message_loop()->PostDelayedTask(FROM_HERE,
+ NewRunnableMethod(this, &DWMVsyncSource::VBlankLoop),
+ delay.ToMilliseconds());
+ }
+
+ TimeStamp GetAdjustedVsyncTimeStamp(LARGE_INTEGER& aFrequency,
+ QPC_TIME& aQpcVblankTime)
+ {
+ TimeStamp vsync = TimeStamp::Now();
+ LARGE_INTEGER qpcNow;
+ QueryPerformanceCounter(&qpcNow);
+
+ const int microseconds = 1000000;
+ int64_t adjust = qpcNow.QuadPart - aQpcVblankTime;
+ int64_t usAdjust = (adjust * microseconds) / aFrequency.QuadPart;
+ vsync -= TimeDuration::FromMicroseconds((double) usAdjust);
+
+ if (IsWin10OrLater()) {
+ // On Windows 10 and on, DWMGetCompositionTimingInfo, mostly
+ // reports the upcoming vsync time, which is in the future.
+ // It can also sometimes report a vblank time in the past.
+ // Since large parts of Gecko assume TimeStamps can't be in future,
+ // use the previous vsync.
+
+ // Windows 10 and Intel HD vsync timestamps are messy and
+ // all over the place once in a while. Most of the time,
+ // it reports the upcoming vsync. Sometimes, that upcoming
+ // vsync is in the past. Sometimes that upcoming vsync is before
+ // the previously seen vsync. Sometimes, the previous vsync
+ // is still in the future. In these error cases,
+ // we try to normalize to Now().
+ TimeStamp upcomingVsync = vsync;
+ if (upcomingVsync < mPrevVsync) {
+ // Windows can report a vsync that's before
+ // the previous one. So update it to sometime in the future.
+ upcomingVsync = TimeStamp::Now() + TimeDuration::FromMilliseconds(1);
}
- // On Windows 7 and 8, DwmFlush wakes up AFTER qpcVBlankTime
- // from DWMGetCompositionTimingInfo. We can return the adjusted vsync.
-
- // Once in a while, the reported vsync timestamp can be in the future.
- // Normalize the reported timestamp to now.
- if (vsync >= TimeStamp::Now()) {
- vsync = TimeStamp::Now();
- }
- return vsync;
+
+ vsync = mPrevVsync;
+ mPrevVsync = upcomingVsync;
+ }
+ // On Windows 7 and 8, DwmFlush wakes up AFTER qpcVBlankTime
+ // from DWMGetCompositionTimingInfo. We can return the adjusted vsync.
+
+ // Once in a while, the reported vsync timestamp can be in the future.
+ // Normalize the reported timestamp to now.
+ if (vsync >= TimeStamp::Now()) {
+ vsync = TimeStamp::Now();
}
-
- void VBlankLoop()
- {
- MOZ_ASSERT(IsInVsyncThread());
- MOZ_ASSERT(sizeof(int64_t) == sizeof(QPC_TIME));
-
- DWM_TIMING_INFO vblankTime;
- // Make sure to init the cbSize, otherwise GetCompositionTiming will fail
- vblankTime.cbSize = sizeof(DWM_TIMING_INFO);
-
- LARGE_INTEGER frequency;
- QueryPerformanceFrequency(&frequency);
- TimeStamp vsync = TimeStamp::Now();
- // On Windows 10, DwmGetCompositionInfo returns the upcoming vsync.
- // See GetAdjustedVsyncTimestamp.
- // On start, set mPrevVsync to the "next" vsync
- // So we'll use this timestamp on the 2nd loop iteration.
- mPrevVsync = vsync + mSoftwareVsyncRate;
-
- for (;;) {
- { // scope lock
- MonitorAutoLock lock(mVsyncEnabledLock);
- if (!mVsyncEnabled) return;
- }
-
- // Large parts of gecko assume that the refresh driver timestamp
- // must be <= Now() and cannot be in the future.
- MOZ_ASSERT(vsync <= TimeStamp::Now());
- Display::NotifyVsync(vsync);
-
- // DwmComposition can be dynamically enabled/disabled
- // so we have to check every time that it's available.
- // When it is unavailable, we fallback to software but will try
- // to get back to dwm rendering once it's re-enabled
- if (!gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) {
- ScheduleSoftwareVsync(vsync);
- return;
- }
-
- // Use a combination of DwmFlush + DwmGetCompositionTimingInfoPtr
- // Using WaitForVBlank, the whole system dies :/
- HRESULT hr = WinUtils::dwmFlushProcPtr();
- if (!SUCCEEDED(hr)) {
- // We don't actually know how long we had to wait on DWMFlush
- // Instead of trying to calculate how long DwmFlush actually took
- // Fallback to software vsync.
- ScheduleSoftwareVsync(TimeStamp::Now());
- return;
- }
-
- hr = WinUtils::dwmGetCompositionTimingInfoPtr(0, &vblankTime);
- vsync = SUCCEEDED(hr) ?
- GetAdjustedVsyncTimeStamp(frequency, vblankTime.qpcVBlank) :
- TimeStamp::Now();
- } // end for
- }
-
- private:
- virtual ~D3DVsyncDisplay()
- {
- MOZ_ASSERT(NS_IsMainThread());
- DisableVsync();
- mVsyncThread->Stop();
- delete mVsyncThread;
- }
-
- bool IsInVsyncThread()
- {
- return mVsyncThread->thread_id() == PlatformThread::CurrentId();
- }
-
- TimeDuration mSoftwareVsyncRate;
- TimeStamp mPrevVsync; // Only used on Windows 10
- Monitor mVsyncEnabledLock;
- base::Thread* mVsyncThread;
- TimeDuration mVsyncRate;
- bool mVsyncEnabled;
- }; // end d3dvsyncdisplay
-
- D3DVsyncSource()
- {
- mPrimaryDisplay = new D3DVsyncDisplay();
- }
-
- virtual Display& GetGlobalDisplay() override
- {
- return *mPrimaryDisplay;
- }
-
-private:
- virtual ~D3DVsyncSource()
- {
- }
- RefPtr<D3DVsyncDisplay> mPrimaryDisplay;
-}; // end D3DVsyncSource
-
-already_AddRefed<mozilla::gfx::VsyncSource>
-gfxWindowsPlatform::CreateHardwareVsyncSource()
+ return vsync;
+ }
+
+ void VBlankLoop()
+ {
+ MOZ_ASSERT(IsInVsyncThread());
+ MOZ_ASSERT(sizeof(int64_t) == sizeof(QPC_TIME));
+
+ DWM_TIMING_INFO vblankTime;
+ // Make sure to init the cbSize, otherwise GetCompositionTiming will fail
+ vblankTime.cbSize = sizeof(DWM_TIMING_INFO);
+
+ LARGE_INTEGER frequency;
+ QueryPerformanceFrequency(&frequency);
+ TimeStamp vsync = TimeStamp::Now();
+ // On Windows 10, DwmGetCompositionInfo returns the upcoming vsync.
+ // See GetAdjustedVsyncTimestamp.
+ // On start, set mPrevVsync to the "next" vsync
+ // So we'll use this timestamp on the 2nd loop iteration.
+ mPrevVsync = vsync + mVsyncInterval;
+
+ for (;;) {
+ { // scope lock
+ MonitorAutoLock lock(mVsyncEnabledLock);
+ if (!mVsyncEnabled) return;
+ }
+
+ // Large parts of gecko assume that the refresh driver timestamp
+ // must be <= Now() and cannot be in the future.
+ MOZ_ASSERT(vsync <= TimeStamp::Now());
+ VsyncSource::OnVsync(vsync);
+
+ // DwmComposition can be dynamically enabled/disabled
+ // so we have to check every time that it's available.
+ // When it is unavailable, we fallback to software but will try
+ // to get back to dwm rendering once it's re-enabled
+ if (!DwmCompositionEnabled()) {
+ ScheduleSoftwareVsync(vsync);
+ return;
+ }
+
+ // Use a combination of DwmFlush + DwmGetCompositionTimingInfoPtr
+ // Using WaitForVBlank, the whole system dies :/
+ HRESULT hr = WinUtils::dwmFlushProcPtr();
+ if (!SUCCEEDED(hr)) {
+ // We don't actually know how long we had to wait on DWMFlush
+ // Instead of trying to calculate how long DwmFlush actually took
+ // Fallback to software vsync.
+ ScheduleSoftwareVsync(TimeStamp::Now());
+ return;
+ }
+ hr = WinUtils::dwmGetCompositionTimingInfoPtr(0, &vblankTime);
+ vsync = SUCCEEDED(hr) ?
+ GetAdjustedVsyncTimeStamp(frequency, vblankTime.qpcVBlank) :
+ TimeStamp::Now();
+ } // end for
+ }
+
+ private:
+ virtual ~DWMVsyncSource()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ // should have been stopped in Shutdown
+ MOZ_ASSERT(!mVsyncThread->IsRunning());
+ delete mVsyncThread;
+ }
+
+ void Shutdown() override {
+ DisableVsync();
+ mVsyncThread->Stop();
+
+ VsyncSource::Shutdown();
+ }
+
+ bool IsInVsyncThread()
+ {
+ return mVsyncThread->thread_id() == PlatformThread::CurrentId();
+ }
+
+ TimeStamp mPrevVsync; // Only used on Windows 10
+ Monitor mVsyncEnabledLock;
+ base::Thread* mVsyncThread;
+ bool mVsyncEnabled;
+ TimeDuration mVsyncInterval;
+}; // DWMVsyncSource
+
+already_AddRefed<mozilla::gfx::VsyncManager>
+gfxWindowsPlatform::CreateHardwareVsyncManager()
{
MOZ_RELEASE_ASSERT(NS_IsMainThread());
if (!WinUtils::dwmIsCompositionEnabledPtr) {
NS_WARNING("Dwm composition not available, falling back to software vsync");
- return gfxPlatform::CreateHardwareVsyncSource();
+ return gfxPlatform::CreateSoftwareVsyncManager();
}
BOOL dwmEnabled = false;
WinUtils::dwmIsCompositionEnabledPtr(&dwmEnabled);
if (!dwmEnabled) {
NS_WARNING("DWM not enabled, falling back to software vsync");
- return gfxPlatform::CreateHardwareVsyncSource();
+ return gfxPlatform::CreateSoftwareVsyncManager();
}
- RefPtr<VsyncSource> d3dVsyncSource = new D3DVsyncSource();
- return d3dVsyncSource.forget();
+ RefPtr<VsyncManager> vsyncManager = new VsyncManager();
+
+ RefPtr<DWMVsyncSource> globalDisplay = new DWMVsyncSource(VsyncManager::kGlobalDisplaySourceID);
+ vsyncManager->RegisterSource(globalDisplay);
+
+ return vsyncManager.forget();
}
bool
gfxWindowsPlatform::SupportsApzTouchInput() const
{
int value = gfxPrefs::TouchEventsEnabled();
return value == 1 || value == 2;
}
--- a/gfx/thebes/gfxWindowsPlatform.h
+++ b/gfx/thebes/gfxWindowsPlatform.h
@@ -248,17 +248,17 @@ public:
// initialization has not been attempted, this returns
// FeatureStatus::Unused.
mozilla::gfx::FeatureStatus GetD3D11Status() const;
mozilla::gfx::FeatureStatus GetD2D1Status() const;
unsigned GetD3D11Version();
void TestDeviceReset(DeviceResetReason aReason);
- virtual already_AddRefed<mozilla::gfx::VsyncSource> CreateHardwareVsyncSource() override;
+ virtual already_AddRefed<mozilla::gfx::VsyncManager> CreateHardwareVsyncManager() override;
static mozilla::Atomic<size_t> sD3D11MemoryUsed;
static mozilla::Atomic<size_t> sD3D9MemoryUsed;
static mozilla::Atomic<size_t> sD3D9SharedTextureUsed;
void GetDeviceInitData(mozilla::gfx::DeviceInitData* aOut) override;
bool SupportsPluginDirectBitmapDrawing() override {
return true;
--- a/gfx/thebes/moz.build
+++ b/gfx/thebes/moz.build
@@ -42,19 +42,18 @@ EXPORTS += [
'gfxSharedQuartzSurface.h',
'gfxSkipChars.h',
'gfxSVGGlyphs.h',
'gfxTeeSurface.h',
'gfxTextRun.h',
'gfxTypes.h',
'gfxUserFontSet.h',
'gfxUtils.h',
+ 'gfxVsync.h',
'RoundedRect.h',
- 'SoftwareVsyncSource.h',
- 'VsyncSource.h',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
EXPORTS += [
'gfxAndroidPlatform.h',
'gfxFT2FontBase.h',
'gfxFT2Fonts.h',
'gfxPDFSurface.h',
@@ -231,19 +230,18 @@ UNIFIED_SOURCES += [
'gfxRect.cpp',
'gfxScriptItemizer.cpp',
'gfxSkipChars.cpp',
'gfxSVGGlyphs.cpp',
'gfxTeeSurface.cpp',
'gfxTextRun.cpp',
'gfxUserFontSet.cpp',
'gfxUtils.cpp',
+ 'gfxVsync.cpp',
'nsUnicodeRange.cpp',
- 'SoftwareVsyncSource.cpp',
- 'VsyncSource.cpp',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
UNIFIED_SOURCES += [
'gfxMacPlatformFontList.mm',
]
# We prefer to use ICU for normalization functions, but currently it is only
--- a/widget/gonk/HwcComposer2D.cpp
+++ b/widget/gonk/HwcComposer2D.cpp
@@ -31,17 +31,17 @@
#include "mozilla/layers/PLayerTransaction.h"
#include "mozilla/layers/ShadowLayerUtilsGralloc.h"
#include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL
#include "mozilla/StaticPtr.h"
#include "nsThreadUtils.h"
#include "cutils/properties.h"
#include "gfx2DGlue.h"
#include "gfxPlatform.h"
-#include "VsyncSource.h"
+#include "gfxVsync.h"
#include "nsScreenManagerGonk.h"
#include "nsWindow.h"
#if ANDROID_VERSION >= 17
#include "libdisplay/DisplaySurface.h"
#endif
#ifdef LOG_TAG
@@ -116,16 +116,18 @@ HwcComposer2D::HwcComposer2D()
GonkDisplay::NativeData data = GetGonkDisplay()->GetNativeData(GonkDisplay::DISPLAY_PRIMARY);
ANativeWindow *win = data.mNativeWindow.get();
win->query(win, NATIVE_WINDOW_WIDTH, &screenSize.width);
win->query(win, NATIVE_WINDOW_HEIGHT, &screenSize.height);
mScreenRect = gfx::IntRect(gfx::IntPoint(0, 0), screenSize);
mColorFill = mHal->Query(HwcHALBase::QueryType::COLOR_FILL);
mRBSwapSupport = mHal->Query(HwcHALBase::QueryType::RB_SWAP);
+
+ mVsyncSource = gfxPlatform::GetPlatform()->GetHardwareVsync()->GetGlobalDisplaySource();
}
HwcComposer2D::~HwcComposer2D()
{
free(mList);
}
HwcComposer2D*
@@ -178,17 +180,17 @@ HwcComposer2D::RegisterHwcEventCallback(
void
HwcComposer2D::Vsync(int aDisplay, nsecs_t aVsyncTimestamp)
{
// Only support hardware vsync on kitkat, L and up due to inaccurate timings
// with JellyBean.
#if (ANDROID_VERSION == 19 || ANDROID_VERSION >= 21)
TimeStamp vsyncTime = mozilla::TimeStamp::FromSystemTime(aVsyncTimestamp);
- gfxPlatform::GetPlatform()->GetHardwareVsync()->GetGlobalDisplay().NotifyVsync(vsyncTime);
+ mVsyncSource->OnVsync(vsyncTime);
#else
// If this device doesn't support vsync, this function should not be used.
MOZ_ASSERT(false);
#endif
}
// Called on the "invalidator" thread (run from HAL).
void
--- a/widget/gonk/HwcComposer2D.h
+++ b/widget/gonk/HwcComposer2D.h
@@ -35,16 +35,20 @@
class nsScreenGonk;
namespace mozilla {
namespace gl {
class GLContext;
}
+namespace gfx {
+class VsyncSource;
+}
+
namespace layers {
class CompositorBridgeParent;
class Layer;
}
/*
* HwcComposer2D provides a way for gecko to render frames
* using hwcomposer.h in the AOSP HAL.
@@ -111,13 +115,14 @@ private:
layers::FenceHandle mPrevRetireFence;
layers::FenceHandle mPrevDisplayFence;
nsTArray<HwcLayer> mCachedSidebandLayers;
nsTArray<layers::LayerComposite*> mHwcLayerMap;
bool mPrepared;
bool mHasHWVsync;
layers::CompositorBridgeParent* mCompositorBridgeParent;
Mutex mLock;
+ RefPtr<gfx::VsyncSource> mVsyncSource;
};
} // namespace mozilla
#endif // mozilla_HwcComposer2D
--- a/widget/gonk/nsScreenManagerGonk.cpp
+++ b/widget/gonk/nsScreenManagerGonk.cpp
@@ -20,17 +20,17 @@
#include "mozilla/MouseEvents.h"
#include "mozilla/TouchEvents.h"
#include "mozilla/Hal.h"
#include "libdisplay/BootAnimation.h"
#include "libdisplay/GonkDisplay.h"
#include "nsScreenManagerGonk.h"
#include "nsThreadUtils.h"
#include "HwcComposer2D.h"
-#include "VsyncSource.h"
+#include "gfxVsync.h"
#include "nsWindow.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/layers/CompositorBridgeParent.h"
#include "mozilla/Services.h"
#include "mozilla/ProcessPriorityManager.h"
#include "nsIdleService.h"
#include "nsIObserverService.h"
#include "nsAppShell.h"
@@ -905,21 +905,21 @@ nsScreenManagerGonk::VsyncControl(bool a
NS_DispatchToMainThread(
NS_NewRunnableMethodWithArgs<bool>(this,
&nsScreenManagerGonk::VsyncControl,
aEnabled));
return;
}
MOZ_ASSERT(NS_IsMainThread());
- VsyncSource::Display &display = gfxPlatform::GetPlatform()->GetHardwareVsync()->GetGlobalDisplay();
+ RefPtr<VsyncSource> display = gfxPlatform::GetPlatform()->GetHardwareVsync()->GetGlobalDisplaySource();
if (aEnabled) {
- display.EnableVsync();
+ display->EnableVsync();
} else {
- display.DisableVsync();
+ display->DisableVsync();
}
}
uint32_t
nsScreenManagerGonk::GetIdFromType(GonkDisplay::DisplayType aDisplayType)
{
// This is the only place where we make the assumption that
// display type is equivalent to screen id.