Browse Source

Implement CVDisplayLink based timer on macOS

pull/12652/head
Max Katz 3 years ago
parent
commit
d07e59bc5e
  1. 4
      native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj
  2. 84
      native/Avalonia.Native/src/OSX/PlatformRenderTimer.mm
  3. 1
      native/Avalonia.Native/src/OSX/common.h
  4. 11
      native/Avalonia.Native/src/OSX/main.mm
  5. 2
      src/Avalonia.Native/AvaloniaNativePlatform.cs
  6. 58
      src/Avalonia.Native/AvaloniaNativeRenderTimer.cs
  7. 10
      src/Avalonia.Native/avn.idl

4
native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj

@ -59,6 +59,7 @@
BC11A5BE2608D58F0017BAD0 /* automation.h in Headers */ = {isa = PBXBuildFile; fileRef = BC11A5BC2608D58F0017BAD0 /* automation.h */; };
BC11A5BF2608D58F0017BAD0 /* automation.mm in Sources */ = {isa = PBXBuildFile; fileRef = BC11A5BD2608D58F0017BAD0 /* automation.mm */; };
ED3791C42862E1F40080BD62 /* UniformTypeIdentifiers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED3791C32862E1F40080BD62 /* UniformTypeIdentifiers.framework */; };
ED754D262A97306B0078B4DF /* PlatformRenderTimer.mm in Sources */ = {isa = PBXBuildFile; fileRef = ED754D252A97306B0078B4DF /* PlatformRenderTimer.mm */; };
EDF8CDCD2964CB01001EE34F /* PlatformSettings.mm in Sources */ = {isa = PBXBuildFile; fileRef = EDF8CDCC2964CB01001EE34F /* PlatformSettings.mm */; };
/* End PBXBuildFile section */
@ -122,6 +123,7 @@
BC11A5BC2608D58F0017BAD0 /* automation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = automation.h; sourceTree = "<group>"; };
BC11A5BD2608D58F0017BAD0 /* automation.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = automation.mm; sourceTree = "<group>"; };
ED3791C32862E1F40080BD62 /* UniformTypeIdentifiers.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UniformTypeIdentifiers.framework; path = System/Library/Frameworks/UniformTypeIdentifiers.framework; sourceTree = SDKROOT; };
ED754D252A97306B0078B4DF /* PlatformRenderTimer.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PlatformRenderTimer.mm; sourceTree = "<group>"; };
EDF8CDCC2964CB01001EE34F /* PlatformSettings.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PlatformSettings.mm; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -162,6 +164,7 @@
AB7A61E62147C814003C5833 = {
isa = PBXGroup;
children = (
ED754D252A97306B0078B4DF /* PlatformRenderTimer.mm */,
855EDC9E28C6546F00807998 /* PlatformBehaviorInhibition.mm */,
8D2F3511292F6AAE007FCF54 /* AvnTextInputMethodDelegate.h */,
8D300D68292E1E5D00320C49 /* AvnTextInputMethod.mm */,
@ -333,6 +336,7 @@
18391068E48EF96E3DB5FDAB /* ResizeScope.mm in Sources */,
18391D4EB311BC7EF8B8C0A6 /* AvnView.mm in Sources */,
18391AA7E0BBA74D184C5734 /* AutoFitContentView.mm in Sources */,
ED754D262A97306B0078B4DF /* PlatformRenderTimer.mm in Sources */,
1839151F32D1BB1AB51A7BB6 /* AvnPanelWindow.mm in Sources */,
18391AC16726CBC45856233B /* AvnWindow.mm in Sources */,
18391D8CD1756DC858DC1A09 /* PopupImpl.mm in Sources */,

84
native/Avalonia.Native/src/OSX/PlatformRenderTimer.mm

@ -0,0 +1,84 @@
#include "common.h"
class PlatformRenderTimer : public ComSingleObject<IAvnPlatformRenderTimer, &IID_IAvnPlatformRenderTimer>
{
private:
ComPtr<IAvnActionCallback> _callback;
CVDisplayLinkRef _displayLink;
public:
FORWARD_IUNKNOWN()
virtual HRESULT RegisterTick (
IAvnActionCallback* callback) override
{
START_COM_CALL;
@autoreleasepool
{
if (_displayLink != nil)
{
return E_UNEXPECTED;
}
_callback = callback;
auto result = CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink);
if (result != 0)
{
return E_FAIL;
}
result = CVDisplayLinkSetOutputCallback(_displayLink, OnTick, this);
if (result != 0)
{
return E_FAIL;
}
}
return S_OK;
}
virtual void Start () override
{
START_COM_CALL;
@autoreleasepool
{
if (CVDisplayLinkIsRunning(_displayLink) == false) {
CVDisplayLinkStart(_displayLink);
}
}
}
virtual void Stop () override
{
START_COM_CALL;
@autoreleasepool
{
if (CVDisplayLinkIsRunning(_displayLink) == true) {
CVDisplayLinkStop(_displayLink);
}
}
}
virtual bool RunsInBackground () override
{
START_COM_CALL;
@autoreleasepool
{
return true;
}
}
static CVReturn OnTick(CVDisplayLinkRef displayLink, const CVTimeStamp *inNow, const CVTimeStamp *inOutputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext)
{
PlatformRenderTimer *object = (PlatformRenderTimer *)displayLinkContext;
object->_callback->Run();
return kCVReturnSuccess;
}
};
extern IAvnPlatformRenderTimer* CreatePlatformRenderTimer()
{
return new PlatformRenderTimer();
}

