diff --git a/crates/molotov-core/src/graphics/mod.rs b/crates/molotov-core/src/graphics/mod.rs index b50bdf4..23f33a0 100644 --- a/crates/molotov-core/src/graphics/mod.rs +++ b/crates/molotov-core/src/graphics/mod.rs @@ -1,2 +1,3 @@ // SPDX-License-Identifier: GPL-3.0-or-later +pub mod surface; pub mod types; diff --git a/crates/molotov-core/src/graphics/surface.rs b/crates/molotov-core/src/graphics/surface.rs new file mode 100644 index 0000000..5714930 --- /dev/null +++ b/crates/molotov-core/src/graphics/surface.rs @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +use super::types::{ANativeWindow_Buffer, ARect, WindowFormat}; +use std::ffi::c_void; +use std::sync::atomic::{AtomicU32, Ordering}; + +pub struct GraphicsSurface { + width: i32, + height: i32, + format: WindowFormat, + back_buffer: Vec, + front_buffer: Vec, + is_locked: bool, + ref_count: AtomicU32, +} + +impl GraphicsSurface { + pub fn new(width: i32, height: i32, format: WindowFormat) -> Self { + let size = (width as usize) * (height as usize) * format.bytes_per_pixel(); + Self { + width, + height, + format, + back_buffer: vec![0u8; size], + front_buffer: vec![0u8; size], + is_locked: false, + ref_count: AtomicU32::new(1), + } + } + + pub fn width(&self) -> i32 { + self.width + } + + pub fn height(&self) -> i32 { + self.height + } + + pub fn format(&self) -> WindowFormat { + self.format + } + + pub fn acquire(&self) -> u32 { + self.ref_count.fetch_add(1, Ordering::Relaxed) + 1 + } + + pub fn release(&self) -> u32 { + self.ref_count.fetch_sub(1, Ordering::Release) - 1 + } + + pub fn set_geometry(&mut self, width: i32, height: i32, format: WindowFormat) { + if width > 0 && height > 0 { + self.width = width; + self.height = height; + self.format = format; + let size = (width as usize) * (height as usize) * format.bytes_per_pixel(); + self.back_buffer.resize(size, 0); + self.front_buffer.resize(size, 0); + } + } + + pub unsafe fn lock( + &mut self, + out_buffer: *mut ANativeWindow_Buffer, + _in_out_dirty: *mut ARect, + ) -> i32 { + if out_buffer.is_null() { + return -libc::EINVAL; + } + if self.is_locked { + return -libc::EBUSY; + } + + self.is_locked = true; + + (*out_buffer).width = self.width; + (*out_buffer).height = self.height; + (*out_buffer).stride = self.width; + (*out_buffer).format = self.format as i32; + (*out_buffer).bits = self.back_buffer.as_mut_ptr() as *mut c_void; + (*out_buffer).reserved = [0; 6]; + + 0 + } + + pub fn unlock_and_post(&mut self) -> i32 { + if !self.is_locked { + return -libc::EINVAL; + } + + // Swap front and back buffer for presentation + std::mem::swap(&mut self.front_buffer, &mut self.back_buffer); + self.is_locked = false; + 0 + } +} diff --git a/crates/molotov-core/tests/surface_buffer_test.rs b/crates/molotov-core/tests/surface_buffer_test.rs new file mode 100644 index 0000000..d637393 --- /dev/null +++ b/crates/molotov-core/tests/surface_buffer_test.rs @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +use molotov_core::graphics::surface::GraphicsSurface; +use molotov_core::graphics::types::WindowFormat; + +#[test] +fn test_surface_lock_and_unlock_post() { + let mut surface = GraphicsSurface::new(800, 600, WindowFormat::Rgba8888); + assert_eq!(surface.width(), 800); + assert_eq!(surface.height(), 600); + + let mut buffer = std::mem::MaybeUninit::uninit(); + let lock_res = unsafe { surface.lock(buffer.as_mut_ptr(), std::ptr::null_mut()) }; + assert_eq!(lock_res, 0); + + let buf = unsafe { buffer.assume_init() }; + assert_eq!(buf.width, 800); + assert_eq!(buf.height, 600); + assert!(!buf.bits.is_null()); + + let post_res = surface.unlock_and_post(); + assert_eq!(post_res, 0); +}