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}