Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
7 changes: 3 additions & 4 deletions neomacs-display-runtime/src/backend/wgpu/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,7 @@ impl WinitBackend {
}

// Create wgpu instance
let mut instance_descriptor = wgpu::InstanceDescriptor::new_without_display_handle();
instance_descriptor.backends = wgpu::Backends::all();
let instance_descriptor = crate::wgpu_instance_descriptor_from_env();
let instance = wgpu::Instance::new(instance_descriptor);

// Request adapter without a surface (headless)
Expand Down Expand Up @@ -349,8 +348,8 @@ impl WinitBackend {
self.height = size.height;

// Create wgpu instance
let mut instance_descriptor = wgpu::InstanceDescriptor::new_without_display_handle();
instance_descriptor.backends = wgpu::Backends::all();
let instance_descriptor =
crate::wgpu_instance_descriptor_with_display(event_loop.owned_display_handle());
let instance = wgpu::Instance::new(instance_descriptor);

// Create surface - we need to use unsafe to create a surface from the window
Expand Down
10 changes: 10 additions & 0 deletions neomacs-display-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ pub fn gpu_power_preference() -> wgpu::PowerPreference {
}
}

pub(crate) fn wgpu_instance_descriptor_from_env() -> wgpu::InstanceDescriptor {
wgpu::InstanceDescriptor::new_without_display_handle_from_env()
}

pub(crate) fn wgpu_instance_descriptor_with_display(
display: winit::event_loop::OwnedDisplayHandle,
) -> wgpu::InstanceDescriptor {
wgpu::InstanceDescriptor::new_with_display_handle_from_env(Box::new(display))
}

/// Initialize the display engine.
///
/// Logging is initialized separately by the binary entry point via
Expand Down
30 changes: 19 additions & 11 deletions neomacs-display-runtime/src/render_thread/bootstrap.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use super::{
RenderApp, RenderUserEvent, SharedImageDimensions, SharedMonitorInfo, surface_readback,
};
use crate::render_thread::state::RenderGpuContext;
use crate::thread_comm::{InputEvent, RenderComms};
use neomacs_renderer_wgpu::{WgpuGlyphAtlas, WgpuRenderer};
use std::sync::Arc;
use winit::event_loop::{ControlFlow, EventLoop};
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
#[cfg(target_os = "linux")]
use winit::platform::wayland::EventLoopBuilderExtWayland;
#[cfg(target_os = "linux")]
Expand All @@ -20,12 +21,16 @@ use crate::backend::wpe::sys::platform as plat;

