Skip to content

Commit e543888

Browse files
committed
Major x11 screen capture optimization
1 parent 439314e commit e543888

File tree

8 files changed

+25
-55
lines changed

8 files changed

+25
-55
lines changed

SightKeeper.Application.Linux/Natives/LibXExt.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ internal static unsafe partial class LibXExt
1212

1313
[LibraryImport(DllName)]
1414
[return: MarshalAs(UnmanagedType.Bool)]
15-
public static partial bool XShmGetImage(IntPtr display, UIntPtr drawable, XImage* image, int x, int y, UIntPtr plane_mask);
15+
public static partial bool XShmGetImage(IntPtr display, UIntPtr drawable, XImage* image, int x, int y, ulong plane_mask);
1616

1717
[LibraryImport(DllName, SetLastError = true)]
1818
public static partial int XShmQueryExtension(IntPtr display);

SightKeeper.Application.Linux/SharedImageMemorySegment.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Numerics.Tensors;
12
using System.Runtime.InteropServices;
23
using SightKeeper.Application.Linux.Natives;
34
using SightKeeper.Domain.Model;
@@ -23,10 +24,14 @@ public unsafe SharedImageMemorySegment(nint display, Vector2<ushort> resolution)
2324

2425
public unsafe void FetchData(int screen, Vector2<ushort> offset)
2526
{
26-
const int allPlanes = ~0;
27-
UIntPtr allPlanes2 = new(unchecked((uint)allPlanes));
27+
const ulong allPlanes = unchecked((ulong)~0);
2828
var drawable = (UIntPtr)LibX.XRootWindow(_display, screen);
29-
LibXExt.XShmGetImage(_display, drawable, _image.ximage, offset.X, offset.Y, allPlanes2);
29+
LibXExt.XShmGetImage(_display, drawable, _image.ximage, offset.X, offset.Y, allPlanes);
30+
31+
// xlib doesn't use most significant byte but fills it with zeros
32+
// because of that ImageSharp treats it as fully transparent Bgra32
33+
Span<uint> span = new(_image.data, Resolution.X * Resolution.Y);
34+
TensorPrimitives.BitwiseOr(span, 0xFF_00_00_00, span);
3035
}
3136

3237
public void Dispose()

SightKeeper.Application.Linux/SightKeeper.Application.Linux.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,8 @@
1111
<ProjectReference Include="..\SightKeeper.Application\SightKeeper.Application.csproj" />
1212
</ItemGroup>
1313

14+
<ItemGroup>
15+
<PackageReference Include="System.Numerics.Tensors" Version="9.0.0-rc.1.24431.7" />
16+
</ItemGroup>
17+
1418
</Project>

SightKeeper.Application.Linux/X11ScreenCapture.cs

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,32 @@
11
using SightKeeper.Application.Linux.Natives;
22
using SightKeeper.Domain.Model;
33
using SixLabors.ImageSharp;
4-
using SixLabors.ImageSharp.Formats;
5-
using SixLabors.ImageSharp.Formats.Bmp;
64
using SixLabors.ImageSharp.PixelFormats;
75

86
namespace SightKeeper.Application.Linux;
97

108
public sealed class X11ScreenCapture : ScreenCapture, IDisposable
119
{
12-
private static readonly IImageEncoder Encoder = new BmpEncoder();
13-
1410
public X11ScreenCapture()
1511
{
1612
_display = LibX.XOpenDisplay(null);
1713
_screen = LibX.XDefaultScreen(_display);
1814
if (LibXExt.XShmQueryExtension(_display) == 0)
1915
{
20-
LibX.XCloseDisplay(_display);
16+
Dispose();
2117
throw new Exception("xserver doesn't support shm");
2218
}
2319
}
2420

25-
public unsafe Stream Capture(Vector2<ushort> resolution, Game? game)
21+
public Image Capture(Vector2<ushort> resolution, Game? game)
2622
{
2723
if (_memorySegment?.Resolution != resolution)
2824
{
2925
_memorySegment?.Dispose();
3026
_memorySegment = new SharedImageMemorySegment<Bgra32>(_display, resolution);
3127
}
3228
_memorySegment.FetchData(_screen, new Vector2<ushort>());
33-
MemoryStream stream = new();
34-
ShmImage image = new();
35-
Image.LoadPixelData(_memorySegment.Data, resolution.X, resolution.Y).Save(stream, Encoder);
36-
XLibShm.DestroyImage(_display, &image);
37-
stream.Position = 0;
38-
return stream;
29+
return Image.LoadPixelData(_memorySegment.Data, resolution.X, resolution.Y);
3930
}
4031

4132
private readonly nint _display;

SightKeeper.Application.Windows/WindowsScreenCapture.cs

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1-
using System.Drawing;
2-
using System.Drawing.Imaging;
3-
using Serilog.Events;
4-
using SerilogTimings;
5-
using SightKeeper.Domain.Model;
1+
using SightKeeper.Domain.Model;
2+
using Image = SixLabors.ImageSharp.Image;
63

74
namespace SightKeeper.Application.Windows;
85

@@ -13,20 +10,16 @@ public WindowsScreenCapture(ScreenBoundsProvider screenBoundsProvider)
1310
_screenBoundsProvider = screenBoundsProvider;
1411
}
1512

