mirror of
https://github.com/elwrcl/intel-hard-graphics.git
synced 2026-10-31 10:58:03 +03:00
hasvk: Track DPB slots instead of assuming a single reference.
The encoder ignored VkVideoReferenceSlotInfoKHR entirely: it took pReferenceSlots[0] as the one reference, wrote its address into frame store 0 and hardcoded the L0 list to that store. Two DPB slots were all it could honestly advertise, which is fewer than a GOP of any length needs. Record each slot the operation names under its own index, since a slot maps onto the frame store of the same index and MFX_PIPE_BUF_ADDR_STATE holds 16 of those, and build the L0 entries from StdVideoEncodeH264ReferenceListsInfo, whose RefPicList0 already carries slot indices. Clients that pass reference slots without a list keep the old behaviour. maxDpbSlots therefore goes to 16. maxActiveReferencePictures stays at 1: every macroblock is still coded against reference index 0, so a picture may be kept in the DPB but only the one it names first is predicted from. dEQP-VK.video.encode.h264.intra_refresh_* goes from 12 Pass and 32 stopped at the maxDpbSlots >= GOP frame count precondition to 36 Pass, the remaining 8 being per-picture-partition mode, which is not offered. The family as a whole is 76 of 120 with no failures. ffmpeg's h264_vulkan encoder no longer trips its slot_index assertion and produces a full GOP. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
5 files changed
+84
-29
No files matched your search
@@ -86,7 +86,8 @@ void genX(hasvk_mfc_emit_static_state)(struct anv_cmd_buffer *cmd_buffer,
|
|||||||
uint32_t dst_page_offset,
|
uint32_t dst_page_offset,
|
||||||
uint64_t dst_buffer_range,
|
uint64_t dst_buffer_range,
|
||||||
uint32_t width_in_mbs, uint32_t height_in_mbs,
|
uint32_t width_in_mbs, uint32_t height_in_mbs,
|
||||||
bool is_p, struct anv_address ref_address);
|
bool is_p,
|
||||||
|
const struct anv_video_reference_state *refs);
|
||||||
|
|
||||||
/* Emits one slice: MFX_AVC_SLICE_STATE, the slice-header insert, the per-MB
|
/* Emits one slice: MFX_AVC_SLICE_STATE, the slice-header insert, the per-MB
|
||||||
* PAK objects for the macroblock range [slice_start_mb, slice_end_mb), and the
|
* PAK objects for the macroblock range [slice_start_mb, slice_end_mb), and the
|
||||||
|
|||||||
@@ -3852,6 +3852,22 @@ struct anv_video_session_params {
|
|||||||
struct vk_video_session_parameters vk;
|
struct vk_video_session_parameters vk;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* MFX_PIPE_BUF_ADDR_STATE has 16 reference picture addresses, so the hardware
|
||||||
|
* can hold 16 frame stores. A DPB slot maps straight onto a frame store of
|
||||||
|
* the same index, which keeps the reference lists trivial to build.
|
||||||
|
*/
|
||||||
|
#define ANV_VIDEO_MAX_DPB_SLOTS 16
|
||||||
|
|
||||||
|
/* The reference pictures one encode operation can reach: where each DPB slot
|
||||||
|
* lives, and which slots its L0 list names, in order.
|
||||||
|
*/
|
||||||
|
struct anv_video_reference_state {
|
||||||
|
struct anv_address slot_address[ANV_VIDEO_MAX_DPB_SLOTS];
|
||||||
|
bool slot_valid[ANV_VIDEO_MAX_DPB_SLOTS];
|
||||||
|
uint8_t l0_slot[ANV_VIDEO_MAX_DPB_SLOTS];
|
||||||
|
uint8_t l0_count;
|
||||||
|
};
|
||||||
|
|
||||||
/* One picture's slice of an intra refresh cycle: the macroblocks of region
|
/* One picture's slice of an intra refresh cycle: the macroblocks of region
|
||||||
* `index` out of `cycle_duration` are coded intra, the rest inter.
|
* `index` out of `cycle_duration` are coded intra, the rest inter.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -202,11 +202,12 @@ anv_GetPhysicalDeviceVideoCapabilitiesKHR(VkPhysicalDevice physicalDevice,
|
|||||||
pCapabilities->stdHeaderVersion.specVersion =
|
pCapabilities->stdHeaderVersion.specVersion =
|
||||||
VK_STD_VULKAN_VIDEO_CODEC_H264_DECODE_SPEC_VERSION;
|
VK_STD_VULKAN_VIDEO_CODEC_H264_DECODE_SPEC_VERSION;
|
||||||
} else {
|
} else {
|
||||||
/* Limits reflecting what the encoder emits: I and P frames, CQP, single
|
/* Limits reflecting what the encoder emits: I and P frames, CQP. A DPB
|
||||||
* reference. P frames use one reference (the previous reconstructed
|
* slot maps onto a frame store of the same index and there are 16 of
|
||||||
* frame) plus the current setup slot, so two DPB slots and one active
|
* those, so a long GOP can keep every picture it likes around. Only one
|
||||||
* reference. B frames and multi-reference are not implemented. */
|
* of them can be predicted from, though: each macroblock is coded against
|
||||||
pCapabilities->maxDpbSlots = 2;
|
* reference index 0, so multi-reference and B frames are still out. */
|
||||||
|
pCapabilities->maxDpbSlots = ANV_VIDEO_MAX_DPB_SLOTS;
|
||||||
pCapabilities->maxActiveReferencePictures = 1;
|
pCapabilities->maxActiveReferencePictures = 1;
|
||||||
|
|
||||||
struct VkVideoEncodeCapabilitiesKHR *enc_caps =
|
struct VkVideoEncodeCapabilitiesKHR *enc_caps =
|
||||||
|
|||||||
@@ -49,7 +49,8 @@ genX(hasvk_mfc_emit_static_state)(struct anv_cmd_buffer *cmd_buffer,
|
|||||||
uint32_t dst_page_offset,
|
uint32_t dst_page_offset,
|
||||||
uint64_t dst_buffer_range,
|
uint64_t dst_buffer_range,
|
||||||
uint32_t width_in_mbs, uint32_t height_in_mbs,
|
uint32_t width_in_mbs, uint32_t height_in_mbs,
|
||||||
bool is_p, struct anv_address ref_address)
|
bool is_p,
|
||||||
|
const struct anv_video_reference_state *refs)
|
||||||
{
|
{
|
||||||
struct anv_video_session *vid = cmd_buffer->video.vid;
|
struct anv_video_session *vid = cmd_buffer->video.vid;
|
||||||
|
|
||||||
@@ -149,10 +150,13 @@ genX(hasvk_mfc_emit_static_state)(struct anv_cmd_buffer *cmd_buffer,
|
|||||||
* them with the valid, mapped recon address. */
|
* them with the valid, mapped recon address. */
|
||||||
for (unsigned i = 0; i < ARRAY_SIZE(buf.ReferencePictureAddress); i++)
|
for (unsigned i = 0; i < ARRAY_SIZE(buf.ReferencePictureAddress); i++)
|
||||||
buf.ReferencePictureAddress[i] = recon_address;
|
buf.ReferencePictureAddress[i] = recon_address;
|
||||||
/* Frame store 0 is the P-frame's reference (previous reconstructed
|
/* Frame store i holds DPB slot i, so a slot the picture can reference
|
||||||
* frame); REF_IDX_STATE L0[0] maps ref index 0 to this frame store. */
|
* goes straight into its own entry; the rest keep the recon address the
|
||||||
if (is_p)
|
* PRM asks for. */
|
||||||
buf.ReferencePictureAddress[0] = ref_address;
|
for (unsigned i = 0; i < ANV_VIDEO_MAX_DPB_SLOTS; i++) {
|
||||||
|
if (refs->slot_valid[i])
|
||||||
|
buf.ReferencePictureAddress[i] = refs->slot_address[i];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
anv_batch_emit(&cmd_buffer->batch, GENX(MFX_BSP_BUF_BASE_ADDR_STATE), bsp) {
|
anv_batch_emit(&cmd_buffer->batch, GENX(MFX_BSP_BUF_BASE_ADDR_STATE), bsp) {
|
||||||
@@ -290,17 +294,22 @@ genX(hasvk_mfc_emit_static_state)(struct anv_cmd_buffer *cmd_buffer,
|
|||||||
dmv.POCList[i] = i / 2;
|
dmv.POCList[i] = i / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reference index lists. Entries are set to the "non-existing" marker 0x80
|
/* Reference index lists. Unused entries carry the "non-existing" marker
|
||||||
* (intel_mfc_avc_ref_idx_state). For a P frame, L0[0] points at frame store
|
* 0x80 (intel_mfc_avc_ref_idx_state). A used one is
|
||||||
* 0: entry = (is_long_term<<6)|((top^bottom^1)<<5)|(frame_store_id<<1)|... ;
|
* (is_long_term<<6)|((top^bottom^1)<<5)|(frame_store_id<<1), which for a
|
||||||
* for a short-term progressive frame at store 0 this is 0x20
|
* short-term progressive frame comes out as 0x20|(frame_store_id<<1)
|
||||||
* (intel_get_ref_idx_state_1). L1 is unused (P has no backward references). */
|
* (intel_get_ref_idx_state_1). L1 stays empty: B frames are out of scope,
|
||||||
|
* so there are no backward references. */
|
||||||
for (unsigned list = 0; list < 2; list++) {
|
for (unsigned list = 0; list < 2; list++) {
|
||||||
anv_batch_emit(&cmd_buffer->batch, GENX(MFX_AVC_REF_IDX_STATE), ref_idx) {
|
anv_batch_emit(&cmd_buffer->batch, GENX(MFX_AVC_REF_IDX_STATE), ref_idx) {
|
||||||
ref_idx.ReferencePictureListSelect = list;
|
ref_idx.ReferencePictureListSelect = list;
|
||||||
memset(ref_idx.ReferenceListEntry, 0x80, sizeof(ref_idx.ReferenceListEntry));
|
memset(ref_idx.ReferenceListEntry, 0x80, sizeof(ref_idx.ReferenceListEntry));
|
||||||
if (is_p && list == 0)
|
if (is_p && list == 0) {
|
||||||
ref_idx.ReferenceListEntry[0] = 0x20;
|
unsigned n = MIN2(refs->l0_count,
|
||||||
|
ARRAY_SIZE(ref_idx.ReferenceListEntry));
|
||||||
|
for (unsigned i = 0; i < n; i++)
|
||||||
|
ref_idx.ReferenceListEntry[i] = 0x20 | (refs->l0_slot[i] << 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif /* GFX_VERx10 == 70 */
|
#endif /* GFX_VERx10 == 70 */
|
||||||
|
|||||||
@@ -1110,27 +1110,55 @@ anv_h264_encode_video(struct anv_cmd_buffer *cmd_buffer,
|
|||||||
anv_image_dpb_address(recon_iv,
|
anv_image_dpb_address(recon_iv,
|
||||||
encode_info->pSetupReferenceSlot->pPictureResource->baseArrayLayer);
|
encode_info->pSetupReferenceSlot->pPictureResource->baseArrayLayer);
|
||||||
|
|
||||||
/* P frame: the reconstructed picture of the previous frame, supplied as
|
/* Reference pictures. Every slot this operation names gets its address
|
||||||
* pReferenceSlots[0], is the single L0 reference. Absence of a reference
|
* recorded under its own index, and the L0 list is taken from the picture's
|
||||||
* slot means this is an I frame. */
|
* reference lists, whose entries are DPB slot indices. No reference slot at
|
||||||
|
* all means this is an I frame. */
|
||||||
bool is_p = encode_info->referenceSlotCount > 0 &&
|
bool is_p = encode_info->referenceSlotCount > 0 &&
|
||||||
encode_info->pReferenceSlots &&
|
encode_info->pReferenceSlots &&
|
||||||
encode_info->pReferenceSlots[0].pPictureResource;
|
encode_info->pReferenceSlots[0].pPictureResource;
|
||||||
struct anv_address ref_address = ANV_NULL_ADDRESS;
|
struct anv_video_reference_state refs;
|
||||||
if (is_p) {
|
memset(&refs, 0, sizeof(refs));
|
||||||
|
for (uint32_t i = 0; i < encode_info->referenceSlotCount; i++) {
|
||||||
|
const VkVideoReferenceSlotInfoKHR *slot = &encode_info->pReferenceSlots[i];
|
||||||
|
if (!slot->pPictureResource || slot->slotIndex < 0 ||
|
||||||
|
slot->slotIndex >= ANV_VIDEO_MAX_DPB_SLOTS)
|
||||||
|
continue;
|
||||||
const struct anv_image_view *ref_iv =
|
const struct anv_image_view *ref_iv =
|
||||||
anv_image_view_from_handle(
|
anv_image_view_from_handle(slot->pPictureResource->imageViewBinding);
|
||||||
encode_info->pReferenceSlots[0].pPictureResource->imageViewBinding);
|
refs.slot_address[slot->slotIndex] =
|
||||||
ref_address =
|
anv_image_dpb_address(ref_iv, slot->pPictureResource->baseArrayLayer);
|
||||||
anv_image_dpb_address(ref_iv,
|
refs.slot_valid[slot->slotIndex] = true;
|
||||||
encode_info->pReferenceSlots[0].pPictureResource->baseArrayLayer);
|
}
|
||||||
|
|
||||||
|
if (is_p) {
|
||||||
|
const StdVideoEncodeH264ReferenceListsInfo *ref_lists =
|
||||||
|
h264_pic_info->pStdPictureInfo->pRefLists;
|
||||||
|
if (ref_lists) {
|
||||||
|
unsigned n = MIN2(ref_lists->num_ref_idx_l0_active_minus1 + 1,
|
||||||
|
ANV_VIDEO_MAX_DPB_SLOTS);
|
||||||
|
for (unsigned i = 0; i < n; i++) {
|
||||||
|
uint8_t s = ref_lists->RefPicList0[i];
|
||||||
|
if (s >= ANV_VIDEO_MAX_DPB_SLOTS || !refs.slot_valid[s])
|
||||||
|
break;
|
||||||
|
refs.l0_slot[refs.l0_count++] = s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* A client that supplies reference slots but no list still means the
|
||||||
|
* first one, which is what the encoder used to assume unconditionally. */
|
||||||
|
if (refs.l0_count == 0 &&
|
||||||
|
encode_info->pReferenceSlots[0].slotIndex >= 0 &&
|
||||||
|
encode_info->pReferenceSlots[0].slotIndex < ANV_VIDEO_MAX_DPB_SLOTS) {
|
||||||
|
refs.l0_slot[refs.l0_count++] =
|
||||||
|
encode_info->pReferenceSlots[0].slotIndex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Frame-level static state, emitted once before any slice. */
|
/* Frame-level static state, emitted once before any slice. */
|
||||||
genX(hasvk_mfc_emit_static_state)(cmd_buffer, img, recon_address, sps, pps,
|
genX(hasvk_mfc_emit_static_state)(cmd_buffer, img, recon_address, sps, pps,
|
||||||
dst_address, dst_page_off,
|
dst_address, dst_page_off,
|
||||||
encode_info->dstBufferRange,
|
encode_info->dstBufferRange,
|
||||||
width_in_mbs, height_in_mbs, is_p, ref_address);
|
width_in_mbs, height_in_mbs, is_p, &refs);
|
||||||
|
|
||||||
/* Intra refresh, if the session was created with a mode and this picture
|
/* 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
|
* carries a cycle position. An I frame refreshes everything by itself, so
|
||||||
|
|||||||
Reference in new issue
Block a user