Skip to content

Commit 8183b9d

Browse files
committed
vd_lavc: derive hwdec surface pool size from VO requirements
Change --hwdec-extra-frames default to auto, which sizes fixed hw frame pools from the number of frames the VO declares to reference, instead of a static guess. Fixes pool exhaustion with e.g. --interpolation and a tscale filter with larger radius. Fixes: #18205
1 parent 519250a commit 8183b9d

3 files changed

Lines changed: 61 additions & 16 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
change `--hwdec-extra-frames` default to `auto`, sized from VO frame retention

DOCS/man/options.rst

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1569,20 +1569,27 @@ Video
15691569
Runtime changes to this are ignored (the current option value is used
15701570
whenever the renderer is created).
15711571

1572-
``--hwdec-extra-frames=<N>``
1573-
Number of GPU frames hardware decoding should preallocate (default: see
1574-
``--list-options`` output). If this is too low, frame allocation may fail
1575-
during decoding, and video frames might get dropped and/or corrupted.
1576-
Setting it too high simply wastes GPU memory and has no advantages.
1572+
``--hwdec-extra-frames=<auto|N>``
1573+
Number of extra GPU frames hardware decoding should preallocate, on top of
1574+
what the codec itself requires (default: ``auto``).
15771575

15781576
This value is used only for hardware decoding APIs which require
15791577
preallocating surfaces (known examples include ``d3d11va`` and ``vaapi``).
15801578
For other APIs, frames are allocated as needed. The details depend on the
15811579
libavcodec implementations of the hardware decoders.
15821580

1583-
The required number of surfaces depends on dynamic runtime situations. The
1584-
default is a fixed value that is thought to be sufficient for most uses. But
1585-
in certain situations, it may not be enough.
1581+
In ``auto`` mode, the number is derived from how many frames the player
1582+
and the VO may reference at the same time. For example, enabling
1583+
``--interpolation`` increases it in proportion to the ``--tscale`` filter
1584+
radius, and VO deinterlacing adds the temporal reference frames it needs.
1585+
The value is determined when the decoder is initialized; runtime option
1586+
changes that increase frame requirements do not resize the pool, so set a
1587+
fixed value if you intend to switch such options during playback.
1588+
1589+
Setting a fixed value overrides the automatic sizing in both directions.
1590+
If it is too low, frame allocation may fail during decoding, and video
1591+
frames might get dropped and/or corrupted. Setting it too high simply
1592+
wastes GPU memory and has no advantages.
15861593

15871594
``--hwdec-image-format=<name>``
15881595
Set the internal pixel format used by hardware decoding via ``--hwdec``
@@ -1822,6 +1829,10 @@ Video
18221829
inserted deinterlacing filters, and that this will make video look worse if
18231830
it's not actually interlaced.
18241831

1832+
Enabling video output deinterlacing at runtime may require setting
1833+
``--hwdec-extra-frames``, as the hardware decoder's surface pool is sized
1834+
at decoder initialization.
1835+
18251836
``--deinterlace-field-parity=<tff|bff|auto>``
18261837
Specify the field parity/order when deinterlacing (default: auto).
18271838
Each frame of an interlaced video is divided into two fields, which are
@@ -5984,6 +5995,10 @@ them.
59845995
being the smoothest/blurriest and ``oversample`` being the sharpest/least
59855996
smooth.
59865997

5998+
Switching to a filter with a larger radius at runtime may require setting
5999+
``--hwdec-extra-frames``, as the hardware decoder's surface pool is sized
6000+
at decoder initialization.
6001+
59876002
``--scale-param1=<value>``, ``--scale-param2=<value>``, ``--cscale-param1=<value>``, ``--cscale-param2=<value>``, ``--dscale-param1=<value>``, ``--dscale-param2=<value>``, ``--tscale-param1=<value>``, ``--tscale-param2=<value>``
59886003
Set filter parameters. By default, these are set to the special string
59896004
``default``, which maps to a scaler-specific default value. Ignored if the
@@ -6136,6 +6151,9 @@ them.
61366151
the video along the temporal axis. The filter used can be controlled using
61376152
the ``--tscale`` setting.
61386153

6154+
Enabling this at runtime may require setting ``--hwdec-extra-frames``, as
6155+
the hardware decoder's surface pool is sized at decoder initialization.
6156+
61396157
``--interpolation-threshold=<0..1,-1>``
61406158
Threshold below which frame ratio interpolation gets disabled (default:
61416159
``0.01``). This is calculated as ``abs(disphz/vfps - 1) < threshold``,

