From f77eec12993ac337b3b577a71719418c00919a1c Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Wed, 15 Jul 2026 01:42:15 +0200 Subject: [PATCH] feat(client): PyroWave planar present path + Linux NVENC match-arm fix (Phase 2b, part 2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The arch package job (--features nvenc) tripped the same class of Codec::PyroWave non-exhaustive matches as windows-host had, in nvenc_cuda.rs (6 sites) — dispatch-guarded unreachable!() arms, plus the vk_util-extraction leftover unused imports in vulkan_video.rs. All Linux host feature combos (none / pyrowave / nvenc,vulkan-encode / all three) now compile clean on .21. Presenter: planar_csc.frag (+ committed .spv) — the 3-plane variant of nv12_csc.frag (separate Cb/Cr R8 planes, same push-constant CSC-row contract, siting correction self-disables at full-res chroma). CscPass grows a shared builder + new_planar()/bind_planes_planar() (GENERAL-layout descriptors — pyrowave planes stay GENERAL); the Vk presenter builds the planar pass when the device passed the pyrowave probe, FrameInput::PyroWave rides present_frame (no acquire barrier needed: the decoder fence-completed and barriered the planes on the same queue), and run.rs presents it with no demote rung (only device loss ends the session). Co-Authored-By: Claude Fable 5 --- crates/pf-presenter/Cargo.toml | 5 + crates/pf-presenter/shaders/planar_csc.frag | 84 +++++++++++ .../pf-presenter/shaders/planar_csc.frag.spv | Bin 0 -> 3864 bytes crates/pf-presenter/src/csc.rs | 74 ++++++++-- crates/pf-presenter/src/run.rs | 22 +++ crates/pf-presenter/src/vk.rs | 137 ++++++++++++++++++ .../src/encode/linux/nvenc_cuda.rs | 9 ++ .../src/encode/linux/vulkan_video.rs | 8 +- 8 files changed, 318 insertions(+), 21 deletions(-) create mode 100644 crates/pf-presenter/shaders/planar_csc.frag create mode 100644 crates/pf-presenter/shaders/planar_csc.frag.spv diff --git a/crates/pf-presenter/Cargo.toml b/crates/pf-presenter/Cargo.toml index 4e86564c..68f0024c 100644 --- a/crates/pf-presenter/Cargo.toml +++ b/crates/pf-presenter/Cargo.toml @@ -43,3 +43,8 @@ windows-sys = { version = "0.61", features = [ "Win32_UI_Shell", "Win32_UI_WindowsAndMessaging", ] } + +[features] +# PyroWave planar present path (the wired-LAN wavelet codec) — forwards to the decode +# backend in pf-client-core; OFF by default. +pyrowave = ["pf-client-core/pyrowave"] diff --git a/crates/pf-presenter/shaders/planar_csc.frag b/crates/pf-presenter/shaders/planar_csc.frag new file mode 100644 index 00000000..f28afb4b --- /dev/null +++ b/crates/pf-presenter/shaders/planar_csc.frag @@ -0,0 +1,84 @@ +// Planar 3-plane YCbCr → RGBA — the PyroWave variant of nv12_csc.frag (separate Cb and +// Cr R8 planes instead of an interleaved CbCr plane; design/pyrowave-codec-plan.md §4.5). +// Same push-constant contract (csc_rows precomputes the matrix + range expansion), same +// output modes — though PyroWave itself is 8-bit SDR BT.709 limited, keeping parity means +// one less divergence if the codec ever signals more. 4:4:4 needs no shader change: the +// chroma planes arrive full-res and the siting correction self-disables. +// +// Regenerate: shaders/build.sh (committed .spv, no build-time toolchain). +#version 450 + +layout(location = 0) in vec2 v_uv; +layout(location = 0) out vec4 frag; + +layout(set = 0, binding = 0) uniform sampler2D u_y; +layout(set = 0, binding = 1) uniform sampler2D u_cb; +layout(set = 0, binding = 2) uniform sampler2D u_cr; + +layout(push_constant) uniform Csc { + vec4 r0; + vec4 r1; + vec4 r2; + vec4 params; // x: mode, y: tonemap peak, z/w: reserved +} pc; + +// SMPTE ST.2084 (PQ) EOTF: code value → display-referred linear, normalized to 1.0 = +// 10000 nits. +vec3 pq_eotf(vec3 e) { + const float m1 = 0.1593017578125; // 2610/16384 + const float m2 = 78.84375; // 2523/4096 * 128 + const float c1 = 0.8359375; // 3424/4096 + const float c2 = 18.8515625; // 2413/4096 * 32 + const float c3 = 18.6875; // 2392/4096 * 32 + vec3 p = pow(max(e, vec3(0.0)), vec3(1.0 / m2)); + return pow(max(p - c1, vec3(0.0)) / (c2 - c3 * p), vec3(1.0 / m1)); +} + +// BT.2020 → BT.709 primaries (linear light). +vec3 bt2020_to_709(vec3 c) { + return mat3( + 1.6605, -0.1246, -0.0182, + -0.5876, 1.1329, -0.1006, + -0.0728, -0.0083, 1.1187 + ) * c; +} + +// Linear → sRGB OETF. +vec3 srgb_oetf(vec3 c) { + c = clamp(c, 0.0, 1.0); + bvec3 lo = lessThanEqual(c, vec3(0.0031308)); + vec3 hi = 1.055 * pow(c, vec3(1.0 / 2.4)) - 0.055; + return mix(hi, c * 12.92, vec3(lo)); +} + +void main() { + // Left-cosited 4:2:0 chroma sampled at luma UV assumes CENTER siting — offset +0.25 + // chroma texels to re-align (same correction as nv12_csc.frag; self-disables when the + // chroma plane is full-res). + vec2 cuv = v_uv; + int cw = textureSize(u_cb, 0).x; + if (cw < textureSize(u_y, 0).x) { + cuv.x += 0.25 / float(cw); + } + vec3 yuv = vec3(texture(u_y, v_uv).r, texture(u_cb, cuv).r, texture(u_cr, cuv).r); + vec3 rgb = vec3( + dot(pc.r0.xyz, yuv) + pc.r0.w, + dot(pc.r1.xyz, yuv) + pc.r1.w, + dot(pc.r2.xyz, yuv) + pc.r2.w + ); + + if (pc.params.x > 0.5) { + vec3 lin = pq_eotf(clamp(rgb, 0.0, 1.0)) * (10000.0 / 203.0); + lin = max(bt2020_to_709(lin), vec3(0.0)); + float peak = max(pc.params.y, 1.0001); + float l = max(lin.r, max(lin.g, lin.b)); + if (l > 1.0) { + float mapped = 1.0 + (l - 1.0) / (1.0 + (l - 1.0) / (peak - 1.0)); + lin *= mapped / l; + } + rgb = srgb_oetf(lin); + } else { + rgb = clamp(rgb, 0.0, 1.0); + } + frag = vec4(rgb, 1.0); +} diff --git a/crates/pf-presenter/shaders/planar_csc.frag.spv b/crates/pf-presenter/shaders/planar_csc.frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..ba08afdcf4d42ab415c21ee0923e718a7ef99b6b GIT binary patch literal 3864 zcmZ9OX^2);7{~9-sN%FtLtsH@1{PFg(T6sOK2)fs)$eyZ&+Em*Jf8pm|19S@=bn3Ct15?8XIVwo zC+nAuugLOodRB>&WzJ)n`(!E2V26A)vwlbLqJJa0{>tQ+pY&H{k0#&Dtb2~3 z*)acMgy!060}7h;)Jg@dDrp{oy|};F%b9dwP96I-PFw@(!Cb=5Cf+lU>#v842V#zq zF#R={T3E!@qHWvXdP&3$f~$o^+z_-ZySg!WNqTTgQ*h5JYwsxY;n1bueSB%u9RXJh ztEJD8aC_fagIQcWmznS(4OdKwb!NfU!lGUa+SJ}_%ln)SpEvLGX_40o zR||{0xoF#)7fua6A3k^I?54zkYXO{f)Wb_q9j<9WbV7_Wc-IFU)U-dGg1TrvHhA&5OP|!9IWe zTba#w?bzoF;EDp@4tAg6-w7_>pFMC!`CjaUwDr0CvRH3FJnFg5L9Dr|z0viLf~{qL z_AmDvr55*N49R{Y!a^U5UP2EGeH^-XE^H#-QTz979o9aI(w%uKs&{HJwkYu>%*!yJ z|6=^!krhe1jx3-3jY;!PT#4pgSe109ysmX_#eD8j=eDFpo!jAR#X5Ju)g3#iJ)QYZ z%=-F{u!Fmq9e>fEzPm6r_a$D5?pfc9nfCy7>kk2jc!u^iw^BZB^eqih9i@N>6 z{AKo`-(I8cM_~Q?m^Y;wXTT@J^+kE4iEqIA8!_+1x6BUjgucU= zns-9%vvVKcW04oy4`}YsTJgR95p1l!xZ6L0jnNlzKZA|a7jeIUjnfx#zk=0WJD%Tf zV9!XO-}LC$*>8F^^C%9-81J?5?j`Q`V0@lkJ!YKGwGHcHuQQm3pcyj@9Cu+ToWJ}& z>1zc?Kj*^rx1hycuY;@m+a7V}gUy{!+$)@_eO>@}J=eF7=y5n2fBFCAt!1vU?$`I& z=i|E@@khYMyQktiTZ}g^&Sx~iuHl)PYpu9%e}nZqLOTvtQx5G!(!7^**p0s}|6t}D Tuixi6jeB;CS>FjNOv3&Hg Result { + Self::build( + device, + attachment_format, + 2, + include_bytes!("../shaders/nv12_csc.frag.spv"), + ) + } + + /// The planar 3-plane variant (separate Cb/Cr R8 planes — the PyroWave decode + /// output, design/pyrowave-codec-plan.md §4.5). Same push-constant contract. + #[cfg(feature = "pyrowave")] + pub fn new_planar(device: &ash::Device, attachment_format: vk::Format) -> Result { + Self::build( + device, + attachment_format, + 3, + include_bytes!("../shaders/planar_csc.frag.spv"), + ) + } + + fn build( + device: &ash::Device, + attachment_format: vk::Format, + plane_bindings: u32, + frag_spv: &[u8], + ) -> Result { // One color attachment: the presenter's video image. Content is fully // overwritten (DONT_CARE load), and the pass ends in TRANSFER_SRC so the // existing letterbox blit consumes it with no extra barrier. @@ -89,20 +115,16 @@ impl CscPass { }?; let samplers = [sampler]; - let bindings = [ - vk::DescriptorSetLayoutBinding::default() - .binding(0) - .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) - .descriptor_count(1) - .stage_flags(vk::ShaderStageFlags::FRAGMENT) - .immutable_samplers(&samplers), - vk::DescriptorSetLayoutBinding::default() - .binding(1) - .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) - .descriptor_count(1) - .stage_flags(vk::ShaderStageFlags::FRAGMENT) - .immutable_samplers(&samplers), - ]; + let bindings: Vec = (0..plane_bindings) + .map(|b| { + vk::DescriptorSetLayoutBinding::default() + .binding(b) + .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) + .descriptor_count(1) + .stage_flags(vk::ShaderStageFlags::FRAGMENT) + .immutable_samplers(&samplers) + }) + .collect(); let set_layout = unsafe { device.create_descriptor_set_layout( &vk::DescriptorSetLayoutCreateInfo::default().bindings(&bindings), @@ -124,7 +146,7 @@ impl CscPass { let pool_sizes = [vk::DescriptorPoolSize::default() .ty(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) - .descriptor_count(2)]; + .descriptor_count(plane_bindings)]; let desc_pool = unsafe { device.create_descriptor_pool( &vk::DescriptorPoolCreateInfo::default() @@ -145,7 +167,7 @@ impl CscPass { device, render_pass, pipeline_layout, - include_bytes!("../shaders/nv12_csc.frag.spv"), + frag_spv, false, // opaque — the CSC output IS the video )?; @@ -184,6 +206,26 @@ impl CscPass { unsafe { device.update_descriptor_sets(&writes, &[]) }; } + /// Planar variant of [`bind_planes`](Self::bind_planes): three single-component + /// plane views in GENERAL layout (the pyrowave decode leaves them there; same + /// fence-wait safety contract). + #[cfg(feature = "pyrowave")] + pub fn bind_planes_planar(&self, device: &ash::Device, planes: [vk::ImageView; 3]) { + let infos = planes.map(|view| { + [vk::DescriptorImageInfo::default() + .image_view(view) + .image_layout(vk::ImageLayout::GENERAL)] + }); + let writes = [0u32, 1, 2].map(|b| { + vk::WriteDescriptorSet::default() + .dst_set(self.desc_set) + .dst_binding(b) + .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) + .image_info(&infos[b as usize]) + }); + unsafe { device.update_descriptor_sets(&writes, &[]) }; + } + pub fn destroy(&self, device: &ash::Device) { unsafe { device.destroy_pipeline(self.pipeline, None); diff --git a/crates/pf-presenter/src/run.rs b/crates/pf-presenter/src/run.rs index 12f9d16f..adda6388 100644 --- a/crates/pf-presenter/src/run.rs +++ b/crates/pf-presenter/src/run.rs @@ -968,6 +968,28 @@ fn run_inner(mut opts: SessionOpts, mut mode: ModeCtl) -> Result image, } = f; let did_present = match image { + // PyroWave planar frames: already on the presenter's device and + // fence-complete — a present failure has no demote rung (nothing + // else decodes the codec); only device loss ends the session. + #[cfg(all(target_os = "linux", feature = "pyrowave"))] + DecodedImage::PyroWave(f) => { + st.hdr = false; // 8-bit SDR codec + match presenter.present( + &window, + FrameInput::PyroWave(f), + overlay_frame.as_ref(), + ) { + Ok(p) => p, + Err(e) => { + if device_lost(&e) { + return Err(e) + .context("GPU device lost — the session cannot continue"); + } + tracing::warn!(error = %format!("{e:#}"), "pyrowave present failed"); + false + } + } + } DecodedImage::Cpu(c) => { st.hdr = c.color.is_pq(); presenter.present(&window, FrameInput::Cpu(&c), overlay_frame.as_ref())? diff --git a/crates/pf-presenter/src/vk.rs b/crates/pf-presenter/src/vk.rs index 38fa93fe..eff7528e 100644 --- a/crates/pf-presenter/src/vk.rs +++ b/crates/pf-presenter/src/vk.rs @@ -40,6 +40,10 @@ pub enum FrameInput<'a> { /// D3D11VA hand-off — a shareable NT-handle texture to import (`d3d11.rs`). #[cfg(windows)] D3d11(pf_client_core::video::D3d11Frame), + /// PyroWave planar output — three R8 plane views already on THIS device, decode + /// fence-complete, GENERAL layout (`pf_client_core::video_pyrowave`). + #[cfg(all(target_os = "linux", feature = "pyrowave"))] + PyroWave(pf_client_core::video_pyrowave::PyroWavePlanarFrame), } /// The dmabuf/CSC machinery, present only when the device carries the import extensions. @@ -321,6 +325,10 @@ pub struct Presenter { #[cfg(windows)] hw_win: Option, csc: CscPass, + /// The planar (3-plane) CSC variant for PyroWave frames; built only when the device + /// passed the pyrowave probe. + #[cfg(all(target_os = "linux", feature = "pyrowave"))] + csc_planar: Option, /// FFmpeg Vulkan Video decode handles — `None` when the stack can't do it. video_export: Option, /// The console-UI composite quad (§6.1's presenter half). @@ -641,6 +649,13 @@ impl Presenter { ext_mem_win32: ash::khr::external_memory_win32::Device::new(&instance, &device), }); let csc = CscPass::new(&device, vk::Format::R8G8B8A8_UNORM)?; + // PyroWave is 8-bit SDR only, so the planar pass never needs the HDR10 rebuild. + #[cfg(all(target_os = "linux", feature = "pyrowave"))] + let csc_planar = if pyrowave_ok { + Some(CscPass::new_planar(&device, vk::Format::R8G8B8A8_UNORM)?) + } else { + None + }; // The exported handle bundle: FFmpeg Vulkan Video handles when the device can // decode, AND (Windows) the D3D11-interop facts — so it's built whenever EITHER @@ -769,6 +784,8 @@ impl Presenter { #[cfg(windows)] hw_win, csc, + #[cfg(all(target_os = "linux", feature = "pyrowave"))] + csc_planar, video_export, overlay_pipe, retired_hw: None, @@ -1044,6 +1061,10 @@ impl Presenter { vk::Format::R8G8B8A8_UNORM }; self.csc.destroy(&self.device); // fence-safe: only our cmd bufs reference it + #[cfg(all(target_os = "linux", feature = "pyrowave"))] + if let Some(p) = &self.csc_planar { + p.destroy(&self.device); + } self.csc = CscPass::new(&self.device, self.video_format)?; if let Some(v) = self.video.take() { unsafe { @@ -1102,6 +1123,8 @@ impl Presenter { FrameInput::VkFrame(v) => Some(v.color.is_pq()), #[cfg(windows)] FrameInput::D3d11(d) => Some(d.color.is_pq()), + #[cfg(all(target_os = "linux", feature = "pyrowave"))] + FrameInput::PyroWave(f) => Some(f.color.is_pq()), // always SDR today }; if let Some(pq) = frame_pq { // A PQ stream we can only tone-map (no HDR10 surface) is the silent failure behind @@ -1130,6 +1153,8 @@ impl Presenter { #[cfg(windows)] let mut win_frame: Option = None; let mut vk_frame: Option<(VkVideoFrame, [vk::ImageView; 2])> = None; + #[cfg(all(target_os = "linux", feature = "pyrowave"))] + let mut pyro_frame: Option = None; let cpu_frame = match input { FrameInput::Redraw => None, FrameInput::Cpu(f) => Some(f), @@ -1156,6 +1181,11 @@ impl Presenter { vk_frame = Some((v, views)); None } + #[cfg(all(target_os = "linux", feature = "pyrowave"))] + FrameInput::PyroWave(f) => { + pyro_frame = Some(f); + None + } }; // One frame in flight: the fence covers the command buffer, the staging buffer @@ -1210,6 +1240,22 @@ impl Presenter { } self.csc.bind_planes(&self.device, views[0], views[1]); } + #[cfg(all(target_os = "linux", feature = "pyrowave"))] + if let Some(f) = &pyro_frame { + if self + .video + .as_ref() + .is_none_or(|v| v.width != f.width || v.height != f.height) + { + self.rebuild_video_image(f.width, f.height)?; + tracing::info!(width = f.width, height = f.height, "video image (re)built"); + } + let planar = self + .csc_planar + .as_ref() + .context("PyroWave frame but the device failed the pyrowave probe")?; + planar.bind_planes_planar(&self.device, f.views.map(|v| vk::ImageView::from_raw(v))); + } if let Some(o) = overlay { // Point the composite at this overlay image (same fence-wait safety). let infos = [vk::DescriptorImageInfo::default() @@ -1352,6 +1398,18 @@ impl Presenter { vk_sync = Some(sync); } + // PyroWave frame: the planes are already on THIS device, decode + // fence-complete and barriered to fragment sampling (GENERAL) by the + // decoder — no acquire needed, just the planar CSC pass. + #[cfg(all(target_os = "linux", feature = "pyrowave"))] + if let (Some(f), Some(v)) = (&pyro_frame, &self.video) { + let extent = vk::Extent2D { + width: v.width, + height: v.height, + }; + self.record_csc_planar(v.framebuffer, extent, f.color); + } + // New frame: staging → video image (stride carried by buffer_row_length). if let (Some(f), Some(v), Some(s)) = (cpu_frame, &self.video, &self.staging) { barrier( @@ -1708,6 +1766,81 @@ impl Presenter { } } + /// [`record_csc`] over the planar (PyroWave) pass — always 8-bit, no MSB packing. + #[cfg(all(target_os = "linux", feature = "pyrowave"))] + unsafe fn record_csc_planar( + &self, + framebuffer: vk::Framebuffer, + extent: vk::Extent2D, + color: pf_client_core::video::ColorDesc, + ) { + // The planar pass exists whenever a PyroWave frame reached us (checked at bind). + let Some(planar) = self.csc_planar.as_ref() else { + return; + }; + unsafe { + self.device.cmd_begin_render_pass( + self.cmd_buf, + &vk::RenderPassBeginInfo::default() + .render_pass(planar.render_pass) + .framebuffer(framebuffer) + .render_area(vk::Rect2D { + offset: vk::Offset2D { x: 0, y: 0 }, + extent, + }), + vk::SubpassContents::INLINE, + ); + self.device.cmd_bind_pipeline( + self.cmd_buf, + vk::PipelineBindPoint::GRAPHICS, + planar.pipeline, + ); + self.device.cmd_set_viewport( + self.cmd_buf, + 0, + &[vk::Viewport { + x: 0.0, + y: 0.0, + width: extent.width as f32, + height: extent.height as f32, + min_depth: 0.0, + max_depth: 1.0, + }], + ); + self.device.cmd_set_scissor( + self.cmd_buf, + 0, + &[vk::Rect2D { + offset: vk::Offset2D { x: 0, y: 0 }, + extent, + }], + ); + self.device.cmd_bind_descriptor_sets( + self.cmd_buf, + vk::PipelineBindPoint::GRAPHICS, + planar.pipeline_layout, + 0, + &[planar.desc_set], + &[], + ); + let rows = csc_rows(color, 8, false); + let mut pc = [0f32; 16]; + pc[..12].copy_from_slice(bytemuck_rows(&rows)); + pc[12] = 0.0; // SDR passthrough — PyroWave has no PQ path + pc[13] = 0.0; + let bytes = std::slice::from_raw_parts(pc.as_ptr().cast::(), 64); + self.device.cmd_push_constants( + self.cmd_buf, + planar.pipeline_layout, + vk::ShaderStageFlags::FRAGMENT, + 0, + bytes, + ); + self.device.cmd_draw(self.cmd_buf, 3, 1, 0, 0); + self.device.cmd_end_render_pass(self.cmd_buf); + } + } + /// Per-plane views over a Vulkan-Video frame's multiplanar image — the CSC pass's /// exact sampling contract (the frames pool was created MUTABLE_FORMAT for this). /// 8-bit NV12 (R8 + R8G8) and 10-bit P010/X6 (R10X6 + R10X6G10X6). @@ -1956,6 +2089,10 @@ impl Drop for Presenter { #[cfg(target_os = "linux")] self.hw.take(); self.csc.destroy(&self.device); + #[cfg(all(target_os = "linux", feature = "pyrowave"))] + if let Some(p) = &self.csc_planar { + p.destroy(&self.device); + } self.overlay_pipe.destroy(&self.device); for s in self.render_sems.drain(..) { self.device.destroy_semaphore(s, None); diff --git a/crates/punktfunk-host/src/encode/linux/nvenc_cuda.rs b/crates/punktfunk-host/src/encode/linux/nvenc_cuda.rs index 04dceec3..8835bc6c 100644 --- a/crates/punktfunk-host/src/encode/linux/nvenc_cuda.rs +++ b/crates/punktfunk-host/src/encode/linux/nvenc_cuda.rs @@ -224,6 +224,8 @@ fn codec_guid(codec: Codec) -> nv::GUID { Codec::H264 => nv::NV_ENC_CODEC_H264_GUID, Codec::H265 => nv::NV_ENC_CODEC_HEVC_GUID, Codec::Av1 => nv::NV_ENC_CODEC_AV1_GUID, + // Guarded by the open_video dispatch: a PyroWave session never reaches NVENC. + Codec::PyroWave => unreachable!("PyroWave never opens the direct-NVENC backend"), } } @@ -522,6 +524,7 @@ impl NvencCudaEncoder { } Codec::Av1 => {} Codec::H264 => {} + Codec::PyroWave => unreachable!("PyroWave never opens the direct-NVENC backend"), } // Chroma + bit depth. 4:4:4 (HEVC Range Extensions, chromaFormatIDC=3) engages on a YUV444 @@ -549,6 +552,7 @@ impl NvencCudaEncoder { .set_inputPixelBitDepthMinus8(0); } Codec::H264 => {} + Codec::PyroWave => unreachable!("PyroWave never opens the direct-NVENC backend"), } } @@ -596,6 +600,7 @@ impl NvencCudaEncoder { av1.matrixCoefficients = mat; av1.colorRange = 0; } + Codec::PyroWave => unreachable!("PyroWave never opens the direct-NVENC backend"), } } @@ -616,6 +621,7 @@ impl NvencCudaEncoder { Codec::Av1 => { cfg.encodeCodecConfig.av1Config.maxNumRefFramesInDPB = RFI_DPB; } + Codec::PyroWave => unreachable!("PyroWave never opens the direct-NVENC backend"), } } Ok(cfg) @@ -1014,6 +1020,9 @@ impl Encoder for NvencCudaEncoder { pic.codecPicParams.h264PicParams.seiPayloadArrayCnt = sei.len() as u32; } Codec::Av1 => {} + Codec::PyroWave => { + unreachable!("PyroWave never opens the direct-NVENC backend") + } } } (api().encode_picture)(self.encoder, &mut pic) diff --git a/crates/punktfunk-host/src/encode/linux/vulkan_video.rs b/crates/punktfunk-host/src/encode/linux/vulkan_video.rs index bc27324c..5fcf5b53 100644 --- a/crates/punktfunk-host/src/encode/linux/vulkan_video.rs +++ b/crates/punktfunk-host/src/encode/linux/vulkan_video.rs @@ -10,16 +10,14 @@ //! The AV1 encode structs our pinned `ash 0.38` predates are vendored in `vk_av1_encode.rs`. #![allow(clippy::too_many_arguments)] -use super::vk_util::{ - color_range, find_mem, fourcc_to_vk, make_plain_image, make_view, pixel_to_vk, -}; -use crate::capture::{CapturedFrame, FramePayload, PixelFormat}; +use super::vk_util::{color_range, find_mem, make_plain_image, make_view, pixel_to_vk}; +use crate::capture::{CapturedFrame, FramePayload}; use crate::encode::{Codec, EncodedFrame, Encoder, EncoderCaps}; use anyhow::{bail, Context, Result}; use ash::vk; use std::collections::VecDeque; use std::ffi::c_void; -use std::os::fd::{AsRawFd, IntoRawFd}; +use std::os::fd::AsRawFd; const NV12: vk::Format = vk::Format::G8_B8R8_2PLANE_420_UNORM; /// Max resident dmabuf imports (comfortably above any PipeWire pool depth; imports alias existing