mirror of
https://github.com/elwrcl/intel-hard-graphics.git
synced 2026-11-01 03:18:02 +03:00
hasvk: Add VK_KHR_video_encode_intra_refresh for gen7.
Each macroblock's intra/inter decision is already made on its own when the PAK objects are emitted, so refreshing a region is a matter of coding the macroblocks it covers the way an I slice codes them. Regions divide the picture evenly along rows, columns or raster order, which leaves the block, block-row and block-column modes supported and gives empty regions for free when the cycle is longer than the split axis. Two things needed care. PAK keeps the I slice numbering for MbType5Bits inside a P slice, where H.264 itself offsets intra mb_types by 5; handing it the offset value hangs the MFC. And under constrained intra prediction a neighbour only counts as available if it is in the refresh region too, because prediction may not cross into an inter macroblock. Nothing constrains motion away from not-yet-refreshed regions, which is sound only because there is no motion estimation: every vector is zero, so a macroblock reads its own co-located block and never leaves its region. Motion estimation will have to honour VkVideoReferenceIntraRefreshInfoKHR's dirty region count. Per-picture-partition mode is not offered. It wants the refreshed slice coded as an I slice, header included, rather than as intra macroblocks inside a P slice. dEQP-VK.video.encode.h264.intra_refresh_* goes from 44 NotSupported to 12 Pass; all three modes pass in the variants that can run. The other 32 stop at a CTS precondition, maxDpbSlots >= GOP frame count, which the driver's two slots do not meet. Raising that needs real DPB slot tracking and is what gates B frames and ffmpeg's encoder too. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
6 files changed
+126
-12
No files matched your search
@@ -316,7 +316,8 @@ genX(hasvk_mfc_emit_slice)(struct anv_cmd_buffer *cmd_buffer,
|
||||
uint32_t slice_start_mb, uint32_t slice_end_mb,
|
||||
uint32_t width_in_mbs, uint32_t height_in_mbs,
|
||||
uint32_t qp, bool is_last_slice,
|
||||
uint32_t dst_page_offset)
|
||||
uint32_t dst_page_offset,
|
||||
const struct anv_video_intra_refresh *intra_refresh)
|
||||
{
|
||||
/* A P slice references the previous reconstructed frame; an I slice does
|
||||
* not. B slices are out of scope. */
|
||||
@@ -426,7 +427,13 @@ genX(hasvk_mfc_emit_slice)(struct anv_cmd_buffer *cmd_buffer,
|
||||
pak.InlineData[1] = (y << 8) | x; /* Cbp4x4Y=0, MbY/XCnt */
|
||||
pak.InlineData[3] = ((uint32_t)last_mb << 26) | qp;
|
||||
|
||||
if (is_p) {
|
||||
/* A macroblock in the refresh region is coded exactly like an I slice
|
||||
* one; the rest of the picture keeps the inter path. */
|
||||
const bool intra_mb = !is_p ||
|
||||
anv_video_mb_is_intra_refreshed(intra_refresh, x, y,
|
||||
width_in_mbs, height_in_mbs);
|
||||
|
||||
if (!intra_mb) {
|
||||
/* HASVK_ENC_SKIP_ONLY falls back to the older all-skip path, which
|
||||
* carries no residual at all: useful for isolating whether a P
|
||||
* frame problem is in the prediction or in the residual. */
|
||||
@@ -449,6 +456,9 @@ genX(hasvk_mfc_emit_slice)(struct anv_cmd_buffer *cmd_buffer,
|
||||
pak.InlineData[6] = 0; /* ref index L1 = 0 */
|
||||
pak.InlineData[7] = 0;
|
||||
} else {
|
||||
/* MbType5Bits stays the I slice numbering even inside a P slice,
|
||||
* where H.264 itself offsets intra mb_types by 5: PAK does that
|
||||
* remapping, and feeding it the offset value hangs the MFC. */
|
||||
pak.InlineData[0] = 0x000e0000 /* CbpDcY/U/V = 1 */
|
||||
| (1u << 13) /* IntraMbFlag */
|
||||
| (0x17u << 8); /* MbType5Bits = 23: I_16x16 DC */
|
||||
@@ -462,15 +472,27 @@ genX(hasvk_mfc_emit_slice)(struct anv_cmd_buffer *cmd_buffer,
|
||||
* >= slice_start_mb as well as inside the picture.
|
||||
* bit7 F, bit6 A, bit5 E: left | bit4 B: top | bit3 C: top-right |
|
||||
* bit2 D: top-left. Bits [1:0] hold ChromaIntraPredMode (DC = 0). */
|
||||
/* Under constrained intra prediction a neighbour in a P slice also
|
||||
* has to be intra itself, which here means inside the intra band.
|
||||
* Without the flag the decoder does treat inter neighbours as
|
||||
* available, so masking them off would desync the two. */
|
||||
const bool cip = pps->flags.constrained_intra_pred_flag;
|
||||
#define NEIGHBOUR_OK(nb_mb, nb_x, nb_y) \
|
||||
((nb_mb) >= slice_start_mb && \
|
||||
(!is_p || !cip || \
|
||||
anv_video_mb_is_intra_refreshed(intra_refresh, nb_x, nb_y, \
|
||||
width_in_mbs, height_in_mbs)))
|
||||
uint32_t intra_avail = 0;
|
||||
if (x > 0 && mb - 1 >= slice_start_mb)
|
||||
if (x > 0 && NEIGHBOUR_OK(mb - 1, x - 1, y))
|
||||
intra_avail |= (1u << 7) | (1u << 6) | (1u << 5);
|
||||
if (y > 0 && mb - width_in_mbs >= slice_start_mb)
|
||||
if (y > 0 && NEIGHBOUR_OK(mb - width_in_mbs, x, y - 1))
|
||||
intra_avail |= (1u << 4);
|
||||
if (y > 0 && x < width_in_mbs - 1 && mb - width_in_mbs + 1 >= slice_start_mb)
|
||||
if (y > 0 && x < width_in_mbs - 1 &&
|
||||
NEIGHBOUR_OK(mb - width_in_mbs + 1, x + 1, y - 1))
|
||||
intra_avail |= (1u << 3);
|
||||
if (x > 0 && y > 0 && mb - width_in_mbs - 1 >= slice_start_mb)
|
||||
if (x > 0 && y > 0 && NEIGHBOUR_OK(mb - width_in_mbs - 1, x - 1, y - 1))
|
||||
intra_avail |= (1u << 2);
|
||||
#undef NEIGHBOUR_OK
|
||||
pak.InlineData[6] = intra_avail;
|
||||
pak.InlineData[7] = 0;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user