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

Commit 81bc772f authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Add methods to lock and unlock HardwareBuffer." into main

parents 84c4614f e0361625
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);
    }
}