Skip to content

Commit 80a5c9b

Browse files
Copilotphilstopford
andcommitted
Implement comprehensive refactor using DrawingArea widget and GTK+Vulkan patterns for better Wayland support
Co-authored-by: philstopford <1983851+philstopford@users.noreply.github.com>
1 parent 6cff74f commit 80a5c9b

File tree

3 files changed

+124
-55
lines changed

3 files changed

+124
-55
lines changed

Directory.Build.targets

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@
2323
<Target Name="CopyVeldridSpirvNative" AfterTargets="SetPaths" BeforeTargets="Cleanup" Condition="$(PackageVeldrid) == 'true' AND $(NoPackageVeldrid) != 'true'">
2424
<Copy
2525
SourceFiles="$([MSBuild]::EnsureTrailingSlash('$(NuGetPackageRoot)'))veldrid.spirv\$(VeldridSpirvVersion)\runtimes\$(RuntimeID)\native\$(VeldridSpirvNativeName)"
26-
DestinationFolder="$(OutputPath)"/>
26+
DestinationFolder="$(OutputPath)"
27+
SkipUnchangedFiles="true"
28+
ContinueOnError="true"/>
2729
</Target>
2830

2931
<Target Name="Cleanup" AfterTargets="SetPaths" Condition="$(Configuration.Contains('Release'))">

Eto/Eto.Veldrid.Gtk/GtkVeldridSurfaceHandler.cs

Lines changed: 117 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@ namespace Eto.Veldrid.Gtk;
1313
public class GtkVeldridSurfaceHandler : GtkControl<global::Gtk.Widget, VeldridSurface, VeldridSurface.ICallback>, VeldridSurface.IHandler, VeldridSurface.IOpenGL
1414
{
1515
private GLArea? glArea;
16+
private DrawingArea? drawingArea;
1617
private System.Action _makeCurrent;
1718
private System.Action _clearCurrent;
1819
public Size RenderSize => Size.Round((SizeF)Widget.Size * Scale);
1920

2021
private float Scale => Widget.ParentWindow?.Screen?.LogicalPixelSize ?? 1;
2122

22-
public override global::Gtk.Widget ContainerContentControl => glArea ?? base.ContainerContentControl;
23+
public override global::Gtk.Widget ContainerContentControl => glArea ?? drawingArea ?? base.ContainerContentControl;
2324

2425
public GtkVeldridSurfaceHandler()
2526
{
@@ -34,49 +35,42 @@ public GtkVeldridSurfaceHandler()
3435
return Widget.GraphicsDevice?.MainSwapchain;
3536
}
3637

37-
// For Vulkan backend, create swapchain immediately using simplified approach
38-
return CreateSwapchainNow();
38+
// For Vulkan backend, create swapchain using proper GTK+Vulkan patterns
39+
return CreateVulkanSwapchain();
3940
}
4041

41-
private Swapchain? CreateSwapchainNow()
42+
private Swapchain? CreateVulkanSwapchain()
4243
{
43-
// To embed Veldrid in an Eto control, these platform-specific
44-
// versions of CreateSwapchain use the technique outlined here:
45-
//
46-
// https://github.com/mellinoe/veldrid/issues/155
47-
//
48-
SwapchainSource source;
49-
50-
// Detect whether we're running on X11 or Wayland and create appropriate SwapchainSource
51-
var gdkDisplay = Control.Display.Handle;
52-
53-
bool isX11 = X11Interop.IsX11Display(gdkDisplay);
54-
bool isWayland = X11Interop.IsWaylandDisplay(gdkDisplay);
55-
56-
// Add debugging information for diagnostic purposes
57-
var displayNamePtr = X11Interop.gdk_display_get_name(gdkDisplay);
58-
var displayName = displayNamePtr == IntPtr.Zero ? "unknown" : System.Runtime.InteropServices.Marshal.PtrToStringAnsi(displayNamePtr) ?? "unknown";
59-
Console.WriteLine($"[DEBUG] Display name: {displayName}, IsX11: {isX11}, IsWayland: {isWayland}");
60-
61-
if (isX11 && !isWayland)
44+
// Ensure we have a proper native window for Vulkan operations
45+
if (drawingArea?.Window == null)
6246
{
63-
// X11 path - use Xlib SwapchainSource
64-
Console.WriteLine("[DEBUG] Using X11/Xlib SwapchainSource");
65-
source = SwapchainSource.CreateXlib(
66-
X11Interop.gdk_x11_display_get_xdisplay(gdkDisplay),
67-
X11Interop.gdk_x11_window_get_xid(Control.Window.Handle));
47+
Console.WriteLine("[DEBUG] DrawingArea window not available for Vulkan swapchain creation");
48+
return null;
6849
}
69-
else if (isWayland && !isX11)
50+
51+
// Ensure the widget is properly realized and has a native window
52+
if (!drawingArea.IsRealized)
7053
{
71-
// Wayland path - use Wayland SwapchainSource with simplified approach
72-
Console.WriteLine("[DEBUG] Using Wayland SwapchainSource");
73-
source = CreateWaylandSwapchainSource(gdkDisplay);
54+
Console.WriteLine("[DEBUG] DrawingArea not realized, cannot create Vulkan swapchain");
55+
return null;
7456
}
75-
else
57+
58+
// For Wayland, ensure the window is visible and has been committed by compositor
59+
var gdkDisplay = drawingArea.Display.Handle;
60+
bool isWayland = X11Interop.IsWaylandDisplay(gdkDisplay);
61+
62+
if (isWayland)
7663
{
77-
throw new NotSupportedException($"Unsupported or ambiguous windowing system detected. Display: {displayName}, IsX11: {isX11}, IsWayland: {isWayland}. Only X11 and Wayland are supported for Vulkan backend.");
64+
// On Wayland, ensure window is visible before accessing surface
65+
if (!drawingArea.Window.IsVisible)
66+
{
67+
Console.WriteLine("[DEBUG] Wayland window not visible, cannot create swapchain");
68+
return null;
69+
}
7870
}
7971

72+
SwapchainSource source = CreateSwapchainSource();
73+
8074
Size renderSize = RenderSize;
8175
var swapchain = Widget.GraphicsDevice?.ResourceFactory.CreateSwapchain(
8276
new SwapchainDescription(
@@ -87,30 +81,60 @@ public GtkVeldridSurfaceHandler()
8781
Widget.GraphicsDeviceOptions.SyncToVerticalBlank,
8882
Widget.GraphicsDeviceOptions.SwapchainSrgbFormat));
8983

84+
if (swapchain != null)
85+
{
86+
Console.WriteLine("[DEBUG] Vulkan swapchain created successfully");
87+
}
88+
9089
return swapchain;
9190
}
9291

