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_decode_h264 = video_decode,
|
||||||
.KHR_video_encode_queue = video_encode,
|
.KHR_video_encode_queue = video_encode,
|
||||||
.KHR_video_encode_h264 = video_encode,
|
.KHR_video_encode_h264 = video_encode,
|
||||||
|
.KHR_video_encode_intra_refresh = video_encode,
|
||||||
.KHR_video_maintenance1 = video_decode || video_encode,
|
.KHR_video_maintenance1 = video_decode || video_encode,
|
||||||
.KHR_video_maintenance2 = video_decode || video_encode,
|
.KHR_video_maintenance2 = video_decode || video_encode,
|
||||||
.KHR_unified_image_layouts = true,
|
.KHR_unified_image_layouts = true,
|
||||||
@@ -735,6 +736,9 @@ get_features(const struct anv_physical_device *pdevice,
|
|||||||
/* VK_KHR_video_maintenance2 */
|
/* VK_KHR_video_maintenance2 */
|
||||||
.videoMaintenance2 = true,
|
.videoMaintenance2 = true,
|
||||||
|
|
||||||
|
/* VK_KHR_video_encode_intra_refresh */
|
||||||
|
.videoEncodeIntraRefresh = true,
|
||||||
|
|
||||||
/* VK_KHR_unified_image_layouts
|
/* VK_KHR_unified_image_layouts
|
||||||
*
|
*
|
||||||
* Aux usage is derived from the layout's usage flags, and GENERAL simply
|
* 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 slice_start_mb, uint32_t slice_end_mb,
|
||||||
uint32_t width_in_mbs, uint32_t height_in_mbs,
|
uint32_t width_in_mbs, uint32_t height_in_mbs,
|
||||||
uint32_t qp, bool is_last_slice,
|
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
|
enum anv_pipe_bits
|
||||||
genX(emit_apply_pipe_flushes)(struct anv_batch *batch,
|
genX(emit_apply_pipe_flushes)(struct anv_batch *batch,
|
||||||
|
|||||||
@@ -3852,6 +3852,50 @@ struct anv_video_session_params {
|
|||||||
struct vk_video_session_parameters vk;
|
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,
|
static inline uint32_t khr_perf_query_preamble_offset(const struct anv_query_pool *pool,
|
||||||
uint32_t pass)
|
uint32_t pass)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -222,15 +222,39 @@ anv_GetPhysicalDeviceVideoCapabilitiesKHR(VkPhysicalDevice physicalDevice,
|
|||||||
enc_caps->maxQualityLevels = 1;
|
enc_caps->maxQualityLevels = 1;
|
||||||
enc_caps->encodeInputPictureGranularity.width = ANV_MB_WIDTH;
|
enc_caps->encodeInputPictureGranularity.width = ANV_MB_WIDTH;
|
||||||
enc_caps->encodeInputPictureGranularity.height = ANV_MB_HEIGHT;
|
enc_caps->encodeInputPictureGranularity.height = ANV_MB_HEIGHT;
|
||||||
/* Both flags are required by the ffmpeg h264_vulkan encoder. Note
|
/* Bytes written comes from the MFC bitstream byte counter; see the
|
||||||
* that bytes-written currently reports the full dstBufferRange rather
|
* feedback query write in anv_h264_encode_video(). */
|
||||||
* than the real encoded size; reading back the MFC bit counter is not
|
|
||||||
* yet implemented. */
|
|
||||||
enc_caps->supportedEncodeFeedbackFlags =
|
enc_caps->supportedEncodeFeedbackFlags =
|
||||||
VK_VIDEO_ENCODE_FEEDBACK_BITSTREAM_BUFFER_OFFSET_BIT_KHR |
|
VK_VIDEO_ENCODE_FEEDBACK_BITSTREAM_BUFFER_OFFSET_BIT_KHR |
|
||||||
VK_VIDEO_ENCODE_FEEDBACK_BITSTREAM_BYTES_WRITTEN_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 *h264_enc_ext =
|
||||||
(struct VkVideoEncodeH264CapabilitiesKHR *)
|
(struct VkVideoEncodeH264CapabilitiesKHR *)
|
||||||
vk_find_struct(pCapabilities->pNext, VIDEO_ENCODE_H264_CAPABILITIES_KHR);
|
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 slice_start_mb, uint32_t slice_end_mb,
|
||||||
uint32_t width_in_mbs, uint32_t height_in_mbs,
|
uint32_t width_in_mbs, uint32_t height_in_mbs,
|
||||||
uint32_t qp, bool is_last_slice,
|
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
|
/* A P slice references the previous reconstructed frame; an I slice does
|
||||||
* not. B slices are out of scope. */
|
* 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[1] = (y << 8) | x; /* Cbp4x4Y=0, MbY/XCnt */
|
||||||
pak.InlineData[3] = ((uint32_t)last_mb << 26) | qp;
|
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
|
/* HASVK_ENC_SKIP_ONLY falls back to the older all-skip path, which
|
||||||
* carries no residual at all: useful for isolating whether a P
|
* carries no residual at all: useful for isolating whether a P
|
||||||
* frame problem is in the prediction or in the residual. */
|
* 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[6] = 0; /* ref index L1 = 0 */
|
||||||
pak.InlineData[7] = 0;
|
pak.InlineData[7] = 0;
|
||||||
} else {
|
} 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 */
|
pak.InlineData[0] = 0x000e0000 /* CbpDcY/U/V = 1 */
|
||||||
| (1u << 13) /* IntraMbFlag */
|
| (1u << 13) /* IntraMbFlag */
|
||||||
| (0x17u << 8); /* MbType5Bits = 23: I_16x16 DC */
|
| (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.
|
* >= slice_start_mb as well as inside the picture.
|
||||||
* bit7 F, bit6 A, bit5 E: left | bit4 B: top | bit3 C: top-right |
|
* 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). */
|
* 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;
|
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);
|
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);
|
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);
|
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);
|
intra_avail |= (1u << 2);
|
||||||
|
#undef NEIGHBOUR_OK
|
||||||
pak.InlineData[6] = intra_avail;
|
pak.InlineData[6] = intra_avail;
|
||||||
pak.InlineData[7] = 0;
|
pak.InlineData[7] = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1132,6 +1132,24 @@ anv_h264_encode_video(struct anv_cmd_buffer *cmd_buffer,
|
|||||||
encode_info->dstBufferRange,
|
encode_info->dstBufferRange,
|
||||||
width_in_mbs, height_in_mbs, is_p, ref_address);
|
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
|
/* 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
|
* [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
|
* 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,
|
slice_start_mb, slice_end_mb,
|
||||||
width_in_mbs, height_in_mbs,
|
width_in_mbs, height_in_mbs,
|
||||||
(uint32_t)entry->constantQp,
|
(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
|
/* Write the VK_QUERY_TYPE_VIDEO_ENCODE_FEEDBACK_KHR result here, where the
|
||||||
|
|||||||
Reference in new issue
Block a user