feat(client/windows): PyroWave decode + surface it in the GUI codec picker
The decoder was gated to Linux because, when it landed, the Windows client still had its own in-process WinUI/D3D11 presenter and the PyroWave present path there was an open question. That client has since been retired: Windows now spawns the SAME Vulkan session presenter as Linux, and the decoder is plain Vulkan compute on the presenter's device (no fds, no dmabuf, no D3D11 interop), so the question that gated it answered itself. pyrowave-sys already builds on Windows too -- the Windows HOST encoder ships on it. So this is a port by un-gating: every cfg(all(target_os = "linux", feature = "pyrowave")) becomes any(linux, windows) -- decoder module, backend variant, Decoder::new_pyrowave, the CODEC_PYROWAVE advertisement, the session pump's opt-in/build/label arms, and the presenter's planar CSC pass. No new code. Then offer it in the Windows GUI, which is what prompted this. It stays preference-only (resolve_codec never auto-picks it) and a host or device that can't do PyroWave just falls back down the ladder to HEVC. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -194,7 +194,7 @@ struct StreamState {
|
||||
/// PyroWave present has no demote rung (nothing else decodes the codec), so a
|
||||
/// persistent non-device-lost present failure would warn on every frame. Latch it:
|
||||
/// warn on the first failure of a streak, then stay quiet until a present succeeds.
|
||||
#[cfg(all(target_os = "linux", feature = "pyrowave"))]
|
||||
#[cfg(all(any(target_os = "linux", windows), feature = "pyrowave"))]
|
||||
pyro_present_warned: bool,
|
||||
hw_fails: u32,
|
||||
/// The OSD's text (multi-line; rebuilt each Stats window and on a live tier cycle).
|
||||
@@ -267,7 +267,7 @@ impl StreamState {
|
||||
win_start: Instant::now(),
|
||||
presented: PresentedWindow::default(),
|
||||
dmabuf_demoted: false,
|
||||
#[cfg(all(target_os = "linux", feature = "pyrowave"))]
|
||||
#[cfg(all(any(target_os = "linux", windows), feature = "pyrowave"))]
|
||||
pyro_present_warned: false,
|
||||
hw_fails: 0,
|
||||
osd_text: String::new(),
|
||||
@@ -1007,7 +1007,7 @@ fn run_inner(mut opts: SessionOpts, mut mode: ModeCtl) -> Result<Option<Outcome>
|
||||
// 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"))]
|
||||
#[cfg(all(any(target_os = "linux", windows), feature = "pyrowave"))]
|
||||
DecodedImage::PyroWave(f) => {
|
||||
// The wavelet stream carries the negotiated ColorInfo (no VUI): an
|
||||
// HDR (PQ) pyrowave session presents through the HDR10 path exactly
|
||||
|
||||
@@ -47,7 +47,7 @@ pub enum FrameInput<'a> {
|
||||
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"))]
|
||||
#[cfg(all(any(target_os = "linux", windows), feature = "pyrowave"))]
|
||||
PyroWave(pf_client_core::video_pyrowave::PyroWavePlanarFrame),
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ pub struct Presenter {
|
||||
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"))]
|
||||
#[cfg(all(any(target_os = "linux", windows), feature = "pyrowave"))]
|
||||
csc_planar: Option<CscPass>,
|
||||
/// FFmpeg Vulkan Video decode handles — `None` when the stack can't do it.
|
||||
video_export: Option<pf_client_core::video::VulkanDecodeDevice>,
|
||||
@@ -304,7 +304,7 @@ impl Drop for Presenter {
|
||||
#[cfg(target_os = "linux")]
|
||||
self.hw.take();
|
||||
self.csc.destroy(&self.device);
|
||||
#[cfg(all(target_os = "linux", feature = "pyrowave"))]
|
||||
#[cfg(all(any(target_os = "linux", windows), feature = "pyrowave"))]
|
||||
if let Some(p) = &self.csc_planar {
|
||||
p.destroy(&self.device);
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ 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"))]
|
||||
#[cfg(all(any(target_os = "linux", windows), feature = "pyrowave"))]
|
||||
FrameInput::PyroWave(f) => Some(f.color.is_pq()),
|
||||
};
|
||||
if let Some(pq) = frame_pq {
|
||||
@@ -68,7 +68,7 @@ impl Presenter {
|
||||
#[cfg(windows)]
|
||||
let mut win_frame: Option<crate::d3d11::HwFrame> = None;
|
||||
let mut vk_frame: Option<(VkVideoFrame, [vk::ImageView; 2])> = None;
|
||||
#[cfg(all(target_os = "linux", feature = "pyrowave"))]
|
||||
#[cfg(all(any(target_os = "linux", windows), feature = "pyrowave"))]
|
||||
let mut pyro_frame: Option<pf_client_core::video_pyrowave::PyroWavePlanarFrame> = None;
|
||||
let cpu_frame = match input {
|
||||
FrameInput::Redraw => None,
|
||||
@@ -96,7 +96,7 @@ impl Presenter {
|
||||
vk_frame = Some((v, views));
|
||||
None
|
||||
}
|
||||
#[cfg(all(target_os = "linux", feature = "pyrowave"))]
|
||||
#[cfg(all(any(target_os = "linux", windows), feature = "pyrowave"))]
|
||||
FrameInput::PyroWave(f) => {
|
||||
pyro_frame = Some(f);
|
||||
None
|
||||
@@ -155,7 +155,7 @@ impl Presenter {
|
||||
}
|
||||
self.csc.bind_planes(&self.device, views[0], views[1]);
|
||||
}
|
||||
#[cfg(all(target_os = "linux", feature = "pyrowave"))]
|
||||
#[cfg(all(any(target_os = "linux", windows), feature = "pyrowave"))]
|
||||
if let Some(f) = &pyro_frame {
|
||||
if self
|
||||
.video
|
||||
@@ -317,7 +317,7 @@ impl Presenter {
|
||||
// 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"))]
|
||||
#[cfg(all(any(target_os = "linux", windows), feature = "pyrowave"))]
|
||||
if let (Some(f), Some(v)) = (&pyro_frame, &self.video) {
|
||||
let extent = vk::Extent2D {
|
||||
width: v.width,
|
||||
@@ -695,7 +695,7 @@ impl Presenter {
|
||||
}
|
||||
|
||||
/// [`record_csc`] over the planar (PyroWave) pass — always 8-bit, no MSB packing.
|
||||
#[cfg(all(target_os = "linux", feature = "pyrowave"))]
|
||||
#[cfg(all(any(target_os = "linux", windows), feature = "pyrowave"))]
|
||||
unsafe fn record_csc_planar(
|
||||
&self,
|
||||
framebuffer: vk::Framebuffer,
|
||||
|
||||
@@ -207,7 +207,7 @@ impl Presenter {
|
||||
// The planar (PyroWave) pass renders to the same intermediate — rebuild it at the
|
||||
// new format too (an HDR pyrowave session needs the 10-bit intermediate exactly
|
||||
// like the H.26x path; 8-bit PQ bands visibly).
|
||||
#[cfg(all(target_os = "linux", feature = "pyrowave"))]
|
||||
#[cfg(all(any(target_os = "linux", windows), feature = "pyrowave"))]
|
||||
if let Some(p) = self.csc_planar.take() {
|
||||
p.destroy(&self.device);
|
||||
self.csc_planar = Some(CscPass::new_planar(&self.device, self.video_format)?);
|
||||
|
||||
@@ -318,7 +318,7 @@ impl Presenter {
|
||||
let csc = CscPass::new(&device, vk::Format::R8G8B8A8_UNORM)?;
|
||||
// Starts SDR like `csc`; an HDR (PQ) pyrowave session rebuilds it at the 10-bit
|
||||
// intermediate via `set_hdr_mode`, exactly like the H.26x pass.
|
||||
#[cfg(all(target_os = "linux", feature = "pyrowave"))]
|
||||
#[cfg(all(any(target_os = "linux", windows), feature = "pyrowave"))]
|
||||
let csc_planar = if pyrowave_ok {
|
||||
Some(CscPass::new_planar(&device, vk::Format::R8G8B8A8_UNORM)?)
|
||||
} else {
|
||||
@@ -463,7 +463,7 @@ impl Presenter {
|
||||
#[cfg(windows)]
|
||||
hw_win,
|
||||
csc,
|
||||
#[cfg(all(target_os = "linux", feature = "pyrowave"))]
|
||||
#[cfg(all(any(target_os = "linux", windows), feature = "pyrowave"))]
|
||||
csc_planar,
|
||||
video_export,
|
||||
overlay_pipe,
|
||||
|
||||
Reference in New Issue
Block a user