diff --git a/Cargo.toml b/Cargo.toml index a27564c..96def43 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,7 +44,7 @@ serde = { version = "1", features = ["derive"] } serde_json = "1" thread-priority = "3" tokio = { version = "1", features = ["io-util", "macros", "net", "rt", "sync", "time"] } -tracing = { version = "0.1", features = ["release_max_level_info"] } +tracing = { version = "0.1", features = ["release_max_level_debug"] } tracing-subscriber = { version = "0.3", features = ["env-filter"] } wgpu = "29" wholesym = "0.8" diff --git a/src/app.rs b/src/app.rs index 92919e2..66233c6 100644 --- a/src/app.rs +++ b/src/app.rs @@ -14,11 +14,11 @@ use egui::{ use egui_wgpu::winit::Painter; use egui_winit::EventResponse; use gilrs::{EventType, Gilrs}; -use tracing::{error, warn}; +use tracing::{debug, error, warn}; use winit::{ application::ApplicationHandler, event::WindowEvent, - event_loop::{ActiveEventLoop, ControlFlow, EventLoopProxy}, + event_loop::{ActiveEventLoop, ControlFlow, EventLoopProxy, OwnedDisplayHandle}, window::{Window, WindowId}, }; @@ -48,9 +48,30 @@ fn load_icon() -> anyhow::Result { struct SharedViewportState { viewport_info: ViewportIdMap, - painter: Painter, + wgpu: WgpuState, + painter: Option, resized_viewport: Option, } +impl SharedViewportState { + fn init_painter(&mut self, ctx: &Context, window: &Window) -> &mut Painter { + if self.painter.is_none() { + let wgpu_config = egui_wgpu::WgpuConfiguration { + present_mode: wgpu::PresentMode::AutoVsync, + wgpu_setup: egui_wgpu::WgpuSetup::Existing(self.wgpu.init(window)), + ..egui_wgpu::WgpuConfiguration::default() + }; + + let options = egui_wgpu::RendererOptions::default(); + let painter = + pollster::block_on(Painter::new(ctx.clone(), wgpu_config, false, options)); + self.painter = Some(painter); + } + self.painter.as_mut().unwrap() + } + fn get_painter(&mut self) -> &mut Painter { + self.painter.as_mut().unwrap() + } +} pub struct Application { client: EmulatorClient, @@ -70,10 +91,11 @@ impl Application { pub fn new( client: EmulatorClient, proxy: EventLoopProxy, + handle: OwnedDisplayHandle, persistence: Persistence, args: CliArgs, ) -> Self { - let wgpu = WgpuState::new(); + let wgpu = WgpuState::new(handle); let icon = load_icon().ok().map(Arc::new); let mappings = MappingProvider::new(persistence.clone(), args.player2_controller); let shortcuts = ShortcutProvider::new(persistence.clone()); @@ -131,20 +153,6 @@ impl Application { }); } - let wgpu_config = egui_wgpu::WgpuConfiguration { - present_mode: wgpu::PresentMode::AutoVsync, - wgpu_setup: egui_wgpu::WgpuSetup::Existing(egui_wgpu::WgpuSetupExisting { - instance: wgpu.instance.clone(), - adapter: wgpu.adapter.clone(), - device: wgpu.device.clone(), - queue: wgpu.queue.clone(), - }), - ..egui_wgpu::WgpuConfiguration::default() - }; - - let options = egui_wgpu::RendererOptions::default(); - let painter = pollster::block_on(Painter::new(ctx.clone(), wgpu_config, false, options)); - let mut initial_windows = vec![]; if let Some(port) = args.debug_port { initial_windows.push(ChildWindow::Debugger { port: Some(port) }); @@ -184,7 +192,8 @@ impl Application { ctx, shared: SharedViewportState { viewport_info: ViewportIdMap::default(), - painter, + wgpu, + painter: None, resized_viewport: None, }, icon, @@ -289,7 +298,7 @@ impl Application { } let clipped_primitives = self.ctx.tessellate(output.shapes, output.pixels_per_point); viewport.window.pre_present_notify(); - self.shared.painter.paint_and_update_textures( + self.shared.get_painter().paint_and_update_textures( viewport.id, output.pixels_per_point, [0.0, 0.0, 0.0, 0.0], @@ -325,7 +334,11 @@ impl Application { id, InitArgs { window: &manager.window, - render_state: &self.shared.painter.render_state().unwrap(), + render_state: &self + .shared + .get_painter() + .render_state() + .unwrap(), }, ); manager @@ -341,7 +354,9 @@ impl Application { if !active_viewports.contains(&ViewportId::ROOT) { event_loop.exit(); } - self.shared.painter.gc_viewports(&active_viewports); + if let Some(painter) = self.shared.painter.as_mut() { + painter.gc_viewports(&active_viewports); + } } } @@ -359,7 +374,7 @@ impl ApplicationHandler for Application { viewport_builder, &mut self.shared, ); - let render_state = self.shared.painter.render_state().unwrap(); + let render_state = self.shared.get_painter().render_state().unwrap(); GameScreen::init_pipeline(&render_state); self.app.handle_init( ViewportId::ROOT, @@ -480,47 +495,62 @@ impl ApplicationHandler for Application { struct WgpuState { instance: wgpu::Instance, - adapter: wgpu::Adapter, - device: wgpu::Device, - queue: wgpu::Queue, + device_descriptor: Arc wgpu::DeviceDescriptor<'static> + Send + Sync>, } impl WgpuState { - fn new() -> Self { + fn new(handle: OwnedDisplayHandle) -> Self { #[allow(unused_variables)] - let egui_wgpu::WgpuConfiguration { - wgpu_setup: - egui_wgpu::WgpuSetup::CreateNew(egui_wgpu::WgpuSetupCreateNew { - instance_descriptor: wgpu::InstanceDescriptor { backends, .. }, - device_descriptor, - .. - }), + let egui_wgpu::WgpuSetupCreateNew { + instance_descriptor: wgpu::InstanceDescriptor { backends, .. }, + device_descriptor, .. - } = egui_wgpu::WgpuConfiguration::default() - else { - panic!("required fields not found") - }; + } = egui_wgpu::WgpuSetupCreateNew::without_display_handle(); #[cfg(windows)] let backends = wgpu::Backends::from_env() .unwrap_or((wgpu::Backends::PRIMARY | wgpu::Backends::GL) - wgpu::Backends::VULKAN); let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { backends, - ..wgpu::InstanceDescriptor::new_without_display_handle_from_env() + ..wgpu::InstanceDescriptor::new_with_display_handle_from_env(Box::new(handle)) }); - let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions { - power_preference: - wgpu::PowerPreference::from_env().unwrap_or(wgpu::PowerPreference::LowPower), - compatible_surface: None, - force_fallback_adapter: false, - })) - .expect("could not create adapter"); - - let (device, queue) = - pollster::block_on(adapter.request_device(&(*device_descriptor)(&adapter))) - .expect("could not request device"); Self { instance, + device_descriptor, + } + } + + fn init(&mut self, window: &Window) -> egui_wgpu::WgpuSetupExisting { + let power_preference = + wgpu::PowerPreference::from_env().unwrap_or(wgpu::PowerPreference::LowPower); + debug!("power preference: {power_preference:?}"); + + let force_fallback_adapter = std::env::var("WGPU_FORCE_FALLBACK") + .is_ok_and(|f| f.eq_ignore_ascii_case("true") || f.eq_ignore_ascii_case("1")); + debug!("force fallback: {force_fallback_adapter}"); + + let surface = self + .instance + .create_surface(window) + .expect("could not create surface"); + + let adapter = + pollster::block_on(self.instance.request_adapter(&wgpu::RequestAdapterOptions { + power_preference, + compatible_surface: Some(&surface), + force_fallback_adapter, + })) + .expect("could not create adapter"); + debug!("selected adapter: {:?}", adapter.get_info()); + + drop(surface); + + let (device, queue) = + pollster::block_on(adapter.request_device(&(*self.device_descriptor)(&adapter))) + .expect("could not request device"); + + egui_wgpu::WgpuSetupExisting { + instance: self.instance.clone(), adapter, device, queue, @@ -544,14 +574,15 @@ impl ViewportManager { shared: &mut SharedViewportState, ) -> Self { let window = Arc::new(egui_winit::create_window(ctx, event_loop, &builder).unwrap()); - pollster::block_on(shared.painter.set_window(viewport_id, Some(window.clone()))).unwrap(); + let painter = shared.init_painter(ctx, &window); + pollster::block_on(painter.set_window(viewport_id, Some(window.clone()))).unwrap(); let state = egui_winit::State::new( ctx.clone(), viewport_id, event_loop, Some(window.scale_factor() as f32), event_loop.system_theme(), - shared.painter.max_texture_side(), + painter.max_texture_side(), ); let mut info = ViewportInfo { @@ -588,9 +619,13 @@ impl ViewportManager { { if shared.resized_viewport != Some(self.id) { shared.resized_viewport = Some(self.id); - shared.painter.on_window_resize_state_change(self.id, true); + shared + .get_painter() + .on_window_resize_state_change(self.id, true); } - shared.painter.on_window_resized(self.id, width, height); + shared + .get_painter() + .on_window_resized(self.id, width, height); } let response = self.state.on_window_event(&self.window, event); let info = shared.viewport_info.get_mut(&self.id).unwrap(); diff --git a/src/main.rs b/src/main.rs index 200328e..6d60eae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -136,6 +136,13 @@ fn main() -> Result<()> { let event_loop = EventLoop::with_user_event().build().unwrap(); event_loop.set_control_flow(ControlFlow::Poll); let proxy = event_loop.create_proxy(); - event_loop.run_app(&mut Application::new(client, proxy, persistence, args))?; + let handle = event_loop.owned_display_handle(); + event_loop.run_app(&mut Application::new( + client, + proxy, + handle, + persistence, + args, + ))?; Ok(()) } diff --git a/src/window/game.rs b/src/window/game.rs index 9be7418..71e6724 100644 --- a/src/window/game.rs +++ b/src/window/game.rs @@ -180,7 +180,9 @@ impl GameWindow { let mut viewport = child.initial_viewport(); let title = viewport.title.clone().unwrap(); let sim_id = child.sim_id(); - if let Some(sim_id) = sim_id && self.show_player_name { + if let Some(sim_id) = sim_id + && self.show_player_name + { viewport.title = Some(format!("{title} ({sim_id})")); } if let Some(state) = self.child_states.get(&viewport_id) {