fix(client/pyrowave): declare the decode planes' real format — 10-bit sessions decoded through an R8 view of R16 planes
apple / swift (push) Successful in 5m14s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 6m32s
arch / build-publish (push) Failing after 7m6s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 10s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 9s
decky / build-publish (push) Successful in 18s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 8s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 8s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 8s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 3m53s
android / android (push) Successful in 12m56s
docker / deploy-docs (push) Successful in 24s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 5m41s
docker / build-push-arm64cross (push) Successful in 4m15s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 5m40s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Failing after 7m44s
deb / build-publish-host (push) Successful in 10m43s
deb / build-publish-client-arm64 (push) Successful in 9m8s
apple / screenshots (push) Successful in 22m58s
flatpak / build-publish (push) Successful in 6m56s
deb / build-publish (push) Successful in 8m58s
ci / rust (push) Successful in 22m10s
ci / rust-arm64 (push) Successful in 9m22s
ci / web (push) Successful in 48s
ci / docs-site (push) Successful in 1m13s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Failing after 12m48s
ci / bench (push) Successful in 6m3s
apple / swift (push) Successful in 5m14s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 6m32s
arch / build-publish (push) Failing after 7m6s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 10s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 9s
decky / build-publish (push) Successful in 18s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 8s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 8s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 8s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 3m53s
android / android (push) Successful in 12m56s
docker / deploy-docs (push) Successful in 24s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 5m41s
docker / build-push-arm64cross (push) Successful in 4m15s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 5m40s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Failing after 7m44s
deb / build-publish-host (push) Successful in 10m43s
deb / build-publish-client-arm64 (push) Successful in 9m8s
apple / screenshots (push) Successful in 22m58s
flatpak / build-publish (push) Successful in 6m56s
deb / build-publish (push) Successful in 8m58s
ci / rust (push) Successful in 22m10s
ci / rust-arm64 (push) Successful in 9m22s
ci / web (push) Successful in 48s
ci / docs-site (push) Successful in 1m13s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Failing after 12m48s
ci / bench (push) Successful in 6m3s
The HDR/4:4:4 leg (188edde2, shipped 0.14.0) taught the client's plane
ring to allocate R16_UNORM on a 10-bit session, but the
pyrowave_image_view structs handed to decode_gpu_buffer kept the
original SDR shape: image_format/view_format hardcoded R8_UNORM, chroma
extents hardcoded half-res. pyrowave wraps our VkImage under the
DECLARED format and vkCreateImageView's its storage view from it
(pyrowave_c.cpp WrappedViewBuffers::wrap) — an R8 view over a
non-MUTABLE R16 image is invalid Vulkan that release drivers execute
anyway: the view's addressing covers half the surface, so the iDWT's
8-bit codes fuse pairwise into 16-bit texels (structured tile-scrambled
garbage) and the never-written remainder samples as all-plane zeros
(saturated green). Field report 2026-07-25: RX 9070 XT host → 780M
client, total corruption on every 10-bit PyroWave session regardless of
bitrate/refresh, wire counters spotless; milder shapes of the same UB
on NVIDIA clients; Arc validation never saw it because it ran SDR
(R8 ring — declaration consistent by accident).
Declare what the ring really built: format from hdr16 (R16/R8), chroma
extents from chroma444 (full-/half-res) — exactly what upstream's own
pyrowave_image_get_image_view fills from the image itself. Host
encoders audited clean: Windows keys plane formats off hdr16 already,
Linux is 8-bit end to end.
This commit is contained in:
@@ -6,11 +6,12 @@
|
|||||||
//! queue under the device's [`QueueLock`], fence-waited (sub-ms — Phase-0 measured
|
//! queue under the device's [`QueueLock`], fence-waited (sub-ms — Phase-0 measured
|
||||||
//! 0.067 ms GPU at 1080p on the RTX 5070 Ti).
|
//! 0.067 ms GPU at 1080p on the RTX 5070 Ti).
|
||||||
//!
|
//!
|
||||||
//! Output: three separate R8 planes (Y full-res, Cb/Cr half-res) — the decode path
|
//! Output: three separate single-component planes (Y full-res; Cb/Cr half-res, or
|
||||||
//! requires STORAGE usage and IDENTITY/R swizzles, so the encoder's two-component
|
//! full-res on a 4:4:4 session) — R8, or R16 UNORM on a 10-bit session — the decode
|
||||||
//! RG8 trick is not allowed here (pyrowave.h validation). The presenter samples them
|
//! path requires STORAGE usage and IDENTITY/R swizzles, so the encoder's two-component
|
||||||
//! with its planar CSC variant (BT.709 limited — the codec's fixed colour contract,
|
//! RG trick is not allowed here (pyrowave.h validation). The presenter samples them
|
||||||
//! there is no VUI). A small ring of plane-sets keeps a decode from overwriting the set
|
//! with its planar CSC variant (colour per the negotiated `ColorInfo` — the wavelet
|
||||||
|
//! bitstream carries no VUI). A small ring of plane-sets keeps a decode from overwriting the set
|
||||||
//! the presenter is still sampling; the synchronous fence bounds decode-side reuse and
|
//! the presenter is still sampling; the synchronous fence bounds decode-side reuse and
|
||||||
//! the ring depth covers present-side latency (≤ 1–2 frames in this pipeline).
|
//! the ring depth covers present-side latency (≤ 1–2 frames in this pipeline).
|
||||||
//!
|
//!
|
||||||
@@ -222,9 +223,10 @@ unsafe extern "C" fn queue_unlock_cb(ud: *mut c_void) {
|
|||||||
unsafe { (*(ud as *const crate::video::QueueLock)).unlock() }
|
unsafe { (*(ud as *const crate::video::QueueLock)).unlock() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// One decoded PyroWave frame: three R8 plane images on the presenter's device, GENERAL
|
/// One decoded PyroWave frame: three single-component plane images (R8, or R16 on a
|
||||||
/// layout, decode-complete (the decoder fence-waits before handing it over). `slot`
|
/// 10-bit session) on the presenter's device, GENERAL layout, decode-complete (the
|
||||||
/// identifies the ring entry; the images/views live as long as the decoder.
|
/// decoder fence-waits before handing it over). `slot` identifies the ring entry; the
|
||||||
|
/// images/views live as long as the decoder.
|
||||||
pub struct PyroWavePlanarFrame {
|
pub struct PyroWavePlanarFrame {
|
||||||
/// Raw `VkImageView`s (Y, Cb, Cr) for the presenter's planar CSC sampling.
|
/// Raw `VkImageView`s (Y, Cb, Cr) for the presenter's planar CSC sampling.
|
||||||
pub views: [u64; 3],
|
pub views: [u64; 3],
|
||||||
@@ -252,7 +254,8 @@ struct RetiredRing {
|
|||||||
retired_at: Instant,
|
retired_at: Instant,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// One decode-output plane: R8, storage (decode writes) + sampled (presenter CSC).
|
/// One decode-output plane (`fmt` = R8, or R16 on a 10-bit session): storage (decode
|
||||||
|
/// writes) + sampled (presenter CSC).
|
||||||
unsafe fn make_plane(
|
unsafe fn make_plane(
|
||||||
device: &ash::Device,
|
device: &ash::Device,
|
||||||
mem_props: &vk::PhysicalDeviceMemoryProperties,
|
mem_props: &vk::PhysicalDeviceMemoryProperties,
|
||||||
@@ -539,7 +542,8 @@ impl PyroWaveDecoder {
|
|||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Plane-set ring: 3 × R8, storage (decode writes) + sampled (presenter CSC).
|
// Plane-set ring: 3 single-component planes (R8; R16 on a 10-bit session),
|
||||||
|
// storage (decode writes) + sampled (presenter CSC).
|
||||||
let mem_props = instance.get_physical_device_memory_properties(
|
let mem_props = instance.get_physical_device_memory_properties(
|
||||||
vk::PhysicalDevice::from_raw(vkd.physical_device as u64),
|
vk::PhysicalDevice::from_raw(vkd.physical_device as u64),
|
||||||
);
|
);
|
||||||
@@ -873,12 +877,26 @@ impl PyroWaveDecoder {
|
|||||||
&vk::DependencyInfo::default().image_memory_barriers(&pre),
|
&vk::DependencyInfo::default().image_memory_barriers(&pre),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// The declared format/extent MUST equal the ring image's real ones: pyrowave wraps
|
||||||
|
// our VkImage under `image_format` and vkCreateImageView's its storage view with
|
||||||
|
// `view_format` (pyrowave_c.cpp `WrappedViewBuffers::wrap` — its own
|
||||||
|
// `pyrowave_image_get_image_view` helper fills both from the image itself).
|
||||||
|
// Declaring R8 over a 10-bit session's R16_UNORM planes is an invalid view the
|
||||||
|
// driver executes anyway: its addressing covers half the surface, so decoded 8-bit
|
||||||
|
// codes fuse pairwise into 16-bit texels (structured garbage) and the never-written
|
||||||
|
// remainder samples as all-plane zeros (saturated green) — the 2026-07 AMD-client
|
||||||
|
// field report. Same discipline for chroma extents: a 4:4:4 ring is full-res.
|
||||||
|
let fmt = if self.hdr16 {
|
||||||
|
pw::VkFormat_VK_FORMAT_R16_UNORM
|
||||||
|
} else {
|
||||||
|
pw::VkFormat_VK_FORMAT_R8_UNORM
|
||||||
|
};
|
||||||
let plane = |img: vk::Image, w: u32, h: u32| pw::pyrowave_image_view {
|
let plane = |img: vk::Image, w: u32, h: u32| pw::pyrowave_image_view {
|
||||||
image: img.as_raw() as usize as pw::VkImage,
|
image: img.as_raw() as usize as pw::VkImage,
|
||||||
width: w,
|
width: w,
|
||||||
height: h,
|
height: h,
|
||||||
image_format: pw::VkFormat_VK_FORMAT_R8_UNORM,
|
image_format: fmt,
|
||||||
view_format: pw::VkFormat_VK_FORMAT_R8_UNORM,
|
view_format: fmt,
|
||||||
mip_level: 0,
|
mip_level: 0,
|
||||||
layer: 0,
|
layer: 0,
|
||||||
aspect: pw::VkImageAspectFlagBits_VK_IMAGE_ASPECT_COLOR_BIT,
|
aspect: pw::VkImageAspectFlagBits_VK_IMAGE_ASPECT_COLOR_BIT,
|
||||||
@@ -886,11 +904,16 @@ impl PyroWaveDecoder {
|
|||||||
layout: pw::VkImageLayout_VK_IMAGE_LAYOUT_GENERAL,
|
layout: pw::VkImageLayout_VK_IMAGE_LAYOUT_GENERAL,
|
||||||
};
|
};
|
||||||
let (w, h) = (self.width, self.height);
|
let (w, h) = (self.width, self.height);
|
||||||
|
let (cw, ch) = if self.chroma444 {
|
||||||
|
(w, h)
|
||||||
|
} else {
|
||||||
|
(w / 2, h / 2)
|
||||||
|
};
|
||||||
let buffers = pw::pyrowave_gpu_buffers {
|
let buffers = pw::pyrowave_gpu_buffers {
|
||||||
planes: [
|
planes: [
|
||||||
plane(self.ring[slot].imgs[0], w, h),
|
plane(self.ring[slot].imgs[0], w, h),
|
||||||
plane(self.ring[slot].imgs[1], w / 2, h / 2),
|
plane(self.ring[slot].imgs[1], cw, ch),
|
||||||
plane(self.ring[slot].imgs[2], w / 2, h / 2),
|
plane(self.ring[slot].imgs[2], cw, ch),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
pw::pyrowave_device_set_command_buffer(
|
pw::pyrowave_device_set_command_buffer(
|
||||||
|
|||||||
Reference in New Issue
Block a user