feat(graphics): implement double-buffered graphics surface with lock and post engine

This commit is contained in:
katR 2026-09-30 08:13:58 +05:30
parent 6ae2bcb091
commit 94584bed1e
3 changed files with 118 additions and 0 deletions

View file

@ -1,2 +1,3 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pub mod surface;
pub mod types;

View file

@ -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<u8>,
front_buffer: Vec<u8>,
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
}
}

View file

@ -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);
}