hasvk: Properly handle interlaced video.

There was no handling here at all so the GPU would just hang immediately.

Assisted-by: Claude
This commit is contained in:
dancingmirrors authored and elwrcl committed 2026-07-21 22:25:25 +03:00
1 parent a7089e8341
commit 2e5cca4d05
3 files changed
+361 -17

No files matched your search

+324 -9
View File
@@ -102,6 +102,190 @@ anv_h264_rbsp_se(struct anv_h264_rbsp *st)
return (v & 1) ? (int)((v + 1) >> 1) : -(int)(v >> 1); return (v & 1) ? (int)((v + 1) >> 1) : -(int)(v >> 1);
} }
struct anv_h264_fref {
uint8_t slot;
uint8_t parities;
int32_t frame_poc;
int32_t fnwrap;
uint32_t lt_idx;
bool is_long;
};
#define ANV_H264_PAR_TOP 1
#define ANV_H264_PAR_BOTTOM 2
static void
anv_h264_gather_frefs(
uint32_t ref_slot_count,
const VkVideoReferenceSlotInfoKHR *ref_slots,
uint32_t cur_frame_num,
uint32_t MaxFrameNum,
struct anv_h264_fref *st, int *nst_out,
struct anv_h264_fref *lt, int *nlt_out)
{
int nst = 0, nlt = 0;
for (uint32_t i = 0; i < ref_slot_count; i++) {
const VkVideoDecodeH264DpbSlotInfoKHR *dpb =
vk_find_struct_const(ref_slots[i].pNext,
VIDEO_DECODE_H264_DPB_SLOT_INFO_KHR);
if (!dpb || !dpb->pStdReferenceInfo) continue;
const StdVideoDecodeH264ReferenceInfo *ri = dpb->pStdReferenceInfo;
uint8_t parities = (ri->flags.top_field_flag ? ANV_H264_PAR_TOP : 0) |
(ri->flags.bottom_field_flag ? ANV_H264_PAR_BOTTOM : 0);
if (parities == 0)
continue;
struct anv_h264_fref f;
f.slot = (uint8_t)ref_slots[i].slotIndex;
f.parities = parities;
f.is_long = ri->flags.used_for_long_term_reference;
int32_t poc_top = ri->PicOrderCnt[0];
int32_t poc_bot = ri->PicOrderCnt[1];
if (parities == ANV_H264_PAR_TOP)
f.frame_poc = poc_top;
else if (parities == ANV_H264_PAR_BOTTOM)
f.frame_poc = poc_bot;
else
f.frame_poc = MIN2(poc_top, poc_bot);
f.lt_idx = ri->FrameNum;
f.fnwrap = (ri->FrameNum > cur_frame_num) ?
(int32_t)ri->FrameNum - (int32_t)MaxFrameNum :
(int32_t)ri->FrameNum;
if (f.is_long) {
if (nlt < ANV_H264_MAX_REF_FRAMES) lt[nlt++] = f;
} else {
if (nst < ANV_H264_MAX_REF_FRAMES) st[nst++] = f;
}
}
*nst_out = nst;
*nlt_out = nlt;
}
/* H.264 8.2.4.2.5 */
static void
anv_h264_emit_field_list(uint8_t *slots, uint8_t *bottoms, int cap, int *idx,
const struct anv_h264_fref *in, int len, int sel)
{
int i0 = 0, i1 = 0;
int opp = sel ^ 3;
while ((i0 < len || i1 < len) && *idx < cap) {
while (i0 < len && !(in[i0].parities & sel)) i0++;
while (i1 < len && !(in[i1].parities & opp)) i1++;
if (i0 < len && *idx < cap) {
slots[*idx] = in[i0].slot;
bottoms[*idx] = (sel == ANV_H264_PAR_BOTTOM);
(*idx)++; i0++;
}
if (i1 < len && *idx < cap) {
slots[*idx] = in[i1].slot;
bottoms[*idx] = (opp == ANV_H264_PAR_BOTTOM);
(*idx)++; i1++;
}
}
}
static void
anv_h264_sort_fnwrap_desc(struct anv_h264_fref *a, int n)
{
for (int i = 1; i < n; i++) {
struct anv_h264_fref v = a[i];
int j = i - 1;
while (j >= 0 && a[j].fnwrap < v.fnwrap) { a[j+1] = a[j]; j--; }
a[j+1] = v;
}
}
static void
anv_h264_sort_ltidx_asc(struct anv_h264_fref *a, int n)
{
for (int i = 1; i < n; i++) {
struct anv_h264_fref v = a[i];
int j = i - 1;
while (j >= 0 && a[j].lt_idx > v.lt_idx) { a[j+1] = a[j]; j--; }
a[j+1] = v;
}
}
static void
anv_h264_build_field_ref_lists(
int slice_type, int sel, int32_t cur_field_poc,
uint32_t cur_frame_num, uint32_t MaxFrameNum,
uint32_t ref_slot_count, const VkVideoReferenceSlotInfoKHR *ref_slots,
uint8_t l0[], uint8_t l0b[], uint8_t l1[], uint8_t l1b[])
{
memset(l0, ANV_H264_INVALID_SLOT, ANV_H264_MAX_REF_FRAMES);
memset(l1, ANV_H264_INVALID_SLOT, ANV_H264_MAX_REF_FRAMES);
memset(l0b, 0, ANV_H264_MAX_REF_FRAMES);
memset(l1b, 0, ANV_H264_MAX_REF_FRAMES);
if (slice_type == ANV_H264_SLICE_I || slice_type == ANV_H264_SLICE_SI)
return;
struct anv_h264_fref st[ANV_H264_MAX_REF_FRAMES];
struct anv_h264_fref lt[ANV_H264_MAX_REF_FRAMES];
int nst = 0, nlt = 0;
anv_h264_gather_frefs(ref_slot_count, ref_slots, cur_frame_num, MaxFrameNum,
st, &nst, lt, &nlt);
anv_h264_sort_ltidx_asc(lt, nlt);
if (slice_type == ANV_H264_SLICE_P || slice_type == ANV_H264_SLICE_SP) {
anv_h264_sort_fnwrap_desc(st, nst);
int idx = 0;
anv_h264_emit_field_list(l0, l0b, ANV_H264_MAX_REF_FRAMES, &idx,
st, nst, sel);
anv_h264_emit_field_list(l0, l0b, ANV_H264_MAX_REF_FRAMES, &idx,
lt, nlt, sel);
return;
}
struct anv_h264_fref o0[ANV_H264_MAX_REF_FRAMES];
struct anv_h264_fref o1[ANV_H264_MAX_REF_FRAMES];
int no0 = 0, no1 = 0;
for (int i = 0; i < nst; i++)
if (st[i].frame_poc < cur_field_poc) o0[no0++] = st[i];
for (int i = 1; i < no0; i++) {
struct anv_h264_fref v = o0[i]; int j = i - 1;
while (j >= 0 && o0[j].frame_poc < v.frame_poc) { o0[j+1] = o0[j]; j--; }
o0[j+1] = v;
}
int split0 = no0;
for (int i = 0; i < nst; i++)
if (st[i].frame_poc >= cur_field_poc) o0[no0++] = st[i];
for (int i = split0 + 1; i < no0; i++) {
struct anv_h264_fref v = o0[i]; int j = i - 1;
while (j >= split0 && o0[j].frame_poc > v.frame_poc) { o0[j+1] = o0[j]; j--; }
o0[j+1] = v;
}
for (int i = 0; i < nst; i++)
if (st[i].frame_poc >= cur_field_poc) o1[no1++] = st[i];
for (int i = 1; i < no1; i++) {
struct anv_h264_fref v = o1[i]; int j = i - 1;
while (j >= 0 && o1[j].frame_poc > v.frame_poc) { o1[j+1] = o1[j]; j--; }
o1[j+1] = v;
}
int split1 = no1;
for (int i = 0; i < nst; i++)
if (st[i].frame_poc < cur_field_poc) o1[no1++] = st[i];
for (int i = split1 + 1; i < no1; i++) {
struct anv_h264_fref v = o1[i]; int j = i - 1;
while (j >= split1 && o1[j].frame_poc < v.frame_poc) { o1[j+1] = o1[j]; j--; }
o1[j+1] = v;
}
int idx0 = 0, idx1 = 0;
anv_h264_emit_field_list(l0, l0b, ANV_H264_MAX_REF_FRAMES, &idx0, o0, no0, sel);
anv_h264_emit_field_list(l0, l0b, ANV_H264_MAX_REF_FRAMES, &idx0, lt, nlt, sel);
anv_h264_emit_field_list(l1, l1b, ANV_H264_MAX_REF_FRAMES, &idx1, o1, no1, sel);
anv_h264_emit_field_list(l1, l1b, ANV_H264_MAX_REF_FRAMES, &idx1, lt, nlt, sel);
}
static void static void
anv_h264_build_default_ref_list( anv_h264_build_default_ref_list(
int slice_type, int slice_type,
@@ -273,6 +457,107 @@ anv_h264_apply_ref_pic_list_mod(
} while (idc != 3); } while (idc != 3);
} }
/* H.264 8.2.4.3.1 */
static void
anv_h264_apply_ref_pic_list_mod_field(
struct anv_h264_rbsp *st_rbsp,
uint32_t ref_slot_count,
const VkVideoReferenceSlotInfoKHR *ref_slots,
uint32_t cur_frame_num,
uint32_t MaxFrameNum,
int sel,
uint8_t list[ANV_H264_MAX_REF_FRAMES],
uint8_t listb[ANV_H264_MAX_REF_FRAMES],
int active_count)
{
int mod_flag = (int)anv_h264_rbsp_u(st_rbsp, 1);
if (!mod_flag || st_rbsp->error) return;
struct anv_h264_fref st[ANV_H264_MAX_REF_FRAMES];
struct anv_h264_fref lt[ANV_H264_MAX_REF_FRAMES];
int nst = 0, nlt = 0;
anv_h264_gather_frefs(ref_slot_count, ref_slots, cur_frame_num, MaxFrameNum,
st, &nst, lt, &nlt);
int32_t CurrPicNum = 2 * (int32_t)cur_frame_num + 1;
int32_t MaxPicNum = 2 * (int32_t)MaxFrameNum;
int32_t picNumPred = CurrPicNum;
int refIdx = 0;
unsigned idc;
do {
idc = anv_h264_rbsp_ue(st_rbsp);
if (st_rbsp->error) return;
if (idc == 3) break;
if (idc > 3) { st_rbsp->error = true; return; }
uint8_t found_slot = ANV_H264_INVALID_SLOT;
uint8_t found_bottom = 0;
if (idc == 0 || idc == 1) {
int32_t diff = (int32_t)anv_h264_rbsp_ue(st_rbsp) + 1;
if (st_rbsp->error) return;
int32_t noWrap;
if (idc == 0)
noWrap = (picNumPred - diff < 0) ?
picNumPred - diff + MaxPicNum : picNumPred - diff;
else
noWrap = (picNumPred + diff >= MaxPicNum) ?
picNumPred + diff - MaxPicNum : picNumPred + diff;
picNumPred = noWrap;
int32_t picNum = (noWrap > CurrPicNum) ? noWrap - MaxPicNum : noWrap;
int32_t want_fnw = picNum >> 1;
int want_par = (picNum & 1) ? sel : (sel ^ 3);
for (int i = 0; i < nst; i++) {
if (st[i].fnwrap == want_fnw && (st[i].parities & want_par)) {
found_slot = st[i].slot;
found_bottom = (want_par == ANV_H264_PAR_BOTTOM);
break;
}
}
} else {
uint32_t ltpn = anv_h264_rbsp_ue(st_rbsp);
if (st_rbsp->error) return;
uint32_t want_lt = ltpn >> 1;
int want_par = (ltpn & 1) ? sel : (sel ^ 3);
for (int i = 0; i < nlt; i++) {
if (lt[i].lt_idx == want_lt && (lt[i].parities & want_par)) {
found_slot = lt[i].slot;
found_bottom = (want_par == ANV_H264_PAR_BOTTOM);
break;
}
}
}
if (found_slot == ANV_H264_INVALID_SLOT) continue;
for (int k = active_count - 1; k > refIdx && k < ANV_H264_MAX_REF_FRAMES; k--) {
list[k] = list[k - 1];
listb[k] = listb[k - 1];
}
if (refIdx < ANV_H264_MAX_REF_FRAMES) {
list[refIdx] = found_slot;
listb[refIdx] = found_bottom;
}
int w = refIdx + 1;
for (int k = refIdx + 1;
k < active_count + 1 && k < ANV_H264_MAX_REF_FRAMES &&
w < ANV_H264_MAX_REF_FRAMES; k++) {
if (!(list[k] == found_slot && listb[k] == found_bottom)) {
list[w] = list[k];
listb[w] = listb[k];
w++;
}
}
if (w < ANV_H264_MAX_REF_FRAMES)
list[w] = ANV_H264_INVALID_SLOT;
refIdx++;
} while (idc != 3);
}
static void static void
anv_h264_parse_pred_weight_table( anv_h264_parse_pred_weight_table(
struct anv_h264_rbsp *st, struct anv_h264_rbsp *st,
@@ -419,10 +704,28 @@ anv_h264_parse_slice_header(
if (slice_type > 4) slice_type -= 5; if (slice_type > 4) slice_type -= 5;
out->slice_type = slice_type; out->slice_type = slice_type;
int32_t curr_poc = pic_info->pStdPictureInfo->PicOrderCnt[0]; bool cur_field_pic = pic_info->pStdPictureInfo->flags.field_pic_flag;
anv_h264_build_default_ref_list(slice_type, curr_poc, bool cur_bottom = pic_info->pStdPictureInfo->flags.bottom_field_flag;
ref_slot_count, ref_slots, int cur_sel = (cur_field_pic && cur_bottom) ?
out->ref_list0, out->ref_list1); ANV_H264_PAR_BOTTOM : ANV_H264_PAR_TOP;
uint32_t cur_frame_num = pic_info->pStdPictureInfo->frame_num;
uint32_t MaxFrameNumForList = 1u << (sps->log2_max_frame_num_minus4 + 4);
int32_t curr_poc = cur_field_pic && cur_bottom ?
pic_info->pStdPictureInfo->PicOrderCnt[1] :
pic_info->pStdPictureInfo->PicOrderCnt[0];
if (cur_field_pic) {
anv_h264_build_field_ref_lists(slice_type, cur_sel, curr_poc,
cur_frame_num, MaxFrameNumForList,
ref_slot_count, ref_slots,
out->ref_list0, out->ref_list0_bottom,
out->ref_list1, out->ref_list1_bottom);
} else {
anv_h264_build_default_ref_list(slice_type, curr_poc,
ref_slot_count, ref_slots,
out->ref_list0, out->ref_list1);
}
anv_h264_rbsp_ue(&st); anv_h264_rbsp_ue(&st);
@@ -480,13 +783,25 @@ anv_h264_parse_slice_header(
if (active_l1 > ANV_H264_MAX_REF_FRAMES) active_l1 = ANV_H264_MAX_REF_FRAMES; if (active_l1 > ANV_H264_MAX_REF_FRAMES) active_l1 = ANV_H264_MAX_REF_FRAMES;
if (slice_type != ANV_H264_SLICE_I && slice_type != ANV_H264_SLICE_SI) { if (slice_type != ANV_H264_SLICE_I && slice_type != ANV_H264_SLICE_SI) {
anv_h264_apply_ref_pic_list_mod(&st, ref_slot_count, ref_slots, if (cur_field_pic) {
frame_num, MaxFrameNum, anv_h264_apply_ref_pic_list_mod_field(&st, ref_slot_count, ref_slots,
out->ref_list0, active_l0); frame_num, MaxFrameNum, cur_sel,
if (slice_type == ANV_H264_SLICE_B) out->ref_list0,
out->ref_list0_bottom, active_l0);
if (slice_type == ANV_H264_SLICE_B)
anv_h264_apply_ref_pic_list_mod_field(&st, ref_slot_count, ref_slots,
frame_num, MaxFrameNum, cur_sel,
out->ref_list1,
out->ref_list1_bottom, active_l1);
} else {
anv_h264_apply_ref_pic_list_mod(&st, ref_slot_count, ref_slots, anv_h264_apply_ref_pic_list_mod(&st, ref_slot_count, ref_slots,
frame_num, MaxFrameNum, frame_num, MaxFrameNum,
out->ref_list1, active_l1); out->ref_list0, active_l0);
if (slice_type == ANV_H264_SLICE_B)
anv_h264_apply_ref_pic_list_mod(&st, ref_slot_count, ref_slots,
frame_num, MaxFrameNum,
out->ref_list1, active_l1);
}
} }
VALID_RBSP_OR_BAIL(st) VALID_RBSP_OR_BAIL(st)
@@ -55,6 +55,9 @@ struct anv_h264_slice_params {
uint8_t ref_list0[ANV_H264_MAX_REF_FRAMES]; uint8_t ref_list0[ANV_H264_MAX_REF_FRAMES];
uint8_t ref_list1[ANV_H264_MAX_REF_FRAMES]; uint8_t ref_list1[ANV_H264_MAX_REF_FRAMES];
uint8_t ref_list0_bottom[ANV_H264_MAX_REF_FRAMES];
uint8_t ref_list1_bottom[ANV_H264_MAX_REF_FRAMES];
bool has_weight_offsets_l0; bool has_weight_offsets_l0;
bool has_weight_offsets_l1; bool has_weight_offsets_l1;
@@ -96,4 +99,4 @@ anv_h264_parse_slice_header(
const VkVideoReferenceSlotInfoKHR *ref_slots, const VkVideoReferenceSlotInfoKHR *ref_slots,
struct anv_h264_slice_params *out); struct anv_h264_slice_params *out);
#endif /* ANV_VIDEO_H264_SLICE_H */ #endif /* ANV_VIDEO_H264_SLICE_H */
+33 -7
View File
@@ -512,6 +512,14 @@ anv_h264_decode_video(struct anv_cmd_buffer *cmd_buffer,
uint32_t mb_height = sps->flags.frame_mbs_only_flag ? uint32_t mb_height = sps->flags.frame_mbs_only_flag ?
pic_height_in_map_units : pic_height_in_map_units * 2; pic_height_in_map_units : pic_height_in_map_units * 2;
/* Slice vertical positions are expressed in macroblock rows.
* Under MBAFF first_mb_in_slice addresses a macroblock pair, so the true MB
* row is (first_mb_in_slice / mb_width) * 2 while the column is unchanged.
*/
bool field_pic = h264_pic_info->pStdPictureInfo->flags.field_pic_flag;
bool mbaff = sps->flags.mb_adaptive_frame_field_flag && !field_pic;
uint32_t pic_height_mbs = field_pic ? (mb_height / 2) : mb_height;
struct anv_h264_slice_params *sp = struct anv_h264_slice_params *sp =
calloc(h264_pic_info->sliceCount, sizeof(*sp)); calloc(h264_pic_info->sliceCount, sizeof(*sp));
if (sp) { if (sp) {
@@ -633,6 +641,9 @@ anv_h264_decode_video(struct anv_cmd_buffer *cmd_buffer,
const uint8_t *rl = (list == 0) ? const uint8_t *rl = (list == 0) ?
(cur ? cur->ref_list0 : NULL) : (cur ? cur->ref_list0 : NULL) :
(cur ? cur->ref_list1 : NULL); (cur ? cur->ref_list1 : NULL);
const uint8_t *rlb = (list == 0) ?
(cur ? cur->ref_list0_bottom : NULL) :
(cur ? cur->ref_list1_bottom : NULL);
for (unsigned e = 0; e < 32; e++) { for (unsigned e = 0; e < 32; e++) {
uint8_t slot = (rl && e < ANV_H264_MAX_REF_FRAMES) ? uint8_t slot = (rl && e < ANV_H264_MAX_REF_FRAMES) ?
rl[e] : ANV_H264_INVALID_SLOT; rl[e] : ANV_H264_INVALID_SLOT;
@@ -654,8 +665,11 @@ anv_h264_decode_video(struct anv_cmd_buffer *cmd_buffer,
break; break;
} }
} }
bool ref_bottom = field_pic && rlb &&
e < ANV_H264_MAX_REF_FRAMES && rlb[e];
ref_idx.ReferenceListEntry[e] = ref_idx.ReferenceListEntry[e] =
(is_long ? 0x60u : 0x20u) | ((uint8_t)hw << 1); (is_long ? 0x60u : 0x20u) | ((uint8_t)hw << 1) |
(ref_bottom ? 0x01u : 0x00u);
} }
} }
} }
@@ -697,6 +711,8 @@ anv_h264_decode_video(struct anv_cmd_buffer *cmd_buffer,
if (s == 0 && cur && cur->first_mb_in_slice != 0) { if (s == 0 && cur && cur->first_mb_in_slice != 0) {
uint32_t first_hor = cur->first_mb_in_slice % mb_width; uint32_t first_hor = cur->first_mb_in_slice % mb_width;
uint32_t first_ver = cur->first_mb_in_slice / mb_width; uint32_t first_ver = cur->first_mb_in_slice / mb_width;
if (mbaff)
first_ver <<= 1;
anv_batch_emit(&cmd_buffer->batch, GENX(MFX_AVC_SLICE_STATE), phantom) { anv_batch_emit(&cmd_buffer->batch, GENX(MFX_AVC_SLICE_STATE), phantom) {
phantom.SliceType = ISlice; phantom.SliceType = ISlice;
phantom.SliceStartMBNumber = 0; phantom.SliceStartMBNumber = 0;
@@ -722,15 +738,25 @@ anv_h264_decode_video(struct anv_cmd_buffer *cmd_buffer,
{ {
int st = cur ? cur->slice_type : ANV_H264_SLICE_I; int st = cur ? cur->slice_type : ANV_H264_SLICE_I;
uint32_t next_fmb = (uint32_t)(mb_width * mb_height);
if (!last_slice && sp)
next_fmb = (uint32_t)sp[s + 1].first_mb_in_slice;
uint32_t cur_fmb = cur ? (uint32_t)cur->first_mb_in_slice : 0; uint32_t cur_fmb = cur ? (uint32_t)cur->first_mb_in_slice : 0;
uint32_t cur_hor = cur_fmb % mb_width; uint32_t cur_hor = cur_fmb % mb_width;
uint32_t cur_ver = cur_fmb / mb_width; uint32_t cur_ver = cur_fmb / mb_width;
uint32_t next_hor = next_fmb % mb_width; if (mbaff)
uint32_t next_ver = next_fmb / mb_width; cur_ver <<= 1;
if (next_ver >= mb_height) { next_hor = 0; next_ver = mb_height; }
uint32_t next_hor, next_ver;
if (last_slice || !sp) {
/* This value is already in MB rows, so it can't be doubled for MBAFF. */
next_hor = 0;
next_ver = pic_height_mbs;
} else {
uint32_t next_fmb = (uint32_t)sp[s + 1].first_mb_in_slice;
next_hor = next_fmb % mb_width;
next_ver = next_fmb / mb_width;
if (mbaff)
next_ver <<= 1;
if (next_ver >= pic_height_mbs) { next_hor = 0; next_ver = pic_height_mbs; }
}
anv_batch_emit(&cmd_buffer->batch, GENX(MFX_AVC_SLICE_STATE), ss) { anv_batch_emit(&cmd_buffer->batch, GENX(MFX_AVC_SLICE_STATE), ss) {
ss.SliceType = ss.SliceType =