Bug 552020. Part 1: Hook into CVDisplayLink to get vsync events on OSX. r=benwa,mstange

This commit is contained in:
Mason Chang 2014-11-14 08:31:04 -08:00
parent 03b8edc061
commit be755464d3
4 changed files with 124 additions and 7 deletions

View File

@ -41,6 +41,12 @@ VsyncDispatcher::~VsyncDispatcher()
mCompositorObservers.Clear();
}
void
VsyncDispatcher::SetVsyncSource(VsyncSource* aVsyncSource)
{
mVsyncSource = aVsyncSource;
}
void
VsyncDispatcher::DispatchTouchEvents(bool aNotifiedCompositors, TimeStamp aVsyncTime)
{
@ -88,7 +94,7 @@ VsyncDispatcher::AddCompositorVsyncObserver(VsyncObserver* aVsyncObserver)
void
VsyncDispatcher::RemoveCompositorVsyncObserver(VsyncObserver* aVsyncObserver)
{
MOZ_ASSERT(CompositorParent::IsInCompositorThread());
MOZ_ASSERT(CompositorParent::IsInCompositorThread() || NS_IsMainThread());
MutexAutoLock lock(mCompositorObserverLock);
if (mCompositorObservers.Contains(aVsyncObserver)) {
mCompositorObservers.RemoveElement(aVsyncObserver);

View File

@ -21,6 +21,19 @@ namespace layers {
class CompositorVsyncObserver;
}
// Controls how and when to enable/disable vsync.
class VsyncSource
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VsyncSource)
virtual void EnableVsync() = 0;
virtual void DisableVsync() = 0;
virtual bool IsVsyncEnabled() = 0;
protected:
virtual ~VsyncSource() {}
}; // VsyncSource
class VsyncObserver
{
// Must be destroyed on main thread since the compositor is as well
@ -34,7 +47,7 @@ public:
protected:
VsyncObserver() {}
virtual ~VsyncObserver() {}
};
}; // VsyncObserver
// VsyncDispatcher is used to dispatch vsync events to the registered observers.
class VsyncDispatcher
@ -44,7 +57,13 @@ class VsyncDispatcher
public:
static VsyncDispatcher* GetInstance();
// Called on the vsync thread when a hardware vsync occurs
// The aVsyncTimestamp can mean different things depending on the platform:
// b2g - The vsync timestamp of the previous frame that was just displayed
// OSX - The vsync timestamp of the upcoming frame
// TODO: Windows / Linux. DOCUMENT THIS WHEN IMPLEMENTING ON THOSE PLATFORMS
// Android: TODO
void NotifyVsync(TimeStamp aVsyncTimestamp);
void SetVsyncSource(VsyncSource* aVsyncSource);
// Compositor vsync observers must be added/removed on the compositor thread
void AddCompositorVsyncObserver(VsyncObserver* aVsyncObserver);
@ -61,7 +80,8 @@ private:
// Can have multiple compositors. On desktop, this is 1 compositor per window
Mutex mCompositorObserverLock;
nsTArray<nsRefPtr<VsyncObserver>> mCompositorObservers;
};
nsRefPtr<VsyncSource> mVsyncSource;
}; // VsyncDispatcher
} // namespace mozilla

View File

@ -5,7 +5,7 @@
/*
* Runs the main native Cocoa run loop, interrupting it as needed to process
* Gecko events.
* Gecko events.
*/
#ifndef nsAppShell_h_
@ -30,7 +30,7 @@ class nsAppShell : public nsBaseAppShell
{
public:
NS_IMETHOD ResumeNative(void);
nsAppShell();
nsresult Init();

View File

@ -9,6 +9,7 @@
*/
#import <Cocoa/Cocoa.h>
#import <CoreVideo/CoreVideo.h>
#include "CustomCocoaEvents.h"
#include "mozilla/WidgetTraceEvent.h"
@ -37,6 +38,9 @@
#include <IOKit/pwr_mgt/IOPMLib.h>
#include "nsIDOMWakeLockListener.h"
#include "nsIPowerManagerService.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/VsyncDispatcher.h"
#include "gfxPrefs.h"
using namespace mozilla::widget;
@ -87,7 +91,85 @@ private:
}
return NS_OK;
}
};
}; // MacWakeLockListener
// This is the renderer output callback function, called on the vsync thread
static CVReturn VsyncCallback(CVDisplayLinkRef aDisplayLink,
const CVTimeStamp* aNow,
const CVTimeStamp* aOutputTime,
CVOptionFlags aFlagsIn,
CVOptionFlags* aFlagsOut,
void* aDisplayLinkContext)
{
VsyncSource* vsyncSource = (VsyncSource*) aDisplayLinkContext;
if (vsyncSource->IsVsyncEnabled()) {
// Now refers to "Now" as in when this callback is called or when the current frame
// is displayed. aOutputTime is when the next frame should be displayed.
// Now is VERY VERY noisy, aOutputTime is in the future though.
int64_t timestamp = aOutputTime->hostTime;
mozilla::TimeStamp vsyncTime = mozilla::TimeStamp::FromSystemTime(timestamp);
mozilla::VsyncDispatcher::GetInstance()->NotifyVsync(vsyncTime);
return kCVReturnSuccess;
} else {
return kCVReturnDisplayLinkNotRunning;
}
}
class OSXVsyncSource MOZ_FINAL : public VsyncSource
{
public:
OSXVsyncSource()
{
EnableVsync();
}
virtual void EnableVsync() MOZ_OVERRIDE
{
// 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, returning");
return;
}
// Set the renderer output callback function
if (CVDisplayLinkSetOutputCallback(mDisplayLink, &VsyncCallback, this) != kCVReturnSuccess) {
NS_WARNING("Could not set displaylink output callback");
return;
}
// Activate the display link
if (CVDisplayLinkStart(mDisplayLink) != kCVReturnSuccess) {
NS_WARNING("Could not activate the display link");
mDisplayLink = nullptr;
}
}
virtual void DisableVsync() MOZ_OVERRIDE
{
// Release the display link
if (mDisplayLink) {
CVDisplayLinkRelease(mDisplayLink);
mDisplayLink = nullptr;
}
}
virtual bool IsVsyncEnabled() MOZ_OVERRIDE
{
return mDisplayLink != nullptr;
}
private:
virtual ~OSXVsyncSource()
{
DisableVsync();
}
// Manages the display link render thread
CVDisplayLinkRef mDisplayLink;
}; // OSXVsyncSource
// defined in nsCocoaWindow.mm
extern int32_t gXULModalLevel;
@ -287,7 +369,7 @@ nsAppShell::Init()
// context.version = 0;
context.info = this;
context.perform = ProcessGeckoEvents;
mCFRunLoopSource = ::CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
NS_ENSURE_STATE(mCFRunLoopSource);
@ -295,6 +377,15 @@ nsAppShell::Init()
rv = nsBaseAppShell::Init();
// gfxPrefs are init on the main thread and we need it super early
// to see if we should enable vsync aligned compositor
gfxPrefs::GetSingleton();
if (gfxPrefs::HardwareVsyncEnabled() && gfxPrefs::VsyncAlignedCompositor())
{
nsRefPtr<VsyncSource> osxVsyncSource = new OSXVsyncSource();
mozilla::VsyncDispatcher::GetInstance()->SetVsyncSource(osxVsyncSource);
}
#ifndef __LP64__
TextInputHandler::InstallPluginKeyEventsHandler();
#endif