Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf(x11): use a Timer instead of a DispatcherTimer for render callbacks #19054

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/Uno.UI.Runtime.Skia.X11/X11WindowWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,16 +104,17 @@ public override void ExtendContentIntoTitleBar(bool extend)
_host.ExtendContentIntoTitleBar(extend);
}

private void OnWindowClosing()
private bool OnWindowClosing()
{
var closingArgs = RaiseClosing();
if (closingArgs.Cancel)
{
return;
return false;
}

// All prerequisites passed, can safely close.
Close();
return true;
}

private void OnNativeActivated(bool focused) => ActivationState = focused ? CoreWindowActivationState.PointerActivated : CoreWindowActivationState.Deactivated;
Expand Down
56 changes: 30 additions & 26 deletions src/Uno.UI.Runtime.Skia.X11/X11XamlRootHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using SkiaSharp;
using Uno.Disposables;
using Uno.UI;
using Uno.UI.Dispatching;
using Uno.UI.Xaml.Controls;

namespace Uno.WinUI.Runtime.Skia.X11;
Expand Down Expand Up @@ -73,8 +74,7 @@ internal partial class X11XamlRootHost : IXamlRootHost
private readonly ApplicationView _applicationView;
private readonly X11WindowWrapper _wrapper;
private readonly Window _window;
private readonly CompositeDisposable _disposables = new();
private readonly XamlRoot _xamlRoot;
private readonly IDisposable _windowBackgroundDisposable;

private int _synchronizedShutDownTopWindowIdleCounter;

Expand All @@ -83,14 +83,14 @@ internal partial class X11XamlRootHost : IXamlRootHost
private IX11Renderer? _renderer;

private bool _renderDirty = true;
private readonly DispatcherTimer _renderTimer;
// ReSharper disable once NotAccessedField.Local
private readonly Timer _timer; // this field is necessary to prevent the timer from being GCed

public X11Window RootX11Window => _x11Window!.Value;
public X11Window TopX11Window => _x11TopWindow!.Value;

public X11XamlRootHost(X11WindowWrapper wrapper, Window winUIWindow, XamlRoot xamlRoot, Action configureCallback, Action closingCallback, Action<bool> focusCallback, Action<bool> visibilityCallback)
public X11XamlRootHost(X11WindowWrapper wrapper, Window winUIWindow, XamlRoot xamlRoot, Action configureCallback, Func<bool> closingCallback, Action<bool> focusCallback, Action<bool> visibilityCallback)
{
_xamlRoot = xamlRoot;
_wrapper = wrapper;
_window = winUIWindow;

Expand All @@ -99,22 +99,32 @@ public X11XamlRootHost(X11WindowWrapper wrapper, Window winUIWindow, XamlRoot xa
_visibilityCallback = visibilityCallback;
_configureCallback = configureCallback;

_renderTimer = new DispatcherTimer();
_renderTimer.Interval = TimeSpan.FromSeconds(1.0 / X11ApplicationHost.RenderFrameRate); // we're on the UI thread
_renderTimer.Tick += (sender, o) =>
// running XConfigureEvent and rendering callbacks on a timer is necessary to avoid freezing in certain situations
// where we get spammed with these events. Most notably, when resizing a window by dragging an edge, we'll get spammed
// with XConfigureEvents.
_timer = new Timer(static obj =>
ramezgerges marked this conversation as resolved.
Show resolved Hide resolved
{
if (Interlocked.Exchange(ref _needsConfigureCallback, 0) == 1)
{
_configureCallback();
}
var @this = (X11XamlRootHost)obj!;
var needsConfigureCallback = @this._needsConfigureCallback;
var needsRender = @this._renderDirty;

if (_renderDirty)
if (needsConfigureCallback || needsRender)
{
_renderDirty = false;
_renderer?.Render();
NativeDispatcher.Main.Enqueue(() =>
{
if (needsConfigureCallback)
{
@this._needsConfigureCallback = false;
@this._configureCallback();
}
if (needsRender)
{
@this._renderDirty = false;
@this._renderer?.Render();
}
});
}
};
_renderTimer.Start();
}, this, TimeSpan.Zero, TimeSpan.FromSeconds(1.0 / X11ApplicationHost.RenderFrameRate));

_applicationView = ApplicationView.GetForWindowId(winUIWindow.AppWindow.Id);
_applicationView.PropertyChanged += OnApplicationViewPropertyChanged;
Expand Down Expand Up @@ -150,7 +160,8 @@ public X11XamlRootHost(X11WindowWrapper wrapper, Window winUIWindow, XamlRoot xa
// only start listening to events after we're done setting everything up
InitializeX11EventsThread();

RegisterForBackgroundColor();
_windowBackgroundDisposable = _window.RegisterBackgroundChangedEvent((_, _) => UpdateRendererBackground());
UpdateRendererBackground();
}

public static X11XamlRootHost? GetHostFromWindow(Window window)
Expand Down Expand Up @@ -444,7 +455,7 @@ private unsafe static X11Window CreateGLXWindow(IntPtr display, int screen, Size
}

IntPtr context = GlxInterface.glXCreateNewContext(display, bestFbc, GlxConsts.GLX_RGBA_TYPE, IntPtr.Zero, /* True */ 1);
var _1 = XLib.XSync(display, false);
_ = XLib.XSync(display, false);

XSetWindowAttributes attribs = default;
attribs.border_pixel = XLib.XBlackPixel(display, screen);
Expand Down Expand Up @@ -593,13 +604,6 @@ private void SynchronizedShutDown(X11Window x11Window)
}
}

