diff --git a/Cargo.toml b/Cargo.toml index c15510d..71f81c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ log = "0.4.17" raw_window_handle = { package = "raw-window-handle", version = "0.6", features = ["std"] } [target.'cfg(target_os = "android")'.dependencies] +bytemuck.version = "1.12.3" ndk = "0.9.0" [target.'cfg(all(unix, not(any(target_vendor = "apple", target_os = "android", target_os = "redox"))))'.dependencies] diff --git a/src/backends/android.rs b/src/backends/android.rs index 995e03a..011ec72 100644 --- a/src/backends/android.rs +++ b/src/backends/android.rs @@ -13,19 +13,14 @@ use crate::error::InitError; use crate::{Rect, SoftBufferError}; /// The handle to a window for software buffering. -pub struct AndroidImpl { +pub struct AndroidImpl { native_window: NativeWindow, - - _display: PhantomData, - - /// The pointer to the window object. - /// - /// This is pretty useless because it gives us a pointer to [`NativeWindow`] that we have to increase the refcount on. - /// Alternatively we can use [`NativeWindow::from_ptr()`] wrapped in [`std::mem::ManuallyDrop`] window: W, + _display: PhantomData, } // TODO: Current system doesn't require a trait to be implemented here, even though it exists. +// impl SurfaceInterface for ... impl AndroidImpl { /// Create a new [`AndroidImpl`] from an [`AndroidNdkWindowHandle`]. /// @@ -45,9 +40,6 @@ impl AndroidImpl { Ok(Self { native_window, - // _display: DisplayHandle::borrow_raw(raw_window_handle::RawDisplayHandle::Android( - // AndroidDisplayHandle, - // )), _display: PhantomData, window, }) @@ -73,7 +65,7 @@ impl AndroidImpl { width.into(), height.into(), // Default is typically R5G6B5 16bpp, switch to 32bpp - Some(HardwareBufferFormat::R8G8B8A8_UNORM), + Some(HardwareBufferFormat::R8G8B8X8_UNORM), ) .map_err(|err| { SoftBufferError::PlatformError( @@ -84,21 +76,30 @@ impl AndroidImpl { } pub fn buffer_mut(&mut self) -> Result, SoftBufferError> { - let lock_guard = self.native_window.lock(None).map_err(|err| { + let native_window_buffer = self.native_window.lock(None).map_err(|err| { SoftBufferError::PlatformError( Some("Failed to lock ANativeWindow".to_owned()), Some(Box::new(err)), ) })?; - assert_eq!( - lock_guard.format().bytes_per_pixel(), - Some(4), - "Unexpected buffer format {:?}, please call .resize() first to change it to RGBA8888", - lock_guard.format() + assert!( + matches!( + native_window_buffer.format(), + // These are the only formats we support + HardwareBufferFormat::R8G8B8A8_UNORM | HardwareBufferFormat::R8G8B8X8_UNORM + ), + "Unexpected buffer format {:?}, please call .resize() first to change it to RGBx8888", + native_window_buffer.format() ); - Ok(BufferImpl(lock_guard, PhantomData, PhantomData)) + let buffer = vec![0; native_window_buffer.width() * native_window_buffer.height()]; + + Ok(BufferImpl { + native_window_buffer, + buffer, + marker: PhantomData, + }) } /// Fetch the buffer from the window. @@ -107,11 +108,11 @@ impl AndroidImpl { } } -pub struct BufferImpl<'a, D: ?Sized, W>( - NativeWindowBufferLockGuard<'a>, - PhantomData<&'a D>, - PhantomData<&'a W>, -); +pub struct BufferImpl<'a, D: ?Sized, W> { + native_window_buffer: NativeWindowBufferLockGuard<'a>, + buffer: Vec, + marker: PhantomData<(&'a D, &'a W)>, +} // TODO: Move to NativeWindowBufferLockGuard? unsafe impl<'a, D, W> Send for BufferImpl<'a, D, W> {} @@ -119,36 +120,46 @@ unsafe impl<'a, D, W> Send for BufferImpl<'a, D, W> {} impl<'a, D: HasDisplayHandle + ?Sized, W: HasWindowHandle> BufferImpl<'a, D, W> { #[inline] pub fn pixels(&self) -> &[u32] { - todo!() - // unsafe { - // std::slice::from_raw_parts( - // self.0.bits().cast_const().cast(), - // (self.0.stride() * self.0.height()) as usize, - // ) - // } + &self.buffer } #[inline] pub fn pixels_mut(&mut self) -> &mut [u32] { - let bytes = self.0.bytes().expect("Nonplanar format"); - unsafe { - std::slice::from_raw_parts_mut( - bytes.as_mut_ptr().cast(), - bytes.len() / std::mem::size_of::(), - ) - } + &mut self.buffer } pub fn age(&self) -> u8 { 0 } - pub fn present(self) -> Result<(), SoftBufferError> { - // Dropping the guard automatically unlocks and posts it + // TODO: This function is pretty slow this way + pub fn present(mut self) -> Result<(), SoftBufferError> { + let input_lines = self.buffer.chunks(self.native_window_buffer.width()); + for (output, input) in self + .native_window_buffer + .lines() + .expect("Nonplanar format") + .zip(input_lines) + { + // .lines() removed the stride + assert_eq!(output.len(), input.len() * 4); + + for (i, pixel) in input.iter().enumerate() { + // Swizzle colors from RGBX to BGR + let [b, g, r, _] = pixel.to_le_bytes(); + output[i * 4].write(b); + output[i * 4 + 1].write(g); + output[i * 4 + 2].write(r); + // TODO alpha? + } + } Ok(()) } pub fn present_with_damage(self, _damage: &[Rect]) -> Result<(), SoftBufferError> { - Err(SoftBufferError::Unimplemented) + // TODO: Android requires the damage rect _at lock time_ + // Since we're faking the backing buffer _anyway_, we could even fake the surface lock + // and lock it here (if it doesn't influence timings). + self.present() } } diff --git a/src/lib.rs b/src/lib.rs index 8b569c7..0c1e484 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -195,13 +195,16 @@ impl HasWindowHandle for Surface /// Currently [`Buffer::present`] must block copying image data on: /// - Web /// - macOS +/// +/// Buffer copies an channel swizzling happen on: +/// - Android pub struct Buffer<'a, D, W> { buffer_impl: BufferDispatch<'a, D, W>, _marker: PhantomData<(Arc, Cell<()>)>, } impl<'a, D: HasDisplayHandle, W: HasWindowHandle> Buffer<'a, D, W> { - /// Is age is the number of frames ago this buffer was last presented. So if the value is + /// `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 /// buffer that has unspecified contents.