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