diff --git a/meson.build b/meson.build index 598e830725..10cfa54a90 100644 --- a/meson.build +++ b/meson.build @@ -965,14 +965,14 @@ if libplacebo.found() sources += files('video/out/placebo/ra_pl.c', 'video/out/placebo/utils.c') pl_api_ver = libplacebo.version().split('.')[1] - if pl_api_ver.version_compare('>=190') + if pl_api_ver.version_compare('>=197') features += 'libplacebo-next' libplacebo_next = true - message('libplacebo v4.190+ found! Enabling vo_gpu_next.') + message('libplacebo v4.197+ found! Enabling vo_gpu_next.') sources += files('video/out/vo_gpu_next.c', 'video/out/gpu_next/context.c') else - message('libplacebo v4.190+ not found! Disabling vo_gpu_next.') + message('libplacebo v4.197+ not found! Disabling vo_gpu_next.') endif endif diff --git a/player/command.c b/player/command.c index 4f2eb4741d..2b4650e062 100644 --- a/player/command.c +++ b/player/command.c @@ -6619,6 +6619,8 @@ void mp_option_change_callback(void *ctx, struct m_config_option *co, int flags, } } osd_changed(mpctx->osd); + if (mpctx->video_out) + vo_control_async(mpctx->video_out, VOCTRL_OSD_CHANGED, NULL); if (flags & (UPDATE_SUB_FILT | UPDATE_SUB_HARD)) mp_force_video_refresh(mpctx); mp_wakeup_core(mpctx); diff --git a/video/out/vo.c b/video/out/vo.c index 253cb6274a..11ef596227 100644 --- a/video/out/vo.c +++ b/video/out/vo.c @@ -678,6 +678,7 @@ void vo_control_async(struct vo *vo, int request, void *data) break; case VOCTRL_KILL_SCREENSAVER: case VOCTRL_RESTORE_SCREENSAVER: + case VOCTRL_OSD_CHANGED: break; default: abort(); // requires explicit support diff --git a/video/out/vo.h b/video/out/vo.h index a8e8b9ddaf..63d6c7caa7 100644 --- a/video/out/vo.h +++ b/video/out/vo.h @@ -72,6 +72,9 @@ enum mp_voctrl { // you could install your own listener. VOCTRL_VO_OPTS_CHANGED, + // Triggered by any change to the OSD (e.g. OSD style changes) + VOCTRL_OSD_CHANGED, + /* private to vo_gpu */ VOCTRL_LOAD_HWDEC_API, diff --git a/video/out/vo_gpu_next.c b/video/out/vo_gpu_next.c index da613f498b..67e44e90cd 100644 --- a/video/out/vo_gpu_next.c +++ b/video/out/vo_gpu_next.c @@ -90,6 +90,7 @@ struct priv { struct osd_state osd_state; uint64_t last_id; + uint64_t osd_sync; double last_pts; bool is_interpolated; bool want_reset; @@ -208,9 +209,9 @@ static struct mp_image *get_image(struct vo *vo, int imgfmt, int w, int h, return mpi; } -static void write_overlays(struct vo *vo, struct mp_osd_res res, double pts, - int flags, struct osd_state *state, - struct pl_frame *frame, bool flip) +static void update_overlays(struct vo *vo, struct mp_osd_res res, double pts, + int flags, struct osd_state *state, + struct pl_frame *frame, bool flip) { struct priv *p = vo->priv; static const bool subfmt_all[SUBBITMAP_COUNT] = { @@ -219,8 +220,8 @@ static void write_overlays(struct vo *vo, struct mp_osd_res res, double pts, }; struct sub_bitmap_list *subs = osd_render(vo->osd, res, pts, flags, subfmt_all); - frame->num_overlays = 0; frame->overlays = state->overlays; + frame->num_overlays = 0; for (int n = 0; n < subs->num_items; n++) { const struct sub_bitmaps *item = subs->items[n]; @@ -280,6 +281,7 @@ static void write_overlays(struct vo *vo, struct mp_osd_res res, double pts, .parts = entry->parts, .num_parts = entry->num_parts, .color = frame->color, + .coords = PL_OVERLAY_COORDS_DST_FRAME, }; switch (item->format) { @@ -300,6 +302,7 @@ static void write_overlays(struct vo *vo, struct mp_osd_res res, double pts, struct frame_priv { struct vo *vo; struct osd_state subs; + uint64_t osd_sync; }; static int plane_data_from_imgfmt(struct pl_plane_data out_data[4], @@ -553,14 +556,6 @@ static bool map_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *src // still images so it shouldn't matter. pl_icc_profile_compute_signature(&frame->profile); - // Generate subtitles for this frame - struct mp_osd_res vidres = { - .w = mpi->w, .h = mpi->h, - // compensate for anamorphic sources (render subtitles as normal) - .display_par = (float) par->p_h / par->p_w, - }; - write_overlays(vo, vidres, mpi->pts, OSD_DRAW_SUB_ONLY, &fp->subs, frame, false); - // Update LUT attached to this frame update_lut(p, &p->image_lut); frame->lut = p->image_lut.lut; @@ -768,7 +763,7 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame) struct pl_frame target; pl_frame_from_swapchain(&target, &swframe); apply_target_options(p, &target); - write_overlays(vo, p->osd_res, 0, OSD_DRAW_OSD_ONLY, &p->osd_state, &target, swframe.flipped); + update_overlays(vo, p->osd_res, 0, OSD_DRAW_OSD_ONLY, &p->osd_state, &target, swframe.flipped); apply_crop(&target, p->dst, swframe.fbo->params.w, swframe.fbo->params.h); if (swframe.flipped) MPSWAP(float, target.crop.y0, target.crop.y1); @@ -800,13 +795,22 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame) break; } - // Update source crop on all existing frames. We technically own the - // `pl_frame` struct so this is kosher. This could be avoided by - // instead flushing the queue on resizes, but doing it this way avoids - // unnecessarily re-uploading frames. + // Update source crop and overlays on all existing frames. We + // technically own the `pl_frame` struct so this is kosher. This could + // be partially avoided by instead flushing the queue on resizes, but + // doing it this way avoids unnecessarily re-uploading frames. for (int i = 0; i < mix.num_frames; i++) { - apply_crop((struct pl_frame *) mix.frames[i], p->src, - vo->params->w, vo->params->h); + struct pl_frame *image = (struct pl_frame *) mix.frames[i]; + struct mp_image *mpi = image->user_data; + struct frame_priv *fp = mpi->priv; + apply_crop(image, p->src, vo->params->w, vo->params->h); + + if (fp->osd_sync < p->osd_sync) { + // Only update the overlays if the state has changed + update_overlays(vo, p->osd_res, mpi->pts, OSD_DRAW_SUB_ONLY, + &fp->subs, image, false); + fp->osd_sync = p->osd_sync; + } } } @@ -872,9 +876,22 @@ static int query_format(struct vo *vo, int format) static void resize(struct vo *vo) { struct priv *p = vo->priv; - vo_get_src_dst_rects(vo, &p->src, &p->dst, &p->osd_res); + struct mp_rect src, dst; + struct mp_osd_res osd; + vo_get_src_dst_rects(vo, &src, &dst, &osd); gpu_ctx_resize(p->context, vo->dwidth, vo->dheight); vo->want_redraw = true; + + if (mp_rect_equals(&p->src, &src) && + mp_rect_equals(&p->dst, &dst) && + osd_res_equals(p->osd_res, osd)) + return; + + pl_renderer_flush_cache(p->rr); + p->osd_sync++; + p->osd_res = osd; + p->src = src; + p->dst = dst; } static int reconfig(struct vo *vo, struct mp_image_params *params) @@ -950,10 +967,6 @@ static void video_screenshot(struct vo *vo, struct voctrl_screenshot *args) // the resulting mix is the correct frame for this PTS struct pl_frame *image = (struct pl_frame *) mix.frames[0]; struct mp_image *mpi = image->user_data; - int orig_overlays = image->num_overlays; - if (!args->subs) - image->num_overlays = 0; - struct mp_rect src = p->src, dst = p->dst; struct mp_osd_res osd = p->osd_res; if (!args->scaled) { @@ -989,7 +1002,7 @@ static void video_screenshot(struct vo *vo, struct voctrl_screenshot *args) if (!fbo) { MP_ERR(vo, "Failed creating target FBO for screenshot!\n"); - goto done; + return; } struct pl_frame target = { @@ -1004,8 +1017,13 @@ static void video_screenshot(struct vo *vo, struct voctrl_screenshot *args) apply_target_options(p, &target); apply_crop(image, src, mpi->params.w, mpi->params.h); apply_crop(&target, dst, fbo->params.w, fbo->params.h); - if (args->osd) - write_overlays(vo, osd, 0, OSD_DRAW_OSD_ONLY, &p->osd_state, &target, false); + + int osd_flags = 0; + if (!args->subs) + osd_flags |= OSD_DRAW_OSD_ONLY; + if (!args->osd) + osd_flags |= OSD_DRAW_SUB_ONLY; + update_overlays(vo, osd, 0, osd_flags, &p->osd_state, &target, false); if (!pl_render_image_mix(p->rr, &mix, &target, &p->params)) { MP_ERR(vo, "Failed rendering frame!\n"); @@ -1028,7 +1046,6 @@ static void video_screenshot(struct vo *vo, struct voctrl_screenshot *args) // fall through done: pl_tex_destroy(gpu, &fbo); - image->num_overlays = orig_overlays; } static int control(struct vo *vo, uint32_t request, void *data) @@ -1037,15 +1054,19 @@ static int control(struct vo *vo, uint32_t request, void *data) switch (request) { case VOCTRL_SET_PANSCAN: - pl_renderer_flush_cache(p->rr); // invalidate source crop resize(vo); - // fall through + return VO_TRUE; case VOCTRL_SET_EQUALIZER: case VOCTRL_PAUSE: if (p->is_interpolated) vo->want_redraw = true; return VO_TRUE; + case VOCTRL_OSD_CHANGED: + pl_renderer_flush_cache(p->rr); + p->osd_sync++; + return VO_TRUE; + case VOCTRL_UPDATE_RENDER_OPTS: { m_config_cache_update(p->opts_cache); const struct gl_video_opts *opts = p->opts_cache->opts; @@ -1184,6 +1205,7 @@ static int preinit(struct vo *vo) p->queue = pl_queue_create(p->gpu); p->osd_fmt[SUBBITMAP_LIBASS] = pl_find_named_fmt(p->gpu, "r8"); p->osd_fmt[SUBBITMAP_BGRA] = pl_find_named_fmt(p->gpu, "bgra8"); + p->osd_sync = 1; char *cache_file = get_cache_file(p); if (cache_file) { diff --git a/wscript b/wscript index 58ed8e5f39..a3152aa3b0 100644 --- a/wscript +++ b/wscript @@ -741,9 +741,9 @@ video_output_features = [ 'func': check_pkg_config('libplacebo >= 4.157.0'), }, { 'name': 'libplacebo-next', - 'desc': 'libplacebo v4.190+, needed for vo_gpu_next', + 'desc': 'libplacebo v4.197+, needed for vo_gpu_next', 'deps': 'libplacebo', - 'func': check_preprocessor('libplacebo/config.h', 'PL_API_VER >= 190', + 'func': check_preprocessor('libplacebo/config.h', 'PL_API_VER >= 197', use='libplacebo'), }, { 'name': '--vulkan',