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,
|
||||
uint64_t dst_buffer_range,
|
||||
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
|
||||
* 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;
|
||||
};
|
||||
|
||||
/* 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
|
||||
* `index` out of `cycle_duration` are coded intra, the rest inter.
|
||||
*/
|
||||
|
||||
@@ -202,11 +202,12 @@ anv_GetPhysicalDeviceVideoCapabilitiesKHR(VkPhysicalDevice physicalDevice,
|
||||
pCapabilities->stdHeaderVersion.specVersion =
|
||||
VK_STD_VULKAN_VIDEO_CODEC_H264_DECODE_SPEC_VERSION;
|
||||
} else {
|
||||
/* Limits reflecting what the encoder emits: I and P frames, CQP, single
|
||||
* reference. P frames use one reference (the previous reconstructed
|
||||
* frame) plus the current setup slot, so two DPB slots and one active
|
||||
* reference. B frames and multi-reference are not implemented. */
|
||||
pCapabilities->maxDpbSlots = 2;
|
||||
/* Limits reflecting what the encoder emits: I and P frames, CQP. A DPB
|
||||
* slot maps onto a frame store of the same index and there are 16 of
|
||||
* those, so a long GOP can keep every picture it likes around. Only one
|
||||
* of them can be predicted from, though: each macroblock is coded against
|
||||
* reference index 0, so multi-reference and B frames are still out. */
|
||||
pCapabilities->maxDpbSlots = ANV_VIDEO_MAX_DPB_SLOTS;
|
||||
pCapabilities->maxActiveReferencePictures = 1;
|
||||
|
||||
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,
|
||||
uint64_t dst_buffer_range,
|
||||
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;
|
||||
|
||||
@@ -149,10 +150,13 @@ genX(hasvk_mfc_emit_static_state)(struct anv_cmd_buffer *cmd_buffer,
|
||||
* them with the valid, mapped recon address. */
|
||||
for (unsigned i = 0; i < ARRAY_SIZE(buf.ReferencePictureAddress); i++)
|
||||
buf.ReferencePictureAddress[i] = recon_address;
|
||||
/* Frame store 0 is the P-frame's reference (previous reconstructed
|
||||
* frame); REF_IDX_STATE L0[0] maps ref index 0 to this frame store. */
|
||||
if (is_p)
|
||||
buf.ReferencePictureAddress[0] = ref_address;
|
||||
/* Frame store i holds DPB slot i, so a slot the picture can reference
|
||||
* goes straight into its own entry; the rest keep the recon address the
|
||||
* PRM asks for. */
|
||||
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) {
|
||||
@@ -290,17 +294,22 @@ genX(hasvk_mfc_emit_static_state)(struct anv_cmd_buffer *cmd_buffer,
|
||||
dmv.POCList[i] = i / 2;
|
||||
}
|
||||
|
||||
/* Reference index lists. Entries are set to the "non-existing" marker 0x80
|
||||
* (intel_mfc_avc_ref_idx_state). For a P frame, L0[0] points at frame store
|
||||
* 0: entry = (is_long_term<<6)|((top^bottom^1)<<5)|(frame_store_id<<1)|... ;
|
||||
* for a short-term progressive frame at store 0 this is 0x20
|
||||
* (intel_get_ref_idx_state_1). L1 is unused (P has no backward references). */
|
||||
/* Reference index lists. Unused entries carry the "non-existing" marker
|
||||
* 0x80 (intel_mfc_avc_ref_idx_state). A used one is
|
||||
* (is_long_term<<6)|((top^bottom^1)<<5)|(frame_store_id<<1), which for a
|
||||
* short-term progressive frame comes out as 0x20|(frame_store_id<<1)
|
||||
* (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++) {
|
||||
anv_batch_emit(&cmd_buffer->batch, GENX(MFX_AVC_REF_IDX_STATE), ref_idx) {
|
||||
ref_idx.ReferencePictureListSelect = list;
|
||||
memset(ref_idx.ReferenceListEntry, 0x80, sizeof(ref_idx.ReferenceListEntry));
|
||||
if (is_p && list == 0)
|
||||
ref_idx.ReferenceListEntry[0] = 0x20;
|
||||
if (is_p && list == 0) {
|
||||
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 */
|
||||
|
||||
@@ -1110,27 +1110,55 @@ anv_h264_encode_video(struct anv_cmd_buffer *cmd_buffer,
|
||||
anv_image_dpb_address(recon_iv,
|
||||
encode_info->pSetupReferenceSlot->pPictureResource->baseArrayLayer);
|
||||
|
||||
/* P frame: the reconstructed picture of the previous frame, supplied as
|
||||
* pReferenceSlots[0], is the single L0 reference. Absence of a reference
|
||||
* slot means this is an I frame. */
|
||||
/* Reference pictures. Every slot this operation names gets its address
|
||||
* recorded under its own index, and the L0 list is taken from the picture's
|
||||
* 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 &&
|
||||
encode_info->pReferenceSlots &&
|
||||
encode_info->pReferenceSlots[0].pPictureResource;
|
||||
struct anv_address ref_address = ANV_NULL_ADDRESS;
|
||||
if (is_p) {
|
||||
struct anv_video_reference_state refs;
|
||||
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 =
|
||||
anv_image_view_from_handle(
|
||||
encode_info->pReferenceSlots[0].pPictureResource->imageViewBinding);
|
||||
ref_address =
|
||||
anv_image_dpb_address(ref_iv,
|
||||
encode_info->pReferenceSlots[0].pPictureResource->baseArrayLayer);
|
||||
anv_image_view_from_handle(slot->pPictureResource->imageViewBinding);
|
||||
refs.slot_address[slot->slotIndex] =
|
||||
anv_image_dpb_address(ref_iv, slot->pPictureResource->baseArrayLayer);
|
||||
refs.slot_valid[slot->slotIndex] = true;
|
||||
}
|
||||
|
||||
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. */
|
||||
genX(hasvk_mfc_emit_static_state)(cmd_buffer, img, recon_address, sps, pps,
|
||||
dst_address, dst_page_off,
|
||||
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
|
||||
* carries a cycle position. An I frame refreshes everything by itself, so
|
||||
|
||||
Reference in new issue
Block a user