gpui: Remove all (unsound) `ManuallyDrop` usages, panic on device loss (#42114) (cherry-pick to stable) (#42603)

zed-zippy[bot] and Lukas Wirth created

Cherry-pick of #42114 to stable

----
Given that when we lose our devices unrecoverably we will panic anyways,
might as well do so eagerly which makes it clearer.

Additionally this PR replaces all uses of `ManuallyDrop` with `Option`,
as otherwise we need to do manual bookkeeping of what is and isn't
initialized when we try to recover devices as we can bail out halfway
while recovering. In other words, the code prior to this was fairly
unsound due to freely using `ManuallyDrop::drop`.
 
Fixes ZED-1SS
 
Release Notes:

- N/A *or* Added/Fixed/Improved ...

Co-authored-by: Lukas Wirth <lukas@zed.dev>

Change summary

crates/gpui/src/platform/windows/direct_write.rs     |  21 
crates/gpui/src/platform/windows/directx_devices.rs  |  31 
crates/gpui/src/platform/windows/directx_renderer.rs | 386 +++++++------
crates/gpui/src/platform/windows/events.rs           |   4 
crates/gpui/src/platform/windows/platform.rs         |  55 -
5 files changed, 239 insertions(+), 258 deletions(-)

Detailed changes

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

@@ -211,8 +211,8 @@ impl DirectWriteTextSystem {
         })))
     }
 
-    pub(crate) fn handle_gpu_lost(&self, directx_devices: &DirectXDevices) {
-        self.0.write().handle_gpu_lost(directx_devices);
+    pub(crate) fn handle_gpu_lost(&self, directx_devices: &DirectXDevices) -> Result<()> {
+        self.0.write().handle_gpu_lost(directx_devices)
     }
 }
 
@@ -1215,18 +1215,11 @@ 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.
-            },
-        );
+    fn handle_gpu_lost(&mut self, directx_devices: &DirectXDevices) -> Result<()> {
+        try_to_recover_from_device_lost(|| {
+            GPUState::new(directx_devices).context("Recreating GPU state for DirectWrite")
+        })
+        .map(|gpu_state| self.components.gpu_state = gpu_state)
     }
 }
 

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

@@ -1,4 +1,5 @@
 use anyhow::{Context, Result};
+use itertools::Itertools;
 use util::ResultExt;
 use windows::Win32::{
     Foundation::HMODULE,
@@ -20,24 +21,18 @@ use windows::Win32::{
 };
 use windows::core::Interface;
 
-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();
-    }
+pub(crate) fn try_to_recover_from_device_lost<T>(mut f: impl FnMut() -> Result<T>) -> Result<T> {
+    (0..5)
+        .map(|i| {
+            if i > 0 {
+                // Add a small delay before retrying
+                std::thread::sleep(std::time::Duration::from_millis(100 + i * 10));
+            }
+            f()
+        })
+        .find_or_last(Result::is_ok)
+        .unwrap()
+        .context("DirectXRenderer failed to recover from lost device after multiple attempts")
 }
 
 #[derive(Clone)]

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

@@ -1,5 +1,5 @@
 use std::{
-    mem::ManuallyDrop,
+    slice,
     sync::{Arc, OnceLock},
 };
 
