diff --git a/crates/gpui/src/platform/windows/directx_atlas.rs b/crates/gpui/src/platform/windows/directx_atlas.rs index 38c22a41bf9d32cf43f585050390b75602a6bf42..9deae392d1a5ef18a6af644031f047780fa23f70 100644 --- a/crates/gpui/src/platform/windows/directx_atlas.rs +++ b/crates/gpui/src/platform/windows/directx_atlas.rs @@ -234,11 +234,14 @@ impl DirectXAtlasState { } fn texture(&self, id: AtlasTextureId) -> &DirectXAtlasTexture { - let textures = match id.kind { - crate::AtlasTextureKind::Monochrome => &self.monochrome_textures, - crate::AtlasTextureKind::Polychrome => &self.polychrome_textures, - }; - textures[id.index as usize].as_ref().unwrap() + match id.kind { + crate::AtlasTextureKind::Monochrome => &self.monochrome_textures[id.index as usize] + .as_ref() + .unwrap(), + crate::AtlasTextureKind::Polychrome => &self.polychrome_textures[id.index as usize] + .as_ref() + .unwrap(), + } } } diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index b4180708aa510158456e6b9b0fe1ba1e0dfea85b..608ac2c3b065c598547be8b79f8d7fae8070ff48 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -48,6 +48,12 @@ pub(crate) struct DirectXRenderer { width: u32, height: u32, + + /// Whether we want to skip drwaing due to device lost events. + /// + /// In that case we want to discard the first frame that we draw as we got reset in the middle of a frame + /// meaning we lost all the allocated gpu textures and scene resources. + skip_draws: bool, } /// Direct3D objects @@ -167,6 +173,7 @@ impl DirectXRenderer { font_info: Self::get_font_info(), width: 1, height: 1, + skip_draws: false, }) } @@ -192,8 +199,13 @@ impl DirectXRenderer { }], )?; unsafe { - device_context - .ClearRenderTargetView(resources.render_target_view.as_ref().unwrap(), &[0.0; 4]); + device_context.ClearRenderTargetView( + resources + .render_target_view + .as_ref() + .context("missing render target view")?, + &[0.0; 4], + ); device_context .OMSetRenderTargets(Some(slice::from_ref(&resources.render_target_view)), None); device_context.RSSetViewports(Some(slice::from_ref(&resources.viewport))); @@ -283,10 +295,16 @@ impl DirectXRenderer { self.globals = globals; self.pipelines = pipelines; self.direct_composition = direct_composition; + self.skip_draws = true; Ok(()) } pub(crate) fn draw(&mut self, scene: &Scene) -> Result<()> { + if self.skip_draws { + // skip drawing this frame, we just recovered from a device lost event + // and so likely do not have the textures anymore that are required for drawing + return Ok(()); + } self.pre_draw()?; for batch in scene.batches() { match batch { @@ -306,14 +324,18 @@ impl DirectXRenderer { sprites, } => self.draw_polychrome_sprites(texture_id, sprites), PrimitiveBatch::Surfaces(surfaces) => self.draw_surfaces(surfaces), - }.context(format!("scene too large: {} paths, {} shadows, {} quads, {} underlines, {} mono, {} poly, {} surfaces", - scene.paths.len(), - scene.shadows.len(), - scene.quads.len(), - scene.underlines.len(), - scene.monochrome_sprites.len(), - scene.polychrome_sprites.len(), - scene.surfaces.len(),))?; + } + .context(format!( + "scene too large:\ + {} paths, {} shadows, {} quads, {} underlines, {} mono, {} poly, {} surfaces", + scene.paths.len(), + scene.shadows.len(), + scene.quads.len(), + scene.underlines.len(), + scene.monochrome_sprites.len(), + scene.polychrome_sprites.len(), + scene.surfaces.len(), + ))?; } self.present() } @@ -352,6 +374,7 @@ impl DirectXRenderer { } resources.recreate_resources(devices, width, height)?; + unsafe { devices .device_context @@ -647,6 +670,10 @@ impl DirectXRenderer { } }) } + + pub(crate) fn mark_drawable(&mut self) { + self.skip_draws = false; + } } impl DirectXResources { diff --git a/crates/gpui/src/platform/windows/events.rs b/crates/gpui/src/platform/windows/events.rs index f80348fdc1f88aedc1231d6579c82af4c76f3c34..cc39d3bcedd370fb4dc2fdb4c1d8304ad6b99b79 100644 --- a/crates/gpui/src/platform/windows/events.rs +++ b/crates/gpui/src/platform/windows/events.rs @@ -201,8 +201,10 @@ impl WindowsWindowInner { let new_logical_size = device_size.to_pixels(scale_factor); let mut lock = self.state.borrow_mut(); lock.logical_size = new_logical_size; - if should_resize_renderer { - lock.renderer.resize(device_size).log_err(); + if should_resize_renderer && let Err(e) = lock.renderer.resize(device_size) { + log::error!("Failed to resize renderer, invalidating devices: {}", e); + lock.invalidate_devices + .store(true, std::sync::atomic::Ordering::Release); } if let Some(mut callback) = lock.callbacks.resize.take() { drop(lock); @@ -1138,6 +1140,11 @@ impl WindowsWindowInner { #[inline] fn draw_window(&self, handle: HWND, force_render: bool) -> Option { let mut request_frame = self.state.borrow_mut().callbacks.request_frame.take()?; + + // we are instructing gpui to force render a frame, this will + // re-populate all the gpu textures for us so we can resume drawing in + // case we disabled drawing earlier due to a device loss + self.state.borrow_mut().renderer.mark_drawable(); request_frame(RequestFrameOptions { require_presentation: false, force_render, diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index d845c9520f736d06a6cee637328871af7e329241..b7f13f1fab495b1040d1be8e7b86376c450b5f7e 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -3,7 +3,10 @@ use std::{ ffi::OsStr, path::{Path, PathBuf}, rc::{Rc, Weak}, - sync::{Arc, atomic::Ordering}, + sync::{ + Arc, + atomic::{AtomicBool, Ordering}, + }, }; use ::util::{ResultExt, paths::SanitizedPath}; @@ -36,6 +39,9 @@ pub(crate) struct WindowsPlatform { text_system: Arc, windows_version: WindowsVersion, drop_target_helper: IDropTargetHelper, + /// 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. + invalidate_devices: Arc, handle: HWND, disable_direct_composition: bool, } @@ -162,6 +168,7 @@ impl WindowsPlatform { disable_direct_composition, windows_version, drop_target_helper, + invalidate_devices: Arc::new(AtomicBool::new(false)), }) } @@ -195,6 +202,7 @@ impl WindowsPlatform { platform_window_handle: self.handle, disable_direct_composition: self.disable_direct_composition, directx_devices: self.inner.state.borrow().directx_devices.clone().unwrap(), + invalidate_devices: self.invalidate_devices.clone(), } } @@ -247,13 +255,17 @@ impl WindowsPlatform { let validation_number = self.inner.validation_number; let all_windows = Arc::downgrade(&self.raw_window_handles); let text_system = Arc::downgrade(&self.text_system); + let invalidate_devices = self.invalidate_devices.clone(); + std::thread::Builder::new() .name("VSyncProvider".to_owned()) .spawn(move || { let vsync_provider = VSyncProvider::new(); loop { vsync_provider.wait_for_vsync(); - if check_device_lost(&directx_device.device) { + 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(), @@ -877,6 +889,9 @@ pub(crate) struct WindowCreationInfo { pub(crate) platform_window_handle: HWND, pub(crate) disable_direct_composition: bool, 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. + pub(crate) invalidate_devices: Arc, } struct PlatformWindowCreateContext { diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index 241293f0caa6c13de350c8b2fc44cb9d5abd82ec..fe6e6ff664a6c8f9b9524501ca1e875b5023169e 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -6,7 +6,7 @@ use std::{ path::PathBuf, rc::{Rc, Weak}, str::FromStr, - sync::{Arc, Once}, + sync::{Arc, Once, atomic::AtomicBool}, time::{Duration, Instant}, }; @@ -53,6 +53,9 @@ pub struct WindowsWindowState { pub nc_button_pressed: Option, pub display: WindowsDisplay, + /// 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. + pub invalidate_devices: Arc, fullscreen: Option, initial_placement: Option, hwnd: HWND, @@ -83,6 +86,7 @@ impl WindowsWindowState { min_size: Option>, appearance: WindowAppearance, disable_direct_composition: bool, + invalidate_devices: Arc, ) -> Result { let scale_factor = { let monitor_dpi = unsafe { GetDpiForWindow(hwnd) } as f32; @@ -138,6 +142,7 @@ impl WindowsWindowState { fullscreen, initial_placement, hwnd, + invalidate_devices, }) } @@ -211,6 +216,7 @@ impl WindowsWindowInner { context.min_size, context.appearance, context.disable_direct_composition, + context.invalidate_devices.clone(), )?); Ok(Rc::new(Self { @@ -361,6 +367,7 @@ struct WindowCreateContext { appearance: WindowAppearance, disable_direct_composition: bool, directx_devices: DirectXDevices, + invalidate_devices: Arc, } impl WindowsWindow { @@ -380,6 +387,7 @@ impl WindowsWindow { platform_window_handle, disable_direct_composition, directx_devices, + invalidate_devices, } = creation_info; register_window_class(icon); let hide_title_bar = params @@ -440,6 +448,7 @@ impl WindowsWindow { appearance, disable_direct_composition, directx_devices, + invalidate_devices, }; let creation_result = unsafe { CreateWindowExW(