1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// 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 log::error;

use crate::bindings;
use crate::display::Display;
use crate::generic_value::GenericValue;
use crate::status::Status;

/// A configuration for a given [`Display`].
pub struct Config {
    display: Rc<Display>,
    id: bindings::VAConfigID,
}

impl Config {
    /// Creates a Config by wrapping around the `vaCreateConfig` call. This is just a helper for
    /// [`Display::create_config`].
    pub(crate) fn new(
        display: Rc<Display>,
        mut attrs: Vec<bindings::VAConfigAttrib>,
        profile: bindings::VAProfile::Type,
        entrypoint: bindings::VAEntrypoint::Type,
    ) -> Result<Self> {
        let mut config_id = 0u32;

        // Safe because `self` represents a valid `VADisplay`.
        //
        // The `attrs` vector is also properly initialized and its actual size is passed to
        // `vaCreateConfig`, so it is impossible to write past the end of its storage by mistake.
        Status(unsafe {
            bindings::vaCreateConfig(
                display.handle(),
                profile,
                entrypoint,
                attrs.as_mut_ptr(),
                attrs.len() as i32,
                &mut config_id,
            )
        })
        .check()?;

        Ok(Self {
            display,
            id: config_id,
        })
    }

    /// Returns the ID of this config.
    pub(crate) fn id(&self) -> bindings::VAConfigID {
        self.id
    }

    // Queries surface attributes for this config.
    //
    // This function queries for all supported attributes for this configuration. In particular, if
    // the underlying hardware supports the creation of VA surfaces in various formats, then this
    // function will enumerate all pixel formats that are supported.
    fn query_surface_attributes(&mut self) -> Result<Vec<bindings::VASurfaceAttrib>> {
        // Safe because `self` represents a valid VAConfig. We first query how
        // much space is needed by the C API by passing in NULL in the first
        // call to `vaQuerySurfaceAttributes`.
        let attrs_len: std::os::raw::c_uint = 0;
        Status(unsafe {
            bindings::vaQuerySurfaceAttributes(
                self.display.handle(),
                self.id,
                std::ptr::null_mut(),
                &attrs_len as *const _ as *mut std::os::raw::c_uint,
            )
        })
        .check()?;

        let mut attrs = Vec::with_capacity(attrs_len as usize);
        // Safe because we allocate a vector with the required capacity as
        // returned by the initial call to vaQuerySurfaceAttributes. We then
        // pass a valid pointer to it.
        Status(unsafe {
            bindings::vaQuerySurfaceAttributes(
                self.display.handle(),
                self.id,
                attrs.as_mut_ptr(),
                &attrs_len as *const _ as *mut std::os::raw::c_uint,
            )
        })
        .check()?;

        // Safe because vaQuerySurfaceAttributes will have written to
        // exactly attrs_len entries in the vector.
        unsafe {
            attrs.set_len(attrs_len as usize);
        }

        Ok(attrs)
    }

    /// Query the surface attributes of type `attr_type`. The attribute may or may not be defined by
    /// the driver.
    pub fn query_surface_attributes_by_type(
        &mut self,
        attr_type: bindings::VASurfaceAttribType::Type,
    ) -> Result<Vec<GenericValue>> {
        let surface_attributes = self.query_surface_attributes()?;

        surface_attributes
            .into_iter()
            .filter(|attr| attr.type_ == attr_type)
            .map(|attrib| GenericValue::try_from(attrib.value))
            .collect()
    }
}

impl Drop for Config {
    fn drop(&mut self) {
        // Safe because `self` represents a valid Config.
        let status =
            Status(unsafe { bindings::vaDestroyConfig(self.display.handle(), self.id) }).check();
        if status.is_err() {
            error!("vaDestroyConfig failed: {}", status.unwrap_err());
        }
    }
}