libva 0.1.4

Safe bindings over libva
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::sync::Arc;

use crate::Display;
use crate::Surface;
use crate::SurfaceMemoryDescriptor;
use crate::VaError;
use crate::bindings;
use crate::va_check;

/// Wrapper around `VAImage` that is tied to the lifetime of a given `Picture`.
///
/// An image is used to either get the surface data to client memory, or to copy image data in
/// client memory to a surface.
pub struct Image<'a> {
    /// The display from which the image was created, so we can unmap it upon destruction.
    display: Arc<Display>,
    /// The `VAImage` returned by libva.
    image: bindings::VAImage,
    /// The mapped surface data.
    ///
    /// The lifetime also allows us to ensure that the `Surface` we have been created from
    /// does not drop or get changed while we exist.
    data: &'a mut [u8],
    /// Whether the image was derived using the `vaDeriveImage` API or created using the
    /// `vaCreateImage` API.
    derived: bool,
    /// The display resolution requested by the client. The implementation is
    /// free to enlarge this value as needed. In any case, we guarantee that an
    /// image at least as large is returned.
    display_resolution: (u32, u32),
    /// Tracks whether the underlying data has possibly been written to, i.e. an encoder will create
    /// an image and map its buffer in order to write to it, so we must writeback later.
    dirty: bool,
    /// The ID of the `Surface` we have been created from.
    surface_id: u32,
}

impl<'a> Image<'a> {
    /// Helper method to map a `VAImage` using `vaMapBuffer` and return an `Image`.
    ///
    /// Returns an error if the mapping failed.
    fn new<D: SurfaceMemoryDescriptor>(
        surface: &'a Surface<D>,
        image: bindings::VAImage,
        derived: bool,
        display_resolution: (u32, u32),
    ) -> Result<Self, VaError> {
        let mut addr = std::ptr::null_mut();

        // Safe since `picture.inner.context` represents a valid `VAContext` and `image` has been
        // successfully created at this point.
        match va_check(unsafe {
            crate::va::vaMapBuffer(surface.display().handle(), image.buf, &mut addr)
        }) {
            Ok(_) => {
                // Assert that libva provided us with a coded resolution that is
                // at least as large as `display_resolution`.
                assert!(u32::from(image.width) >= display_resolution.0);
                assert!(u32::from(image.height) >= display_resolution.1);

                // Safe since `addr` points to data mapped onto our address space since we called
                // `vaMapBuffer` above, which also guarantees that the data is valid for
                // `image.data_size`.
                let data =
                    unsafe { std::slice::from_raw_parts_mut(addr as _, image.data_size as usize) };
                Ok(Image {
                    display: Arc::clone(surface.display()),
                    image,
                    data,
                    derived,
                    display_resolution,
                    dirty: false,
                    surface_id: surface.id(),
                })
            }
            Err(e) => {
                // Safe because `picture.inner.context` represents a valid `VAContext` and `image`
                // represents a valid `VAImage`.
                unsafe {
                    crate::va::vaDestroyImage(surface.display().handle(), image.image_id);
                }

                Err(e)
            }
        }
    }

    /// Create a new derived image from this `surface` using `vaDeriveImage`.
    ///
    /// Derived images are a direct view (i.e. without any copy) on the buffer content of
    /// `surface`. On the other hand, not all `Surface`s can be derived.
    ///
    /// `visible_rect` is the visible rectangle inside `surface` that we want to access.
    pub fn derive_from<D: SurfaceMemoryDescriptor>(
        surface: &'a Surface<D>,
        visible_rect: (u32, u32),
    ) -> Result<Self, VaError> {
        // An all-zero byte-pattern is a valid initial value for `VAImage`.
        let mut image: bindings::VAImage = Default::default();

        // Safe because `self` has a valid display handle and ID.
        va_check(unsafe {
            crate::va::vaDeriveImage(surface.display().handle(), surface.id(), &mut image)
        })?;

        Self::new(surface, image, true, visible_rect)
    }

