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
128
129
130
131
132
133
134
135
136
137
// 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 log::error;
use thiserror::Error;

use crate::bindings;
use crate::display::Display;
use crate::generic_value::GenericValue;
use crate::va_check;
use crate::GenericValueError;
use crate::VaError;

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

#[derive(Debug, Error)]
pub enum QuerySurfaceAttributesError {
    #[error("error while calling vaQuerySurfaceAttributes: {0}")]
    VaError(#[from] VaError),
    #[error("error while converting attribute: {0}")]
    GenericValueError(#[from] GenericValueError),
}

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, VaError> {
        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.
        va_check(unsafe {
            bindings::vaCreateConfig(
                display.handle(),
                profile,
                entrypoint,
                attrs.as_mut_ptr(),
                attrs.len() as i32,
                &mut config_id,
            )
        })?;

        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>, VaError> {
        // 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;
        va_check(unsafe {
            bindings::vaQuerySurfaceAttributes(
                self.display.handle(),
                self.id,
                std::ptr::null_mut(),
                &attrs_len as *const _ as *mut std::os::raw::c_uint,
            )
        })?;

        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.
        va_check(unsafe {
            bindings::vaQuerySurfaceAttributes(
                self.display.handle(),
                self.id,
                attrs.as_mut_ptr(),
                &attrs_len as *const _ as *mut std::os::raw::c_uint,
            )
        })?;

        // 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>, QuerySurfaceAttributesError> {
        let surface_attributes = self.query_surface_attributes()?;

        surface_attributes
            .into_iter()
            .filter(|attr| attr.type_ == attr_type)
            .map(|attr| {
                GenericValue::try_from(attr.value)
                    .map_err(QuerySurfaceAttributesError::GenericValueError)
            })
            .collect()
    }
}

impl Drop for Config {
    fn drop(&mut self) {
        // Safe because `self` represents a valid Config.
        let status = va_check(unsafe { bindings::vaDestroyConfig(self.display.handle(), self.id) });

        if status.is_err() {
            error!("vaDestroyConfig failed: {}", status.unwrap_err());
        }
    }
}