Skip to content

Commit a16fb9a

Browse files
MarijnS95notgull
authored andcommitted
android: Implement format swizzle via buffer copies
1 parent 9e3c594 commit a16fb9a

File tree

3 files changed

+85
-63
lines changed

3 files changed

+85
-63
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ log = "0.4.17"
2929
raw_window_handle = { package = "raw-window-handle", version = "0.6", features = ["std"] }
3030

3131
[target.'cfg(target_os = "android")'.dependencies]
32+
bytemuck = "1.12.3"
3233
ndk = "0.9.0"
3334

3435
[target.'cfg(all(unix, not(any(target_vendor = "apple", target_os = "android", target_os = "redox"))))'.dependencies]

src/backends/android.rs

+80-62
Original file line numberDiff line numberDiff line change
@@ -7,73 +7,65 @@ use ndk::{
77
hardware_buffer_format::HardwareBufferFormat,
88
native_window::{NativeWindow, NativeWindowBufferLockGuard},
99
};
10+
#[cfg(doc)]
11+
use raw_window_handle::AndroidNdkWindowHandle;
1012
use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawWindowHandle};
1113

1214
use crate::error::InitError;
13-
use crate::{Rect, SoftBufferError};
15+
use crate::{BufferInterface, Rect, SoftBufferError, SurfaceInterface};
1416

