Skip to main content

gpui_wgpu/
platform_renderer.rs

1//! `WgpuRenderer` as a window's renderer.
2//!
3//! A window holds a `Box<dyn gpui_platform::PlatformRenderer>`; this is how the wgpu renderer
4//! answers that contract, for the windows that hold one: the wayland and X11 backends, where it
5//! is also the default, and Windows, where a factory installs it in place of the Direct3D renderer
6//! the backend builds itself.
7//!
8//! macOS is the one platform left out. A window's renderer there must answer `MacSceneRenderer`,
9//! whose layer pointer a wgpu renderer cannot supply yet — the wgpu path does not present through a
10//! `CAMetalLayer` the window can install as its view's backing layer. The module therefore carries
11//! one `cfg` — not wasm, not macOS — instead of an item-by-item one.
12
13use anyhow::Result;
14use gpui_engine::GpuRenderer;
15use gpui_platform::{DevicePixels, GpuSpecs, PlatformRenderer, RendererTarget, Size};
16#[cfg(target_os = "windows")]
17use gpui_platform::{WinSceneRenderer, WindowBackgroundAppearance};
18use raw_window_handle::{
19    DisplayHandle, HandleError, HasDisplayHandle, HasWindowHandle, RawDisplayHandle,
20    RawWindowHandle, WindowHandle,
21};
22
23use crate::WgpuRenderer;
24
25impl PlatformRenderer for WgpuRenderer {
26    fn update_drawable_size(&mut self, size: Size<DevicePixels>) {
27        WgpuRenderer::update_drawable_size(self, size);
28    }
29
30    fn max_texture_size(&self) -> u32 {
31        WgpuRenderer::max_texture_size(self)
32    }
33
34    fn gpu_specs(&self) -> Option<GpuSpecs> {
35        WgpuRenderer::gpu_specs(self)
36    }
37
38    fn set_subpixel_layout(&mut self, is_bgr: bool) {
39        WgpuRenderer::set_subpixel_layout(self, is_bgr);
40    }
41
42    fn update_transparency(&mut self, transparent: bool) {
43        WgpuRenderer::update_transparency(self, transparent);
44    }
45
46    fn destroy(&mut self) {
47        WgpuRenderer::destroy(self);
48    }
49
50    fn device_lost(&self) -> bool {
51        WgpuRenderer::device_lost(self)
52    }
53
54    fn needs_redraw(&mut self) -> bool {
55        WgpuRenderer::needs_redraw(self)
56    }
57
58    fn recover(&mut self, target: RendererTarget<'_>) -> Result<()> {
59        let window = RawWindowHandles::from_target(&target)?;
60        WgpuRenderer::recover(self, &window)
61    }
62}
63
64impl GpuRenderer for WgpuRenderer {
65    /// The shared context slot, which is where a producer on this path reaches the device and the
66    /// queue: it is the same `Rc` every window in the process draws through, so reading them out
67    /// of it is the rendezvous it exists for. `None` for a renderer built offscreen, which holds
68    /// no context because it has no window to coordinate with.
69    type Device = (std::sync::Arc<wgpu::Device>, std::sync::Arc<wgpu::Queue>);
70
71    fn device(&self) -> Option<Self::Device> {
72        let context = WgpuRenderer::gpu_context(self)?;
73        let context = context.borrow();
74        let context = context.as_ref()?;
75        Some((context.device.clone(), context.queue.clone()))
76    }
77}
78
79#[cfg(target_os = "windows")]
80impl WinSceneRenderer for WgpuRenderer {
81    /// The Direct3D 12 surface offers no alpha mode but `Opaque`, so a wgpu-backed window has no
82    /// background appearance to honour; the factory that installs one asks for an opaque surface.
83    fn set_background_appearance(&mut self, _appearance: WindowBackgroundAppearance) {}
84
85    /// Resizing configures the surface's new size, which is what the trait's own
86    /// `update_drawable_size` does; the error it cannot return is only reachable from DXGI, and
87    /// wgpu reports a surface it cannot configure through `device_lost` instead.
88    fn resize(&mut self, size: Size<DevicePixels>) -> Result<()> {
89        WgpuRenderer::update_drawable_size(self, size);
90        Ok(())
91    }
92}
93
94/// A window's raw handles, owned, so a renderer can rebuild its surface after a device loss
95/// without borrowing the window.
96///
97/// `WgpuRenderer::new` is handed the window itself, which the backend keeps alive for as long as
98/// the renderer exists; recovery only has a [`RendererTarget`], and the handles it carries by
99/// value are what the surface is rebuilt from. `WgpuRenderer::recover` still wants a
100/// window-shaped value, so this is it.
101#[derive(Debug, Clone, Copy)]
102struct RawWindowHandles {
103    window: RawWindowHandle,
104    display: RawDisplayHandle,
105}
106
107// Safety: the handles name process-global native objects that outlive the renderer, the same
108// guarantee the backends' own window wrappers make when they hand their handles to wgpu.
109unsafe impl Send for RawWindowHandles {}
110unsafe impl Sync for RawWindowHandles {}
111
112impl RawWindowHandles {
113    fn from_target(target: &RendererTarget<'_>) -> Result<Self> {
114        Ok(Self {
115            window: target
116                .window_handle
117                .ok_or_else(|| anyhow::anyhow!("renderer target has no window handle"))?,
118            display: target
119                .display_handle
120                .ok_or_else(|| anyhow::anyhow!("renderer target has no display handle"))?,
121        })
122    }
123}
124
125impl HasWindowHandle for RawWindowHandles {
126    fn window_handle(&self) -> std::result::Result<WindowHandle<'_>, HandleError> {
127        Ok(unsafe { WindowHandle::borrow_raw(self.window) })
128    }
129}
130
131impl HasDisplayHandle for RawWindowHandles {
132    fn display_handle(&self) -> std::result::Result<DisplayHandle<'_>, HandleError> {
133        Ok(unsafe { DisplayHandle::borrow_raw(self.display) })
134    }
135}