mirror of
https://github.com/elwrcl/intel-hard-graphics.git
synced 2026-10-31 10:58:03 +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
@@ -289,6 +289,7 @@ get_device_extensions(const struct anv_physical_device *device,
|
||||
.KHR_video_decode_h264 = video_decode,
|
||||
.KHR_video_encode_queue = video_encode,
|
||||
.KHR_video_encode_h264 = video_encode,
|
||||
.KHR_video_encode_intra_refresh = video_encode,
|
||||
.KHR_video_maintenance1 = video_decode || video_encode,
|
||||
.KHR_video_maintenance2 = video_decode || video_encode,
|
||||
.KHR_unified_image_layouts = true,
|
||||
@@ -735,6 +736,9 @@ get_features(const struct anv_physical_device *pdevice,
|
||||
/* VK_KHR_video_maintenance2 */
|
||||
.videoMaintenance2 = true,
|
||||
|
||||
/* VK_KHR_video_encode_intra_refresh */
|
||||
.videoEncodeIntraRefresh = true,
|
||||
|
||||
/* VK_KHR_unified_image_layouts
|
||||
*
|
||||
* Aux usage is derived from the layout's usage flags, and GENERAL simply
|
||||
|
||||
@@ -101,7 +101,8 @@ void 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);
|
||||
|
||||
enum anv_pipe_bits
|
||||
genX(emit_apply_pipe_flushes)(struct anv_batch *batch,
|
||||
|
||||
@@ -3852,6 +3852,50 @@ struct anv_video_session_params {
|
||||
struct vk_video_session_parameters vk;
|
||||
};
|
||||
|
||||
/* One picture's slice of an intra refresh cycle: the macroblocks of region
|
||||
* `index` out of `cycle_duration` are coded intra, the rest inter.
|
||||
*/
|
||||
struct anv_video_intra_refresh {
|
||||
VkVideoEncodeIntraRefreshModeFlagBitsKHR mode;
|
||||
uint32_t cycle_duration;
|
||||
uint32_t index;
|
||||
};
|
||||
|
||||
/* Whether the macroblock at (x, y) falls in the region being refreshed. The
|
||||
* regions divide the picture evenly, so a cycle longer than the number of
|
||||
* blocks along the split axis simply leaves some of them empty.
|
||||
*/
|
||||
static inline bool
|
||||
anv_video_mb_is_intra_refreshed(const struct anv_video_intra_refresh *ir,
|
||||
uint32_t x, uint32_t y,
|
||||
uint32_t width_in_mbs, uint32_t height_in_mbs)
|
||||
{
|
||||
if (ir == NULL || ir->cycle_duration == 0)
|
||||
return false;
|
||||
|
||||
uint32_t pos, total;
|
||||
switch (ir->mode) {
|
||||
case VK_VIDEO_ENCODE_INTRA_REFRESH_MODE_BLOCK_ROW_BASED_BIT_KHR:
|
||||
pos = y;
|
||||
total = height_in_mbs;
|
||||
break;
|
||||
case VK_VIDEO_ENCODE_INTRA_REFRESH_MODE_BLOCK_COLUMN_BASED_BIT_KHR:
|
||||
pos = x;
|
||||
total = width_in_mbs;
|
||||
break;
|
||||
case VK_VIDEO_ENCODE_INTRA_REFRESH_MODE_BLOCK_BASED_BIT_KHR:
|
||||
pos = y * width_in_mbs + x;
|
||||
total = width_in_mbs * height_in_mbs;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t first = (uint64_t)ir->index * total / ir->cycle_duration;
|
||||
uint32_t last = (uint64_t)(ir->index + 1) * total / ir->cycle_duration;
|
||||
return pos >= first && pos < last;
|
||||
}
|
||||
|
||||
static inline uint32_t khr_perf_query_preamble_offset(const struct anv_query_pool *pool,
|
||||
uint32_t pass)
|
||||
{
|
||||
|
||||
@@ -222,15 +222,39 @@ anv_GetPhysicalDeviceVideoCapabilitiesKHR(VkPhysicalDevice physicalDevice,
|
||||
enc_caps->maxQualityLevels = 1;
|
||||
enc_caps->encodeInputPictureGranularity.width = ANV_MB_WIDTH;
|
||||
enc_caps->encodeInputPictureGranularity.height = ANV_MB_HEIGHT;
|
||||
/* Both flags are required by the ffmpeg h264_vulkan encoder. Note
|
||||
* that bytes-written currently reports the full dstBufferRange rather
|
||||
* than the real encoded size; reading back the MFC bit counter is not
|
||||
* yet implemented. */
|
||||
/* Bytes written comes from the MFC bitstream byte counter; see the
|
||||
* feedback query write in anv_h264_encode_video(). */
|
||||
enc_caps->supportedEncodeFeedbackFlags =
|
||||
VK_VIDEO_ENCODE_FEEDBACK_BITSTREAM_BUFFER_OFFSET_BIT_KHR |
|
||||
VK_VIDEO_ENCODE_FEEDBACK_BITSTREAM_BYTES_WRITTEN_BIT_KHR;
|
||||
}
|
||||
|
||||
struct VkVideoEncodeIntraRefreshCapabilitiesKHR *ir_caps =
|
||||
(struct VkVideoEncodeIntraRefreshCapabilitiesKHR *)
|
||||
vk_find_struct(pCapabilities->pNext,
|
||||
VIDEO_ENCODE_INTRA_REFRESH_CAPABILITIES_KHR);
|
||||
if (ir_caps) {
|
||||
/* Each macroblock's intra/inter decision is made on its own when the
|
||||
* PAK objects are emitted, so the picture can be split along any of
|
||||
* these axes. 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.
|
||||
*/
|
||||
ir_caps->intraRefreshModes =
|
||||
VK_VIDEO_ENCODE_INTRA_REFRESH_MODE_BLOCK_BASED_BIT_KHR |
|
||||
VK_VIDEO_ENCODE_INTRA_REFRESH_MODE_BLOCK_ROW_BASED_BIT_KHR |
|
||||
VK_VIDEO_ENCODE_INTRA_REFRESH_MODE_BLOCK_COLUMN_BASED_BIT_KHR;
|
||||
/* Down to one region per macroblock at the largest coded extent. */
|
||||
ir_caps->maxIntraRefreshCycleDuration =
|
||||
(4096 / ANV_MB_WIDTH) * (4096 / ANV_MB_HEIGHT);
|
||||
ir_caps->maxIntraRefreshActiveReferencePictures = 1;
|
||||
/* Regions are macroblock ranges that owe nothing to slice boundaries,
|
||||
* and a block-based one is not rectangular.
|
||||
*/
|
||||
ir_caps->partitionIndependentIntraRefreshRegions = VK_TRUE;
|
||||
ir_caps->nonRectangularIntraRefreshRegions = VK_TRUE;
|
||||
}
|
||||
|
||||
struct VkVideoEncodeH264CapabilitiesKHR *h264_enc_ext =
|
||||
(struct VkVideoEncodeH264CapabilitiesKHR *)
|
||||
vk_find_struct(pCapabilities->pNext, VIDEO_ENCODE_H264_CAPABILITIES_KHR);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1132,6 +1132,24 @@ anv_h264_encode_video(struct anv_cmd_buffer *cmd_buffer,
|
||||
encode_info->dstBufferRange,
|
||||
width_in_mbs, height_in_mbs, is_p, ref_address);
|
||||
|
||||
/* Intra refresh, if the session was created with a mode and this picture
|
||||
* carries a cycle position. An I frame refreshes everything by itself, so
|
||||
* only P frames need a region. */
|
||||
struct anv_video_intra_refresh intra_refresh;
|
||||
const struct anv_video_intra_refresh *intra_refresh_ptr = NULL;
|
||||
const VkVideoEncodeIntraRefreshInfoKHR *ir_info =
|
||||
vk_find_struct_const(encode_info->pNext, VIDEO_ENCODE_INTRA_REFRESH_INFO_KHR);
|
||||
if (is_p && ir_info && ir_info->intraRefreshCycleDuration > 0 &&
|
||||
cmd_buffer->video.vid->vk.intra_refresh_mode !=
|
||||
VK_VIDEO_ENCODE_INTRA_REFRESH_MODE_NONE_KHR) {
|
||||
intra_refresh = (struct anv_video_intra_refresh) {
|
||||
.mode = cmd_buffer->video.vid->vk.intra_refresh_mode,
|
||||
.cycle_duration = ir_info->intraRefreshCycleDuration,
|
||||
.index = ir_info->intraRefreshIndex,
|
||||
};
|
||||
intra_refresh_ptr = &intra_refresh;
|
||||
}
|
||||
|
||||
/* Emit each slice. Slice s covers the macroblock range
|
||||
* [first_mb_in_slice[s], first_mb_in_slice[s+1]); the last slice runs to the
|
||||
* end of the frame. Each NALU slice entry carries its own QP and slice
|
||||
@@ -1186,7 +1204,8 @@ anv_h264_encode_video(struct anv_cmd_buffer *cmd_buffer,
|
||||
slice_start_mb, slice_end_mb,
|
||||
width_in_mbs, height_in_mbs,
|
||||
(uint32_t)entry->constantQp,
|
||||
s + 1 == slice_count, dst_page_off);
|
||||
s + 1 == slice_count, dst_page_off,
|
||||
intra_refresh_ptr);
|
||||
}
|
||||
|
||||
/* Write the VK_QUERY_TYPE_VIDEO_ENCODE_FEEDBACK_KHR result here, where the
|
||||
|
||||
Reference in new issue
Block a user