video/decode/vd_lavc.c

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,13 @@ static int hwdec_opt_help(struct mp_log *log, const m_option_t *opt,
6868
#define HWDEC_DELAY_QUEUE_COUNT 2
6969
#define HWDEC_WAIT_KEYFRAME_COUNT 96
7070

71+
// Fallback number of extra surfaces for fixed-size hw frame pools, if the
72+
// actual requirement cannot be determined.
73+
#define HWDEC_EXTRA_FRAMES 6
74+
75+
// Surfaces referenced outside of libavcodec on top of what the VO declares
76+
#define HWDEC_EXTRA_INFLIGHT_FRAMES 3
77+
7178
#define OPT_BASE_STRUCT struct vd_lavc_params
7279

7380
struct vd_lavc_params {
@@ -151,8 +158,8 @@ const struct m_sub_options hwdec_conf = {
151158
.flags = M_OPT_OPTIONAL_PARAM | M_OPT_ALLOW_NO | UPDATE_HWDEC},
152159
{"hwdec-codecs", OPT_STRING(hwdec_codecs),
153160
.flags = UPDATE_HWDEC},
154-
{"hwdec-extra-frames", OPT_INT(hwdec_extra_frames), M_RANGE(0, 256),
155-
.flags = UPDATE_VD},
161+
{"hwdec-extra-frames", OPT_CHOICE(hwdec_extra_frames, {"auto", -1}),
162+
M_RANGE(0, 256), .flags = UPDATE_VD},
156163
{"hwdec-image-format", OPT_IMAGEFORMAT(hwdec_image_format),
157164
.flags = UPDATE_VO},
158165
{"hwdec-software-fallback", OPT_CHOICE(software_fallback,
@@ -167,11 +174,7 @@ const struct m_sub_options hwdec_conf = {
167174
.software_fallback = 3,
168175
.hwdec_api = (char *[]){"no", NULL,},
169176
.hwdec_codecs = "h264,vc1,hevc,vp8,vp9,av1,prores,prores_raw,ffv1,dpx,apv",
170-
// Maximum number of surfaces the player wants to buffer. This number
171-
// might require adjustment depending on whatever the player does;
172-
// for example, if vo_gpu increases the number of reference surfaces for
173-
// interpolation, this value has to be increased too.
174-
.hwdec_extra_frames = 6,
177+
.hwdec_extra_frames = -1,
175178
.hwdec_threads = 4,
176179
},
177180
};
@@ -946,6 +949,24 @@ static void uninit_avctx(struct mp_filter *vd)
946949
ctx->use_hwdec = false;
947950
}
948951

952+
// Number of extra surfaces to allocate on top of what libavcodec computed
953+
// for the codec itself, i.e. how many frames the rest of the player may hold
954+
// on to at any given time.
955+
static int hwdec_get_extra_frames(vd_ffmpeg_ctx *ctx)
956+
{
957+
int extra = ctx->hwdec_opts->hwdec_extra_frames;
958+
if (extra >= 0)
959+
return extra;
960+
961+
extra = HWDEC_EXTRA_FRAMES;
962+
// With native hwdec, every frame queued towards or retained by the VO
963+
// pins a surface from the fixed pool. Copying wrappers release surfaces
964+
// right after download, the fallback is enough for them.
965+
if (!ctx->hwdec.copying && ctx->vo)
966+
extra = MPMAX(extra, vo_get_num_frame_refs(ctx->vo) + HWDEC_EXTRA_INFLIGHT_FRAMES);
967+
return extra;
968+
}
969+
949970
static int init_generic_hwaccel(struct AVCodecContext *avctx, enum AVPixelFormat hw_fmt)
950971
{
951972
struct mp_filter *vd = avctx->opaque;
@@ -975,7 +996,7 @@ static int init_generic_hwaccel(struct AVCodecContext *avctx, enum AVPixelFormat
975996
// 1 surface is already included by libavcodec. The field is 0 if the
976997
// hwaccel supports dynamic surface allocation.
977998
if (new_fctx->initial_pool_size)
978-
new_fctx->initial_pool_size += ctx->hwdec_opts->hwdec_extra_frames - 1;
999+
new_fctx->initial_pool_size += hwdec_get_extra_frames(ctx) - 1;
9791000

9801001
const struct hwcontext_fns *fns =
9811002
hwdec_get_hwcontext_fns(new_fctx->device_ctx->type);
@@ -1001,6 +1022,11 @@ static int init_generic_hwaccel(struct AVCodecContext *avctx, enum AVPixelFormat
10011022
goto error;
10021023
}
10031024

1025+
if (new_fctx->initial_pool_size) {
1026+
MP_VERBOSE(ctx, "Allocated a fixed pool of %d hw surfaces.\n",
1027+
new_fctx->initial_pool_size);
1028+
}
1029+
10041030
ctx->cached_hw_frames_ctx = new_frames_ctx;
10051031
new_frames_ctx = NULL;
10061032
}

0 commit comments

Comments
 (0)