Skip to content

Commit

Permalink
Work around Mesa bug, improve some APIs a bit
Browse files Browse the repository at this point in the history
  • Loading branch information
SludgePhD committed Mar 8, 2024
1 parent d68a454 commit a6951c7
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 73 deletions.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@ softbuffer = "0.3.0"
jpeg-decoder = "0.3.0"
anyhow = "1.0.68"
expect-test = "1.4.0"

[profile.dev.package."*"]
opt-level = 3
12 changes: 10 additions & 2 deletions examples/infodump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,16 @@ fn main() -> Result<(), Box<dyn Error>> {
"- Output Color Standards: {:?}",
caps.output_color_standards()
);
println!("- Input Pixel Formats: {:?}", caps.input_pixel_formats());
println!("- Output Pixel Formats: {:?}", caps.output_pixel_formats());
print!("- Input Pixel Formats: ");
match caps.input_pixel_formats() {
Some(fmts) => println!("{fmts:?}"),
None => println!("<unknown>"),
}
print!("- Output Pixel Formats: ");
match caps.output_pixel_formats() {
Some(fmts) => println!("{fmts:?}"),
None => println!("<unknown>"),
}
}

Ok(())
Expand Down
20 changes: 16 additions & 4 deletions examples/jpeg-decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ use std::{num::NonZeroU32, rc::Rc, time::Instant};
use anyhow::bail;
use fev::{
display::Display,
image::{Image, ImageFormat},
jpeg::{JpegDecodeSession, JpegInfo},
surface::ExportSurfaceFlags,
PixelFormat,
};
use softbuffer::{Context, Surface};
use winit::{
dpi::PhysicalSize,
event::{ElementState, Event, KeyboardInput, MouseButton, VirtualKeyCode, WindowEvent},
Expand Down Expand Up @@ -49,8 +50,8 @@ fn main() -> anyhow::Result<()> {
.build(&ev)?;
let win = Rc::new(win);

let graphics_context = unsafe { Context::new(&win).unwrap() };
let mut surface = unsafe { Surface::new(&graphics_context, &win).unwrap() };
let graphics_context = unsafe { softbuffer::Context::new(&win).unwrap() };
let mut surface = unsafe { softbuffer::Surface::new(&graphics_context, &win).unwrap() };
let PhysicalSize { width, height } = win.inner_size();
surface
.resize(
Expand All @@ -68,8 +69,19 @@ fn main() -> anyhow::Result<()> {
.export_prime(ExportSurfaceFlags::SEPARATE_LAYERS | ExportSurfaceFlags::READ)?;
log::debug!("PRIME export: {prime:#?}");

let mut image = Image::new(
&display,
ImageFormat::new(PixelFormat::RGBA),
jpeg_info.width().into(),
jpeg_info.height().into(),
)?;

log::debug!("<decode>");
let start = Instant::now();
let surf = context.decode_and_convert(&jpeg)?;
let mapping = surf.map_sync()?;
log::debug!("</decode> took {:?}", start.elapsed());
surf.copy_to_image(&mut image)?;
let mapping = image.map()?;

log::debug!("{} byte output", mapping.len());

Expand Down
2 changes: 1 addition & 1 deletion src/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ impl Display {
Ok(Entrypoints { vec: entrypoints })
}

/// Queries the supported [`ImageFormat`][crate::image::ImageFormat]s.
/// Queries the supported [`ImageFormat`]s.
pub fn query_image_formats(&self) -> Result<ImageFormats> {
unsafe {
let max = self.d.libva.vaMaxNumImageFormats(self.d.raw) as usize;
Expand Down
35 changes: 32 additions & 3 deletions src/dlopen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
#![allow(bad_style)]

use std::{
ffi::c_void,
os::raw::{c_char, c_float, c_int, c_uchar, c_uint},
os::raw::{c_char, c_float, c_int, c_short, c_uchar, c_uint, c_ulong, c_ushort, c_void},
sync::OnceLock,
};

Expand Down Expand Up @@ -133,7 +132,19 @@ dylib! {
fn vaDestroyImage(dpy: VADisplay, image: VAImageID) -> VAStatus;
fn vaSetImagePalette(dpy: VADisplay, image: VAImageID, palette: *mut c_uchar) -> VAStatus;
fn vaGetImage(dpy: VADisplay, surface: VASurfaceID, x: c_int, y: c_int, width: c_uint, height: c_uint, image: VAImageID) -> VAStatus;
fn vaPutImage(dpy: VADisplay, surface: VASurfaceID, image: VAImageID, src_x: c_int, src_y: c_int, src_width: c_uint, src_height: c_uint, dest_x: c_int, dest_y: c_int, dest_width: c_uint, dest_height: c_uint) -> VAStatus;
fn vaPutImage(
dpy: VADisplay,
surface: VASurfaceID,
image: VAImageID,
src_x: c_int,
src_y: c_int,
src_width: c_uint,
src_height: c_uint,
dest_x: c_int,
dest_y: c_int,
dest_width: c_uint,
dest_height: c_uint
) -> VAStatus;
fn vaDeriveImage(dpy: VADisplay, surface: VASurfaceID, image: *mut VAImage) -> VAStatus;
fn vaMaxNumSubpictureFormats(dpy: VADisplay) -> c_int;
fn vaQuerySubpictureFormats(dpy: VADisplay, format_list: *mut ImageFormat, flags: *mut c_uint, num_formats: *mut c_uint) -> VAStatus;
Expand Down Expand Up @@ -172,6 +183,22 @@ dylib! {
pub struct libva_x11;

fn vaGetDisplay(dpy: *mut Display) -> VADisplay;
fn vaPutSurface(
dpy: VADisplay,
surface: VASurfaceID,
draw: Drawable,
srcx: c_short,
srcy: c_short,
srcw: c_ushort,
srch: c_ushort,
destx: c_short,
desty: c_short,
destw: c_ushort,
desth: c_ushort,
cliprects: *mut Rectangle,
number_cliprects: c_uint,
flags: c_uint
) -> VAStatus;
}

dylib! {
Expand All @@ -193,3 +220,5 @@ pub struct wl_buffer;

/// Opaque type representing the Xlib X11 `Display` type.
pub struct Display;

pub type Drawable = c_ulong;
33 changes: 18 additions & 15 deletions src/jpeg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ use crate::{
context::Context,
display::Display,
error::Error,
raw::{VA_PADDING_LOW, VA_PADDING_MEDIUM},
surface::SurfaceWithImage,
raw::{Rectangle, VA_PADDING_LOW, VA_PADDING_MEDIUM},
surface::{RTFormat, Surface},
vpp::{ColorProperties, ColorStandardType, ProcPipelineParameterBuffer, SourceRange},
Entrypoint, PixelFormat, Profile, Result, Rotation, SliceParameterBufferBase,
};
Expand Down Expand Up @@ -63,7 +63,8 @@ pub struct PictureParameterBuffer {
num_components: u8,
color_space: ColorSpace,
rotation: Rotation,
va_reserved: [u32; VA_PADDING_MEDIUM - 1],
crop_rectangle: Rectangle,
va_reserved: [u32; VA_PADDING_MEDIUM - 3],
}

#[derive(Clone, Copy)]
Expand Down Expand Up @@ -420,8 +421,8 @@ pub struct JpegDecodeSession {
width: u32,
height: u32,

jpeg_surface: SurfaceWithImage,
vpp_surface: SurfaceWithImage,
jpeg_surface: Surface,
vpp_surface: Surface,

jpeg_context: Context,
vpp_context: Context,
Expand All @@ -446,10 +447,8 @@ impl JpegDecodeSession {
let config = Config::new(&display, Profile::None, Entrypoint::VideoProc)?;
let vpp_context = Context::new(&config, width, height)?;

let jpeg_surface = SurfaceWithImage::new(&display, width, height, PixelFormat::NV12)?;
let vpp_surface = SurfaceWithImage::new(&display, width, height, PixelFormat::RGBA)?;

log::debug!("image format = {:?}", vpp_surface.image());
let jpeg_surface = Surface::new(&display, width, height, RTFormat::YUV420)?;
let vpp_surface = Surface::with_pixel_format(&display, width, height, PixelFormat::RGBA)?;

Ok(Self {
width,
Expand All @@ -462,19 +461,19 @@ impl JpegDecodeSession {
}

#[inline]
pub fn surface(&mut self) -> &mut SurfaceWithImage {
pub fn surface(&mut self) -> &mut Surface {
&mut self.jpeg_surface
}

/// Decodes a baseline JPEG, returning a [`SurfaceWithImage`] containing the decoded image.
/// Decodes a baseline JPEG, returning a [`Surface`] containing the decoded image.
///
/// The decoded image is in the JPEG's native color space and uses an unspecified pixel format.
///
/// # Errors
///
/// This method returns an error when the JPEG is malformed or VA-API returns an error during
/// decoding.
pub fn decode(&mut self, jpeg: &[u8]) -> Result<&mut SurfaceWithImage> {
pub fn decode(&mut self, jpeg: &[u8]) -> Result<&mut Surface> {
// TODO make this more flexible and move to `error` module
macro_rules! bail {
($($args:tt)*) => {
Expand Down Expand Up @@ -589,8 +588,12 @@ impl JpegDecodeSession {
}
}

let Some(ppbuf) = ppbuf else { bail!("file is missing SOI segment") };
let Some((slice_params, slice_data)) = slice else { bail!("file is missing SOS header") };
let Some(ppbuf) = ppbuf else {
bail!("file is missing SOI segment")
};
let Some((slice_params, slice_data)) = slice else {
bail!("file is missing SOS header")
};

let mut buf_dht = Buffer::new_param(&self.jpeg_context, BufferType::HuffmanTable, dhtbuf)?;
let mut buf_iq = Buffer::new_param(&self.jpeg_context, BufferType::IQMatrix, iqbuf)?;
Expand All @@ -612,7 +615,7 @@ impl JpegDecodeSession {
Ok(&mut self.jpeg_surface)
}

pub fn decode_and_convert(&mut self, jpeg: &[u8]) -> Result<&mut SurfaceWithImage> {
pub fn decode_and_convert(&mut self, jpeg: &[u8]) -> Result<&mut Surface> {
self.decode(jpeg)?;

let mut pppbuf = ProcPipelineParameterBuffer::new(&self.jpeg_surface);
Expand Down
9 changes: 7 additions & 2 deletions src/pixelformat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,14 @@ impl PixelFormat {

/// Interleaved YUV 4:2:2, stored in memory as `yyyyyyyy uuuuuuuu YYYYYYYY vvvvvvvv`.
///
/// `uuuuuuuu` and `vvvvvvvv` are shared by 2 neighboring pixels.
/// `uuuuuuuu` and `vvvvvvvv` are shared by 2 horizontally neighboring pixels.
///
/// Also known as [`YUYV`](Self::YUYV).
pub const YUY2: Self = f(b"YUY2");

/// Identical to [`YUY2`](Self::YUY2).
pub const YUYV: Self = f(b"YUYV");

/// Interleaved YUV 4:2:2, stored in memory as `uuuuuuuu yyyyyyyy vvvvvvvv YYYYYYYY`.
///
/// `uuuuuuuu` and `vvvvvvvv` are shared by 2 neighboring pixels.
Expand Down Expand Up @@ -72,7 +77,7 @@ impl PixelFormat {
pub fn to_rtformat(self) -> Option<RTFormat> {
Some(match self {
Self::NV12 | Self::NV21 => RTFormat::YUV420,
Self::YUY2 | Self::UYVY => RTFormat::YUV422,
Self::YUY2 | Self::YUYV | Self::UYVY => RTFormat::YUV422,
Self::RGBA | Self::RGBX | Self::ARGB | Self::BGRA | Self::BGRX => RTFormat::RGB32,
_ => return None,
})
Expand Down
52 changes: 34 additions & 18 deletions src/surface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,9 +326,33 @@ pub struct Surface {

impl Surface {
pub fn new(display: &Display, width: u32, height: u32, format: RTFormat) -> Result<Self> {
log::trace!("creating {width}x{height} surface with {format:?}");
Self::with_attribs(display, width, height, format, &mut [])
}

pub fn with_pixel_format(
display: &Display,
width: u32,
height: u32,
format: PixelFormat,
) -> Result<Self> {
let rtformat = format.to_rtformat().ok_or_else(|| {
Error::from(format!(
"no RTFormat to go with the requested pixel format {:?}",
format
))
})?;

log::trace!("creating {width}x{height} surface with format {format:?} and {rtformat:?}");
Self::with_attribs(
&display,
width,
height,
rtformat,
&mut [SurfaceAttribEnum::PixelFormat(format).into()],
)
}

pub fn with_attribs(
display: &Display,
width: u32,
Expand Down Expand Up @@ -458,7 +482,9 @@ impl Surface {
/// Creates an [`Image`] that allows direct access to the surface's image data.
///
/// Only supported by some drivers, and only for some surface formats. Will return
/// [`VAError::ERROR_OPERATION_FAILED`] if it's not supported.
/// [`VAError::ERROR_OPERATION_FAILED`] if it's not supported. In that case, the caller should
/// fall back to creating an [`Image`] manually and using [`Surface::copy_to_image`]. The
/// [`SurfaceWithImage`] type encapsulates that pattern and should be used for this if possible.
pub fn derive_image(&mut self) -> Result<Image> {
unsafe {
let mut image = MaybeUninit::uninit();
Expand Down Expand Up @@ -531,28 +557,14 @@ impl SurfaceWithImage {
/// The [`RTFormat`] of the [`Surface`] will be determined automatically based on the specified
/// [`PixelFormat`].
pub fn new(display: &Display, width: u32, height: u32, format: PixelFormat) -> Result<Self> {
let rtformat = format.to_rtformat().ok_or_else(|| {
Error::from(format!(
"pixel format {:?} is unknown or unimplemented",
format
))
})?;

let mut surface = Surface::with_attribs(
&display,
width,
height,
rtformat,
&mut [SurfaceAttribEnum::PixelFormat(format).into()],
)?;
let mut surface = Surface::with_pixel_format(display, width, height, format)?;

// Try to use `vaDeriveImage` first, fall back if that fails.
match surface.derive_image() {
Ok(image) => {
log::trace!(
"using vaDeriveImage for fast surface access \
(surface format = {:?}, image format = {:?})",
rtformat,
(image format = {:?})",
format,
);

Expand All @@ -563,7 +575,10 @@ impl SurfaceWithImage {
})
}
Err(e) if e.as_libva() == Some(VAError::ERROR_OPERATION_FAILED) => {
log::trace!("vaDeriveImage not supported, using vaGetImage (surface format = {:?}, image format = {:?})", rtformat, format);
log::trace!(
"vaDeriveImage not supported, using vaGetImage (simage format = {:?})",
format
);

let image = Image::new(display, ImageFormat::new(format), width, height)?;
Ok(Self {
Expand All @@ -586,6 +601,7 @@ impl SurfaceWithImage {
&self.image
}

/// Synchronizes the [`Surface`] and [`Image`] contents and maps the [`Image`] into memory.
pub fn map_sync(&mut self) -> Result<Mapping<'_, u8>> {
if self.derived {
self.surface.sync()?;
Expand Down
Loading

0 comments on commit a6951c7

Please sign in to comment.