impl RenderApp {
/// Initialize wgpu with the window
pub(super) fn init_wgpu(&mut self, window: Arc<Window>) {
pub(super) fn init_wgpu(&mut self, event_loop: &ActiveEventLoop, window: Arc<Window>) {
tracing::info!("Initializing wgpu for render thread");

// Create wgpu instance
let mut instance_descriptor = wgpu::InstanceDescriptor::new_without_display_handle();
instance_descriptor.backends = wgpu::Backends::all();
// GLES presentation needs the compositor/display handle, especially on Wayland.
let instance_descriptor =
crate::wgpu_instance_descriptor_with_display(event_loop.owned_display_handle());
tracing::info!(
"wgpu requested backends: {:?}",
instance_descriptor.backends
);
let instance = wgpu::Instance::new(instance_descriptor);

// Create surface from window
Expand Down Expand Up @@ -135,11 +140,14 @@ impl RenderApp {
format
);

self.adapter = Some(adapter);
self.gpu = Some(RenderGpuContext {
instance,
adapter,
device: device.clone(),
queue: queue.clone(),
});
Comment on lines +143 to +148
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since device and queue are not used after this point in init_wgpu, we can move them directly into RenderGpuContext instead of cloning them.

Suggested change
self.gpu = Some(RenderGpuContext {
instance,
adapter,
device: device.clone(),
queue: queue.clone(),
});
self.gpu = Some(RenderGpuContext {
instance,
adapter,
device,
queue,
});

self.surface = Some(surface);
self.surface_config = Some(config);
self.device = Some(device.clone());
self.queue = Some(queue);
self.renderer = Some(renderer);
self.glyph_atlas = Some(glyph_atlas);

Expand Down Expand Up @@ -183,12 +191,12 @@ impl RenderApp {
self.height = height;

// Reconfigure surface
if let (Some(surface), Some(config), Some(device)) =
(&self.surface, &mut self.surface_config, &self.device)
if let (Some(surface), Some(config), Some(gpu)) =
(&self.surface, &mut self.surface_config, &self.gpu)
{
config.width = width;
config.height = height;
surface.configure(device, config);
surface.configure(&gpu.device, config);
}

// Resize renderer
Expand Down
30 changes: 21 additions & 9 deletions neomacs-display-runtime/src/render_thread/lifecycle.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use super::RenderApp;
use super::state::{effective_window_scale_factor, window_size_from_emacs_pixels};
use super::state::{
RenderGpuContext, effective_window_scale_factor, window_size_from_emacs_pixels,
};
use super::x11_hints::apply_window_geometry_hints;
use crate::thread_comm::InputEvent;
use std::sync::Arc;
Expand Down Expand Up @@ -125,7 +127,7 @@ impl RenderApp {
);

// Initialize wgpu with the window
self.init_wgpu(window.clone());
self.init_wgpu(event_loop, window.clone());

// Enable IME input for CJK and compose support
window.set_ime_allowed(true);
Expand Down Expand Up @@ -164,9 +166,13 @@ impl RenderApp {
}

// Process multi-window creates/destroys
if let (Some(device), Some(adapter)) = (&self.device, &self.adapter) {
self.multi_windows
.process_creates(event_loop, device, adapter);
if let Some(gpu) = &self.gpu {
self.multi_windows.process_creates(
event_loop,
&gpu.instance,
&gpu.device,
&gpu.adapter,
);
}
self.multi_windows.process_destroys();

Expand Down Expand Up @@ -336,16 +342,22 @@ impl RenderApp {
// Drop surface (holds wl_surface proxy if on Wayland)
drop(self.surface.take());
self.surface_config = None;
// Drop device and queue
drop(self.device.take());
drop(self.queue.take());
// Drop multi-window state (secondary surfaces)
self.multi_windows.destroy_all();
// Leak the adapter to prevent eglTerminate crash on Wayland.
// The adapter's Drop triggers eglTerminate → dri2_teardown_wayland which
// SEGVs if the Wayland connection is already gone. Since we're exiting,
// the OS will reclaim all GPU/EGL resources.
if let Some(adapter) = self.adapter.take() {
if let Some(gpu) = self.gpu.take() {
let RenderGpuContext {
instance,
adapter,
device,
queue,
} = gpu;
drop(device);
drop(queue);
drop(instance);
std::mem::forget(adapter);
}

Expand Down
7 changes: 2 additions & 5 deletions neomacs-display-runtime/src/render_thread/multi_window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ impl MultiWindowManager {
pub fn process_creates(
&mut self,
event_loop: &ActiveEventLoop,
instance: &wgpu::Instance,
device: &wgpu::Device,
adapter: &wgpu::Adapter,
) {
Expand All @@ -143,11 +144,7 @@ impl MultiWindowManager {
let scale_factor = effective_window_scale_factor(raw_scale_factor);
let phys = window.inner_size();

// Create surface for this window
let mut instance_descriptor =
wgpu::InstanceDescriptor::new_without_display_handle();
instance_descriptor.backends = wgpu::Backends::all();
let instance = wgpu::Instance::new(instance_descriptor);
// Create surface for this window using the primary display-bound instance.
let surface = match instance.create_surface(window.clone()) {
Ok(s) => s,
Err(e) => {
Expand Down
19 changes: 10 additions & 9 deletions neomacs-display-runtime/src/render_thread/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,13 @@ pub(super) struct ImeCursorArea {
pub(super) height: u32,
}

pub(super) struct RenderGpuContext {
pub(super) instance: wgpu::Instance,
pub(super) adapter: wgpu::Adapter,
pub(super) device: Arc<wgpu::Device>,
pub(super) queue: Arc<wgpu::Queue>,
}

pub(super) struct RenderApp {
pub(super) comms: RenderComms,
pub(super) window: Option<Arc<Window>>,
Expand All @@ -195,12 +202,11 @@ pub(super) struct RenderApp {
pub(super) title: String,
pub(super) primary_geometry_hints: Option<GuiFrameGeometryHints>,

// wgpu state
// Shared wgpu context used by the primary surface and secondary windows.
pub(super) gpu: Option<RenderGpuContext>,
pub(super) renderer: Option<WgpuRenderer>,
pub(super) surface: Option<wgpu::Surface<'static>>,
pub(super) surface_config: Option<wgpu::SurfaceConfiguration>,
pub(super) device: Option<Arc<wgpu::Device>>,
pub(super) queue: Option<Arc<wgpu::Queue>>,
pub(super) glyph_atlas: Option<WgpuGlyphAtlas>,

// Face cache built from frame data
Expand Down Expand Up @@ -256,9 +262,6 @@ pub(super) struct RenderApp {

// Multi-window manager (secondary OS windows for top-level frames)
pub(super) multi_windows: MultiWindowManager,
// wgpu adapter (needed for creating surfaces on new windows)
pub(super) adapter: Option<wgpu::Adapter>,

// Child frames (posframe, which-key-posframe, etc.)
pub(super) child_frames: ChildFrameManager,
// Child frame visual style
Expand Down Expand Up @@ -375,11 +378,10 @@ impl RenderApp {
title,
primary_geometry_hints: None,
scale_factor: 1.0,
gpu: None,
renderer: None,
surface: None,
surface_config: None,
device: None,
queue: None,
glyph_atlas: None,
faces: HashMap::new(),
modifiers: 0,
Expand All @@ -404,7 +406,6 @@ impl RenderApp {
#[cfg(feature = "neo-term")]
shared_terminals,
multi_windows: MultiWindowManager::new(),
adapter: None,
child_frames: ChildFrameManager::new(),
child_frame_corner_radius: 8.0,
child_frame_shadow_enabled: true,
Expand Down
2 changes: 1 addition & 1 deletion neomacs-display-runtime/src/render_thread/window_events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ impl RenderApp {
height: emacs_h,
emacs_frame_id: 0,
});
} else if let Some(device) = self.device.clone() {
} else if let Some(device) = self.gpu.as_ref().map(|gpu| gpu.device.clone()) {
if let Some(ws) = self.multi_windows.get_mut(emacs_fid) {
ws.handle_resize(&device, size.width, size.height);
Comment on lines +57 to 59
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

We can avoid cloning the Arc<wgpu::Device> here by matching on &self.gpu and passing a reference to gpu.device directly, since ws.handle_resize only requires a reference &wgpu::Device.

Suggested change
} else if let Some(device) = self.gpu.as_ref().map(|gpu| gpu.device.clone()) {
if let Some(ws) = self.multi_windows.get_mut(emacs_fid) {
ws.handle_resize(&device, size.width, size.height);
} else if let Some(gpu) = &self.gpu {
if let Some(ws) = self.multi_windows.get_mut(emacs_fid) {
ws.handle_resize(&gpu.device, size.width, size.height);

let (emacs_w, emacs_h) =
Expand Down
3 changes: 1 addition & 2 deletions neomacs-renderer-wgpu/src/renderer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1295,8 +1295,7 @@ impl WgpuRenderer {
height: u32,
) -> Result<Self, String> {
// Create wgpu instance
let mut instance_descriptor = wgpu::InstanceDescriptor::new_without_display_handle();
instance_descriptor.backends = wgpu::Backends::all();
let instance_descriptor = wgpu::InstanceDescriptor::new_without_display_handle_from_env();
let instance = wgpu::Instance::new(instance_descriptor);

// Request adapter
Expand Down
Loading