softbuffer/
lib.rs

1#![doc = include_str!("../README.md")]
2#![allow(clippy::needless_doctest_main)]
3#![deny(unsafe_op_in_unsafe_fn)]
4#![warn(missing_docs)]
5#![cfg_attr(docsrs, feature(doc_cfg))]
6
7extern crate core;
8
9mod backend_dispatch;
10use backend_dispatch::*;
11mod backend_interface;
12use backend_interface::*;
13mod backends;
14mod error;
15mod util;
16
17use std::cell::Cell;
18use std::marker::PhantomData;
19use std::num::NonZeroU32;
20use std::ops;
21use std::sync::Arc;
22
23use error::InitError;
24pub use error::SoftBufferError;
25
26use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawDisplayHandle, RawWindowHandle};
27
28#[cfg(target_family = "wasm")]
29pub use backends::web::SurfaceExtWeb;
30
31/// An instance of this struct contains the platform-specific data that must be managed in order to
32/// write to a window on that platform.
33#[derive(Clone, Debug)]
34pub struct Context<D> {
35    /// The inner static dispatch object.
36    context_impl: ContextDispatch<D>,
37
38    /// This is Send+Sync IFF D is Send+Sync.
39    _marker: PhantomData<Arc<D>>,
40}
41
42impl<D: HasDisplayHandle> Context<D> {
43    /// Creates a new instance of this struct, using the provided display.
44    pub fn new(display: D) -> Result<Self, SoftBufferError> {
45        match ContextDispatch::new(display) {
46            Ok(context_impl) => Ok(Self {
47                context_impl,
48                _marker: PhantomData,
49            }),
50            Err(InitError::Unsupported(display)) => {
51                let raw = display.display_handle()?.as_raw();
52                Err(SoftBufferError::UnsupportedDisplayPlatform {
53                    human_readable_display_platform_name: display_handle_type_name(&raw),
54                    display_handle: raw,
55                })
56            }
57            Err(InitError::Failure(f)) => Err(f),
58        }
59    }
60}
61
62/// A rectangular region of the buffer coordinate space.
63#[derive(Clone, Copy, Debug)]
64pub struct Rect {
65    /// x coordinate of top left corner
66    pub x: u32,
67    /// y coordinate of top left corner
68    pub y: u32,
69    /// width
70    pub width: NonZeroU32,
71    /// height
72    pub height: NonZeroU32,
73}
74
75/// A surface for drawing to a window with software buffers.
76#[derive(Debug)]
77pub struct Surface<D, W> {
78    /// This is boxed so that `Surface` is the same size on every platform.
79    surface_impl: Box<SurfaceDispatch<D, W>>,
80    _marker: PhantomData<Cell<()>>,
81}
82
83impl<D: HasDisplayHandle, W: HasWindowHandle> Surface<D, W> {
84    /// Creates a new surface for the context for the provided window.
85    pub fn new(context: &Context<D>, window: W) -> Result<Self, SoftBufferError> {
86        match SurfaceDispatch::new(window, &context.context_impl) {
87            Ok(surface_dispatch) => Ok(Self {
88                surface_impl: Box::new(surface_dispatch),
89                _marker: PhantomData,
90            }),
91            Err(InitError::Unsupported(window)) => {
92                let raw = window.window_handle()?.as_raw();
93                Err(SoftBufferError::UnsupportedWindowPlatform {
94                    human_readable_window_platform_name: window_handle_type_name(&raw),
95                    human_readable_display_platform_name: context.context_impl.variant_name(),
96                    window_handle: raw,
97                })
98            }
99            Err(InitError::Failure(f)) => Err(f),
100        }
101    }
102
103    /// Get a reference to the underlying window handle.
104    pub fn window(&self) -> &W {
105        self.surface_impl.window()
106    }
107
108    /// Set the size of the buffer that will be returned by [`Surface::buffer_mut`].
109    ///
110    /// If the size of the buffer does not match the size of the window, the buffer is drawn
111    /// in the upper-left corner of the window. It is recommended in most production use cases
112    /// to have the buffer fill the entire window. Use your windowing library to find the size
113    /// of the window.
114    pub fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> {
115        self.surface_impl.resize(width, height)
116    }
117
118    /// Copies the window contents into a buffer.
119    ///
120    /// ## Platform Dependent Behavior
121    ///
122    /// - On X11, the window must be visible.
123    /// - On AppKit, UIKit, Redox and Wayland, this function is unimplemented.
124    /// - On Web, this will fail if the content was supplied by
125    ///   a different origin depending on the sites CORS rules.
126    pub fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
127        self.surface_impl.fetch()
128    }
129
130    /// Return a [`Buffer`] that the next frame should be rendered into. The size must
131    /// be set with [`Surface::resize`] first. The initial contents of the buffer may be zeroed, or
132    /// may contain a previous frame. Call [`Buffer::age`] to determine this.
133    ///
134    /// ## Platform Dependent Behavior
135    ///
136    /// - On DRM/KMS, there is no reliable and sound way to wait for the page flip to happen from within
137    ///   `softbuffer`. Therefore it is the responsibility of the user to wait for the page flip before
138    ///   sending another frame.
139    pub fn buffer_mut(&mut self) -> Result<Buffer<'_, D, W>, SoftBufferError> {
140        Ok(Buffer {
141            buffer_impl: self.surface_impl.buffer_mut()?,
142            _marker: PhantomData,
143        })
144    }
145}
146
147impl<D: HasDisplayHandle, W: HasWindowHandle> AsRef<W> for Surface<D, W> {
148    #[inline]
149    fn as_ref(&self) -> &W {
150        self.window()
151    }
152}
153
154impl<D: HasDisplayHandle, W: HasWindowHandle> HasWindowHandle for Surface<D, W> {
155    #[inline]
156    fn window_handle(
157        &self,
158    ) -> Result<raw_window_handle::WindowHandle<'_>, raw_window_handle::HandleError> {
159        self.window().window_handle()
160    }
161}
162
163/// A buffer that can be written to by the CPU and presented to the window.
164///
165/// This derefs to a `[u32]`, which depending on the backend may be a mapping into shared memory
166/// accessible to the display server, so presentation doesn't require any (client-side) copying.
167///
168/// This trusts the display server not to mutate the buffer, which could otherwise be unsound.
169///
170/// # Data representation
171///
172/// The format of the buffer is as follows. There is one `u32` in the buffer for each pixel in
173/// the area to draw. The first entry is the upper-left most pixel. The second is one to the right
174/// etc. (Row-major top to bottom left to right one `u32` per pixel). Within each `u32` the highest
175/// order 8 bits are to be set to 0. The next highest order 8 bits are the red channel, then the
176/// green channel, and then the blue channel in the lowest-order 8 bits. See the examples for
177/// one way to build this format using bitwise operations.
178///
179/// --------
180///
181/// Pixel format (`u32`):
182///
183/// 00000000RRRRRRRRGGGGGGGGBBBBBBBB
184///
185/// 0: Bit is 0
186/// R: Red channel
187/// G: Green channel
188/// B: Blue channel
189///
190/// # Platform dependent behavior
191/// No-copy presentation is currently supported on:
192/// - Wayland
193/// - X, when XShm is available
194/// - Win32
195/// - Orbital, when buffer size matches window size
196///
197/// Currently [`Buffer::present`] must block copying image data on:
198/// - Web
199/// - AppKit
200/// - UIKit
201///
202/// Buffer copies an channel swizzling happen on:
203/// - Android
204#[derive(Debug)]
205pub struct Buffer<'a, D, W> {
206    buffer_impl: BufferDispatch<'a, D, W>,
207    _marker: PhantomData<(Arc<D>, Cell<()>)>,
208}
209
210impl<D: HasDisplayHandle, W: HasWindowHandle> Buffer<'_, D, W> {
211    /// The amount of pixels wide the buffer is.
212    pub fn width(&self) -> NonZeroU32 {
213        let width = self.buffer_impl.width();
214        debug_assert_eq!(
215            width.get() as usize * self.buffer_impl.height().get() as usize,
216            self.len(),
217            "buffer must be sized correctly"
218        );
219        width
220    }
221
222    /// The amount of pixels tall the buffer is.
223    pub fn height(&self) -> NonZeroU32 {
224        let height = self.buffer_impl.height();
225        debug_assert_eq!(
226            height.get() as usize * self.buffer_impl.width().get() as usize,
227            self.len(),
228            "buffer must be sized correctly"
229        );
230        height
231    }
232
233    /// `age` is the number of frames ago this buffer was last presented. So if the value is
234    /// `1`, it is the same as the last frame, and if it is `2`, it is the same as the frame
235    /// before that (for backends using double buffering). If the value is `0`, it is a new
236    /// buffer that has unspecified contents.
237    ///
238    /// This can be used to update only a portion of the buffer.
239    pub fn age(&self) -> u8 {
240        self.buffer_impl.age()
241    }
242
243    /// Presents buffer to the window.
244    ///
245    /// # Platform dependent behavior
246    ///
247    /// ## Wayland
248    ///
249    /// On Wayland, calling this function may send requests to the underlying `wl_surface`. The
250    /// graphics context may issue `wl_surface.attach`, `wl_surface.damage`, `wl_surface.damage_buffer`
251    /// and `wl_surface.commit` requests when presenting the buffer.
252    ///
253    /// If the caller wishes to synchronize other surface/window changes, such requests must be sent to the
254    /// Wayland compositor before calling this function.
255    pub fn present(self) -> Result<(), SoftBufferError> {
256        self.buffer_impl.present()
257    }
258
259    /// Presents buffer to the window, with damage regions.
260    ///
261    /// # Platform dependent behavior
262    ///
263    /// Supported on:
264    /// - Wayland
265    /// - X, when XShm is available
266    /// - Win32
267    /// - Web
268    ///
269    /// Otherwise this is equivalent to [`Self::present`].
270    pub fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError> {
271        self.buffer_impl.present_with_damage(damage)
272    }
273}
274
275impl<D: HasDisplayHandle, W: HasWindowHandle> ops::Deref for Buffer<'_, D, W> {
276    type Target = [u32];
277
278    #[inline]
279    fn deref(&self) -> &[u32] {
280        self.buffer_impl.pixels()
281    }
282}
283
284impl<D: HasDisplayHandle, W: HasWindowHandle> ops::DerefMut for Buffer<'_, D, W> {
285    #[inline]
286    fn deref_mut(&mut self) -> &mut [u32] {
287        self.buffer_impl.pixels_mut()
288    }
289}
290
291/// There is no display handle.
292#[derive(Debug)]
293#[allow(dead_code)]
294pub struct NoDisplayHandle(core::convert::Infallible);
295
296impl HasDisplayHandle for NoDisplayHandle {
297    fn display_handle(
298        &self,
299    ) -> Result<raw_window_handle::DisplayHandle<'_>, raw_window_handle::HandleError> {
300        match self.0 {}
301    }
302}
303
304/// There is no window handle.
305#[derive(Debug)]
306pub struct NoWindowHandle(());
307
308impl HasWindowHandle for NoWindowHandle {
309    fn window_handle(
310        &self,
311    ) -> Result<raw_window_handle::WindowHandle<'_>, raw_window_handle::HandleError> {
312        Err(raw_window_handle::HandleError::NotSupported)
313    }
314}
315
316fn window_handle_type_name(handle: &RawWindowHandle) -> &'static str {
317    match handle {
318        RawWindowHandle::Xlib(_) => "Xlib",
319        RawWindowHandle::Win32(_) => "Win32",
320        RawWindowHandle::WinRt(_) => "WinRt",
321        RawWindowHandle::Web(_) => "Web",
322        RawWindowHandle::Wayland(_) => "Wayland",
323        RawWindowHandle::AndroidNdk(_) => "AndroidNdk",
324        RawWindowHandle::AppKit(_) => "AppKit",
325        RawWindowHandle::Orbital(_) => "Orbital",
326        RawWindowHandle::UiKit(_) => "UiKit",
327        RawWindowHandle::Xcb(_) => "XCB",
328        RawWindowHandle::Drm(_) => "DRM",
329        RawWindowHandle::Gbm(_) => "GBM",
330        RawWindowHandle::Haiku(_) => "Haiku",
331        _ => "Unknown Name", //don't completely fail to compile if there is a new raw window handle type that's added at some point
332    }
333}
334
335fn display_handle_type_name(handle: &RawDisplayHandle) -> &'static str {
336    match handle {
337        RawDisplayHandle::Xlib(_) => "Xlib",
338        RawDisplayHandle::Web(_) => "Web",
339        RawDisplayHandle::Wayland(_) => "Wayland",
340        RawDisplayHandle::AppKit(_) => "AppKit",
341        RawDisplayHandle::Orbital(_) => "Orbital",
342        RawDisplayHandle::UiKit(_) => "UiKit",
343        RawDisplayHandle::Xcb(_) => "XCB",
344        RawDisplayHandle::Drm(_) => "DRM",
345        RawDisplayHandle::Gbm(_) => "GBM",
346        RawDisplayHandle::Haiku(_) => "Haiku",
347        RawDisplayHandle::Windows(_) => "Windows",
348        RawDisplayHandle::Android(_) => "Android",
349        _ => "Unknown Name", //don't completely fail to compile if there is a new raw window handle type that's added at some point
350    }
351}
352
353#[cfg(not(target_family = "wasm"))]
354fn __assert_send() {
355    fn is_send<T: Send>() {}
356    fn is_sync<T: Sync>() {}
357
358    is_send::<Context<()>>();
359    is_sync::<Context<()>>();
360    is_send::<Surface<(), ()>>();
361    is_send::<Buffer<'static, (), ()>>();
362
363    /// ```compile_fail
364    /// use softbuffer::Surface;
365    ///
366    /// fn __is_sync<T: Sync>() {}
367    /// __is_sync::<Surface<(), ()>>();
368    /// ```
369    fn __surface_not_sync() {}
370    /// ```compile_fail
371    /// use softbuffer::Buffer;
372    ///
373    /// fn __is_sync<T: Sync>() {}
374    /// __is_sync::<Buffer<'static, (), ()>>();
375    /// ```
376    fn __buffer_not_sync() {}
377}