summaryrefslogtreecommitdiffstats
path: root/video/out/vo_gpu_next.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:13:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:13:14 +0000
commit60e8a3d404f0640fa5a3f834eae54b4f1fb9127d (patch)
tree1da89a218d0ecf010c67a87cb2f625c4cb18e7d7 /video/out/vo_gpu_next.c
parentAdding upstream version 0.37.0. (diff)
downloadmpv-upstream.tar.xz
mpv-upstream.zip
Adding upstream version 0.38.0.upstream/0.38.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--video/out/vo_gpu_next.c511
1 files changed, 345 insertions, 166 deletions
diff --git a/video/out/vo_gpu_next.c b/video/out/vo_gpu_next.c
index 1dc1b18..0a93f63 100644
--- a/video/out/vo_gpu_next.c
+++ b/video/out/vo_gpu_next.c
@@ -17,6 +17,9 @@
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <dirent.h>
+#include <sys/stat.h>
+#include <time.h>
#include <unistd.h>
#include <libplacebo/colorspace.h>
@@ -29,7 +32,9 @@
#include "config.h"
#include "common/common.h"
+#include "misc/io_utils.h"
#include "options/m_config.h"
+#include "options/options.h"
#include "options/path.h"
#include "osdep/io.h"
#include "osdep/threads.h"
@@ -90,9 +95,12 @@ struct frame_info {
};
struct cache {
- char *path;
+ struct mp_log *log;
+ struct mpv_global *global;
+ char *dir;
+ const char *name;
+ size_t size_limit;
pl_cache cache;
- uint64_t sig;
};
struct priv {
@@ -131,21 +139,18 @@ struct priv {
pl_options pars;
struct m_config_cache *opts_cache;
+ struct m_config_cache *next_opts_cache;
+ struct gl_next_opts *next_opts;
struct cache shader_cache, icc_cache;
struct mp_csp_equalizer_state *video_eq;
struct scaler_params scalers[SCALER_COUNT];
const struct pl_hook **hooks; // storage for `params.hooks`
- enum mp_csp_levels output_levels;
- char **raw_opts;
+ enum pl_color_levels output_levels;
struct pl_icc_params icc_params;
char *icc_path;
pl_icc_object icc_profile;
- struct user_lut image_lut;
- struct user_lut target_lut;
- struct user_lut lut;
-
// Cached shaders, preserved across options updates
struct user_hook *user_hooks;
int num_user_hooks;
@@ -154,15 +159,59 @@ struct priv {
struct frame_info perf_fresh;
struct frame_info perf_redraw;
+ struct mp_image_params target_params;
+};
+
+static void update_render_options(struct vo *vo);
+static void update_lut(struct priv *p, struct user_lut *lut);
+
+struct gl_next_opts {
bool delayed_peak;
+ int border_background;
+ float corner_rounding;
bool inter_preserve;
+ struct user_lut lut;
+ struct user_lut image_lut;
+ struct user_lut target_lut;
bool target_hint;
+ char **raw_opts;
+};
- float corner_rounding;
+const struct m_opt_choice_alternatives lut_types[] = {
+ {"auto", PL_LUT_UNKNOWN},
+ {"native", PL_LUT_NATIVE},
+ {"normalized", PL_LUT_NORMALIZED},
+ {"conversion", PL_LUT_CONVERSION},
+ {0}
};
-static void update_render_options(struct vo *vo);
-static void update_lut(struct priv *p, struct user_lut *lut);
+#define OPT_BASE_STRUCT struct gl_next_opts
+const struct m_sub_options gl_next_conf = {
+ .opts = (const struct m_option[]) {
+ {"allow-delayed-peak-detect", OPT_BOOL(delayed_peak)},
+ {"border-background", OPT_CHOICE(border_background,
+ {"none", BACKGROUND_NONE},
+ {"color", BACKGROUND_COLOR},
+ {"tiles", BACKGROUND_TILES})},
+ {"corner-rounding", OPT_FLOAT(corner_rounding), M_RANGE(0, 1)},
+ {"interpolation-preserve", OPT_BOOL(inter_preserve)},
+ {"lut", OPT_STRING(lut.opt), .flags = M_OPT_FILE},
+ {"lut-type", OPT_CHOICE_C(lut.type, lut_types)},
+ {"image-lut", OPT_STRING(image_lut.opt), .flags = M_OPT_FILE},
+ {"image-lut-type", OPT_CHOICE_C(image_lut.type, lut_types)},
+ {"target-lut", OPT_STRING(target_lut.opt), .flags = M_OPT_FILE},
+ {"target-colorspace-hint", OPT_BOOL(target_hint)},
+ // No `target-lut-type` because we don't support non-RGB targets
+ {"libplacebo-opts", OPT_KEYVALUELIST(raw_opts)},
+ {0},
+ },
+ .defaults = &(struct gl_next_opts) {
+ .border_background = BACKGROUND_COLOR,
+ .inter_preserve = true,
+ },
+ .size = sizeof(struct gl_next_opts),
+ .change_flags = UPDATE_VIDEO,
+};
static pl_buf get_dr_buf(struct priv *p, const uint8_t *ptr)
{
@@ -238,8 +287,6 @@ static struct mp_image *get_image(struct vo *vo, int imgfmt, int w, int h,
return mpi;
}
-static struct pl_color_space get_mpi_csp(struct vo *vo, struct mp_image *mpi);
-
static void update_overlays(struct vo *vo, struct mp_osd_res res,
int flags, enum pl_overlay_coords coords,
struct osd_state *state, struct pl_frame *frame,
@@ -290,6 +337,8 @@ static void update_overlays(struct vo *vo, struct mp_osd_res res,
entry->num_parts = 0;
for (int i = 0; i < item->num_parts; i++) {
const struct sub_bitmap *b = &item->parts[i];
+ if (b->dw == 0 || b->dh == 0)
+ continue;
uint32_t c = b->libass.color;
struct pl_overlay_part part = {
.src = { b->src_x, b->src_y, b->src_x + b->w, b->src_y + b->h },
@@ -322,7 +371,7 @@ static void update_overlays(struct vo *vo, struct mp_osd_res res,
ol->repr.alpha = PL_ALPHA_PREMULTIPLIED;
// Infer bitmap colorspace from source
if (src) {
- ol->color = get_mpi_csp(vo, src);
+ ol->color = src->params.color;
// Seems like HDR subtitles are targeting SDR white
if (pl_color_transfer_is_hdr(ol->color.transfer)) {
ol->color.hdr = (struct pl_hdr_metadata) {
@@ -332,6 +381,8 @@ static void update_overlays(struct vo *vo, struct mp_osd_res res,
}
break;
case SUBBITMAP_LIBASS:
+ if (src && item->video_color_space && !pl_color_space_is_hdr(&src->params.color))
+ ol->color = src->params.color;
ol->mode = PL_OVERLAY_MONOCHROME;
ol->repr.alpha = PL_ALPHA_INDEPENDENT;
break;
@@ -441,21 +492,15 @@ static int plane_data_from_imgfmt(struct pl_plane_data out_data[4],
return desc.num_planes;
}
-static struct pl_color_space get_mpi_csp(struct vo *vo, struct mp_image *mpi)
-{
- struct pl_color_space csp = {
- .primaries = mp_prim_to_pl(mpi->params.color.primaries),
- .transfer = mp_trc_to_pl(mpi->params.color.gamma),
- .hdr = mpi->params.color.hdr,
- };
- return csp;
-}
-
static bool hwdec_reconfig(struct priv *p, struct ra_hwdec *hwdec,
const struct mp_image_params *par)
{
if (p->hwdec_mapper) {
- if (mp_image_params_equal(par, &p->hwdec_mapper->src_params)) {
+ if (mp_image_params_static_equal(par, &p->hwdec_mapper->src_params)) {
+ p->hwdec_mapper->src_params.repr.dovi = par->repr.dovi;
+ p->hwdec_mapper->dst_params.repr.dovi = par->repr.dovi;
+ p->hwdec_mapper->src_params.color.hdr = par->color.hdr;
+ p->hwdec_mapper->dst_params.color.hdr = par->color.hdr;
return p->hwdec_mapper;
} else {
ra_hwdec_mapper_free(&p->hwdec_mapper);
@@ -571,12 +616,8 @@ static bool map_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *src
}
*frame = (struct pl_frame) {
- .color = get_mpi_csp(vo, mpi),
- .repr = {
- .sys = mp_csp_to_pl(par->color.space),
- .levels = mp_levels_to_pl(par->color.levels),
- .alpha = mp_alpha_to_pl(par->alpha),
- },
+ .color = par->color,
+ .repr = par->repr,
.profile = {
.data = mpi->icc_profile ? mpi->icc_profile->data : NULL,
.len = mpi->icc_profile ? mpi->icc_profile->size : 0,
@@ -588,14 +629,14 @@ static bool map_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *src
// mp_image, like AVFrame, likes communicating RGB/XYZ/YCbCr status
// implicitly via the image format, rather than the actual tagging.
switch (mp_imgfmt_get_forced_csp(par->imgfmt)) {
- case MP_CSP_RGB:
+ case PL_COLOR_SYSTEM_RGB:
frame->repr.sys = PL_COLOR_SYSTEM_RGB;
frame->repr.levels = PL_COLOR_LEVELS_FULL;
break;
- case MP_CSP_XYZ:
+ case PL_COLOR_SYSTEM_XYZ:
frame->repr.sys = PL_COLOR_SYSTEM_XYZ;
break;
- case MP_CSP_AUTO:
+ case PL_COLOR_SYSTEM_UNKNOWN:
if (!frame->repr.sys)
frame->repr.sys = pl_color_system_guess_ycbcr(par->w, par->h);
break;
@@ -664,10 +705,7 @@ static bool map_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *src
}
// Update chroma location, must be done after initializing planes
- pl_frame_set_chroma_location(frame, mp_chroma_to_pl(par->chroma_location));
-
- // Set the frame DOVI metadata
- mp_map_dovi_metadata_to_pl(mpi, frame);
+ pl_frame_set_chroma_location(frame, par->chroma_location);
if (mpi->film_grain)
pl_film_grain_from_av(&frame->film_grain, (AVFilmGrainParams *) mpi->film_grain->data);
@@ -679,9 +717,9 @@ static bool map_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *src
pl_icc_profile_compute_signature(&frame->profile);
// Update LUT attached to this frame
- update_lut(p, &p->image_lut);
- frame->lut = p->image_lut.lut;
- frame->lut_type = p->image_lut.type;
+ update_lut(p, &p->next_opts->image_lut);
+ frame->lut = p->next_opts->image_lut.lut;
+ frame->lut_type = p->next_opts->image_lut.type;
return true;
}
@@ -727,12 +765,14 @@ static void update_options(struct vo *vo)
{
struct priv *p = vo->priv;
pl_options pars = p->pars;
- if (m_config_cache_update(p->opts_cache))
+ bool changed = m_config_cache_update(p->opts_cache);
+ changed = m_config_cache_update(p->next_opts_cache) || changed;
+ if (changed)
update_render_options(vo);
- update_lut(p, &p->lut);
- pars->params.lut = p->lut.lut;
- pars->params.lut_type = p->lut.type;
+ update_lut(p, &p->next_opts->lut);
+ pars->params.lut = p->next_opts->lut.lut;
+ pars->params.lut_type = p->next_opts->lut.type;
// Update equalizer state
struct mp_csp_params cparams = MP_CSP_PARAMS_DEFAULTS;
@@ -744,7 +784,7 @@ static void update_options(struct vo *vo)
pars->color_adjustment.gamma = cparams.gamma;
p->output_levels = cparams.levels_out;
- for (char **kv = p->raw_opts; kv && kv[0]; kv += 2)
+ for (char **kv = p->next_opts->raw_opts; kv && kv[0]; kv += 2)
pl_options_set_str(pars, kv[0], kv[1]);
}
@@ -776,18 +816,18 @@ static void apply_target_contrast(struct priv *p, struct pl_color_space *color)
static void apply_target_options(struct priv *p, struct pl_frame *target)
{
- update_lut(p, &p->target_lut);
- target->lut = p->target_lut.lut;
- target->lut_type = p->target_lut.type;
+ update_lut(p, &p->next_opts->target_lut);
+ target->lut = p->next_opts->target_lut.lut;
+ target->lut_type = p->next_opts->target_lut.type;
// Colorspace overrides
const struct gl_video_opts *opts = p->opts_cache->opts;
if (p->output_levels)
- target->repr.levels = mp_levels_to_pl(p->output_levels);
+ target->repr.levels = p->output_levels;
if (opts->target_prim)
- target->color.primaries = mp_prim_to_pl(opts->target_prim);
+ target->color.primaries = opts->target_prim;
if (opts->target_trc)
- target->color.transfer = mp_trc_to_pl(opts->target_trc);
+ target->color.transfer = opts->target_trc;
// If swapchain returned a value use this, override is used in hint
if (opts->target_peak && !target->color.hdr.max_luma)
target->color.hdr.max_luma = opts->target_peak;
@@ -796,14 +836,23 @@ static void apply_target_options(struct priv *p, struct pl_frame *target)
if (opts->target_gamut) {
// Ensure resulting gamut still fits inside container
const struct pl_raw_primaries *gamut, *container;
- gamut = pl_raw_primaries_get(mp_prim_to_pl(opts->target_gamut));
+ gamut = pl_raw_primaries_get(opts->target_gamut);
container = pl_raw_primaries_get(target->color.primaries);
target->color.hdr.prim = pl_primaries_clip(gamut, container);
}
- if (opts->dither_depth > 0) {
+ int dither_depth = opts->dither_depth;
+ if (dither_depth == 0) {
+ struct ra_swapchain *sw = p->ra_ctx->swapchain;
+ if (sw->fns->color_depth) {
+ dither_depth = sw->fns->color_depth(sw);
+ } else if (!pl_color_transfer_is_hdr(target->color.transfer)) {
+ dither_depth = 8;
+ }
+ }
+ if (dither_depth > 0) {
struct pl_bit_encoding *tbits = &target->repr.bits;
- tbits->color_depth += opts->dither_depth - tbits->sample_depth;
- tbits->sample_depth = opts->dither_depth;
+ tbits->color_depth += dither_depth - tbits->sample_depth;
+ tbits->sample_depth = dither_depth;
}
if (opts->icc_opts->icc_use_luma) {
@@ -882,7 +931,7 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame)
params.info_callback = info_callback;
params.info_priv = vo;
params.skip_caching_single_frame = !cache_frame;
- params.preserve_mixing_cache = p->inter_preserve && !frame->still;
+ params.preserve_mixing_cache = p->next_opts->inter_preserve && !frame->still;
if (frame->still)
params.frame_mixer = NULL;
@@ -939,17 +988,17 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame)
p->last_id = id;
}
- if (p->target_hint && frame->current) {
- struct pl_color_space hint = get_mpi_csp(vo, frame->current);
+ if (p->next_opts->target_hint && frame->current) {
+ struct pl_color_space hint = frame->current->params.color;
if (opts->target_prim)
- hint.primaries = mp_prim_to_pl(opts->target_prim);
+ hint.primaries = opts->target_prim;
if (opts->target_trc)
- hint.transfer = mp_trc_to_pl(opts->target_trc);
+ hint.transfer = opts->target_trc;
if (opts->target_peak)
hint.hdr.max_luma = opts->target_peak;
apply_target_contrast(p, &hint);
pl_swapchain_colorspace_hint(p->sw, &hint);
- } else if (!p->target_hint) {
+ } else if (!p->next_opts->target_hint) {
pl_swapchain_colorspace_hint(p->sw, NULL);
}
@@ -959,14 +1008,15 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame)
if (!should_draw || !pl_swapchain_start_frame(p->sw, &swframe)) {
if (frame->current) {
// Advance the queue state to the current PTS to discard unused frames
- pl_queue_update(p->queue, NULL, pl_queue_params(
+ struct pl_queue_params qparams = *pl_queue_params(
.pts = frame->current->pts + pts_offset,
.radius = pl_frame_mix_radius(&params),
.vsync_duration = can_interpolate ? frame->ideal_frame_vsync_duration : 0,
+ );
#if PL_API_VER >= 340
- .drift_compensation = 0,
+ qparams.drift_compensation = 0;
#endif
- ));
+ pl_queue_update(p->queue, NULL, &qparams);
}
return;
}
@@ -992,10 +1042,10 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame)
.radius = pl_frame_mix_radius(&params),
.vsync_duration = can_interpolate ? frame->ideal_frame_vsync_duration : 0,
.interpolation_threshold = opts->interpolation_threshold,
+ );
#if PL_API_VER >= 340
- .drift_compensation = 0,
+ qparams.drift_compensation = 0;
#endif
- );
// Depending on the vsync ratio, we may be up to half of the vsync
// duration before the current frame time. This works fine because
@@ -1035,7 +1085,9 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame)
struct frame_priv *fp = mpi->priv;
apply_crop(image, p->src, vo->params->w, vo->params->h);
if (opts->blend_subs) {
- if (frame->redraw || fp->osd_sync < p->osd_sync) {
+ if (frame->redraw)
+ p->osd_sync++;
+ if (fp->osd_sync < p->osd_sync) {
float rx = pl_rect_w(p->dst) / pl_rect_w(image->crop);
float ry = pl_rect_h(p->dst) / pl_rect_h(image->crop);
struct mp_osd_res res = {
@@ -1047,9 +1099,6 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame)
.mb = (image->crop.y1 - vo->params->h) * ry,
.display_par = 1.0,
};
- // TODO: fix this doing pointless updates
- if (frame->redraw)
- p->osd_sync++;
update_overlays(vo, res, OSD_DRAW_SUB_ONLY,
PL_OVERLAY_COORDS_DST_CROP,
&fp->subs, image, mpi);
@@ -1078,12 +1127,27 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame)
goto done;
}
- const struct pl_frame *cur_frame = pl_frame_mix_nearest(&mix);
- if (cur_frame && vo->params) {
- vo->params->color.hdr = cur_frame->color.hdr;
+ struct pl_frame ref_frame;
+ pl_frames_infer_mix(p->rr, &mix, &target, &ref_frame);
+
+ mp_mutex_lock(&vo->params_mutex);
+ p->target_params = (struct mp_image_params){
+ .imgfmt_name = swframe.fbo->params.format
+ ? swframe.fbo->params.format->name : NULL,
+ .w = mp_rect_w(p->dst),
+ .h = mp_rect_h(p->dst),
+ .color = target.color,
+ .repr = target.repr,
+ .rotate = target.rotation,
+ };
+ vo->target_params = &p->target_params;
+
+ if (vo->params) {
+ vo->params->color.hdr = ref_frame.color.hdr;
// Augment metadata with peak detection max_pq_y / avg_pq_y
pl_renderer_get_hdr_metadata(p->rr, &vo->params->color.hdr);
}
+ mp_mutex_unlock(&vo->params_mutex);
p->is_interpolated = pts_offset != 0 && mix.num_frames > 1;
valid = true;
@@ -1168,6 +1232,9 @@ static int reconfig(struct vo *vo, struct mp_image_params *params)
return -1;
resize(vo);
+ mp_mutex_lock(&vo->params_mutex);
+ vo->target_params = NULL;
+ mp_mutex_unlock(&vo->params_mutex);
return 0;
}
@@ -1236,12 +1303,13 @@ static void video_screenshot(struct vo *vo, struct voctrl_screenshot *args)
// Retrieve the current frame from the frame queue
struct pl_frame_mix mix;
enum pl_queue_status status;
- status = pl_queue_update(p->queue, &mix, pl_queue_params(
+ struct pl_queue_params qparams = *pl_queue_params(
.pts = p->last_pts,
+ );
#if PL_API_VER >= 340
- .drift_compensation = 0,
+ qparams.drift_compensation = 0;
#endif
- ));
+ status = pl_queue_update(p->queue, &mix, &qparams);
assert(status != PL_QUEUE_EOF);
if (status == PL_QUEUE_ERR) {
MP_ERR(vo, "Unknown error occurred while trying to take screenshot!\n");
@@ -1379,9 +1447,9 @@ static void video_screenshot(struct vo *vo, struct voctrl_screenshot *args)
if (!args->res)
goto done;
- args->res->params.color.primaries = mp_prim_from_pl(target.color.primaries);
- args->res->params.color.gamma = mp_trc_from_pl(target.color.transfer);
- args->res->params.color.levels = mp_levels_from_pl(target.repr.levels);
+ args->res->params.color.primaries = target.color.primaries;
+ args->res->params.color.transfer = target.color.transfer;
+ args->res->params.repr.levels = target.repr.levels;
args->res->params.color.hdr = target.color.hdr;
if (args->scaled)
args->res->params.p_w = args->res->params.p_h = 1;
@@ -1424,6 +1492,19 @@ static inline void copy_frame_info_to_mp(struct frame_info *pl,
}
}
+static void update_ra_ctx_options(struct vo *vo, struct ra_ctx_opts *ctx_opts)
+{
+ struct priv *p = vo->priv;
+ struct gl_video_opts *gl_opts = p->opts_cache->opts;
+ bool border_alpha = (p->next_opts->border_background == BACKGROUND_COLOR &&
+ gl_opts->background_color.a != 255) ||
+ p->next_opts->border_background == BACKGROUND_NONE;
+ ctx_opts->want_alpha = (gl_opts->background == BACKGROUND_COLOR &&
+ gl_opts->background_color.a != 255) ||
+ gl_opts->background == BACKGROUND_NONE ||
+ border_alpha;
+}
+
static int control(struct vo *vo, uint32_t request, void *data)
{
struct priv *p = vo->priv;
@@ -1432,7 +1513,6 @@ static int control(struct vo *vo, uint32_t request, void *data)
case VOCTRL_SET_PANSCAN:
resize(vo);
return VO_TRUE;
- case VOCTRL_SET_EQUALIZER:
case VOCTRL_PAUSE:
if (p->is_interpolated)
vo->want_redraw = true;
@@ -1440,8 +1520,7 @@ static int control(struct vo *vo, uint32_t request, void *data)
case VOCTRL_UPDATE_RENDER_OPTS: {
m_config_cache_update(p->opts_cache);
- const struct gl_video_opts *opts = p->opts_cache->opts;
- p->ra_ctx->opts.want_alpha = opts->alpha_mode == ALPHA_YES;
+ update_ra_ctx_options(vo, &p->ra_ctx->opts);
if (p->ra_ctx->fns->update_render_opts)
p->ra_ctx->fns->update_render_opts(p->ra_ctx);
update_render_options(vo);
@@ -1513,84 +1592,190 @@ static void wait_events(struct vo *vo, int64_t until_time_ns)
}
}
-#if PL_API_VER < 342
-static inline void xor_hash(void *hash, pl_cache_obj obj)
+static char *cache_filepath(void *ta_ctx, char *dir, const char *prefix, uint64_t key)
{
- *((uint64_t *) hash) ^= obj.key;
+ bstr filename = {0};
+ bstr_xappend_asprintf(ta_ctx, &filename, "%s_%016" PRIx64, prefix, key);
+ return mp_path_join_bstr(ta_ctx, bstr0(dir), filename);
}
-static inline uint64_t pl_cache_signature(pl_cache cache)
+static pl_cache_obj cache_load_obj(void *p, uint64_t key)
{
- uint64_t hash = 0;
- pl_cache_iterate(cache, xor_hash, &hash);
- return hash;
+ struct cache *c = p;
+ void *ta_ctx = talloc_new(NULL);
+ pl_cache_obj obj = {0};
+
+ if (!c->dir)
+ goto done;
+
+ char *filepath = cache_filepath(ta_ctx, c->dir, c->name, key);
+ if (!filepath)
+ goto done;
+
+ if (stat(filepath, &(struct stat){0}))
+ goto done;
+
+ int64_t load_start = mp_time_ns();
+ struct bstr data = stream_read_file(filepath, ta_ctx, c->global, STREAM_MAX_READ_SIZE);
+ int64_t load_end = mp_time_ns();
+ MP_DBG(c, "%s: key(%" PRIx64 "), size(%zu), load time(%.3f ms)\n",
+ __func__, key, data.len,
+ MP_TIME_NS_TO_MS(load_end - load_start));
+
+ obj = (pl_cache_obj){
+ .key = key,
+ .data = talloc_steal(NULL, data.start),
+ .size = data.len,
+ .free = talloc_free,
+ };
+
+done:
+ talloc_free(ta_ctx);
+ return obj;
+}
+
+static void cache_save_obj(void *p, pl_cache_obj obj)
+{
+ if (!obj.data || !obj.size)
+ return;
+
+ const struct cache *c = p;
+ void *ta_ctx = talloc_new(NULL);
+
+ if (!c->dir)
+ goto done;
+
+ char *filepath = cache_filepath(ta_ctx, c->dir, c->name, obj.key);
+ if (!filepath)
+ goto done;
+
+ // Don't save if already exists
+ if (!stat(filepath, &(struct stat){0})) {
+ MP_DBG(c, "%s: key(%"PRIx64"), size(%zu)\n", __func__, obj.key, obj.size);
+ goto done;
+ }
+
+ int64_t save_start = mp_time_ns();
+ mp_save_to_file(filepath, obj.data, obj.size);
+ int64_t save_end = mp_time_ns();
+ MP_DBG(c, "%s: key(%" PRIx64 "), size(%zu), save time(%.3f ms)\n",
+ __func__, obj.key, obj.size,
+ MP_TIME_NS_TO_MS(save_end - save_start));
+
+done:
+ talloc_free(ta_ctx);
}
-#endif
static void cache_init(struct vo *vo, struct cache *cache, size_t max_size,
const char *dir_opt)
{
struct priv *p = vo->priv;
- const char *name = cache == &p->shader_cache ? "shader.cache" : "icc.cache";
+ const char *name = cache == &p->shader_cache ? "shader" : "icc";
+ const size_t limit = cache == &p->shader_cache ? 128 << 20 : 1536 << 20;
char *dir;
if (dir_opt && dir_opt[0]) {
- dir = mp_get_user_path(NULL, p->global, dir_opt);
+ dir = mp_get_user_path(vo, p->global, dir_opt);
} else {
- dir = mp_find_user_file(NULL, p->global, "cache", "");
+ dir = mp_find_user_file(vo, p->global, "cache", "");
}
if (!dir || !dir[0])
- goto done;
+ return;
mp_mkdirp(dir);
- cache->path = mp_path_join(vo, dir, name);
- cache->cache = pl_cache_create(pl_cache_params(
- .log = p->pllog,
- .max_total_size = max_size,
- ));
+ *cache = (struct cache){
+ .log = p->log,
+ .global = p->global,
+ .dir = dir,
+ .name = name,
+ .size_limit = limit,
+ .cache = pl_cache_create(pl_cache_params(
+ .log = p->pllog,
+ .get = cache_load_obj,
+ .set = cache_save_obj,
+ .priv = cache
+ )),
+ };
+}
- FILE *file = fopen(cache->path, "rb");
- if (file) {
- int ret = pl_cache_load_file(cache->cache, file);
- fclose(file);
- if (ret < 0)
- MP_WARN(p, "Failed loading cache from %s\n", cache->path);
- }
+struct file_entry {
+ char *filepath;
+ size_t size;
+ time_t atime;
+};
- cache->sig = pl_cache_signature(cache->cache);
-done:
- talloc_free(dir);
+static int compare_atime(const void *a, const void *b)
+{
+ return (((struct file_entry *)b)->atime - ((struct file_entry *)a)->atime);
}
static void cache_uninit(struct priv *p, struct cache *cache)
{
if (!cache->cache)
- goto done;
- if (pl_cache_signature(cache->cache) == cache->sig)
- goto done; // skip re-saving identical cache
+ return;
- assert(cache->path);
- char *tmp = talloc_asprintf(cache->path, "%sXXXXXX", cache->path);
- int fd = mkstemp(tmp);
- if (fd < 0)
- goto done;
- FILE *file = fdopen(fd, "wb");
- if (!file) {
- close(fd);
- unlink(tmp);
+ void *ta_ctx = talloc_new(NULL);
+ struct file_entry *files = NULL;
+ size_t num_files = 0;
+ assert(cache->dir);
+ assert(cache->name);
+
+ DIR *d = opendir(cache->dir);
+ if (!d)
goto done;
+
+ struct dirent *dir;
+ while ((dir = readdir(d)) != NULL) {
+ char *filepath = mp_path_join(ta_ctx, cache->dir, dir->d_name);
+ if (!filepath)
+ continue;
+ struct stat filestat;
+ if (stat(filepath, &filestat))
+ continue;
+ if (!S_ISREG(filestat.st_mode))
+ continue;
+ bstr fname = bstr0(dir->d_name);
+ if (!bstr_eatstart0(&fname, cache->name))
+ continue;
+ if (!bstr_eatstart0(&fname, "_"))
+ continue;
+ if (fname.len != 16) // %016x
+ continue;
+ MP_TARRAY_APPEND(ta_ctx, files, num_files,
+ (struct file_entry){
+ .filepath = filepath,
+ .size = filestat.st_size,
+ .atime = filestat.st_atime,
+ });
}
- int ret = pl_cache_save_file(cache->cache, file);
- fclose(file);
- if (ret >= 0)
- ret = rename(tmp, cache->path);
- if (ret < 0) {
- MP_WARN(p, "Failed saving cache to %s\n", cache->path);
- unlink(tmp);
+ closedir(d);
+
+ if (!num_files)
+ goto done;
+
+ qsort(files, num_files, sizeof(struct file_entry), compare_atime);
+
+ time_t t = time(NULL);
+ size_t cache_size = 0;
+ size_t cache_limit = cache->size_limit ? cache->size_limit : SIZE_MAX;
+ for (int i = 0; i < num_files; i++) {
+ // Remove files that exceed the size limit but are older than one day.
+ // This allows for temporary maintaining a larger cache size while
+ // adjusting the configuration. The cache will be cleared the next day
+ // for unused entries. We don't need to be overly aggressive with cache
+ // cleaning; in most cases, it will not grow much, and in others, it may
+ // actually be useful to cache more.
+ cache_size += files[i].size;
+ double rel_use = difftime(t, files[i].atime);
+ if (cache_size > cache_limit && rel_use > 60 * 60 * 24) {
+ MP_VERBOSE(p, "Removing %s | size: %9zu bytes | last used: %9d seconds ago\n",
+ files[i].filepath, files[i].size, (int)rel_use);
+ unlink(files[i].filepath);
+ }
}
- // fall through
done:
+ talloc_free(ta_ctx);
pl_cache_destroy(&cache->cache);
}
@@ -1618,6 +1803,10 @@ static void uninit(struct vo *vo)
cache_uninit(p, &p->shader_cache);
cache_uninit(p, &p->icc_cache);
+ pl_lut_free(&p->next_opts->image_lut.lut);
+ pl_lut_free(&p->next_opts->lut.lut);
+ pl_lut_free(&p->next_opts->target_lut.lut);
+
pl_icc_close(&p->icc_profile);
pl_renderer_destroy(&p->rr);
@@ -1644,12 +1833,17 @@ static int preinit(struct vo *vo)
{
struct priv *p = vo->priv;
p->opts_cache = m_config_cache_alloc(p, vo->global, &gl_video_conf);
+ p->next_opts_cache = m_config_cache_alloc(p, vo->global, &gl_next_conf);
+ p->next_opts = p->next_opts_cache->opts;
p->video_eq = mp_csp_equalizer_create(p, vo->global);
p->global = vo->global;
p->log = vo->log;
struct gl_video_opts *gl_opts = p->opts_cache->opts;
- p->context = gpu_ctx_create(vo, gl_opts);
+ struct ra_ctx_opts *ctx_opts = mp_get_config_group(vo, vo->global, &ra_ctx_conf);
+ update_ra_ctx_options(vo, ctx_opts);
+ p->context = gpu_ctx_create(vo, ctx_opts);
+ talloc_free(ctx_opts);
if (!p->context)
goto err_out;
// For the time being
@@ -1857,6 +2051,7 @@ static void update_lut(struct priv *p, struct user_lut *lut)
MP_VERBOSE(p, "Loading custom LUT '%s'\n", fname);
struct bstr lutdata = stream_read_file(fname, p, p->global, 100000000); // 100 MB
lut->lut = pl_lut_parse_cube(p->pllog, lutdata.start, lutdata.len);
+ talloc_free(fname);
talloc_free(lutdata.start);
}
@@ -1934,15 +2129,27 @@ static void update_render_options(struct vo *vo)
pl_options pars = p->pars;
const struct gl_video_opts *opts = p->opts_cache->opts;
pars->params.antiringing_strength = opts->scaler[0].antiring;
- pars->params.background_color[0] = opts->background.r / 255.0;
- pars->params.background_color[1] = opts->background.g / 255.0;
- pars->params.background_color[2] = opts->background.b / 255.0;
- pars->params.background_transparency = 1.0 - opts->background.a / 255.0;
+ pars->params.background_color[0] = opts->background_color.r / 255.0;
+ pars->params.background_color[1] = opts->background_color.g / 255.0;
+ pars->params.background_color[2] = opts->background_color.b / 255.0;
+ pars->params.background_transparency = 1 - opts->background_color.a / 255.0;
pars->params.skip_anti_aliasing = !opts->correct_downscaling;
pars->params.disable_linear_scaling = !opts->linear_downscaling && !opts->linear_upscaling;
pars->params.disable_fbos = opts->dumb_mode == 1;
- pars->params.blend_against_tiles = opts->alpha_mode == ALPHA_BLEND_TILES;
- pars->params.corner_rounding = p->corner_rounding;
+
+#if PL_API_VER >= 346
+ int map_background_types[3] = {
+ PL_CLEAR_SKIP, // BACKGROUND_NONE
+ PL_CLEAR_COLOR, // BACKGROUND_COLOR
+ PL_CLEAR_TILES, // BACKGROUND_TILES
+ };
+ pars->params.background = map_background_types[opts->background];
+ pars->params.border = map_background_types[p->next_opts->border_background];
+#else
+ pars->params.blend_against_tiles = opts->background == BACKGROUND_TILES;
+#endif
+
+ pars->params.corner_rounding = p->next_opts->corner_rounding;
pars->params.correct_subpixel_offsets = !opts->scaler_resizes_only;
// Map scaler options as best we can
@@ -1976,7 +2183,7 @@ static void update_render_options(struct vo *vo)
pars->peak_detect_params.scene_threshold_low = opts->tone_map.scene_threshold_low;
pars->peak_detect_params.scene_threshold_high = opts->tone_map.scene_threshold_high;
pars->peak_detect_params.percentile = opts->tone_map.peak_percentile;
- pars->peak_detect_params.allow_delayed = p->delayed_peak;
+ pars->peak_detect_params.allow_delayed = p->next_opts->delayed_peak;
const struct pl_tone_map_function * const tone_map_funs[] = {
[TONE_MAPPING_AUTO] = &pl_tone_map_auto,
@@ -2055,16 +2262,6 @@ static void update_render_options(struct vo *vo)
pars->params.hooks = p->hooks;
}
-#define OPT_BASE_STRUCT struct priv
-
-const struct m_opt_choice_alternatives lut_types[] = {
- {"auto", PL_LUT_UNKNOWN},
- {"native", PL_LUT_NATIVE},
- {"normalized", PL_LUT_NORMALIZED},
- {"conversion", PL_LUT_CONVERSION},
- {0}
-};
-
const struct vo_driver video_out_gpu_next = {
.description = "Video output based on libplacebo",
.name = "gpu-next",
@@ -2083,22 +2280,4 @@ const struct vo_driver video_out_gpu_next = {
.wakeup = wakeup,
.uninit = uninit,
.priv_size = sizeof(struct priv),
- .priv_defaults = &(const struct priv) {
- .inter_preserve = true,
- },
-
- .options = (const struct m_option[]) {
- {"allow-delayed-peak-detect", OPT_BOOL(delayed_peak)},
- {"corner-rounding", OPT_FLOAT(corner_rounding), M_RANGE(0, 1)},
- {"interpolation-preserve", OPT_BOOL(inter_preserve)},
- {"lut", OPT_STRING(lut.opt), .flags = M_OPT_FILE},
- {"lut-type", OPT_CHOICE_C(lut.type, lut_types)},
- {"image-lut", OPT_STRING(image_lut.opt), .flags = M_OPT_FILE},
- {"image-lut-type", OPT_CHOICE_C(image_lut.type, lut_types)},
- {"target-lut", OPT_STRING(target_lut.opt), .flags = M_OPT_FILE},
- {"target-colorspace-hint", OPT_BOOL(target_hint)},
- // No `target-lut-type` because we don't support non-RGB targets
- {"libplacebo-opts", OPT_KEYVALUELIST(raw_opts)},
- {0}
- },
};