gpui: Fix crashes when losing devices while resizing on windows (#42740)
Fixes ZED-1HC Release Notes: - Fixed Zed panicking when moving Zed windows over different screens associated with different gpu devices on windows
This commit is contained in:
@@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<isize> {
|
||||
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,
|
||||
|
||||
@@ -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<DirectWriteTextSystem>,
|
||||
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<AtomicBool>,
|
||||
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<AtomicBool>,
|
||||
}
|
||||
|
||||
struct PlatformWindowCreateContext {
|
||||
|
||||
@@ -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<u32>,
|
||||
|
||||
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<AtomicBool>,
|
||||
fullscreen: Option<StyleAndBounds>,
|
||||
initial_placement: Option<WindowOpenStatus>,
|
||||
hwnd: HWND,
|
||||
@@ -83,6 +86,7 @@ impl WindowsWindowState {
|
||||
min_size: Option<Size<Pixels>>,
|
||||
appearance: WindowAppearance,
|
||||
disable_direct_composition: bool,
|
||||
invalidate_devices: Arc<AtomicBool>,
|
||||
) -> Result<Self> {
|
||||
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<AtomicBool>,
|
||||
}
|
||||
|
||||
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(
|
||||
|
||||
Reference in New Issue
Block a user