Browse Source

Make a render timer based on libc nanosleep.

timer-overload
Jumar Macato 6 years ago
parent
commit
70cc4bd8b8
No known key found for this signature in database GPG Key ID: B19884DAC3A5BF3F
  1. 153
      src/Avalonia.X11/X11HighResRenderTimer.cs
  2. 7
      src/Avalonia.X11/X11Platform.cs

153
src/Avalonia.X11/X11HighResRenderTimer.cs

@ -0,0 +1,153 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Avalonia.Controls.Platform;
using Avalonia.FreeDesktop;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.OpenGL;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.X11.Glx;
using Avalonia.X11.NativeDialogs;
using static Avalonia.X11.XLib;
namespace Avalonia.X11
{
internal class LinuxHighResolutionTimer : IDisposable
{
[DllImport("libc", SetLastError = true)]
static extern int nanosleep(ref TimeSpec duration, ref TimeSpec remaining);
[DllImport("libc", SetLastError = true)]
static extern int clock_gettime(uint clk_id, ref TimeSpec tp);
const uint CLOCK_MONOTONIC = 1;
struct TimeSpec
{
public long tv_sec;
public long tv_nsec;
public TimeSpec(int tv_sec, long tv_nsec) : this()
{
this.tv_sec = tv_sec;
this.tv_nsec = tv_nsec;
}
public TimeSpan ToTimeSpan()
=> TimeSpan.FromSeconds(tv_sec) + TimeSpan.FromTicks(tv_nsec / 100);
public static TimeSpec ConvertToTimeSpec(TimeSpan timeSpan)
=> new TimeSpec(timeSpan.Seconds, timeSpan.Ticks * 100);
};
TimeSpec GetElapsedTimeSpec(ref TimeSpec start, ref TimeSpec stop)
{
var elapsed_time = new TimeSpec();
if ((stop.tv_nsec - start.tv_nsec) < 0)
{
elapsed_time.tv_sec = stop.tv_sec - start.tv_sec - 1;
elapsed_time.tv_nsec = stop.tv_nsec - start.tv_nsec + 1000000000;
}
else
{
elapsed_time.tv_sec = stop.tv_sec - start.tv_sec;
elapsed_time.tv_nsec = stop.tv_nsec - start.tv_nsec;
}
return elapsed_time;
}
public LinuxHighResolutionTimer(double seconds)
{
totalInterval = TimeSpan.FromSeconds(seconds);
}
private volatile bool shouldStop = false;
private TimeSpan elapsedTotal = TimeSpan.Zero;
private IDisposable _diposable1;
private TimeSpan totalInterval;
private void TickTock(Action<TimeSpan> observer)
{
var start = new TimeSpec();
var frameStop = new TimeSpec();
var sleepStop = new TimeSpec();
var remaining = new TimeSpec();
TimeSpan frameTime, totalTime;
while (!shouldStop)
{
clock_gettime(CLOCK_MONOTONIC, ref start);
observer(elapsedTotal);
clock_gettime(CLOCK_MONOTONIC, ref frameStop);
frameTime = GetElapsedTimeSpec(ref start, ref frameStop).ToTimeSpan();
var calc = (totalInterval - frameTime);
if (calc < TimeSpan.Zero)
calc = totalInterval;
var finDur = TimeSpec.ConvertToTimeSpec(calc);
nanosleep(ref finDur, ref remaining);
clock_gettime(CLOCK_MONOTONIC, ref sleepStop);
totalTime = GetElapsedTimeSpec(ref start, ref sleepStop).ToTimeSpan();
elapsedTotal += totalTime;
}
}
public void Dispose()
{
shouldStop = true;
_diposable1?.Dispose();
}
public IDisposable Subscribe(Action<TimeSpan> observer)
{
_diposable1 = Task.Run(() => TickTock(observer));
return this;
}
}
internal class X11HighResRenderTimer : DefaultRenderTimer
{
private double intervalMills;
public X11HighResRenderTimer(int framesPerSecond) : base(framesPerSecond)
{
this.intervalMills = 1d / (double)framesPerSecond;
}
public override bool Equals(object obj)
{
return base.Equals(obj);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
public override string ToString()
{
return base.ToString();
}
protected override IDisposable StartCore(Action<TimeSpan> tick)
{
return new LinuxHighResolutionTimer(intervalMills).Subscribe(tick);
}
}
}

7
src/Avalonia.X11/X11Platform.cs

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
using Avalonia.Controls;
using Avalonia.Controls.Platform;
using Avalonia.FreeDesktop;
@ -42,7 +43,7 @@ namespace Avalonia.X11
AvaloniaLocator.CurrentMutable.BindToSelf(this)
.Bind<IWindowingPlatform>().ToConstant(this)
.Bind<IPlatformThreadingInterface>().ToConstant(new X11PlatformThreading(this))
.Bind<IRenderTimer>().ToConstant(new DefaultRenderTimer(60))
.Bind<IRenderTimer>().ToConstant(new X11HighResRenderTimer(60))
.Bind<IRenderLoop>().ToConstant(new RenderLoop())
.Bind<PlatformHotkeyConfiguration>().ToConstant(new PlatformHotkeyConfiguration(InputModifiers.Control))
.Bind<IKeyboardDevice>().ToFunc(() => KeyboardDevice)
@ -52,7 +53,7 @@ namespace Avalonia.X11
.Bind<IPlatformIconLoader>().ToConstant(new X11IconLoader(Info))
.Bind<ISystemDialogImpl>().ToConstant(new GtkSystemDialog())
.Bind<IMountedVolumeInfoProvider>().ToConstant(new LinuxMountedVolumeInfoProvider());
X11Screens = Avalonia.X11.X11Screens.Init(this);
Screens = new X11Screens(X11Screens);
if (Info.XInputVersion != null)
@ -70,7 +71,7 @@ namespace Avalonia.X11
GlxGlPlatformFeature.TryInitialize(Info);
}
}
public IntPtr DeferredDisplay { get; set; }

Loading…
Cancel
Save