Skip to main content

pito_codecs/gpu/vaapi/
va.rs

1use std::ffi::{CStr, c_int, c_uint, c_void};
2use std::fs::{File, OpenOptions};
3use std::os::fd::{AsRawFd, FromRawFd, OwnedFd};
4use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
5use std::sync::{Arc, Mutex, OnceLock, PoisonError};
6
7use libloading::Library;
8
9use super::ffi::{self, VAConfigAttrib, VAImage, VAImageFormat, VASurfaceID};
10use super::host::HostBuffer;
11use super::{Accel, LAG, SLACK};
12use crate::gpu::biplanar::{self, Biplanar, Geometry};
13use crate::media::{Frame, Rgba};
14
15struct Api {
16    _va: Library,
17    _drm: Library,
18    initialize: ffi::vaInitialize,
19    terminate: ffi::vaTerminate,
20    error_str: ffi::vaErrorStr,
21    vendor_string: ffi::vaQueryVendorString,
22    set_info_callback: ffi::vaSetInfoCallback,
23    set_error_callback: ffi::vaSetErrorCallback,
24    get_config_attributes: ffi::vaGetConfigAttributes,
25    create_config: ffi::vaCreateConfig,
26    destroy_config: ffi::vaDestroyConfig,
27    create_surfaces: ffi::vaCreateSurfaces,
28    destroy_surfaces: ffi::vaDestroySurfaces,
29    create_context: ffi::vaCreateContext,
30    destroy_context: ffi::vaDestroyContext,
31    create_buffer: ffi::vaCreateBuffer,
32    destroy_buffer: ffi::vaDestroyBuffer,
33    begin_picture: ffi::vaBeginPicture,
34    render_picture: ffi::vaRenderPicture,
35    end_picture: ffi::vaEndPicture,
36    sync_surface: ffi::vaSyncSurface,
37    create_image: ffi::vaCreateImage,
38    destroy_image: ffi::vaDestroyImage,
39    get_image: ffi::vaGetImage,
40    derive_image: ffi::vaDeriveImage,
41    export_surface: ffi::vaExportSurfaceHandle,
42    put_image: ffi::vaPutImage,
43    map_buffer: ffi::vaMapBuffer,
44    unmap_buffer: ffi::vaUnmapBuffer,
45    get_display_drm: ffi::vaGetDisplayDRM,
46}
47
48static API: OnceLock<Result<Api, String>> = OnceLock::new();
49
50fn api() -> Result<&'static Api, String> {
51    API.get_or_init(load).as_ref().map_err(Clone::clone)
52}
53
54fn load() -> Result<Api, String> {
55    let va = unsafe { Library::new("libva.so.2") }.map_err(|e| format!("libva.so.2: {e}"))?;
56    let drm =
57        unsafe { Library::new("libva-drm.so.2") }.map_err(|e| format!("libva-drm.so.2: {e}"))?;
58    macro_rules! sym {
59        ($lib:expr, $name:ident) => {{
60            let s = unsafe { $lib.get::<ffi::$name>(concat!(stringify!($name), "\0").as_bytes()) };
61            *s.map_err(|e| format!("{}: {e}", stringify!($name)))?
62        }};
63    }
64    Ok(Api {
65        initialize: sym!(va, vaInitialize),
66        terminate: sym!(va, vaTerminate),
67        error_str: sym!(va, vaErrorStr),
68        vendor_string: sym!(va, vaQueryVendorString),
69        set_info_callback: sym!(va, vaSetInfoCallback),
70        set_error_callback: sym!(va, vaSetErrorCallback),
71        get_config_attributes: sym!(va, vaGetConfigAttributes),
72        create_config: sym!(va, vaCreateConfig),
73        destroy_config: sym!(va, vaDestroyConfig),
74        create_surfaces: sym!(va, vaCreateSurfaces),
75        destroy_surfaces: sym!(va, vaDestroySurfaces),
76        create_context: sym!(va, vaCreateContext),
77        destroy_context: sym!(va, vaDestroyContext),
78        create_buffer: sym!(va, vaCreateBuffer),
79        destroy_buffer: sym!(va, vaDestroyBuffer),
80        begin_picture: sym!(va, vaBeginPicture),
81        render_picture: sym!(va, vaRenderPicture),
82        end_picture: sym!(va, vaEndPicture),
83        sync_surface: sym!(va, vaSyncSurface),
84        create_image: sym!(va, vaCreateImage),
85        destroy_image: sym!(va, vaDestroyImage),
86        get_image: sym!(va, vaGetImage),
87        derive_image: sym!(va, vaDeriveImage),
88        export_surface: sym!(va, vaExportSurfaceHandle),
89        put_image: sym!(va, vaPutImage),
90        map_buffer: sym!(va, vaMapBuffer),
91        unmap_buffer: sym!(va, vaUnmapBuffer),
92        get_display_drm: sym!(drm, vaGetDisplayDRM),
93        _va: va,
94        _drm: drm,
95    })
96}
97
98pub struct Display {
99    api: &'static Api,
100    dpy: ffi::VADisplay,
101    vendor: String,
102    _device: File,
103}
104
105unsafe impl Send for Display {}
106unsafe impl Sync for Display {}
107
108static SHARED: Mutex<Option<Arc<Display>>> = Mutex::new(None);
109
110fn shared() -> Result<Arc<Display>, String> {
111    let mut shared = SHARED.lock().unwrap_or_else(PoisonError::into_inner);
112    if let Some(d) = shared.as_ref() {
113        return Ok(d.clone());
114    }
115    let d = Arc::new(Display::open()?);
116    *shared = Some(d.clone());
117    Ok(d)
118}
119
120fn replace_shared(old: &Arc<Display>, new: Arc<Display>) {
121    let mut shared = SHARED.lock().unwrap_or_else(PoisonError::into_inner);
122    if shared.as_ref().is_none_or(|d| Arc::ptr_eq(d, old)) {
123        *shared = Some(new);
124    }
125}
126
127fn with_display<T>(make: impl Fn(Arc<Display>) -> Result<T, String>) -> Result<T, String> {
128    let display = shared()?;
129    match make(display.clone()) {
130        Ok(s) => Ok(s),
131        Err(first) => {
132            let fresh =
133                Arc::new(Display::open().map_err(|e| format!("{first}; a new display: {e}"))?);
134            let s = make(fresh.clone()).map_err(|_| first)?;
135            replace_shared(&display, fresh);
136            Ok(s)
137        }
138    }
139}
140
141impl Display {
142    pub fn open() -> Result<Self, String> {
143        let api = api()?;
144        let mut why = "no DRM render node (/dev/dri/renderD*)".to_string();
145        for n in 128..144 {
146            let path = format!("/dev/dri/renderD{n}");
147            let Ok(file) = OpenOptions::new().read(true).write(true).open(&path) else {
148                continue;
149            };
150            match Self::init(api, file) {
151                Ok(d) => return Ok(d),
152                Err(e) => why = format!("{path}: {e}"),
153            }
154        }
155        Err(why)
156    }
157
158    fn init(api: &'static Api, device: File) -> Result<Self, String> {
159        let dpy = unsafe { (api.get_display_drm)(device.as_raw_fd()) };
160        if dpy.is_null() {
161            return Err("vaGetDisplayDRM failed".into());
162        }
163        let mut d = Self {
164            api,
165            dpy,
166            vendor: String::new(),
167            _device: device,
168        };
169        unsafe {
170            (api.set_info_callback)(dpy, None, std::ptr::null_mut());
171            (api.set_error_callback)(dpy, None, std::ptr::null_mut());
172        }
173        let (mut major, mut minor): (c_int, c_int) = (0, 0);
174        let st = unsafe { (api.initialize)(dpy, &mut major, &mut minor) };
175        d.check(st, "vaInitialize")?;
176        let vendor = unsafe { (api.vendor_string)(dpy) };
177        if !vendor.is_null() {
178            d.vendor = unsafe { CStr::from_ptr(vendor) }
179                .to_string_lossy()
180                .into_owned();
181        }
182        Ok(d)
183    }
184
185    pub fn vendor(&self) -> &str {
186        &self.vendor
187    }
188
189    fn check(&self, st: ffi::VAStatus, what: &str) -> Result<(), String> {
190        if st == ffi::VA_STATUS_SUCCESS {
191            return Ok(());
192        }
193        let text = unsafe { (self.api.error_str)(st) };
194        let text = if text.is_null() {
195            String::new()
196        } else {
197            unsafe { CStr::from_ptr(text) }
198                .to_string_lossy()
199                .into_owned()
200        };
201        Err(format!("{what}: {text} ({st:#x})"))
202    }
203
204    pub fn attributes(
205        &self,
206        profile: ffi::VAProfile,
207        entrypoint: ffi::VAEntrypoint,
208        types: &[ffi::VAConfigAttribType],
209    ) -> Result<Vec<u32>, String> {
210        let mut attrs: Vec<VAConfigAttrib> = types
211            .iter()
212            .map(|&type_| VAConfigAttrib { type_, value: 0 })
213            .collect();
214        let n = c_int::try_from(attrs.len()).map_err(|_| "too many attributes")?;
215        let st = unsafe {
216            (self.api.get_config_attributes)(self.dpy, profile, entrypoint, attrs.as_mut_ptr(), n)
217        };
218        self.check(st, "vaGetConfigAttributes")?;
219        Ok(attrs.into_iter().map(|a| a.value).collect())
220    }
221
222    fn create_config(
223        &self,
224        profile: ffi::VAProfile,
225        entrypoint: ffi::VAEntrypoint,
226        attrs: &mut [VAConfigAttrib],
227    ) -> Result<ffi::VAConfigID, String> {
228        let n = c_int::try_from(attrs.len()).map_err(|_| "too many attributes")?;
229        let mut id = ffi::VA_INVALID_ID;
230        let st = unsafe {
231            (self.api.create_config)(
232                self.dpy,
233                profile,
234                entrypoint,
235                attrs.as_mut_ptr(),
236                n,
237                &mut id,
238            )
239        };
240        self.check(st, "vaCreateConfig")?;
241        Ok(id)
242    }
243
244    fn create_surfaces(
245        &self,
246        rt_format: c_uint,
247        size: (u32, u32),
248        count: usize,
249    ) -> Result<Vec<VASurfaceID>, String> {
250        let count_c = c_uint::try_from(count).map_err(|_| "too many surfaces")?;
251        let mut surfaces = vec![ffi::VA_INVALID_SURFACE; count];
252        let st = unsafe {
253            (self.api.create_surfaces)(
254                self.dpy,
255                rt_format,
256                size.0,
257                size.1,
258                surfaces.as_mut_ptr(),
259                count_c,
260                std::ptr::null_mut(),
261                0,
262            )
263        };
264        self.check(st, "vaCreateSurfaces")?;
265        Ok(surfaces)
266    }
267
268    fn import(
269        &self,
270        rt_format: c_uint,
271        size: (u32, u32),
272        descriptor: &mut ffi::VADRMPRIMESurfaceDescriptor,
273    ) -> Result<VASurfaceID, String> {
274        let attribute = |type_, value| ffi::VASurfaceAttrib {
275            type_,
276            flags: ffi::VA_SURFACE_ATTRIB_SETTABLE,
277            value,
278        };
279        let mut attributes = [
280            attribute(
281                ffi::VASurfaceAttribMemoryType,
282                ffi::VAGenericValue {
283                    type_: ffi::VAGenericValueTypeInteger,
284                    value: ffi::VAGenericValueUnion {
285                        i: ffi::VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2 as c_int,
286                    },
287                },
288            ),
289            attribute(
290                ffi::VASurfaceAttribExternalBufferDescriptor,
291                ffi::VAGenericValue {
292                    type_: ffi::VAGenericValueTypePointer,
293                    value: ffi::VAGenericValueUnion {
294                        p: (descriptor as *mut ffi::VADRMPRIMESurfaceDescriptor).cast(),
295                    },
296                },
297            ),
298        ];
299        let mut surface = ffi::VA_INVALID_SURFACE;
300        let st = unsafe {
301            (self.api.create_surfaces)(
302                self.dpy,
303                rt_format,
304                size.0,
305                size.1,
306                &mut surface,
307                1,
308                attributes.as_mut_ptr().cast(),
309                attributes.len() as c_uint,
310            )
311        };
312        self.check(st, "vaCreateSurfaces (import)")?;
313        Ok(surface)
314    }
315
316    fn create_rgba(&self, size: (u32, u32)) -> Result<VASurfaceID, String> {
317        let mut attributes = [ffi::VASurfaceAttrib {
318            type_: ffi::VASurfaceAttribPixelFormat,
319            flags: ffi::VA_SURFACE_ATTRIB_SETTABLE,
320            value: ffi::VAGenericValue {
321                type_: ffi::VAGenericValueTypeInteger,
322                value: ffi::VAGenericValueUnion {
323                    i: ffi::VA_FOURCC_RGBA as c_int,
324                },
325            },
326        }];
327        let mut surface = ffi::VA_INVALID_SURFACE;
328        let st = unsafe {
329            (self.api.create_surfaces)(
330                self.dpy,
331                ffi::VA_RT_FORMAT_RGB32,
332                size.0,
333                size.1,
334                &mut surface,
335                1,
336                attributes.as_mut_ptr().cast(),
337                attributes.len() as c_uint,
338            )
339        };
340        self.check(st, "vaCreateSurfaces (RGBA)")?;
341        Ok(surface)
342    }
343
344    fn create_rgba_image(&self, size: (u32, u32)) -> Result<VAImage, String> {
345        let mut format = VAImageFormat {
346            fourcc: ffi::VA_FOURCC_RGBA,
347            byte_order: 1,
348            bits_per_pixel: 32,
349            depth: 32,
350            red_mask: 0x0000_00ff,
351            green_mask: 0x0000_ff00,
352            blue_mask: 0x00ff_0000,
353            alpha_mask: 0xff00_0000,
354            ..Default::default()
355        };
356        let mut image = VAImage {
357            image_id: ffi::VA_INVALID_ID,
358            buf: ffi::VA_INVALID_ID,
359            ..Default::default()
360        };
361        let (w, h) = (
362            c_int::try_from(size.0).map_err(|_| "picture too wide")?,
363            c_int::try_from(size.1).map_err(|_| "picture too tall")?,
364        );
365        let st = unsafe { (self.api.create_image)(self.dpy, &mut format, w, h, &mut image) };
366        self.check(st, "vaCreateImage (RGBA)")?;
367        Ok(image)
368    }
369
370    fn create_context(
371        &self,
372        config: ffi::VAConfigID,
373        size: (u32, u32),
374        targets: &mut [VASurfaceID],
375    ) -> Result<ffi::VAContextID, String> {
376        let (w, h) = (
377            c_int::try_from(size.0).map_err(|_| "picture too wide")?,
378            c_int::try_from(size.1).map_err(|_| "picture too tall")?,
379        );
380        let n = c_int::try_from(targets.len()).map_err(|_| "too many surfaces")?;
381        let mut context = ffi::VA_INVALID_ID;
382        let st = unsafe {
383            (self.api.create_context)(
384                self.dpy,
385                config,
386                w,
387                h,
388                ffi::VA_PROGRESSIVE,
389                targets.as_mut_ptr(),
390                n,
391                &mut context,
392            )
393        };
394        self.check(st, "vaCreateContext")?;
395        Ok(context)
396    }
397
398    fn buffer(
399        &self,
400        context: ffi::VAContextID,
401        ty: ffi::VABufferType,
402        data: *const c_void,
403        len: usize,
404    ) -> Result<ffi::VABufferID, String> {
405        let size = c_uint::try_from(len).map_err(|_| "buffer too large")?;
406        let mut id = ffi::VA_INVALID_ID;
407        let st = unsafe {
408            (self.api.create_buffer)(self.dpy, context, ty, size, 1, data.cast_mut(), &mut id)
409        };
410        self.check(st, "vaCreateBuffer")?;
411        Ok(id)
412    }
413
414    fn value_buffer<T>(
415        &self,
416        context: ffi::VAContextID,
417        ty: ffi::VABufferType,
418        value: &T,
419    ) -> Result<ffi::VABufferID, String> {
420        self.buffer(
421            context,
422            ty,
423            (value as *const T).cast(),
424            std::mem::size_of::<T>(),
425        )
426    }
427
428    fn destroy_buffers(&self, buffers: &[ffi::VABufferID]) {
429        for &id in buffers {
430            unsafe { (self.api.destroy_buffer)(self.dpy, id) };
431        }
432    }
433
434    fn render(
435        &self,
436        context: ffi::VAContextID,
437        buffers: &mut [ffi::VABufferID],
438    ) -> Result<(), String> {
439        let n = c_int::try_from(buffers.len()).map_err(|_| "too many buffers")?;
440        let st = unsafe { (self.api.render_picture)(self.dpy, context, buffers.as_mut_ptr(), n) };
441        self.check(st, "vaRenderPicture")
442    }
443
444    fn picture(
445        &self,
446        context: ffi::VAContextID,
447        surface: VASurfaceID,
448        groups: &mut [&mut [ffi::VABufferID]],
449    ) -> Result<(), String> {
450        let st = unsafe { (self.api.begin_picture)(self.dpy, context, surface) };
451        self.check(st, "vaBeginPicture")?;
452        let mut rendered = Ok(());
453        for group in groups.iter_mut() {
454            if group.is_empty() {
455                continue;
456            }
457            rendered = self.render(context, group);
458            if rendered.is_err() {
459                break;
460            }
461        }
462        let st = unsafe { (self.api.end_picture)(self.dpy, context) };
463        rendered?;
464        self.check(st, "vaEndPicture")
465    }
466
467    fn sync(&self, surface: VASurfaceID) -> Result<(), String> {
468        let st = unsafe { (self.api.sync_surface)(self.dpy, surface) };
469        self.check(st, "vaSyncSurface")
470    }
471
472    fn create_image(&self, bits: u32, size: (u32, u32)) -> Result<VAImage, String> {
473        let (_, fourcc, bits_per_pixel) = formats(bits)?;
474        let mut format = VAImageFormat {
475            fourcc,
476            byte_order: 1,
477            bits_per_pixel,
478            ..Default::default()
479        };
480        let mut image = VAImage {
481            image_id: ffi::VA_INVALID_ID,
482            buf: ffi::VA_INVALID_ID,
483            ..Default::default()
484        };
485        let (w, h) = (
486            c_int::try_from(size.0).map_err(|_| "picture too wide")?,
487            c_int::try_from(size.1).map_err(|_| "picture too tall")?,
488        );
489        let st = unsafe { (self.api.create_image)(self.dpy, &mut format, w, h, &mut image) };
490        self.check(st, "vaCreateImage")?;
491        Ok(image)
492    }
493
494    fn with_mapped<T>(
495        &self,
496        buf: ffi::VABufferID,
497        f: impl FnOnce(*mut c_void) -> Result<T, String>,
498    ) -> Result<T, String> {
499        let mut ptr: *mut c_void = std::ptr::null_mut();
500        let st = unsafe { (self.api.map_buffer)(self.dpy, buf, &mut ptr) };
501        self.check(st, "vaMapBuffer")?;
502        let result = if ptr.is_null() {
503            Err("vaMapBuffer returned no data".to_string())
504        } else {
505            f(ptr)
506        };
507        let st = unsafe { (self.api.unmap_buffer)(self.dpy, buf) };
508        let value = result?;
509        self.check(st, "vaUnmapBuffer")?;
510        Ok(value)
511    }
512
513    fn with_image<T>(
514        &self,
515        image: &VAImage,
516        f: impl FnOnce(&mut [u8]) -> Result<T, String>,
517    ) -> Result<T, String> {
518        self.with_mapped(image.buf, |ptr| {
519            let data = unsafe {
520                std::slice::from_raw_parts_mut(ptr.cast::<u8>(), image.data_size as usize)
521            };
522            f(data)
523        })
524    }
525
526    fn derive(&self, surface: VASurfaceID) -> Result<VAImage, String> {
527        self.sync(surface)?;
528        let mut image = VAImage {
529            image_id: ffi::VA_INVALID_ID,
530            buf: ffi::VA_INVALID_ID,
531            ..Default::default()
532        };
533        let st = unsafe { (self.api.derive_image)(self.dpy, surface, &mut image) };
534        self.check(st, "vaDeriveImage")?;
535        Ok(image)
536    }
537
538    fn export(
539        &self,
540        surface: VASurfaceID,
541        separate: bool,
542    ) -> Result<ffi::VADRMPRIMESurfaceDescriptor, String> {
543        self.sync(surface)?;
544        let mut d = ffi::VADRMPRIMESurfaceDescriptor::default();
545        let layout = if separate {
546            ffi::VA_EXPORT_SURFACE_SEPARATE_LAYERS
547        } else {
548            ffi::VA_EXPORT_SURFACE_COMPOSED_LAYERS
549        };
550        let st = unsafe {
551            (self.api.export_surface)(
552                self.dpy,
553                surface,
554                ffi::VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
555                ffi::VA_EXPORT_SURFACE_READ_ONLY | layout,
556                (&mut d as *mut ffi::VADRMPRIMESurfaceDescriptor).cast(),
557            )
558        };
559        self.check(st, "vaExportSurfaceHandle")?;
560        Ok(d)
561    }
562
563    fn destroy_image(&self, image: &VAImage) {
564        unsafe { (self.api.destroy_image)(self.dpy, image.image_id) };
565    }
566
567    fn get_image(
568        &self,
569        surface: VASurfaceID,
570        image: &VAImage,
571        size: (u32, u32),
572    ) -> Result<(), String> {
573        self.sync(surface)?;
574        let st = unsafe {
575            (self.api.get_image)(self.dpy, surface, 0, 0, size.0, size.1, image.image_id)
576        };
577        self.check(st, "vaGetImage")
578    }
579
580    fn put_image(
581        &self,
582        surface: VASurfaceID,
583        image: &VAImage,
584        size: (u32, u32),
585    ) -> Result<(), String> {
586        let (w, h) = size;
587        let st = unsafe {
588            (self.api.put_image)(self.dpy, surface, image.image_id, 0, 0, w, h, 0, 0, w, h)
589        };
590        self.check(st, "vaPutImage")
591    }
592
593    fn release(
594        &self,
595        image: Option<VAImage>,
596        context: ffi::VAContextID,
597        surfaces: &mut [VASurfaceID],
598        config: ffi::VAConfigID,
599    ) {
600        unsafe {
601            if let Some(image) = image {
602                (self.api.destroy_image)(self.dpy, image.image_id);
603            }
604            if context != ffi::VA_INVALID_ID {
605                (self.api.destroy_context)(self.dpy, context);
606            }
607            if !surfaces.is_empty() {
608                let n = c_int::try_from(surfaces.len()).unwrap_or(0);
609                (self.api.destroy_surfaces)(self.dpy, surfaces.as_mut_ptr(), n);
610            }
611            if config != ffi::VA_INVALID_ID {
612                (self.api.destroy_config)(self.dpy, config);
613            }
614        }
615    }
616}
617
618impl Drop for Display {
619    fn drop(&mut self) {
620        unsafe { (self.api.terminate)(self.dpy) };
621    }
622}
623
624pub fn formats(bits: u32) -> Result<(c_uint, u32, u32), String> {
625    match bits {
626        8 => Ok((ffi::VA_RT_FORMAT_YUV420, ffi::VA_FOURCC_NV12, 12)),
627        10 => Ok((ffi::VA_RT_FORMAT_YUV420_10, ffi::VA_FOURCC_P010, 24)),
628        _ => Err(format!("{bits}-bit pictures are not taken through VA-API")),
629    }
630}
631
632fn biplanar_view<'a>(image: &VAImage, data: &'a [u8]) -> Result<Biplanar<'a>, String> {
633    if !matches!(
634        image.format.fourcc,
635        ffi::VA_FOURCC_NV12 | ffi::VA_FOURCC_P010
636    ) || image.num_planes < 2
637    {
638        return Err(format!(
639            "the image is not NV12 / P010 (fourcc {:#x}, {} planes)",
640            image.format.fourcc, image.num_planes
641        ));
642    }
643    let [p0, p1, _] = image.pitches;
644    if p0 != p1 {
645        return Err(format!("NV12 planes with different pitches ({p0}, {p1})"));
646    }
647    let luma = data
648        .get(image.offsets[0] as usize..)
649        .ok_or("NV12 luma offset out of range")?;
650    let chroma = data
651        .get(image.offsets[1] as usize..)
652        .ok_or("NV12 chroma offset out of range")?;
653    Ok(Biplanar {
654        luma,
655        chroma,
656        stride: p0 as usize,
657        rows: usize::from(image.height),
658    })
659}
660
661pub struct Pool {
662    display: Arc<Display>,
663    id: u64,
664    ids: Vec<VASurfaceID>,
665    held: Vec<AtomicBool>,
666    size: (u32, u32),
667    bound: Mutex<(ffi::VAContextID, ffi::VAConfigID)>,
668}
669
670impl Pool {
671    fn new(
672        display: Arc<Display>,
673        rt_format: c_uint,
674        size: (u32, u32),
675        count: usize,
676    ) -> Result<Arc<Self>, String> {
677        static POOLS: AtomicU64 = AtomicU64::new(1);
678        let ids = display.create_surfaces(rt_format, size, count)?;
679        Ok(Arc::new(Pool {
680            held: ids.iter().map(|_| AtomicBool::new(false)).collect(),
681            display,
682            id: POOLS.fetch_add(1, Ordering::Relaxed),
683            ids,
684            size,
685            bound: Mutex::new((ffi::VA_INVALID_ID, ffi::VA_INVALID_ID)),
686        }))
687    }
688
689    fn bind(&self, context: ffi::VAContextID, config: ffi::VAConfigID) {
690        *self.bound.lock().unwrap_or_else(PoisonError::into_inner) = (context, config);
691    }
692
693    fn take(self: &Arc<Self>, geometry: Geometry) -> Option<GpuSurface> {
694        let index = self.held.iter().position(|h| {
695            h.compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
696                .is_ok()
697        })?;
698        Some(GpuSurface {
699            pool: self.clone(),
700            index,
701            geometry,
702            pts: 0,
703        })
704    }
705}
706
707impl Drop for Pool {
708    fn drop(&mut self) {
709        let (context, config) = *self.bound.get_mut().unwrap_or_else(PoisonError::into_inner);
710        self.display.release(None, context, &mut self.ids, config);
711    }
712}
713
714pub struct GpuSurface {
715    pool: Arc<Pool>,
716    index: usize,
717    geometry: Geometry,
718    pub pts: i64,
719}
720
721impl Drop for GpuSurface {
722    fn drop(&mut self) {
723        if let Some(h) = self.pool.held.get(self.index) {
724            h.store(false, Ordering::Release);
725        }
726    }
727}
728
729impl GpuSurface {
730    pub fn width(&self) -> u32 {
731        self.geometry.crop.2
732    }
733
734    pub fn height(&self) -> u32 {
735        self.geometry.crop.3
736    }
737
738    fn id(&self) -> Result<VASurfaceID, String> {
739        self.pool
740            .ids
741            .get(self.index)
742            .copied()
743            .ok_or_else(|| "surface out of range".to_string())
744    }
745
746    pub fn key(&self) -> (u64, u32) {
747        (self.pool.id, self.index as u32)
748    }
749
750    pub fn sync(&self) -> Result<(), String> {
751        self.pool.display.sync(self.id()?)
752    }
753
754    pub fn dmabuf(&self, separate: bool) -> Result<DmaBuf, String> {
755        let d = self.pool.display.export(self.id()?, separate)?;
756        let count = (d.num_objects as usize).min(4);
757        let mut objects = Vec::with_capacity(count);
758        for o in &d.objects[..count] {
759            if o.fd < 0 {
760                return Err("vaExportSurfaceHandle gave no file descriptor".into());
761            }
762            objects.push(DmaObject {
763                fd: unsafe { OwnedFd::from_raw_fd(o.fd) },
764                size: o.size,
765                modifier: o.drm_format_modifier,
766            });
767        }
768        let layers = d.layers[..(d.num_layers as usize).min(4)]
769            .iter()
770            .map(|l| DmaLayer {
771                drm_format: l.drm_format,
772                planes: (0..(l.num_planes as usize).min(4))
773                    .map(|i| DmaPlane {
774                        object: l.object_index[i] as usize,
775                        offset: l.offset[i],
776                        pitch: l.pitch[i],
777                    })
778                    .collect(),
779            })
780            .collect();
781        Ok(DmaBuf {
782            fourcc: d.fourcc,
783            width: d.width,
784            height: d.height,
785            crop: self.geometry.crop,
786            objects,
787            layers,
788        })
789    }
790
791    pub fn download(&self) -> Result<Frame, String> {
792        let surface = self.id()?;
793        let d = &self.pool.display;
794        let geometry = self.geometry;
795        if let Ok(image) = d.derive(surface) {
796            let frame = d.with_image(&image, |data| {
797                biplanar::to_frame(&biplanar_view(&image, data)?, &geometry)
798            });
799            d.destroy_image(&image);
800            if let Ok(f) = frame {
801                return Ok(Frame { pts: self.pts, ..f });
802            }
803        }
804        let image = d.create_image(geometry.bits, self.pool.size)?;
805        let frame = d.get_image(surface, &image, self.pool.size).and_then(|()| {
806            d.with_image(&image, |data| {
807                biplanar::to_frame(&biplanar_view(&image, data)?, &geometry)
808            })
809        });
810        d.destroy_image(&image);
811        frame.map(|f| Frame { pts: self.pts, ..f })
812    }
813}
814
815pub struct DmaObject {
816    pub fd: OwnedFd,
817    pub size: u32,
818    pub modifier: u64,
819}
820
821pub struct DmaPlane {
822    pub object: usize,
823    pub offset: u32,
824    pub pitch: u32,
825}
826
827pub struct DmaLayer {
828    pub drm_format: u32,
829    pub planes: Vec<DmaPlane>,
830}
831
832pub struct DmaBuf {
833    pub fourcc: u32,
834    pub width: u32,
835    pub height: u32,
836    pub crop: (u32, u32, u32, u32),
837    pub objects: Vec<DmaObject>,
838    pub layers: Vec<DmaLayer>,
839}
840
841pub enum Picture {
842    Cpu(Frame),
843    Gpu(GpuSurface),
844}
845
846impl Picture {
847    pub fn at(self, pts: i64) -> Self {
848        match self {
849            Picture::Cpu(f) => Picture::Cpu(Frame { pts, ..f }),
850            Picture::Gpu(mut g) => {
851                g.pts = pts;
852                Picture::Gpu(g)
853            }
854        }
855    }
856}
857
858pub struct Session {
859    display: Arc<Display>,
860    config: ffi::VAConfigID,
861    context: ffi::VAContextID,
862    pool: Arc<Pool>,
863    image: Option<VAImage>,
864    size: (u32, u32),
865    geometry: Geometry,
866    derive: bool,
867    staging: Option<Staging>,
868    staged: bool,
869    pub gpu: bool,
870}
871
872struct Staged {
873    surface: VASurfaceID,
874    buffer: HostBuffer,
875    holds: Option<usize>,
876    order: u64,
877}
878
879struct Staging {
880    vpp: Vpp,
881    slots: Vec<Staged>,
882    pitch: usize,
883    chroma: usize,
884    size: (u32, u32),
885    bits: u32,
886    order: u64,
887    most: usize,
888}
889
890impl Staging {
891    fn new(
892        display: Arc<Display>,
893        geometry: Geometry,
894        surface: (u32, u32),
895        (count, most): (usize, usize),
896    ) -> Result<Self, String> {
897        let (w, h) = (
898            geometry.crop.2.next_multiple_of(2),
899            geometry.crop.3.next_multiple_of(2),
900        );
901        if geometry.crop.0.saturating_add(w) > surface.0
902            || geometry.crop.1.saturating_add(h) > surface.1
903        {
904            return Err("the picture has no even-sized area to copy".into());
905        }
906        let bps = if geometry.bits > 8 { 2 } else { 1 };
907        let pitch = (w as usize * bps).next_multiple_of(256);
908        let chroma = pitch * h as usize;
909        let vpp = Vpp::new(display.clone())?;
910        let mut staging = Self {
911            vpp,
912            slots: Vec::with_capacity(most),
913            pitch,
914            chroma,
915            size: (w, h),
916            bits: geometry.bits,
917            order: 0,
918            most,
919        };
920        for _ in 0..count.min(most) {
921            let slot = staging.slot()?;
922            staging.slots.push(slot);
923        }
924        Ok(staging)
925    }
926
927    fn slot(&self) -> Result<Staged, String> {
928        let ten = self.bits > 8;
929        let (rt_format, fourcc, _) = formats(self.bits)?;
930        let (w, h) = self.size;
931        let buffer = HostBuffer::new(self.chroma + self.pitch * (h as usize / 2))?;
932        let pitch = u32::try_from(self.pitch).map_err(|_| "picture too wide")?;
933        let mut layer = ffi::VADRMPRIMELayer {
934            drm_format: if ten {
935                ffi::DRM_FORMAT_P010
936            } else {
937                ffi::DRM_FORMAT_NV12
938            },
939            num_planes: 2,
940            ..Default::default()
941        };
942        layer.offset[1] = u32::try_from(self.chroma).map_err(|_| "picture too large")?;
943        layer.pitch = [pitch, pitch, 0, 0];
944        let mut descriptor = ffi::VADRMPRIMESurfaceDescriptor {
945            fourcc,
946            width: w,
947            height: h,
948            num_objects: 1,
949            num_layers: 1,
950            ..Default::default()
951        };
952        descriptor.objects[0] = ffi::VADRMPRIMEObject {
953            fd: buffer.fd(),
954            size: u32::try_from(buffer.len()).map_err(|_| "picture too large")?,
955            drm_format_modifier: ffi::DRM_FORMAT_MOD_LINEAR,
956        };
957        descriptor.layers[0] = layer;
958        let surface = self
959            .vpp
960            .display
961            .import(rt_format, (w, h), &mut descriptor)?;
962        Ok(Staged {
963            surface,
964            buffer,
965            holds: None,
966            order: 0,
967        })
968    }
969
970    fn copy(
971        &mut self,
972        src: VASurfaceID,
973        index: usize,
974        crop: (u32, u32, u32, u32),
975    ) -> Result<(), String> {
976        for slot in &mut self.slots {
977            if slot.holds == Some(index) {
978                slot.holds = None;
979            }
980        }
981        if self.slots.iter().all(|s| s.holds.is_some())
982            && self.slots.len() < self.most
983            && let Ok(slot) = self.slot()
984        {
985            self.slots.push(slot);
986        }
987        let Some(slot) = self
988            .slots
989            .iter_mut()
990            .min_by_key(|s| (s.holds.is_some(), s.order))
991        else {
992            return Err("no staging surfaces".into());
993        };
994        slot.holds = None;
995        let region = (crop.0, crop.1, self.size.0, self.size.1);
996        self.vpp.copy(src, region, slot.surface, self.size)?;
997        self.order += 1;
998        slot.holds = Some(index);
999        slot.order = self.order;
1000        Ok(())
1001    }
1002
1003    fn read(&mut self, index: usize, crop: (u32, u32, u32, u32)) -> Option<Result<Frame, String>> {
1004        let slot = self.slots.iter_mut().find(|s| s.holds == Some(index))?;
1005        slot.holds = None;
1006        let display = &self.vpp.display;
1007        if let Err(e) = display.sync(slot.surface) {
1008            return Some(Err(e));
1009        }
1010        let data = slot.buffer.data();
1011        let (Some(luma), Some(chroma)) = (data.get(..self.chroma), data.get(self.chroma..)) else {
1012            return Some(Err("staging buffer too small".into()));
1013        };
1014        let view = Biplanar {
1015            luma,
1016            chroma,
1017            stride: self.pitch,
1018            rows: self.size.1 as usize,
1019        };
1020        let geometry = Geometry {
1021            crop: (0, 0, crop.2, crop.3),
1022            bits: self.bits,
1023        };
1024        Some(biplanar::to_frame(&view, &geometry))
1025    }
1026}
1027
1028impl Drop for Staging {
1029    fn drop(&mut self) {
1030        let mut ids: Vec<VASurfaceID> = self.slots.iter().map(|s| s.surface).collect();
1031        self.vpp
1032            .display
1033            .release(None, ffi::VA_INVALID_ID, &mut ids, ffi::VA_INVALID_ID);
1034    }
1035}
1036
1037impl Session {
1038    pub fn new(
1039        profiles: &[ffi::VAProfile],
1040        size: (u32, u32),
1041        count: usize,
1042        geometry: Geometry,
1043    ) -> Result<Self, String> {
1044        with_display(|d| Self::on(d, profiles, size, count, geometry))
1045    }
1046
1047    fn on(
1048        display: Arc<Display>,
1049        profiles: &[ffi::VAProfile],
1050        size: (u32, u32),
1051        count: usize,
1052        geometry: Geometry,
1053    ) -> Result<Self, String> {
1054        let (rt_format, _, _) = formats(geometry.bits)?;
1055        let mut config = None;
1056        let mut why = "no VA-API profile for the stream".to_string();
1057        for &profile in profiles {
1058            let value = match display.attributes(
1059                profile,
1060                ffi::VAEntrypointVLD,
1061                &[ffi::VAConfigAttribRTFormat],
1062            ) {
1063                Ok(v) => v.first().copied().unwrap_or(0),
1064                Err(e) => {
1065                    why = e;
1066                    continue;
1067                }
1068            };
1069            if value == ffi::VA_ATTRIB_NOT_SUPPORTED || value & rt_format == 0 {
1070                why = format!(
1071                    "profile {profile} does not decode to {}-bit 4:2:0",
1072                    geometry.bits
1073                );
1074                continue;
1075            }
1076            let mut attr = [VAConfigAttrib {
1077                type_: ffi::VAConfigAttribRTFormat,
1078                value: rt_format,
1079            }];
1080            match display.create_config(profile, ffi::VAEntrypointVLD, &mut attr) {
1081                Ok(id) => {
1082                    config = Some(id);
1083                    break;
1084                }
1085                Err(e) => why = e,
1086            }
1087        }
1088        let config = config.ok_or(why)?;
1089        let size = (size.0.next_multiple_of(2), size.1.next_multiple_of(2));
1090        let pool = match Pool::new(display.clone(), rt_format, size, count) {
1091            Ok(p) => p,
1092            Err(e) => {
1093                display.release(None, ffi::VA_INVALID_ID, &mut [], config);
1094                return Err(e);
1095            }
1096        };
1097        pool.bind(ffi::VA_INVALID_ID, config);
1098        let mut s = Self {
1099            display,
1100            config,
1101            context: ffi::VA_INVALID_ID,
1102            pool,
1103            image: None,
1104            size,
1105            geometry,
1106            derive: std::env::var_os("PITO_VA_NO_DERIVE").is_none(),
1107            staging: None,
1108            staged: std::env::var_os("PITO_VA_NO_STAGING").is_none(),
1109            gpu: false,
1110        };
1111        let mut targets = s.pool.ids.clone();
1112        s.context = s.display.create_context(s.config, size, &mut targets)?;
1113        s.pool.bind(s.context, s.config);
1114        Ok(s)
1115    }
1116
1117    pub fn vendor(&self) -> &str {
1118        self.display.vendor()
1119    }
1120
1121    pub fn direct(&mut self) {
1122        self.staged = false;
1123        self.staging = None;
1124    }
1125
1126    fn image(&mut self) -> Result<VAImage, String> {
1127        if let Some(image) = self.image {
1128            return Ok(image);
1129        }
1130        let image = self.display.create_image(self.geometry.bits, self.size)?;
1131        self.image = Some(image);
1132        Ok(image)
1133    }
1134
1135    fn surface(&self, index: usize) -> Result<VASurfaceID, String> {
1136        self.pool
1137            .ids
1138            .get(index)
1139            .copied()
1140            .ok_or_else(|| "surface out of range".to_string())
1141    }
1142
1143    fn get_image(&mut self, index: usize) -> Result<VAImage, String> {
1144        let surface = self.surface(index)?;
1145        let image = self.image()?;
1146        self.display.get_image(surface, &image, self.size)?;
1147        Ok(image)
1148    }
1149
1150    pub(super) fn decode_picture<P, Q, S>(
1151        &mut self,
1152        target: usize,
1153        pic: &P,
1154        iq: Option<&Q>,
1155        slices: &[(S, Vec<u8>)],
1156    ) -> Result<(), String> {
1157        let surface = self.surface(target)?;
1158        let mut buffers = Vec::with_capacity(2 + 2 * slices.len());
1159        let decoded = self.decode_into(surface, &mut buffers, pic, iq, slices);
1160        self.display.destroy_buffers(&buffers);
1161        decoded
1162    }
1163
1164    fn decode_into<P, Q, S>(
1165        &self,
1166        surface: VASurfaceID,
1167        buffers: &mut Vec<ffi::VABufferID>,
1168        pic: &P,
1169        iq: Option<&Q>,
1170        slices: &[(S, Vec<u8>)],
1171    ) -> Result<(), String> {
1172        let d = &self.display;
1173        let ctx = self.context;
1174        buffers.push(d.value_buffer(ctx, ffi::VAPictureParameterBufferType, pic)?);
1175        if let Some(iq) = iq {
1176            buffers.push(d.value_buffer(ctx, ffi::VAIQMatrixBufferType, iq)?);
1177        }
1178        let head = buffers.len();
1179        for (params, data) in slices {
1180            buffers.push(d.value_buffer(ctx, ffi::VASliceParameterBufferType, params)?);
1181            buffers.push(d.buffer(
1182                ctx,
1183                ffi::VASliceDataBufferType,
1184                data.as_ptr().cast(),
1185                data.len(),
1186            )?);
1187        }
1188        let (picture, slice_buffers) = buffers.split_at_mut(head);
1189        let mut groups: Vec<&mut [ffi::VABufferID]> = vec![picture];
1190        groups.extend(slice_buffers.chunks_mut(2));
1191        d.picture(ctx, surface, &mut groups)
1192    }
1193
1194    fn read_cpu(&mut self, index: usize) -> Result<Frame, String> {
1195        if let Some(staging) = &mut self.staging
1196            && let Some(frame) = staging.read(index, self.geometry.crop)
1197        {
1198            match frame {
1199                Ok(frame) => return Ok(frame),
1200                Err(_) => {
1201                    self.staging = None;
1202                    self.staged = false;
1203                }
1204            }
1205        }
1206        if self.derive {
1207            let surface = self.surface(index)?;
1208            match self.display.derive(surface) {
1209                Ok(image) => {
1210                    let geometry = self.geometry;
1211                    let frame = self.display.with_image(&image, |data| {
1212                        biplanar::to_frame(&biplanar_view(&image, data)?, &geometry)
1213                    });
1214                    self.display.destroy_image(&image);
1215                    if frame.is_ok() {
1216                        return frame;
1217                    }
1218                    self.derive = false;
1219                }
1220                Err(_) => self.derive = false,
1221            }
1222        }
1223        let image = self.get_image(index)?;
1224        let geometry = self.geometry;
1225        self.display.with_image(&image, |data| {
1226            biplanar::to_frame(&biplanar_view(&image, data)?, &geometry)
1227        })
1228    }
1229}
1230
1231impl Accel for Session {
1232    fn surfaces(&self) -> &[VASurfaceID] {
1233        &self.pool.ids
1234    }
1235
1236    fn held(&self, index: usize) -> bool {
1237        self.pool
1238            .held
1239            .get(index)
1240            .is_some_and(|h| h.load(Ordering::Acquire))
1241    }
1242
1243    fn decode_h264(
1244        &mut self,
1245        target: usize,
1246        pic: &ffi::VAPictureParameterBufferH264,
1247        iq: &ffi::VAIQMatrixBufferH264,
1248        slices: &[(ffi::VASliceParameterBufferH264, Vec<u8>)],
1249    ) -> Result<(), String> {
1250        self.decode_picture(target, pic, Some(iq), slices)
1251    }
1252
1253    fn decode_hevc(
1254        &mut self,
1255        target: usize,
1256        pic: &ffi::VAPictureParameterBufferHEVC,
1257        iq: Option<&ffi::VAIQMatrixBufferHEVC>,
1258        slices: &[(ffi::VASliceParameterBufferHEVC, Vec<u8>)],
1259    ) -> Result<(), String> {
1260        self.decode_picture(target, pic, iq, slices)
1261    }
1262
1263    fn copies(&self) -> bool {
1264        !self.gpu && self.staged
1265    }
1266
1267    fn prefetch(&mut self, index: usize) {
1268        if self.gpu || !self.staged {
1269            return;
1270        }
1271        let Ok(surface) = self.surface(index) else {
1272            return;
1273        };
1274        if self.staging.is_none() {
1275            match Staging::new(
1276                self.display.clone(),
1277                self.geometry,
1278                self.size,
1279                (LAG + 2, LAG + SLACK + 2),
1280            ) {
1281                Ok(s) => self.staging = Some(s),
1282                Err(_) => {
1283                    self.staged = false;
1284                    return;
1285                }
1286            }
1287        }
1288        if let Some(staging) = &mut self.staging
1289            && staging.copy(surface, index, self.geometry.crop).is_err()
1290        {
1291            self.staging = None;
1292            self.staged = false;
1293        }
1294    }
1295
1296    fn read(&mut self, index: usize) -> Result<Picture, String> {
1297        if !self.gpu {
1298            return self.read_cpu(index).map(Picture::Cpu);
1299        }
1300        let flag = self.pool.held.get(index).ok_or("surface out of range")?;
1301        flag.store(true, Ordering::Release);
1302        Ok(Picture::Gpu(GpuSurface {
1303            pool: self.pool.clone(),
1304            index,
1305            geometry: self.geometry,
1306            pts: 0,
1307        }))
1308    }
1309
1310    fn fill(&mut self, index: usize, from: Option<usize>) -> Result<(), String> {
1311        let surface = self.surface(index)?;
1312        let image = match from {
1313            Some(from) => self.get_image(from)?,
1314            None => {
1315                let image = self.image()?;
1316                let ten = self.geometry.bits > 8;
1317                self.display.with_image(&image, |data| {
1318                    if ten {
1319                        for w in data.as_chunks_mut::<2>().0 {
1320                            w.copy_from_slice(&0x8000u16.to_le_bytes());
1321                        }
1322                    } else {
1323                        data.fill(128);
1324                    }
1325                    Ok(())
1326                })?;
1327                image
1328            }
1329        };
1330        self.display.put_image(surface, &image, self.size)
1331    }
1332}
1333
1334impl Drop for Session {
1335    fn drop(&mut self) {
1336        self.display.release(
1337            self.image.take(),
1338            ffi::VA_INVALID_ID,
1339            &mut [],
1340            ffi::VA_INVALID_ID,
1341        );
1342    }
1343}
1344
1345pub struct Vpp {
1346    display: Arc<Display>,
1347    config: ffi::VAConfigID,
1348    context: ffi::VAContextID,
1349    owned: bool,
1350}
1351
1352impl Vpp {
1353    fn new(display: Arc<Display>) -> Result<Self, String> {
1354        let config =
1355            display.create_config(ffi::VAProfileNone, ffi::VAEntrypointVideoProc, &mut [])?;
1356        let mut vpp = Self {
1357            display,
1358            config,
1359            context: ffi::VA_INVALID_ID,
1360            owned: true,
1361        };
1362        vpp.context = vpp.display.create_context(vpp.config, (0, 0), &mut [])?;
1363        Ok(vpp)
1364    }
1365
1366    fn blit(&self, src: &GpuSurface, dst: VASurfaceID, size: (u32, u32)) -> Result<(), String> {
1367        self.copy(src.id()?, src.geometry.crop, dst, size)
1368    }
1369
1370    fn copy(
1371        &self,
1372        src: VASurfaceID,
1373        crop: (u32, u32, u32, u32),
1374        dst: VASurfaceID,
1375        size: (u32, u32),
1376    ) -> Result<(), String> {
1377        self.compose(src, crop, dst, size, None)
1378    }
1379
1380    fn compose(
1381        &self,
1382        src: VASurfaceID,
1383        crop: (u32, u32, u32, u32),
1384        dst: VASurfaceID,
1385        size: (u32, u32),
1386        over: Option<Over>,
1387    ) -> Result<(), String> {
1388        let from = rectangle(crop)?;
1389        let to = rectangle((0, 0, size.0, size.1))?;
1390        let video = pipeline(
1391            src,
1392            &from,
1393            &to,
1394            ffi::VAProcColorStandardBT709,
1395            std::ptr::null(),
1396        );
1397        let mut buffers = vec![self.display.value_buffer(
1398            self.context,
1399            ffi::VAProcPipelineParameterBufferType,
1400            &video,
1401        )?];
1402        let blend = ffi::VABlendState {
1403            flags: ffi::VA_BLEND_PREMULTIPLIED_ALPHA,
1404            global_alpha: 1.0,
1405            ..Default::default()
1406        };
1407        if let Some(o) = over {
1408            let painted = rectangle((0, 0, o.rect.2, o.rect.3))?;
1409            let placed = rectangle(o.rect)?;
1410            let mut layer = pipeline(
1411                o.surface,
1412                &painted,
1413                &placed,
1414                o.standard,
1415                (&blend as *const ffi::VABlendState).cast(),
1416            );
1417            layer.output_color_properties.chroma_sample_location =
1418                ffi::VA_CHROMA_SITING_VERTICAL_CENTER | ffi::VA_CHROMA_SITING_HORIZONTAL_CENTER;
1419            match self.display.value_buffer(
1420                self.context,
1421                ffi::VAProcPipelineParameterBufferType,
1422                &layer,
1423            ) {
1424                Ok(id) => buffers.push(id),
1425                Err(e) => {
1426                    self.display.destroy_buffers(&buffers);
1427                    return Err(e);
1428                }
1429            }
1430        }
1431        let mut groups: Vec<&mut [ffi::VABufferID]> = vec![&mut buffers[..]];
1432        let done = self.display.picture(self.context, dst, &mut groups);
1433        self.display.destroy_buffers(&buffers);
1434        done
1435    }
1436}
1437
1438fn rectangle((x, y, w, h): (u32, u32, u32, u32)) -> Result<ffi::VARectangle, String> {
1439    let narrow = |v: u32| u16::try_from(v).map_err(|_| "picture too large".to_string());
1440    let wide = |v: u32| i16::try_from(v).map_err(|_| "picture too large".to_string());
1441    Ok(ffi::VARectangle {
1442        x: wide(x)?,
1443        y: wide(y)?,
1444        width: narrow(w)?,
1445        height: narrow(h)?,
1446    })
1447}
1448
1449fn pipeline(
1450    surface: VASurfaceID,
1451    from: &ffi::VARectangle,
1452    to: &ffi::VARectangle,
1453    standard: c_int,
1454    blend_state: *const c_void,
1455) -> ffi::VAProcPipelineParameterBuffer {
1456    ffi::VAProcPipelineParameterBuffer {
1457        surface,
1458        surface_region: from,
1459        surface_color_standard: standard,
1460        output_region: to,
1461        output_background_color: 0xff00_0000,
1462        output_color_standard: standard,
1463        pipeline_flags: 0,
1464        filter_flags: 0,
1465        filters: std::ptr::null_mut(),
1466        num_filters: 0,
1467        forward_references: std::ptr::null_mut(),
1468        num_forward_references: 0,
1469        backward_references: std::ptr::null_mut(),
1470        num_backward_references: 0,
1471        rotation_state: 0,
1472        blend_state,
1473        mirror_state: 0,
1474        additional_outputs: std::ptr::null_mut(),
1475        num_additional_outputs: 0,
1476        input_surface_flag: 0,
1477        output_surface_flag: 0,
1478        input_color_properties: ffi::VAProcColorProperties::default(),
1479        output_color_properties: ffi::VAProcColorProperties::default(),
1480        processing_mode: 0,
1481        output_hdr_metadata: std::ptr::null_mut(),
1482        va_reserved: [0; ffi::PROC_RESERVED],
1483    }
1484}
1485
1486#[derive(Clone, Copy)]
1487struct Over {
1488    surface: VASurfaceID,
1489    rect: (u32, u32, u32, u32),
1490    standard: c_int,
1491}
1492
1493const PAINTED: usize = 3;
1494
1495struct Painted {
1496    surface: VASurfaceID,
1497    image: VAImage,
1498    size: (u32, u32),
1499}
1500
1501struct Shown {
1502    layer: Arc<Rgba>,
1503    size: (u32, u32),
1504    over: Option<Over>,
1505}
1506
1507struct Painter {
1508    display: Arc<Display>,
1509    ring: Vec<Painted>,
1510    next: usize,
1511    layer: Option<Shown>,
1512}
1513
1514impl Painter {
1515    fn new(display: Arc<Display>) -> Self {
1516        Self {
1517            display,
1518            ring: Vec::new(),
1519            next: 0,
1520            layer: None,
1521        }
1522    }
1523
1524    fn paint(&mut self, layer: &Arc<Rgba>, size: (u32, u32)) -> Result<Option<Over>, String> {
1525        if let Some(shown) = &self.layer
1526            && Arc::ptr_eq(&shown.layer, layer)
1527            && shown.size == size
1528        {
1529            return Ok(shown.over);
1530        }
1531        self.layer = None;
1532        if (layer.width, layer.height) != size {
1533            return Err(format!(
1534                "the overlay is {}x{}, the picture {}x{}",
1535                layer.width, layer.height, size.0, size.1
1536            ));
1537        }
1538        let over = match painted_box(layer) {
1539            Some(rect) => Some(self.upload(layer, rect, size)?),
1540            None => None,
1541        };
1542        self.layer = Some(Shown {
1543            layer: layer.clone(),
1544            size,
1545            over,
1546        });
1547        Ok(over)
1548    }
1549
1550    fn upload(
1551        &mut self,
1552        layer: &Rgba,
1553        (x, y, w, h): (u32, u32, u32, u32),
1554        size: (u32, u32),
1555    ) -> Result<Over, String> {
1556        let want = (w.next_multiple_of(64), h.next_multiple_of(64));
1557        let i = self.next % PAINTED;
1558        self.next = self.next.wrapping_add(1);
1559        let fits = self
1560            .ring
1561            .get(i)
1562            .is_some_and(|p| p.size.0 >= want.0 && p.size.1 >= want.1);
1563        if !fits {
1564            let surface = self.display.create_rgba(want)?;
1565            let image = match self.display.create_rgba_image(want) {
1566                Ok(image) => image,
1567                Err(e) => {
1568                    self.display.release(
1569                        None,
1570                        ffi::VA_INVALID_ID,
1571                        &mut [surface],
1572                        ffi::VA_INVALID_ID,
1573                    );
1574                    return Err(e);
1575                }
1576            };
1577            let fresh = Painted {
1578                surface,
1579                image,
1580                size: want,
1581            };
1582            if i < self.ring.len() {
1583                let mut old = std::mem::replace(&mut self.ring[i], fresh);
1584                self.display.release(
1585                    Some(old.image),
1586                    ffi::VA_INVALID_ID,
1587                    std::slice::from_mut(&mut old.surface),
1588                    ffi::VA_INVALID_ID,
1589                );
1590            } else {
1591                self.ring.push(fresh);
1592            }
1593        }
1594        let slot = self.ring.get(i).ok_or("no overlay surface")?;
1595        let image = slot.image;
1596        let (pitch, offset) = (image.pitches[0] as usize, image.offsets[0] as usize);
1597        let width = layer.width as usize;
1598        self.display.with_image(&image, |data| {
1599            data.fill(0);
1600            for r in 0..h as usize {
1601                let from = ((y as usize + r) * width + x as usize) * 4;
1602                let src = layer
1603                    .data
1604                    .get(from..from + w as usize * 4)
1605                    .ok_or("the overlay is shorter than its size")?;
1606                let at = offset + r * pitch;
1607                let dst = data
1608                    .get_mut(at..at + w as usize * 4)
1609                    .ok_or("the overlay image is too small")?;
1610                for (d, s) in dst.chunks_exact_mut(4).zip(src.chunks_exact(4)) {
1611                    let a = u32::from(s[3]);
1612                    for k in 0..3 {
1613                        d[k] = ((u32::from(s[k]) * a + 127) / 255) as u8;
1614                    }
1615                    d[3] = s[3];
1616                }
1617            }
1618            Ok(())
1619        })?;
1620        self.display.put_image(slot.surface, &image, slot.size)?;
1621        Ok(Over {
1622            surface: slot.surface,
1623            rect: (x, y, w, h),
1624            standard: if size.1 >= 720 {
1625                ffi::VAProcColorStandardBT709
1626            } else {
1627                ffi::VAProcColorStandardBT601
1628            },
1629        })
1630    }
1631}
1632
1633impl Drop for Painter {
1634    fn drop(&mut self) {
1635        for p in &mut self.ring {
1636            self.display.release(
1637                Some(p.image),
1638                ffi::VA_INVALID_ID,
1639                std::slice::from_mut(&mut p.surface),
1640                ffi::VA_INVALID_ID,
1641            );
1642        }
1643    }
1644}
1645
1646fn painted_box(layer: &Rgba) -> Option<(u32, u32, u32, u32)> {
1647    let (w, h) = (layer.width as usize, layer.height as usize);
1648    let rows = layer.data.get(..w * h * 4)?.chunks_exact(w * 4);
1649    let mut span: Option<(usize, usize, usize, usize)> = None;
1650    for (y, row) in rows.enumerate() {
1651        let alpha = || row.iter().skip(3).step_by(4);
1652        let Some(first) = alpha().position(|&a| a != 0) else {
1653            continue;
1654        };
1655        let last = w - 1 - alpha().rev().position(|&a| a != 0).unwrap_or(0);
1656        span = Some(match span {
1657            Some((x0, y0, x1, _)) => (x0.min(first), y0, x1.max(last), y),
1658            None => (first, y, last, y),
1659        });
1660    }
1661    let (x0, y0, x1, y1) = span?;
1662    let (x0, y0) = (x0 & !1, y0 & !1);
1663    let (x1, y1) = (((x1 + 2) & !1).min(w), ((y1 + 2) & !1).min(h));
1664    Some((x0 as u32, y0 as u32, (x1 - x0) as u32, (y1 - y0) as u32))
1665}
1666
1667pub struct Scaler {
1668    vpp: Vpp,
1669    pool: Arc<Pool>,
1670    size: (u32, u32),
1671    bits: u32,
1672}
1673
1674impl Scaler {
1675    pub fn new(size: (u32, u32), bits: u32, count: usize) -> Result<Self, String> {
1676        let display = shared()?;
1677        let (rt_format, _, _) = formats(bits)?;
1678        let mut vpp = Vpp::new(display.clone())?;
1679        let pool = Pool::new(display, rt_format, size, count.max(1))?;
1680        pool.bind(vpp.context, vpp.config);
1681        vpp.owned = false;
1682        Ok(Self {
1683            vpp,
1684            pool,
1685            size,
1686            bits,
1687        })
1688    }
1689
1690    pub fn scale(&self, src: &GpuSurface) -> Result<GpuSurface, String> {
1691        if !Arc::ptr_eq(&src.pool.display, &self.vpp.display) {
1692            return Err("the GPU frame is on another VA display".into());
1693        }
1694        let geometry = Geometry {
1695            crop: (0, 0, self.size.0, self.size.1),
1696            bits: self.bits,
1697        };
1698        let mut out = self
1699            .pool
1700            .take(geometry)
1701            .ok_or_else(|| format!("all {} scaled frames are still held", self.pool.ids.len()))?;
1702        self.vpp.blit(src, out.id()?, self.size)?;
1703        out.pts = src.pts;
1704        Ok(out)
1705    }
1706}
1707
1708impl Drop for Vpp {
1709    fn drop(&mut self) {
1710        if !self.owned {
1711            return;
1712        }
1713        self.display
1714            .release(None, self.context, &mut [], self.config);
1715    }
1716}
1717
1718pub fn probe() -> Result<Arc<Display>, String> {
1719    static PROBE: OnceLock<Result<(), String>> = OnceLock::new();
1720    PROBE.get_or_init(|| shared().map(|_| ())).clone()?;
1721    shared()
1722}
1723
1724fn write_frame(
1725    image: &VAImage,
1726    data: &mut [u8],
1727    frame: &Frame,
1728    size: (u32, u32),
1729) -> Result<(), String> {
1730    if !matches!(
1731        image.format.fourcc,
1732        ffi::VA_FOURCC_NV12 | ffi::VA_FOURCC_P010
1733    ) || image.num_planes < 2
1734    {
1735        return Err(format!(
1736            "the input image is not NV12 / P010 (fourcc {:#x})",
1737            image.format.fourcc
1738        ));
1739    }
1740    let [p0, p1, _] = image.pitches;
1741    let (o0, o1) = (image.offsets[0] as usize, image.offsets[1] as usize);
1742    biplanar::from_frame(frame, data, (o0, p0 as usize), (o1, p1 as usize), size)
1743}
1744
1745pub struct Slot {
1746    pub input: VASurfaceID,
1747    pub coded: ffi::VABufferID,
1748}
1749
1750pub struct EncodeSession {
1751    display: Arc<Display>,
1752    config: ffi::VAConfigID,
1753    context: ffi::VAContextID,
1754    surfaces: Vec<VASurfaceID>,
1755    coded: Vec<ffi::VABufferID>,
1756    image: Option<VAImage>,
1757    size: (u32, u32),
1758    bits: u32,
1759    inputs: usize,
1760    derive: bool,
1761    vpp: Option<Vpp>,
1762    painter: Option<Painter>,
1763}
1764
1765pub struct EncodeSetup {
1766    pub profile: ffi::VAProfile,
1767    pub rate_control: u32,
1768    pub size: (u32, u32),
1769    pub bits: u32,
1770    pub inputs: usize,
1771    pub references: usize,
1772    pub coded_size: usize,
1773}
1774
1775impl EncodeSession {
1776    pub fn new(setup: &EncodeSetup) -> Result<Self, String> {
1777        with_display(|d| Self::on(d, setup))
1778    }
1779
1780    fn on(display: Arc<Display>, setup: &EncodeSetup) -> Result<Self, String> {
1781        let (rt_format, _, _) = formats(setup.bits)?;
1782        let mut why = String::new();
1783        let mut config = None;
1784        for entrypoint in [ffi::VAEntrypointEncSlice, ffi::VAEntrypointEncSliceLP] {
1785            let mut attrs = [
1786                VAConfigAttrib {
1787                    type_: ffi::VAConfigAttribRTFormat,
1788                    value: rt_format,
1789                },
1790                VAConfigAttrib {
1791                    type_: ffi::VAConfigAttribRateControl,
1792                    value: setup.rate_control,
1793                },
1794                VAConfigAttrib {
1795                    type_: ffi::VAConfigAttribEncPackedHeaders,
1796                    value: ffi::VA_ENC_PACKED_HEADER_SEQUENCE
1797                        | ffi::VA_ENC_PACKED_HEADER_PICTURE
1798                        | ffi::VA_ENC_PACKED_HEADER_SLICE,
1799                },
1800            ];
1801            match display.create_config(setup.profile, entrypoint, &mut attrs) {
1802                Ok(id) => {
1803                    config = Some(id);
1804                    break;
1805                }
1806                Err(e) => why = e,
1807            }
1808        }
1809        let config = config.ok_or(why)?;
1810        let mut s = Self {
1811            display,
1812            config,
1813            context: ffi::VA_INVALID_ID,
1814            surfaces: Vec::new(),
1815            coded: Vec::new(),
1816            image: None,
1817            size: setup.size,
1818            bits: setup.bits,
1819            inputs: setup.inputs,
1820            derive: std::env::var_os("PITO_VA_NO_DERIVE").is_none(),
1821            vpp: None,
1822            painter: None,
1823        };
1824        s.surfaces =
1825            s.display
1826                .create_surfaces(rt_format, setup.size, setup.inputs + setup.references)?;
1827        s.context = s
1828            .display
1829            .create_context(s.config, setup.size, &mut s.surfaces)?;
1830        for _ in 0..setup.inputs {
1831            let id = s.display.buffer(
1832                s.context,
1833                ffi::VAEncCodedBufferType,
1834                std::ptr::null(),
1835                setup.coded_size,
1836            )?;
1837            s.coded.push(id);
1838        }
1839        Ok(s)
1840    }
1841
1842    pub fn vendor(&self) -> &str {
1843        self.display.vendor()
1844    }
1845
1846    pub fn slot(&self, i: usize) -> Option<Slot> {
1847        Some(Slot {
1848            input: *self.surfaces.get(i)?,
1849            coded: *self.coded.get(i)?,
1850        })
1851    }
1852
1853    pub fn reference(&self, i: usize) -> Option<VASurfaceID> {
1854        self.surfaces.get(self.inputs + i).copied()
1855    }
1856
1857    pub fn blit(
1858        &mut self,
1859        surface: VASurfaceID,
1860        src: &GpuSurface,
1861        size: (u32, u32),
1862    ) -> Result<(), String> {
1863        if !Arc::ptr_eq(&src.pool.display, &self.display) {
1864            return Err("the GPU frame is on another VA display".into());
1865        }
1866        if self.vpp.is_none() {
1867            self.vpp = Some(Vpp::new(self.display.clone())?);
1868        }
1869        match &self.vpp {
1870            Some(vpp) => vpp.blit(src, surface, size),
1871            None => Err("no VA-API video processor".into()),
1872        }
1873    }
1874
1875    pub fn composes(&self) -> bool {
1876        self.display.vendor().starts_with("Mesa")
1877    }
1878
1879    pub fn blit_over(
1880        &mut self,
1881        surface: VASurfaceID,
1882        src: &GpuSurface,
1883        size: (u32, u32),
1884        layer: &Arc<Rgba>,
1885    ) -> Result<(), String> {
1886        if !self.composes() {
1887            return Err(format!(
1888                "drawing an overlay on the GPU is tested only on Mesa's VA-API, not {}",
1889                self.display.vendor()
1890            ));
1891        }
1892        if !Arc::ptr_eq(&src.pool.display, &self.display) {
1893            return Err("the GPU frame is on another VA display".into());
1894        }
1895        if self.vpp.is_none() {
1896            self.vpp = Some(Vpp::new(self.display.clone())?);
1897        }
1898        let painter = self
1899            .painter
1900            .get_or_insert_with(|| Painter::new(self.display.clone()));
1901        let over = painter.paint(layer, size)?;
1902        match &self.vpp {
1903            Some(vpp) => vpp.compose(src.id()?, src.geometry.crop, surface, size, over),
1904            None => Err("no VA-API video processor".into()),
1905        }
1906    }
1907
1908    pub fn upload(&mut self, surface: VASurfaceID, frame: &Frame) -> Result<(), String> {
1909        let size = self.size;
1910        if self.derive {
1911            match self.display.derive(surface) {
1912                Ok(image) => {
1913                    let written = self
1914                        .display
1915                        .with_image(&image, |data| write_frame(&image, data, frame, size));
1916                    self.display.destroy_image(&image);
1917                    if written.is_ok() {
1918                        return Ok(());
1919                    }
1920                    self.derive = false;
1921                }
1922                Err(_) => self.derive = false,
1923            }
1924        }
1925        let image = match self.image {
1926            Some(i) => i,
1927            None => {
1928                let i = self.display.create_image(self.bits, self.size)?;
1929                self.image = Some(i);
1930                i
1931            }
1932        };
1933        self.display
1934            .with_image(&image, |data| write_frame(&image, data, frame, size))?;
1935        self.display.put_image(surface, &image, self.size)
1936    }
1937
1938    pub fn encode(
1939        &self,
1940        input: VASurfaceID,
1941        params: &[(ffi::VABufferType, Vec<u8>)],
1942    ) -> Result<(), String> {
1943        let mut buffers = Vec::with_capacity(params.len());
1944        let mut made = Ok(());
1945        for (ty, bytes) in params {
1946            match self
1947                .display
1948                .buffer(self.context, *ty, bytes.as_ptr().cast(), bytes.len())
1949            {
1950                Ok(id) => buffers.push(id),
1951                Err(e) => {
1952                    made = Err(e);
1953                    break;
1954                }
1955            }
1956        }
1957        let result = made.and_then(|()| {
1958            let mut groups: Vec<&mut [ffi::VABufferID]> = vec![&mut buffers[..]];
1959            self.display.picture(self.context, input, &mut groups)
1960        });
1961        self.display.destroy_buffers(&buffers);
1962        result
1963    }
1964
1965    pub fn collect(&self, input: VASurfaceID, coded: ffi::VABufferID) -> Result<Vec<u8>, String> {
1966        self.display.sync(input)?;
1967        self.display.with_mapped(coded, |ptr| {
1968            let mut out = Vec::new();
1969            let mut seg = ptr.cast::<ffi::VACodedBufferSegment>();
1970            let mut guard = 0;
1971            while !seg.is_null() && guard < 64 {
1972                let s = unsafe { *seg };
1973                if !s.buf.is_null() && s.size > 0 {
1974                    let bytes =
1975                        unsafe { std::slice::from_raw_parts(s.buf.cast::<u8>(), s.size as usize) };
1976                    out.extend_from_slice(bytes);
1977                }
1978                seg = s.next.cast();
1979                guard += 1;
1980            }
1981            Ok(out)
1982        })
1983    }
1984}
1985
1986impl Drop for EncodeSession {
1987    fn drop(&mut self) {
1988        self.painter = None;
1989        self.vpp = None;
1990        self.display.destroy_buffers(&self.coded);
1991        self.display.release(
1992            self.image.take(),
1993            self.context,
1994            &mut self.surfaces,
1995            self.config,
1996        );
1997    }
1998}
1999
2000#[cfg(test)]
2001mod tests {
2002    use super::painted_box;
2003    use crate::media::Rgba;
2004
2005    #[test]
2006    fn the_painted_box_covers_every_visible_pixel_on_even_bounds() {
2007        let mut layer = Rgba::new(16, 10);
2008        assert_eq!(painted_box(&layer), None);
2009        for (x, y) in [(3, 3), (10, 6)] {
2010            layer.data[(y * 16 + x) * 4 + 3] = 1;
2011        }
2012        assert_eq!(painted_box(&layer), Some((2, 2, 10, 6)));
2013        layer.data[(9 * 16 + 15) * 4 + 3] = 255;
2014        assert_eq!(painted_box(&layer), Some((2, 2, 14, 8)));
2015    }
2016}