@@ -39,12 +39,15 @@ pub(crate) struct FontInfo {
 pub(crate) struct DirectXRenderer {
     hwnd: HWND,
     atlas: Arc<DirectXAtlas>,
-    devices: ManuallyDrop<DirectXRendererDevices>,
-    resources: ManuallyDrop<DirectXResources>,
+    devices: Option<DirectXRendererDevices>,
+    resources: Option<DirectXResources>,
     globals: DirectXGlobalElements,
     pipelines: DirectXRenderPipelines,
     direct_composition: Option<DirectComposition>,
     font_info: &'static FontInfo,
+
+    width: u32,
+    height: u32,
 }
 
 /// Direct3D objects
@@ -60,19 +63,17 @@ pub(crate) struct DirectXRendererDevices {
 struct DirectXResources {
     // Direct3D rendering objects
     swap_chain: IDXGISwapChain1,
-    render_target: ManuallyDrop<ID3D11Texture2D>,
-    render_target_view: [Option<ID3D11RenderTargetView>; 1],
+    render_target: Option<ID3D11Texture2D>,
+    render_target_view: Option<ID3D11RenderTargetView>,
 
     // Path intermediate textures (with MSAA)
     path_intermediate_texture: ID3D11Texture2D,
-    path_intermediate_srv: [Option<ID3D11ShaderResourceView>; 1],
+    path_intermediate_srv: Option<ID3D11ShaderResourceView>,
     path_intermediate_msaa_texture: ID3D11Texture2D,
-    path_intermediate_msaa_view: [Option<ID3D11RenderTargetView>; 1],
+    path_intermediate_msaa_view: Option<ID3D11RenderTargetView>,
 
-    // Cached window size and viewport
-    width: u32,
-    height: u32,
-    viewport: [D3D11_VIEWPORT; 1],
+    // Cached viewport
+    viewport: D3D11_VIEWPORT,
 }
 
 struct DirectXRenderPipelines {
@@ -86,8 +87,8 @@ struct DirectXRenderPipelines {
 }
 
 struct DirectXGlobalElements {
-    global_params_buffer: [Option<ID3D11Buffer>; 1],
-    sampler: [Option<ID3D11SamplerState>; 1],
+    global_params_buffer: Option<ID3D11Buffer>,
+    sampler: Option<ID3D11SamplerState>,
 }
 
 struct DirectComposition {
@@ -100,7 +101,7 @@ impl DirectXRendererDevices {
     pub(crate) fn new(
         directx_devices: &DirectXDevices,
         disable_direct_composition: bool,
-    ) -> Result<ManuallyDrop<Self>> {
+    ) -> Result<Self> {
         let DirectXDevices {
             adapter,
             dxgi_factory,
@@ -113,13 +114,13 @@ impl DirectXRendererDevices {
             Some(device.cast().context("Creating DXGI device")?)
         };
 
-        Ok(ManuallyDrop::new(Self {
+        Ok(Self {
             adapter: adapter.clone(),
             dxgi_factory: dxgi_factory.clone(),
             device: device.clone(),
             device_context: device_context.clone(),
             dxgi_device,
-        }))
+        })
     }
 }
 
@@ -158,12 +159,14 @@ impl DirectXRenderer {
         Ok(DirectXRenderer {
             hwnd,
             atlas,
-            devices,
-            resources,
+            devices: Some(devices),
+            resources: Some(resources),
             globals,
             pipelines,
             direct_composition,
             font_info: Self::get_font_info(),
+            width: 1,
+            height: 1,
         })
     }
 
@@ -172,55 +175,49 @@ impl DirectXRenderer {
     }
 
     fn pre_draw(&self) -> Result<()> {
+        let resources = self.resources.as_ref().expect("resources missing");
+        let device_context = &self
+            .devices
+            .as_ref()
+            .expect("devices missing")
+            .device_context;
         update_buffer(
-            &self.devices.device_context,
-            self.globals.global_params_buffer[0].as_ref().unwrap(),
+            device_context,
+            self.globals.global_params_buffer.as_ref().unwrap(),
             &[GlobalParams {
                 gamma_ratios: self.font_info.gamma_ratios,
-                viewport_size: [
-                    self.resources.viewport[0].Width,
-                    self.resources.viewport[0].Height,
-                ],
+                viewport_size: [resources.viewport.Width, resources.viewport.Height],
                 grayscale_enhanced_contrast: self.font_info.grayscale_enhanced_contrast,
                 _pad: 0,
             }],
         )?;
         unsafe {
-            self.devices.device_context.ClearRenderTargetView(
-                self.resources.render_target_view[0].as_ref().unwrap(),
-                &[0.0; 4],
-            );
-            self.devices
-                .device_context
-                .OMSetRenderTargets(Some(&self.resources.render_target_view), None);
-            self.devices
-                .device_context
-                .RSSetViewports(Some(&self.resources.viewport));
+            device_context
+                .ClearRenderTargetView(resources.render_target_view.as_ref().unwrap(), &[0.0; 4]);
+            device_context
+                .OMSetRenderTargets(Some(slice::from_ref(&resources.render_target_view)), None);
+            device_context.RSSetViewports(Some(slice::from_ref(&resources.viewport)));
         }
         Ok(())
     }
 
     #[inline]
     fn present(&mut self) -> Result<()> {
-        let result = unsafe { self.resources.swap_chain.Present(0, DXGI_PRESENT(0)) };
+        let result = unsafe {
+            self.resources
+                .as_ref()
+                .expect("resources missing")
+                .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!(
-                    "DirectXRenderer failed to recover from device lost after multiple attempts"
-                );
-                // Do something here?
-                // At this point, the device loss is considered unrecoverable.
-            },
-        );
+    pub(crate) fn handle_device_lost(&mut self, directx_devices: &DirectXDevices) -> Result<()> {
+        try_to_recover_from_device_lost(|| {
+            self.handle_device_lost_impl(directx_devices)
+                .context("DirectXRenderer handling device lost")
+        })
     }
 
     fn handle_device_lost_impl(&mut self, directx_devices: &DirectXDevices) -> Result<()> {
@@ -228,35 +225,41 @@ impl DirectXRenderer {
 
         unsafe {
             #[cfg(debug_assertions)]
-            report_live_objects(&self.devices.device)
-                .context("Failed to report live objects after device lost")
-                .log_err();
-
-            ManuallyDrop::drop(&mut self.resources);
-            self.devices.device_context.OMSetRenderTargets(None, None);
-            self.devices.device_context.ClearState();
-            self.devices.device_context.Flush();
+            if let Some(devices) = &self.devices {
+                report_live_objects(&devices.device)
+                    .context("Failed to report live objects after device lost")
+                    .log_err();
+            }
 
-            #[cfg(debug_assertions)]
-            report_live_objects(&self.devices.device)
-                .context("Failed to report live objects after device lost")
-                .log_err();
+            self.resources.take();
+            if let Some(devices) = &self.devices {
+                devices.device_context.OMSetRenderTargets(None, None);
+                devices.device_context.ClearState();
+                devices.device_context.Flush();
+                #[cfg(debug_assertions)]
+                report_live_objects(&devices.device)
+                    .context("Failed to report live objects after device lost")
+                    .log_err();
+            }
 
-            drop(self.direct_composition.take());
-            ManuallyDrop::drop(&mut self.devices);
+            self.direct_composition.take();
+            self.devices.take();
         }
 
         let devices = DirectXRendererDevices::new(directx_devices, disable_direct_composition)
             .context("Recreating DirectX devices")?;
         let resources = DirectXResources::new(
             &devices,
-            self.resources.width,
-            self.resources.height,
+            self.width,
+            self.height,
             self.hwnd,
             disable_direct_composition,
-        )?;
-        let globals = DirectXGlobalElements::new(&devices.device)?;
-        let pipelines = DirectXRenderPipelines::new(&devices.device)?;
+        )
+        .context("Creating DirectX resources")?;
+        let globals = DirectXGlobalElements::new(&devices.device)
+            .context("Creating DirectXGlobalElements")?;
+        let pipelines = DirectXRenderPipelines::new(&devices.device)
+            .context("Creating DirectXRenderPipelines")?;
 
         let direct_composition = if disable_direct_composition {
             None
@@ -269,17 +272,17 @@ impl DirectXRenderer {
 
         self.atlas
             .handle_device_lost(&devices.device, &devices.device_context);
-        self.devices = devices;
-        self.resources = resources;
-        self.globals = globals;
-        self.pipelines = pipelines;
-        self.direct_composition = direct_composition;
 
         unsafe {
-            self.devices
+            devices
                 .device_context
-                .OMSetRenderTargets(Some(&self.resources.render_target_view), None);
+                .OMSetRenderTargets(Some(slice::from_ref(&resources.render_target_view)), None);
         }
+        self.devices = Some(devices);
+        self.resources = Some(resources);
+        self.globals = globals;
+        self.pipelines = pipelines;
+        self.direct_composition = direct_composition;
         Ok(())
     }
 
@@ -318,23 +321,25 @@ impl DirectXRenderer {
     pub(crate) fn resize(&mut self, new_size: Size<DevicePixels>) -> Result<()> {
         let width = new_size.width.0.max(1) as u32;
         let height = new_size.height.0.max(1) as u32;
-        if self.resources.width == width && self.resources.height == height {
+        if self.width == width && self.height == height {
             return Ok(());
         }
-        self.resources.width = width;
-        self.resources.height = height;
+        self.width = width;
+        self.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());
+        let devices = self.devices.as_ref().context("devices missing")?;
+        unsafe { devices.device_context.OMSetRenderTargets(None, None) };
+        let resources = self.resources.as_mut().context("resources missing")?;
+        resources.render_target.take();
+        resources.render_target_view.take();
 
         // 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 {
-            self.resources
+            resources
                 .swap_chain
                 .ResizeBuffers(
                     BUFFER_COUNT as u32,
@@ -346,12 +351,11 @@ impl DirectXRenderer {
                 .context("Failed to resize swap chain")?;
         }
 
-        self.resources
-            .recreate_resources(&self.devices, width, height)?;
+        resources.recreate_resources(devices, width, height)?;
         unsafe {
-            self.devices
+            devices
                 .device_context
-                .OMSetRenderTargets(Some(&self.resources.render_target_view), None);
+                .OMSetRenderTargets(Some(slice::from_ref(&resources.render_target_view)), None);
         }
 
         Ok(())
@@ -361,15 +365,22 @@ impl DirectXRenderer {
         if shadows.is_empty() {
             return Ok(());
         }
+        let devices = self.devices.as_ref().context("devices missing")?;
         self.pipelines.shadow_pipeline.update_buffer(
-            &self.devices.device,
-            &self.devices.device_context,
+            &devices.device,
+            &devices.device_context,
             shadows,
         )?;
         self.pipelines.shadow_pipeline.draw(
-            &self.devices.device_context,
-            &self.resources.viewport,
-            &self.globals.global_params_buffer,
+            &devices.device_context,
+            slice::from_ref(
+                &self
+                    .resources
+                    .as_ref()
+                    .context("resources missing")?
+                    .viewport,
+            ),
+            slice::from_ref(&self.globals.global_params_buffer),
             D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
             4,
             shadows.len() as u32,
@@ -380,15 +391,22 @@ impl DirectXRenderer {
         if quads.is_empty() {
             return Ok(());
         }
+        let devices = self.devices.as_ref().context("devices missing")?;
         self.pipelines.quad_pipeline.update_buffer(
-            &self.devices.device,
-            &self.devices.device_context,
+            &devices.device,
+            &devices.device_context,
             quads,
         )?;
         self.pipelines.quad_pipeline.draw(
-            &self.devices.device_context,
-            &self.resources.viewport,
-            &self.globals.global_params_buffer,
+            &devices.device_context,
+            slice::from_ref(
+                &self
+                    .resources
+                    .as_ref()
+                    .context("resources missing")?
+                    .viewport,
+            ),
+            slice::from_ref(&self.globals.global_params_buffer),
             D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
             4,
             quads.len() as u32,
@@ -400,18 +418,19 @@ impl DirectXRenderer {
             return Ok(());
         }
 
+        let devices = self.devices.as_ref().context("devices missing")?;
+        let resources = self.resources.as_ref().context("resources missing")?;
         // Clear intermediate MSAA texture
         unsafe {
-            self.devices.device_context.ClearRenderTargetView(
-                self.resources.path_intermediate_msaa_view[0]
-                    .as_ref()
-                    .unwrap(),
+            devices.device_context.ClearRenderTargetView(
+                resources.path_intermediate_msaa_view.as_ref().unwrap(),
                 &[0.0; 4],
             );
             // Set intermediate MSAA texture as render target
-            self.devices
-                .device_context
-                .OMSetRenderTargets(Some(&self.resources.path_intermediate_msaa_view), None);
+            devices.device_context.OMSetRenderTargets(
+                Some(slice::from_ref(&resources.path_intermediate_msaa_view)),
+                None,
+            );
         }
 
         // Collect all vertices and sprites for a single draw call
@@ -427,14 +446,15 @@ impl DirectXRenderer {
         }
 
         self.pipelines.path_rasterization_pipeline.update_buffer(
-            &self.devices.device,
-            &self.devices.device_context,
+            &devices.device,
+            &devices.device_context,
             &vertices,
         )?;
+
         self.pipelines.path_rasterization_pipeline.draw(
-            &self.devices.device_context,
-            &self.resources.viewport,
-            &self.globals.global_params_buffer,
+            &devices.device_context,
+            slice::from_ref(&resources.viewport),
+            slice::from_ref(&self.globals.global_params_buffer),
             D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
             vertices.len() as u32,
             1,
@@ -442,17 +462,17 @@ impl DirectXRenderer {
 
         // Resolve MSAA to non-MSAA intermediate texture
         unsafe {
-            self.devices.device_context.ResolveSubresource(
-                &self.resources.path_intermediate_texture,
+            devices.device_context.ResolveSubresource(
+                &resources.path_intermediate_texture,
                 0,
-                &self.resources.path_intermediate_msaa_texture,
+                &resources.path_intermediate_msaa_texture,
                 0,
                 RENDER_TARGET_FORMAT,
             );
             // Restore main render target
-            self.devices
+            devices
                 .device_context
-                .OMSetRenderTargets(Some(&self.resources.render_target_view), None);
+                .OMSetRenderTargets(Some(slice::from_ref(&resources.render_target_view)), None);
         }
 
         Ok(())
@@ -485,19 +505,21 @@ impl DirectXRenderer {
             vec![PathSprite { bounds }]
         };
 
+        let devices = self.devices.as_ref().context("devices missing")?;
+        let resources = self.resources.as_ref().context("resources missing")?;
         self.pipelines.path_sprite_pipeline.update_buffer(
-            &self.devices.device,
-            &self.devices.device_context,
+            &devices.device,
+            &devices.device_context,
             &sprites,
         )?;
 
         // Draw the sprites with the path texture
         self.pipelines.path_sprite_pipeline.draw_with_texture(
-            &self.devices.device_context,
-            &self.resources.path_intermediate_srv,
-            &self.resources.viewport,
-            &self.globals.global_params_buffer,
-            &self.globals.sampler,
+            &devices.device_context,
+            slice::from_ref(&resources.path_intermediate_srv),
+            slice::from_ref(&resources.viewport),
+            slice::from_ref(&self.globals.global_params_buffer),
+            slice::from_ref(&self.globals.sampler),
             sprites.len() as u32,
         )
     }
@@ -506,15 +528,17 @@ impl DirectXRenderer {
         if underlines.is_empty() {
             return Ok(());
         }
+        let devices = self.devices.as_ref().context("devices missing")?;
+        let resources = self.resources.as_ref().context("resources missing")?;
         self.pipelines.underline_pipeline.update_buffer(
-            &self.devices.device,
-            &self.devices.device_context,
+            &devices.device,
+            &devices.device_context,
             underlines,
         )?;
         self.pipelines.underline_pipeline.draw(
-            &self.devices.device_context,
-            &self.resources.viewport,
-            &self.globals.global_params_buffer,
+            &devices.device_context,
+            slice::from_ref(&resources.viewport),
+            slice::from_ref(&self.globals.global_params_buffer),
             D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
             4,
             underlines.len() as u32,
@@ -529,18 +553,20 @@ impl DirectXRenderer {
         if sprites.is_empty() {
             return Ok(());
         }
+        let devices = self.devices.as_ref().context("devices missing")?;
+        let resources = self.resources.as_ref().context("resources missing")?;
         self.pipelines.mono_sprites.update_buffer(
-            &self.devices.device,
-            &self.devices.device_context,
+            &devices.device,
+            &devices.device_context,
             sprites,
         )?;
         let texture_view = self.atlas.get_texture_view(texture_id);
         self.pipelines.mono_sprites.draw_with_texture(
-            &self.devices.device_context,
+            &devices.device_context,
             &texture_view,
-            &self.resources.viewport,
-            &self.globals.global_params_buffer,
-            &self.globals.sampler,
+            slice::from_ref(&resources.viewport),
+            slice::from_ref(&self.globals.global_params_buffer),
+            slice::from_ref(&self.globals.sampler),
             sprites.len() as u32,
         )
     }
@@ -553,18 +579,21 @@ impl DirectXRenderer {
         if sprites.is_empty() {
             return Ok(());
         }
+
+        let devices = self.devices.as_ref().context("devices missing")?;
+        let resources = self.resources.as_ref().context("resources missing")?;
         self.pipelines.poly_sprites.update_buffer(
-            &self.devices.device,
-            &self.devices.device_context,
+            &devices.device,
+            &devices.device_context,
             sprites,
         )?;
         let texture_view = self.atlas.get_texture_view(texture_id);
         self.pipelines.poly_sprites.draw_with_texture(
-            &self.devices.device_context,
+            &devices.device_context,
             &texture_view,
-            &self.resources.viewport,
-            &self.globals.global_params_buffer,
-            &self.globals.sampler,
+            slice::from_ref(&resources.viewport),
+            slice::from_ref(&self.globals.global_params_buffer),
+            slice::from_ref(&self.globals.sampler),
             sprites.len() as u32,
         )
     }
@@ -577,7 +606,8 @@ impl DirectXRenderer {
     }
 
     pub(crate) fn gpu_specs(&self) -> Result<GpuSpecs> {
-        let desc = unsafe { self.devices.adapter.GetDesc1() }?;
+        let devices = self.devices.as_ref().context("devices missing")?;
+        let desc = unsafe { devices.adapter.GetDesc1() }?;
         let is_software_emulated = (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE.0 as u32) != 0;
         let device_name = String::from_utf16_lossy(&desc.Description)
             .trim_matches(char::from(0))
@@ -592,7 +622,7 @@ impl DirectXRenderer {
             0x10DE => nvidia::get_driver_version(),
             0x1002 => amd::get_driver_version(),
             // For Intel and other vendors, we use the DXGI API to get the driver version.
-            _ => dxgi::get_driver_version(&self.devices.adapter),
+            _ => dxgi::get_driver_version(&devices.adapter),
         }
         .context("Failed to get gpu driver info")
         .log_err()
@@ -626,7 +656,7 @@ impl DirectXResources {
         height: u32,
         hwnd: HWND,
         disable_direct_composition: bool,
-    ) -> Result<ManuallyDrop<Self>> {
+    ) -> Result<Self> {
         let swap_chain = if disable_direct_composition {
             create_swap_chain(&devices.dxgi_factory, &devices.device, hwnd, width, height)?
         } else {
@@ -649,18 +679,16 @@ impl DirectXResources {
         ) = create_resources(devices, &swap_chain, width, height)?;
         set_rasterizer_state(&devices.device, &devices.device_context)?;
 
-        Ok(ManuallyDrop::new(Self {
+        Ok(Self {
             swap_chain,
-            render_target,
+            render_target: Some(render_target),
             render_target_view,
             path_intermediate_texture,
             path_intermediate_msaa_texture,
             path_intermediate_msaa_view,
             path_intermediate_srv,
             viewport,
-            width,
-            height,
-        }))
+        })
     }
 
     #[inline]
@@ -679,7 +707,7 @@ impl DirectXResources {
             path_intermediate_msaa_view,
             viewport,
         ) = create_resources(devices, &self.swap_chain, width, height)?;
-        self.render_target = render_target;
+        self.render_target = Some(render_target);
         self.render_target_view = render_target_view;
         self.path_intermediate_texture = path_intermediate_texture;
         self.path_intermediate_msaa_texture = path_intermediate_msaa_texture;
@@ -789,7 +817,7 @@ impl DirectXGlobalElements {
             };
             let mut buffer = None;
             device.CreateBuffer(&desc, None, Some(&mut buffer))?;
-            [buffer]
+            buffer
         };
 
         let sampler = unsafe {
@@ -807,7 +835,7 @@ impl DirectXGlobalElements {
             };
             let mut output = None;
             device.CreateSamplerState(&desc, Some(&mut output))?;
-            [output]
+            output
         };
 
         Ok(Self {
@@ -832,7 +860,7 @@ struct PipelineState<T> {
     fragment: ID3D11PixelShader,
     buffer: ID3D11Buffer,
     buffer_size: usize,
-    view: [Option<ID3D11ShaderResourceView>; 1],
+    view: Option<ID3D11ShaderResourceView>,
     blend_state: ID3D11BlendState,
     _marker: std::marker::PhantomData<T>,
 }
@@ -902,7 +930,7 @@ impl<T> PipelineState<T> {
     ) -> Result<()> {
         set_pipeline_state(
             device_context,
-            &self.view,
+            slice::from_ref(&self.view),
             topology,
             viewport,
             &self.vertex,
@@ -927,7 +955,7 @@ impl<T> PipelineState<T> {
     ) -> Result<()> {
         set_pipeline_state(
             device_context,
-            &self.view,
+            slice::from_ref(&self.view),
             D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
             viewport,
             &self.vertex,
@@ -964,18 +992,8 @@ struct PathSprite {
 impl Drop for DirectXRenderer {
     fn drop(&mut self) {
         #[cfg(debug_assertions)]
-        report_live_objects(&self.devices.device).ok();
-        unsafe {
-            ManuallyDrop::drop(&mut self.devices);
-            ManuallyDrop::drop(&mut self.resources);
-        }
-    }
-}
-
-impl Drop for DirectXResources {
-    fn drop(&mut self) {
-        unsafe {
-            ManuallyDrop::drop(&mut self.render_target);
+        if let Some(devices) = &self.devices {
+            report_live_objects(&devices.device).ok();
         }
     }
 }
@@ -1049,13 +1067,13 @@ fn create_resources(
     width: u32,
     height: u32,
 ) -> Result<(
-    ManuallyDrop<ID3D11Texture2D>,
-    [Option<ID3D11RenderTargetView>; 1],
     ID3D11Texture2D,
-    [Option<ID3D11ShaderResourceView>; 1],
+    Option<ID3D11RenderTargetView>,
+    ID3D11Texture2D,
+    Option<ID3D11ShaderResourceView>,
     ID3D11Texture2D,
-    [Option<ID3D11RenderTargetView>; 1],
-    [D3D11_VIEWPORT; 1],
+    Option<ID3D11RenderTargetView>,
+    D3D11_VIEWPORT,
 )> {
     let (render_target, render_target_view) =
         create_render_target_and_its_view(swap_chain, &devices.device)?;
@@ -1079,17 +1097,11 @@ fn create_resources(
 fn create_render_target_and_its_view(
     swap_chain: &IDXGISwapChain1,
     device: &ID3D11Device,
-) -> Result<(
-    ManuallyDrop<ID3D11Texture2D>,
-    [Option<ID3D11RenderTargetView>; 1],
-)> {
+) -> Result<(ID3D11Texture2D, Option<ID3D11RenderTargetView>)> {
     let render_target: ID3D11Texture2D = unsafe { swap_chain.GetBuffer(0) }?;
     let mut render_target_view = None;
     unsafe { device.CreateRenderTargetView(&render_target, None, Some(&mut render_target_view))? };
-    Ok((
-        ManuallyDrop::new(render_target),
-        [Some(render_target_view.unwrap())],
-    ))
+    Ok((render_target, render_target_view))
 }
 
 #[inline]
@@ -1097,7 +1109,7 @@ fn create_path_intermediate_texture(
     device: &ID3D11Device,
     width: u32,
     height: u32,
-) -> Result<(ID3D11Texture2D, [Option<ID3D11ShaderResourceView>; 1])> {
+) -> Result<(ID3D11Texture2D, Option<ID3D11ShaderResourceView>)> {
     let texture = unsafe {
         let mut output = None;
         let desc = D3D11_TEXTURE2D_DESC {
@@ -1122,7 +1134,7 @@ fn create_path_intermediate_texture(
     let mut shader_resource_view = None;
     unsafe { device.CreateShaderResourceView(&texture, None, Some(&mut shader_resource_view))? };
 
-    Ok((texture, [Some(shader_resource_view.unwrap())]))
+    Ok((texture, Some(shader_resource_view.unwrap())))
 }
 
 #[inline]
@@ -1130,7 +1142,7 @@ fn create_path_intermediate_msaa_texture_and_view(
     device: &ID3D11Device,
     width: u32,
     height: u32,
-) -> Result<(ID3D11Texture2D, [Option<ID3D11RenderTargetView>; 1])> {
+) -> Result<(ID3D11Texture2D, Option<ID3D11RenderTargetView>)> {
     let msaa_texture = unsafe {
         let mut output = None;
         let desc = D3D11_TEXTURE2D_DESC {
@@ -1153,15 +1165,11 @@ fn create_path_intermediate_msaa_texture_and_view(
     };
     let mut msaa_view = None;
     unsafe { device.CreateRenderTargetView(&msaa_texture, None, Some(&mut msaa_view))? };
-    Ok((msaa_texture, [Some(msaa_view.unwrap())]))
+    Ok((msaa_texture, Some(msaa_view.unwrap())))
 }
 
 #[inline]
-fn set_viewport(
-    device_context: &ID3D11DeviceContext,
-    width: f32,
-    height: f32,
-) -> [D3D11_VIEWPORT; 1] {
+fn set_viewport(device_context: &ID3D11DeviceContext, width: f32, height: f32) -> D3D11_VIEWPORT {
     let viewport = [D3D11_VIEWPORT {
         TopLeftX: 0.0,
         TopLeftY: 0.0,
@@ -1171,7 +1179,7 @@ fn set_viewport(
         MaxDepth: 1.0,
     }];
     unsafe { device_context.RSSetViewports(Some(&viewport)) };
-    viewport
+    viewport[0]
 }
 
 #[inline]
@@ -1299,10 +1307,10 @@ fn create_buffer(
 fn create_buffer_view(
     device: &ID3D11Device,
     buffer: &ID3D11Buffer,
-) -> Result<[Option<ID3D11ShaderResourceView>; 1]> {
+) -> Result<Option<ID3D11ShaderResourceView>> {
     let mut view = None;
     unsafe { device.CreateShaderResourceView(buffer, None, Some(&mut view)) }?;
-    Ok([view])
+    Ok(view)
 }
 
 #[inline]

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

@@ -1133,7 +1133,9 @@ impl WindowsWindowInner {
         let mut lock = self.state.borrow_mut();
         let devices = lparam.0 as *const DirectXDevices;
         let devices = unsafe { &*devices };
-        lock.renderer.handle_device_lost(&devices);
+        if let Err(err) = lock.renderer.handle_device_lost(&devices) {
+            panic!("Device lost: {err}");
+        }
         Some(0)
     }
 

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

@@ -1,7 +1,6 @@
 use std::{
     cell::RefCell,
     ffi::OsStr,
-    mem::ManuallyDrop,
     path::{Path, PathBuf},
     rc::{Rc, Weak},
     sync::{Arc, atomic::Ordering},
@@ -57,7 +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>,
+    directx_devices: Option<DirectXDevices>,
 }
 
 #[derive(Default)]
@@ -76,7 +75,7 @@ impl WindowsPlatformState {
         let callbacks = PlatformCallbacks::default();
         let jump_list = JumpList::new();
         let current_cursor = load_cursor(CursorStyle::Arrow);
-        let directx_devices = ManuallyDrop::new(directx_devices);
+        let directx_devices = Some(directx_devices);
 
         Self {
             callbacks,
@@ -190,7 +189,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(),
+            directx_devices: self.inner.state.borrow().directx_devices.clone().unwrap(),
         }
     }
 
@@ -238,7 +237,7 @@ impl WindowsPlatform {
     }
 
     fn begin_vsync_thread(&self) {
-        let mut directx_device = (*self.inner.state.borrow().directx_devices).clone();
+        let mut directx_device = self.inner.state.borrow().directx_devices.clone().unwrap();
         let platform_window: SafeHwnd = self.handle.into();
         let validation_number = self.inner.validation_number;
         let all_windows = Arc::downgrade(&self.raw_window_handles);
@@ -250,13 +249,15 @@ impl WindowsPlatform {
                 loop {
                     vsync_provider.wait_for_vsync();
                     if check_device_lost(&directx_device.device) {
-                        handle_gpu_device_lost(
+                        if let Err(err) = handle_gpu_device_lost(
                             &mut directx_device,
                             platform_window.as_raw(),
                             validation_number,
                             &all_windows,
                             &text_system,
-                        );
+                        ) {
+                            panic!("Device lost: {err}");
+                        }
                     }
                     let Some(all_windows) = all_windows.upgrade() else {
                         break;
@@ -826,10 +827,8 @@ impl WindowsPlatformInner {
         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());
+        lock.directx_devices.take();
+        lock.directx_devices = Some(directx_devices.clone());
 
         Some(0)
     }
@@ -846,14 +845,6 @@ 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,
@@ -1077,37 +1068,28 @@ fn handle_gpu_device_lost(
     validation_number: usize,
     all_windows: &std::sync::Weak<RwLock<SmallVec<[SafeHwnd; 4]>>>,
     text_system: &std::sync::Weak<DirectWriteTextSystem>,
-) {
+) -> Result<()> {
     // Here we wait a bit to ensure 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);
-        },
-    );
+    *directx_devices = try_to_recover_from_device_lost(|| {
+        DirectXDevices::new().context("Failed to recreate new DirectX devices after device lost")
+    })?;
     log::info!("DirectX devices successfully recreated.");
 
+    let lparam = LPARAM(directx_devices as *const _ as _);
     unsafe {
         SendMessageW(
             platform_window,
             WM_GPUI_GPU_DEVICE_LOST,
             Some(WPARAM(validation_number)),
-            Some(LPARAM(directx_devices as *const _ as _)),
+            Some(lparam),
         );
     }
 
     if let Some(text_system) = text_system.upgrade() {
-        text_system.handle_gpu_lost(&directx_devices);
+        text_system.handle_gpu_lost(&directx_devices)?;
     }
     if let Some(all_windows) = all_windows.upgrade() {
         for window in all_windows.read().iter() {
@@ -1116,7 +1098,7 @@ fn handle_gpu_device_lost(
                     window.as_raw(),
                     WM_GPUI_GPU_DEVICE_LOST,
                     Some(WPARAM(validation_number)),
-                    Some(LPARAM(directx_devices as *const _ as _)),
+                    Some(lparam),
                 );
             }
         }
@@ -1132,6 +1114,7 @@ fn handle_gpu_device_lost(
             }
         }
     }
+    Ok(())
 }
 
 const PLATFORM_WINDOW_CLASS_NAME: PCWSTR = w!("Zed::PlatformWindow");