diff options
-rw-r--r-- | Android.bp | 1 | ||||
-rw-r--r-- | libs/nativewindow/rust/src/lib.rs | 276 | ||||
-rw-r--r-- | services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp | 1 |
3 files changed, 274 insertions, 4 deletions
diff --git a/Android.bp b/Android.bp index 119c7ea5a4..ba55082248 100644 --- a/Android.bp +++ b/Android.bp @@ -42,6 +42,7 @@ cc_library_headers { export_include_dirs: [ "include/", ], + product_available: true, } ndk_headers { diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs index 014c912a67..9876362cec 100644 --- a/libs/nativewindow/rust/src/lib.rs +++ b/libs/nativewindow/rust/src/lib.rs @@ -31,11 +31,13 @@ use binder::{ }; use ffi::{ AHardwareBuffer, AHardwareBuffer_Desc, AHardwareBuffer_readFromParcel, - AHardwareBuffer_writeToParcel, + AHardwareBuffer_writeToParcel, ARect, }; +use std::ffi::c_void; use std::fmt::{self, Debug, Formatter}; -use std::mem::ManuallyDrop; -use std::ptr::{self, null_mut, NonNull}; +use std::mem::{forget, ManuallyDrop}; +use std::os::fd::{AsRawFd, BorrowedFd, FromRawFd, OwnedFd}; +use std::ptr::{self, null, null_mut, NonNull}; /// Wrapper around a C `AHardwareBuffer_Desc`. #[derive(Clone, Debug, PartialEq, Eq)] @@ -267,10 +269,141 @@ impl HardwareBuffer { rfu0: 0, rfu1: 0, }; - // SAFETY: neither the buffer nor AHardwareBuffer_Desc pointers will be null. + // SAFETY: The `AHardwareBuffer` pointer we wrap is always valid, and the + // AHardwareBuffer_Desc pointer is valid because it comes from a reference. unsafe { ffi::AHardwareBuffer_describe(self.0.as_ref(), &mut buffer_desc) }; HardwareBufferDescription(buffer_desc) } + + /// Locks the hardware buffer for direct CPU access. + /// + /// # Safety + /// + /// - If `fence` is `None`, the caller must ensure that all writes to the buffer have completed + /// before calling this function. + /// - If the buffer has `AHARDWAREBUFFER_FORMAT_BLOB`, multiple threads or process may lock the + /// buffer simultaneously, but the caller must ensure that they don't access it simultaneously + /// and break Rust's aliasing rules, like any other shared memory. + /// - Otherwise if `usage` includes `AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY` or + /// `AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN`, the caller must ensure that no other threads or + /// processes lock the buffer simultaneously for any usage. + /// - Otherwise, the caller must ensure that no other threads lock the buffer for writing + /// simultaneously. + /// - If `rect` is not `None`, the caller must not modify the buffer outside of that rectangle. + pub unsafe fn lock<'a>( + &'a self, + usage: AHardwareBuffer_UsageFlags, + fence: Option<BorrowedFd>, + rect: Option<&ARect>, + ) -> Result<HardwareBufferGuard<'a>, StatusCode> { + let fence = if let Some(fence) = fence { fence.as_raw_fd() } else { -1 }; + let rect = rect.map(ptr::from_ref).unwrap_or(null()); + let mut address = null_mut(); + // SAFETY: The `AHardwareBuffer` pointer we wrap is always valid, and the buffer address out + // pointer is valid because it comes from a reference. Our caller promises that writes have + // completed and there will be no simultaneous read/write locks. + let status = unsafe { + ffi::AHardwareBuffer_lock(self.0.as_ptr(), usage.0, fence, rect, &mut address) + }; + status_result(status)?; + Ok(HardwareBufferGuard { + buffer: self, + address: NonNull::new(address) + .expect("AHardwareBuffer_lock set a null outVirtualAddress"), + }) + } + + /// Locks the hardware buffer for direct CPU access, returning information about the bytes per + /// pixel and stride as well. + /// + /// # Safety + /// + /// - If `fence` is `None`, the caller must ensure that all writes to the buffer have completed + /// before calling this function. + /// - If the buffer has `AHARDWAREBUFFER_FORMAT_BLOB`, multiple threads or process may lock the + /// buffer simultaneously, but the caller must ensure that they don't access it simultaneously + /// and break Rust's aliasing rules, like any other shared memory. + /// - Otherwise if `usage` includes `AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY` or + /// `AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN`, the caller must ensure that no other threads or + /// processes lock the buffer simultaneously for any usage. + /// - Otherwise, the caller must ensure that no other threads lock the buffer for writing + /// simultaneously. + pub unsafe fn lock_and_get_info<'a>( + &'a self, + usage: AHardwareBuffer_UsageFlags, + fence: Option<BorrowedFd>, + rect: Option<&ARect>, + ) -> Result<LockedBufferInfo<'a>, StatusCode> { + let fence = if let Some(fence) = fence { fence.as_raw_fd() } else { -1 }; + let rect = rect.map(ptr::from_ref).unwrap_or(null()); + let mut address = null_mut(); + let mut bytes_per_pixel = 0; + let mut stride = 0; + // SAFETY: The `AHardwareBuffer` pointer we wrap is always valid, and the various out + // pointers are valid because they come from references. Our caller promises that writes have + // completed and there will be no simultaneous read/write locks. + let status = unsafe { + ffi::AHardwareBuffer_lockAndGetInfo( + self.0.as_ptr(), + usage.0, + fence, + rect, + &mut address, + &mut bytes_per_pixel, + &mut stride, + ) + }; + status_result(status)?; + Ok(LockedBufferInfo { + guard: HardwareBufferGuard { + buffer: self, + address: NonNull::new(address) + .expect("AHardwareBuffer_lockAndGetInfo set a null outVirtualAddress"), + }, + bytes_per_pixel: bytes_per_pixel as u32, + stride: stride as u32, + }) + } + + /// Unlocks the hardware buffer from direct CPU access. + /// + /// Must be called after all changes to the buffer are completed by the caller. This will block + /// until the unlocking is complete and the buffer contents are updated. + fn unlock(&self) -> Result<(), StatusCode> { + // SAFETY: The `AHardwareBuffer` pointer we wrap is always valid. + let status = unsafe { ffi::AHardwareBuffer_unlock(self.0.as_ptr(), null_mut()) }; + status_result(status)?; + Ok(()) + } + + /// Unlocks the hardware buffer from direct CPU access. + /// + /// Must be called after all changes to the buffer are completed by the caller. + /// + /// This may not block until all work is completed, but rather will return a file descriptor + /// which will be signalled once the unlocking is complete and the buffer contents is updated. + /// If `Ok(None)` is returned then unlocking has already completed and no further waiting is + /// necessary. The file descriptor may be passed to a subsequent call to [`Self::lock`]. + pub fn unlock_with_fence( + &self, + guard: HardwareBufferGuard, + ) -> Result<Option<OwnedFd>, StatusCode> { + // Forget the guard so that its `Drop` implementation doesn't try to unlock the + // HardwareBuffer again. + forget(guard); + + let mut fence = -2; + // SAFETY: The `AHardwareBuffer` pointer we wrap is always valid. + let status = unsafe { ffi::AHardwareBuffer_unlock(self.0.as_ptr(), &mut fence) }; + let fence = if fence < 0 { + None + } else { + // SAFETY: `AHardwareBuffer_unlock` gives us ownership of the fence file descriptor. + Some(unsafe { OwnedFd::from_raw_fd(fence) }) + }; + status_result(status)?; + Ok(fence) + } } impl Drop for HardwareBuffer { @@ -346,6 +479,37 @@ unsafe impl Send for HardwareBuffer {} // according to the docs on the underlying gralloc calls) unsafe impl Sync for HardwareBuffer {} +/// A guard for when a `HardwareBuffer` is locked. +/// +/// The `HardwareBuffer` will be unlocked when this is dropped, or may be unlocked via +/// [`HardwareBuffer::unlock_with_fence`]. +#[derive(Debug)] +pub struct HardwareBufferGuard<'a> { + buffer: &'a HardwareBuffer, + /// The address of the buffer in memory. + pub address: NonNull<c_void>, +} + +impl<'a> Drop for HardwareBufferGuard<'a> { + fn drop(&mut self) { + self.buffer + .unlock() + .expect("Failed to unlock HardwareBuffer when dropping HardwareBufferGuard"); + } +} + +/// A guard for when a `HardwareBuffer` is locked, with additional information about the number of +/// bytes per pixel and stride. +#[derive(Debug)] +pub struct LockedBufferInfo<'a> { + /// The locked buffer guard. + pub guard: HardwareBufferGuard<'a>, + /// The number of bytes used for each pixel in the buffer. + pub bytes_per_pixel: u32, + /// The stride in bytes between rows in the buffer. + pub stride: u32, +} + #[cfg(test)] mod test { use super::*; @@ -499,4 +663,108 @@ mod test { assert_eq!(buffer.description(), buffer_description); assert_eq!(buffer2.description(), buffer_description); } + + #[test] + fn lock() { + let buffer = HardwareBuffer::new(&HardwareBufferDescription::new( + 1024, + 512, + 1, + AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, + AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, + 0, + )) + .expect("Failed to create buffer"); + + // SAFETY: No other threads or processes have access to the buffer. + let guard = unsafe { + buffer.lock( + AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, + None, + None, + ) + } + .unwrap(); + + drop(guard); + } + + #[test] + fn lock_with_rect() { + let buffer = HardwareBuffer::new(&HardwareBufferDescription::new( + 1024, + 512, + 1, + AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, + AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, + 0, + )) + .expect("Failed to create buffer"); + let rect = ARect { left: 10, right: 20, top: 35, bottom: 45 }; + + // SAFETY: No other threads or processes have access to the buffer. + let guard = unsafe { + buffer.lock( + AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, + None, + Some(&rect), + ) + } + .unwrap(); + + drop(guard); + } + + #[test] + fn unlock_with_fence() { + let buffer = HardwareBuffer::new(&HardwareBufferDescription::new( + 1024, + 512, + 1, + AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, + AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, + 0, + )) + .expect("Failed to create buffer"); + + // SAFETY: No other threads or processes have access to the buffer. + let guard = unsafe { + buffer.lock( + AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, + None, + None, + ) + } + .unwrap(); + + buffer.unlock_with_fence(guard).unwrap(); + } + + #[test] + fn lock_with_info() { + const WIDTH: u32 = 1024; + let buffer = HardwareBuffer::new(&HardwareBufferDescription::new( + WIDTH, + 512, + 1, + AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, + AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, + 0, + )) + .expect("Failed to create buffer"); + + // SAFETY: No other threads or processes have access to the buffer. + let info = unsafe { + buffer.lock_and_get_info( + AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, + None, + None, + ) + } + .unwrap(); + + assert_eq!(info.bytes_per_pixel, 4); + assert_eq!(info.stride, WIDTH * 4); + drop(info); + } } diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp index dac9265b71..fae236dfbb 100644 --- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp +++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp @@ -158,6 +158,7 @@ public: a.presentTime == b.presentTime; } + NO_THREAD_SAFETY_ANALYSIS const std::map<int64_t, TimelineItem>& getPredictions() const { return mTokenManager->mPredictions; } |