-
Notifications
You must be signed in to change notification settings - Fork 69
android: Make the backend zero-copy #331
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
On macOS, the locking/unlocking operation that So I don't think there are other platforms where this is a problem, Android is a bit special in "managing" the buffer(s) for us like this. Maybe an option would be to store the locked buffer on the surface? Something like: struct AndroidImpl {
native_window: NativeWindow,
in_progress_buffer: Option<NativeWindowBufferLockGuard<'static>>, // + unsafe magic
}
fn buffer_mut() {
if let Some(native_window_buffer) = self.in_progress_buffer {
return BufferImpl { native_window_buffer };
}
let native_window_buffer = self.native_window.lock(None)?;
BufferImpl { native_window_buffer, surface_in_progress_buffer: &mut self.in_progress_buffer }
}
impl Drop for BufferImpl {
fn drop(&mut self) {
let buffer = self.native_window_buffer.take();
*self.surface_in_progress_buffer = Some(buffer);
}
}That would allow the following to work the same as on other platforms: let mut buffer = surface.buffer_mut();
buffer.pixels().fill(Pixel::rgb(0, 0, 0)); // Clear
drop(buffer);
let mut buffer = surface.buffer_mut();
draw(&mut buffer);
buffer.present(); |
| // TODO: Validate that Android actually initializes (possibly with garbage) the buffer, or | ||
| // let softbuffer initialize it, or return MaybeUninit<Pixel>? | ||
| // i.e. consider age(). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As long as android is using a swap chain of two or three buffers and we can get the buffer age, it probably makes the most sense for softbuffer to zero-initialize the buffers, if Android doesn't already do that.
It shouldn't really add any meaningful cost to zero each buffer once.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can somehow infer the amount of buffers with ANativeWindow_getBuffersDataSpace?
But I'd also be fine with zero-initializing in buffer_mut for now, and coming back to this later, this PR will be an improvement anyhow.
…d via `BufferImpl`
|
@madsmtm something like that would work. I dropped the Unfortunately that requires various unwraps (like your suggested |
madsmtm
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately that requires various unwraps
I'm totally fine with a bit of unwrapping, especially after #313, where we don't need to make pixels_mut zero-cost.
should not be susceptible to
std::mem::forget()scenarios (which are safe) where unlock is not called andAndroidImpl::in_progess_bufferremainsNone?
I don't think my solution would've caused unsoundness either if you forget the buffer? It'd just cause a panic because you'd try to lock the buffer twice.
Anyhow, forgetting the buffer is definitely unsupported, as long as things are sound I don't care what the behavior of it is - I'll leave that up to whatever is easiest for you to maintain.
| let native_window_buffer: NativeWindowBufferLockGuard<'static> = | ||
| unsafe { std::mem::transmute(native_window_buffer) }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: When transmuting, I prefer:
| let native_window_buffer: NativeWindowBufferLockGuard<'static> = | |
| unsafe { std::mem::transmute(native_window_buffer) }; | |
| let native_window_buffer = unsafe { | |
| std::mem::transmute::< | |
| NativeWindowBufferLockGuard<'_>, | |
| NativeWindowBufferLockGuard<'static>, | |
| >(native_window_buffer) | |
| }; |
To make it be a compile-time error if we made a mistake and forgot to unwrap the error from ANativeWindow::lock.
WIP but tested change that closes #318.
Still need to discuss how to handle
MaybeUninit, and the now side-effect presenting in-progress modifications topixels_mut()if the caller ended up dropping theirBufferhalf-way through, which is an unfortunate caveat of having a locked buffer for the surface that unlocks and presents on drop. The turning point is that it's not allowed tolock()a surface twice beforeunlock()(ing) and inherently presenting it.Are other platforms affected by such a lock-unlock kind of API? As hinted in #318
ASurfaceControl+ASurfaceTransaction+AHardwareBuffercompletely obviate this issue, but that has very high Android requirements (the API's are there for a while, but I seem to have been the first one ever using it on the rootSurface, the one you get fromNativeActivity, and it didn't work until a bug report and fix since Android 15 (API 35)).