From a2c37fd691d42726d8c95acd0e2375c8ec03d39d Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Thu, 5 Jun 2025 16:10:48 +0200 Subject: [PATCH 1/3] Add `Buffer::width` and `Buffer::height` --- CHANGELOG.md | 1 + src/backend_dispatch.rs | 20 ++++++++++++++++++++ src/backend_interface.rs | 2 ++ src/backends/android.rs | 8 ++++++++ src/backends/cg.rs | 8 ++++++++ src/backends/kms.rs | 8 ++++++++ src/backends/orbital.rs | 8 ++++++++ src/backends/wayland/mod.rs | 8 ++++++++ src/backends/web.rs | 16 ++++++++++++++++ src/backends/win32.rs | 8 ++++++++ src/backends/x11.rs | 8 ++++++++ src/lib.rs | 22 ++++++++++++++++++++++ 12 files changed, 117 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8669f2a4..04bc7106 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ - Update to `objc2` 0.6.0. - Bump MSRV to Rust 1.71. +- Added `Buffer::width` and `Buffer::height`. # 0.4.6 diff --git a/src/backend_dispatch.rs b/src/backend_dispatch.rs index a86832ea..bdb42db0 100644 --- a/src/backend_dispatch.rs +++ b/src/backend_dispatch.rs @@ -124,6 +124,26 @@ macro_rules! make_dispatch { } impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for BufferDispatch<'a, D, W> { + #[inline] + fn width(&self) -> usize { + match self { + $( + $(#[$attr])* + Self::$name(inner) => inner.width(), + )* + } + } + + #[inline] + fn height(&self) -> usize { + match self { + $( + $(#[$attr])* + Self::$name(inner) => inner.height(), + )* + } + } + #[inline] fn pixels(&self) -> &[u32] { match self { diff --git a/src/backend_interface.rs b/src/backend_interface.rs index 13e3555c..4939dc96 100644 --- a/src/backend_interface.rs +++ b/src/backend_interface.rs @@ -35,6 +35,8 @@ pub(crate) trait SurfaceInterface usize; + fn height(&self) -> usize; 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 a14aaefd..a3ae7524 100644 --- a/src/backends/android.rs +++ b/src/backends/android.rs @@ -123,6 +123,14 @@ 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) -> usize { + self.native_window_buffer.width() + } + + fn height(&self) -> usize { + self.native_window_buffer.height() + } + #[inline] fn pixels(&self) -> &[u32] { &self.buffer diff --git a/src/backends/cg.rs b/src/backends/cg.rs index ba6c17aa..677634d2 100644 --- a/src/backends/cg.rs +++ b/src/backends/cg.rs @@ -269,6 +269,14 @@ pub struct BufferImpl<'a, D, W> { } impl BufferInterface for BufferImpl<'_, D, W> { + fn width(&self) -> usize { + self.imp.width + } + + fn height(&self) -> usize { + self.imp.height + } + #[inline] fn pixels(&self) -> &[u32] { &self.buffer diff --git a/src/backends/kms.rs b/src/backends/kms.rs index 35ed5b8f..a2d63611 100644 --- a/src/backends/kms.rs +++ b/src/backends/kms.rs @@ -293,6 +293,14 @@ impl Drop for KmsImpl { } impl BufferInterface for BufferImpl<'_, D, W> { + fn width(&self) -> usize { + self.size.0.get() as usize + } + + fn height(&self) -> usize { + self.size.1.get() as usize + } + #[inline] fn pixels(&self) -> &[u32] { bytemuck::cast_slice(self.mapping.as_ref()) diff --git a/src/backends/orbital.rs b/src/backends/orbital.rs index d54683f1..c449e92c 100644 --- a/src/backends/orbital.rs +++ b/src/backends/orbital.rs @@ -191,6 +191,14 @@ pub struct BufferImpl<'a, D, W> { } impl BufferInterface for BufferImpl<'_, D, W> { + fn width(&self) -> usize { + self.imp.width as usize + } + + fn height(&self) -> usize { + self.imp.height as usize + } + #[inline] fn pixels(&self) -> &[u32] { match &self.pixels { diff --git a/src/backends/wayland/mod.rs b/src/backends/wayland/mod.rs index 000e553c..cdae0d83 100644 --- a/src/backends/wayland/mod.rs +++ b/src/backends/wayland/mod.rs @@ -263,6 +263,14 @@ pub struct BufferImpl<'a, D: ?Sized, W> { } impl BufferInterface for BufferImpl<'_, D, W> { + fn width(&self) -> usize { + self.stack.member().width + } + + fn height(&self) -> usize { + self.stack.member().height + } + #[inline] fn pixels(&self) -> &[u32] { self.stack.member() diff --git a/src/backends/web.rs b/src/backends/web.rs index 4eb8fe0c..64e35e47 100644 --- a/src/backends/web.rs +++ b/src/backends/web.rs @@ -377,6 +377,22 @@ pub struct BufferImpl<'a, D, W> { } impl BufferInterface for BufferImpl<'_, D, W> { + fn width(&self) -> usize { + self.imp + .size + .expect("must set size of surface before calling `width()` on the buffer") + .0 + .get() as usize + } + + fn height(&self) -> usize { + self.imp + .size + .expect("must set size of surface before calling `height()` on the buffer") + .1 + .get() as usize + } + fn pixels(&self) -> &[u32] { &self.imp.buffer } diff --git a/src/backends/win32.rs b/src/backends/win32.rs index 72dc6d0f..eda528cd 100644 --- a/src/backends/win32.rs +++ b/src/backends/win32.rs @@ -284,6 +284,14 @@ impl SurfaceInterface for Win32Im pub struct BufferImpl<'a, D, W>(&'a mut Win32Impl); impl BufferInterface for BufferImpl<'_, D, W> { + fn width(&self) -> usize { + self.0.buffer.as_ref().unwrap().width.get() as usize + } + + fn height(&self) -> usize { + self.0.buffer.as_ref().unwrap().height.get() as usize + } + #[inline] fn pixels(&self) -> &[u32] { self.0.buffer.as_ref().unwrap().pixels() diff --git a/src/backends/x11.rs b/src/backends/x11.rs index b4271f53..a141334b 100644 --- a/src/backends/x11.rs +++ b/src/backends/x11.rs @@ -388,6 +388,14 @@ pub struct BufferImpl<'a, D: ?Sized, W: ?Sized>(&'a mut X11Impl); impl BufferInterface for BufferImpl<'_, D, W> { + fn width(&self) -> usize { + self.0.size.unwrap().0.get() as usize + } + + fn height(&self) -> usize { + self.0.size.unwrap().1.get() as usize + } + #[inline] fn pixels(&self) -> &[u32] { // SAFETY: We called `finish_wait` on the buffer, so it is safe to call `buffer()`. diff --git a/src/lib.rs b/src/lib.rs index 2e25967e..ee085e1a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -205,6 +205,28 @@ pub struct Buffer<'a, D, W> { } impl Buffer<'_, D, W> { + /// The amount of pixels wide the buffer is. + pub fn width(&self) -> usize { + let width = self.buffer_impl.width(); + debug_assert_eq!( + width * self.buffer_impl.height(), + self.len(), + "buffer must be sized correctly" + ); + width + } + + /// The amount of pixels tall the buffer is. + pub fn height(&self) -> usize { + let height = self.buffer_impl.height(); + debug_assert_eq!( + height * self.buffer_impl.width(), + self.len(), + "buffer must be sized correctly" + ); + height + } + /// `age` is the number of frames ago this buffer was last presented. So if the value is /// `1`, it is the same as the last frame, and if it is `2`, it is the same as the frame /// before that (for backends using double buffering). If the value is `0`, it is a new From f168182d4b0dc0bb08f8037260dbc34d14567f22 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Thu, 5 Jun 2025 17:01:08 +0200 Subject: [PATCH 2/3] Update examples --- README.md | 25 +++++++++++-------------- examples/animation.rs | 25 +++++++++++-------------- examples/rectangle.rs | 25 +++++++++---------------- examples/winit.rs | 26 +++++++++++--------------- examples/winit_multithread.rs | 12 ++++++------ examples/winit_wrong_sized_buffer.rs | 15 +++++---------- 6 files changed, 53 insertions(+), 75 deletions(-) diff --git a/README.md b/README.md index c17e01fe..4b430a8a 100644 --- a/README.md +++ b/README.md @@ -101,26 +101,23 @@ fn main() { eprintln!("RedrawRequested fired before Resumed or after Suspended"); return; }; - let (width, height) = { - let size = window.inner_size(); - (size.width, size.height) - }; + let size = window.inner_size(); surface .resize( - NonZeroU32::new(width).unwrap(), - NonZeroU32::new(height).unwrap(), + NonZeroU32::new(size.width).unwrap(), + NonZeroU32::new(size.height).unwrap(), ) .unwrap(); let mut buffer = surface.buffer_mut().unwrap(); - for index in 0..(width * height) { - let y = index / width; - let x = index % width; - let red = x % 255; - let green = y % 255; - let blue = (x * y) % 255; - - buffer[index as usize] = blue | (green << 8) | (red << 16); + for index in 0..(buffer.width() * buffer.height()) { + let y = index / buffer.width(); + let x = index % buffer.width(); + let red = x as u32 % 255; + let green = y as u32 % 255; + let blue = (x as u32 * y as u32) % 255; + + buffer[index] = blue | (green << 8) | (red << 16); } buffer.present().unwrap(); diff --git a/examples/animation.rs b/examples/animation.rs index c075923e..c6d503a6 100644 --- a/examples/animation.rs +++ b/examples/animation.rs @@ -59,23 +59,20 @@ fn main() { return; }; - let size = window.inner_size(); - if let (Some(width), Some(height)) = - (NonZeroU32::new(size.width), NonZeroU32::new(size.height)) - { - let elapsed = start.elapsed().as_secs_f64() % 1.0; + let elapsed = start.elapsed().as_secs_f64() % 1.0; - if (width.get(), height.get()) != *old_size { - *old_size = (width.get(), height.get()); - *frames = pre_render_frames(width.get() as usize, height.get() as usize); - }; + let mut buffer = surface.buffer_mut().unwrap(); - let frame = &frames[((elapsed * 60.0).round() as usize).clamp(0, 59)]; - - let mut buffer = surface.buffer_mut().unwrap(); - buffer.copy_from_slice(frame); - buffer.present().unwrap(); + let size = (buffer.width(), buffer.height()); + if size != *old_size { + *old_size = size; + *frames = pre_render_frames(size.0, size.1); } + + let frame = &frames[((elapsed * 60.0).round() as usize).clamp(0, 59)]; + + buffer.copy_from_slice(frame); + buffer.present().unwrap(); } Event::AboutToWait => { window.request_redraw(); diff --git a/examples/rectangle.rs b/examples/rectangle.rs index b92f30b2..aba1a88e 100644 --- a/examples/rectangle.rs +++ b/examples/rectangle.rs @@ -1,3 +1,5 @@ +use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; +use softbuffer::Buffer; use std::num::NonZeroU32; use winit::event::{ElementState, Event, KeyEvent, WindowEvent}; use winit::event_loop::{ControlFlow, EventLoop}; @@ -6,7 +8,9 @@ use winit::keyboard::{Key, NamedKey}; #[path = "utils/winit_app.rs"] mod winit_app; -fn redraw(buffer: &mut [u32], width: usize, height: usize, flag: bool) { +fn redraw(buffer: &mut Buffer<'_, impl HasDisplayHandle, impl HasWindowHandle>, flag: bool) { + 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 { @@ -71,21 +75,10 @@ fn main() { eprintln!("RedrawRequested fired before Resumed or after Suspended"); return; }; - // Grab the window's client area dimensions, and ensure they're valid - let size = window.inner_size(); - if let (Some(width), Some(height)) = - (NonZeroU32::new(size.width), NonZeroU32::new(size.height)) - { - // Draw something in the window - let mut buffer = surface.buffer_mut().unwrap(); - redraw( - &mut buffer, - width.get() as usize, - height.get() as usize, - *flag, - ); - buffer.present().unwrap(); - } + // Draw something in the window + let mut buffer = surface.buffer_mut().unwrap(); + redraw(&mut buffer, *flag); + buffer.present().unwrap(); } Event::WindowEvent { diff --git a/examples/winit.rs b/examples/winit.rs index 27a3cefb..a18d601b 100644 --- a/examples/winit.rs +++ b/examples/winit.rs @@ -49,23 +49,19 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { eprintln!("RedrawRequested fired before Resumed or after Suspended"); return; }; - let size = window.inner_size(); - if let (Some(width), Some(height)) = - (NonZeroU32::new(size.width), NonZeroU32::new(size.height)) - { - let mut buffer = surface.buffer_mut().unwrap(); - for y in 0..height.get() { - for x in 0..width.get() { - let red = x % 255; - let green = y % 255; - let blue = (x * y) % 255; - let index = y as usize * width.get() as usize + x as usize; - buffer[index] = blue | (green << 8) | (red << 16); - } - } - buffer.present().unwrap(); + let mut buffer = surface.buffer_mut().unwrap(); + for y in 0..buffer.height() { + for x in 0..buffer.width() { + let red = x as u32 % 255; + let green = y as u32 % 255; + let blue = (x as u32 * y as u32) % 255; + let index = y * buffer.width() + x; + buffer[index] = blue | (green << 8) | (red << 16); + } } + + buffer.present().unwrap(); } Event::WindowEvent { event: diff --git a/examples/winit_multithread.rs b/examples/winit_multithread.rs index 03afcc56..140b0358 100644 --- a/examples/winit_multithread.rs +++ b/examples/winit_multithread.rs @@ -40,12 +40,12 @@ pub mod ex { surface.resize(width, height).unwrap(); let mut buffer = surface.buffer_mut().unwrap(); - for y in 0..height.get() { - for x in 0..width.get() { - let red = x % 255; - let green = y % 255; - let blue = (x * y) % 255; - let index = y as usize * width.get() as usize + x as usize; + for y in 0..buffer.height() { + for x in 0..buffer.width() { + let red = x as u32 % 255; + let green = y as u32 % 255; + let blue = (x as u32 * y as u32) % 255; + let index = y * buffer.width() + x; buffer[index] = blue | (green << 8) | (red << 16); } } diff --git a/examples/winit_wrong_sized_buffer.rs b/examples/winit_wrong_sized_buffer.rs index 0c58a116..febe7e0b 100644 --- a/examples/winit_wrong_sized_buffer.rs +++ b/examples/winit_wrong_sized_buffer.rs @@ -6,9 +6,6 @@ use winit::keyboard::{Key, NamedKey}; #[path = "utils/winit_app.rs"] mod winit_app; -const BUFFER_WIDTH: usize = 256; -const BUFFER_HEIGHT: usize = 128; - fn main() { let event_loop = EventLoop::new().unwrap(); @@ -24,10 +21,7 @@ fn main() { 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(BUFFER_WIDTH as u32).unwrap(), - NonZeroU32::new(BUFFER_HEIGHT as u32).unwrap(), - ) + .resize(NonZeroU32::new(256).unwrap(), NonZeroU32::new(128).unwrap()) .unwrap(); surface }, @@ -47,14 +41,15 @@ fn main() { }; let mut buffer = surface.buffer_mut().unwrap(); - for y in 0..BUFFER_HEIGHT { - for x in 0..BUFFER_WIDTH { + let width = buffer.width(); + for y in 0..buffer.height() { + for x in 0..width { let red = x as u32 % 255; let green = y as u32 % 255; let blue = (x as u32 * y as u32) % 255; let color = blue | (green << 8) | (red << 16); - buffer[y * BUFFER_WIDTH + x] = color; + buffer[y * width + x] = color; } } buffer.present().unwrap(); From 3f255f43208e88cee7b814d7b0b2eb194aaaac41 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Thu, 5 Jun 2025 19:20:31 +0200 Subject: [PATCH 3/3] Finish Wayland impl --- src/backends/wayland/buffer.rs | 4 ++-- src/backends/wayland/mod.rs | 10 ++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/backends/wayland/buffer.rs b/src/backends/wayland/buffer.rs index c4ebef0d..407dd742 100644 --- a/src/backends/wayland/buffer.rs +++ b/src/backends/wayland/buffer.rs @@ -74,8 +74,8 @@ pub(super) struct WaylandBuffer { pool: wl_shm_pool::WlShmPool, pool_size: i32, buffer: wl_buffer::WlBuffer, - width: i32, - height: i32, + pub width: i32, + pub height: i32, released: Arc, pub age: u8, } diff --git a/src/backends/wayland/mod.rs b/src/backends/wayland/mod.rs index cdae0d83..eabc7cef 100644 --- a/src/backends/wayland/mod.rs +++ b/src/backends/wayland/mod.rs @@ -240,11 +240,15 @@ impl SurfaceInterface )); }; + let width = self.buffers.as_mut().unwrap().1.width; + let height = self.buffers.as_mut().unwrap().1.height; let age = self.buffers.as_mut().unwrap().1.age; Ok(BufferImpl { stack: util::BorrowStack::new(self, |buffer| { Ok(unsafe { buffer.buffers.as_mut().unwrap().1.mapped_mut() }) })?, + width, + height, age, }) } @@ -259,16 +263,18 @@ impl Drop for WaylandImpl { pub struct BufferImpl<'a, D: ?Sized, W> { stack: util::BorrowStack<'a, WaylandImpl, [u32]>, + width: i32, + height: i32, age: u8, } impl BufferInterface for BufferImpl<'_, D, W> { fn width(&self) -> usize { - self.stack.member().width + self.width as usize } fn height(&self) -> usize { - self.stack.member().height + self.height as usize } #[inline]