From 8b775e3e7bd55e3cd76f82bfb83c75447b88b8b1 Mon Sep 17 00:00:00 2001 From: lindexi Date: Wed, 25 Sep 2024 20:52:53 +0800 Subject: [PATCH] Try to get the XShmCompletionEvent without DeferredDisplayEvents --- src/Avalonia.X11/X11Platform.cs | 2 + src/Avalonia.X11/X11Window.cs | 15 ++++ .../XShmExtensions/X11ShmImage.cs | 70 +++++++++++++++++-- 3 files changed, 83 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs index 582608045f..c92f97e01d 100644 --- a/src/Avalonia.X11/X11Platform.cs +++ b/src/Avalonia.X11/X11Platform.cs @@ -370,6 +370,8 @@ namespace Avalonia /// public bool? UseRetainedFramebuffer { get; set; } + public bool? UseXShmFramebuffer { get; set; } + public X11PlatformOptions() { try diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index 6c1b381a40..cda422ae9a 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -26,6 +26,7 @@ using Avalonia.Input.Platform; using System.Runtime.InteropServices; using Avalonia.Dialogs; using Avalonia.Platform.Storage.FileIO; +using Avalonia.X11.XShmExtensions; // ReSharper disable IdentifierTypo // ReSharper disable StringLiteralTypo @@ -195,6 +196,13 @@ namespace Avalonia.X11 new X11FramebufferSurface(_x11.DeferredDisplay, _renderHandle, depth, _platform.Options.UseRetainedFramebuffer ?? false) }; + + if (_platform.Options.UseXShmFramebuffer is true) + { + var x11ShmFramebufferSurface = new X11ShmFramebufferSurface(this, _x11.DeferredDisplay, _handle, _renderHandle); + _x11ShmFramebufferSurface = x11ShmFramebufferSurface; + surfaces.Insert(0, x11ShmFramebufferSurface); + } if (egl != null) surfaces.Insert(0, @@ -611,6 +619,12 @@ namespace Avalonia.X11 return; HandleKeyEvent(ref ev); } + else if (ev.type == XEventName.XShmCompletionEvent) + { + // Because the XShmCompletionEvent only occurs when the XShmPutImage is called, and the XShmPutImage only be called when use X11ShmFramebufferSurface, so we can safely assume that the _x11ShmFramebufferSurface is not null. + Debug.Assert(_x11ShmFramebufferSurface!=null, "If application can receive the XShmCompletionEvent, that the _x11ShmFramebufferSurface must not be null."); + _x11ShmFramebufferSurface?.OnXShmCompletionEvent(ref ev); + } } private Thickness? GetFrameExtents() @@ -798,6 +812,7 @@ namespace Avalonia.X11 new PixelSize(MaxWindowDimension, MaxWindowDimension)); private double _scaling = 1; + private X11ShmFramebufferSurface? _x11ShmFramebufferSurface; private void ScheduleInput(RawInputEventArgs args, ref XEvent xev) { diff --git a/src/Avalonia.X11/XShmExtensions/X11ShmImage.cs b/src/Avalonia.X11/XShmExtensions/X11ShmImage.cs index f55adb955f..7cfc5c0866 100644 --- a/src/Avalonia.X11/XShmExtensions/X11ShmImage.cs +++ b/src/Avalonia.X11/XShmExtensions/X11ShmImage.cs @@ -9,16 +9,51 @@ using Avalonia.Platform; namespace Avalonia.X11.XShmExtensions; +class X11ShmFramebufferContext +{ + public X11ShmFramebufferContext(X11Window x11Window, IntPtr display, IntPtr windowHandle, IntPtr renderHandle) + { + X11Window = x11Window; + Display = display; + WindowHandle = windowHandle; + RenderHandle = renderHandle; + } + + public X11Window X11Window { get; } + + public IntPtr Display { get; } + + public IntPtr WindowHandle { get; } + + public IntPtr RenderHandle { get; } +} + internal class X11ShmFramebufferSurface : IFramebufferPlatformSurface { + public X11ShmFramebufferSurface(X11Window x11Window, IntPtr display, IntPtr windowHandle, IntPtr renderHandle) + { + _context = new X11ShmFramebufferContext(x11Window, display, windowHandle, renderHandle); + } + + private readonly X11ShmFramebufferContext _context; + public IFramebufferRenderTarget CreateFramebufferRenderTarget() { - return new X11ShmImageSwapchain(); + return new X11ShmImageSwapchain(_context); + } + + public void OnXShmCompletionEvent(ref XEvent ev) + { + } } internal class X11ShmImage { + /// + /// Returns false if we haven't got a completion event since the last Present, can call ProcessPendingEvents here + /// + public bool IsReady { get; } } internal class X11ShmImageManager @@ -26,20 +61,47 @@ internal class X11ShmImageManager } -class DeferredDisplayEvents -{ +//class DeferredDisplayEvents +//{ -} +//} internal class X11ShmImageSwapchain : IFramebufferRenderTarget { + public X11ShmImageSwapchain(X11ShmFramebufferContext context) + { + _context = context; + } + + private readonly X11ShmFramebufferContext _context; + + private Queue _availableQueue = new(); + private Queue _presentationQueue = new(); + private PixelSize? _lastSize; + public void Dispose() { } + private void DrainPresentationQueue() + { + while (_presentationQueue.Count > 0 && _presentationQueue.Peek().IsReady) + { + _availableQueue.Enqueue(_presentationQueue.Dequeue()); + } + } + public ILockedFramebuffer Lock() { + /* + 1) gets the current window geometry, if it doesn't match the lastSize disposes images and clears queues + 2) calls DrainPresentationQueue(); + 3) if there is an image in _availableQueue - use this image + 4) else if there are no images in _availableQueue and presentationQueue.Count < swapchainSize - create new image + 5) else synchronously wait for the first image from _presentationQueue and use it + 6) return a framebuffer associated with chosen image + */ throw new NotImplementedException(); } }