16-
public Stream Capture(Vector2<ushort> resolution, Game? game)
13+
public Image Capture(Vector2<ushort> resolution, Game? game)
1714
{
18-
var screenCenter = _screenBoundsProvider.MainScreenCenter;
19-
var operation = Operation.At(LogEventLevel.Verbose).Begin("Screen capturing");
15+
throw new NotImplementedException();
16+
/*var screenCenter = _screenBoundsProvider.MainScreenCenter;
2017
using Bitmap windowsBitmap = new(resolution.X, resolution.Y);
2118
using var graphics = Graphics.FromImage(windowsBitmap);
2219
var halfResolution = resolution / 2;
2320
Point position = new(screenCenter.X - halfResolution.X, screenCenter.Y - halfResolution.Y);
2421
Size size = new(resolution.X, resolution.Y);
25-
graphics.CopyFromScreen(position, Point.Empty, size);
26-
MemoryStream stream = new();
27-
windowsBitmap.Save(stream, ImageFormat.Bmp);
28-
operation.Complete();
29-
return stream;
22+
graphics.CopyFromScreen(position, Point.Empty, size);*/
3023
}
3124

3225
private readonly ScreenBoundsProvider _screenBoundsProvider;
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
using SightKeeper.Domain.Model;
2+
using SixLabors.ImageSharp;
23

34
namespace SightKeeper.Application;
45

56
public interface ScreenCapture
67
{
7-
Stream Capture(Vector2<ushort> resolution, Game? game);
8+
Image Capture(Vector2<ushort> resolution, Game? game);
89
}

SightKeeper.Application/SightKeeper.Application.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.5" />
1919
<PackageReference Include="System.Reactive" Version="6.0.1" />
2020
<PackageReference Include="YamlDotNet" Version="16.0.0" />
21-
<PackageReference Include="YoloV8.Gpu" Version="4.1.7" />
21+
<PackageReference Include="YoloV8.Gpu" Version="5.0.1" />
2222
</ItemGroup>
2323

2424
<ItemGroup>

SightKeeper.Avalonia/Program.cs

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
using System;
2-
using System.IO;
32
using System.Threading.Tasks;
43
using Avalonia;
54
using Avalonia.Threading;
65
using Serilog;
7-
using SightKeeper.Application.Linux;
8-
using SightKeeper.Domain.Model;
9-
using SixLabors.ImageSharp;
106

117
namespace SightKeeper.Avalonia;
128

@@ -18,26 +14,6 @@ internal static class Program
1814
[STAThread]
1915
public static void Main(string[] args)
2016
{
21-
X11ScreenCapture screenCapture = new();
22-
using var initial = screenCapture.Capture(new Vector2<ushort>(640, 640), null);
23-
if (File.Exists("Test.png"))
24-
File.Delete("Test.png");
25-
Image.Load(initial).Save("Test.png");
26-
// warmup
27-
for (int i = 0; i < 200; i++)
28-
{
29-
using var stream = screenCapture.Capture(new Vector2<ushort>(640, 640), null);
30-
}
31-
DateTime start = DateTime.UtcNow;
32-
const int samples = 1000;
33-
for (int i = 0; i < samples; i++)
34-
{
35-
using var stream = screenCapture.Capture(new Vector2<ushort>(640, 640), null);
36-
}
37-
var end = DateTime.UtcNow - start;
38-
Console.WriteLine($"Elapsed {end.TotalMilliseconds / samples}ms per capture");
39-
Console.ReadKey(true);
40-
return;
4117
SetupLogger();
4218
AppBuilder? appBuilder = null;
4319
try

0 commit comments

Comments
 (0)