diff --git a/src/intel/vulkan_hasvk/anv_genX.h b/src/intel/vulkan_hasvk/anv_genX.h index 174b1d9..507f307 100644 --- a/src/intel/vulkan_hasvk/anv_genX.h +++ b/src/intel/vulkan_hasvk/anv_genX.h @@ -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 diff --git a/src/intel/vulkan_hasvk/anv_private.h b/src/intel/vulkan_hasvk/anv_private.h index 6efb9b0..07645e7 100644 --- a/src/intel/vulkan_hasvk/anv_private.h +++ b/src/intel/vulkan_hasvk/anv_private.h @@ -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. */ diff --git a/src/intel/vulkan_hasvk/anv_video.c b/src/intel/vulkan_hasvk/anv_video.c index 04eb73b..42b7464 100644 --- a/src/intel/vulkan_hasvk/anv_video.c +++ b/src/intel/vulkan_hasvk/anv_video.c @@ -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 = diff --git a/src/intel/vulkan_hasvk/anv_video_mfc.c b/src/intel/vulkan_hasvk/anv_video_mfc.c index fcfb897..f1f5600 100644 --- a/src/intel/vulkan_hasvk/anv_video_mfc.c +++ b/src/intel/vulkan_hasvk/anv_video_mfc.c @@ -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 */ diff --git a/src/intel/vulkan_hasvk/genX_cmd_video.c b/src/intel/vulkan_hasvk/genX_cmd_video.c index 0b1c0db..278ff00 100644 --- a/src/intel/vulkan_hasvk/genX_cmd_video.c +++ b/src/intel/vulkan_hasvk/genX_cmd_video.c @@ -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