windows: Keep just one copy of GPU instance (#37445)

张小白 created

Now we only keep a single copy of the GPU device. The GPU lost handling
got broken after #35376, but it’s properly handled again now.

Release Notes:

- N/A

Change summary

crates/gpui/src/platform/windows.rs                  |   2 
crates/gpui/src/platform/windows/direct_write.rs     |  34 +
crates/gpui/src/platform/windows/directx_devices.rs  | 180 +++++++++
crates/gpui/src/platform/windows/directx_renderer.rs | 268 ++++---------
crates/gpui/src/platform/windows/events.rs           |  29 -
crates/gpui/src/platform/windows/platform.rs         | 139 ++++++
crates/gpui/src/platform/windows/window.rs           |   9 
7 files changed, 429 insertions(+), 232 deletions(-)

Detailed changes

crates/gpui/src/platform/windows.rs πŸ”—

@@ -2,6 +2,7 @@ mod clipboard;
 mod destination_list;
 mod direct_write;
 mod directx_atlas;
+mod directx_devices;
 mod directx_renderer;
 mod dispatcher;
 mod display;
@@ -18,6 +19,7 @@ pub(crate) use clipboard::*;
 pub(crate) use destination_list::*;
 pub(crate) use direct_write::*;
 pub(crate) use directx_atlas::*;
+pub(crate) use directx_devices::*;
 pub(crate) use directx_renderer::*;
 pub(crate) use dispatcher::*;
 pub(crate) use display::*;

crates/gpui/src/platform/windows/direct_write.rs πŸ”—

@@ -1,7 +1,7 @@
 use std::{borrow::Cow, sync::Arc};
 
 use ::util::ResultExt;
-use anyhow::Result;
+use anyhow::{Context, Result};
 use collections::HashMap;
 use itertools::Itertools;
 use parking_lot::{RwLock, RwLockUpgradableReadGuard};
@@ -70,7 +70,7 @@ struct FontIdentifier {
 }
 
 impl DirectWriteComponent {
-    pub fn new(gpu_context: &DirectXDevices) -> Result<Self> {
+    pub fn new(directx_devices: &DirectXDevices) -> Result<Self> {
         // todo: ideally this would not be a large unsafe block but smaller isolated ones for easier auditing
         unsafe {
             let factory: IDWriteFactory5 = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED)?;
@@ -85,7 +85,7 @@ impl DirectWriteComponent {
             let locale = String::from_utf16_lossy(&locale_vec);
             let text_renderer = Arc::new(TextRendererWrapper::new(&locale));
 
-            let gpu_state = GPUState::new(gpu_context)?;
+            let gpu_state = GPUState::new(directx_devices)?;
 
             Ok(DirectWriteComponent {
                 locale,
@@ -100,9 +100,9 @@ impl DirectWriteComponent {
 }
 
 impl GPUState {
-    fn new(gpu_context: &DirectXDevices) -> Result<Self> {
-        let device = gpu_context.device.clone();
-        let device_context = gpu_context.device_context.clone();
+    fn new(directx_devices: &DirectXDevices) -> Result<Self> {
+        let device = directx_devices.device.clone();
+        let device_context = directx_devices.device_context.clone();
 
         let blend_state = {
             let mut blend_state = None;
@@ -183,8 +183,8 @@ impl GPUState {
 }
 
 impl DirectWriteTextSystem {
-    pub(crate) fn new(gpu_context: &DirectXDevices) -> Result<Self> {
-        let components = DirectWriteComponent::new(gpu_context)?;
+    pub(crate) fn new(directx_devices: &DirectXDevices) -> Result<Self> {
+        let components = DirectWriteComponent::new(directx_devices)?;
         let system_font_collection = unsafe {
             let mut result = std::mem::zeroed();
             components
@@ -210,6 +210,10 @@ impl DirectWriteTextSystem {
             font_id_by_identifier: HashMap::default(),
         })))
     }
+
+    pub(crate) fn handle_gpu_lost(&self, directx_devices: &DirectXDevices) {
+        self.0.write().handle_gpu_lost(directx_devices);
+    }
 }
 
 impl PlatformTextSystem for DirectWriteTextSystem {
@@ -1211,6 +1215,20 @@ impl DirectWriteState {
         ));
         result
     }
+
+    fn handle_gpu_lost(&mut self, directx_devices: &DirectXDevices) {
+        try_to_recover_from_device_lost(
+            || GPUState::new(directx_devices).context("Recreating GPU state for DirectWrite"),
+            |gpu_state| self.components.gpu_state = gpu_state,
+            || {
+                log::error!(
+                    "Failed to recreate GPU state for DirectWrite after multiple attempts."
+                );
+                // Do something here?
+                // At this point, the device loss is considered unrecoverable.
+            },
+        );
+    }
 }
 
 impl Drop for DirectWriteState {

crates/gpui/src/platform/windows/directx_devices.rs πŸ”—

@@ -0,0 +1,180 @@
+use anyhow::{Context, Result};
+use util::ResultExt;
+use windows::Win32::{
+    Foundation::HMODULE,
+    Graphics::{
+        Direct3D::{
+            D3D_DRIVER_TYPE_UNKNOWN, D3D_FEATURE_LEVEL, D3D_FEATURE_LEVEL_10_1,
+            D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1,
+        },
+        Direct3D11::{
+            D3D11_CREATE_DEVICE_BGRA_SUPPORT, D3D11_CREATE_DEVICE_DEBUG, D3D11_SDK_VERSION,
+            D3D11CreateDevice, ID3D11Device, ID3D11DeviceContext,
+        },
+        Dxgi::{
+            CreateDXGIFactory2, DXGI_CREATE_FACTORY_DEBUG, DXGI_CREATE_FACTORY_FLAGS,
+            DXGI_GPU_PREFERENCE_MINIMUM_POWER, IDXGIAdapter1, IDXGIFactory6,
+        },
+    },
+};
+
+pub(crate) fn try_to_recover_from_device_lost<T>(
+    mut f: impl FnMut() -> Result<T>,
+    on_success: impl FnOnce(T),
+    on_error: impl FnOnce(),
+) {
+    let result = (0..5).find_map(|i| {
+        if i > 0 {
+            // Add a small delay before retrying
+            std::thread::sleep(std::time::Duration::from_millis(100));
+        }
+        f().log_err()
+    });
+
+    if let Some(result) = result {
+        on_success(result);
+    } else {
+        on_error();
+    }
+}
+
+#[derive(Clone)]
+pub(crate) struct DirectXDevices {
+    pub(crate) adapter: IDXGIAdapter1,
+    pub(crate) dxgi_factory: IDXGIFactory6,
+    pub(crate) device: ID3D11Device,
+    pub(crate) device_context: ID3D11DeviceContext,
+}
+
+impl DirectXDevices {
+    pub(crate) fn new() -> Result<Self> {
+        let debug_layer_available = check_debug_layer_available();
+        let dxgi_factory =
+            get_dxgi_factory(debug_layer_available).context("Creating DXGI factory")?;
+        let adapter =
+            get_adapter(&dxgi_factory, debug_layer_available).context("Getting DXGI adapter")?;
+        let (device, device_context) = {
+            let mut device: Option<ID3D11Device> = None;
+            let mut context: Option<ID3D11DeviceContext> = None;
+            let mut feature_level = D3D_FEATURE_LEVEL::default();
+            get_device(
+                &adapter,
+                Some(&mut device),
+                Some(&mut context),
+                Some(&mut feature_level),
+                debug_layer_available,
+            )
+            .context("Creating Direct3D device")?;
+            match feature_level {
+                D3D_FEATURE_LEVEL_11_1 => {
+                    log::info!("Created device with Direct3D 11.1 feature level.")
+                }
+                D3D_FEATURE_LEVEL_11_0 => {
+                    log::info!("Created device with Direct3D 11.0 feature level.")
+                }
+                D3D_FEATURE_LEVEL_10_1 => {
+                    log::info!("Created device with Direct3D 10.1 feature level.")
+                }
+                _ => unreachable!(),
+            }
+            (device.unwrap(), context.unwrap())
+        };
+
+        Ok(Self {
+            adapter,
+            dxgi_factory,
+            device,
+            device_context,
+        })
+    }
+}
+
+#[inline]
+fn check_debug_layer_available() -> bool {
+    #[cfg(debug_assertions)]
+    {
+        use windows::Win32::Graphics::Dxgi::{DXGIGetDebugInterface1, IDXGIInfoQueue};
+
+        unsafe { DXGIGetDebugInterface1::<IDXGIInfoQueue>(0) }
+            .log_err()
+            .is_some()
+    }
+    #[cfg(not(debug_assertions))]
+    {
+        false
+    }
+}
+
+#[inline]
+fn get_dxgi_factory(debug_layer_available: bool) -> Result<IDXGIFactory6> {
+    let factory_flag = if debug_layer_available {
+        DXGI_CREATE_FACTORY_DEBUG
+    } else {
+        #[cfg(debug_assertions)]
+        log::warn!(
+            "Failed to get DXGI debug interface. DirectX debugging features will be disabled."
+        );
+        DXGI_CREATE_FACTORY_FLAGS::default()
+    };
+    unsafe { Ok(CreateDXGIFactory2(factory_flag)?) }
+}
+
+#[inline]
+fn get_adapter(dxgi_factory: &IDXGIFactory6, debug_layer_available: bool) -> Result<IDXGIAdapter1> {
+    for adapter_index in 0.. {
+        let adapter: IDXGIAdapter1 = unsafe {
+            dxgi_factory
+                .EnumAdapterByGpuPreference(adapter_index, DXGI_GPU_PREFERENCE_MINIMUM_POWER)
+        }?;
+        if let Ok(desc) = unsafe { adapter.GetDesc1() } {
+            let gpu_name = String::from_utf16_lossy(&desc.Description)
+                .trim_matches(char::from(0))
+                .to_string();
+            log::info!("Using GPU: {}", gpu_name);
+        }
+        // Check to see whether the adapter supports Direct3D 11, but don't
+        // create the actual device yet.
+        if get_device(&adapter, None, None, None, debug_layer_available)
+            .log_err()
+            .is_some()
+        {
+            return Ok(adapter);
+        }
+    }
+
+    unreachable!()
+}
+
+#[inline]
+fn get_device(
+    adapter: &IDXGIAdapter1,
+    device: Option<*mut Option<ID3D11Device>>,
+    context: Option<*mut Option<ID3D11DeviceContext>>,
+    feature_level: Option<*mut D3D_FEATURE_LEVEL>,
+    debug_layer_available: bool,
+) -> Result<()> {
+    let device_flags = if debug_layer_available {
+        D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG
+    } else {
+        D3D11_CREATE_DEVICE_BGRA_SUPPORT
+    };
+    unsafe {
+        D3D11CreateDevice(
+            adapter,
+            D3D_DRIVER_TYPE_UNKNOWN,
+            HMODULE::default(),
+            device_flags,
+            // 4x MSAA is required for Direct3D Feature Level 10.1 or better
+            Some(&[
+                D3D_FEATURE_LEVEL_11_1,
+                D3D_FEATURE_LEVEL_11_0,
+                D3D_FEATURE_LEVEL_10_1,
+            ]),
+            D3D11_SDK_VERSION,
+            device,
+            feature_level,
+            context,
+        )?;
+    }
+    Ok(())
+}

crates/gpui/src/platform/windows/directx_renderer.rs πŸ”—

@@ -7,7 +7,7 @@ use ::util::ResultExt;
 use anyhow::{Context, Result};
 use windows::{
     Win32::{
-        Foundation::{HMODULE, HWND},
+        Foundation::HWND,
         Graphics::{
             Direct3D::*,
             Direct3D11::*,
@@ -39,7 +39,7 @@ pub(crate) struct FontInfo {
 pub(crate) struct DirectXRenderer {
     hwnd: HWND,
     atlas: Arc<DirectXAtlas>,
-    devices: ManuallyDrop<DirectXDevices>,
+    devices: ManuallyDrop<DirectXRendererDevices>,
     resources: ManuallyDrop<DirectXResources>,
     globals: DirectXGlobalElements,
     pipelines: DirectXRenderPipelines,
@@ -49,9 +49,9 @@ pub(crate) struct DirectXRenderer {
 
 /// Direct3D objects
 #[derive(Clone)]
-pub(crate) struct DirectXDevices {
-    adapter: IDXGIAdapter1,
-    dxgi_factory: IDXGIFactory6,
+pub(crate) struct DirectXRendererDevices {
+    pub(crate) adapter: IDXGIAdapter1,
+    pub(crate) dxgi_factory: IDXGIFactory6,
     pub(crate) device: ID3D11Device,
     pub(crate) device_context: ID3D11DeviceContext,
     dxgi_device: Option<IDXGIDevice>,
@@ -96,39 +96,17 @@ struct DirectComposition {
     comp_visual: IDCompositionVisual,
 }
 
-impl DirectXDevices {
-    pub(crate) fn new(disable_direct_composition: bool) -> Result<ManuallyDrop<Self>> {
-        let debug_layer_available = check_debug_layer_available();
-        let dxgi_factory =
-            get_dxgi_factory(debug_layer_available).context("Creating DXGI factory")?;
-        let adapter =
-            get_adapter(&dxgi_factory, debug_layer_available).context("Getting DXGI adapter")?;
-        let (device, device_context) = {
-            let mut device: Option<ID3D11Device> = None;
-            let mut context: Option<ID3D11DeviceContext> = None;
-            let mut feature_level = D3D_FEATURE_LEVEL::default();
-            get_device(
-                &adapter,
-                Some(&mut device),
-                Some(&mut context),
-                Some(&mut feature_level),
-                debug_layer_available,
-            )
-            .context("Creating Direct3D device")?;
-            match feature_level {
-                D3D_FEATURE_LEVEL_11_1 => {
-                    log::info!("Created device with Direct3D 11.1 feature level.")
-                }
-                D3D_FEATURE_LEVEL_11_0 => {
-                    log::info!("Created device with Direct3D 11.0 feature level.")
-                }
-                D3D_FEATURE_LEVEL_10_1 => {
-                    log::info!("Created device with Direct3D 10.1 feature level.")
-                }
-                _ => unreachable!(),
-            }
-            (device.unwrap(), context.unwrap())
-        };
+impl DirectXRendererDevices {
+    pub(crate) fn new(
+        directx_devices: &DirectXDevices,
+        disable_direct_composition: bool,
+    ) -> Result<ManuallyDrop<Self>> {
+        let DirectXDevices {
+            adapter,
+            dxgi_factory,
+            device,
+            device_context,
+        } = directx_devices;
         let dxgi_device = if disable_direct_composition {
             None
         } else {
@@ -136,23 +114,27 @@ impl DirectXDevices {
         };
 
         Ok(ManuallyDrop::new(Self {
-            adapter,
-            dxgi_factory,
+            adapter: adapter.clone(),
+            dxgi_factory: dxgi_factory.clone(),
+            device: device.clone(),
+            device_context: device_context.clone(),
             dxgi_device,
-            device,
-            device_context,
         }))
     }
 }
 
 impl DirectXRenderer {
-    pub(crate) fn new(hwnd: HWND, disable_direct_composition: bool) -> Result<Self> {
+    pub(crate) fn new(
+        hwnd: HWND,
+        directx_devices: &DirectXDevices,
+        disable_direct_composition: bool,
+    ) -> Result<Self> {
         if disable_direct_composition {
             log::info!("Direct Composition is disabled.");
         }
 
-        let devices =
-            DirectXDevices::new(disable_direct_composition).context("Creating DirectX devices")?;
+        let devices = DirectXRendererDevices::new(directx_devices, disable_direct_composition)
+            .context("Creating DirectX devices")?;
         let atlas = Arc::new(DirectXAtlas::new(&devices.device, &devices.device_context));
 
         let resources = DirectXResources::new(&devices, 1, 1, hwnd, disable_direct_composition)
@@ -218,28 +200,30 @@ impl DirectXRenderer {
         Ok(())
     }
 
+    #[inline]
     fn present(&mut self) -> Result<()> {
-        unsafe {
-            let result = self.resources.swap_chain.Present(0, DXGI_PRESENT(0));
-            // Presenting the swap chain can fail if the DirectX device was removed or reset.
-            if result == DXGI_ERROR_DEVICE_REMOVED || result == DXGI_ERROR_DEVICE_RESET {
-                let reason = self.devices.device.GetDeviceRemovedReason();
+        let result = unsafe { self.resources.swap_chain.Present(0, DXGI_PRESENT(0)) };
+        result.ok().context("Presenting swap chain failed")
+    }
+
+    pub(crate) fn handle_device_lost(&mut self, directx_devices: &DirectXDevices) {
+        try_to_recover_from_device_lost(
+            || {
+                self.handle_device_lost_impl(directx_devices)
+                    .context("DirectXRenderer handling device lost")
+            },
+            |_| {},
+            || {
                 log::error!(
-                    "DirectX device removed or reset when drawing. Reason: {:?}",
-                    reason
+                    "DirectXRenderer failed to recover from device lost after multiple attempts"
                 );
-                self.handle_device_lost()?;
-            } else {
-                result.ok()?;
-            }
-        }
-        Ok(())
+                // Do something here?
+                // At this point, the device loss is considered unrecoverable.
+            },
+        );
     }
 
-    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));
+    fn handle_device_lost_impl(&mut self, directx_devices: &DirectXDevices) -> Result<()> {
         let disable_direct_composition = self.direct_composition.is_none();
 
         unsafe {
@@ -262,7 +246,7 @@ impl DirectXRenderer {
             ManuallyDrop::drop(&mut self.devices);
         }
 
-        let devices = DirectXDevices::new(disable_direct_composition)
+        let devices = DirectXRendererDevices::new(directx_devices, disable_direct_composition)
             .context("Recreating DirectX devices")?;
         let resources = DirectXResources::new(
             &devices,
@@ -337,49 +321,39 @@ impl DirectXRenderer {
         if self.resources.width == width && self.resources.height == height {
             return Ok(());
         }
+        self.resources.width = width;
+        self.resources.height = height;
+
+        // Clear the render target before resizing
+        unsafe { self.devices.device_context.OMSetRenderTargets(None, None) };
+        unsafe { ManuallyDrop::drop(&mut self.resources.render_target) };
+        drop(self.resources.render_target_view[0].take().unwrap());
+
+        // Resizing the swap chain requires a call to the underlying DXGI adapter, which can return the device removed error.
+        // The app might have moved to a monitor that's attached to a different graphics device.
+        // When a graphics device is removed or reset, the desktop resolution often changes, resulting in a window size change.
+        // But here we just return the error, because we are handling device lost scenarios elsewhere.
         unsafe {
-            // Clear the render target before resizing
-            self.devices.device_context.OMSetRenderTargets(None, None);
-            ManuallyDrop::drop(&mut self.resources.render_target);
-            drop(self.resources.render_target_view[0].take().unwrap());
-
-            let result = self.resources.swap_chain.ResizeBuffers(
-                BUFFER_COUNT as u32,
-                width,
-                height,
-                RENDER_TARGET_FORMAT,
-                DXGI_SWAP_CHAIN_FLAG(0),
-            );
-            // Resizing the swap chain requires a call to the underlying DXGI adapter, which can return the device removed error.
-            // The app might have moved to a monitor that's attached to a different graphics device.
-            // When a graphics device is removed or reset, the desktop resolution often changes, resulting in a window size change.
-            match result {
-                Ok(_) => {}
-                Err(e) => {
-                    if e.code() == DXGI_ERROR_DEVICE_REMOVED || e.code() == DXGI_ERROR_DEVICE_RESET
-                    {
-                        let reason = self.devices.device.GetDeviceRemovedReason();
-                        log::error!(
-                            "DirectX device removed or reset when resizing. Reason: {:?}",
-                            reason
-                        );
-                        self.resources.width = width;
-                        self.resources.height = height;
-                        self.handle_device_lost()?;
-                        return Ok(());
-                    } else {
-                        log::error!("Failed to resize swap chain: {:?}", e);
-                        return Err(e.into());
-                    }
-                }
-            }
-
             self.resources
-                .recreate_resources(&self.devices, width, height)?;
+                .swap_chain
+                .ResizeBuffers(
+                    BUFFER_COUNT as u32,
+                    width,
+                    height,
+                    RENDER_TARGET_FORMAT,
+                    DXGI_SWAP_CHAIN_FLAG(0),
+                )
+                .context("Failed to resize swap chain")?;
+        }
+
+        self.resources
+            .recreate_resources(&self.devices, width, height)?;
+        unsafe {
             self.devices
                 .device_context
                 .OMSetRenderTargets(Some(&self.resources.render_target_view), None);
         }
+
         Ok(())
     }
 
@@ -680,7 +654,7 @@ impl DirectXRenderer {
 
 impl DirectXResources {
     pub fn new(
-        devices: &DirectXDevices,
+        devices: &DirectXRendererDevices,
         width: u32,
         height: u32,
         hwnd: HWND,
@@ -725,7 +699,7 @@ impl DirectXResources {
     #[inline]
     fn recreate_resources(
         &mut self,
-        devices: &DirectXDevices,
+        devices: &DirectXRendererDevices,
         width: u32,
         height: u32,
     ) -> Result<()> {
@@ -745,8 +719,6 @@ impl DirectXResources {
         self.path_intermediate_msaa_view = path_intermediate_msaa_view;
         self.path_intermediate_srv = path_intermediate_srv;
         self.viewport = viewport;
-        self.width = width;
-        self.height = height;
         Ok(())
     }
 }
@@ -1041,92 +1013,6 @@ impl Drop for DirectXResources {
     }
 }
 
-#[inline]
-fn check_debug_layer_available() -> bool {
-    #[cfg(debug_assertions)]
-    {
-        unsafe { DXGIGetDebugInterface1::<IDXGIInfoQueue>(0) }
-            .log_err()
-            .is_some()
-    }
-    #[cfg(not(debug_assertions))]
-    {
-        false
-    }
-}
-
-#[inline]
-fn get_dxgi_factory(debug_layer_available: bool) -> Result<IDXGIFactory6> {
-    let factory_flag = if debug_layer_available {
-        DXGI_CREATE_FACTORY_DEBUG
-    } else {
-        #[cfg(debug_assertions)]
-        log::warn!(
-            "Failed to get DXGI debug interface. DirectX debugging features will be disabled."
-        );
-        DXGI_CREATE_FACTORY_FLAGS::default()
-    };
-    unsafe { Ok(CreateDXGIFactory2(factory_flag)?) }
-}
-
-fn get_adapter(dxgi_factory: &IDXGIFactory6, debug_layer_available: bool) -> Result<IDXGIAdapter1> {
-    for adapter_index in 0.. {
-        let adapter: IDXGIAdapter1 = unsafe {
-            dxgi_factory
-                .EnumAdapterByGpuPreference(adapter_index, DXGI_GPU_PREFERENCE_MINIMUM_POWER)
-        }?;
-        if let Ok(desc) = unsafe { adapter.GetDesc1() } {
-            let gpu_name = String::from_utf16_lossy(&desc.Description)
-                .trim_matches(char::from(0))
-                .to_string();
-            log::info!("Using GPU: {}", gpu_name);
-        }
-        // Check to see whether the adapter supports Direct3D 11, but don't
-        // create the actual device yet.
-        if get_device(&adapter, None, None, None, debug_layer_available)
-            .log_err()
-            .is_some()
-        {
-            return Ok(adapter);
-        }
-    }
-
-    unreachable!()
-}
-
-fn get_device(
-    adapter: &IDXGIAdapter1,
-    device: Option<*mut Option<ID3D11Device>>,
-    context: Option<*mut Option<ID3D11DeviceContext>>,
-    feature_level: Option<*mut D3D_FEATURE_LEVEL>,
-    debug_layer_available: bool,
-) -> Result<()> {
-    let device_flags = if debug_layer_available {
-        D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG
-    } else {
-        D3D11_CREATE_DEVICE_BGRA_SUPPORT
-    };
-    unsafe {
-        D3D11CreateDevice(
-            adapter,
-            D3D_DRIVER_TYPE_UNKNOWN,
-            HMODULE::default(),
-            device_flags,
-            // 4x MSAA is required for Direct3D Feature Level 10.1 or better
-            Some(&[
-                D3D_FEATURE_LEVEL_11_1,
-                D3D_FEATURE_LEVEL_11_0,
-                D3D_FEATURE_LEVEL_10_1,
-            ]),
-            D3D11_SDK_VERSION,
-            device,
-            feature_level,
-            context,
-        )?;
-    }
-    Ok(())
-}
-
 #[inline]
 fn get_comp_device(dxgi_device: &IDXGIDevice) -> Result<IDCompositionDevice> {
     Ok(unsafe { DCompositionCreateDevice(dxgi_device)? })
@@ -1191,7 +1077,7 @@ fn create_swap_chain(
 
 #[inline]
 fn create_resources(
-    devices: &DirectXDevices,
+    devices: &DirectXRendererDevices,
     swap_chain: &IDXGISwapChain1,
     width: u32,
     height: u32,

crates/gpui/src/platform/windows/events.rs πŸ”—

@@ -25,6 +25,7 @@ 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;
 pub(crate) const WM_GPUI_KEYBOARD_LAYOUT_CHANGED: u32 = WM_USER + 6;
+pub(crate) const WM_GPUI_GPU_DEVICE_LOST: u32 = WM_USER + 7;
 
 const SIZE_MOVE_LOOP_TIMER_ID: usize = 1;
 const AUTO_HIDE_TASKBAR_THICKNESS_PX: i32 = 1;
@@ -40,7 +41,6 @@ impl WindowsWindowInner {
         let handled = match msg {
             WM_ACTIVATE => self.handle_activate_msg(wparam),
             WM_CREATE => self.handle_create_msg(handle),
-            WM_DEVICECHANGE => self.handle_device_change_msg(handle, wparam),
             WM_MOVE => self.handle_move_msg(handle, lparam),
             WM_SIZE => self.handle_size_msg(wparam, lparam),
             WM_GETMINMAXINFO => self.handle_get_min_max_info_msg(lparam),
@@ -104,6 +104,7 @@ impl WindowsWindowInner {
             WM_SHOWWINDOW => self.handle_window_visibility_changed(handle, wparam),
             WM_GPUI_CURSOR_STYLE_CHANGED => self.handle_cursor_changed(lparam),
             WM_GPUI_FORCE_UPDATE_WINDOW => self.draw_window(handle, true),
+            WM_GPUI_GPU_DEVICE_LOST => self.handle_device_lost(lparam),
             _ => None,
         };
         if let Some(n) = handled {
@@ -1167,26 +1168,12 @@ impl WindowsWindowInner {
         None
     }
 
-    fn handle_device_change_msg(&self, handle: HWND, wparam: WPARAM) -> 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.
-            self.draw_window(handle, true)
-        } else {
-            // Other device change messages are not handled.
-            None
-        }
+    fn handle_device_lost(&self, lparam: LPARAM) -> Option<isize> {
+        let mut lock = self.state.borrow_mut();
+        let devices = lparam.0 as *const DirectXDevices;
+        let devices = unsafe { &*devices };
+        lock.renderer.handle_device_lost(&devices);
+        Some(0)
     }
 
     #[inline]

crates/gpui/src/platform/windows/platform.rs πŸ”—

@@ -1,6 +1,7 @@
 use std::{
     cell::RefCell,
     ffi::OsStr,
+    mem::ManuallyDrop,
     path::{Path, PathBuf},
     rc::{Rc, Weak},
     sync::Arc,
@@ -17,7 +18,7 @@ use windows::{
     UI::ViewManagement::UISettings,
     Win32::{
         Foundation::*,
-        Graphics::Gdi::*,
+        Graphics::{Direct3D11::ID3D11Device, Gdi::*},
         Security::Credentials::*,
         System::{Com::*, LibraryLoader::*, Ole::*, SystemInformation::*},
         UI::{Input::KeyboardAndMouse::*, Shell::*, WindowsAndMessaging::*},
@@ -55,6 +56,7 @@ pub(crate) struct WindowsPlatformState {
     jump_list: JumpList,
     // NOTE: standard cursor handles don't need to close.
     pub(crate) current_cursor: Option<HCURSOR>,
+    directx_devices: ManuallyDrop<DirectXDevices>,
 }
 
 #[derive(Default)]
@@ -69,15 +71,17 @@ struct PlatformCallbacks {
 }
 
 impl WindowsPlatformState {
-    fn new() -> Self {
+    fn new(directx_devices: DirectXDevices) -> Self {
         let callbacks = PlatformCallbacks::default();
         let jump_list = JumpList::new();
         let current_cursor = load_cursor(CursorStyle::Arrow);
+        let directx_devices = ManuallyDrop::new(directx_devices);
 
         Self {
             callbacks,
             jump_list,
             current_cursor,
+            directx_devices,
             menus: Vec::new(),
         }
     }
@@ -88,15 +92,21 @@ impl WindowsPlatform {
         unsafe {
             OleInitialize(None).context("unable to initialize Windows OLE")?;
         }
+        let directx_devices = DirectXDevices::new().context("Creating DirectX devices")?;
         let (main_sender, main_receiver) = flume::unbounded::<Runnable>();
         let validation_number = rand::random::<usize>();
         let raw_window_handles = Arc::new(RwLock::new(SmallVec::new()));
+        let text_system = Arc::new(
+            DirectWriteTextSystem::new(&directx_devices)
+                .context("Error creating DirectWriteTextSystem")?,
+        );
         register_platform_window_class();
         let mut context = PlatformWindowCreateContext {
             inner: None,
             raw_window_handles: Arc::downgrade(&raw_window_handles),
             validation_number,
             main_receiver: Some(main_receiver),
+            directx_devices: Some(directx_devices),
         };
         let result = unsafe {
             CreateWindowExW(
@@ -125,12 +135,7 @@ impl WindowsPlatform {
             .is_ok_and(|value| value == "true" || value == "1");
         let background_executor = BackgroundExecutor::new(dispatcher.clone());
         let foreground_executor = ForegroundExecutor::new(dispatcher);
-        let directx_devices = DirectXDevices::new(disable_direct_composition)
-            .context("Unable to init directx devices.")?;
-        let text_system = Arc::new(
-            DirectWriteTextSystem::new(&directx_devices)
-                .context("Error creating DirectWriteTextSystem")?,
-        );
+
         let drop_target_helper: IDropTargetHelper = unsafe {
             CoCreateInstance(&CLSID_DragDropHelper, None, CLSCTX_INPROC_SERVER)
                 .context("Error creating drop target helper.")?
@@ -181,6 +186,7 @@ impl WindowsPlatform {
             main_receiver: self.inner.main_receiver.clone(),
             platform_window_handle: self.handle,
             disable_direct_composition: self.disable_direct_composition,
+            directx_devices: (*self.inner.state.borrow().directx_devices).clone(),
         }
     }
 
@@ -228,11 +234,24 @@ impl WindowsPlatform {
     }
 
     fn begin_vsync_thread(&self) {
+        let mut directx_device = (*self.inner.state.borrow().directx_devices).clone();
+        let platform_window: SafeHwnd = self.handle.into();
+        let validation_number = self.inner.validation_number;
         let all_windows = Arc::downgrade(&self.raw_window_handles);
+        let text_system = Arc::downgrade(&self.text_system);
         std::thread::spawn(move || {
             let vsync_provider = VSyncProvider::new();
             loop {
                 vsync_provider.wait_for_vsync();
+                if check_device_lost(&directx_device.device) {
+                    handle_gpu_device_lost(
+                        &mut directx_device,
+                        platform_window.as_raw(),
+                        validation_number,
+                        &all_windows,
+                        &text_system,
+                    );
+                }
                 let Some(all_windows) = all_windows.upgrade() else {
                     break;
                 };
@@ -647,7 +666,9 @@ impl Platform for WindowsPlatform {
 
 impl WindowsPlatformInner {
     fn new(context: &mut PlatformWindowCreateContext) -> Result<Rc<Self>> {
-        let state = RefCell::new(WindowsPlatformState::new());
+        let state = RefCell::new(WindowsPlatformState::new(
+            context.directx_devices.take().unwrap(),
+        ));
         Ok(Rc::new(Self {
             state,
             raw_window_handles: context.raw_window_handles.clone(),
@@ -667,7 +688,8 @@ impl WindowsPlatformInner {
             WM_GPUI_CLOSE_ONE_WINDOW
             | WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD
             | WM_GPUI_DOCK_MENU_ACTION
-            | WM_GPUI_KEYBOARD_LAYOUT_CHANGED => self.handle_gpui_events(msg, wparam, lparam),
+            | WM_GPUI_KEYBOARD_LAYOUT_CHANGED
+            | WM_GPUI_GPU_DEVICE_LOST => self.handle_gpui_events(msg, wparam, lparam),
             _ => None,
         };
         if let Some(result) = handled {
@@ -692,6 +714,7 @@ impl WindowsPlatformInner {
             WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD => self.run_foreground_task(),
             WM_GPUI_DOCK_MENU_ACTION => self.handle_dock_action_event(lparam.0 as _),
             WM_GPUI_KEYBOARD_LAYOUT_CHANGED => self.handle_keyboard_layout_change(),
+            WM_GPUI_GPU_DEVICE_LOST => self.handle_device_lost(lparam),
             _ => unreachable!(),
         }
     }
@@ -749,6 +772,18 @@ impl WindowsPlatformInner {
         self.state.borrow_mut().callbacks.keyboard_layout_change = Some(callback);
         Some(0)
     }
+
+    fn handle_device_lost(&self, lparam: LPARAM) -> Option<isize> {
+        let mut lock = self.state.borrow_mut();
+        let directx_devices = lparam.0 as *const DirectXDevices;
+        let directx_devices = unsafe { &*directx_devices };
+        unsafe {
+            ManuallyDrop::drop(&mut lock.directx_devices);
+        }
+        lock.directx_devices = ManuallyDrop::new(directx_devices.clone());
+
+        Some(0)
+    }
 }
 
 impl Drop for WindowsPlatform {
@@ -762,6 +797,14 @@ impl Drop for WindowsPlatform {
     }
 }
 
+impl Drop for WindowsPlatformState {
+    fn drop(&mut self) {
+        unsafe {
+            ManuallyDrop::drop(&mut self.directx_devices);
+        }
+    }
+}
+
 pub(crate) struct WindowCreationInfo {
     pub(crate) icon: HICON,
     pub(crate) executor: ForegroundExecutor,
@@ -772,6 +815,7 @@ pub(crate) struct WindowCreationInfo {
     pub(crate) main_receiver: flume::Receiver<Runnable>,
     pub(crate) platform_window_handle: HWND,
     pub(crate) disable_direct_composition: bool,
+    pub(crate) directx_devices: DirectXDevices,
 }
 
 struct PlatformWindowCreateContext {
@@ -779,6 +823,7 @@ struct PlatformWindowCreateContext {
     raw_window_handles: std::sync::Weak<RwLock<SmallVec<[SafeHwnd; 4]>>>,
     validation_number: usize,
     main_receiver: Option<flume::Receiver<Runnable>>,
+    directx_devices: Option<DirectXDevices>,
 }
 
 fn open_target(target: impl AsRef<OsStr>) -> Result<()> {
@@ -951,6 +996,80 @@ fn should_auto_hide_scrollbars() -> Result<bool> {
     Ok(ui_settings.AutoHideScrollBars()?)
 }
 
+fn check_device_lost(device: &ID3D11Device) -> bool {
+    let device_state = unsafe { device.GetDeviceRemovedReason() };
+    match device_state {
+        Ok(_) => false,
+        Err(err) => {
+            log::error!("DirectX device lost detected: {:?}", err);
+            true
+        }
+    }
+}
+
+fn handle_gpu_device_lost(
+    directx_devices: &mut DirectXDevices,
+    platform_window: HWND,
+    validation_number: usize,
+    all_windows: &std::sync::Weak<RwLock<SmallVec<[SafeHwnd; 4]>>>,
+    text_system: &std::sync::Weak<DirectWriteTextSystem>,
+) {
+    // 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(350));
+
+    try_to_recover_from_device_lost(
+        || {
+            DirectXDevices::new()
+                .context("Failed to recreate new DirectX devices after device lost")
+        },
+        |new_devices| *directx_devices = new_devices,
+        || {
+            log::error!("Failed to recover DirectX devices after multiple attempts.");
+            // Do something here?
+            // At this point, the device loss is considered unrecoverable.
+            // std::process::exit(1);
+        },
+    );
+    log::info!("DirectX devices successfully recreated.");
+
+    unsafe {
+        SendMessageW(
+            platform_window,
+            WM_GPUI_GPU_DEVICE_LOST,
+            Some(WPARAM(validation_number)),
+            Some(LPARAM(directx_devices as *const _ as _)),
+        );
+    }
+
+    if let Some(text_system) = text_system.upgrade() {
+        text_system.handle_gpu_lost(&directx_devices);
+    }
+    if let Some(all_windows) = all_windows.upgrade() {
+        for window in all_windows.read().iter() {
+            unsafe {
+                SendMessageW(
+                    window.as_raw(),
+                    WM_GPUI_GPU_DEVICE_LOST,
+                    Some(WPARAM(validation_number)),
+                    Some(LPARAM(directx_devices as *const _ as _)),
+                );
+            }
+        }
+        std::thread::sleep(std::time::Duration::from_millis(200));
+        for window in all_windows.read().iter() {
+            unsafe {
+                SendMessageW(
+                    window.as_raw(),
+                    WM_GPUI_FORCE_UPDATE_WINDOW,
+                    Some(WPARAM(validation_number)),
+                    None,
+                );
+            }
+        }
+    }
+}
+
 const PLATFORM_WINDOW_CLASS_NAME: PCWSTR = w!("Zed::PlatformWindow");
 
 fn register_platform_window_class() {

crates/gpui/src/platform/windows/window.rs πŸ”—

@@ -79,6 +79,7 @@ pub(crate) struct WindowsWindowInner {
 impl WindowsWindowState {
     fn new(
         hwnd: HWND,
+        directx_devices: &DirectXDevices,
         window_params: &CREATESTRUCTW,
         current_cursor: Option<HCURSOR>,
         display: WindowsDisplay,
@@ -104,7 +105,7 @@ impl WindowsWindowState {
         };
         let border_offset = WindowBorderOffset::default();
         let restore_from_minimized = None;
-        let renderer = DirectXRenderer::new(hwnd, disable_direct_composition)
+        let renderer = DirectXRenderer::new(hwnd, directx_devices, disable_direct_composition)
             .context("Creating DirectX renderer")?;
         let callbacks = Callbacks::default();
         let input_handler = None;
@@ -205,9 +206,10 @@ impl WindowsWindowState {
 }
 
 impl WindowsWindowInner {
-    fn new(context: &WindowCreateContext, hwnd: HWND, cs: &CREATESTRUCTW) -> Result<Rc<Self>> {
+    fn new(context: &mut WindowCreateContext, hwnd: HWND, cs: &CREATESTRUCTW) -> Result<Rc<Self>> {
         let state = RefCell::new(WindowsWindowState::new(
             hwnd,
+            &context.directx_devices,
             cs,
             context.current_cursor,
             context.display,
@@ -345,6 +347,7 @@ struct WindowCreateContext {
     platform_window_handle: HWND,
     appearance: WindowAppearance,
     disable_direct_composition: bool,
+    directx_devices: DirectXDevices,
 }
 
 impl WindowsWindow {
@@ -363,6 +366,7 @@ impl WindowsWindow {
             main_receiver,
             platform_window_handle,
             disable_direct_composition,
+            directx_devices,
         } = creation_info;
         register_window_class(icon);
         let hide_title_bar = params
@@ -422,6 +426,7 @@ impl WindowsWindow {
             platform_window_handle,
             appearance,
             disable_direct_composition,
+            directx_devices,
         };
         let creation_result = unsafe {
             CreateWindowExW(