93-
private SwapchainSource CreateWaylandSwapchainSource(IntPtr gdkDisplay)
92+
private SwapchainSource CreateSwapchainSource()
9493
{
95-
Console.WriteLine("[DEBUG] Creating Wayland SwapchainSource");
94+
// Use drawingArea for Vulkan operations instead of Control
95+
var gdkDisplay = drawingArea!.Display.Handle;
9696

97-
// Ensure widget is properly realized before accessing Wayland handles
98-
if (!Control.IsRealized)
97+
bool isX11 = X11Interop.IsX11Display(gdkDisplay);
98+
bool isWayland = X11Interop.IsWaylandDisplay(gdkDisplay);
99+
100+
// Add debugging information
101+
var displayNamePtr = X11Interop.gdk_display_get_name(gdkDisplay);
102+
var displayName = displayNamePtr == IntPtr.Zero ? "unknown" : System.Runtime.InteropServices.Marshal.PtrToStringAnsi(displayNamePtr) ?? "unknown";
103+
Console.WriteLine($"[DEBUG] Display name: {displayName}, IsX11: {isX11}, IsWayland: {isWayland}");
104+
105+
if (isX11 && !isWayland)
106+
{
107+
Console.WriteLine("[DEBUG] Creating X11/Xlib SwapchainSource");
108+
return SwapchainSource.CreateXlib(
109+
X11Interop.gdk_x11_display_get_xdisplay(gdkDisplay),
110+
X11Interop.gdk_x11_window_get_xid(drawingArea.Window.Handle));
111+
}
112+
else if (isWayland && !isX11)
113+
{
114+
Console.WriteLine("[DEBUG] Creating Wayland SwapchainSource");
115+
return CreateWaylandSwapchainSource(gdkDisplay);
116+
}
117+
else
99118
{
100-
throw new InvalidOperationException("Widget must be realized before creating Wayland surface");
119+
throw new NotSupportedException($"Unsupported or ambiguous windowing system. Display: {displayName}, IsX11: {isX11}, IsWayland: {isWayland}");
101120
}
121+
}
122+
123+
private SwapchainSource CreateWaylandSwapchainSource(IntPtr gdkDisplay)
124+
{
125+
Console.WriteLine("[DEBUG] Creating Wayland SwapchainSource from DrawingArea");
102126

103-
// Basic validation that we have a valid window
104-
if (Control.Window == null)
127+
// Use drawingArea for Wayland operations
128+
if (drawingArea?.Window == null)
105129
{
106-
throw new InvalidOperationException("Widget window is null");
130+
throw new InvalidOperationException("DrawingArea window is null");
107131
}
108132

109-
Console.WriteLine($"[DEBUG] Window state - Visible: {Control.Window.IsVisible}, Realized: {Control.IsRealized}, Handle: {Control.Window.Handle}");
133+
Console.WriteLine($"[DEBUG] DrawingArea window state - Visible: {drawingArea.Window.IsVisible}, Realized: {drawingArea.IsRealized}");
110134

111-
// Get Wayland handles
135+
// Get Wayland handles from DrawingArea
112136
var waylandDisplay = X11Interop.gdk_wayland_display_get_wl_display(gdkDisplay);
113-
var waylandSurface = X11Interop.gdk_wayland_window_get_wl_surface(Control.Window.Handle);
137+
var waylandSurface = X11Interop.gdk_wayland_window_get_wl_surface(drawingArea.Window.Handle);
114138

115139
if (waylandDisplay == IntPtr.Zero || waylandSurface == IntPtr.Zero)
116140
{
@@ -142,13 +166,44 @@ private void glArea_InitializeGraphicsBackend(object? sender, EventArgs e)
142166
glArea.Resize += glArea_Resize;
143167
}
144168

145-
private void Control_InitializeGraphicsBackend(object? sender, EventArgs e)
169+
private void DrawingArea_InitializeGraphicsBackend(object? sender, EventArgs e)
146170
{
147-
// Simple immediate backend initialization for both X11 and Wayland
171+
Console.WriteLine("[DEBUG] DrawingArea backend initialization triggered");
172+
173+
// Ensure native window for proper Vulkan surface access
174+
if (drawingArea?.Window != null)
175+
{
176+
X11Interop.gdk_window_ensure_native(drawingArea.Window.Handle);
177+
Console.WriteLine("[DEBUG] Ensured native window for DrawingArea");
178+
}
179+
180+
// For Wayland, we need to ensure the window is properly mapped before initializing
181+
var gdkDisplay = drawingArea!.Display.Handle;
182+
bool isWayland = X11Interop.IsWaylandDisplay(gdkDisplay);
183+
184+
if (isWayland && !drawingArea.Window.IsVisible)
185+
{
186+
Console.WriteLine("[DEBUG] Wayland window not visible yet, deferring initialization");
187+
// Defer initialization until window is visible
188+
drawingArea.Shown += (s, args) => {
189+
Console.WriteLine("[DEBUG] Wayland window shown, initializing backend");
190+
Callback.OnInitializeBackend(Widget, new InitializeEventArgs(RenderSize));
191+
};
192+
return;
193+
}
194+
195+
// Initialize immediately for X11 or when Wayland window is ready
148196
Console.WriteLine("[DEBUG] Initializing graphics backend");
149197
Callback.OnInitializeBackend(Widget, new InitializeEventArgs(RenderSize));
150198
}
151199

200+
private void Control_InitializeGraphicsBackend(object? sender, EventArgs e)
201+
{
202+
// Legacy method for EtoEventBox - kept for compatibility
203+
Console.WriteLine("[DEBUG] Initializing graphics backend (legacy)");
204+
Callback.OnInitializeBackend(Widget, new InitializeEventArgs(RenderSize));
205+
}
206+
152207
private bool skipDraw;
153208

154209
private void glArea_Resize(object o, ResizeArgs args)
@@ -264,20 +319,28 @@ void Forms.Control.IHandler.Invalidate(bool invalidateChildren)
264319

265320
glArea.HasDepthBuffer = true;
266321
glArea.HasStencilBuffer = true;
267-
// Control.Child = glArea;
268322
glArea.Realized += glArea_InitializeGraphicsBackend;
269323
return glArea;
270324
}
271325
else
272326
{
273-
EtoEventBox box = new();
274-
box.CanFocus = true;
275-
box.CanDefault = true;
327+
// Use DrawingArea for Vulkan backend instead of EtoEventBox for better native window support
328+
drawingArea = new DrawingArea();
329+
drawingArea.CanFocus = true;
330+
drawingArea.CanDefault = true;
331+
332+
// Set minimum size to ensure proper widget allocation
333+
drawingArea.SetSizeRequest(100, 100);
334+
335+
// Ensure native window creation for proper Vulkan surface access
336+
// This is critical for Wayland support
337+
drawingArea.AppPaintable = true;
276338

277-
// Use Realized event for both X11 and Wayland - keep it simple
278-
box.Realized += Control_InitializeGraphicsBackend;
339+
// Use Realized event for initialization
340+
drawingArea.Realized += DrawingArea_InitializeGraphicsBackend;
279341

280-
return box;
342+
Console.WriteLine("[DEBUG] Created DrawingArea for Vulkan backend");
343+
return drawingArea;
281344
}
282345
}
283346
}

Eto/Eto.Veldrid.Gtk/X11Interop.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ private const string
3333
[DllImport(linux_libgdk_name)]
3434
public static extern IntPtr gdk_wayland_window_get_wl_surface(IntPtr gdkWindow);
3535

36+
// Additional GDK functions for native window management
37+
[DllImport(linux_libgdk_name)]
38+
public static extern void gdk_window_ensure_native(IntPtr gdkWindow);
39+
3640
// OpenGL function
3741
[DllImport(linux_libGL_name)]
3842
public static extern IntPtr glXGetProcAddress(string name);

0 commit comments

Comments
 (0)