Detailed changes
@@ -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(