From ce08af96db6c3474ab3b388325cdd1a7c3de06d9 Mon Sep 17 00:00:00 2001 From: Scar-00 <69057575+Scar-00@users.noreply.github.com> Date: Thu, 25 Jun 2026 01:10:56 +0200 Subject: [PATCH] add wgpu renderer for windows (#44) --- crates/gpui_platform/Cargo.toml | 1 + crates/gpui_wgpu/src/wgpu_context.rs | 7 +- crates/gpui_windows/Cargo.toml | 2 + crates/gpui_windows/src/events.rs | 70 ++++++++----- crates/gpui_windows/src/gpui_windows.rs | 8 ++ crates/gpui_windows/src/platform.rs | 64 +++++++++--- crates/gpui_windows/src/util.rs | 11 ++- crates/gpui_windows/src/window.rs | 125 +++++++++++++++++++++--- 8 files changed, 231 insertions(+), 57 deletions(-) diff --git a/crates/gpui_platform/Cargo.toml b/crates/gpui_platform/Cargo.toml index d25b707211..f04a11f789 100644 --- a/crates/gpui_platform/Cargo.toml +++ b/crates/gpui_platform/Cargo.toml @@ -24,6 +24,7 @@ screen-capture = [ runtime_shaders = ["gpui_macos/runtime_shaders"] wayland = ["gpui_linux/wayland"] x11 = ["gpui_linux/x11"] +wgpu = ["gpui_windows/wgpu"] [dependencies] gpui.workspace = true diff --git a/crates/gpui_wgpu/src/wgpu_context.rs b/crates/gpui_wgpu/src/wgpu_context.rs index 21ca362979..8bc47eaece 100644 --- a/crates/gpui_wgpu/src/wgpu_context.rs +++ b/crates/gpui_wgpu/src/wgpu_context.rs @@ -209,8 +209,13 @@ impl WgpuContext { #[cfg(not(target_family = "wasm"))] pub fn instance(display: Box) -> wgpu::Instance { + #[cfg(not(target_os = "windows"))] + let backends = wgpu::Backends::VULKAN | wgpu::Backends::GL; + #[cfg(target_os = "windows")] + let backends = wgpu::Backends::DX12; + wgpu::Instance::new(wgpu::InstanceDescriptor { - backends: wgpu::Backends::VULKAN | wgpu::Backends::GL, + backends, flags: wgpu::InstanceFlags::default(), backend_options: wgpu::BackendOptions::default(), memory_budget_thresholds: wgpu::MemoryBudgetThresholds::default(), diff --git a/crates/gpui_windows/Cargo.toml b/crates/gpui_windows/Cargo.toml index f5a93e7f5a..c2aa3eea8e 100644 --- a/crates/gpui_windows/Cargo.toml +++ b/crates/gpui_windows/Cargo.toml @@ -15,9 +15,11 @@ path = "src/gpui_windows.rs" default = ["gpui/default"] test-support = ["gpui/test-support"] screen-capture = ["gpui/screen-capture", "scap"] +wgpu = ["dep:gpui_wgpu"] [dependencies] gpui.workspace = true +gpui_wgpu = { workspace = true, optional = true, features = ["font-kit"] } [target.'cfg(target_os = "windows")'.dependencies] accesskit.workspace = true diff --git a/crates/gpui_windows/src/events.rs b/crates/gpui_windows/src/events.rs index 51205946ef..efefc375ac 100644 --- a/crates/gpui_windows/src/events.rs +++ b/crates/gpui_windows/src/events.rs @@ -1,7 +1,8 @@ -use std::{rc::Rc, sync::atomic::Ordering}; - +#[cfg(feature = "wgpu")] +use crate::window::RawWindow; use ::util::ResultExt; use anyhow::Context as _; +use std::{rc::Rc, sync::atomic::Ordering}; use windows::{ Win32::{ Foundation::*, @@ -209,13 +210,25 @@ impl WindowsWindowInner { let new_logical_size = device_size.to_pixels(scale_factor); self.state.logical_size.set(new_logical_size); - if should_resize_renderer - && let Err(e) = self.state.renderer.borrow_mut().resize(device_size) + #[cfg(not(feature = "wgpu"))] { - log::error!("Failed to resize renderer, invalidating devices: {}", e); - self.state - .invalidate_devices - .store(true, std::sync::atomic::Ordering::Release); + if should_resize_renderer + && let Err(e) = self.state.renderer.borrow_mut().resize(device_size) + { + log::error!("Failed to resize renderer, invalidating devices: {}", e); + self.state + .invalidate_devices + .store(true, std::sync::atomic::Ordering::Release); + } + } + #[cfg(feature = "wgpu")] + { + if should_resize_renderer { + self.state + .renderer + .borrow_mut() + .update_drawable_size(device_size) + } } if let Some(mut callback) = self.state.callbacks.resize.take() { callback(new_logical_size, scale_factor); @@ -1193,15 +1206,27 @@ impl WindowsWindowInner { } fn handle_device_lost(&self, lparam: LPARAM) -> Option { - let devices = lparam.0 as *const DirectXDevices; - let devices = unsafe { &*devices }; - if let Err(err) = self - .state - .renderer - .borrow_mut() - .handle_device_lost(&devices) + #[cfg(not(feature = "wgpu"))] { - panic!("Device lost: {err}"); + let devices = lparam.0 as *const DirectXDevices; + let devices = unsafe { &*devices }; + if let Err(err) = self + .state + .renderer + .borrow_mut() + .handle_device_lost(&devices) + { + panic!("Device lost: {err}"); + } + } + #[cfg(feature = "wgpu")] + { + _ = lparam; + if let Err(err) = self.state.renderer.borrow_mut().recover(&RawWindow { + hwnd: self.platform_window_handle, + }) { + panic!("Device lost: {err}"); + } } // Make sure the first `draw_window` after recovery (whether it comes // from the forced WM_GPUI_FORCE_UPDATE_WINDOW or a stray WM_PAINT in @@ -1219,7 +1244,6 @@ impl WindowsWindowInner { #[inline] fn draw_window(&self, handle: HWND, force_render: bool) -> Option { let mut request_frame = self.state.callbacks.request_frame.take()?; - self.state.direct_manipulation.update(); let events = self.state.direct_manipulation.drain_events(); @@ -1231,12 +1255,14 @@ impl WindowsWindowInner { self.state.callbacks.input.set(Some(func)); } } - let force_render = force_render || self.state.force_render_after_recovery.take(); - if force_render { - // Re-enable drawing after a device loss recovery. The forced render - // will rebuild the scene with fresh atlas textures. - self.state.renderer.borrow_mut().mark_drawable(); + #[cfg(not(feature = "wgpu"))] + { + if force_render { + // Re-enable drawing after a device loss recovery. The forced render + // will rebuild the scene with fresh atlas textures. + self.state.renderer.borrow_mut().mark_drawable(); + } } request_frame(RequestFrameOptions { require_presentation: false, diff --git a/crates/gpui_windows/src/gpui_windows.rs b/crates/gpui_windows/src/gpui_windows.rs index 0af5411d20..4e111cfdde 100644 --- a/crates/gpui_windows/src/gpui_windows.rs +++ b/crates/gpui_windows/src/gpui_windows.rs @@ -3,9 +3,13 @@ mod clipboard; mod destination_list; mod direct_manipulation; +#[cfg(not(feature = "wgpu"))] mod direct_write; +#[cfg(not(feature = "wgpu"))] mod directx_atlas; +#[cfg(not(feature = "wgpu"))] mod directx_devices; +#[cfg(not(feature = "wgpu"))] mod directx_renderer; mod dispatcher; mod display; @@ -20,9 +24,13 @@ mod wrapper; pub(crate) use clipboard::*; pub(crate) use destination_list::*; +#[cfg(not(feature = "wgpu"))] pub(crate) use direct_write::*; +#[cfg(not(feature = "wgpu"))] pub(crate) use directx_atlas::*; +#[cfg(not(feature = "wgpu"))] pub(crate) use directx_devices::*; +#[cfg(not(feature = "wgpu"))] pub(crate) use directx_renderer::*; pub(crate) use dispatcher::*; pub(crate) use display::*; diff --git a/crates/gpui_windows/src/platform.rs b/crates/gpui_windows/src/platform.rs index 7b76c4d12a..a0366be537 100644 --- a/crates/gpui_windows/src/platform.rs +++ b/crates/gpui_windows/src/platform.rs @@ -15,11 +15,13 @@ use futures::channel::oneshot::{self, Receiver}; use itertools::Itertools; use parking_lot::RwLock; use smallvec::SmallVec; +#[cfg(not(feature = "wgpu"))] +use windows::Win32::Graphics::Direct3D11::ID3D11Device; use windows::{ UI::ViewManagement::UISettings, Win32::{ Foundation::*, - Graphics::{Direct3D11::ID3D11Device, Gdi::*}, + Graphics::Gdi::*, Security::Credentials::*, System::{Com::*, LibraryLoader::*, Ole::*, SystemInformation::*}, UI::{Input::KeyboardAndMouse::*, Shell::*, WindowsAndMessaging::*}, @@ -39,6 +41,7 @@ pub struct WindowsPlatform { background_executor: BackgroundExecutor, foreground_executor: ForegroundExecutor, text_system: Arc, + #[cfg(not(feature = "wgpu"))] direct_write_text_system: Option>, drop_target_helper: Option, /// Flag to instruct the `VSyncProvider` thread to invalidate the directx devices @@ -65,6 +68,7 @@ pub(crate) struct WindowsPlatformState { pub(crate) current_cursor: Cell>, /// Shared with each window so `WM_SETCURSOR` can read it directly. pub(crate) cursor_visible: Arc, + #[cfg(not(feature = "wgpu"))] directx_devices: RefCell>, } @@ -80,7 +84,7 @@ struct PlatformCallbacks { } impl WindowsPlatformState { - fn new(directx_devices: Option) -> Self { + fn new(#[cfg(not(feature = "wgpu"))] directx_devices: Option) -> Self { let callbacks = PlatformCallbacks::default(); let jump_list = JumpList::new(); let current_cursor = load_cursor(CursorStyle::Arrow); @@ -90,6 +94,7 @@ impl WindowsPlatformState { jump_list: RefCell::new(jump_list), current_cursor: Cell::new(current_cursor), cursor_visible: Arc::new(AtomicBool::new(true)), + #[cfg(not(feature = "wgpu"))] directx_devices: RefCell::new(directx_devices), menus: RefCell::new(Vec::new()), } @@ -101,6 +106,7 @@ impl WindowsPlatform { unsafe { OleInitialize(None).context("unable to initialize Windows OLE")?; } + #[cfg(not(feature = "wgpu"))] let (directx_devices, text_system, direct_write_text_system) = if !headless { let devices = DirectXDevices::new().context("Creating DirectX devices")?; let dw_text_system = Arc::new( @@ -119,6 +125,9 @@ impl WindowsPlatform { None, ) }; + #[cfg(feature = "wgpu")] + let text_system = + Arc::new(gpui_wgpu::CosmicTextSystem::new("Segoe UI")) as Arc; let (main_sender, main_receiver) = PriorityQueueReceiver::new(); let validation_number = if usize::BITS == 64 { @@ -135,6 +144,7 @@ impl WindowsPlatform { validation_number, main_sender: Some(main_sender), main_receiver: Some(main_receiver), + #[cfg(not(feature = "wgpu"))] directx_devices, dispatcher: None, }; @@ -164,6 +174,9 @@ impl WindowsPlatform { .context("CreateWindowExW did not run correctly")?; let handle = result?; + #[cfg(feature = "wgpu")] + let disable_direct_composition = true; + #[cfg(not(feature = "wgpu"))] let disable_direct_composition = std::env::var(DISABLE_DIRECT_COMPOSITION) .is_ok_and(|value| value == "true" || value == "1"); let background_executor = BackgroundExecutor::new(dispatcher.clone()); @@ -192,6 +205,7 @@ impl WindowsPlatform { background_executor, foreground_executor, text_system, + #[cfg(not(feature = "wgpu"))] direct_write_text_system, disable_direct_composition, drop_target_helper, @@ -228,6 +242,7 @@ impl WindowsPlatform { main_receiver: self.inner.main_receiver.clone(), platform_window_handle: self.handle, disable_direct_composition: self.disable_direct_composition, + #[cfg(not(feature = "wgpu"))] directx_devices: self.inner.state.directx_devices.borrow().clone().unwrap(), invalidate_devices: self.invalidate_devices.clone(), } @@ -295,17 +310,24 @@ impl WindowsPlatform { } fn begin_vsync_thread(&self) { + #[cfg(not(feature = "wgpu"))] let Some(directx_devices) = self.inner.state.directx_devices.borrow().clone() else { return; }; + #[cfg(not(feature = "wgpu"))] let Some(direct_write_text_system) = &self.direct_write_text_system else { return; }; + #[cfg(not(feature = "wgpu"))] let mut directx_device = directx_devices; + #[cfg(not(feature = "wgpu"))] let platform_window: SafeHwnd = self.handle.into(); + #[cfg(not(feature = "wgpu"))] let validation_number = self.inner.validation_number; let all_windows = Arc::downgrade(&self.raw_window_handles); + #[cfg(not(feature = "wgpu"))] let text_system = Arc::downgrade(direct_write_text_system); + #[cfg(not(feature = "wgpu"))] let invalidate_devices = self.invalidate_devices.clone(); std::thread::Builder::new() @@ -314,17 +336,20 @@ impl WindowsPlatform { let vsync_provider = VSyncProvider::new(); loop { vsync_provider.wait_for_vsync(); - if check_device_lost(&directx_device.device) - || invalidate_devices.fetch_and(false, Ordering::Acquire) + #[cfg(not(feature = "wgpu"))] { - if let Err(err) = handle_gpu_device_lost( - &mut directx_device, - platform_window.as_raw(), - validation_number, - &all_windows, - &text_system, - ) { - panic!("Device lost: {err}"); + if check_device_lost(&directx_device.device) + || invalidate_devices.fetch_and(false, Ordering::Acquire) + { + if let Err(err) = handle_gpu_device_lost( + &mut directx_device, + platform_window.as_raw(), + validation_number, + &all_windows, + &text_system, + ) { + panic!("Device lost: {err}"); + } } } let Some(all_windows) = all_windows.upgrade() else { @@ -840,7 +865,10 @@ impl Platform for WindowsPlatform { impl WindowsPlatformInner { fn new(context: &mut PlatformWindowCreateContext) -> Result> { + #[cfg(not(feature = "wgpu"))] let state = WindowsPlatformState::new(context.directx_devices.take()); + #[cfg(feature = "wgpu")] + let state = WindowsPlatformState::new(); Ok(Rc::new(Self { state, raw_window_handles: context.raw_window_handles.clone(), @@ -905,7 +933,12 @@ impl WindowsPlatformInner { WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD => self.run_foreground_task(), WM_GPUI_DOCK_MENU_ACTION => self.handle_dock_action_event(lparam.0 as _), WM_GPUI_KEYBOARD_LAYOUT_CHANGED => self.handle_keyboard_layout_change(), - WM_GPUI_GPU_DEVICE_LOST => self.handle_device_lost(lparam), + WM_GPUI_GPU_DEVICE_LOST => { + #[cfg(not(feature = "wgpu"))] + return self.handle_device_lost(lparam); + #[cfg(feature = "wgpu")] + Some(0) + } _ => unreachable!(), } } @@ -1019,6 +1052,7 @@ impl WindowsPlatformInner { Some(0) } + #[cfg(not(feature = "wgpu"))] fn handle_device_lost(&self, lparam: LPARAM) -> Option { let directx_devices = lparam.0 as *const DirectXDevices; let directx_devices = unsafe { &*directx_devices }; @@ -1050,6 +1084,7 @@ pub(crate) struct WindowCreationInfo { pub(crate) main_receiver: PriorityQueueReceiver, pub(crate) platform_window_handle: HWND, pub(crate) disable_direct_composition: bool, + #[cfg(not(feature = "wgpu"))] pub(crate) directx_devices: DirectXDevices, /// Flag to instruct the `VSyncProvider` thread to invalidate the directx devices /// as resizing them has failed, causing us to have lost at least the render target. @@ -1062,6 +1097,7 @@ struct PlatformWindowCreateContext { validation_number: usize, main_sender: Option>, main_receiver: Option>, + #[cfg(not(feature = "wgpu"))] directx_devices: Option, dispatcher: Option>, } @@ -1249,6 +1285,7 @@ fn should_auto_hide_scrollbars() -> Result { Ok(ui_settings.AutoHideScrollBars()?) } +#[cfg(not(feature = "wgpu"))] fn check_device_lost(device: &ID3D11Device) -> bool { let device_state = unsafe { device.GetDeviceRemovedReason() }; match device_state { @@ -1260,6 +1297,7 @@ fn check_device_lost(device: &ID3D11Device) -> bool { } } +#[cfg(not(feature = "wgpu"))] fn handle_gpu_device_lost( directx_devices: &mut DirectXDevices, platform_window: HWND, diff --git a/crates/gpui_windows/src/util.rs b/crates/gpui_windows/src/util.rs index 15dd4c6998..04e26990a8 100644 --- a/crates/gpui_windows/src/util.rs +++ b/crates/gpui_windows/src/util.rs @@ -1,18 +1,18 @@ use std::sync::OnceLock; use ::util::ResultExt; +#[cfg(not(feature = "wgpu"))] use anyhow::Context; use windows::{ UI::{ Color, ViewManagement::{UIColorType, UISettings}, }, - Win32::{ - Foundation::*, Graphics::Dwm::*, System::LibraryLoader::LoadLibraryA, - UI::WindowsAndMessaging::*, - }, - core::{BOOL, PCSTR}, + Win32::{Foundation::*, Graphics::Dwm::*, UI::WindowsAndMessaging::*}, + core::BOOL, }; +#[cfg(not(feature = "wgpu"))] +use windows::{Win32::System::LibraryLoader::LoadLibraryA, core::PCSTR}; use crate::*; use gpui::*; @@ -174,6 +174,7 @@ fn is_color_light(color: &Color) -> bool { ((5 * color.G as u32) + (2 * color.R as u32) + color.B as u32) > (8 * 128) } +#[cfg(not(feature = "wgpu"))] pub(crate) fn with_dll_library(dll_name: PCSTR, f: F) -> Result where F: FnOnce(HMODULE) -> Result, diff --git a/crates/gpui_windows/src/window.rs b/crates/gpui_windows/src/window.rs index 473a9c3541..2ea69ddac3 100644 --- a/crates/gpui_windows/src/window.rs +++ b/crates/gpui_windows/src/window.rs @@ -32,6 +32,9 @@ use crate::direct_manipulation::DirectManipulationHandler; use crate::*; use gpui::*; +#[cfg(feature = "wgpu")] +use gpui_wgpu::{WgpuRenderer, WgpuSurfaceConfig, wgpu}; + pub(crate) struct WindowsWindow(pub Rc); impl std::ops::Deref for WindowsWindow { @@ -62,6 +65,9 @@ pub struct WindowsWindowState { pub hovered: Cell, pub direct_manipulation: DirectManipulationHandler, + #[cfg(feature = "wgpu")] + pub renderer: RefCell, + #[cfg(not(feature = "wgpu"))] pub renderer: RefCell, /// Set after a GPU device-lost recovery so the next `draw_window` call is /// treated as a forced render. This guarantees the next frame both @@ -79,6 +85,7 @@ pub struct WindowsWindowState { pub display: Cell, /// Flag to instruct the `VSyncProvider` thread to invalidate the directx devices /// as resizing them has failed, causing us to have lost at least the render target. + #[cfg(not(feature = "wgpu"))] pub invalidate_devices: Arc, fullscreen: Cell>, initial_placement: Cell>, @@ -104,34 +111,45 @@ pub(crate) struct WindowsWindowInner { impl WindowsWindowState { fn new( hwnd: HWND, - directx_devices: &DirectXDevices, + #[cfg(not(feature = "wgpu"))] directx_devices: &DirectXDevices, window_params: &CREATESTRUCTW, current_cursor: Option, cursor_visible: Arc, display: WindowsDisplay, min_size: Option>, appearance: WindowAppearance, - disable_direct_composition: bool, - invalidate_devices: Arc, + #[cfg(not(feature = "wgpu"))] disable_direct_composition: bool, + #[cfg(not(feature = "wgpu"))] invalidate_devices: Arc, ) -> Result { let scale_factor = { let monitor_dpi = unsafe { GetDpiForWindow(hwnd) } as f32; monitor_dpi / USER_DEFAULT_SCREEN_DPI as f32 }; let origin = logical_point(window_params.x as f32, window_params.y as f32, scale_factor); - let logical_size = { - let physical_size = size( - DevicePixels(window_params.cx), - DevicePixels(window_params.cy), - ); - physical_size.to_pixels(scale_factor) - }; + let physical_size = size( + DevicePixels(window_params.cx), + DevicePixels(window_params.cy), + ); + let logical_size = { physical_size.to_pixels(scale_factor) }; let fullscreen_restore_bounds = Bounds { origin, size: logical_size, }; let border_offset = WindowBorderOffset::default(); let restore_from_minimized = None; + #[cfg(feature = "wgpu")] + let renderer = WgpuRenderer::new( + Rc::new(RefCell::new(None)), + &RawWindow { hwnd }, + WgpuSurfaceConfig { + size: physical_size, + transparent: false, + preferred_present_mode: Some(wgpu::PresentMode::Mailbox), + }, + None, + ) + .context("Creating Wgpu renderer")?; + #[cfg(not(feature = "wgpu"))] let renderer = DirectXRenderer::new(hwnd, directx_devices, disable_direct_composition) .context("Creating DirectX renderer")?; let callbacks = Callbacks::default(); @@ -175,6 +193,7 @@ impl WindowsWindowState { fullscreen: Cell::new(fullscreen), initial_placement: Cell::new(initial_placement), hwnd, + #[cfg(not(feature = "wgpu"))] invalidate_devices, direct_manipulation, a11y: RefCell::new(None), @@ -244,6 +263,7 @@ impl WindowsWindowInner { fn new(context: &mut WindowCreateContext, hwnd: HWND, cs: &CREATESTRUCTW) -> Result> { let state = WindowsWindowState::new( hwnd, + #[cfg(not(feature = "wgpu"))] &context.directx_devices, cs, context.current_cursor, @@ -251,7 +271,9 @@ impl WindowsWindowInner { context.display, context.min_size, context.appearance, + #[cfg(not(feature = "wgpu"))] context.disable_direct_composition, + #[cfg(not(feature = "wgpu"))] context.invalidate_devices.clone(), )?; @@ -393,8 +415,11 @@ struct WindowCreateContext { main_receiver: PriorityQueueReceiver, platform_window_handle: HWND, appearance: WindowAppearance, + #[cfg(not(feature = "wgpu"))] disable_direct_composition: bool, + #[cfg(not(feature = "wgpu"))] directx_devices: DirectXDevices, + #[cfg(not(feature = "wgpu"))] invalidate_devices: Arc, parent_hwnd: Option, } @@ -415,9 +440,14 @@ impl WindowsWindow { main_receiver, platform_window_handle, disable_direct_composition, + #[cfg(not(feature = "wgpu"))] directx_devices, invalidate_devices, } = creation_info; + #[cfg(feature = "wgpu")] + { + _ = invalidate_devices; + } register_window_class(icon); let parent_hwnd = if params.kind == WindowKind::Dialog { let parent_window = unsafe { GetActiveWindow() }; @@ -496,8 +526,11 @@ impl WindowsWindow { main_receiver, platform_window_handle, appearance, + #[cfg(not(feature = "wgpu"))] disable_direct_composition, + #[cfg(not(feature = "wgpu"))] directx_devices, + #[cfg(not(feature = "wgpu"))] invalidate_devices, parent_hwnd, }; @@ -548,6 +581,35 @@ impl WindowsWindow { } } +#[cfg(feature = "wgpu")] +#[derive(Debug, Clone, Copy)] +pub(crate) struct RawWindow { + pub hwnd: HWND, +} + +#[cfg(feature = "wgpu")] +unsafe impl Send for RawWindow {} +#[cfg(feature = "wgpu")] +unsafe impl Sync for RawWindow {} + +#[cfg(feature = "wgpu")] +impl rwh::HasWindowHandle for RawWindow { + fn window_handle(&self) -> std::result::Result, rwh::HandleError> { + let raw = rwh::Win32WindowHandle::new(unsafe { + NonZeroIsize::new_unchecked(self.hwnd.0 as isize) + }) + .into(); + Ok(unsafe { rwh::WindowHandle::borrow_raw(raw) }) + } +} + +#[cfg(feature = "wgpu")] +impl rwh::HasDisplayHandle for RawWindow { + fn display_handle(&self) -> std::result::Result, rwh::HandleError> { + Ok(rwh::DisplayHandle::windows()) + } +} + impl rwh::HasWindowHandle for WindowsWindow { fn window_handle(&self) -> std::result::Result, rwh::HandleError> { let raw = rwh::Win32WindowHandle::new(unsafe { @@ -951,15 +1013,43 @@ impl PlatformWindow for WindowsWindow { } fn draw(&self, scene: &Scene) { - self.state - .renderer - .borrow_mut() - .draw(scene, self.state.background_appearance.get()) - .log_err(); + #[cfg(not(feature = "wgpu"))] + { + self.state + .renderer + .borrow_mut() + .draw(scene, self.state.background_appearance.get()) + .log_err(); + } + + #[cfg(feature = "wgpu")] + { + let mut renderer = self.state.renderer.borrow_mut(); + if renderer.device_lost() { + match renderer.recover(&RawWindow { + hwnd: self.platform_window_handle, + }) { + Ok(()) => {} + Err(err) => { + log::warn!("GPU recovery failed, will retry on next frame: {err}"); + } + } + + self.state.force_render_after_recovery.set(true); + return; + } + if !renderer.draw(scene) { + log::error!("failed to render scene"); + } + + if renderer.needs_redraw() { + self.state.force_render_after_recovery.set(true); + } + } } fn sprite_atlas(&self) -> Arc { - self.state.renderer.borrow().sprite_atlas() + self.state.renderer.borrow().sprite_atlas().clone() } fn get_raw_handle(&self) -> HWND { @@ -967,6 +1057,9 @@ impl PlatformWindow for WindowsWindow { } fn gpu_specs(&self) -> Option { + #[cfg(feature = "wgpu")] + return Some(self.state.renderer.borrow().gpu_specs()); + #[cfg(not(feature = "wgpu"))] self.state.renderer.borrow().gpu_specs().log_err() }