mirror of
https://github.com/elwrcl/intel-hard-graphics.git
synced 2026-11-01 03:18:02 +03:00
hasvk: Add host_image_copy, H.264 encode and a batch of gen7 fixes.
Squashed work that had been sitting in the working tree. Grouped by area: Extensions VK_EXT_host_image_copy, with a MEMCPY_BIT path that reads and writes the image's own tiled layout, VkSubresourceHostMemcpySizeEXT filled in, and VkHostImageCopyDevicePerformanceQueryEXT answered. Depth/stencil and non-power-of-two bpb stay NotSupported. VK_EXT_pipeline_robustness, VK_KHR_shader_float_controls2, VK_KHR_shader_subgroup_rotate, VK_EXT_vertex_input_dynamic_state, VK_EXT_conditional_rendering (an MI_PREDICATE path for gen7, which has no GPRs), custom_border_color, border_color_swizzle, map_memory2, load_store_op_none, depth_clamp_zero_one, dynamic_rendering_unused_attachments, index_type_uint8, line_rasterization, vertex_attribute_divisor, mutable_descriptor_type. H.264 encode MFC command emission and a bitstream writer, driving intra-only encode through the video queue. Video profile reporting was also corrected from 270 combinations down to the 11 that actually exist. Correctness gfx7 routes typed surface writes through the render cache, so VK_ACCESS_SHADER_WRITE barriers have to flush it -- without that, nothing a fragment shader stored was visible. nir_opt_algebraic's fadd->flrp fusion rules dropped signed zero; '~' only excludes nir_fp_exact, not preserve_signed_zero, so reflect() with a -0 input came out wrong. Guarded with (nsz). Stop advertising STORAGE_READ_WITHOUT_FORMAT: shaderStorageImageReadWithout- Format is false here, and since Vulkan 1.3 the per-format bit is what grants permission, so reporting it while rejecting the pipeline is a contradiction. gfx7's typed data port ignores SURFTYPE_NULL -- the null surface still has one addressable texel, which read and wrote graphics address zero. Bounds check typed image access against the image param when nullDescriptor is enabled, the same trick lower_image_atomic_instr already used on IVB. Formats that require a sampler Y'CbCr conversion no longer report BLIT_SRC/BLIT_DST/STORAGE_IMAGE. Video decode does not need them. Check the surface state allocations in emit_binding_table() and in the push descriptor path instead of writing through a NULL map in release builds. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
28 files changed
+2666
-327
No files matched your search
@@ -0,0 +1,450 @@
|
||||
/*
|
||||
* Copyright © 2024 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* H.264 encode: MFC/MFX static state emission for the AVC encode pipeline.
|
||||
*
|
||||
* This lives in a separate translation unit because genX_pack.h and
|
||||
* genX_video_pack.h share the same include guard (GFX7_IVB_PACK_H); a TU that
|
||||
* includes both silently drops the second. The MFX and MFC encode commands
|
||||
* need genX_video_pack.h, mirroring the decode split between genX_cmd_video.c
|
||||
* and genX_cmd_buffer.c.
|
||||
*
|
||||
* Field names are taken from the decode paths in genX_cmd_video.c where the
|
||||
* same commands are used. MFX_AVC_SLICE_STATE is the exception: it is not used
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "anv_private.h"
|
||||
#include "genxml/gen_macros.h"
|
||||
#include "anv_genX.h"
|
||||
#include "genxml/genX_video_pack.h"
|
||||
#include "util/vl_zscan_data.h"
|
||||
#include "anv_video_h264_bitstream.h"
|
||||
|
||||
void
|
||||
genX(hasvk_mfc_emit_static_state)(struct anv_cmd_buffer *cmd_buffer,
|
||||
const struct anv_image *source_image,
|
||||
struct anv_address recon_address,
|
||||
const StdVideoH264SequenceParameterSet *sps,
|
||||
const StdVideoH264PictureParameterSet *pps,
|
||||
struct anv_address dst_address,
|
||||
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)
|
||||
{
|
||||
struct anv_video_session *vid = cmd_buffer->video.vid;
|
||||
|
||||
anv_batch_emit(&cmd_buffer->batch, GENX(MFX_PIPE_MODE_SELECT), sel) {
|
||||
sel.StandardSelect = SS_AVC;
|
||||
sel.CodecSelect = Encode;
|
||||
/* PAK requires the long-format interface and writes its output through
|
||||
* the stream-out path. Deblocking is disabled here, so reconstruction
|
||||
* uses the pre-deblocking destination. */
|
||||
sel.DecoderShortFormatMode = LongFormatDriverInterface;
|
||||
sel.StreamOutEnable = true;
|
||||
sel.PreDeblockingOutputEnable = true;
|
||||
}
|
||||
|
||||
anv_batch_emit(&cmd_buffer->batch, GENX(MFX_SURFACE_STATE), ss) {
|
||||
ss.Width = source_image->vk.extent.width - 1;
|
||||
ss.Height = height_in_mbs * 16 - 1;
|
||||
ss.SurfaceFormat = PLANAR_420_8;
|
||||
ss.InterleaveChroma = 1;
|
||||
ss.SurfacePitch = source_image->planes[0].primary_surface.isl.row_pitch_B - 1;
|
||||
ss.TiledSurface = source_image->planes[0].primary_surface.isl.tiling != ISL_TILING_LINEAR;
|
||||
ss.TileWalk = TW_YMAJOR;
|
||||
ss.YOffsetforUCb =
|
||||
MIN2((source_image->planes[1].primary_surface.memory_range.offset -
|
||||
source_image->planes[0].primary_surface.memory_range.offset) /
|
||||
source_image->planes[0].primary_surface.isl.row_pitch_B, 32767u);
|
||||
ss.YOffsetforVCr = 0;
|
||||
}
|
||||
|
||||
struct anv_address mv_object_address =
|
||||
(struct anv_address) { vid->vid_mem[ANV_VID_MEM_H264_ENC_INPUT_MV].mem->bo,
|
||||
vid->vid_mem[ANV_VID_MEM_H264_ENC_INPUT_MV].offset };
|
||||
anv_batch_emit(&cmd_buffer->batch, GENX(MFX_IND_OBJ_BASE_ADDR_STATE), index_obj) {
|
||||
index_obj.MFCIndirectPAKBSEObjectAddress = dst_address;
|
||||
/* Base for the per-MB indirect PAK-MV input data (inter/skip MBs). The
|
||||
* MFC sets up its MV fetch unit from this base even when a given MB reads
|
||||
* no MV data, so it must be a valid address rather than null. */
|
||||
index_obj.MFXIndirectMVObjectAddress = mv_object_address;
|
||||
#if GFX_VERx10 == 70
|
||||
/* AccessUpperBound and CacheabilityControl exist under these names only
|
||||
* on Gen7; Gen7.5 has only the MOCS field and Gen8 renames the bound.
|
||||
* Encode is Gen7-only, so gate the extra fields accordingly. */
|
||||
index_obj.MFCIndirectPAKBSEObjectAccessUpperBound =
|
||||
anv_address_add(dst_address, dst_page_offset + dst_buffer_range);
|
||||
index_obj.MFCIndirectPAKBSEObjectCacheabilityControl = 2;
|
||||
index_obj.MFXIndirectMVObjectCacheabilityControl = 2;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Zero the first 32 bytes of the indirect MV buffer so every P macroblock,
|
||||
* which reads its 8MV (32-byte) block from offset 0, sees a zero motion
|
||||
* vector (co-located prediction). The MFC executes these serialising writes
|
||||
* before it fetches the MV data for the PAK objects that follow. */
|
||||
if (is_p) {
|
||||
for (unsigned i = 0; i < 32; i += 8)
|
||||
genX(emit_query_mi_write_u64)(cmd_buffer,
|
||||
anv_address_add(mv_object_address, i), 0);
|
||||
}
|
||||
|
||||
/* Scratch buffers are bound at video-session level via
|
||||
* vkBindVideoSessionMemoryKHR (ANV_VID_MEM_H264_ENC_*). The reconstructed
|
||||
* picture (pre-deblocking destination) is the caller's setup DPB slot.
|
||||
*
|
||||
* This block (PIPE_BUF_ADDR_STATE .. REF_IDX_STATE) is Gen7-only: field
|
||||
* names and array layouts differ across gens (e.g. DirectMVBufferAddress[]
|
||||
* vs the per-index fields on Gen7.5, and MFX_FQM_STATE not existing at all
|
||||
* on Gen7.5/Gen8). */
|
||||
#if GFX_VERx10 == 70
|
||||
anv_batch_emit(&cmd_buffer->batch, GENX(MFX_PIPE_BUF_ADDR_STATE), buf) {
|
||||
buf.PreDeblockingDestinationAddress = recon_address;
|
||||
buf.OriginalUncompressedPictureSourceCacheabilityControl = 2;
|
||||
buf.StreamOutDataDestinationCacheabilityControl = 2;
|
||||
buf.IntraRowStoreScratchBufferCacheabilityControl = 2;
|
||||
buf.DeblockingFilterRowStoreScratchBufferCacheabilityControl = 2;
|
||||
buf.MBStatusBufferCacheabilityControl = 2;
|
||||
memset(buf.ReferencePictureCacheabilityControl, 0,
|
||||
sizeof(buf.ReferencePictureCacheabilityControl));
|
||||
buf.OriginalUncompressedPictureSourceAddress =
|
||||
anv_image_address(source_image,
|
||||
&source_image->planes[0].primary_surface.memory_range);
|
||||
buf.StreamOutDataDestinationAddress =
|
||||
(struct anv_address) { vid->vid_mem[ANV_VID_MEM_H264_ENC_MB_STATUS].mem->bo,
|
||||
vid->vid_mem[ANV_VID_MEM_H264_ENC_MB_STATUS].offset };
|
||||
buf.IntraRowStoreScratchBufferAddress =
|
||||
(struct anv_address) { vid->vid_mem[ANV_VID_MEM_H264_INTRA_ROW_STORE].mem->bo,
|
||||
vid->vid_mem[ANV_VID_MEM_H264_INTRA_ROW_STORE].offset };
|
||||
buf.DeblockingFilterRowStoreScratchBufferAddress =
|
||||
(struct anv_address) { vid->vid_mem[ANV_VID_MEM_H264_DEBLOCK_FILTER_ROW_STORE].mem->bo,
|
||||
vid->vid_mem[ANV_VID_MEM_H264_DEBLOCK_FILTER_ROW_STORE].offset };
|
||||
buf.MBStatusBufferAddress =
|
||||
(struct anv_address) { vid->vid_mem[ANV_VID_MEM_H264_ENC_MB_STATUS].mem->bo,
|
||||
vid->vid_mem[ANV_VID_MEM_H264_ENC_MB_STATUS].offset };
|
||||
/* PRM Vol2 Pt3, MFX_PIPE_BUF_ADDR_STATE, ReferencePictureAddress[0..15]:
|
||||
* "AVC: Always specifies all 16 addresses ... All invalid addresses must
|
||||
* be set to a valid address RefAddr[0] by the driver." Leaving these at
|
||||
* zero risks a fault even when references are unused (I-frame), so fill
|
||||
* 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;
|
||||
}
|
||||
|
||||
anv_batch_emit(&cmd_buffer->batch, GENX(MFX_BSP_BUF_BASE_ADDR_STATE), bsp) {
|
||||
/* Only the BSD/MPC row store is used by PAK; MPR row store and bitplane
|
||||
* read buffer are left null, matching gen6_mfc_bsp_buf_base_addr_state. */
|
||||
bsp.BSDMPCRowStoreScratchBufferAddress =
|
||||
(struct anv_address) { vid->vid_mem[ANV_VID_MEM_H264_BSD_MPC_ROW_SCRATCH].mem->bo,
|
||||
vid->vid_mem[ANV_VID_MEM_H264_BSD_MPC_ROW_SCRATCH].offset };
|
||||
bsp.BSDMPCRowStoreScratchBufferCacheabilityControl = 2;
|
||||
}
|
||||
#endif /* GFX_VERx10 == 70 */
|
||||
|
||||
anv_batch_emit(&cmd_buffer->batch, GENX(MFX_AVC_IMG_STATE), avc_img) {
|
||||
avc_img.FrameWidth = MIN2(width_in_mbs - 1, 255u);
|
||||
avc_img.FrameHeight = MIN2(height_in_mbs - 1, 254u);
|
||||
avc_img.FrameSize = width_in_mbs * height_in_mbs - 1;
|
||||
avc_img.ImageStructure = FramePicture;
|
||||
|
||||
avc_img.WeightedBiPredictionIDC = pps->weighted_bipred_idc;
|
||||
avc_img.WeightedPredictionEnable = pps->flags.weighted_pred_flag;
|
||||
avc_img.FirstChromaQPOffset = CLAMP(pps->chroma_qp_index_offset, -12, 12);
|
||||
avc_img.SecondChromaQPOffset = CLAMP(pps->second_chroma_qp_index_offset, -12, 12);
|
||||
avc_img.FieldPicture = false;
|
||||
avc_img.MBAFFMode = false;
|
||||
avc_img.FrameMBOnly = sps->flags.frame_mbs_only_flag;
|
||||
avc_img._8x8IDCTTransformMode = pps->flags.transform_8x8_mode_flag;
|
||||
avc_img.Direct8x8Inference = sps->flags.direct_8x8_inference_flag;
|
||||
avc_img.ConstrainedIntraPrediction = pps->flags.constrained_intra_pred_flag;
|
||||
avc_img.NonReferencePicture = false;
|
||||
avc_img.EntropyCodingSyncEnable = pps->flags.entropy_coding_mode_flag;
|
||||
avc_img.ChromaFormatIDC = sps->chroma_format_idc;
|
||||
avc_img.MVUnpackedEnable = true;
|
||||
avc_img.TrellisQuantizationChromaDisable = false;
|
||||
avc_img.IntraMBConformanceMaxSize = 0xEE8;
|
||||
avc_img.InterMBConformanceMaxSize = 0xBB8;
|
||||
avc_img.NumberofReferenceFrames = is_p ? 1 : 0;
|
||||
avc_img.NumberofActiveReferencePicturesfromL0 = is_p ? 1 : 0;
|
||||
avc_img.NumberofActiveReferencePicturesfromL1 = 0;
|
||||
avc_img.InitialQPValue = 0; /* per-slice QP comes from SLICE_STATE */
|
||||
avc_img.PicOrderPresent = pps->flags.bottom_field_pic_order_in_frame_present_flag;
|
||||
avc_img.DeltaPicOrderAlwaysZero = sps->flags.delta_pic_order_always_zero_flag;
|
||||
avc_img.PicOrderCountType = sps->pic_order_cnt_type;
|
||||
avc_img.DeblockingFilterControlPresent = pps->flags.deblocking_filter_control_present_flag;
|
||||
avc_img.RedundantPicCountPresent = pps->flags.redundant_pic_cnt_present_flag;
|
||||
avc_img.Log2MaxFrameNumber = MIN2((uint32_t)sps->log2_max_frame_num_minus4, 12u);
|
||||
avc_img.Log2MaxPicOrderCountLSB = MIN2((uint32_t)sps->log2_max_pic_order_cnt_lsb_minus4, 12u);
|
||||
avc_img.CurrentPictureFrameNumber = 0; /* single IDR frame */
|
||||
}
|
||||
|
||||
/* Quantizer matrices. With no custom scaling list in the PPS,
|
||||
* vk_video_derive_h264_scaling_list() returns the flat default (all 16). */
|
||||
StdVideoH264ScalingLists scaling_lists;
|
||||
vk_video_derive_h264_scaling_list(sps, pps, &scaling_lists);
|
||||
anv_batch_emit(&cmd_buffer->batch, GENX(MFX_QM_STATE), qm) {
|
||||
qm.DWordLength = 16;
|
||||
qm.AVC = AVC_4x4_Intra_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[m][q];
|
||||
}
|
||||
if (pps->flags.transform_8x8_mode_flag) {
|
||||
anv_batch_emit(&cmd_buffer->batch, GENX(MFX_QM_STATE), qm) {
|
||||
qm.DWordLength = 16;
|
||||
qm.AVC = AVC_8x8_Intra_MATRIX;
|
||||
for (unsigned q = 0; q < 64; q++)
|
||||
qm.ForwardQuantizerMatrix[vl_zscan_normal[q]] =
|
||||
scaling_lists.ScalingList8x8[0][q];
|
||||
}
|
||||
}
|
||||
|
||||
/* MFX_FQM_STATE (forward quantizer matrix) does not exist on Gen7.5/Gen8;
|
||||
* the block below is Gen7-only. For a flat scaling list the VA-API encoder
|
||||
* (gen7_mfc_avc_fqm_state) programs the constant 0x10001000; a custom PPS
|
||||
* scaling list would require the proper reciprocal and is not handled. */
|
||||
#if GFX_VERx10 == 70
|
||||
anv_batch_emit(&cmd_buffer->batch, GENX(MFX_FQM_STATE), fqm) {
|
||||
fqm.DWordLength = 32;
|
||||
fqm.AVC = AVC_4x4_Intra_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;
|
||||
fqm.AVC = AVC_8x8_Intra_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
|
||||
* current-picture write targets (top/bottom) are set. The POC list is
|
||||
* filled as gen6_mfc_avc_directmode_state does (POCList[i] = i/2). */
|
||||
anv_batch_emit(&cmd_buffer->batch, GENX(MFX_AVC_DIRECTMODE_STATE), dmv) {
|
||||
memset(dmv.DirectMVBufferAddress, 0, sizeof(dmv.DirectMVBufferAddress));
|
||||
dmv.DirectMVBufferWriteAddress[0] =
|
||||
(struct anv_address) { vid->vid_mem[ANV_VID_MEM_H264_ENC_DIRECT_MV_TOP].mem->bo,
|
||||
vid->vid_mem[ANV_VID_MEM_H264_ENC_DIRECT_MV_TOP].offset };
|
||||
dmv.DirectMVBufferWriteAddress[1] =
|
||||
(struct anv_address) { vid->vid_mem[ANV_VID_MEM_H264_ENC_DIRECT_MV_BOTTOM].mem->bo,
|
||||
vid->vid_mem[ANV_VID_MEM_H264_ENC_DIRECT_MV_BOTTOM].offset };
|
||||
for (unsigned i = 0; i < 32; i++)
|
||||
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). */
|
||||
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;
|
||||
}
|
||||
}
|
||||
#endif /* GFX_VERx10 == 70 */
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
genX(hasvk_mfc_emit_slice)(struct anv_cmd_buffer *cmd_buffer,
|
||||
const StdVideoH264SequenceParameterSet *sps,
|
||||
const StdVideoH264PictureParameterSet *pps,
|
||||
const StdVideoEncodeH264PictureInfo *pic_info,
|
||||
const StdVideoEncodeH264SliceHeader *slice_hdr,
|
||||
uint32_t slice_start_mb, uint32_t slice_end_mb,
|
||||
uint32_t width_in_mbs, uint32_t height_in_mbs,
|
||||
uint32_t qp, bool is_last_slice,
|
||||
uint32_t dst_page_offset)
|
||||
{
|
||||
/* A P slice references the previous reconstructed frame; an I slice does
|
||||
* not. B slices are out of scope. */
|
||||
bool is_p = (slice_hdr->slice_type == STD_VIDEO_H264_SLICE_TYPE_P);
|
||||
|
||||
/* MFX_AVC_SLICE_STATE is not used by decode; fields are matched against the
|
||||
* VA-API reference encoder (gen6_mfc_avc_slice_state). The slice covers the
|
||||
* macroblock range [slice_start_mb, slice_end_mb) in raster order. */
|
||||
anv_batch_emit(&cmd_buffer->batch, GENX(MFX_AVC_SLICE_STATE), slice) {
|
||||
slice.SliceType = is_p ? PSlice : ISlice; /* P=0,B=1,I=2 */
|
||||
slice.NumberofReferencePicturesinInterpredictionList0 = is_p ? 1 : 0;
|
||||
slice.SliceQuantizationParameter = qp;
|
||||
slice.CABACInitIDC = is_p ? slice_hdr->cabac_init_idc : 0;
|
||||
slice.DisableDeblockingFilterIndicator = 1;
|
||||
slice.SliceAlphaC0OffsetDiv2 = 0;
|
||||
slice.SliceBetaOffsetDiv2 = 0;
|
||||
|
||||
slice.SliceStartMBNumber = slice_start_mb;
|
||||
slice.SliceHorizontalPosition = slice_start_mb % width_in_mbs;
|
||||
slice.SliceVerticalPosition = slice_start_mb / width_in_mbs;
|
||||
slice.NextSliceHorizontalPosition = slice_end_mb % width_in_mbs;
|
||||
slice.NextSliceVerticalPosition = slice_end_mb / width_in_mbs;
|
||||
|
||||
slice.LastSliceGroup = is_last_slice;
|
||||
slice.HeaderInsertionPresent = true;
|
||||
slice.SliceDataInsertionPresent = true;
|
||||
slice.TailInsertionPresent = true;
|
||||
slice.EmulationByteSliceInsertEnable = true;
|
||||
slice.CABACZeroWordInsertionEnable = false;
|
||||
|
||||
/* Without these the inserted slice header/tail are not emitted into the
|
||||
* bitstream (gen6_mfc_avc_slice_state). */
|
||||
slice.ResetRateControlCounter = true;
|
||||
slice.RateControlStableTolerance = 4;
|
||||
|
||||
/* All slices point at the frame's bitstream start; the MFC advances the
|
||||
* write pointer internally between slices because LoadBitstreamPointerPer-
|
||||
* Slice is left 0 in MFX_AVC_IMG_STATE (pointer loaded once per frame).
|
||||
* The start is a byte offset from the 4KiB-aligned PAK-BSE object base in
|
||||
* MFX_IND_OBJ_BASE_ADDR_STATE, so it carries the sub-page part of the
|
||||
* destination buffer offset that the page-granular base cannot encode. */
|
||||
slice.IndirectPAKBSEDataStartAddress = dst_page_offset;
|
||||
}
|
||||
|
||||
/* Slice header RBSP. SkipEmulationByteCount = 5 covers the 4-byte start
|
||||
* code plus the 1-byte NAL header. */
|
||||
uint8_t slice_header_buf[64];
|
||||
uint32_t slice_header_bits =
|
||||
anv_h264_build_encode_slice_header(slice_header_buf, sizeof(slice_header_buf),
|
||||
sps, pps, pic_info, slice_hdr);
|
||||
if (slice_header_bits == 0)
|
||||
return;
|
||||
uint32_t slice_header_dws = DIV_ROUND_UP(slice_header_bits, 32);
|
||||
/* PRM Vol2 Pt3, MFX_PAK_INSERT_OBJECT, DataBitsInLastDW: valid range is
|
||||
* [1,32]. A naive bits % 32 yields 0 when bits is a multiple of 32, which
|
||||
* is illegal; map that to 32. */
|
||||
uint32_t slice_header_bits_in_last_dw = slice_header_bits & 0x1f;
|
||||
if (slice_header_bits_in_last_dw == 0)
|
||||
slice_header_bits_in_last_dw = 32;
|
||||
|
||||
uint32_t *header_dst =
|
||||
anv_batch_emitn(&cmd_buffer->batch, 2 + slice_header_dws,
|
||||
GENX(MFX_PAK_INSERT_OBJECT),
|
||||
.EmulationByteBitsInsertEnable = true,
|
||||
.SkipEmulationByteCount = 5,
|
||||
.DataBitsInLastDW = slice_header_bits_in_last_dw,
|
||||
.LastHeader = true,
|
||||
.EndofSlice = false);
|
||||
if (header_dst)
|
||||
memcpy(header_dst + 2, slice_header_buf, slice_header_dws * 4);
|
||||
|
||||
/* One MFC_AVC_PAK_OBJECT per macroblock. Field layout per PRM Vol2 Pt3
|
||||
* §2.3.1.1 "PAK Object Inline Data Description". An I slice codes every MB as
|
||||
* I_16x16 DC; a P slice codes every MB as P_Skip (see below).
|
||||
*
|
||||
* InlineData[0] (DW3): I-frame path:
|
||||
* bit 13 IntraMbFlag = 1.
|
||||
* bits 12:8 MbType5Bits = 23: the raw AVC mb_type (PRM: "unified
|
||||
* encoding ... according to AVC Spec", not mb_type-1). Per
|
||||
* Table 7-11, I_16x16 mb_type = 1 + PredMode + 4*CBPChroma +
|
||||
* 12*CBPLuma, so 23 = I_16x16_2_2_1 (PredMode = DC).
|
||||
* bits 19:17 CbpDcY/U/V = 1: DC sub-blocks coded.
|
||||
* InlineData[4] (DW7) bits 1:0: LumaIntraMode[0] = DC (only [1:0] valid for
|
||||
* Intra16x16).
|
||||
* 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). */
|
||||
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;
|
||||
bool last_mb = (mb == slice_end_mb - 1);
|
||||
|
||||
anv_batch_emit(&cmd_buffer->batch, GENX(MFC_AVC_PAK_OBJECT), pak) {
|
||||
pak.IndirectPAKMVDataLength = 0;
|
||||
pak.IndirectPAKMVDataStartAddressOffset = 0;
|
||||
pak.InlineData[1] = (y << 8) | x; /* Cbp4x4Y=0, MbY/XCnt */
|
||||
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. */
|
||||
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 */
|
||||
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 */
|
||||
pak.InlineData[7] = 0;
|
||||
} else {
|
||||
pak.InlineData[0] = 0x000e0000 /* CbpDcY/U/V = 1 */
|
||||
| (1u << 13) /* IntraMbFlag */
|
||||
| (0x17u << 8); /* MbType5Bits = 23: I_16x16 DC */
|
||||
pak.InlineData[1] = 0xFFFF0000 | (y << 8) | x;
|
||||
pak.InlineData[2] = 0x000F000F;
|
||||
pak.InlineData[4] = 2; /* LumaIntraMode[0] = DC */
|
||||
pak.InlineData[5] = 0;
|
||||
/* IntraPredAvailFlags must reflect real neighbor availability so PAK
|
||||
* DC prediction matches the decoder; intra prediction does not cross
|
||||
* slice boundaries, so a neighbour counts only when its MB address is
|
||||
* >= slice_start_mb as well as inside the picture.
|
||||
* bit7 F, bit6 A, bit5 E: left | bit4 B: top | bit3 C: top-right |
|
||||
* bit2 D: top-left. Bits [1:0] hold ChromaIntraPredMode (DC = 0). */
|
||||
uint32_t intra_avail = 0;
|
||||
if (x > 0 && mb - 1 >= slice_start_mb)
|
||||
intra_avail |= (1u << 7) | (1u << 6) | (1u << 5);
|
||||
if (y > 0 && mb - width_in_mbs >= slice_start_mb)
|
||||
intra_avail |= (1u << 4);
|
||||
if (y > 0 && x < width_in_mbs - 1 && mb - width_in_mbs + 1 >= slice_start_mb)
|
||||
intra_avail |= (1u << 3);
|
||||
if (x > 0 && y > 0 && mb - width_in_mbs - 1 >= slice_start_mb)
|
||||
intra_avail |= (1u << 2);
|
||||
pak.InlineData[6] = intra_avail;
|
||||
pak.InlineData[7] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Slice tail: two zero bytes, marking end of slice (gen6_mfc_avc). */
|
||||
uint32_t tail_data[2] = { 0, 0 };
|
||||
uint32_t *tail_dst =
|
||||
anv_batch_emitn(&cmd_buffer->batch, 2 + 2, GENX(MFX_PAK_INSERT_OBJECT),
|
||||
.EmulationByteBitsInsertEnable = false,
|
||||
.SkipEmulationByteCount = 2,
|
||||
.DataBitsInLastDW = 8,
|
||||
.LastHeader = true,
|
||||
.EndofSlice = true);
|
||||
if (tail_dst)
|
||||
memcpy(tail_dst + 2, tail_data, sizeof(tail_data));
|
||||
}
|
||||
Reference in new issue
Block a user