diff --git a/src/intel/vulkan_hasvk/anv_video_mfc.c b/src/intel/vulkan_hasvk/anv_video_mfc.c index ccdded5..60b1632 100644 --- a/src/intel/vulkan_hasvk/anv_video_mfc.c +++ b/src/intel/vulkan_hasvk/anv_video_mfc.c @@ -16,9 +16,8 @@ * by decode, so its fields are matched against the VA-API reference encoder * (gen6_mfc_avc_slice_state, intel-vaapi-driver) rather than an in-tree user. * - * All values below are validated only for the single-slice, I-frame, CQP case - * currently implemented. Inter prediction, references and rate control are not - * emitted. + * Validated for I and P frames in CQP mode, single reference, no motion + * estimation. B frames, multiple references and rate control are not emitted. */ #include @@ -30,6 +29,16 @@ #include "util/vl_zscan_data.h" #include "anv_video_h264_bitstream.h" +/* Debug switch: code P macroblocks as pure skips, carrying no residual. */ +static bool +anv_video_enc_skip_only(void) +{ + static int enabled = -1; + if (enabled < 0) + enabled = getenv("HASVK_ENC_SKIP_ONLY") != NULL; + return enabled; +} + void genX(hasvk_mfc_emit_static_state)(struct anv_cmd_buffer *cmd_buffer, const struct anv_image *source_image, @@ -205,6 +214,18 @@ genX(hasvk_mfc_emit_static_state)(struct anv_cmd_buffer *cmd_buffer, qm.ForwardQuantizerMatrix[m * 16 + vl_zscan_normal_16[q]] = scaling_lists.ScalingList4x4[m][q]; } + /* Inter matrices are a separate set of registers: ScalingList4x4[3..5] are + * the inter Y/Cb/Cr lists, [0..2] the intra ones. Without this the hardware + * quantizes every inter residual with whatever those registers happen to + * hold, which leaves I frames correct and P residuals corrupted. */ + anv_batch_emit(&cmd_buffer->batch, GENX(MFX_QM_STATE), qm) { + qm.DWordLength = 16; + qm.AVC = AVC_4x4_Inter_MATRIX; + for (unsigned m = 0; m < 3; m++) + for (unsigned q = 0; q < 16; q++) + qm.ForwardQuantizerMatrix[m * 16 + vl_zscan_normal_16[q]] = + scaling_lists.ScalingList4x4[3 + m][q]; + } if (pps->flags.transform_8x8_mode_flag) { anv_batch_emit(&cmd_buffer->batch, GENX(MFX_QM_STATE), qm) { qm.DWordLength = 16; @@ -213,6 +234,13 @@ genX(hasvk_mfc_emit_static_state)(struct anv_cmd_buffer *cmd_buffer, qm.ForwardQuantizerMatrix[vl_zscan_normal[q]] = scaling_lists.ScalingList8x8[0][q]; } + anv_batch_emit(&cmd_buffer->batch, GENX(MFX_QM_STATE), qm) { + qm.DWordLength = 16; + qm.AVC = AVC_8x8_Inter_MATRIX; + for (unsigned q = 0; q < 64; q++) + qm.ForwardQuantizerMatrix[vl_zscan_normal[q]] = + scaling_lists.ScalingList8x8[1][q]; + } } /* MFX_FQM_STATE (forward quantizer matrix) does not exist on Gen7.5/Gen8; @@ -226,6 +254,12 @@ genX(hasvk_mfc_emit_static_state)(struct anv_cmd_buffer *cmd_buffer, for (unsigned q = 0; q < 32; q++) fqm.ForwardQuantizerMatrix[q] = 0x10001000; } + anv_batch_emit(&cmd_buffer->batch, GENX(MFX_FQM_STATE), fqm) { + fqm.DWordLength = 32; + fqm.AVC = AVC_4x4_Inter_MATRIX; + for (unsigned q = 0; q < 32; q++) + fqm.ForwardQuantizerMatrix[q] = 0x10001000; + } if (pps->flags.transform_8x8_mode_flag) { anv_batch_emit(&cmd_buffer->batch, GENX(MFX_FQM_STATE), fqm) { fqm.DWordLength = 32; @@ -233,6 +267,12 @@ genX(hasvk_mfc_emit_static_state)(struct anv_cmd_buffer *cmd_buffer, for (unsigned q = 0; q < 32; q++) fqm.ForwardQuantizerMatrix[q] = 0x10001000; } + anv_batch_emit(&cmd_buffer->batch, GENX(MFX_FQM_STATE), fqm) { + fqm.DWordLength = 32; + fqm.AVC = AVC_8x8_Inter_MATRIX; + for (unsigned q = 0; q < 32; q++) + fqm.ForwardQuantizerMatrix[q] = 0x10001000; + } } /* Direct MV buffers. I-frames scan no reference direct MVs, so only the @@ -364,14 +404,17 @@ genX(hasvk_mfc_emit_slice)(struct anv_cmd_buffer *cmd_buffer, * InlineData[6] (DW9): IntraStruct = per-MB IntraPredAvailFlags plus * ChromaIntraPredMode = DC in bits [1:0]. * - * P-frame path: every MB is P_Skip. Per PRM §2.3.1.1, DW3 bit13 IntraMbFlag=0 - * (inter), bits12:8 MbType5Bits=1 (BP_L0_16x16 — the only P type convertible - * to skip, PRM MbType table), bit2 SkipMbFlag=1 (forces P_Skip: HW uses the - * predicted MV and forces CBP to 0), bits22:20 MvFormat=0 (no MV data). No - * residual and no indirect MV buffer are needed; the reconstructed MB equals - * the motion-compensated (predicted-MV) reference block. For static content - * relative to the reference this is exact. DW7-9 carry the inter sub-mb shape - * (0 = 16x16) and reference indices L0/L1 (0 for the single reference). */ + * P-frame path: every MB is a coded BP_L0_16x16 inter MB with a zero motion + * vector, so PAK transforms and codes the residual against the co-located + * reference block. Per PRM §2.3.1.1, DW3 bit13 IntraMbFlag=0 (inter), + * bits12:8 MbType5Bits=1 (BP_L0_16x16), bits22:20 MvFormat=100 (8MV, reading + * the zeroed 32-byte block from the indirect MV buffer; MvFormat=0 hangs the + * MFC). Unlike Intra16x16 there is no luma DC block, but chroma keeps its 2x2 + * DC one. DW7-9 carry the inter sub-mb shape (0 = 16x16) and reference + * indices L0/L1 (0 for the single reference). + * + * There is no motion estimation yet: a moving block still costs a full + * residual rather than a motion vector. */ for (uint32_t mb = slice_start_mb; mb < slice_end_mb; mb++) { uint32_t x = mb % width_in_mbs; uint32_t y = mb / width_in_mbs; @@ -384,25 +427,23 @@ genX(hasvk_mfc_emit_slice)(struct anv_cmd_buffer *cmd_buffer, pak.InlineData[3] = ((uint32_t)last_mb << 26) | qp; if (is_p) { - /* P_Skip: SkipMbFlag forces the skip (predicted MV = 0 for the - * uniform all-skip case, CBP forced to 0), so the reconstructed MB - * equals the co-located reference block. Exact for content identical - * to the reference. An inter MB requires a valid MvFormat (100/110); - * MvFormat=0 hangs the MFC, so 8MV (100) reads a zeroed 32-byte block - * from the dedicated indirect MV buffer. - * - * NOTE: coded inter (SkipMbFlag=0, CBP set) for changed content was - * implemented and works at QP0 (bit-exact residual) but shows per-MB - * blocky artefacts at higher QP — an encoder/decoder inter MV- - * predictor (mvp) mismatch, analogous to the intra edge-DC corner. - * Left as future work (needs per-MB mvp/neighbour handling); skip is - * the validated path. */ + /* HASVK_ENC_SKIP_ONLY falls back to the older all-skip path, which + * carries no residual at all: useful for isolating whether a P + * frame problem is in the prediction or in the residual. */ pak.IndirectPAKMVDataLength = 32; /* 8MV * 4 bytes */ - pak.InlineData[0] = (1u << 8) /* MbType5Bits = 1: BP_L0_16x16 */ - | (4u << 20) /* MvFormat = 100 (8MV) */ - | (1u << 2); /* SkipMbFlag: force P_Skip */ - pak.InlineData[1] = (y << 8) | x; /* Cbp4x4Y = 0 for skip */ - pak.InlineData[2] = 0; /* Cbp4x4V/U = 0 for skip */ + if (anv_video_enc_skip_only()) { + pak.InlineData[0] = (1u << 8) /* MbType5Bits = 1: BP_L0_16x16 */ + | (4u << 20) /* MvFormat = 100 (8MV) */ + | (1u << 2); /* SkipMbFlag: force P_Skip */ + pak.InlineData[1] = (y << 8) | x; /* Cbp4x4Y = 0 for skip */ + pak.InlineData[2] = 0; /* Cbp4x4V/U = 0 for skip */ + } else { + pak.InlineData[0] = (1u << 8) /* MbType5Bits = 1: BP_L0_16x16 */ + | (4u << 20) /* MvFormat = 100 (8MV) */ + | (3u << 18); /* CbpDcU/V: chroma DC coded */ + pak.InlineData[1] = 0xFFFF0000 | (y << 8) | x; /* all luma 4x4 */ + pak.InlineData[2] = 0x000F000F; /* all chroma AC */ + } pak.InlineData[4] = 0; /* inter sub-mb shape = 16x16 */ pak.InlineData[5] = 0; /* ref index L0 = 0 */ pak.InlineData[6] = 0; /* ref index L1 = 0 */