start work on nihed-cros-libva
[nihav-player.git] / nihed-cros-libva / src / surface.rs
diff --git a/nihed-cros-libva/src/surface.rs b/nihed-cros-libva/src/surface.rs
new file mode 100644 (file)
index 0000000..8c626ce
--- /dev/null
@@ -0,0 +1,142 @@
+// 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::rc::Rc;
+
+use anyhow::Result;
+
+use crate::bindings;
+use crate::display::Display;
+use crate::status::Status;
+use crate::UsageHint;
+
+/// An owned VA surface that is tied to the lifetime of a particular VADisplay
+pub struct Surface {
+    display: Rc<Display>,
+    id: bindings::VASurfaceID,
+    width: u32,
+    height: u32,
+}
+
+impl Surface {
+    /// Create `Surfaces` by wrapping around a `vaCreateSurfaces` call. This is just a helper for
+    /// [`Display::create_surfaces`].
+    pub(crate) fn new(
+        display: Rc<Display>,
+        rt_format: u32,
+        va_fourcc: Option<u32>,
+        width: u32,
+        height: u32,
+        usage_hint: Option<UsageHint>,
+        num_surfaces: u32,
+    ) -> Result<Vec<Self>> {
+        let mut attrs = vec![];
+
+        if let Some(usage_hint) = usage_hint {
+            let attr = bindings::VASurfaceAttrib {
+                type_: bindings::VASurfaceAttribType::VASurfaceAttribUsageHint,
+                flags: bindings::constants::VA_SURFACE_ATTRIB_SETTABLE,
+                value: bindings::VAGenericValue {
+                    type_: bindings::VAGenericValueType::VAGenericValueTypeInteger,
+                    value: bindings::_VAGenericValue__bindgen_ty_1 {
+                        i: usage_hint.bits() as i32,
+                    },
+                },
+            };
+
+            attrs.push(attr);
+        }
+
+        if let Some(fourcc) = va_fourcc {
+            let attr = bindings::VASurfaceAttrib {
+                type_: bindings::VASurfaceAttribType::VASurfaceAttribPixelFormat,
+                flags: bindings::constants::VA_DISPLAY_ATTRIB_SETTABLE,
+                value: bindings::VAGenericValue {
+                    type_: bindings::VAGenericValueType::VAGenericValueTypeInteger,
+                    value: bindings::_VAGenericValue__bindgen_ty_1 { i: fourcc as i32 },
+                },
+            };
+
+            attrs.push(attr);
+        }
+
+        let mut surfaces = Vec::with_capacity(num_surfaces as usize);
+
+        // Safe because `self` represents a valid VADisplay. The `surface` and `attrs` vectors are
+        // properly initialized and valid sizes are passed to the C function, so it is impossible to
+        // write past the end of their storage by mistake.
+        Status(unsafe {
+            bindings::vaCreateSurfaces(
+                display.handle(),
+                rt_format,
+                width,
+                height,
+                surfaces.as_mut_ptr(),
+                num_surfaces,
+                attrs.as_mut_ptr(),
+                attrs.len() as u32,
+            )
+        })
+        .check()?;
+
+        // Safe because the C function will have written to exactly `num_surfaces` entries, which is
+        // known to be within the vector's capacity.
+        unsafe {
+            surfaces.set_len(num_surfaces as usize);
+        }
+
+        let va_surfaces = surfaces
+            .iter()
+            .map(|&id| Self {
+                display: Rc::clone(&display),
+                id,
+                width,
+                height,
+            })
+            .collect();
+
+        Ok(va_surfaces)
+    }
+
+    /// Blocks until all pending operations on the render target have been completed. Upon return it
+    /// is safe to use the render target for a different picture.
+    pub fn sync(&self) -> Result<()> {
+        // Safe because `self` represents a valid VASurface.
+        Status(unsafe { bindings::vaSyncSurface(self.display.handle(), self.id) }).check()
+    }
+
+    /// Convenience function to return a VASurfaceID vector. Useful to interface with the C API
+    /// where a surface array might be needed.
+    pub fn as_id_vec(surfaces: &[Self]) -> Vec<bindings::VASurfaceID> {
+        surfaces.iter().map(|surface| surface.id).collect()
+    }
+
+    /// Wrapper over `vaQuerySurfaceStatus` to find out any pending ops on the render target.
+    pub fn query_status(&self) -> Result<bindings::VASurfaceStatus::Type> {
+        let mut status: bindings::VASurfaceStatus::Type = 0;
+        // Safe because `self` represents a valid VASurface.
+        Status(unsafe {
+            bindings::vaQuerySurfaceStatus(self.display.handle(), self.id, &mut status)
+        })
+        .check()?;
+        Ok(status)
+    }
+
+    /// Returns the ID of this surface.
+    pub fn id(&self) -> bindings::VASurfaceID {
+        self.id
+    }
+
+    /// Returns the dimensions of this surface.
+    pub fn size(&self) -> (u32, u32) {
+        (self.width, self.height)
+    }
+}
+
+impl Drop for Surface {
+    fn drop(&mut self) {
+        // Safe because `self` represents a valid VASurface.
+        unsafe { bindings::vaDestroySurfaces(self.display.handle(), &mut self.id, 1) };
+    }
+}