    /// Create new image from `surface` using `vaCreateImage` and `vaGetImage`.
    ///
    /// `visible_rect` is the visible rectangle inside `surface` that we want to access.
    ///
    /// The image will contain a copy of `surface`'s data' in the desired `format` and
    /// `coded_resolution`, meaning the data can be scaled if `coded_resolution` and `visible_rect`
    /// differ.
    pub fn create_from<D: SurfaceMemoryDescriptor>(
        surface: &'a Surface<D>,
        mut format: bindings::VAImageFormat,
        coded_resolution: (u32, u32),
        visible_rect: (u32, u32),
    ) -> Result<Image<'a>, VaError> {
        // An all-zero byte-pattern is a valid initial value for `VAImage`.
        let mut image: bindings::VAImage = Default::default();
        let dpy = surface.display().handle();

        // Safe because `dpy` is a valid display handle.
        va_check(unsafe {
            crate::va::vaCreateImage(
                dpy,
                &mut format,
                coded_resolution.0 as i32,
                coded_resolution.1 as i32,
                &mut image,
            )
        })?;

        // Safe because `dpy` is a valid display handle, `picture.surface` is a valid VASurface and
        // `image` is a valid `VAImage`.
        match va_check(unsafe {
            crate::va::vaGetImage(
                dpy,
                surface.id(),
                0,
                0,
                coded_resolution.0,
                coded_resolution.1,
                image.image_id,
            )
        }) {
            Ok(()) => Self::new(surface, image, false, visible_rect),

            Err(e) => {
                // Safe because `image` is a valid `VAImage`.
                unsafe {
                    crate::va::vaDestroyImage(dpy, image.image_id);
                }

                Err(e)
            }
        }
    }

    /// Get a reference to the underlying `VAImage` that describes this image.
    pub fn image(&self) -> &bindings::VAImage {
        &self.image
    }

    /// Returns whether this image is directly derived from its underlying `Picture`, as opposed to
    /// being a view/copy of said `Picture` in a guaranteed pixel format.
    pub fn is_derived(&self) -> bool {
        self.derived
    }

    /// Returns the display resolution as passed in by the client. This is a
    /// value that is less than or equal to the coded resolution.
    pub fn display_resolution(&self) -> (u32, u32) {
        self.display_resolution
    }

    /// Returns the coded resolution. This value can be larger than the value
    /// passed in when the image was created if the driver needs to.
    pub fn coded_resolution(&self) -> (u32, u32) {
        (self.image.width.into(), self.image.height.into())
    }
}

impl<'a> AsRef<[u8]> for Image<'a> {
    fn as_ref(&self) -> &[u8] {
        self.data
    }
}

impl<'a> AsMut<[u8]> for Image<'a> {
    fn as_mut(&mut self) -> &mut [u8] {
        self.dirty = true;
        self.data
    }
}

impl<'a> Drop for Image<'a> {
    fn drop(&mut self) {
        if !self.derived && self.dirty {
            // Safe because `picture.inner.context` represents a valid `VAContext`,
            // `picture.surface` represents a valid `VASurface` and `image` represents a valid
            // `VAImage`.
            unsafe {
                crate::va::vaPutImage(
                    self.display.handle(),
                    self.surface_id,
                    self.image.image_id,
                    0,
                    0,
                    self.image.width as u32,
                    self.image.height as u32,
                    0,
                    0,
                    self.image.width as u32,
                    self.image.height as u32,
                );
            }
        }

        unsafe {
            // Safe since the buffer is mapped in `Image::new`, so `self.image.buf` points to a
            // valid `VABufferID`.
            crate::va::vaUnmapBuffer(self.display.handle(), self.image.buf);
            // Safe since `self.image` represents a valid `VAImage`.
            crate::va::vaDestroyImage(self.display.handle(), self.image.image_id);
        }
    }
}