gpu-video-encoder: VAAPI driver retry, Vello-capable device, Send
Three fixes found while running the zero-copy export on real Intel hardware: - vaapi::create_device() retries LIBVA_DRIVER_NAME in order iHD -> auto -> i965 -> radeonsi. libva was auto-selecting the legacy i965 driver, which fails on newer Intel GPUs; the modern iHD (intel-media-driver) is needed. encoder.rs now builds its hwdevice through this helper. - vk_device: request the adapter's full limits instead of downlevel_defaults. Vello's compute pipelines need max_storage_buffers_per_shader_stage >= 5 (downlevel caps at 4), which panicked Vello's shader init on the export device. This device only ever runs on a real VAAPI GPU. - ZeroCopyEncoder: unsafe impl Send. It owns its FFmpeg/Vulkan handles exclusively and is only moved (onto the export thread), never shared.
This commit is contained in:
parent
3e4f29c297
commit
2bce5e93a6
|
|
@ -35,6 +35,10 @@ pub struct ZeroCopyEncoder {
|
||||||
cache: HashMap<usize, ImportedNv12>,
|
cache: HashMap<usize, ImportedNv12>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The encoder owns its FFmpeg contexts (raw `*mut`) and Vulkan/wgpu handles exclusively; it is
|
||||||
|
// never shared, only moved. Sending it to a dedicated export thread is sound.
|
||||||
|
unsafe impl Send for ZeroCopyEncoder {}
|
||||||
|
|
||||||
impl ZeroCopyEncoder {
|
impl ZeroCopyEncoder {
|
||||||
/// Build a zero-copy `h264_vaapi` encoder writing to `output_path` (container inferred
|
/// Build a zero-copy `h264_vaapi` encoder writing to `output_path` (container inferred
|
||||||
/// from the extension, e.g. `.mp4`). `Err` if VAAPI/the device is unavailable.
|
/// from the extension, e.g. `.mp4`). `Err` if VAAPI/the device is unavailable.
|
||||||
|
|
@ -48,18 +52,7 @@ impl ZeroCopyEncoder {
|
||||||
let drm = vk_device::create()?;
|
let drm = vk_device::create()?;
|
||||||
let renderer = Rgba2Nv12::new(&drm.device);
|
let renderer = Rgba2Nv12::new(&drm.device);
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut hw_device: *mut ff::AVBufferRef = ptr::null_mut();
|
let mut hw_device = crate::vaapi::create_device()?;
|
||||||
let node = CString::new("/dev/dri/renderD128").unwrap();
|
|
||||||
if ff::av_hwdevice_ctx_create(
|
|
||||||
&mut hw_device,
|
|
||||||
ff::AVHWDeviceType::AV_HWDEVICE_TYPE_VAAPI,
|
|
||||||
node.as_ptr(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
0,
|
|
||||||
) < 0
|
|
||||||
{
|
|
||||||
return Err("av_hwdevice_ctx_create failed".into());
|
|
||||||
}
|
|
||||||
let name = CString::new("h264_vaapi").unwrap();
|
let name = CString::new("h264_vaapi").unwrap();
|
||||||
let codec = ff::avcodec_find_encoder_by_name(name.as_ptr());
|
let codec = ff::avcodec_find_encoder_by_name(name.as_ptr());
|
||||||
if codec.is_null() {
|
if codec.is_null() {
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,48 @@ fn averror(e: i32) -> i32 {
|
||||||
-e
|
-e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a VAAPI hwdevice on `/dev/dri/renderD128`, trying driver names in turn.
|
||||||
|
///
|
||||||
|
/// libva's auto-selection can pick a driver that doesn't support the GPU — notably it
|
||||||
|
/// chooses the legacy `i965` driver on newer Intel parts (Gen 11+) where the modern `iHD`
|
||||||
|
/// driver is required. Each `av_hwdevice_ctx_create` opens a fresh VADisplay, so
|
||||||
|
/// `LIBVA_DRIVER_NAME` is re-read per attempt. We try `iHD` first (modern Intel), then the
|
||||||
|
/// caller's original setting, then `i965` (older Intel) and `radeonsi` (AMD). On success the
|
||||||
|
/// working driver name is left in the env; on total failure the original value is restored.
|
||||||
|
pub fn create_device() -> Result<*mut ff::AVBufferRef, String> {
|
||||||
|
unsafe {
|
||||||
|
let node = CString::new("/dev/dri/renderD128").unwrap();
|
||||||
|
let original = std::env::var_os("LIBVA_DRIVER_NAME");
|
||||||
|
let attempts: [Option<&str>; 4] = [Some("iHD"), None, Some("i965"), Some("radeonsi")];
|
||||||
|
for drv in attempts {
|
||||||
|
match drv {
|
||||||
|
Some(d) => std::env::set_var("LIBVA_DRIVER_NAME", d),
|
||||||
|
// `None` = the caller's original setting (or libva auto if unset).
|
||||||
|
None => match &original {
|
||||||
|
Some(v) => std::env::set_var("LIBVA_DRIVER_NAME", v),
|
||||||
|
None => std::env::remove_var("LIBVA_DRIVER_NAME"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
let mut hw: *mut ff::AVBufferRef = ptr::null_mut();
|
||||||
|
if ff::av_hwdevice_ctx_create(
|
||||||
|
&mut hw,
|
||||||
|
ff::AVHWDeviceType::AV_HWDEVICE_TYPE_VAAPI,
|
||||||
|
node.as_ptr(),
|
||||||
|
ptr::null_mut(),
|
||||||
|
0,
|
||||||
|
) >= 0
|
||||||
|
{
|
||||||
|
return Ok(hw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match &original {
|
||||||
|
Some(v) => std::env::set_var("LIBVA_DRIVER_NAME", v),
|
||||||
|
None => std::env::remove_var("LIBVA_DRIVER_NAME"),
|
||||||
|
}
|
||||||
|
Err("av_hwdevice_ctx_create(VAAPI) failed for all drivers (iHD/i965/radeonsi)".into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Copy tight NV12 (`Y` then interleaved `UV`) into an AVFrame's planes, respecting
|
/// Copy tight NV12 (`Y` then interleaved `UV`) into an AVFrame's planes, respecting
|
||||||
/// each plane's linesize (which FFmpeg may pad).
|
/// each plane's linesize (which FFmpeg may pad).
|
||||||
unsafe fn fill_nv12(frame: *mut ff::AVFrame, nv12: &[u8], width: u32, height: u32) {
|
unsafe fn fill_nv12(frame: *mut ff::AVFrame, nv12: &[u8], width: u32, height: u32) {
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,10 @@ unsafe fn create_inner() -> Result<DrmDevice, String> {
|
||||||
&wgpu::DeviceDescriptor {
|
&wgpu::DeviceDescriptor {
|
||||||
label: Some("drm-import-device"),
|
label: Some("drm-import-device"),
|
||||||
required_features: wgpu::Features::empty(),
|
required_features: wgpu::Features::empty(),
|
||||||
required_limits: wgpu::Limits::downlevel_defaults(),
|
// Vello's compute pipelines need more than downlevel limits (e.g.
|
||||||
|
// max_storage_buffers_per_shader_stage >= 5). This device only ever runs on a
|
||||||
|
// real VAAPI-capable GPU, so request the adapter's full limits.
|
||||||
|
required_limits: wgpu_adapter.limits(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue