feat(graphics): implement double-buffered graphics surface with lock and post engine
This commit is contained in:
parent
6ae2bcb091
commit
94584bed1e
3 changed files with 118 additions and 0 deletions
|
|
@ -1,2 +1,3 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
pub mod surface;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
|
||||||
95
crates/molotov-core/src/graphics/surface.rs
Normal file
95
crates/molotov-core/src/graphics/surface.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
22
crates/molotov-core/tests/surface_buffer_test.rs
Normal file
22
crates/molotov-core/tests/surface_buffer_test.rs
Normal 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);
|
||||||
|
}
|
||||||
Reference in a new issue