1
native/Avalonia.Native/src/OSX/common.h

@ -32,6 +32,7 @@ extern IAvnApplicationCommands* CreateApplicationCommands();
extern IAvnPlatformBehaviorInhibition* CreatePlatformBehaviorInhibition();
extern IAvnNativeControlHost* CreateNativeControlHost(NSView* parent);
extern IAvnPlatformSettings* CreatePlatformSettings();
extern IAvnPlatformRenderTimer* CreatePlatformRenderTimer();
extern void SetAppMenu(IAvnMenu *menu);
extern void SetServicesMenu (IAvnMenu* menu);
extern IAvnMenu* GetAppMenu ();

11
native/Avalonia.Native/src/OSX/main.mm

@ -446,6 +446,17 @@ public:
return S_OK;
}
}
virtual HRESULT CreatePlatformRenderTimer(IAvnPlatformRenderTimer** ppv) override
{
START_COM_CALL;
@autoreleasepool
{
*ppv = ::CreatePlatformRenderTimer();
return S_OK;
}
}
};
extern "C" IAvaloniaNativeFactory* CreateAvaloniaNative()

2
src/Avalonia.Native/AvaloniaNativePlatform.cs

@ -108,7 +108,7 @@ namespace Avalonia.Native
.Bind<IPlatformSettings>().ToConstant(new NativePlatformSettings(_factory.CreatePlatformSettings()))
.Bind<IWindowingPlatform>().ToConstant(this)
.Bind<IClipboard>().ToConstant(new ClipboardImpl(_factory.CreateClipboard()))
.Bind<IRenderTimer>().ToConstant(new DefaultRenderTimer(60))
.Bind<IRenderTimer>().ToConstant(new AvaloniaNativeRenderTimer(_factory.CreatePlatformRenderTimer()))
.Bind<IMountedVolumeInfoProvider>().ToConstant(new MacOSMountedVolumeInfoProvider())
.Bind<IPlatformDragSource>().ToConstant(new AvaloniaNativeDragSource(_factory))
.Bind<IPlatformLifetimeEventsImpl>().ToConstant(applicationPlatform)

58
src/Avalonia.Native/AvaloniaNativeRenderTimer.cs

@ -0,0 +1,58 @@
using System;
using System.Diagnostics;
using Avalonia.Native.Interop;
using Avalonia.Rendering;
#nullable enable
namespace Avalonia.Native;
internal sealed class AvaloniaNativeRenderTimer : NativeCallbackBase, IRenderTimer, IAvnActionCallback
{
private readonly IAvnPlatformRenderTimer _platformRenderTimer;
private readonly Stopwatch _stopwatch;
private Action<TimeSpan>? _tick;
private int _subscriberCount;
private bool registered;
public AvaloniaNativeRenderTimer(IAvnPlatformRenderTimer platformRenderTimer)
{
_platformRenderTimer = platformRenderTimer;
_stopwatch = Stopwatch.StartNew();
}
public event Action<TimeSpan> Tick
{
add
{
_tick += value;
if (!registered)
{
registered = true;
_platformRenderTimer.RegisterTick(this);
}
if (_subscriberCount++ == 0)
{
_platformRenderTimer.Start();
}
}
remove
{
if (--_subscriberCount == 0)
{
_platformRenderTimer.Stop();
}
_tick -= value;
}
}
public bool RunsInBackground => _platformRenderTimer.RunsInBackground().FromComBool();
public void Run()
{
_tick?.Invoke(_stopwatch.Elapsed);
}
}

10
src/Avalonia.Native/avn.idl

@ -507,6 +507,7 @@ interface IAvaloniaNativeFactory : IUnknown
HRESULT CreateApplicationCommands(IAvnApplicationCommands** ppv);
HRESULT CreatePlatformSettings(IAvnPlatformSettings** ppv);
HRESULT CreatePlatformBehaviorInhibition(IAvnPlatformBehaviorInhibition** ppv);
HRESULT CreatePlatformRenderTimer(IAvnPlatformRenderTimer** ppv);
}
[uuid(233e094f-9b9f-44a3-9a6e-6948bbdd9fb1)]
@ -999,3 +1000,12 @@ interface IAvnPlatformBehaviorInhibition : IUnknown
{
void SetInhibitAppSleep(bool inhibitAppSleep, char* reason);
}
[uuid(22edf20d-5803-2d3f-9247-b4842e5e9322)]
interface IAvnPlatformRenderTimer : IUnknown
{
HRESULT RegisterTick(IAvnActionCallback* callback);
void Start();
void Stop();
bool RunsInBackground();
}

Loading…
Cancel
Save