feat(linux/vulkan-encode): PUNKTFUNK_VULKAN_RGB_TRUE_EXTENT bring-up gate

Opt-in alternative to RGB-direct's padded-copy staging at unaligned modes:
direct-import the visible-size capture and declare the TRUE-SIZE source
codedExtent. RADV has derived the VCN session_init firmware padding from
srcPictureResource.codedExtent since Mesa 24.2, so the EFC is told the source
lacks the alignment rows and the hardware edge-extends them internally --
which also reframes the 2026-07-20 field GPU reset: that crash passed the
ALIGNED extent as the source codedExtent (firmware padding zero) over a
visible-size buffer. Off by default until a guarded live test proves the EFC
front-end honors the padding like the YUV fetch path does; the padded-copy
staging remains the shipped behavior.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-21 13:06:50 +02:00
parent 004f0cacb8
commit b4fbc94e36
+84 -26
View File
@@ -84,17 +84,35 @@ fn rgb_request() -> Option<bool> {
} }
} }
/// `PUNKTFUNK_VULKAN_RGB_TRUE_EXTENT=1` (bring-up): at unaligned modes, replace RGB-direct's
/// padded-copy staging with a direct import carrying the TRUE-SIZE source `codedExtent` — RADV
/// derives nonzero VCN firmware padding from it, so the EFC is told the source lacks the
/// alignment rows (see [`RgbDirect::true_extent`]). Off by default until the EFC front-end is
/// proven to honor the padding like the YUV fetch path does.
fn rgb_true_extent_request() -> bool {
std::env::var("PUNKTFUNK_VULKAN_RGB_TRUE_EXTENT").as_deref() == Ok("1")
}
/// Live RGB-direct session config: the chroma-siting bits the session was created with /// Live RGB-direct session config: the chroma-siting bits the session was created with
/// (chosen from what the driver advertises — see [`probe_rgb_direct`]). /// (chosen from what the driver advertises — see [`probe_rgb_direct`]).
struct RgbDirect { struct RgbDirect {
x_offset: u32, // vk_valve_rgb::CHROMA_OFFSET_* x_offset: u32, // vk_valve_rgb::CHROMA_OFFSET_*
y_offset: u32, y_offset: u32,
/// The mode is not 64x16-aligned, so the captured buffer cannot be the encode source /// The mode is not 64x16-aligned, so the captured buffer cannot be the encode source
/// directly (the EFC would read past it — the 2026-07-20 field GPU hang). Instead each /// under the session's ALIGNED source extent (the EFC read past it — the 2026-07-20 field
/// frame is copied into a per-slot ALIGNED BGRA staging image with the edge rows/columns /// GPU hang, when the source `codedExtent` was the aligned size and RADV therefore derived
/// duplicated into the padding (transfer-only, no shader) and encoded from there. Aligned /// ZERO firmware padding). Each frame is copied into a per-slot ALIGNED BGRA staging image
/// modes keep the true zero-copy import. /// with the edge rows/columns duplicated into the padding (transfer-only, no shader) and
/// encoded from there. Aligned modes keep the true zero-copy import.
padded: bool, padded: bool,
/// `PUNKTFUNK_VULKAN_RGB_TRUE_EXTENT=1` bring-up alternative to `padded` at unaligned
/// modes: direct-import the visible-size buffer and pass the TRUE-SIZE source
/// `codedExtent` — RADV then programs nonzero firmware padding from it (Mesa ≥ 24.2
/// derives `session_init` padding from `srcPictureResource.codedExtent`; see
/// [`VulkanVideoEncoder::native_nv12`]), telling the VCN the source lacks the alignment
/// rows. The session/SPS/DPB stay app-aligned. Field-proven for YUV fetches; the EFC RGB
/// front-end honoring the padding is what this gate exists to prove.
true_extent: bool,
} }
/// Stack storage for a complete rgb-chained video profile. Profiled image creation AFTER open /// Stack storage for a complete rgb-chained video profile. Profiled image creation AFTER open
@@ -644,11 +662,15 @@ impl VulkanVideoEncoder {
probe_rgb_direct(&instance, &vq_inst, pd, codec_op, av1) probe_rgb_direct(&instance, &vq_inst, pd, codec_op, av1)
}; };
let rgb_cfg: Option<RgbDirect> = match (&rgb_probe, want_rgb) { let rgb_cfg: Option<RgbDirect> = match (&rgb_probe, want_rgb) {
(Ok((x, y)), true) => Some(RgbDirect { (Ok((x, y)), true) => {
let true_extent = !aligned && rgb_true_extent_request();
Some(RgbDirect {
x_offset: *x, x_offset: *x,
y_offset: *y, y_offset: *y,
padded: !aligned, padded: !aligned && !true_extent,
}), true_extent,
})
}
_ => None, _ => None,
}; };
if native_nv12 { if native_nv12 {
@@ -665,6 +687,16 @@ impl VulkanVideoEncoder {
} else { } else {
tracing::info!( tracing::info!(
rgb_direct = match (&rgb_probe, want_rgb, &rgb_cfg) { rgb_direct = match (&rgb_probe, want_rgb, &rgb_cfg) {
(
_,
_,
Some(RgbDirect {
true_extent: true, ..
}),
) =>
"active(true-extent: unaligned mode, direct import with the true-size \
source codedExtent — RADV firmware padding covers the alignment rows; \
PUNKTFUNK_VULKAN_RGB_TRUE_EXTENT bring-up)",
(_, _, Some(RgbDirect { padded: false, .. })) => "active", (_, _, Some(RgbDirect { padded: false, .. })) => "active",
(_, _, Some(RgbDirect { padded: true, .. })) => (_, _, Some(RgbDirect { padded: true, .. })) =>
"active(padded-copy: mode is not 64x16-aligned — staging blit + edge \ "active(padded-copy: mode is not 64x16-aligned — staging blit + edge \
@@ -2250,18 +2282,26 @@ impl VulkanVideoEncoder {
let (src_img, src_view, acquire, compute_active) = match &frame.payload { let (src_img, src_view, acquire, compute_active) = match &frame.payload {
FramePayload::Dmabuf(d) if !padded => { FramePayload::Dmabuf(d) if !padded => {
// Defense in depth for the OOB class the alignment gate closes at open: the // Defense in depth for the OOB class the alignment gate closes at open: the
// imported buffer must cover the FULL coded extent, or the EFC reads past it // imported buffer must cover the source extent the encode declares — the FULL
// (VM faults → VCN ring hang → GPU reset, the 2026-07-20 field report). A // aligned coded extent normally (or the EFC reads past it: VM faults → VCN
// mismatched frame (mid-flight mode change, odd capture) errors out here and // ring hang → GPU reset, the 2026-07-20 field report), the render size in
// takes the encoder-rebuild path instead of faulting the GPU. // true-extent mode (where the declared source codedExtent shrinks with it and
if frame.width != self.width || frame.height != self.height { // RADV's firmware padding covers the alignment rows). A mismatched frame
// (mid-flight mode change, odd capture) errors out here and takes the
// encoder-rebuild path instead of faulting the GPU.
let (need_w, need_h) = if self.rgb.as_ref().is_some_and(|r| r.true_extent) {
(self.render_w, self.render_h)
} else {
(self.width, self.height)
};
if frame.width != need_w || frame.height != need_h {
bail!( bail!(
"vulkan-encode (rgb-direct): frame {}x{} does not cover the coded \ "vulkan-encode (rgb-direct): frame {}x{} does not cover the declared \
extent {}x{} — refusing an out-of-bounds encode source", source extent {}x{} — refusing an out-of-bounds encode source",
frame.width, frame.width,
frame.height, frame.height,
self.width, need_w,
self.height need_h
); );
} }
let (img, view, fresh) = self.import_cached(d, frame.width, frame.height)?; let (img, view, fresh) = self.import_cached(d, frame.width, frame.height)?;
@@ -2547,10 +2587,8 @@ impl VulkanVideoEncoder {
poc: i32, poc: i32,
) -> Result<()> { ) -> Result<()> {
use ash::vk::native as h; use ash::vk::native as h;
// Setup/reference AND source share ONE coded extent. App-aligned sessions (CSC, RGB) // Setup/reference extent: the aligned size for app-aligned sessions (CSC, RGB — it
// use the aligned size — their sources cover it by construction. Native NV12 runs // pairs with their aligned SPS), the render size for native NV12's true-size headers.
// true-size headers: the render-size extent matches the SPS/sequence header, and RADV
// keys the firmware's internal alignment padding off it (see `native_nv12`).
let ext2d = if self.native_nv12 { let ext2d = if self.native_nv12 {
vk::Extent2D { vk::Extent2D {
width: self.render_w, width: self.render_w,
@@ -2562,6 +2600,17 @@ impl VulkanVideoEncoder {
height: self.height, height: self.height,
} }
}; };
// Source extent additionally drops to the render size in RGB true-extent mode: RADV
// derives the VCN firmware padding from srcPictureResource.codedExtent (Mesa ≥ 24.2),
// so the visible-size import is never read past its extent (see RgbDirect::true_extent).
let src_extent = if self.rgb.as_ref().is_some_and(|r| r.true_extent) {
vk::Extent2D {
width: self.render_w,
height: self.render_h,
}
} else {
ext2d
};
let ref_poc = if is_idr { 0 } else { self.slot_poc[ref_slot] }; let ref_poc = if is_idr { 0 } else { self.slot_poc[ref_slot] };
let mut pic_flags: h::StdVideoEncodeH265PictureInfoFlags = std::mem::zeroed(); let mut pic_flags: h::StdVideoEncodeH265PictureInfoFlags = std::mem::zeroed();
@@ -2732,7 +2781,7 @@ impl VulkanVideoEncoder {
} }
dev.cmd_begin_query(cmd, query_pool, 0, vk::QueryControlFlags::empty()); dev.cmd_begin_query(cmd, query_pool, 0, vk::QueryControlFlags::empty());
let src_res = vk::VideoPictureResourceInfoKHR::default() let src_res = vk::VideoPictureResourceInfoKHR::default()
.coded_extent(ext2d) .coded_extent(src_extent)
.image_view_binding(src_view); .image_view_binding(src_view);
let mut enc = vk::VideoEncodeInfoKHR::default() let mut enc = vk::VideoEncodeInfoKHR::default()
.dst_buffer(bs_buf) .dst_buffer(bs_buf)
@@ -2776,10 +2825,8 @@ impl VulkanVideoEncoder {
) -> Result<()> { ) -> Result<()> {
use super::vk_av1_encode as av1; use super::vk_av1_encode as av1;
use ash::vk::native as h; use ash::vk::native as h;
// Setup/reference AND source share ONE coded extent. App-aligned sessions (CSC, RGB) // Setup/reference extent: the aligned size for app-aligned sessions (CSC, RGB — it
// use the aligned size — their sources cover it by construction. Native NV12 runs // pairs with their aligned SPS), the render size for native NV12's true-size headers.
// true-size headers: the render-size extent matches the SPS/sequence header, and RADV
// keys the firmware's internal alignment padding off it (see `native_nv12`).
let ext2d = if self.native_nv12 { let ext2d = if self.native_nv12 {
vk::Extent2D { vk::Extent2D {
width: self.render_w, width: self.render_w,
@@ -2791,6 +2838,17 @@ impl VulkanVideoEncoder {
height: self.height, height: self.height,
} }
}; };
// Source extent additionally drops to the render size in RGB true-extent mode: RADV
// derives the VCN firmware padding from srcPictureResource.codedExtent (Mesa ≥ 24.2),
// so the visible-size import is never read past its extent (see RgbDirect::true_extent).
let src_extent = if self.rgb.as_ref().is_some_and(|r| r.true_extent) {
vk::Extent2D {
width: self.render_w,
height: self.render_h,
}
} else {
ext2d
};
// ---- required AV1 frame sub-structs (single tile; no CDEF/LR/segmentation/global-motion) ---- // ---- required AV1 frame sub-structs (single tile; no CDEF/LR/segmentation/global-motion) ----
let mut tile_flags: h::StdVideoAV1TileInfoFlags = std::mem::zeroed(); let mut tile_flags: h::StdVideoAV1TileInfoFlags = std::mem::zeroed();
@@ -3011,7 +3069,7 @@ impl VulkanVideoEncoder {
} }
dev.cmd_begin_query(cmd, query_pool, 0, vk::QueryControlFlags::empty()); dev.cmd_begin_query(cmd, query_pool, 0, vk::QueryControlFlags::empty());
let src_res = vk::VideoPictureResourceInfoKHR::default() let src_res = vk::VideoPictureResourceInfoKHR::default()
.coded_extent(ext2d) .coded_extent(src_extent)
.image_view_binding(src_view); .image_view_binding(src_view);
let mut enc = vk::VideoEncodeInfoKHR::default() let mut enc = vk::VideoEncodeInfoKHR::default()
.dst_buffer(bs_buf) .dst_buffer(bs_buf)