From 64b9be2dc51d49d05ad46edec85d68034dd120e2 Mon Sep 17 00:00:00 2001 From: stevxiao Date: Wed, 19 Nov 2025 13:14:17 -0500 Subject: [PATCH] avcodec/d3d12va_encode: support motion estimation precision mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By default, the D3D12 video encoder uses MAXIMUM, which means no restriction—it uses the highest precision supported by the driver. Applications may want to reduce precision to improve speed or reduce power consumption. This requires the encoder to support user-defined motion estimation precision modes. D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE defines several precision modes: maximum: No restriction, uses the maximum precision supported by the driver. full_pixel: Allows only full-pixel precision. half_pixel: Allows half-pixel precision. quarter-pixel: Allows quarter-pixel precision. eighth-pixel: Allows eighth-pixel precision (introduced in Windows 11). Sample Command Line: ffmpeg -hwaccel d3d12va -hwaccel_output_format d3d12 -extra_hw_frames 20 -i input.mp4 -an -c:v h264_d3d12va -me_precision half_pixel out.mp4 --- configure | 2 + libavcodec/d3d12va_encode.c | 91 ++++++++++++++++++++++++++++++++++++- libavcodec/d3d12va_encode.h | 38 +++++++++++++++- 3 files changed, 129 insertions(+), 2 deletions(-) diff --git a/configure b/configure index 4791a67562..301a3e5e3e 100755 --- a/configure +++ b/configure @@ -2624,6 +2624,7 @@ CONFIG_EXTRA=" celp_math d3d12_intra_refresh d3d12va_encode + d3d12va_me_precision_eighth_pixel deflate_wrapper dirac_parse dnn @@ -6979,6 +6980,7 @@ test_code cc "windows.h d3d12video.h" "D3D12_FEATURE_VIDEO feature = D3D12_FEATU test_code cc "windows.h d3d12video.h" "D3D12_FEATURE_DATA_VIDEO_ENCODER_RESOURCE_REQUIREMENTS req" && enable d3d12_encoder_feature test_code cc "windows.h d3d12video.h" "D3D12_VIDEO_ENCODER_CODEC c = D3D12_VIDEO_ENCODER_CODEC_AV1; (void)c;" && enable d3d12va_av1_headers test_code cc "windows.h d3d12video.h" "D3D12_FEATURE_DATA_VIDEO_ENCODER_INTRA_REFRESH_MODE check = { 0 };" && enable d3d12_intra_refresh +test_code cc "windows.h d3d12video.h" "D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE m = D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_EIGHTH_PIXEL; (void)m;" && enable d3d12va_me_precision_eighth_pixel check_type "windows.h" "DPI_AWARENESS_CONTEXT" -D_WIN32_WINNT=0x0A00 check_type "windows.h security.h schnlsp.h" SecPkgContext_KeyingMaterialInfo -DSECURITY_WIN32 check_type "d3d9.h dxva2api.h" DXVA2_ConfigPictureDecode -D_WIN32_WINNT=0x0602 diff --git a/libavcodec/d3d12va_encode.c b/libavcodec/d3d12va_encode.c index f15df37b96..de95518be5 100644 --- a/libavcodec/d3d12va_encode.c +++ b/libavcodec/d3d12va_encode.c @@ -1143,6 +1143,91 @@ rc_mode_found: return 0; } +static int d3d12va_encode_init_motion_estimation_precision(AVCodecContext *avctx) +{ + FFHWBaseEncodeContext *base_ctx = avctx->priv_data; + D3D12VAEncodeContext *ctx = avctx->priv_data; + AVD3D12VAFramesContext *hwctx = base_ctx->input_frames->hwctx; + HRESULT hr; + + if (ctx->me_precision == D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_MAXIMUM) + return 0; + + D3D12_VIDEO_ENCODER_PROFILE_DESC profile = { 0 }; + D3D12_VIDEO_ENCODER_PROFILE_H264 h264_profile = D3D12_VIDEO_ENCODER_PROFILE_H264_MAIN; + D3D12_VIDEO_ENCODER_PROFILE_HEVC hevc_profile = D3D12_VIDEO_ENCODER_PROFILE_HEVC_MAIN; +#if CONFIG_AV1_D3D12VA_ENCODER + D3D12_VIDEO_ENCODER_AV1_PROFILE av1_profile = D3D12_VIDEO_ENCODER_AV1_PROFILE_MAIN; +#endif + + D3D12_VIDEO_ENCODER_LEVEL_SETTING level = { 0 }; + D3D12_VIDEO_ENCODER_LEVELS_H264 h264_level = { 0 }; + D3D12_VIDEO_ENCODER_LEVEL_TIER_CONSTRAINTS_HEVC hevc_level = { 0 }; +#if CONFIG_AV1_D3D12VA_ENCODER + D3D12_VIDEO_ENCODER_AV1_LEVEL_TIER_CONSTRAINTS av1_level = { 0 }; +#endif + + switch (ctx->codec->d3d12_codec) { + case D3D12_VIDEO_ENCODER_CODEC_H264: + profile.DataSize = sizeof(D3D12_VIDEO_ENCODER_PROFILE_H264); + profile.pH264Profile = &h264_profile; + level.DataSize = sizeof(D3D12_VIDEO_ENCODER_LEVELS_H264); + level.pH264LevelSetting = &h264_level; + break; + case D3D12_VIDEO_ENCODER_CODEC_HEVC: + profile.DataSize = sizeof(D3D12_VIDEO_ENCODER_PROFILE_HEVC); + profile.pHEVCProfile = &hevc_profile; + level.DataSize = sizeof(D3D12_VIDEO_ENCODER_LEVEL_TIER_CONSTRAINTS_HEVC); + level.pHEVCLevelSetting = &hevc_level; + break; +#if CONFIG_AV1_D3D12VA_ENCODER + case D3D12_VIDEO_ENCODER_CODEC_AV1: + profile.DataSize = sizeof(D3D12_VIDEO_ENCODER_AV1_PROFILE); + profile.pAV1Profile = &av1_profile; + level.DataSize = sizeof(D3D12_VIDEO_ENCODER_AV1_LEVEL_TIER_CONSTRAINTS); + level.pAV1LevelSetting = &av1_level; + break; +#endif + default: + av_assert0(0); + } + + D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT support = { + .NodeIndex = 0, + .Codec = ctx->codec->d3d12_codec, + .InputFormat = hwctx->format, + .RateControl = ctx->rc, + .IntraRefresh = D3D12_VIDEO_ENCODER_INTRA_REFRESH_MODE_NONE, + .SubregionFrameEncoding = D3D12_VIDEO_ENCODER_FRAME_SUBREGION_LAYOUT_MODE_FULL_FRAME, + .ResolutionsListCount = 1, + .pResolutionList = &ctx->resolution, + .CodecGopSequence = ctx->gop, + .MaxReferenceFramesInDPB = MAX_DPB_SIZE - 1, + .CodecConfiguration = ctx->codec_conf, + .SuggestedProfile = profile, + .SuggestedLevel = level, + .pResolutionDependentSupport = &ctx->res_limits, + }; + + hr = ID3D12VideoDevice3_CheckFeatureSupport(ctx->video_device3, D3D12_FEATURE_VIDEO_ENCODER_SUPPORT, + &support, sizeof(support)); + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "Failed to check encoder support for motion estimation.\n"); + return AVERROR(EINVAL); + } + + if (!(support.SupportFlags & D3D12_VIDEO_ENCODER_SUPPORT_FLAG_MOTION_ESTIMATION_PRECISION_MODE_LIMIT_AVAILABLE)) { + av_log(avctx, AV_LOG_ERROR, "Hardware does not support motion estimation " + "precision mode limits.\n"); + return AVERROR(ENOTSUP); + } + + av_log(avctx, AV_LOG_VERBOSE, "Hardware supports motion estimation " + "precision mode limits.\n"); + + return 0; +} + static int d3d12va_encode_init_gop_structure(AVCodecContext *avctx) { FFHWBaseEncodeContext *base_ctx = avctx->priv_data; @@ -1322,7 +1407,7 @@ static int d3d12va_create_encoder(AVCodecContext *avctx) .EncodeProfile = ctx->profile->d3d12_profile, .InputFormat = frames_hwctx->format, .CodecConfiguration = ctx->codec_conf, - .MaxMotionEstimationPrecision = D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_MAXIMUM, + .MaxMotionEstimationPrecision = ctx->me_precision, }; hr = ID3D12VideoDevice3_CreateVideoEncoder(ctx->video_device3, &desc, &IID_ID3D12VideoEncoder, @@ -1681,6 +1766,10 @@ int ff_d3d12va_encode_init(AVCodecContext *avctx) goto fail; } + err = d3d12va_encode_init_motion_estimation_precision(avctx); + if (err < 0) + goto fail; + if (ctx->codec->init_sequence_params) { err = ctx->codec->init_sequence_params(avctx); if (err < 0) { diff --git a/libavcodec/d3d12va_encode.h b/libavcodec/d3d12va_encode.h index 699d8d2077..fcb97210b3 100644 --- a/libavcodec/d3d12va_encode.h +++ b/libavcodec/d3d12va_encode.h @@ -277,6 +277,11 @@ typedef struct D3D12VAEncodeContext { */ UINT intra_refresh_frame_index; + /** + * Motion estimation precision mode + */ + D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE me_precision; + } D3D12VAEncodeContext; typedef struct D3D12VAEncodeType { @@ -362,6 +367,24 @@ int ff_d3d12va_encode_close(AVCodecContext *avctx); { #name, desc, 0, AV_OPT_TYPE_CONST, { .i64 = D3D12_VIDEO_ENCODER_INTRA_REFRESH_MODE_ ## mode }, \ 0, 0, FLAGS, .unit = "intra_refresh_mode" } +#if CONFIG_D3D12VA_ME_PRECISION_EIGHTH_PIXEL +#define D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_MAX_VALUE D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_EIGHTH_PIXEL +#else +#define D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_MAX_VALUE D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_QUARTER_PIXEL +#endif + +#define D3D12VA_ENCODE_ME_PRECISION_MODE(name, mode, desc) \ + { #name, #desc " pixel precision", 0, AV_OPT_TYPE_CONST, \ + { .i64 = D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_ ## mode }, \ + 0, 0, FLAGS, .unit = "me_precision" } + +#if CONFIG_D3D12VA_ME_PRECISION_EIGHTH_PIXEL +#define FFPP_D3D12VA_ME_PRECISION_EIGHTH_PIXEL \ + , D3D12VA_ENCODE_ME_PRECISION_MODE(eighth_pixel, EIGHTH_PIXEL, Eighth) +#else +#define FFPP_D3D12VA_ME_PRECISION_EIGHTH_PIXEL +#endif + #define D3D12VA_ENCODE_COMMON_OPTIONS \ { "max_frame_size", \ "Maximum frame size (in bytes)",\ @@ -378,7 +401,20 @@ int ff_d3d12va_encode_close(AVCodecContext *avctx); { "intra_refresh_duration", \ "Number of frames over which to spread intra refresh (0 = GOP size)", \ OFFSET(common.intra_refresh.IntraRefreshDuration), AV_OPT_TYPE_INT, \ - { .i64 = 0 }, 0, INT_MAX, FLAGS } + { .i64 = 0 }, 0, INT_MAX, FLAGS }, \ + { "me_precision", "Motion estimation precision mode", \ + OFFSET(common.me_precision), AV_OPT_TYPE_INT, \ + { .i64 = D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_MAXIMUM }, \ + D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_MAXIMUM, \ + D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_MAX_VALUE, \ + FLAGS, .unit = "me_precision" }, \ + { "maximum", "Maximum (best quality, slowest)", 0, AV_OPT_TYPE_CONST, \ + { .i64 = D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_MAXIMUM }, \ + 0, 0, FLAGS, .unit = "me_precision" }, \ + D3D12VA_ENCODE_ME_PRECISION_MODE(full_pixel, FULL_PIXEL, Full), \ + D3D12VA_ENCODE_ME_PRECISION_MODE(half_pixel, HALF_PIXEL, Half), \ + D3D12VA_ENCODE_ME_PRECISION_MODE(quarter_pixel, QUARTER_PIXEL, Quarter) \ + FFPP_D3D12VA_ME_PRECISION_EIGHTH_PIXEL #define D3D12VA_ENCODE_RC_MODE(name, desc) \ { #name, desc, 0, AV_OPT_TYPE_CONST, { .i64 = RC_MODE_ ## name }, \