1517
/// The handle to a window for software buffering.
16-
pub struct AndroidImpl<D: ?Sized, W: ?Sized> {
18+
pub struct AndroidImpl<D, W> {
1719
native_window: NativeWindow,
18-
19-
_display: PhantomData<D>,
20-
21-
/// The pointer to the window object.
22-
///
23-
/// This is pretty useless because it gives us a pointer to [`NativeWindow`] that we have to increase the refcount on.
24-
/// Alternatively we can use [`NativeWindow::from_ptr()`] wrapped in [`std::mem::ManuallyDrop`]
2520
window: W,
21+
_display: PhantomData<D>,
2622
}
2723

28-
// TODO: Current system doesn't require a trait to be implemented here, even though it exists.
29-
impl<D: HasDisplayHandle, W: HasWindowHandle> AndroidImpl<D, W> {
24+
impl<D: HasDisplayHandle, W: HasWindowHandle> SurfaceInterface<D, W> for AndroidImpl<D, W> {
25+
type Context = D;
26+
type Buffer<'a>
27+
= BufferImpl<'a, D, W>
28+
where
29+
Self: 'a;
30+
3031
/// Create a new [`AndroidImpl`] from an [`AndroidNdkWindowHandle`].
31-
///
32-
/// # Safety
33-
///
34-
/// The [`AndroidNdkWindowHandle`] must be a valid window handle.
35-
// TODO: That's lame, why can't we get an AndroidNdkWindowHandle directly here
36-
pub(crate) fn new(window: W, _display: &D) -> Result<Self, InitError<W>> {
37-
// Get the raw Android window (surface).
32+
fn new(window: W, _display: &Self::Context) -> Result<Self, InitError<W>> {
3833
let raw = window.window_handle()?.as_raw();
3934
let RawWindowHandle::AndroidNdk(a) = raw else {
4035
return Err(InitError::Unsupported(window));
4136
};
4237

4338
// Acquire a new owned reference to the window, that will be freed on drop.
39+
// SAFETY: We have confirmed that the window handle is valid.
4440
let native_window = unsafe { NativeWindow::clone_from_ptr(a.a_native_window.cast()) };
4541

4642
Ok(Self {
4743
native_window,
48-
// _display: DisplayHandle::borrow_raw(raw_window_handle::RawDisplayHandle::Android(
49-
// AndroidDisplayHandle,
50-
// )),
5144
_display: PhantomData,
5245
window,
5346
})
5447
}
5548

5649
#[inline]
57-
pub fn window(&self) -> &W {
50+
fn window(&self) -> &W {
5851
&self.window
5952
}
6053

6154
/// Also changes the pixel format to [`HardwareBufferFormat::R8G8B8A8_UNORM`].
62-
pub fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> {
55+
fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> {
6356
let (width, height) = (|| {
6457
let width = NonZeroI32::try_from(width).ok()?;
6558
let height = NonZeroI32::try_from(height).ok()?;
6659
Some((width, height))
6760
})()
6861
.ok_or(SoftBufferError::SizeOutOfRange { width, height })?;
6962

70-
// Do not change the format.
7163
self.native_window
7264
.set_buffers_geometry(
7365
width.into(),
7466
height.into(),
7567
// Default is typically R5G6B5 16bpp, switch to 32bpp
76-
Some(HardwareBufferFormat::R8G8B8A8_UNORM),
68+
Some(HardwareBufferFormat::R8G8B8X8_UNORM),
7769
)
7870
.map_err(|err| {
7971
SoftBufferError::PlatformError(
@@ -83,72 +75,98 @@ impl<D: HasDisplayHandle, W: HasWindowHandle> AndroidImpl<D, W> {
8375
})
8476
}
8577

86-
pub fn buffer_mut(&mut self) -> Result<BufferImpl<'_, D, W>, SoftBufferError> {
87-
let lock_guard = self.native_window.lock(None).map_err(|err| {
78+
fn buffer_mut(&mut self) -> Result<BufferImpl<'_, D, W>, SoftBufferError> {
79+
let native_window_buffer = self.native_window.lock(None).map_err(|err| {
8880
SoftBufferError::PlatformError(
8981
Some("Failed to lock ANativeWindow".to_owned()),
9082
Some(Box::new(err)),
9183
)
9284
})?;
9385

94-
assert_eq!(
95-
lock_guard.format().bytes_per_pixel(),
96-
Some(4),
97-
"Unexpected buffer format {:?}, please call .resize() first to change it to RGBA8888",
98-
lock_guard.format()
99-
);
86+
if !matches!(
87+
native_window_buffer.format(),
88+
// These are the only formats we support
89+
HardwareBufferFormat::R8G8B8A8_UNORM | HardwareBufferFormat::R8G8B8X8_UNORM
90+
) {
91+
return Err(SoftBufferError::PlatformError(
92+
Some(format!(
93+
"Unexpected buffer format {:?}, please call \
94+
.resize() first to change it to RGBx8888",
95+
native_window_buffer.format()
96+
)),
97+
None,
98+
));
99+
}
100+
101+
let buffer = vec![0; native_window_buffer.width() * native_window_buffer.height()];
100102

101-
Ok(BufferImpl(lock_guard, PhantomData, PhantomData))
103+
Ok(BufferImpl {
104+
native_window_buffer,
105+
buffer,
106+
marker: PhantomData,
107+
})
102108
}
103109

104110
/// Fetch the buffer from the window.
105-
pub fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
111+
fn fetch(&mut self) -> Result<Vec<u32>, SoftBufferError> {
106112
Err(SoftBufferError::Unimplemented)
107113
}
108114
}
109115

110-
pub struct BufferImpl<'a, D: ?Sized, W>(
111-
NativeWindowBufferLockGuard<'a>,
112-
PhantomData<&'a D>,
113-
PhantomData<&'a W>,
114-
);
116+
pub struct BufferImpl<'a, D: ?Sized, W> {
117+
native_window_buffer: NativeWindowBufferLockGuard<'a>,
118+
buffer: Vec<u32>,
119+
marker: PhantomData<(&'a D, &'a W)>,
120+
}
115121

116122
// TODO: Move to NativeWindowBufferLockGuard?
117123
unsafe impl<'a, D, W> Send for BufferImpl<'a, D, W> {}
118124

119-
impl<'a, D: HasDisplayHandle + ?Sized, W: HasWindowHandle> BufferImpl<'a, D, W> {
125+
impl<'a, D: HasDisplayHandle, W: HasWindowHandle> BufferInterface for BufferImpl<'a, D, W> {
120126
#[inline]
121-
pub fn pixels(&self) -> &[u32] {
122-
todo!()
123-
// unsafe {
124-
// std::slice::from_raw_parts(
125-
// self.0.bits().cast_const().cast(),
126-
// (self.0.stride() * self.0.height()) as usize,
127-
// )
128-
// }
127+
fn pixels(&self) -> &[u32] {
128+
&self.buffer
129129
}
130130

131131
#[inline]
132-
pub fn pixels_mut(&mut self) -> &mut [u32] {
133-
let bytes = self.0.bytes().expect("Nonplanar format");
134-
unsafe {
135-
std::slice::from_raw_parts_mut(
136-
bytes.as_mut_ptr().cast(),
137-
bytes.len() / std::mem::size_of::<u32>(),
138-
)
139-
}
132+
fn pixels_mut(&mut self) -> &mut [u32] {
133+
&mut self.buffer
140134
}
141135

142-
pub fn age(&self) -> u8 {
136+
#[inline]
137+
fn age(&self) -> u8 {
143138
0
144139
}
145140

146-
pub fn present(self) -> Result<(), SoftBufferError> {
147-
// Dropping the guard automatically unlocks and posts it
141+
// TODO: This function is pretty slow this way
142+
fn present(mut self) -> Result<(), SoftBufferError> {
143+
let input_lines = self.buffer.chunks(self.native_window_buffer.width());
144+
for (output, input) in self
145+
.native_window_buffer
146+
.lines()
147+
// Unreachable as we checked before that this is a valid, mappable format
148+
.unwrap()
149+
.zip(input_lines)
150+
{
151+
// .lines() removed the stride
152+
assert_eq!(output.len(), input.len() * 4);
153+
154+
for (i, pixel) in input.iter().enumerate() {
155+
// Swizzle colors from RGBX to BGR
156+
let [b, g, r, _] = pixel.to_le_bytes();
157+
output[i * 4].write(b);
158+
output[i * 4 + 1].write(g);
159+
output[i * 4 + 2].write(r);
160+
// TODO alpha?
161+
}
162+
}
148163
Ok(())
149164
}
150165

151-
pub fn present_with_damage(self, _damage: &[Rect]) -> Result<(), SoftBufferError> {
152-
Err(SoftBufferError::Unimplemented)
166+
fn present_with_damage(self, _damage: &[Rect]) -> Result<(), SoftBufferError> {
167+
// TODO: Android requires the damage rect _at lock time_
168+
// Since we're faking the backing buffer _anyway_, we could even fake the surface lock
169+
// and lock it here (if it doesn't influence timings).
170+
self.present()
153171
}
154172
}

src/lib.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -196,13 +196,16 @@ impl<D: HasDisplayHandle, W: HasWindowHandle> HasWindowHandle for Surface<D, W>
196196
/// - Web
197197
/// - AppKit
198198
/// - UIKit
199+
///
200+
/// Buffer copies an channel swizzling happen on:
201+
/// - Android
199202
pub struct Buffer<'a, D, W> {
200203
buffer_impl: BufferDispatch<'a, D, W>,
201204
_marker: PhantomData<(Arc<D>, Cell<()>)>,
202205
}
203206

204207
impl<D: HasDisplayHandle, W: HasWindowHandle> Buffer<'_, D, W> {
205-
/// Is age is the number of frames ago this buffer was last presented. So if the value is
208+
/// `age` is the number of frames ago this buffer was last presented. So if the value is
206209
/// `1`, it is the same as the last frame, and if it is `2`, it is the same as the frame
207210
/// before that (for backends using double buffering). If the value is `0`, it is a new
208211
/// buffer that has unspecified contents.

0 commit comments

Comments
 (0)