Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e0361625 authored by Andrew Walbran's avatar Andrew Walbran
Browse files

Add methods to lock and unlock HardwareBuffer.

Bug: 371874777
Test: atest libnativewindow_rs-internal_test
Change-Id: I1494f17720761db6dbc7ba63ba51c5ec91536f74
parent 84c4614f
Loading
Loading
Loading
Loading
+272 −4
Original line number Diff line number Diff line
@@ -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);
    }
}