From 360321babc3c4c252875f01c980b82490c9c76f6 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Mon, 24 Nov 2025 11:41:13 +0100 Subject: [PATCH] Handle zero-sized buffers internally --- CHANGELOG.md | 2 + README.md | 8 +-- benches/buffer_mut.rs | 8 +-- examples/animation.rs | 9 +--- examples/drm.rs | 6 +-- examples/fruit.rs | 8 +-- examples/libxcb.rs | 7 +-- examples/rectangle.rs | 13 ++--- examples/winit.rs | 13 ++--- examples/winit_multithread.rs | 36 ++++++------- examples/winit_wrong_sized_buffer.rs | 9 ++-- examples/zero-sized.rs | 73 ++++++++++++++++++++++++++ src/backend_dispatch.rs | 7 ++- src/backend_interface.rs | 7 ++- src/backends/android.rs | 19 ++++--- src/backends/cg.rs | 32 +++++++----- src/backends/kms.rs | 53 +++++++++---------- src/backends/orbital.rs | 14 +++-- src/backends/wayland/mod.rs | 66 ++++++++--------------- src/backends/web.rs | 78 ++++++++++++---------------- src/backends/win32.rs | 25 +++++---- src/backends/x11.rs | 70 ++++++++++++------------- src/error.rs | 5 +- src/lib.rs | 15 +++--- src/util.rs | 13 +++-- 25 files changed, 289 insertions(+), 307 deletions(-) create mode 100644 examples/zero-sized.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index e806e037..5b7fff75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Unreleased +- Use `u32` instead of `NonZeroU32`, and handle zero-sized buffers internally. + - Update to `objc2` 0.6.0. - Bump MSRV to Rust 1.71. - Make `Context` cloneable. diff --git a/README.md b/README.md index 85faaaee..6646a126 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,6 @@ To run the Android-specific example on an Android phone: `cargo apk r --example ## Example ```rust,no_run -use std::num::NonZeroU32; use std::rc::Rc; use winit::event::{Event, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; @@ -102,12 +101,7 @@ fn main() { return; }; let size = window.inner_size(); - surface - .resize( - NonZeroU32::new(size.width).unwrap(), - NonZeroU32::new(size.height).unwrap(), - ) - .unwrap(); + surface.resize(size.width, size.height).unwrap(); let mut buffer = surface.buffer_mut().unwrap(); for index in 0..(buffer.width().get() * buffer.height().get()) { diff --git a/benches/buffer_mut.rs b/benches/buffer_mut.rs index eacc58ea..1b138365 100644 --- a/benches/buffer_mut.rs +++ b/benches/buffer_mut.rs @@ -13,7 +13,6 @@ fn buffer_mut(c: &mut Criterion) { { use criterion::black_box; use softbuffer::{Context, Surface}; - use std::num::NonZeroU32; use winit::event_loop::ControlFlow; use winit::platform::run_on_demand::EventLoopExtRunOnDemand; @@ -32,12 +31,7 @@ fn buffer_mut(c: &mut Criterion) { let mut surface = Surface::new(&context, &window).unwrap(); let size = window.inner_size(); - surface - .resize( - NonZeroU32::new(size.width).unwrap(), - NonZeroU32::new(size.height).unwrap(), - ) - .unwrap(); + surface.resize(size.width, size.height).unwrap(); c.bench_function("buffer_mut()", |b| { b.iter(|| { diff --git a/examples/animation.rs b/examples/animation.rs index cfc41747..53dd76c5 100644 --- a/examples/animation.rs +++ b/examples/animation.rs @@ -1,7 +1,6 @@ #[cfg(not(target_arch = "wasm32"))] use rayon::prelude::*; use std::f64::consts::PI; -use std::num::NonZeroU32; use web_time::Instant; use winit::event::{KeyEvent, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; @@ -45,11 +44,7 @@ fn main() { return; }; - if let (Some(width), Some(height)) = - (NonZeroU32::new(size.width), NonZeroU32::new(size.height)) - { - surface.resize(width, height).unwrap(); - } + surface.resize(size.width, size.height).unwrap(); } WindowEvent::RedrawRequested => { let Some(surface) = surface else { @@ -61,7 +56,7 @@ fn main() { let mut buffer = surface.buffer_mut().unwrap(); - let size = (buffer.width().get(), buffer.height().get()); + let size = (buffer.width(), buffer.height()); if size != *old_size { *old_size = size; *frames = pre_render_frames(size.0, size.1); diff --git a/examples/drm.rs b/examples/drm.rs index 9d1b36e1..3359677f 100644 --- a/examples/drm.rs +++ b/examples/drm.rs @@ -17,7 +17,6 @@ mod imple { use raw_window_handle::{DisplayHandle, DrmDisplayHandle, DrmWindowHandle, WindowHandle}; use softbuffer::{Context, Surface}; - use std::num::NonZeroU32; use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd}; use std::path::Path; use std::time::{Duration, Instant}; @@ -112,10 +111,7 @@ mod imple { // Resize the surface. let (width, height) = mode.size(); - surface.resize( - NonZeroU32::new(width as u32).unwrap(), - NonZeroU32::new(height as u32).unwrap(), - )?; + surface.resize(width as u32, height as u32)?; // Start drawing to it. let start = Instant::now(); diff --git a/examples/fruit.rs b/examples/fruit.rs index f07fce2d..927d48c8 100644 --- a/examples/fruit.rs +++ b/examples/fruit.rs @@ -1,5 +1,4 @@ use image::GenericImageView; -use std::num::NonZeroU32; use winit::event::{KeyEvent, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; use winit::keyboard::{Key, NamedKey}; @@ -26,12 +25,7 @@ fn main() { // Intentionally only set the size of the surface once, at creation. // This is needed if the window chooses to ignore the size we passed in above, and for the // platforms softbuffer supports that don't yet extract the size from the window. - surface - .resize( - NonZeroU32::new(width).unwrap(), - NonZeroU32::new(height).unwrap(), - ) - .unwrap(); + surface.resize(width, height).unwrap(); surface }, ) diff --git a/examples/libxcb.rs b/examples/libxcb.rs index 03a55933..e7b3b608 100644 --- a/examples/libxcb.rs +++ b/examples/libxcb.rs @@ -115,12 +115,7 @@ mod example { match event { Event::Expose(_) => { // Draw a width x height red rectangle. - surface - .resize( - NonZeroU32::new(width.into()).unwrap(), - NonZeroU32::new(height.into()).unwrap(), - ) - .unwrap(); + surface.resize(width.into(), height.into()).unwrap(); let mut buffer = surface.buffer_mut().unwrap(); buffer.fill(RED); buffer.present().unwrap(); diff --git a/examples/rectangle.rs b/examples/rectangle.rs index b6558666..59c2a561 100644 --- a/examples/rectangle.rs +++ b/examples/rectangle.rs @@ -1,6 +1,5 @@ use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; use softbuffer::Buffer; -use std::num::NonZeroU32; use winit::event::{ElementState, KeyEvent, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; use winit::keyboard::{Key, NamedKey}; @@ -9,8 +8,8 @@ use winit::keyboard::{Key, NamedKey}; mod winit_app; fn redraw(buffer: &mut Buffer<'_, impl HasDisplayHandle, impl HasWindowHandle>, flag: bool) { - let width = buffer.width().get(); - let height = buffer.height().get(); + let width = buffer.width(); + let height = buffer.height(); for y in 0..height { for x in 0..width { let value = if flag && x >= 100 && x < width - 100 && y >= 100 && y < height - 100 { @@ -58,12 +57,8 @@ fn main() { return; }; - if let (Some(width), Some(height)) = - (NonZeroU32::new(size.width), NonZeroU32::new(size.height)) - { - // Resize surface - surface.resize(width, height).unwrap(); - } + // Resize surface + surface.resize(size.width, size.height).unwrap(); } WindowEvent::RedrawRequested => { let Some(surface) = surface else { diff --git a/examples/winit.rs b/examples/winit.rs index 044ae391..bf2e3538 100644 --- a/examples/winit.rs +++ b/examples/winit.rs @@ -1,4 +1,3 @@ -use std::num::NonZeroU32; use winit::event::{KeyEvent, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; use winit::keyboard::{Key, NamedKey}; @@ -32,11 +31,7 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { return; }; - if let (Some(width), Some(height)) = - (NonZeroU32::new(size.width), NonZeroU32::new(size.height)) - { - surface.resize(width, height).unwrap(); - } + surface.resize(size.width, size.height).unwrap(); } WindowEvent::RedrawRequested => { let Some(surface) = surface else { @@ -45,12 +40,12 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { }; let mut buffer = surface.buffer_mut().unwrap(); - for y in 0..buffer.height().get() { - for x in 0..buffer.width().get() { + for y in 0..buffer.height() { + for x in 0..buffer.width() { let red = x % 255; let green = y % 255; let blue = (x * y) % 255; - let index = y * buffer.width().get() + x; + let index = y * buffer.width() + x; buffer[index as usize] = blue | (green << 8) | (red << 16); } } diff --git a/examples/winit_multithread.rs b/examples/winit_multithread.rs index 36ae2b2c..606425b9 100644 --- a/examples/winit_multithread.rs +++ b/examples/winit_multithread.rs @@ -6,7 +6,6 @@ mod winit_app; #[cfg(not(target_family = "wasm"))] pub mod ex { - use std::num::NonZeroU32; use std::sync::{mpsc, Arc, Mutex}; use winit::event::{KeyEvent, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop, OwnedDisplayHandle}; @@ -31,29 +30,24 @@ pub mod ex { // Perform the rendering. let mut surface = surface.lock().unwrap(); - if let (Some(width), Some(height)) = { - let size = window.inner_size(); - println!("got size: {size:?}"); - (NonZeroU32::new(size.width), NonZeroU32::new(size.height)) - } { - println!("resizing..."); - surface.resize(width, height).unwrap(); - - let mut buffer = surface.buffer_mut().unwrap(); - for y in 0..buffer.height().get() { - for x in 0..buffer.width().get() { - let red = x % 255; - let green = y % 255; - let blue = (x * y) % 255; - let index = y * buffer.width().get() + x; - buffer[index as usize] = blue | (green << 8) | (red << 16); - } + let size = window.inner_size(); + println!("resizing..."); + surface.resize(size.width, size.height).unwrap(); + + let mut buffer = surface.buffer_mut().unwrap(); + for y in 0..buffer.height() { + for x in 0..buffer.width() { + let red = x % 255; + let green = y % 255; + let blue = (x * y) % 255; + let index = y * buffer.width() + x; + buffer[index as usize] = blue | (green << 8) | (red << 16); } - - println!("presenting..."); - buffer.present().unwrap(); } + println!("presenting..."); + buffer.present().unwrap(); + // We're done, tell the main thread to keep going. done.send(()).ok(); } diff --git a/examples/winit_wrong_sized_buffer.rs b/examples/winit_wrong_sized_buffer.rs index ec74c652..2c999d70 100644 --- a/examples/winit_wrong_sized_buffer.rs +++ b/examples/winit_wrong_sized_buffer.rs @@ -1,4 +1,3 @@ -use std::num::NonZeroU32; use winit::event::{KeyEvent, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; use winit::keyboard::{Key, NamedKey}; @@ -15,9 +14,7 @@ fn main() { move |_elwt, window| { let mut surface = softbuffer::Surface::new(&context, window.clone()).unwrap(); // Intentionally set the size of the surface to something different than the size of the window. - surface - .resize(NonZeroU32::new(256).unwrap(), NonZeroU32::new(128).unwrap()) - .unwrap(); + surface.resize(256, 128).unwrap(); surface }, ) @@ -36,8 +33,8 @@ fn main() { }; let mut buffer = surface.buffer_mut().unwrap(); - let width = buffer.width().get(); - for y in 0..buffer.height().get() { + let width = buffer.width(); + for y in 0..buffer.height() { for x in 0..width { let red = x % 255; let green = y % 255; diff --git a/examples/zero-sized.rs b/examples/zero-sized.rs new file mode 100644 index 00000000..c4c9d2fd --- /dev/null +++ b/examples/zero-sized.rs @@ -0,0 +1,73 @@ +use winit::event::{KeyEvent, WindowEvent}; +use winit::event_loop::{ControlFlow, EventLoop}; +use winit::keyboard::{Key, NamedKey}; + +#[path = "utils/winit_app.rs"] +mod winit_app; + +fn main() { + let event_loop = EventLoop::new().unwrap(); + let context = softbuffer::Context::new(event_loop.owned_display_handle()).unwrap(); + + let app = winit_app::WinitAppBuilder::with_init( + |elwt| winit_app::make_window(elwt, |w| w), + move |_elwt, window| { + let mut surface = softbuffer::Surface::new(&context, window.clone()).unwrap(); + surface.resize(0, 0).unwrap(); + surface + }, + ) + .with_event_handler(|window, surface, window_id, event, elwt| { + elwt.set_control_flow(ControlFlow::Wait); + + if window_id != window.id() { + return; + } + + match event { + WindowEvent::Resized(size) => { + let Some(surface) = surface else { + eprintln!("RedrawRequested fired before Resumed or after Suspended"); + return; + }; + + let width = size.width.saturating_sub(100); + let height = size.height.saturating_sub(100); + surface.resize(width, height).unwrap(); + } + WindowEvent::RedrawRequested => { + let Some(surface) = surface else { + eprintln!("RedrawRequested fired before Resumed or after Suspended"); + return; + }; + + let mut buffer = surface.buffer_mut().unwrap(); + for y in 0..buffer.height() { + for x in 0..buffer.width() { + let red = x % 255; + let green = y % 255; + let blue = (x * y) % 255; + let index = y * buffer.width() + x; + buffer[index as usize] = blue | (green << 8) | (red << 16); + } + } + + buffer.present().unwrap(); + } + WindowEvent::CloseRequested + | WindowEvent::KeyboardInput { + event: + KeyEvent { + logical_key: Key::Named(NamedKey::Escape), + .. + }, + .. + } => { + elwt.exit(); + } + _ => {} + } + }); + + winit_app::run_app(event_loop, app); +} diff --git a/src/backend_dispatch.rs b/src/backend_dispatch.rs index 21fe4568..3eb67799 100644 --- a/src/backend_dispatch.rs +++ b/src/backend_dispatch.rs @@ -3,7 +3,6 @@ use crate::{backend_interface::*, backends, InitError, Rect, SoftBufferError}; use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; -use std::num::NonZeroU32; /// A macro for creating the enum used to statically dispatch to the platform-specific implementation. macro_rules! make_dispatch { @@ -87,7 +86,7 @@ macro_rules! make_dispatch { } } - fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> { + fn resize(&mut self, width: u32, height: u32) -> Result<(), SoftBufferError> { match self { $( $(#[$attr])* @@ -124,7 +123,7 @@ macro_rules! make_dispatch { impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for BufferDispatch<'a, D, W> { #[inline] - fn width(&self) -> NonZeroU32 { + fn width(&self) -> u32 { match self { $( $(#[$attr])* @@ -134,7 +133,7 @@ macro_rules! make_dispatch { } #[inline] - fn height(&self) -> NonZeroU32 { + fn height(&self) -> u32 { match self { $( $(#[$attr])* diff --git a/src/backend_interface.rs b/src/backend_interface.rs index b6a56661..35416fea 100644 --- a/src/backend_interface.rs +++ b/src/backend_interface.rs @@ -3,7 +3,6 @@ use crate::{InitError, Rect, SoftBufferError}; use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; -use std::num::NonZeroU32; pub(crate) trait ContextInterface { fn new(display: D) -> Result> @@ -25,7 +24,7 @@ pub(crate) trait SurfaceInterface &W; /// Resize the internal buffer to the given width and height. - fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError>; + fn resize(&mut self, width: u32, height: u32) -> Result<(), SoftBufferError>; /// Get a mutable reference to the buffer. fn buffer_mut(&mut self) -> Result, SoftBufferError>; /// Fetch the buffer from the window. @@ -35,8 +34,8 @@ pub(crate) trait SurfaceInterface NonZeroU32; - fn height(&self) -> NonZeroU32; + fn width(&self) -> u32; + fn height(&self) -> u32; fn pixels(&self) -> &[u32]; fn pixels_mut(&mut self) -> &mut [u32]; fn age(&self) -> u8; diff --git a/src/backends/android.rs b/src/backends/android.rs index 3dab9ace..10716154 100644 --- a/src/backends/android.rs +++ b/src/backends/android.rs @@ -1,7 +1,6 @@ //! Implementation of software buffering for Android. use std::marker::PhantomData; -use std::num::{NonZeroI32, NonZeroU32}; use ndk::{ hardware_buffer_format::HardwareBufferFormat, @@ -52,18 +51,18 @@ impl SurfaceInterface for Android } /// Also changes the pixel format to [`HardwareBufferFormat::R8G8B8A8_UNORM`]. - fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> { + fn resize(&mut self, width: u32, height: u32) -> Result<(), SoftBufferError> { let (width, height) = (|| { - let width = NonZeroI32::try_from(width).ok()?; - let height = NonZeroI32::try_from(height).ok()?; + let width = i32::try_from(width).ok()?; + let height = i32::try_from(height).ok()?; Some((width, height)) })() .ok_or(SoftBufferError::SizeOutOfRange { width, height })?; self.native_window .set_buffers_geometry( - width.into(), - height.into(), + width, + height, // Default is typically R5G6B5 16bpp, switch to 32bpp Some(HardwareBufferFormat::R8G8B8X8_UNORM), ) @@ -123,12 +122,12 @@ pub struct BufferImpl<'a, D: ?Sized, W> { unsafe impl<'a, D, W> Send for BufferImpl<'a, D, W> {} impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for BufferImpl<'a, D, W> { - fn width(&self) -> NonZeroU32 { - NonZeroU32::new(self.native_window_buffer.width() as u32).unwrap() + fn width(&self) -> u32 { + self.native_window_buffer.width() as u32 } - fn height(&self) -> NonZeroU32 { - NonZeroU32::new(self.native_window_buffer.height() as u32).unwrap() + fn height(&self) -> u32 { + self.native_window_buffer.height() as u32 } #[inline] diff --git a/src/backends/cg.rs b/src/backends/cg.rs index 6e8999d5..7fb6fa5d 100644 --- a/src/backends/cg.rs +++ b/src/backends/cg.rs @@ -21,7 +21,6 @@ use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawWindowHandle}; use std::ffi::c_void; use std::marker::PhantomData; use std::mem::size_of; -use std::num::NonZeroU32; use std::ops::Deref; use std::ptr::{self, slice_from_raw_parts_mut, NonNull}; @@ -250,9 +249,9 @@ impl SurfaceInterface for CGImpl< &self.window_handle } - fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> { - self.width = width.get() as usize; - self.height = height.get() as usize; + fn resize(&mut self, width: u32, height: u32) -> Result<(), SoftBufferError> { + self.width = width as usize; + self.height = height as usize; Ok(()) } @@ -270,12 +269,12 @@ pub struct BufferImpl<'a, D, W> { } impl BufferInterface for BufferImpl<'_, D, W> { - fn width(&self) -> NonZeroU32 { - NonZeroU32::new(self.imp.width as u32).unwrap() + fn width(&self) -> u32 { + self.imp.width as u32 } - fn height(&self) -> NonZeroU32 { - NonZeroU32::new(self.imp.height as u32).unwrap() + fn height(&self) -> u32 { + self.imp.height as u32 } #[inline] @@ -305,8 +304,15 @@ impl BufferInterface for BufferImpl<'_, } let data_provider = { - let len = self.buffer.len() * size_of::(); - let buffer: *mut [u32] = Box::into_raw(self.buffer); + let buffer = if self.buffer.is_empty() { + // Clear contents. + vec![0; self.imp.width.max(1) * self.imp.height.max(1)].into() + } else { + self.buffer + }; + + let len = buffer.len() * size_of::(); + let buffer: *mut [u32] = Box::into_raw(buffer); // Convert slice pointer to thin pointer. let data_ptr = buffer.cast::(); @@ -330,11 +336,11 @@ impl BufferInterface for BufferImpl<'_, let image = unsafe { CGImage::new( - self.imp.width, - self.imp.height, + self.imp.width.max(1), + self.imp.height.max(1), 8, 32, - self.imp.width * 4, + self.imp.width.max(1) * 4, Some(&self.imp.color_space), bitmap_info, Some(&data_provider), diff --git a/src/backends/kms.rs b/src/backends/kms.rs index 3008e8c2..bbaebb54 100644 --- a/src/backends/kms.rs +++ b/src/backends/kms.rs @@ -102,8 +102,11 @@ pub(crate) struct BufferImpl<'a, D: ?Sized, W: ?Sized> { /// This is used to change the front buffer. first_is_front: &'a mut bool, - /// The current size. - size: (NonZeroU32, NonZeroU32), + /// The current width. + width: u32, + + /// The current height. + height: u32, /// The display implementation. display: &'a KmsDisplayImpl, @@ -211,7 +214,7 @@ impl SurfaceInterface fo &self.window_handle } - fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> { + fn resize(&mut self, width: u32, height: u32) -> Result<(), SoftBufferError> { // Don't resize if we don't have to. if let Some(buffer) = &self.buffer { let (buffer_width, buffer_height) = buffer.size(); @@ -265,7 +268,8 @@ impl SurfaceInterface fo Ok(BufferImpl { mapping, - size, + width: size.0, + height: size.1, first_is_front: &mut set.first_is_front, front_fb, crtc_handle: self.crtc.handle(), @@ -293,12 +297,12 @@ impl Drop for KmsImpl { } impl BufferInterface for BufferImpl<'_, D, W> { - fn width(&self) -> NonZeroU32 { - self.size.0 + fn width(&self) -> u32 { + self.width } - fn height(&self) -> NonZeroU32 { - self.size.1 + fn height(&self) -> u32 { + self.height } #[inline] @@ -326,11 +330,11 @@ impl BufferInterface for BufferImpl<'_, D, W> { rect.x.try_into().map_err(|_| err())?, rect.y.try_into().map_err(|_| err())?, rect.x - .checked_add(rect.width.get()) + .checked_add(rect.width) .and_then(|x| x.try_into().ok()) .ok_or_else(err)?, rect.y - .checked_add(rect.height.get()) + .checked_add(rect.height) .and_then(|y| y.try_into().ok()) .ok_or_else(err)?, )) @@ -374,13 +378,13 @@ impl BufferInterface for BufferImpl<'_, D, W> { #[inline] fn present(self) -> Result<(), SoftBufferError> { - let (width, height) = self.size; - self.present_with_damage(&[crate::Rect { + let rect = crate::Rect { x: 0, y: 0, - width, - height, - }]) + width: self.width, + height: self.height, + }; + self.present_with_damage(&[rect]) } } @@ -388,11 +392,11 @@ impl SharedBuffer { /// Create a new buffer set. pub(crate) fn new( display: &KmsDisplayImpl, - width: NonZeroU32, - height: NonZeroU32, + width: u32, + height: u32, ) -> Result { let db = display - .create_dumb_buffer((width.get(), height.get()), DrmFourcc::Xrgb8888, 32) + .create_dumb_buffer((width, height), DrmFourcc::Xrgb8888, 32) .swbuf_err("failed to create dumb buffer")?; let fb = display .add_framebuffer(&db, 24, 32) @@ -400,20 +404,11 @@ impl SharedBuffer { Ok(SharedBuffer { fb, db, age: 0 }) } - - /// Get the size of this buffer. - pub(crate) fn size(&self) -> (NonZeroU32, NonZeroU32) { - let (width, height) = self.db.size(); - - NonZeroU32::new(width) - .and_then(|width| NonZeroU32::new(height).map(|height| (width, height))) - .expect("buffer size is zero") - } } impl Buffers { /// Get the size of this buffer. - pub(crate) fn size(&self) -> (NonZeroU32, NonZeroU32) { - self.buffers[0].size() + pub(crate) fn size(&self) -> (u32, u32) { + self.buffers[0].db.size() } } diff --git a/src/backends/orbital.rs b/src/backends/orbital.rs index cea6feb0..ef3e626c 100644 --- a/src/backends/orbital.rs +++ b/src/backends/orbital.rs @@ -1,6 +1,6 @@ use crate::error::InitError; use raw_window_handle::{HasDisplayHandle, HasWindowHandle, OrbitalWindowHandle, RawWindowHandle}; -use std::{cmp, marker::PhantomData, num::NonZeroU32, slice, str}; +use std::{cmp, marker::PhantomData, slice, str}; use crate::backend_interface::*; use crate::{Rect, SoftBufferError}; @@ -154,9 +154,7 @@ impl SurfaceInterface for Orbital &self.window_handle } - fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> { - let width = width.get(); - let height = height.get(); + fn resize(&mut self, width: u32, height: u32) -> Result<(), SoftBufferError> { if width != self.width || height != self.height { self.presented = false; self.width = width; @@ -191,12 +189,12 @@ pub struct BufferImpl<'a, D, W> { } impl BufferInterface for BufferImpl<'_, D, W> { - fn width(&self) -> NonZeroU32 { - NonZeroU32::new(self.imp.width as u32).unwrap() + fn width(&self) -> u32 { + self.imp.width as u32 } - fn height(&self) -> NonZeroU32 { - NonZeroU32::new(self.imp.height as u32).unwrap() + fn height(&self) -> u32 { + self.imp.height as u32 } #[inline] diff --git a/src/backends/wayland/mod.rs b/src/backends/wayland/mod.rs index d497484a..a4edee00 100644 --- a/src/backends/wayland/mod.rs +++ b/src/backends/wayland/mod.rs @@ -4,10 +4,7 @@ use crate::{ util, Rect, SoftBufferError, }; use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawDisplayHandle, RawWindowHandle}; -use std::{ - num::{NonZeroI32, NonZeroU32}, - sync::{Arc, Mutex}, -}; +use std::sync::{Arc, Mutex}; use wayland_client::{ backend::{Backend, ObjectId}, globals::{registry_queue_init, GlobalListContents}, @@ -78,7 +75,8 @@ pub struct WaylandImpl { display: Arc>, surface: Option, buffers: Option<(WaylandBuffer, WaylandBuffer)>, - size: Option<(NonZeroI32, NonZeroI32)>, + width: i32, + height: i32, /// The pointer to the window object. /// @@ -123,8 +121,8 @@ impl WaylandImpl { Some(( i32::try_from(rect.x).ok()?, i32::try_from(rect.y).ok()?, - i32::try_from(rect.width.get()).ok()?, - i32::try_from(rect.height.get()).ok()?, + i32::try_from(rect.width).ok()?, + i32::try_from(rect.height).ok()?, )) })() .ok_or(SoftBufferError::DamageOutOfRange { rect: *rect })?; @@ -175,7 +173,8 @@ impl SurfaceInterface display: display.clone(), surface: Some(surface), buffers: Default::default(), - size: None, + width: 0, + height: 0, window_handle: window, }) } @@ -185,23 +184,17 @@ impl SurfaceInterface &self.window_handle } - fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> { - self.size = Some( - (|| { - let width = NonZeroI32::try_from(width).ok()?; - let height = NonZeroI32::try_from(height).ok()?; - Some((width, height)) - })() - .ok_or(SoftBufferError::SizeOutOfRange { width, height })?, - ); + fn resize(&mut self, width: u32, height: u32) -> Result<(), SoftBufferError> { + (self.width, self.height) = (|| { + let width = i32::try_from(width).ok()?; + let height = i32::try_from(height).ok()?; + Some((width, height)) + })() + .ok_or(SoftBufferError::SizeOutOfRange { width, height })?; Ok(()) } fn buffer_mut(&mut self) -> Result, SoftBufferError> { - let (width, height) = self - .size - .expect("Must set size of surface before calling `buffer_mut()`"); - if let Some((_front, back)) = &mut self.buffers { // Block if back buffer not released yet if !back.released() { @@ -221,22 +214,12 @@ impl SurfaceInterface } // Resize, if buffer isn't large enough - back.resize(width.get(), height.get()); + back.resize(self.width, self.height); } else { // Allocate front and back buffer self.buffers = Some(( - WaylandBuffer::new( - &self.display.shm, - width.get(), - height.get(), - &self.display.qh, - ), - WaylandBuffer::new( - &self.display.shm, - width.get(), - height.get(), - &self.display.qh, - ), + WaylandBuffer::new(&self.display.shm, self.width, self.height, &self.display.qh), + WaylandBuffer::new(&self.display.shm, self.width, self.height, &self.display.qh), )); }; @@ -269,12 +252,12 @@ pub struct BufferImpl<'a, D: ?Sized, W> { } impl BufferInterface for BufferImpl<'_, D, W> { - fn width(&self) -> NonZeroU32 { - NonZeroU32::new(self.width as u32).unwrap() + fn width(&self) -> u32 { + self.width as u32 } - fn height(&self) -> NonZeroU32 { - NonZeroU32::new(self.height as usize as u32).unwrap() + fn height(&self) -> u32 { + self.height as usize as u32 } #[inline] @@ -297,15 +280,12 @@ impl BufferInterface for Buffe fn present(self) -> Result<(), SoftBufferError> { let imp = self.stack.into_container(); - let (width, height) = imp - .size - .expect("Must set size of surface before calling `present()`"); imp.present_with_damage(&[Rect { x: 0, y: 0, // We know width/height will be non-negative - width: width.try_into().unwrap(), - height: height.try_into().unwrap(), + width: imp.width.try_into().unwrap(), + height: imp.height.try_into().unwrap(), }]) } } diff --git a/src/backends/web.rs b/src/backends/web.rs index 3b8efa19..6f4559dc 100644 --- a/src/backends/web.rs +++ b/src/backends/web.rs @@ -13,7 +13,6 @@ use crate::backend_interface::*; use crate::error::{InitError, SwResultExt}; use crate::{util, NoDisplayHandle, NoWindowHandle, Rect, SoftBufferError}; use std::marker::PhantomData; -use std::num::NonZeroU32; /// Display implementation for the web platform. /// @@ -53,8 +52,11 @@ pub struct WebImpl { /// Buffer has been presented. buffer_presented: bool, - /// The current canvas width/height. - size: Option<(NonZeroU32, NonZeroU32)>, + /// The current canvas width. + width: u32, + + /// The current canvas height. + height: u32, /// The underlying window handle. window_handle: W, @@ -84,7 +86,8 @@ impl WebImpl { canvas: Canvas::Canvas { canvas, ctx }, buffer: Vec::new(), buffer_presented: false, - size: None, + width: 0, + height: 0, window_handle: window, _display: PhantomData, }) @@ -100,7 +103,8 @@ impl WebImpl { canvas: Canvas::OffscreenCanvas { canvas, ctx }, buffer: Vec::new(), buffer_presented: false, - size: None, + width: 0, + height: 0, window_handle: window, _display: PhantomData, }) @@ -122,10 +126,6 @@ impl WebImpl { } fn present_with_damage(&mut self, damage: &[Rect]) -> Result<(), SoftBufferError> { - let (buffer_width, _buffer_height) = self - .size - .expect("Must set size of surface before calling `present_with_damage()`"); - let union_damage = if let Some(rect) = util::union_damage(damage) { rect } else { @@ -135,13 +135,13 @@ impl WebImpl { // Create a bitmap from the buffer. let bitmap: Vec<_> = self .buffer - .chunks_exact(buffer_width.get() as usize) + .chunks_exact(self.width as usize) .skip(union_damage.y as usize) - .take(union_damage.height.get() as usize) + .take(union_damage.height as usize) .flat_map(|row| { row.iter() .skip(union_damage.x as usize) - .take(union_damage.width.get() as usize) + .take(union_damage.width as usize) }) .copied() .flat_map(|pixel| [(pixel >> 16) as u8, (pixel >> 8) as u8, pixel as u8, 255]) @@ -149,7 +149,7 @@ impl WebImpl { debug_assert_eq!( bitmap.len() as u32, - union_damage.width.get() * union_damage.height.get() * 4 + union_damage.width * union_damage.height * 4 ); #[cfg(target_feature = "atomics")] @@ -179,7 +179,7 @@ impl WebImpl { #[cfg(not(target_feature = "atomics"))] let result = ImageData::new_with_u8_clamped_array( wasm_bindgen::Clamped(&bitmap), - union_damage.width.get(), + union_damage.width, ); // This should only throw an error if the buffer we pass's size is incorrect. let image_data = result.unwrap(); @@ -193,8 +193,8 @@ impl WebImpl { union_damage.y.into(), (rect.x - union_damage.x).into(), (rect.y - union_damage.y).into(), - rect.width.get().into(), - rect.height.get().into(), + rect.width.into(), + rect.height.into(), ) .unwrap(); } @@ -249,13 +249,14 @@ impl SurfaceInterface for WebImpl /// De-duplicates the error handling between `HtmlCanvasElement` and `OffscreenCanvas`. /// Resize the canvas to the given dimensions. - fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> { - if self.size != Some((width, height)) { + fn resize(&mut self, width: u32, height: u32) -> Result<(), SoftBufferError> { + if self.width != width && self.height != height { self.buffer_presented = false; - self.buffer.resize(total_len(width.get(), height.get()), 0); - self.canvas.set_width(width.get()); - self.canvas.set_height(height.get()); - self.size = Some((width, height)); + self.buffer.resize(total_len(width, height), 0); + self.canvas.set_width(width); + self.canvas.set_height(height); + self.width = width; + self.height = height; } Ok(()) @@ -266,13 +267,9 @@ impl SurfaceInterface for WebImpl } fn fetch(&mut self) -> Result, SoftBufferError> { - let (width, height) = self - .size - .expect("Must set size of surface before calling `fetch()`"); - let image_data = self .canvas - .get_image_data(0., 0., width.get().into(), height.get().into()) + .get_image_data(0., 0., self.width.into(), self.height.into()) .ok() // TODO: Can also error if width or height are 0. .swbuf_err("`Canvas` contains pixels from a different origin")?; @@ -378,18 +375,12 @@ pub struct BufferImpl<'a, D, W> { } impl BufferInterface for BufferImpl<'_, D, W> { - fn width(&self) -> NonZeroU32 { - self.imp - .size - .expect("must set size of surface before calling `width()` on the buffer") - .0 + fn width(&self) -> u32 { + self.imp.width } - fn height(&self) -> NonZeroU32 { - self.imp - .size - .expect("must set size of surface before calling `height()` on the buffer") - .1 + fn height(&self) -> u32 { + self.imp.height } fn pixels(&self) -> &[u32] { @@ -410,16 +401,13 @@ impl BufferInterface for BufferImpl<'_, /// Push the buffer to the canvas. fn present(self) -> Result<(), SoftBufferError> { - let (width, height) = self - .imp - .size - .expect("Must set size of surface before calling `present()`"); - self.imp.present_with_damage(&[Rect { + let rect = Rect { x: 0, y: 0, - width, - height, - }]) + width: self.imp.width, + height: self.imp.height, + }; + self.imp.present_with_damage(&[rect]) } fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError> { diff --git a/src/backends/win32.rs b/src/backends/win32.rs index 3436f556..a1a4f843 100644 --- a/src/backends/win32.rs +++ b/src/backends/win32.rs @@ -9,7 +9,6 @@ use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawWindowHandle}; use std::io; use std::marker::PhantomData; use std::mem; -use std::num::{NonZeroI32, NonZeroU32}; use std::ptr::{self, NonNull}; use std::slice; use std::sync::{mpsc, Mutex, OnceLock}; @@ -29,8 +28,8 @@ struct Buffer { dc: Gdi::HDC, bitmap: Gdi::HBITMAP, pixels: NonNull, - width: NonZeroI32, - height: NonZeroI32, + width: i32, + height: i32, presented: bool, } @@ -47,7 +46,7 @@ impl Drop for Buffer { } impl Buffer { - fn new(window_dc: Gdi::HDC, width: NonZeroI32, height: NonZeroI32) -> Self { + fn new(window_dc: Gdi::HDC, width: i32, height: i32) -> Self { let dc = Allocator::get().allocate(window_dc); assert!(!dc.is_null()); @@ -55,8 +54,8 @@ impl Buffer { let bitmap_info = BitmapInfo { bmi_header: Gdi::BITMAPINFOHEADER { biSize: mem::size_of::() as u32, - biWidth: width.get(), - biHeight: -height.get(), + biWidth: width, + biHeight: -height, biPlanes: 1, biBitCount: 32, biCompression: Gdi::BI_BITFIELDS, @@ -179,8 +178,8 @@ impl Win32Impl { Some(( i32::try_from(rect.x).ok()?, i32::try_from(rect.y).ok()?, - i32::try_from(rect.width.get()).ok()?, - i32::try_from(rect.height.get()).ok()?, + i32::try_from(rect.width).ok()?, + i32::try_from(rect.height).ok()?, )) })() .ok_or(SoftBufferError::DamageOutOfRange { rect })?; @@ -248,10 +247,10 @@ impl SurfaceInterface for Win32Im &self.handle } - fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> { + fn resize(&mut self, width: u32, height: u32) -> Result<(), SoftBufferError> { let (width, height) = (|| { - let width = NonZeroI32::try_from(width).ok()?; - let height = NonZeroI32::try_from(height).ok()?; + let width = i32::try_from(width).ok()?; + let height = i32::try_from(height).ok()?; Some((width, height)) })() .ok_or(SoftBufferError::SizeOutOfRange { width, height })?; @@ -284,11 +283,11 @@ impl SurfaceInterface for Win32Im pub struct BufferImpl<'a, D, W>(&'a mut Win32Impl); impl BufferInterface for BufferImpl<'_, D, W> { - fn width(&self) -> NonZeroU32 { + fn width(&self) -> u32 { self.0.buffer.as_ref().unwrap().width.try_into().unwrap() } - fn height(&self) -> NonZeroU32 { + fn height(&self) -> u32 { self.0.buffer.as_ref().unwrap().height.try_into().unwrap() } diff --git a/src/backends/x11.rs b/src/backends/x11.rs index f1429711..2b55f0b2 100644 --- a/src/backends/x11.rs +++ b/src/backends/x11.rs @@ -22,7 +22,7 @@ use std::{ fmt, fs::File, io, mem, - num::{NonZeroU16, NonZeroU32}, + num::NonZeroU32, ptr::{null_mut, NonNull}, slice, sync::Arc, @@ -147,8 +147,11 @@ pub struct X11Impl { /// Buffer has been presented. buffer_presented: bool, - /// The current buffer width/height. - size: Option<(NonZeroU16, NonZeroU16)>, + /// The current buffer width. + width: u16, + + /// The current buffer height. + height: u16, /// Keep the window alive. window_handle: W, @@ -296,7 +299,8 @@ impl SurfaceInterface fo visual_id, buffer, buffer_presented: false, - size: None, + width: 0, + height: 0, window_handle: window_src, }) } @@ -306,7 +310,7 @@ impl SurfaceInterface fo &self.window_handle } - fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> { + fn resize(&mut self, width: u32, height: u32) -> Result<(), SoftBufferError> { tracing::trace!( "resize: window={:X}, size={}x{}", self.window, @@ -315,22 +319,23 @@ impl SurfaceInterface fo ); // Width and height should fit in u16. - let width: NonZeroU16 = width + let width: u16 = width .try_into() .or(Err(SoftBufferError::SizeOutOfRange { width, height }))?; - let height: NonZeroU16 = height.try_into().or(Err(SoftBufferError::SizeOutOfRange { + let height: u16 = height.try_into().or(Err(SoftBufferError::SizeOutOfRange { width: width.into(), height, }))?; - if self.size != Some((width, height)) { + if self.width != width && self.height != height { self.buffer_presented = false; self.buffer - .resize(self.display.connection(), width.get(), height.get()) + .resize(self.display.connection(), width, height) .swbuf_err("Failed to resize X11 buffer")?; // We successfully resized the buffer. - self.size = Some((width, height)); + self.width = width; + self.height = height; } Ok(()) @@ -349,10 +354,6 @@ impl SurfaceInterface fo fn fetch(&mut self) -> Result, SoftBufferError> { tracing::trace!("fetch: window={:X}", self.window); - let (width, height) = self - .size - .expect("Must set size of surface before calling `fetch()`"); - // TODO: Is it worth it to do SHM here? Probably not. let reply = self .display @@ -362,8 +363,8 @@ impl SurfaceInterface fo self.window, 0, 0, - width.get(), - height.get(), + self.width, + self.height, u32::MAX, ) .swbuf_err("Failed to send image fetching request")? @@ -388,12 +389,12 @@ pub struct BufferImpl<'a, D: ?Sized, W: ?Sized>(&'a mut X11Impl); impl BufferInterface for BufferImpl<'_, D, W> { - fn width(&self) -> NonZeroU32 { - self.0.size.unwrap().0.into() + fn width(&self) -> u32 { + self.0.width as u32 } - fn height(&self) -> NonZeroU32 { - self.0.size.unwrap().1.into() + fn height(&self) -> u32 { + self.0.height as u32 } #[inline] @@ -420,10 +421,6 @@ impl BufferInterface fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError> { let imp = self.0; - let (surface_width, surface_height) = imp - .size - .expect("Must set size of surface before calling `present_with_damage()`"); - tracing::trace!("present: window={:X}", imp.window); match imp.buffer { @@ -437,8 +434,8 @@ impl BufferInterface xproto::ImageFormat::Z_PIXMAP, imp.window, imp.gc, - surface_width.get(), - surface_height.get(), + imp.width, + imp.height, 0, 0, 0, @@ -464,8 +461,8 @@ impl BufferInterface u16::try_from(rect.y).ok()?, i16::try_from(rect.x).ok()?, i16::try_from(rect.y).ok()?, - u16::try_from(rect.width.get()).ok()?, - u16::try_from(rect.height.get()).ok()?, + u16::try_from(rect.width).ok()?, + u16::try_from(rect.height).ok()?, )) })( ) @@ -475,8 +472,8 @@ impl BufferInterface .shm_put_image( imp.window, imp.gc, - surface_width.get(), - surface_height.get(), + imp.width, + imp.height, src_x, src_y, width, @@ -508,16 +505,13 @@ impl BufferInterface } fn present(self) -> Result<(), SoftBufferError> { - let (width, height) = self - .0 - .size - .expect("Must set size of surface before calling `present()`"); - self.present_with_damage(&[Rect { + let rect = Rect { x: 0, y: 0, - width: width.into(), - height: height.into(), - }]) + width: self.0.width.into(), + height: self.0.height.into(), + }; + self.present_with_damage(&[rect]) } } diff --git a/src/error.rs b/src/error.rs index b79be6b6..4405fe7f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,7 +1,6 @@ use raw_window_handle::{HandleError, RawDisplayHandle, RawWindowHandle}; use std::error::Error; use std::fmt; -use std::num::NonZeroU32; #[derive(Debug)] #[non_exhaustive] @@ -80,10 +79,10 @@ pub enum SoftBufferError { /// The provided size is outside of the range supported by the backend. SizeOutOfRange { /// The width that was out of range. - width: NonZeroU32, + width: u32, /// The height that was out of range. - height: NonZeroU32, + height: u32, }, /// The provided damage rect is outside of the range supported by the backend. diff --git a/src/lib.rs b/src/lib.rs index 03246c04..5943decd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,6 @@ mod util; use std::cell::Cell; use std::marker::PhantomData; -use std::num::NonZeroU32; use std::ops; use std::sync::Arc; @@ -67,9 +66,9 @@ pub struct Rect { /// y coordinate of top left corner pub y: u32, /// width - pub width: NonZeroU32, + pub width: u32, /// height - pub height: NonZeroU32, + pub height: u32, } /// A surface for drawing to a window with software buffers. @@ -110,7 +109,7 @@ impl Surface { /// in the upper-left corner of the window. It is recommended in most production use cases /// to have the buffer fill the entire window. Use your windowing library to find the size /// of the window. - pub fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> { + pub fn resize(&mut self, width: u32, height: u32) -> Result<(), SoftBufferError> { self.surface_impl.resize(width, height) } @@ -207,10 +206,10 @@ pub struct Buffer<'a, D, W> { impl Buffer<'_, D, W> { /// The amount of pixels wide the buffer is. - pub fn width(&self) -> NonZeroU32 { + pub fn width(&self) -> u32 { let width = self.buffer_impl.width(); debug_assert_eq!( - width.get() as usize * self.buffer_impl.height().get() as usize, + width as usize * self.buffer_impl.height() as usize, self.len(), "buffer must be sized correctly" ); @@ -218,10 +217,10 @@ impl Buffer<'_, D, W> { } /// The amount of pixels tall the buffer is. - pub fn height(&self) -> NonZeroU32 { + pub fn height(&self) -> u32 { let height = self.buffer_impl.height(); debug_assert_eq!( - height.get() as usize * self.buffer_impl.width().get() as usize, + height as usize * self.buffer_impl.width() as usize, self.len(), "buffer must be sized correctly" ); diff --git a/src/util.rs b/src/util.rs index de46e3fd..c79dcff2 100644 --- a/src/util.rs +++ b/src/util.rs @@ -2,7 +2,6 @@ #![allow(dead_code)] use std::cmp; -use std::num::NonZeroU32; use crate::Rect; use crate::SoftBufferError; @@ -64,8 +63,8 @@ pub(crate) fn union_damage(damage: &[Rect]) -> Option { .map(|rect| Region { left: rect.x, top: rect.y, - right: rect.x + rect.width.get(), - bottom: rect.y + rect.height.get(), + right: rect.x + rect.width, + bottom: rect.y + rect.height, }) .reduce(|mut prev, next| { prev.left = cmp::min(prev.left, next.left); @@ -78,9 +77,13 @@ pub(crate) fn union_damage(damage: &[Rect]) -> Option { Some(Rect { x: region.left, y: region.top, - width: NonZeroU32::new(region.right - region.left) + width: region + .right + .checked_sub(region.left) .expect("`right` must always be bigger then `left`"), - height: NonZeroU32::new(region.bottom - region.top) + height: region + .bottom + .checked_sub(region.top) .expect("`bottom` must always be bigger then `top`"), }) }