fix(graphics): implement thread-safe interior mutability, dirty rect clamping, and ANativeWindow handle table
This commit is contained in:
parent
64d96c7df2
commit
3c27dc574a
7 changed files with 228 additions and 54 deletions
|
|
@ -2,27 +2,61 @@
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
use super::surface::GraphicsSurface;
|
use super::surface::GraphicsSurface;
|
||||||
use super::types::{ANativeWindow_Buffer, ARect, WindowFormat};
|
use super::types::{ANativeWindow_Buffer, ARect, WindowFormat};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
|
static NEXT_WINDOW_ID: AtomicUsize = AtomicUsize::new(2001);
|
||||||
|
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
static ref WINDOW_TABLE: RwLock<HashMap<usize, Arc<GraphicsSurface>>> = RwLock::new(HashMap::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register_window_surface(surface: Arc<GraphicsSurface>) -> *mut () {
|
||||||
|
let id = NEXT_WINDOW_ID.fetch_add(1, Ordering::Relaxed);
|
||||||
|
if let Ok(mut table) = WINDOW_TABLE.write() {
|
||||||
|
table.insert(id, surface);
|
||||||
|
id as *mut ()
|
||||||
|
} else {
|
||||||
|
std::ptr::null_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_window_surface(window: *mut ()) -> Option<Arc<GraphicsSurface>> {
|
||||||
|
let id = window as usize;
|
||||||
|
if let Ok(table) = WINDOW_TABLE.read() {
|
||||||
|
table.get(&id).cloned()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn ANativeWindow_acquire(window: *mut ()) {
|
pub unsafe extern "C" fn ANativeWindow_acquire(window: *mut ()) {
|
||||||
if let Some(s) = (window as *mut GraphicsSurface).as_ref() {
|
if let Some(s) = get_window_surface(window) {
|
||||||
s.acquire();
|
s.acquire();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn ANativeWindow_release(window: *mut ()) {
|
pub unsafe extern "C" fn ANativeWindow_release(window: *mut ()) {
|
||||||
if !window.is_null() {
|
let id = window as usize;
|
||||||
let ptr = window as *mut GraphicsSurface;
|
let should_remove = if let Some(s) = get_window_surface(window) {
|
||||||
if (*ptr).release() == 0 {
|
s.release() == 0
|
||||||
drop(Box::from_raw(ptr));
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
if should_remove {
|
||||||
|
if let Ok(mut table) = WINDOW_TABLE.write() {
|
||||||
|
table.remove(&id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn ANativeWindow_getWidth(window: *mut ()) -> i32 {
|
pub unsafe extern "C" fn ANativeWindow_getWidth(window: *mut ()) -> i32 {
|
||||||
if let Some(s) = (window as *mut GraphicsSurface).as_ref() {
|
if let Some(s) = get_window_surface(window) {
|
||||||
s.width()
|
s.width()
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
|
|
@ -31,7 +65,7 @@ pub unsafe extern "C" fn ANativeWindow_getWidth(window: *mut ()) -> i32 {
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn ANativeWindow_getHeight(window: *mut ()) -> i32 {
|
pub unsafe extern "C" fn ANativeWindow_getHeight(window: *mut ()) -> i32 {
|
||||||
if let Some(s) = (window as *mut GraphicsSurface).as_ref() {
|
if let Some(s) = get_window_surface(window) {
|
||||||
s.height()
|
s.height()
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
|
|
@ -40,7 +74,7 @@ pub unsafe extern "C" fn ANativeWindow_getHeight(window: *mut ()) -> i32 {
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn ANativeWindow_getFormat(window: *mut ()) -> i32 {
|
pub unsafe extern "C" fn ANativeWindow_getFormat(window: *mut ()) -> i32 {
|
||||||
if let Some(s) = (window as *mut GraphicsSurface).as_ref() {
|
if let Some(s) = get_window_surface(window) {
|
||||||
s.format() as i32
|
s.format() as i32
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
|
|
@ -54,10 +88,14 @@ pub unsafe extern "C" fn ANativeWindow_setBuffersGeometry(
|
||||||
height: i32,
|
height: i32,
|
||||||
format: i32,
|
format: i32,
|
||||||
) -> i32 {
|
) -> i32 {
|
||||||
if let Some(s) = (window as *mut GraphicsSurface).as_mut() {
|
let fmt = match WindowFormat::from_i32(format) {
|
||||||
let fmt = WindowFormat::from_i32(format).unwrap_or(WindowFormat::Rgba8888);
|
Some(f) => f,
|
||||||
s.set_geometry(width, height, fmt);
|
None if format == 0 => WindowFormat::Rgba8888,
|
||||||
0
|
None => return -libc::EINVAL,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(s) = get_window_surface(window) {
|
||||||
|
s.set_geometry(width, height, fmt)
|
||||||
} else {
|
} else {
|
||||||
-libc::EINVAL
|
-libc::EINVAL
|
||||||
}
|
}
|
||||||
|
|
@ -69,7 +107,7 @@ pub unsafe extern "C" fn ANativeWindow_lock(
|
||||||
outBuffer: *mut ANativeWindow_Buffer,
|
outBuffer: *mut ANativeWindow_Buffer,
|
||||||
inOutDirtyBounds: *mut ARect,
|
inOutDirtyBounds: *mut ARect,
|
||||||
) -> i32 {
|
) -> i32 {
|
||||||
if let Some(s) = (window as *mut GraphicsSurface).as_mut() {
|
if let Some(s) = get_window_surface(window) {
|
||||||
s.lock(outBuffer, inOutDirtyBounds)
|
s.lock(outBuffer, inOutDirtyBounds)
|
||||||
} else {
|
} else {
|
||||||
-libc::EINVAL
|
-libc::EINVAL
|
||||||
|
|
@ -78,7 +116,7 @@ pub unsafe extern "C" fn ANativeWindow_lock(
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn ANativeWindow_unlockAndPost(window: *mut ()) -> i32 {
|
pub unsafe extern "C" fn ANativeWindow_unlockAndPost(window: *mut ()) -> i32 {
|
||||||
if let Some(s) = (window as *mut GraphicsSurface).as_mut() {
|
if let Some(s) = get_window_surface(window) {
|
||||||
s.unlock_and_post()
|
s.unlock_and_post()
|
||||||
} else {
|
} else {
|
||||||
-libc::EINVAL
|
-libc::EINVAL
|
||||||
|
|
|
||||||
|
|
@ -1,42 +1,54 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
use super::types::{ANativeWindow_Buffer, ARect, WindowFormat};
|
use super::types::{ANativeWindow_Buffer, ARect, WindowFormat};
|
||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
use std::sync::atomic::{AtomicU32, Ordering};
|
use std::sync::atomic::{fence, AtomicU32, Ordering};
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
pub struct GraphicsSurface {
|
struct SurfaceInner {
|
||||||
width: i32,
|
width: i32,
|
||||||
height: i32,
|
height: i32,
|
||||||
format: WindowFormat,
|
format: WindowFormat,
|
||||||
back_buffer: Vec<u8>,
|
back_buffer: Vec<u8>,
|
||||||
front_buffer: Vec<u8>,
|
front_buffer: Vec<u8>,
|
||||||
is_locked: bool,
|
is_locked: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GraphicsSurface {
|
||||||
|
inner: Mutex<SurfaceInner>,
|
||||||
ref_count: AtomicU32,
|
ref_count: AtomicU32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GraphicsSurface {
|
impl GraphicsSurface {
|
||||||
pub fn new(width: i32, height: i32, format: WindowFormat) -> Self {
|
pub fn new(width: i32, height: i32, format: WindowFormat) -> Self {
|
||||||
let size = (width as usize) * (height as usize) * format.bytes_per_pixel();
|
let w = width.max(1);
|
||||||
Self {
|
let h = height.max(1);
|
||||||
width,
|
let size = (w as usize) * (h as usize) * format.bytes_per_pixel();
|
||||||
height,
|
|
||||||
|
let inner = SurfaceInner {
|
||||||
|
width: w,
|
||||||
|
height: h,
|
||||||
format,
|
format,
|
||||||
back_buffer: vec![0u8; size],
|
back_buffer: vec![0u8; size],
|
||||||
front_buffer: vec![0u8; size],
|
front_buffer: vec![0u8; size],
|
||||||
is_locked: false,
|
is_locked: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
inner: Mutex::new(inner),
|
||||||
ref_count: AtomicU32::new(1),
|
ref_count: AtomicU32::new(1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn width(&self) -> i32 {
|
pub fn width(&self) -> i32 {
|
||||||
self.width
|
self.inner.lock().map(|i| i.width).unwrap_or(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn height(&self) -> i32 {
|
pub fn height(&self) -> i32 {
|
||||||
self.height
|
self.inner.lock().map(|i| i.height).unwrap_or(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn format(&self) -> WindowFormat {
|
pub fn format(&self) -> WindowFormat {
|
||||||
self.format
|
self.inner.lock().map(|i| i.format).unwrap_or(WindowFormat::Rgba8888)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn acquire(&self) -> u32 {
|
pub fn acquire(&self) -> u32 {
|
||||||
|
|
@ -44,52 +56,91 @@ impl GraphicsSurface {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn release(&self) -> u32 {
|
pub fn release(&self) -> u32 {
|
||||||
self.ref_count.fetch_sub(1, Ordering::Release) - 1
|
let prev = self.ref_count.fetch_sub(1, Ordering::Release);
|
||||||
|
if prev == 1 {
|
||||||
|
fence(Ordering::Acquire);
|
||||||
|
0
|
||||||
|
} else if prev == 0 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
prev - 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_geometry(&mut self, width: i32, height: i32, format: WindowFormat) {
|
pub fn set_geometry(&self, width: i32, height: i32, format: WindowFormat) -> i32 {
|
||||||
|
let mut inner = match self.inner.lock() {
|
||||||
|
Ok(g) => g,
|
||||||
|
Err(_) => return -libc::EINVAL,
|
||||||
|
};
|
||||||
|
|
||||||
|
if inner.is_locked {
|
||||||
|
return -libc::EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
if width > 0 && height > 0 {
|
if width > 0 && height > 0 {
|
||||||
self.width = width;
|
inner.width = width;
|
||||||
self.height = height;
|
inner.height = height;
|
||||||
self.format = format;
|
inner.format = format;
|
||||||
let size = (width as usize) * (height as usize) * format.bytes_per_pixel();
|
let size = (width as usize) * (height as usize) * format.bytes_per_pixel();
|
||||||
self.back_buffer.resize(size, 0);
|
inner.back_buffer.resize(size, 0);
|
||||||
self.front_buffer.resize(size, 0);
|
inner.front_buffer.resize(size, 0);
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
-libc::EINVAL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn lock(
|
pub unsafe fn lock(
|
||||||
&mut self,
|
&self,
|
||||||
out_buffer: *mut ANativeWindow_Buffer,
|
out_buffer: *mut ANativeWindow_Buffer,
|
||||||
_in_out_dirty: *mut ARect,
|
in_out_dirty: *mut ARect,
|
||||||
) -> i32 {
|
) -> i32 {
|
||||||
if out_buffer.is_null() {
|
if out_buffer.is_null() {
|
||||||
return -libc::EINVAL;
|
return -libc::EINVAL;
|
||||||
}
|
}
|
||||||
if self.is_locked {
|
|
||||||
|
let mut inner = match self.inner.lock() {
|
||||||
|
Ok(g) => g,
|
||||||
|
Err(_) => return -libc::EINVAL,
|
||||||
|
};
|
||||||
|
|
||||||
|
if inner.is_locked {
|
||||||
return -libc::EBUSY;
|
return -libc::EBUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.is_locked = true;
|
inner.is_locked = true;
|
||||||
|
|
||||||
(*out_buffer).width = self.width;
|
// Clamp dirty bounds safely
|
||||||
(*out_buffer).height = self.height;
|
if let Some(dirty) = in_out_dirty.as_mut() {
|
||||||
(*out_buffer).stride = self.width;
|
dirty.left = dirty.left.clamp(0, inner.width);
|
||||||
(*out_buffer).format = self.format as i32;
|
dirty.top = dirty.top.clamp(0, inner.height);
|
||||||
(*out_buffer).bits = self.back_buffer.as_mut_ptr() as *mut c_void;
|
dirty.right = dirty.right.clamp(dirty.left, inner.width);
|
||||||
|
dirty.bottom = dirty.bottom.clamp(dirty.top, inner.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
(*out_buffer).width = inner.width;
|
||||||
|
(*out_buffer).height = inner.height;
|
||||||
|
(*out_buffer).stride = inner.width;
|
||||||
|
(*out_buffer).format = inner.format as i32;
|
||||||
|
(*out_buffer).bits = inner.back_buffer.as_mut_ptr() as *mut c_void;
|
||||||
(*out_buffer).reserved = [0; 6];
|
(*out_buffer).reserved = [0; 6];
|
||||||
|
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unlock_and_post(&mut self) -> i32 {
|
pub fn unlock_and_post(&self) -> i32 {
|
||||||
if !self.is_locked {
|
let mut guard = match self.inner.lock() {
|
||||||
|
Ok(g) => g,
|
||||||
|
Err(_) => return -libc::EINVAL,
|
||||||
|
};
|
||||||
|
|
||||||
|
if !guard.is_locked {
|
||||||
return -libc::EINVAL;
|
return -libc::EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Swap front and back buffer for presentation
|
let inner = &mut *guard;
|
||||||
std::mem::swap(&mut self.front_buffer, &mut self.back_buffer);
|
std::mem::swap(&mut inner.front_buffer, &mut inner.back_buffer);
|
||||||
self.is_locked = false;
|
inner.is_locked = false;
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
use super::c_api::register_window_surface;
|
||||||
use super::surface::GraphicsSurface;
|
use super::surface::GraphicsSurface;
|
||||||
use super::types::WindowFormat;
|
use super::types::WindowFormat;
|
||||||
|
use crate::jni::activity::ActivityRunner;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct WindowConfig {
|
pub struct WindowConfig {
|
||||||
|
|
@ -25,13 +28,32 @@ impl Default for WindowConfig {
|
||||||
|
|
||||||
pub struct WindowManager {
|
pub struct WindowManager {
|
||||||
config: WindowConfig,
|
config: WindowConfig,
|
||||||
pub surface: GraphicsSurface,
|
pub surface: Arc<GraphicsSurface>,
|
||||||
|
pub window_handle: *mut (),
|
||||||
|
activity_runner: Option<Arc<Mutex<ActivityRunner>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowManager {
|
impl WindowManager {
|
||||||
pub fn new(config: WindowConfig) -> Self {
|
pub fn new(config: WindowConfig) -> Self {
|
||||||
let surface = GraphicsSurface::new(config.width, config.height, WindowFormat::Rgba8888);
|
Self::new_with_activity(config, None)
|
||||||
Self { config, surface }
|
}
|
||||||
|
|
||||||
|
pub fn new_with_activity(
|
||||||
|
config: WindowConfig,
|
||||||
|
activity_runner: Option<Arc<Mutex<ActivityRunner>>>,
|
||||||
|
) -> Self {
|
||||||
|
let surface = Arc::new(GraphicsSurface::new(
|
||||||
|
config.width,
|
||||||
|
config.height,
|
||||||
|
WindowFormat::Rgba8888,
|
||||||
|
));
|
||||||
|
let window_handle = register_window_surface(surface.clone());
|
||||||
|
Self {
|
||||||
|
config,
|
||||||
|
surface,
|
||||||
|
window_handle,
|
||||||
|
activity_runner,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn width(&self) -> i32 {
|
pub fn width(&self) -> i32 {
|
||||||
|
|
@ -46,8 +68,15 @@ impl WindowManager {
|
||||||
if width > 0 && height > 0 {
|
if width > 0 && height > 0 {
|
||||||
self.config.width = width;
|
self.config.width = width;
|
||||||
self.config.height = height;
|
self.config.height = height;
|
||||||
self.surface.set_geometry(width, height, self.surface.format());
|
let _ = self.surface.set_geometry(width, height, self.surface.format());
|
||||||
tracing::info!("WindowManager: surface resized to {}x{}", width, height);
|
tracing::info!("WindowManager: surface resized to {}x{}", width, height);
|
||||||
|
|
||||||
|
// Notify activity runner if attached
|
||||||
|
if let Some(runner_arc) = &self.activity_runner {
|
||||||
|
if let Ok(mut runner) = runner_arc.lock() {
|
||||||
|
runner.trigger_window_resized(self.window_handle as *mut _);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,18 +2,19 @@
|
||||||
use molotov_core::graphics::c_api::*;
|
use molotov_core::graphics::c_api::*;
|
||||||
use molotov_core::graphics::surface::GraphicsSurface;
|
use molotov_core::graphics::surface::GraphicsSurface;
|
||||||
use molotov_core::graphics::types::{ANativeWindow_Buffer, WindowFormat};
|
use molotov_core::graphics::types::{ANativeWindow_Buffer, WindowFormat};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_anativewindow_c_api_lifecycle() {
|
fn test_anativewindow_c_api_lifecycle() {
|
||||||
let surface = Box::new(GraphicsSurface::new(640, 480, WindowFormat::Rgba8888));
|
let surface = Arc::new(GraphicsSurface::new(640, 480, WindowFormat::Rgba8888));
|
||||||
let handle = Box::into_raw(surface) as *mut ();
|
let handle = register_window_surface(surface);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_eq!(ANativeWindow_getWidth(handle), 640);
|
assert_eq!(ANativeWindow_getWidth(handle), 640);
|
||||||
assert_eq!(ANativeWindow_getHeight(handle), 480);
|
assert_eq!(ANativeWindow_getHeight(handle), 480);
|
||||||
assert_eq!(ANativeWindow_getFormat(handle), 1);
|
assert_eq!(ANativeWindow_getFormat(handle), 1);
|
||||||
|
|
||||||
ANativeWindow_setBuffersGeometry(handle, 800, 600, 1);
|
assert_eq!(ANativeWindow_setBuffersGeometry(handle, 800, 600, 1), 0);
|
||||||
assert_eq!(ANativeWindow_getWidth(handle), 800);
|
assert_eq!(ANativeWindow_getWidth(handle), 800);
|
||||||
assert_eq!(ANativeWindow_getHeight(handle), 600);
|
assert_eq!(ANativeWindow_getHeight(handle), 600);
|
||||||
|
|
||||||
|
|
|
||||||
54
crates/molotov-core/tests/graphics_fixes_test.rs
Normal file
54
crates/molotov-core/tests/graphics_fixes_test.rs
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
use molotov_core::graphics::c_api::*;
|
||||||
|
use molotov_core::graphics::surface::GraphicsSurface;
|
||||||
|
use molotov_core::graphics::types::{ANativeWindow_Buffer, ARect, WindowFormat};
|
||||||
|
use molotov_core::graphics::window::{WindowConfig, WindowManager};
|
||||||
|
use molotov_core::jni::activity::ActivityRunner;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_dirty_rect_clamping_on_lock() {
|
||||||
|
let surface = Arc::new(GraphicsSurface::new(800, 600, WindowFormat::Rgba8888));
|
||||||
|
let handle = register_window_surface(surface);
|
||||||
|
|
||||||
|
let mut buf = std::mem::MaybeUninit::<ANativeWindow_Buffer>::uninit();
|
||||||
|
let mut dirty = ARect {
|
||||||
|
left: -50,
|
||||||
|
top: -20,
|
||||||
|
right: 1500,
|
||||||
|
bottom: 2000,
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
assert_eq!(ANativeWindow_lock(handle, buf.as_mut_ptr(), &mut dirty), 0);
|
||||||
|
// Clamping check
|
||||||
|
assert_eq!(dirty.left, 0);
|
||||||
|
assert_eq!(dirty.top, 0);
|
||||||
|
assert_eq!(dirty.right, 800);
|
||||||
|
assert_eq!(dirty.bottom, 600);
|
||||||
|
|
||||||
|
// While locked, setBuffersGeometry must return -EBUSY
|
||||||
|
assert_eq!(ANativeWindow_setBuffersGeometry(handle, 1024, 768, 1), -libc::EBUSY);
|
||||||
|
|
||||||
|
assert_eq!(ANativeWindow_unlockAndPost(handle), 0);
|
||||||
|
ANativeWindow_release(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_window_manager_syncs_with_activity_runner() {
|
||||||
|
let runner = Arc::new(Mutex::new(ActivityRunner::new()));
|
||||||
|
let config = WindowConfig {
|
||||||
|
title: "Test".to_string(),
|
||||||
|
width: 1280,
|
||||||
|
height: 720,
|
||||||
|
fullscreen: false,
|
||||||
|
vulkan: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut manager = WindowManager::new_with_activity(config, Some(runner.clone()));
|
||||||
|
manager.handle_resize(1920, 1080);
|
||||||
|
|
||||||
|
assert_eq!(manager.width(), 1920);
|
||||||
|
assert_eq!(manager.height(), 1080);
|
||||||
|
}
|
||||||
|
|
@ -3,12 +3,13 @@ use molotov_core::graphics::c_api::*;
|
||||||
use molotov_core::graphics::surface::GraphicsSurface;
|
use molotov_core::graphics::surface::GraphicsSurface;
|
||||||
use molotov_core::graphics::types::{ANativeWindow_Buffer, WindowFormat};
|
use molotov_core::graphics::types::{ANativeWindow_Buffer, WindowFormat};
|
||||||
use molotov_core::jni::activity::ActivityRunner;
|
use molotov_core::jni::activity::ActivityRunner;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_end_to_end_graphics_surface_and_activity_binding() {
|
fn test_end_to_end_graphics_surface_and_activity_binding() {
|
||||||
let mut runner = ActivityRunner::new();
|
let mut runner = ActivityRunner::new();
|
||||||
let surface = Box::new(GraphicsSurface::new(1280, 720, WindowFormat::Rgba8888));
|
let surface = Arc::new(GraphicsSurface::new(1280, 720, WindowFormat::Rgba8888));
|
||||||
let window_handle = Box::into_raw(surface) as *mut ();
|
let window_handle = register_window_surface(surface);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
// 1. Notify activity that native window was created
|
// 1. Notify activity that native window was created
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use molotov_core::graphics::types::WindowFormat;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_surface_lock_and_unlock_post() {
|
fn test_surface_lock_and_unlock_post() {
|
||||||
let mut surface = GraphicsSurface::new(800, 600, WindowFormat::Rgba8888);
|
let surface = GraphicsSurface::new(800, 600, WindowFormat::Rgba8888);
|
||||||
assert_eq!(surface.width(), 800);
|
assert_eq!(surface.width(), 800);
|
||||||
assert_eq!(surface.height(), 600);
|
assert_eq!(surface.height(), 600);
|
||||||
|
|
||||||
|
|
|
||||||
Reference in a new issue