QOL improvement when device lost happens

This commit is contained in:
Junkui Zhang 2025-07-30 17:36:14 +08:00
parent cc763729a0
commit d194bf4f52
5 changed files with 42 additions and 14 deletions

View file

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

View file

@ -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; 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. // 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; const PATH_MULTISAMPLE_COUNT: u32 = 4;
@ -113,9 +113,7 @@ impl DirectXDevices {
} }
impl DirectXRenderer { impl DirectXRenderer {
pub(crate) fn new(hwnd: HWND) -> Result<Self> { pub(crate) fn new(hwnd: HWND, disable_direct_composition: bool) -> Result<Self> {
let disable_direct_composition = std::env::var(DISABLE_DIRECT_COMPOSITION)
.is_ok_and(|value| value == "true" || value == "1");
if disable_direct_composition { if disable_direct_composition {
log::info!("Direct Composition is disabled."); log::info!("Direct Composition is disabled.");
} }
@ -198,6 +196,9 @@ impl DirectXRenderer {
} }
fn handle_device_lost(&mut self) -> Result<()> { 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(); let disable_direct_composition = self.direct_composition.is_none();
unsafe { unsafe {
@ -323,6 +324,8 @@ impl DirectXRenderer {
"DirectX device removed or reset when resizing. Reason: {:?}", "DirectX device removed or reset when resizing. Reason: {:?}",
reason reason
); );
self.resources.width = width;
self.resources.height = height;
self.handle_device_lost()?; self.handle_device_lost()?;
return Ok(()); return Ok(());
} }

View file

@ -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_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_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_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 SIZE_MOVE_LOOP_TIMER_ID: usize = 1;
const AUTO_HIDE_TASKBAR_THICKNESS_PX: i32 = 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_SETTINGCHANGE => handle_system_settings_changed(handle, wparam, lparam, state_ptr),
WM_INPUTLANGCHANGE => handle_input_language_changed(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_CURSOR_STYLE_CHANGED => handle_cursor_changed(lparam, state_ptr),
WM_GPUI_FORCE_UPDATE_WINDOW => draw_window(handle, true, state_ptr),
_ => None, _ => None,
}; };
if let Some(n) = handled { if let Some(n) = handled {
@ -1202,6 +1204,19 @@ fn handle_device_change_msg(
state_ptr: Rc<WindowsWindowStatePtr>, state_ptr: Rc<WindowsWindowStatePtr>,
) -> Option<isize> { ) -> Option<isize> {
if wparam.0 == DBT_DEVNODES_CHANGED as usize { 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) draw_window(handle, true, state_ptr)
} else { } else {
// Other device change messages are not handled. // Other device change messages are not handled.
@ -1212,7 +1227,7 @@ fn handle_device_change_msg(
#[inline] #[inline]
fn draw_window( fn draw_window(
handle: HWND, handle: HWND,
force_draw: bool, force_render: bool,
state_ptr: Rc<WindowsWindowStatePtr>, state_ptr: Rc<WindowsWindowStatePtr>,
) -> Option<isize> { ) -> Option<isize> {
let mut request_frame = state_ptr let mut request_frame = state_ptr
@ -1222,7 +1237,8 @@ fn draw_window(
.request_frame .request_frame
.take()?; .take()?;
request_frame(RequestFrameOptions { request_frame(RequestFrameOptions {
require_presentation: force_draw, require_presentation: false,
force_render,
}); });
state_ptr.state.borrow_mut().callbacks.request_frame = Some(request_frame); state_ptr.state.borrow_mut().callbacks.request_frame = Some(request_frame);
unsafe { ValidateRect(Some(handle), None).ok().log_err() }; unsafe { ValidateRect(Some(handle), None).ok().log_err() };

View file

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

View file

@ -1020,7 +1020,7 @@ impl Window {
|| (active.get() || (active.get()
&& last_input_timestamp.get().elapsed() < Duration::from_secs(1)); && 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", || { measure("frame duration", || {
handle handle
.update(&mut cx, |_, window, cx| { .update(&mut cx, |_, window, cx| {