QOL improvement when device lost happens

Junkui Zhang created

Change summary

crates/gpui/src/platform.rs                          |  2 +
crates/gpui/src/platform/windows/directx_renderer.rs | 11 ++++--
crates/gpui/src/platform/windows/events.rs           | 20 ++++++++++++-
crates/gpui/src/platform/windows/window.rs           | 21 +++++++++----
crates/gpui/src/window.rs                            |  2 
5 files changed, 42 insertions(+), 14 deletions(-)

Detailed changes

crates/gpui/src/platform.rs 🔗

@@ -447,6 +447,8 @@ impl Tiling {
 #[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
 pub(crate) struct RequestFrameOptions {
     pub(crate) require_presentation: bool,
+    /// Force refresh of all rendering states when true
+    pub(crate) force_render: bool,
 }
 
 pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle {

crates/gpui/src/platform/windows/directx_renderer.rs 🔗

@@ -22,7 +22,7 @@ use crate::{
     *,
 };
 
-const DISABLE_DIRECT_COMPOSITION: &str = "GPUI_DISABLE_DIRECT_COMPOSITION";
+pub(crate) const DISABLE_DIRECT_COMPOSITION: &str = "GPUI_DISABLE_DIRECT_COMPOSITION";
 const RENDER_TARGET_FORMAT: DXGI_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM;
 // This configuration is used for MSAA rendering on paths only, and it's guaranteed to be supported by DirectX 11.
 const PATH_MULTISAMPLE_COUNT: u32 = 4;
@@ -113,9 +113,7 @@ impl DirectXDevices {
 }
 
 impl DirectXRenderer {
-    pub(crate) fn new(hwnd: HWND) -> Result<Self> {
-        let disable_direct_composition = std::env::var(DISABLE_DIRECT_COMPOSITION)
-            .is_ok_and(|value| value == "true" || value == "1");
+    pub(crate) fn new(hwnd: HWND, disable_direct_composition: bool) -> Result<Self> {
         if disable_direct_composition {
             log::info!("Direct Composition is disabled.");
         }
@@ -198,6 +196,9 @@ impl DirectXRenderer {
     }
 
     fn handle_device_lost(&mut self) -> Result<()> {
+        // Here we wait a bit to ensure the the system has time to recover from the device lost state.
+        // If we don't wait, the final drawing result will be blank.
+        std::thread::sleep(std::time::Duration::from_millis(300));
         let disable_direct_composition = self.direct_composition.is_none();
 
         unsafe {
@@ -323,6 +324,8 @@ impl DirectXRenderer {
                             "DirectX device removed or reset when resizing. Reason: {:?}",
                             reason
                         );
+                        self.resources.width = width;
+                        self.resources.height = height;
                         self.handle_device_lost()?;
                         return Ok(());
                     }

crates/gpui/src/platform/windows/events.rs 🔗

@@ -23,6 +23,7 @@ pub(crate) const WM_GPUI_CURSOR_STYLE_CHANGED: u32 = WM_USER + 1;
 pub(crate) const WM_GPUI_CLOSE_ONE_WINDOW: u32 = WM_USER + 2;
 pub(crate) const WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD: u32 = WM_USER + 3;
 pub(crate) const WM_GPUI_DOCK_MENU_ACTION: u32 = WM_USER + 4;
+pub(crate) const WM_GPUI_FORCE_UPDATE_WINDOW: u32 = WM_USER + 5;
 
 const SIZE_MOVE_LOOP_TIMER_ID: usize = 1;
 const AUTO_HIDE_TASKBAR_THICKNESS_PX: i32 = 1;
@@ -97,6 +98,7 @@ pub(crate) fn handle_msg(
         WM_SETTINGCHANGE => handle_system_settings_changed(handle, wparam, lparam, state_ptr),
         WM_INPUTLANGCHANGE => handle_input_language_changed(lparam, state_ptr),
         WM_GPUI_CURSOR_STYLE_CHANGED => handle_cursor_changed(lparam, state_ptr),
+        WM_GPUI_FORCE_UPDATE_WINDOW => draw_window(handle, true, state_ptr),
         _ => None,
     };
     if let Some(n) = handled {
@@ -1202,6 +1204,19 @@ fn handle_device_change_msg(
     state_ptr: Rc<WindowsWindowStatePtr>,
 ) -> Option<isize> {
     if wparam.0 == DBT_DEVNODES_CHANGED as usize {
+        // The reason for sending this message is to actually trigger a redraw of the window.
+        unsafe {
+            PostMessageW(
+                Some(handle),
+                WM_GPUI_FORCE_UPDATE_WINDOW,
+                WPARAM(0),
+                LPARAM(0),
+            )
+            .log_err();
+        }
+        // If the GPU device is lost, this redraw will take care of recreating the device context.
+        // The WM_GPUI_FORCE_UPDATE_WINDOW message will take care of redrawing the window, after
+        // the device context has been recreated.
         draw_window(handle, true, state_ptr)
     } else {
         // Other device change messages are not handled.
@@ -1212,7 +1227,7 @@ fn handle_device_change_msg(
 #[inline]
 fn draw_window(
     handle: HWND,
-    force_draw: bool,
+    force_render: bool,
     state_ptr: Rc<WindowsWindowStatePtr>,
 ) -> Option<isize> {
     let mut request_frame = state_ptr
@@ -1222,7 +1237,8 @@ fn draw_window(
         .request_frame
         .take()?;
     request_frame(RequestFrameOptions {
-        require_presentation: force_draw,
+        require_presentation: false,
+        force_render,
     });
     state_ptr.state.borrow_mut().callbacks.request_frame = Some(request_frame);
     unsafe { ValidateRect(Some(handle), None).ok().log_err() };

crates/gpui/src/platform/windows/window.rs 🔗

@@ -84,6 +84,7 @@ impl WindowsWindowState {
         display: WindowsDisplay,
         min_size: Option<Size<Pixels>>,
         appearance: WindowAppearance,
+        disable_direct_composition: bool,
     ) -> Result<Self> {
         let scale_factor = {
             let monitor_dpi = unsafe { GetDpiForWindow(hwnd) } as f32;
@@ -100,7 +101,7 @@ impl WindowsWindowState {
         };
         let border_offset = WindowBorderOffset::default();
         let restore_from_minimized = None;
-        let renderer = DirectXRenderer::new(hwnd)?;
+        let renderer = DirectXRenderer::new(hwnd, disable_direct_composition)?;
         let callbacks = Callbacks::default();
         let input_handler = None;
         let pending_surrogate = None;
@@ -208,6 +209,7 @@ impl WindowsWindowStatePtr {
             context.display,
             context.min_size,
             context.appearance,
+            context.disable_direct_composition,
         )?);
 
         Ok(Rc::new_cyclic(|this| Self {
@@ -339,6 +341,7 @@ struct WindowCreateContext {
     main_receiver: flume::Receiver<Runnable>,
     main_thread_id_win32: u32,
     appearance: WindowAppearance,
+    disable_direct_composition: bool,
 }
 
 impl WindowsWindow {
@@ -371,17 +374,20 @@ impl WindowsWindow {
                 .map(|title| title.as_ref())
                 .unwrap_or(""),
         );
-        let (dwexstyle, mut dwstyle) = if params.kind == WindowKind::PopUp {
-            (
-                WS_EX_TOOLWINDOW | WS_EX_NOREDIRECTIONBITMAP,
-                WINDOW_STYLE(0x0),
-            )
+        let disable_direct_composition = std::env::var(DISABLE_DIRECT_COMPOSITION)
+            .is_ok_and(|value| value == "true" || value == "1");
+
+        let (mut dwexstyle, dwstyle) = if params.kind == WindowKind::PopUp {
+            (WS_EX_TOOLWINDOW, WINDOW_STYLE(0x0))
         } else {
             (
-                WS_EX_APPWINDOW | WS_EX_NOREDIRECTIONBITMAP,
+                WS_EX_APPWINDOW,
                 WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX,
             )
         };
+        if !disable_direct_composition {
+            dwexstyle |= WS_EX_NOREDIRECTIONBITMAP;
+        }
 
         let hinstance = get_module_handle();
         let display = if let Some(display_id) = params.display_id {
@@ -406,6 +412,7 @@ impl WindowsWindow {
             main_receiver,
             main_thread_id_win32,
             appearance,
+            disable_direct_composition,
         };
         let lpparam = Some(&context as *const _ as *const _);
         let creation_result = unsafe {

crates/gpui/src/window.rs 🔗

@@ -1020,7 +1020,7 @@ impl Window {
                     || (active.get()
                         && last_input_timestamp.get().elapsed() < Duration::from_secs(1));
 
-                if invalidator.is_dirty() {
+                if invalidator.is_dirty() || request_frame_options.force_render {
                     measure("frame duration", || {
                         handle
                             .update(&mut cx, |_, window, cx| {