-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
Optimize Rendering Push Performance Using MIT-SHM #17118
base: master
Are you sure you want to change the base?
Changes from all commits
5be7d3b
54a24f9
a35f4b8
2894603
1b7fe0f
220f175
d5a6c43
8b775e3
9e245be
869dce0
54b1c81
a3c3527
42de87c
15b2253
585509a
7bba7b5
eb26283
e3d9ba3
a0a03a8
26d5761
c6c884d
654122b
3691ea3
156696a
1a1fdf3
2a8cbdf
0f01872
330141b
97e3673
f652e8a
1609f47
a4a409f
e6d57f5
0bd00df
da52ddf
3dee126
1a04014
17e1bf9
9641de1
968607f
ef01609
6ac7b94
ed65472
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
using System; | ||
using System.Runtime.InteropServices; | ||
|
||
namespace Avalonia.X11; | ||
|
||
internal static class LibC | ||
{ | ||
[DllImport("libc", SetLastError = true)] | ||
public static extern IntPtr shmat(int shmid, IntPtr shmaddr, int shmflg); | ||
|
||
[DllImport("libc", SetLastError = true)] | ||
public static extern int shmdt(IntPtr shmaddr); | ||
|
||
/// <summary> | ||
/// create key if key does not exist | ||
/// </summary> | ||
public const int IPC_CREAT = 01000; | ||
|
||
/// <summary> | ||
/// private key | ||
/// </summary> | ||
public const int IPC_PRIVATE = 0; | ||
|
||
/// <summary> | ||
/// Remove the IPC object | ||
/// </summary> | ||
public const int IPC_RMID = 0; | ||
|
||
[DllImport("libc", SetLastError = true)] | ||
public static extern int shmget(int key, IntPtr size, int shmflg); | ||
|
||
[DllImport("libc", SetLastError = true)] | ||
public static extern int shmctl(int shmid, int cmd, IntPtr buf); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using System; | ||
using System.Diagnostics; | ||
using Avalonia.Logging; | ||
|
||
namespace Avalonia.X11.XShmExtensions; | ||
|
||
internal static class X11ShmDebugLogger | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kekekeks Thank you. I intend for the X11ShmDebugLogger to be used by Avalonia framework developers when investigating the XShm module, rather than by application developers using the Avalonia framework. Application developers should not be misled by the debug output from XShm. I'm not sure if this expectation is reasonable. |
||
{ | ||
[Conditional("Disable")] // Do not open it unless you need to debug XShm | ||
public static void WriteLine(string message) | ||
{ | ||
Logger.TryGet(LogEventLevel.Debug, LogArea.X11Platform)?.Log(null, message); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using Avalonia.Logging; | ||
using ShmSeg = System.UInt64; | ||
|
||
namespace Avalonia.X11.XShmExtensions; | ||
|
||
class X11ShmFramebufferContext | ||
{ | ||
public X11ShmFramebufferContext(X11Window x11Window, IntPtr display, IntPtr windowXId, IntPtr visual, int depth, bool shouldRenderOnUiThread) | ||
{ | ||
X11Window = x11Window; | ||
Display = display; | ||
WindowXId = windowXId; | ||
Visual = visual; | ||
Depth = depth; | ||
ShouldRenderOnUiThread = shouldRenderOnUiThread; | ||
} | ||
|
||
public X11Window X11Window { get; } | ||
|
||
public IntPtr Display { get; } | ||
|
||
public IntPtr WindowXId { get; } | ||
|
||
public IntPtr Visual { get; } | ||
public int Depth { get; } | ||
public bool ShouldRenderOnUiThread { get; } | ||
|
||
/// <summary> | ||
/// The maximum number of XShmSwapchain frame count. | ||
/// </summary> | ||
public int MaxXShmSwapchainFrameCount => 2; | ||
|
||
public void OnXShmCompletion(ShmSeg shmseg) | ||
{ | ||
X11ShmDebugLogger.WriteLine($"[X11ShmFramebufferContext] OnXShmCompletion"); | ||
if (_shmImageDictionary.Remove(shmseg, out var image)) | ||
{ | ||
image.ShmImageManager.OnXShmCompletion(image); | ||
} | ||
else | ||
{ | ||
// Unexpected case, all the X11ShmImage should be registered in the dictionary | ||
Logger.TryGet(LogEventLevel.Warning, LogArea.X11Platform)?.Log(this, $"[X11ShmFramebufferContext][OnXShmCompletion] [Warn] Can not find shmseg={shmseg} in Dictionary!!!"); | ||
} | ||
} | ||
|
||
public void RegisterX11ShmImage(X11ShmImage image) | ||
{ | ||
_shmImageDictionary[image.ShmSeg] = image; | ||
} | ||
|
||
private readonly Dictionary<ShmSeg, X11ShmImage> _shmImageDictionary = new Dictionary<ShmSeg, X11ShmImage>(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
using System; | ||
using Avalonia.Controls.Platform.Surfaces; | ||
|
||
using ShmSeg = System.UInt64; | ||
|
||
namespace Avalonia.X11.XShmExtensions; | ||
|
||
internal class X11ShmFramebufferSurface : IFramebufferPlatformSurface | ||
{ | ||
public X11ShmFramebufferSurface(X11Window x11Window, IntPtr display, IntPtr windowHandle, IntPtr visual, int depth, | ||
bool shouldRenderOnUiThread) | ||
{ | ||
// From https://www.x.org/releases/X11R7.5/doc/Xext/mit-shm.html | ||
// > The event type value that will be used can be determined at run time with a line of the form: | ||
// > int CompletionType = XShmGetEventBase (display) + ShmCompletion; | ||
const int ShmCompletion = 0; | ||
XShmCompletionType = XShm.XShmGetEventBase(display) + ShmCompletion; | ||
|
||
_context = new X11ShmFramebufferContext(x11Window, display, windowHandle, visual, depth, shouldRenderOnUiThread); | ||
} | ||
|
||
public int XShmCompletionType { get; } | ||
|
||
private readonly X11ShmFramebufferContext _context; | ||
|
||
public IFramebufferRenderTarget CreateFramebufferRenderTarget() | ||
{ | ||
X11ShmDebugLogger.WriteLine("[X11ShmFramebufferSurface] CreateFramebufferRenderTarget"); | ||
|
||
return new X11ShmImageSwapchain(_context); | ||
} | ||
|
||
public unsafe void OnXShmCompletionEvent(XEvent @event) | ||
{ | ||
var p = &@event; | ||
var xShmCompletionEvent = (XShmCompletionEvent*)p; | ||
ShmSeg shmseg = xShmCompletionEvent->shmseg; | ||
X11ShmDebugLogger.WriteLine($"[X11ShmFramebufferSurface][OnXShmCompletionEvent] shmseg={shmseg}"); | ||
_context.OnXShmCompletion(shmseg); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
using System; | ||
using System.Diagnostics; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using ShmSeg = System.UInt64; | ||
using static Avalonia.X11.LibC; | ||
using static Avalonia.X11.XShmExtensions.XShm; | ||
using System.Runtime.InteropServices; | ||
|
||
namespace Avalonia.X11.XShmExtensions; | ||
|
||
internal unsafe class X11ShmImage : IDisposable | ||
{ | ||
public X11ShmImage(PixelSize size, X11ShmImageManager x11ShmImageManager) | ||
{ | ||
ShmImageManager = x11ShmImageManager; | ||
// The XShmSegmentInfo struct will store in XImage, and it must pin the address. | ||
IntPtr pXShmSegmentInfo = Marshal.AllocHGlobal(Marshal.SizeOf<XShmSegmentInfo>()); | ||
var pShmSegmentInfo = (XShmSegmentInfo*)pXShmSegmentInfo; | ||
PShmSegmentInfo = pShmSegmentInfo; | ||
|
||
var context = x11ShmImageManager.Context; | ||
var display = context.Display; | ||
var visual = context.Visual; | ||
|
||
const int ZPixmap = 2; | ||
|
||
var width = size.Width; | ||
var height = size.Height; | ||
|
||
Size = size; | ||
|
||
var depth = context.Depth; | ||
Debug.Assert(depth is 32, "The PixelFormat must be Bgra8888, so that the depth should be 32."); | ||
|
||
IntPtr data = IntPtr.Zero; | ||
|
||
var shmImage = (XImage*)XShmCreateImage(display, visual, (uint)depth, ZPixmap, data, pShmSegmentInfo, | ||
(uint)width, (uint)height); | ||
ShmImage = shmImage; | ||
|
||
var mapLength = new IntPtr(width * ByteSizeOfPixel * height); | ||
var shmid = shmget(IPC_PRIVATE, mapLength, IPC_CREAT | 0777); | ||
pShmSegmentInfo->shmid = shmid; | ||
|
||
var shmaddr = shmat(shmid, IntPtr.Zero, 0); | ||
|
||
if(shmaddr == new IntPtr(-1)) | ||
{ | ||
shmctl(shmid, IPC_RMID, IntPtr.Zero); | ||
throw new InvalidOperationException("Failed to shmat"); | ||
} | ||
|
||
pShmSegmentInfo->shmaddr = (char*)shmaddr.ToPointer(); | ||
shmImage->data = data = shmaddr; | ||
|
||
XShmAttach(display, pShmSegmentInfo); | ||
|
||
X11ShmDebugLogger.WriteLine($"[X11ShmImage] CreateX11ShmImage Size={Size} shmid={shmid:X} shmaddr={shmaddr}"); | ||
} | ||
|
||
public X11ShmImageManager ShmImageManager { get; } | ||
|
||
public XImage* ShmImage { get; set; } | ||
public XShmSegmentInfo* PShmSegmentInfo { get; } | ||
public IntPtr ShmAddr => new IntPtr(PShmSegmentInfo->shmaddr); | ||
|
||
public const int ByteSizeOfPixel = 4; | ||
|
||
public PixelSize Size { get; } | ||
|
||
public ShmSeg ShmSeg => PShmSegmentInfo->shmseg; | ||
|
||
public void Dispose() | ||
{ | ||
var context = ShmImageManager.Context; | ||
XShmDetach(context.Display, PShmSegmentInfo); | ||
|
||
shmdt(ShmAddr); | ||
shmctl(PShmSegmentInfo->shmid, IPC_RMID, IntPtr.Zero); | ||
|
||
Marshal.FreeHGlobal(new IntPtr(PShmSegmentInfo)); | ||
|
||
X11ShmDebugLogger.WriteLine($"[X11ShmImage] Dispose"); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DeferredDisplay should be used for rendering since compositor runs on a separate thread. You can get events from that connection too as previously discussed in #16690 (reply in thread) (see DeferredDisplayEvents class)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kekekeks The DeferredDisplay do not call the
XSelectInput
, and I do not know how to recevice the XShmCompletionEvent from DeferredDisplay. Thank you.