private void RegisterForBackgroundColor()
{
UpdateRendererBackground();

_disposables.Add(_window.RegisterBackgroundChangedEvent((s, e) => UpdateRendererBackground()));
}

private void UpdateRendererBackground()
{
if (_window.Background is Microsoft.UI.Xaml.Media.SolidColorBrush brush)
Expand Down
14 changes: 10 additions & 4 deletions src/Uno.UI.Runtime.Skia.X11/X11XamlRootHost.x11events.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ internal partial class X11XamlRootHost
{
private static int _threadCount;

private readonly Action _closingCallback;
private readonly Func<bool> _closingCallback;
private readonly Action<bool> _focusCallback;
private readonly Action<bool> _visibilityCallback;
private readonly Action _configureCallback;

private int _needsConfigureCallback;
private bool _needsConfigureCallback;

private X11PointerInputSource? _pointerSource;
private X11KeyboardInputSource? _keyboardSource;
Expand Down Expand Up @@ -185,7 +185,13 @@ static IEnumerable<XEvent> GetEvents(IntPtr display)
// which, according to the source code, just calls XKillClient
// https://gitlab.freedesktop.org/xorg/app/xkill/-/blob/a5f704e4cd30f03859f66bafd609a75aae27cc8c/xkill.c#L234
// In the case of xkill, we can't really do much, it's similar to a SIGKILL but for x connections
QueueAction(this, _closingCallback);
QueueAction(this, () =>
{
if (_closingCallback())
{
_windowBackgroundDisposable.Dispose();
}
});
}
else if (@event.ClientMessageEvent.message_type == X11Helper.GetAtom(x11Window.Display, X11Helper.XdndEnter) ||
@event.ClientMessageEvent.message_type == X11Helper.GetAtom(x11Window.Display, X11Helper.XdndPosition) ||
Expand All @@ -197,7 +203,7 @@ static IEnumerable<XEvent> GetEvents(IntPtr display)
}
break;
case XEventName.ConfigureNotify:
Interlocked.Exchange(ref _needsConfigureCallback, 1);
_needsConfigureCallback = true;
break;
case XEventName.FocusIn:
QueueAction(this, () => _focusCallback(true));
Expand Down
Loading