Skip to main content

gpui_engine/
renderer.rs

1//! The renderer contract that engines implement.
2
3use crate::{PlatformAtlas, Scene};
4use gpui_types::{DevicePixels, Size};
5use std::any::Any;
6use std::sync::Arc;
7
8/// Downcasting for [`SceneRenderer`] trait objects.
9///
10/// `SceneRenderer` is the GPU-agnostic seam: it presents a [`Scene`] and names no graphics API.
11/// The renderer *behind* it is what owns the device — and the caches, the textures and the other
12/// resources built on it — so a caller that needs those downcasts to the concrete backend renderer
13/// here, rather than having the window lend its guest's internals.
14impl dyn SceneRenderer + '_ {
15    /// `self` as the concrete renderer `T`, if that is what it is.
16    pub fn as_renderer<T: Any>(&self) -> Option<&T> {
17        (self as &dyn Any).downcast_ref::<T>()
18    }
19
20    /// The mutable form of [`as_renderer`](Self::as_renderer).
21    pub fn as_renderer_mut<T: Any>(&mut self) -> Option<&mut T> {
22        (self as &mut dyn Any).downcast_mut::<T>()
23    }
24}
25
26/// A renderer that owns a device a producer can make resources on.
27///
28/// The engine's [`SceneRenderer`] is GPU-agnostic and names no device, but a producer that must make
29/// a texture on the *window's* GPU has to reach the concrete device the renderer drew with — the
30/// same-device rule seen from the renderer's side. A renderer that owns one implements this; a
31/// renderer that has none to lend — a discard or capture-only one — simply does not, and is then
32/// absent from this door rather than answering it with a placeholder.
33///
34/// [`Device`](Self::Device) is the backend's own handle — an `ID3D11Device`, a `metal::Device`, a
35/// wgpu context — so this trait adds no dependency on any GPU driver: only an implementor names one.
36pub trait GpuRenderer: SceneRenderer {
37    /// The device this renderer draws on, and so the one a producer must make its texture on.
38    type Device;
39
40    /// The device, or `None` while there is none to lend: a renderer mid-recovery has taken its
41    /// device apart and has not yet rebuilt it.
42    fn device(&self) -> Option<Self::Device>;
43}
44
45/// A frame read back from a renderer, as raw pixels.
46///
47/// This is the engine's own type rather than an `image::RgbaImage` because the contract is what
48/// every renderer implements, and an image codec crate is not something a renderer needs: the
49/// consumers that want one — a PNG in test support, a fixture diff, an export — sit above the
50/// engine and convert. `image` was a dependency of this crate for this signature alone.
51///
52/// The layout is fixed so that the boundary needs no negotiation: RGBA8, unpremultiplied,
53/// tightly packed, row-major, top-left origin. A backend whose target is BGRA or premultiplied
54/// converts while it copies, which is where the swizzle already happens; a backend with other
55/// pixel formats widens this type rather than smuggling a second layout through it.
56#[derive(Clone, Debug, PartialEq, Eq)]
57pub struct PixelBuffer {
58    width: u32,
59    height: u32,
60    data: Vec<u8>,
61}
62
63impl PixelBuffer {
64    /// Wraps `data` as an image of `width` by `height` pixels.
65    ///
66    /// Fails if `data` is not exactly `width * height * 4` bytes, so that the accessors below
67    /// cannot hand out a buffer shorter than the size they report.
68    pub fn new(width: u32, height: u32, data: Vec<u8>) -> anyhow::Result<Self> {
69        let expected = width as usize * height as usize * 4;
70        anyhow::ensure!(
71            data.len() == expected,
72            "a {width}x{height} RGBA8 image is {expected} bytes, but the buffer is {}",
73            data.len()
74        );
75        Ok(Self {
76            width,
77            height,
78            data,
79        })
80    }
81
82    /// The width, in pixels.
83    pub fn width(&self) -> u32 {
84        self.width
85    }
86
87    /// The height, in pixels.
88    pub fn height(&self) -> u32 {
89        self.height
90    }
91
92    /// The pixels, RGBA8 and tightly packed.
93    pub fn data(&self) -> &[u8] {
94        &self.data
95    }
96
97    /// Consumes the buffer, returning its pixels.
98    pub fn into_data(self) -> Vec<u8> {
99        self.data
100    }
101}
102
103/// A renderer that presents a [`Scene`] to some target.
104///
105/// OS backends and alternative engines implement this; `gpui` produces the
106/// [`Scene`] and submits it through the window's platform implementation.
107///
108/// Offscreen rendering is part of this contract rather than a testing shim: a renderer that is
109/// never given a window is how server-side rendering, headless capture and snapshotting are
110/// expressed, and the window's renderer is the only thing that owns the device. Onscreen
111/// renderers therefore only *need* [`draw`](Self::draw) and [`sprite_atlas`](Self::sprite_atlas);
112/// the rest have defaults that report themselves unsupported.
113///
114/// Rendering and readback are separate steps on purpose. [`render_scene`](Self::render_scene)
115/// fills a target without a CPU round trip, which is what a consumer on the GPU wants;
116/// [`read_pixels`](Self::read_pixels) pays for system memory only when a consumer on the CPU
117/// needs it; [`render_scene_to_image`](Self::render_scene_to_image) is the two together.
118pub trait SceneRenderer: Any {
119    /// Encodes and submits `scene`, returning whether it was presented.
120    ///
121    /// Renderers that cannot observe presentation (or that render offscreen)
122    /// return `true`; window backends that pace themselves on compositor
123    /// callbacks use `false` to retry the frame later.
124    fn draw(&mut self, scene: &Scene) -> bool;
125
126    /// Returns the sprite atlas used by this renderer.
127    fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas>;
128
129    /// Sets the size of the target that [`draw`](Self::draw) renders into.
130    ///
131    /// Onscreen renderers derive their target from the window and can ignore
132    /// this; headless renderers use it to size an offscreen texture.
133    fn set_viewport_size(&mut self, _size: Size<DevicePixels>) {}
134
135    /// Renders `scene` into an offscreen target of `size`, without reading it back.
136    ///
137    /// This is the headless analogue of presenting a frame: it performs the same CPU-side
138    /// scene encoding and GPU submission as drawing to a real window, but does not block on
139    /// GPU completion or copy pixels back.
140    fn render_scene(&mut self, _scene: &Scene, _size: Size<DevicePixels>) -> anyhow::Result<()> {
141        anyhow::bail!("offscreen rendering is not supported by this renderer")
142    }
143
144    /// Reads the offscreen target of the last [`render_scene`](Self::render_scene) back.
145    ///
146    /// Only a renderer that has rendered offscreen can answer this; the failure is the same
147    /// "unsupported" one, one step later.
148    fn read_pixels(&mut self) -> anyhow::Result<PixelBuffer> {
149        anyhow::bail!("pixel readback is not supported by this renderer")
150    }
151
152    /// The offscreen target of the last [`render_scene`](Self::render_scene), as a handle that may
153    /// cross a thread boundary.
154    ///
155    /// This is the zero-copy half of the offscreen contract: a CPU consumer calls
156    /// [`read_pixels`](Self::read_pixels), while a GPU consumer — a guest runner's host loop —
157    /// receives this and samples the target on the device it was rendered with. The handle is
158    /// `Send + Sync` and opaque; the engine names no graphics API, so a backend downcasts it to its
159    /// own type. `None` when the renderer has no shareable target or has not rendered one yet.
160    fn offscreen_surface_handle(&self) -> Option<Arc<dyn Any + Send + Sync>> {
161        None
162    }
163
164    /// Renders `scene` offscreen and reads the result back.
165    fn render_scene_to_image(
166        &mut self,
167        scene: &Scene,
168        size: Size<DevicePixels>,
169    ) -> anyhow::Result<PixelBuffer> {
170        self.render_scene(scene, size)?;
171        self.read_pixels()
172    }
173}
174
175/// A renderer that hands every scene it is given to a closure before passing it on.
176///
177/// Observation belongs here rather than in a backend. It needs nothing from the platform, so a
178/// backend that learned about inspection would carry it once per API, and the *scene* is already
179/// an engine type: a scene optimiser, a culling pass, an assertion about what primitives a
180/// renderer is handed and in what order, are all statements about this layer.
181///
182/// What a window painted is observable one layer up, where the frame still holds the scene it
183/// built (`Window::painted_quads`), so this is for observing a renderer rather than a frame.
184///
185/// This wraps a renderer in another renderer, and the choices that make it useful are the
186/// forwarding ones: it observes [`draw`](SceneRenderer::draw) and
187/// [`render_scene`](SceneRenderer::render_scene), and it does *not* override
188/// [`render_scene_to_image`](SceneRenderer::render_scene_to_image), so a readback is observed
189/// once through the `render_scene` it composes rather than twice.
190pub struct ObservingRenderer<R, F> {
191    inner: R,
192    on_scene: F,
193}
194
195impl<R, F> ObservingRenderer<R, F> {
196    /// Wraps `inner`, calling `on_scene` with each scene it is asked to draw.
197    pub fn new(inner: R, on_scene: F) -> Self {
198        Self { inner, on_scene }
199    }
200
201    /// The renderer being observed.
202    pub fn inner(&self) -> &R {
203        &self.inner
204    }
205
206    /// The renderer being observed.
207    pub fn inner_mut(&mut self) -> &mut R {
208        &mut self.inner
209    }
210
211    /// Takes back the observed renderer.
212    pub fn into_inner(self) -> R {
213        self.inner
214    }
215}
216
217impl<R, F> SceneRenderer for ObservingRenderer<R, F>
218where
219    R: SceneRenderer,
220    F: FnMut(&Scene) + 'static,
221{
222    fn draw(&mut self, scene: &Scene) -> bool {
223        (self.on_scene)(scene);
224        self.inner.draw(scene)
225    }
226
227    fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
228        self.inner.sprite_atlas()
229    }
230
231    fn set_viewport_size(&mut self, size: Size<DevicePixels>) {
232        self.inner.set_viewport_size(size)
233    }
234
235    fn render_scene(&mut self, scene: &Scene, size: Size<DevicePixels>) -> anyhow::Result<()> {
236        (self.on_scene)(scene);
237        self.inner.render_scene(scene, size)
238    }
239
240    fn read_pixels(&mut self) -> anyhow::Result<PixelBuffer> {
241        self.inner.read_pixels()
242    }
243
244    fn offscreen_surface_handle(&self) -> Option<Arc<dyn Any + Send + Sync>> {
245        self.inner.offscreen_surface_handle()
246    }
247}
248
249#[cfg(test)]
250mod tests {
251    use std::{borrow::Cow, cell::RefCell, rc::Rc};
252
253    use super::*;
254    use crate::{AtlasKey, AtlasTile, CustomRenderPrimitive, ImportedTextureHandle};
255    use gpui_types::{Bounds, ContentMask, Corners};
256
257    /// The observer never uploads, so its atlas only has to exist.
258    struct StubAtlas;
259
260    impl PlatformAtlas for StubAtlas {
261        fn get_or_insert_with<'a>(
262            &self,
263            _key: AtlasKey,
264            _build: &mut dyn FnMut() -> anyhow::Result<Option<(Size<DevicePixels>, Cow<'a, [u8]>)>>,
265        ) -> anyhow::Result<Option<AtlasTile>> {
266            Ok(None)
267        }
268
269        fn remove(&self, _key: &AtlasKey) {}
270    }
271
272    struct RecordingRenderer {
273        draws: Rc<RefCell<usize>>,
274    }
275
276    impl SceneRenderer for RecordingRenderer {
277        fn draw(&mut self, _scene: &Scene) -> bool {
278            *self.draws.borrow_mut() += 1;
279            true
280        }
281
282        fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
283            Arc::new(StubAtlas)
284        }
285    }
286
287    /// What the wrap is for: the closure sees the scene the caller handed over, and the render it
288    /// forwards to still happens. An observer that swallowed the scene would pass the count and
289    /// fail the second assertion, and one that passed the scene on without observing would fail
290    /// the first.
291    #[test]
292    fn an_observing_renderer_observes_the_scene_and_forwards_it() {
293        let draws = Rc::new(RefCell::new(0));
294        let observed = Rc::new(RefCell::new(Vec::new()));
295        let mut renderer = ObservingRenderer::new(
296            RecordingRenderer {
297                draws: Rc::clone(&draws),
298            },
299            {
300                let observed = Rc::clone(&observed);
301                move |scene: &Scene| observed.borrow_mut().push(scene.custom.len())
302            },
303        );
304
305        let mut scene = Scene::default();
306        scene.custom.push(CustomRenderPrimitive::Texture {
307            order: 0,
308            handle: ImportedTextureHandle::new(()),
309            bounds: Bounds::default(),
310            content_mask: ContentMask::default(),
311            radii: Corners::default(),
312            opacity: 1.0,
313            flip_v: false,
314        });
315
316        assert!(renderer.draw(&scene));
317
318        assert_eq!(*observed.borrow(), [1]);
319        assert_eq!(*draws.borrow(), 1);
320    }
321}