/*
* Copyright (C) 2021 Niklas Haas
*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <libplacebo/renderer.h>
#include <libplacebo/shaders/lut.h>
#include <libplacebo/utils/libav.h>
#include <libplacebo/utils/frame_queue.h>
#ifdef PL_HAVE_LCMS
#include <libplacebo/shaders/icc.h>
#endif
#include "config.h"
#include "common/common.h"
#include "options/m_config.h"
#include "options/path.h"
#include "osdep/io.h"
#include "stream/stream.h"
#include "video/mp_image.h"
#include "video/fmt-conversion.h"
#include "placebo/utils.h"
#include "gpu/context.h"
#include "gpu/video.h"
#include "gpu/video_shaders.h"
#include "sub/osd.h"
#include "gpu_next/context.h"
struct osd_entry {
pl_tex tex;
struct pl_overlay_part *parts;
int num_parts;
};
struct osd_state {
struct osd_entry entries[MAX_OSD_PARTS];
struct pl_overlay overlays[MAX_OSD_PARTS];
};
struct scaler_params {
struct pl_filter_config config;
struct pl_filter_function kernel;
struct pl_filter_function window;
};
struct user_hook {
char *path;
const struct pl_hook *hook;
};
struct user_lut {
char *opt;
char *path;
int type;
struct pl_custom_lut *lut;
};
struct priv {
struct mp_log *log;
struct mpv_global *global;
struct ra_ctx *ra_ctx;
struct gpu_ctx *context;
pl_log pllog;
pl_gpu gpu;
pl_renderer rr;
pl_queue queue;
pl_swapchain sw;
pl_fmt osd_fmt[SUBBITMAP_COUNT];
pl_tex *sub_tex;
int num_sub_tex;
struct mp_rect src, dst;
struct mp_osd_res osd_res;
struct osd_state osd_state;
uint64_t last_id;
uint64_t osd_sync;
double last_pts;
bool is_interpolated;
bool want_reset;
struct m_config_cache *opts_cache;
struct mp_csp_equalizer_state *video_eq;
struct pl_render_params params;
struct pl_deband_params deband;
struct pl_sigmoid_params sigmoid;
struct pl_color_adjustment color_adjustment;
struct pl_peak_detect_params peak_detect;
struct pl_color_map_params color_map;
struct pl_dither_params dither;
struct scaler_params scalers[SCALER_COUNT];
const struct pl_hook **hooks; // storage for `params.hooks`
const struct pl_filter_config *frame_mixer;
#ifdef PL_HAVE_LCMS
struct pl_icc_params icc;
struct pl_icc_profile icc_profile;
char *icc_path;
#endif
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;
// Performance data of last frame
struct voctrl_performance_data perf;
int delayed_peak;
int inter_preserve;
int target_hint;
};
static void update_render_options(struct priv *p);
static void update_lut(struct priv *p, struct user_lut *lut);
// This struct is stored at the end of DR-allocated buffers, and serves to both
// detect such buffers and hold the reference to the actual GPU buffer.
struct dr_buf {
uint64_t sentinel[2];
pl_gpu gpu;
pl_buf buf;
};
static const uint64_t dr_magic[2] = { 0xc6e9222474db53ae, 0x9d49b2de6c3b563e };
static const size_t dr_align = offsetof(struct { char c; struct dr_buf dr; }, dr);
static inline struct dr_buf *dr_header(void *ptr, size_t size)
{
uintptr_t start = (uintptr_t) ptr + size - sizeof(struct dr_buf);
uintptr_t aligned = MP_ALIGN_DOWN(start, dr_align);
assert(aligned >= (uintptr_t) ptr);
return (struct dr_buf *) aligned;
}
static pl_buf get_dr_buf(struct mp_image *mpi)
{
if (!mpi->bufs[0] || mpi->bufs[0]->size < sizeof(struct dr_buf))
return NULL;
struct dr_buf *dr = dr_header(mpi->bufs[0]->data, mpi->bufs[0]->size);
if (memcmp(dr->sentinel, dr_magic, sizeof(dr_magic)) == 0)
return dr->buf;
return NULL;
}
static void free_dr_buf(void *opaque, uint8_t *data)
{
struct dr_buf *dr = opaque;
assert(memcmp(dr->sentinel, dr_magic, sizeof(dr_magic)) == 0);
// Can't use `&dr->buf` because it gets freed during `pl_buf_destroy`
pl_buf_destroy(dr->gpu, &(pl_buf) { dr->buf });
}
static struct mp_image *get_image(struct vo *vo, int imgfmt, int w, int h,
int stride_align)
{
struct priv *p = vo->priv;
pl_gpu gpu = p->gpu;
if (!gpu->limits.thread_safe || !gpu->limits.max_mapped_size)
return NULL;
int size = mp_image_get_alloc_size(imgfmt, w, h, stride_align);
if (s
|