diff options
Diffstat (limited to 'video/out/gpu/video.c')
-rw-r--r-- | video/out/gpu/video.c | 358 |
1 files changed, 196 insertions, 162 deletions
diff --git a/video/out/gpu/video.c b/video/out/gpu/video.c index 852ee78..1478ec4 100644 --- a/video/out/gpu/video.c +++ b/video/out/gpu/video.c @@ -183,6 +183,7 @@ struct gl_video { struct mp_image_params real_image_params; // configured format struct mp_image_params image_params; // texture format (mind hwdec case) + struct mp_image_params target_params; // target format struct ra_imgfmt_desc ra_format; // texture format int plane_count; @@ -212,6 +213,7 @@ struct gl_video { struct ra_tex *merge_tex[4]; struct ra_tex *scale_tex[4]; struct ra_tex *integer_tex[4]; + struct ra_tex *chroma_tex[4]; struct ra_tex *indirect_tex; struct ra_tex *blend_subs_tex; struct ra_tex *error_diffusion_tex[2]; @@ -312,8 +314,8 @@ static const struct gl_video_opts gl_video_opts_def = { .linear_downscaling = true, .sigmoid_upscaling = true, .interpolation_threshold = 0.01, - .alpha_mode = ALPHA_BLEND_TILES, - .background = {0, 0, 0, 255}, + .background = BACKGROUND_TILES, + .background_color = {0, 0, 0, 255}, .gamma = 1.0f, .tone_map = { .curve = TONE_MAPPING_AUTO, @@ -329,14 +331,9 @@ static const struct gl_video_opts gl_video_opts_def = { .hwdec_interop = "auto", }; -static int validate_scaler_opt(struct mp_log *log, const m_option_t *opt, - struct bstr name, const char **value); - -static int validate_window_opt(struct mp_log *log, const m_option_t *opt, - struct bstr name, const char **value); - -static int validate_error_diffusion_opt(struct mp_log *log, const m_option_t *opt, - struct bstr name, const char **value); +static OPT_STRING_VALIDATE_FUNC(validate_scaler_opt); +static OPT_STRING_VALIDATE_FUNC(validate_window_opt); +static OPT_STRING_VALIDATE_FUNC(validate_error_diffusion_opt); #define OPT_BASE_STRUCT struct gl_video_opts @@ -368,13 +365,13 @@ const struct m_sub_options gl_video_conf = { .deprecation_message = "no replacement"}, {"gamma-auto", OPT_BOOL(gamma_auto), .deprecation_message = "no replacement"}, - {"target-prim", OPT_CHOICE_C(target_prim, mp_csp_prim_names)}, - {"target-trc", OPT_CHOICE_C(target_trc, mp_csp_trc_names)}, + {"target-prim", OPT_CHOICE_C(target_prim, pl_csp_prim_names)}, + {"target-trc", OPT_CHOICE_C(target_trc, pl_csp_trc_names)}, {"target-peak", OPT_CHOICE(target_peak, {"auto", 0}), M_RANGE(10, 10000)}, {"target-contrast", OPT_CHOICE(target_contrast, {"auto", 0}, {"inf", -1}), M_RANGE(10, 1000000)}, - {"target-gamut", OPT_CHOICE_C(target_gamut, mp_csp_prim_names)}, + {"target-gamut", OPT_CHOICE_C(target_gamut, pl_csp_prim_names)}, {"tone-mapping", OPT_CHOICE(tone_map.curve, {"auto", TONE_MAPPING_AUTO}, {"clip", TONE_MAPPING_CLIP}, @@ -447,13 +444,12 @@ const struct m_sub_options gl_video_conf = { M_RANGE(1, 128)}, {"error-diffusion", OPT_STRING_VALIDATE(error_diffusion, validate_error_diffusion_opt)}, - {"alpha", OPT_CHOICE(alpha_mode, - {"no", ALPHA_NO}, - {"yes", ALPHA_YES}, - {"blend", ALPHA_BLEND}, - {"blend-tiles", ALPHA_BLEND_TILES})}, + {"background", OPT_CHOICE(background, + {"none", BACKGROUND_NONE}, + {"color", BACKGROUND_COLOR}, + {"tiles", BACKGROUND_TILES})}, {"opengl-rectangle-textures", OPT_BOOL(use_rectangle)}, - {"background", OPT_COLOR(background)}, + {"background-color", OPT_COLOR(background_color)}, {"interpolation", OPT_BOOL(interpolation)}, {"interpolation-threshold", OPT_FLOAT(interpolation_threshold)}, {"blend-subtitles", OPT_CHOICE(blend_subs, @@ -483,6 +479,7 @@ const struct m_sub_options gl_video_conf = { }, .size = sizeof(struct gl_video_opts), .defaults = &gl_video_opts_def, + .change_flags = UPDATE_VIDEO, }; static void uninit_rendering(struct gl_video *p); @@ -578,6 +575,7 @@ static void uninit_rendering(struct gl_video *p) ra_tex_free(p->ra, &p->merge_tex[n]); ra_tex_free(p->ra, &p->scale_tex[n]); ra_tex_free(p->ra, &p->integer_tex[n]); + ra_tex_free(p->ra, &p->chroma_tex[n]); } ra_tex_free(p->ra, &p->indirect_tex); @@ -605,15 +603,6 @@ bool gl_video_gamma_auto_enabled(struct gl_video *p) return p->opts.gamma_auto; } -struct mp_colorspace gl_video_get_output_colorspace(struct gl_video *p) -{ - return (struct mp_colorspace) { - .primaries = p->opts.target_prim, - .gamma = p->opts.target_trc, - .hdr.max_luma = p->opts.target_peak, - }; -} - // Warning: profile.start must point to a ta allocation, and the function // takes over ownership. void gl_video_set_icc_profile(struct gl_video *p, bstr icc_data) @@ -627,8 +616,8 @@ bool gl_video_icc_auto_enabled(struct gl_video *p) return p->opts.icc_opts ? p->opts.icc_opts->profile_auto : false; } -static bool gl_video_get_lut3d(struct gl_video *p, enum mp_csp_prim prim, - enum mp_csp_trc trc) +static bool gl_video_get_lut3d(struct gl_video *p, enum pl_color_primaries prim, + enum pl_color_transfer trc) { if (!p->use_lut_3d) return false; @@ -771,16 +760,16 @@ static void pass_get_images(struct gl_video *p, struct video_image *vimg, struct gl_transform chroma = {{{ls_w, 0.0}, {0.0, ls_h}}}; - if (p->image_params.chroma_location != MP_CHROMA_CENTER) { - int cx, cy; - mp_get_chroma_location(p->image_params.chroma_location, &cx, &cy); + if (p->image_params.chroma_location != PL_CHROMA_CENTER) { + float cx, cy; + pl_chroma_location_offset(p->image_params.chroma_location, &cx, &cy); // By default texture coordinates are such that chroma is centered with // any chroma subsampling. If a specific direction is given, make it // so that the luma and chroma sample line up exactly. // For 4:4:4, setting chroma location should have no effect at all. // luma sample size (in chroma coord. space) - chroma.t[0] = ls_w < 1 ? ls_w * -cx / 2 : 0; - chroma.t[1] = ls_h < 1 ? ls_h * -cy / 2 : 0; + chroma.t[0] = ls_w < 1 ? ls_w * -cx : 0; + chroma.t[1] = ls_h < 1 ? ls_h * -cy : 0; } memset(img, 0, 4 * sizeof(img[0])); @@ -796,9 +785,9 @@ static void pass_get_images(struct gl_video *p, struct video_image *vimg, ctype = PLANE_NONE; } else if (c == 4) { ctype = PLANE_ALPHA; - } else if (p->image_params.color.space == MP_CSP_RGB) { + } else if (p->image_params.repr.sys == PL_COLOR_SYSTEM_RGB) { ctype = PLANE_RGB; - } else if (p->image_params.color.space == MP_CSP_XYZ) { + } else if (p->image_params.repr.sys == PL_COLOR_SYSTEM_XYZ) { ctype = PLANE_XYZ; } else { ctype = c == 1 ? PLANE_LUMA : PLANE_CHROMA; @@ -810,7 +799,7 @@ static void pass_get_images(struct gl_video *p, struct video_image *vimg, int msb_valid_bits = p->ra_format.component_bits + MPMIN(p->ra_format.component_pad, 0); - int csp = type == PLANE_ALPHA ? MP_CSP_RGB : p->image_params.color.space; + int csp = type == PLANE_ALPHA ? PL_COLOR_SYSTEM_RGB : p->image_params.repr.sys; float tex_mul = 1.0 / mp_get_csp_mul(csp, msb_valid_bits, p->ra_format.component_bits); if (p->ra_format.component_type == RA_CTYPE_FLOAT) @@ -1065,13 +1054,13 @@ static void uninit_video(struct gl_video *p) ra_hwdec_mapper_free(&p->hwdec_mapper); } -static void pass_record(struct gl_video *p, struct mp_pass_perf perf) +static void pass_record(struct gl_video *p, const struct mp_pass_perf *perf) { if (!p->pass || p->pass_idx == VO_PASS_PERF_MAX) return; struct pass_info *pass = &p->pass[p->pass_idx]; - pass->perf = perf; + pass->perf = *perf; if (pass->desc.len == 0) bstr_xappend(p, &pass->desc, bstr0("(unknown)")); @@ -1211,12 +1200,13 @@ static void dispatch_compute(struct gl_video *p, int w, int h, if (!(p->ra->caps & RA_CAP_NUM_GROUPS)) PRELUDE("#define gl_NumWorkGroups uvec3(%d, %d, 1)\n", num_x, num_y); - pass_record(p, gl_sc_dispatch_compute(p->sc, num_x, num_y, 1)); + struct mp_pass_perf perf = gl_sc_dispatch_compute(p->sc, num_x, num_y, 1); + pass_record(p, &perf); cleanup_binds(p); } static struct mp_pass_perf render_pass_quad(struct gl_video *p, - struct ra_fbo fbo, bool discard, + const struct ra_fbo *fbo, bool discard, const struct mp_rect *dst) { // The first element is reserved for `vec2 position` @@ -1274,15 +1264,16 @@ static struct mp_pass_perf render_pass_quad(struct gl_video *p, &p->tmp_vertex[num_vertex_attribs * 1], vertex_stride); - return gl_sc_dispatch_draw(p->sc, fbo.tex, discard, p->vao, num_vertex_attribs, + return gl_sc_dispatch_draw(p->sc, fbo->tex, discard, p->vao, num_vertex_attribs, vertex_stride, p->tmp_vertex, num_vertices); } -static void finish_pass_fbo(struct gl_video *p, struct ra_fbo fbo, +static void finish_pass_fbo(struct gl_video *p, const struct ra_fbo *fbo, bool discard, const struct mp_rect *dst) { pass_prepare_src_tex(p); - pass_record(p, render_pass_quad(p, fbo, discard, dst)); + struct mp_pass_perf perf = render_pass_quad(p, fbo, discard, dst); + pass_record(p, &perf); debug_check_gl(p, "after rendering"); cleanup_binds(p); } @@ -1319,7 +1310,7 @@ static void finish_pass_tex(struct gl_video *p, struct ra_tex **dst_tex, debug_check_gl(p, "after dispatching compute shader"); } else { struct ra_fbo fbo = { .tex = *dst_tex, }; - finish_pass_fbo(p, fbo, true, &(struct mp_rect){0, 0, w, h}); + finish_pass_fbo(p, &fbo, true, &(struct mp_rect){0, 0, w, h}); } } @@ -1955,7 +1946,7 @@ static void deband_hook(struct gl_video *p, struct image img, { pass_describe(p, "debanding (%s)", plane_names[img.type]); pass_sample_deband(p->sc, p->opts.deband_opts, &p->lfg, - p->image_params.color.gamma); + p->image_params.color.transfer); } static void unsharp_hook(struct gl_video *p, struct image img, @@ -2046,25 +2037,23 @@ static void user_hook(struct gl_video *p, struct image img, gl_transform_trans(shader->offset, trans); } -static bool add_user_hook(void *priv, struct gl_user_shader_hook hook) +static bool add_user_hook(void *priv, const struct gl_user_shader_hook *hook) { struct gl_video *p = priv; - struct gl_user_shader_hook *copy = talloc_ptrtype(p, copy); - *copy = hook; - + struct gl_user_shader_hook *copy = talloc_dup(p, (struct gl_user_shader_hook *)hook); struct tex_hook texhook = { - .save_tex = bstrdup0(copy, hook.save_tex), - .components = hook.components, - .align_offset = hook.align_offset, + .save_tex = bstrdup0(copy, copy->save_tex), + .components = copy->components, + .align_offset = copy->align_offset, .hook = user_hook, .cond = user_hook_cond, .priv = copy, }; for (int h = 0; h < SHADER_MAX_HOOKS; h++) - texhook.hook_tex[h] = bstrdup0(copy, hook.hook_tex[h]); + texhook.hook_tex[h] = bstrdup0(copy, copy->hook_tex[h]); for (int h = 0; h < SHADER_MAX_BINDS; h++) - texhook.bind_tex[h] = bstrdup0(copy, hook.bind_tex[h]); + texhook.bind_tex[h] = bstrdup0(copy, copy->bind_tex[h]); MP_TARRAY_APPEND(p, p->tex_hooks, p->num_tex_hooks, texhook); return true; @@ -2213,6 +2202,23 @@ static void pass_read_video(struct gl_video *p) } } + // If chroma textures are in a subsampled semi-planar format and rotated, + // introduce an explicit conversion pass to avoid breaking chroma scalers. + for (int n = 0; n < 4; n++) { + if (img[n].tex && img[n].type == PLANE_CHROMA && + img[n].tex->params.format->num_components == 2 && + p->image_params.rotate % 180 == 90 && + p->ra_format.chroma_w != 1) + { + GLSLF("// chroma fix for rotated plane %d\n", n); + copy_image(p, &(int){0}, img[n]); + pass_describe(p, "chroma fix for rotated plane"); + finish_pass_tex(p, &p->chroma_tex[n], img[n].w, img[n].h); + img[n] = image_wrap(p->chroma_tex[n], img[n].type, + img[n].components); + } + } + // At this point all planes are finalized but they may not be at the // required size yet. Furthermore, they may have texture offsets that // require realignment. @@ -2345,29 +2351,29 @@ static void pass_convert_yuv(struct gl_video *p) GLSLF("color = color.%s;\n", p->color_swizzle); // Pre-colormatrix input gamma correction - if (cparams.color.space == MP_CSP_XYZ) - pass_linearize(p->sc, p->image_params.color.gamma); + if (cparams.repr.sys == PL_COLOR_SYSTEM_XYZ) + pass_linearize(p->sc, p->image_params.color.transfer); // We always explicitly normalize the range in pass_read_video cparams.input_bits = cparams.texture_bits = 0; // Conversion to RGB. For RGB itself, this still applies e.g. brightness // and contrast controls, or expansion of e.g. LSB-packed 10 bit data. - struct mp_cmat m = {{{0}}}; + struct pl_transform3x3 m = {0}; mp_get_csp_matrix(&cparams, &m); - gl_sc_uniform_mat3(sc, "colormatrix", true, &m.m[0][0]); + gl_sc_uniform_mat3(sc, "colormatrix", true, &m.mat.m[0][0]); gl_sc_uniform_vec3(sc, "colormatrix_c", m.c); GLSL(color.rgb = mat3(colormatrix) * color.rgb + colormatrix_c;) - if (cparams.color.space == MP_CSP_XYZ) { - pass_delinearize(p->sc, p->image_params.color.gamma); + if (cparams.repr.sys == PL_COLOR_SYSTEM_XYZ) { + pass_delinearize(p->sc, p->image_params.color.transfer); // mp_get_csp_matrix implicitly converts XYZ to DCI-P3 - p->image_params.color.space = MP_CSP_RGB; - p->image_params.color.primaries = MP_CSP_PRIM_DCI_P3; + p->image_params.repr.sys = PL_COLOR_SYSTEM_RGB; + p->image_params.color.primaries = PL_COLOR_PRIM_DCI_P3; } - if (p->image_params.color.space == MP_CSP_BT_2020_C) { + if (p->image_params.repr.sys == PL_COLOR_SYSTEM_BT_2020_C) { // Conversion for C'rcY'cC'bc via the BT.2020 CL system: // C'bc = (B'-Y'c) / 1.9404 | C'bc <= 0 // = (B'-Y'c) / 1.5816 | C'bc > 0 @@ -2404,9 +2410,9 @@ static void pass_convert_yuv(struct gl_video *p) } p->components = 3; - if (!p->has_alpha || p->opts.alpha_mode == ALPHA_NO) { + if (!p->has_alpha) { GLSL(color.a = 1.0;) - } else if (p->image_params.alpha == MP_ALPHA_PREMUL) { + } else if (p->image_params.repr.alpha == PL_ALPHA_PREMULTIPLIED) { p->components = 4; } else { p->components = 4; @@ -2491,7 +2497,7 @@ static void pass_scale_main(struct gl_video *p) // Linear light downscaling results in nasty artifacts for HDR curves // due to the potentially extreme brightness differences severely // compounding any ringing. So just scale in gamma light instead. - if (mp_trc_is_hdr(p->image_params.color.gamma)) + if (pl_color_space_is_hdr(&p->image_params.color)) use_linear = false; } else if (upscaling) { use_linear = p->opts.linear_upscaling || p->opts.sigmoid_upscaling; @@ -2499,7 +2505,7 @@ static void pass_scale_main(struct gl_video *p) if (use_linear) { p->use_linear = true; - pass_linearize(p->sc, p->image_params.color.gamma); + pass_linearize(p->sc, p->image_params.color.transfer); pass_opt_hook_point(p, "LINEAR", NULL); } @@ -2552,8 +2558,9 @@ static void pass_scale_main(struct gl_video *p) // rendering) // If OSD is true, ignore any changes that may have been made to the video // by previous passes (i.e. linear scaling) -static void pass_colormanage(struct gl_video *p, struct mp_colorspace src, - struct mp_colorspace fbo_csp, int flags, bool osd) +static void pass_colormanage(struct gl_video *p, struct pl_color_space src, + enum mp_csp_light src_light, + struct pl_color_space fbo_csp, int flags, bool osd) { struct ra *ra = p->ra; @@ -2561,18 +2568,17 @@ static void pass_colormanage(struct gl_video *p, struct mp_colorspace src, // unless specific transfer function, primaries or target peak // is set. If values are set to _AUTO, the most likely intended // values are guesstimated later in this function. - struct mp_colorspace dst = { - .gamma = p->opts.target_trc == MP_CSP_TRC_AUTO ? - fbo_csp.gamma : p->opts.target_trc, - .primaries = p->opts.target_prim == MP_CSP_PRIM_AUTO ? + struct pl_color_space dst = { + .transfer = p->opts.target_trc == PL_COLOR_TRC_UNKNOWN ? + fbo_csp.transfer : p->opts.target_trc, + .primaries = p->opts.target_prim == PL_COLOR_PRIM_UNKNOWN ? fbo_csp.primaries : p->opts.target_prim, - .light = MP_CSP_LIGHT_DISPLAY, .hdr.max_luma = !p->opts.target_peak ? fbo_csp.hdr.max_luma : p->opts.target_peak, }; if (!p->colorspace_override_warned && - ((fbo_csp.gamma && dst.gamma != fbo_csp.gamma) || + ((fbo_csp.transfer && dst.transfer != fbo_csp.transfer) || (fbo_csp.primaries && dst.primaries != fbo_csp.primaries))) { MP_WARN(p, "One or more colorspace value is being overridden " @@ -2580,44 +2586,44 @@ static void pass_colormanage(struct gl_video *p, struct mp_colorspace src, "transfer function: (dst: %s, fbo: %s), " "primaries: (dst: %s, fbo: %s). " "Rendering can lead to incorrect results!\n", - m_opt_choice_str(mp_csp_trc_names, dst.gamma), - m_opt_choice_str(mp_csp_trc_names, fbo_csp.gamma), - m_opt_choice_str(mp_csp_prim_names, dst.primaries), - m_opt_choice_str(mp_csp_prim_names, fbo_csp.primaries)); + m_opt_choice_str(pl_csp_trc_names, dst.transfer), + m_opt_choice_str(pl_csp_trc_names, fbo_csp.transfer), + m_opt_choice_str(pl_csp_prim_names, dst.primaries), + m_opt_choice_str(pl_csp_prim_names, fbo_csp.primaries)); p->colorspace_override_warned = true; } - if (dst.gamma == MP_CSP_TRC_HLG) - dst.light = MP_CSP_LIGHT_SCENE_HLG; + enum mp_csp_light dst_light = dst.transfer == PL_COLOR_TRC_HLG ? + MP_CSP_LIGHT_SCENE_HLG : MP_CSP_LIGHT_DISPLAY; if (p->use_lut_3d && (flags & RENDER_SCREEN_COLOR)) { // The 3DLUT is always generated against the video's original source // space, *not* the reference space. (To avoid having to regenerate // the 3DLUT for the OSD on every frame) - enum mp_csp_prim prim_orig = p->image_params.color.primaries; - enum mp_csp_trc trc_orig = p->image_params.color.gamma; + enum pl_color_primaries prim_orig = p->image_params.color.primaries; + enum pl_color_transfer trc_orig = p->image_params.color.transfer; // One exception: HDR is not implemented by LittleCMS for technical // limitation reasons, so we use a gamma 2.2 input curve here instead. // We could pick any value we want here, the difference is just coding // efficiency. - if (mp_trc_is_hdr(trc_orig)) - trc_orig = MP_CSP_TRC_GAMMA22; + if (pl_color_space_is_hdr(&p->image_params.color)) + trc_orig = PL_COLOR_TRC_GAMMA22; if (gl_video_get_lut3d(p, prim_orig, trc_orig)) { dst.primaries = prim_orig; - dst.gamma = trc_orig; - assert(dst.primaries && dst.gamma); + dst.transfer = trc_orig; + assert(dst.primaries && dst.transfer); } } - if (dst.primaries == MP_CSP_PRIM_AUTO) { + if (dst.primaries == PL_COLOR_PRIM_UNKNOWN) { // The vast majority of people are on sRGB or BT.709 displays, so pick // this as the default output color space. - dst.primaries = MP_CSP_PRIM_BT_709; + dst.primaries = PL_COLOR_PRIM_BT_709; - if (src.primaries == MP_CSP_PRIM_BT_601_525 || - src.primaries == MP_CSP_PRIM_BT_601_625) + if (src.primaries == PL_COLOR_PRIM_BT_601_525 || + src.primaries == PL_COLOR_PRIM_BT_601_625) { // Since we auto-pick BT.601 and BT.709 based on the dimensions, // combined with the fact that they're very similar to begin with, @@ -2627,28 +2633,28 @@ static void pass_colormanage(struct gl_video *p, struct mp_colorspace src, } } - if (dst.gamma == MP_CSP_TRC_AUTO) { + if (dst.transfer == PL_COLOR_TRC_UNKNOWN) { // Most people seem to complain when the image is darker or brighter // than what they're "used to", so just avoid changing the gamma // altogether by default. The only exceptions to this rule apply to // very unusual TRCs, which even hardcode technoluddites would probably // not enjoy viewing unaltered. - dst.gamma = src.gamma; + dst.transfer = src.transfer; // Avoid outputting linear light or HDR content "by default". For these // just pick gamma 2.2 as a default, since it's a good estimate for // the response of typical displays - if (dst.gamma == MP_CSP_TRC_LINEAR || mp_trc_is_hdr(dst.gamma)) - dst.gamma = MP_CSP_TRC_GAMMA22; + if (dst.transfer == PL_COLOR_TRC_LINEAR || pl_color_space_is_hdr(&dst)) + dst.transfer = PL_COLOR_TRC_GAMMA22; } // If there's no specific signal peak known for the output display, infer // it from the chosen transfer function. Also normalize the src peak, in // case it was unknown if (!dst.hdr.max_luma) - dst.hdr.max_luma = mp_trc_nom_peak(dst.gamma) * MP_REF_WHITE; + dst.hdr.max_luma = pl_color_transfer_nominal_peak(dst.transfer) * MP_REF_WHITE; if (!src.hdr.max_luma) - src.hdr.max_luma = mp_trc_nom_peak(src.gamma) * MP_REF_WHITE; + src.hdr.max_luma = pl_color_transfer_nominal_peak(src.transfer) * MP_REF_WHITE; // Whitelist supported modes switch (p->opts.tone_map.curve) { @@ -2680,7 +2686,7 @@ static void pass_colormanage(struct gl_video *p, struct mp_colorspace src, } struct gl_tone_map_opts tone_map = p->opts.tone_map; - bool detect_peak = tone_map.compute_peak >= 0 && mp_trc_is_hdr(src.gamma) + bool detect_peak = tone_map.compute_peak >= 0 && pl_color_space_is_hdr(&src) && src.hdr.max_luma > dst.hdr.max_luma; if (detect_peak && !p->hdr_peak_ssbo) { @@ -2719,7 +2725,22 @@ static void pass_colormanage(struct gl_video *p, struct mp_colorspace src, } // Adapt from src to dst as necessary - pass_color_map(p->sc, p->use_linear && !osd, src, dst, &tone_map); + pass_color_map(p->sc, p->use_linear && !osd, src, dst, src_light, dst_light, &tone_map); + + if (!osd) { + struct mp_csp_params cparams = MP_CSP_PARAMS_DEFAULTS; + mp_csp_equalizer_state_get(p->video_eq, &cparams); + if (cparams.levels_out == PL_COLOR_LEVELS_UNKNOWN) + cparams.levels_out = PL_COLOR_LEVELS_FULL; + p->target_params = (struct mp_image_params){ + .imgfmt_name = p->fbo_format ? p->fbo_format->name : "unknown", + .w = p->texture_w, + .h = p->texture_h, + .color = dst, + .repr = {.sys = PL_COLOR_SYSTEM_RGB, .levels = cparams.levels_out}, + .rotate = p->image_params.rotate, + }; + } if (p->use_lut_3d && (flags & RENDER_SCREEN_COLOR)) { gl_sc_uniform_texture(p->sc, "lut_3d", p->lut_3d_texture); @@ -2735,7 +2756,7 @@ void gl_video_set_fb_depth(struct gl_video *p, int fb_depth) p->fb_depth = fb_depth; } -static void pass_dither(struct gl_video *p) +static void pass_dither(struct gl_video *p, const struct ra_fbo *fbo) { // Assume 8 bits per component if unknown. int dst_depth = p->fb_depth > 0 ? p->fb_depth : 8; @@ -2868,7 +2889,9 @@ static void pass_dither(struct gl_video *p) gl_sc_uniform_texture(p->sc, "dither", p->dither_texture); - GLSLF("vec2 dither_pos = gl_FragCoord.xy * 1.0/%d.0;\n", dither_size); + GLSLF("vec2 dither_coord = vec2(gl_FragCoord.x, %d.0 + %f * gl_FragCoord.y);", + fbo->flip ? fbo->tex->params.h : 0, fbo->flip ? -1.0 : 1.0); + GLSLF("vec2 dither_pos = dither_coord * 1.0/%d.0;\n", dither_size); if (p->opts.temporal_dither) { int phase = (p->frames_rendered / p->opts.temporal_dither_period) % 8u; @@ -2891,7 +2914,7 @@ static void pass_dither(struct gl_video *p) // Draws the OSD, in scene-referred colors.. If cms is true, subtitles are // instead adapted to the display's gamut. static void pass_draw_osd(struct gl_video *p, int osd_flags, int frame_flags, - double pts, struct mp_osd_res rect, struct ra_fbo fbo, + double pts, struct mp_osd_res rect, const struct ra_fbo *fbo, bool cms) { if (frame_flags & RENDER_FRAME_VF_SUBS) @@ -2910,20 +2933,21 @@ static void pass_draw_osd(struct gl_video *p, int osd_flags, int frame_flags, // When subtitles need to be color managed, assume they're in sRGB // (for lack of anything saner to do) if (cms) { - static const struct mp_colorspace csp_srgb = { - .primaries = MP_CSP_PRIM_BT_709, - .gamma = MP_CSP_TRC_SRGB, - .light = MP_CSP_LIGHT_DISPLAY, + static const struct pl_color_space csp_srgb = { + .primaries = PL_COLOR_PRIM_BT_709, + .transfer = PL_COLOR_TRC_SRGB, }; - pass_colormanage(p, csp_srgb, fbo.color_space, frame_flags, true); + pass_colormanage(p, csp_srgb, MP_CSP_LIGHT_DISPLAY, fbo->color_space, + frame_flags, true); } mpgl_osd_draw_finish(p->osd, n, p->sc, fbo); } timer_pool_stop(p->osd_timer); pass_describe(p, "drawing osd"); - pass_record(p, timer_pool_measure(p->osd_timer)); + struct mp_pass_perf perf = timer_pool_measure(p->osd_timer); + pass_record(p, &perf); } static float chroma_realign(int size, int pixel) @@ -3013,7 +3037,7 @@ static bool pass_render_frame(struct gl_video *p, struct mp_image *mpi, }; finish_pass_tex(p, &p->blend_subs_tex, rect.w, rect.h); struct ra_fbo fbo = { p->blend_subs_tex }; - pass_draw_osd(p, OSD_DRAW_SUB_ONLY, flags, vpts, rect, fbo, false); + pass_draw_osd(p, OSD_DRAW_SUB_ONLY, flags, vpts, rect, &fbo, false); pass_read_tex(p, p->blend_subs_tex); pass_describe(p, "blend subs video"); } @@ -3040,12 +3064,12 @@ static bool pass_render_frame(struct gl_video *p, struct mp_image *mpi, rect.mt *= scale[1]; rect.mb *= scale[1]; // We should always blend subtitles in non-linear light if (p->use_linear) { - pass_delinearize(p->sc, p->image_params.color.gamma); + pass_delinearize(p->sc, p->image_params.color.transfer); p->use_linear = false; } finish_pass_tex(p, &p->blend_subs_tex, p->texture_w, p->texture_h); struct ra_fbo fbo = { p->blend_subs_tex }; - pass_draw_osd(p, OSD_DRAW_SUB_ONLY, flags, vpts, rect, fbo, false); + pass_draw_osd(p, OSD_DRAW_SUB_ONLY, flags, vpts, rect, &fbo, false); pass_read_tex(p, p->blend_subs_tex); pass_describe(p, "blend subs"); } @@ -3055,7 +3079,7 @@ static bool pass_render_frame(struct gl_video *p, struct mp_image *mpi, return true; } -static void pass_draw_to_screen(struct gl_video *p, struct ra_fbo fbo, int flags) +static void pass_draw_to_screen(struct gl_video *p, const struct ra_fbo *fbo, int flags) { if (p->dumb_mode) pass_render_frame_dumb(p); @@ -3067,7 +3091,8 @@ static void pass_draw_to_screen(struct gl_video *p, struct ra_fbo fbo, int flags GLSL(color.rgb = pow(color.rgb, vec3(user_gamma));) } - pass_colormanage(p, p->image_params.color, fbo.color_space, flags, false); + pass_colormanage(p, p->image_params.color, p->image_params.light, + fbo->color_space, flags, false); // Since finish_pass_fbo doesn't work with compute shaders, and neither // does the checkerboard/dither code, we may need an indirection via @@ -3080,28 +3105,30 @@ static void pass_draw_to_screen(struct gl_video *p, struct ra_fbo fbo, int flags copy_image(p, &(int){0}, tmp); } - if (p->has_alpha){ - if (p->opts.alpha_mode == ALPHA_BLEND_TILES) { + if (p->has_alpha) { + if (p->opts.background == BACKGROUND_TILES) { // Draw checkerboard pattern to indicate transparency GLSLF("// transparency checkerboard\n"); - GLSL(bvec2 tile = lessThan(fract(gl_FragCoord.xy * 1.0/32.0), vec2(0.5));) + GLSLF("vec2 tile_coord = vec2(gl_FragCoord.x, %d.0 + %f * gl_FragCoord.y);", + fbo->flip ? fbo->tex->params.h : 0, fbo->flip ? -1.0 : 1.0); + GLSL(bvec2 tile = lessThan(fract(tile_coord * 1.0 / 32.0), vec2(0.5));) GLSL(vec3 background = vec3(tile.x == tile.y ? 0.93 : 0.87);) GLSL(color.rgb += background.rgb * (1.0 - color.a);) GLSL(color.a = 1.0;) - } else if (p->opts.alpha_mode == ALPHA_BLEND) { + } else if (p->opts.background == BACKGROUND_COLOR) { // Blend into background color (usually black) - struct m_color c = p->opts.background; + struct m_color c = p->opts.background_color; GLSLF("vec4 background = vec4(%f, %f, %f, %f);\n", c.r / 255.0, c.g / 255.0, c.b / 255.0, c.a / 255.0); - GLSL(color.rgb += background.rgb * (1.0 - color.a);) - GLSL(color.a = background.a;) + GLSL(color += background * (1.0 - color.a);) + GLSL(color.rgb *= vec3(color.a);); } } pass_opt_hook_point(p, "OUTPUT", NULL); if (flags & RENDER_SCREEN_COLOR) - pass_dither(p); + pass_dither(p, fbo); pass_describe(p, "output to screen"); finish_pass_fbo(p, fbo, false, &p->dst_rect); } @@ -3122,7 +3149,7 @@ static bool update_surface(struct gl_video *p, struct mp_image *mpi, // because mixing in compressed light artificially darkens the results if (!p->use_linear) { p->use_linear = true; - pass_linearize(p->sc, p->image_params.color.gamma); + pass_linearize(p->sc, p->image_params.color.transfer); } finish_pass_tex(p, &surf->tex, vp_w, vp_h); @@ -3134,7 +3161,7 @@ static bool update_surface(struct gl_video *p, struct mp_image *mpi, // Draws an interpolate frame to fbo, based on the frame timing in t // flags: bit set of RENDER_FRAME_* flags static void gl_video_interpolate_frame(struct gl_video *p, struct vo_frame *t, - struct ra_fbo fbo, int flags) + const struct ra_fbo *fbo, int flags) { bool is_new = false; @@ -3201,7 +3228,7 @@ static void gl_video_interpolate_frame(struct gl_video *p, struct vo_frame *t, struct mp_image *f = t->frames[i]; uint64_t f_id = t->frame_id + i; - if (!mp_image_params_equal(&f->params, &p->real_image_params)) + if (!mp_image_params_static_equal(&f->params, &p->real_image_params)) continue; if (f_id > p->surfaces[p->surface_idx].id) { @@ -3306,11 +3333,11 @@ static void gl_video_interpolate_frame(struct gl_video *p, struct vo_frame *t, } void gl_video_render_frame(struct gl_video *p, struct vo_frame *frame, - struct ra_fbo fbo, int flags) + const struct ra_fbo *fbo, int flags) { gl_video_update_options(p); - struct mp_rect target_rc = {0, 0, fbo.tex->params.w, fbo.tex->params.h}; + struct mp_rect target_rc = {0, 0, fbo->tex->params.w, fbo->tex->params.h}; p->broken_frame = false; @@ -3318,12 +3345,15 @@ void gl_video_render_frame(struct gl_video *p, struct vo_frame *frame, struct m_color c = p->clear_color; float clear_color[4] = {c.r / 255.0, c.g / 255.0, c.b / 255.0, c.a / 255.0}; - p->ra->fns->clear(p->ra, fbo.tex, clear_color, &target_rc); + clear_color[0] *= clear_color[3]; + clear_color[1] *= clear_color[3]; + clear_color[2] *= clear_color[3]; + p->ra->fns->clear(p->ra, fbo->tex, clear_color, &target_rc); if (p->hwdec_overlay) { if (has_frame) { float *color = p->hwdec_overlay->overlay_colorkey; - p->ra->fns->clear(p->ra, fbo.tex, color, &p->dst_rect); + p->ra->fns->clear(p->ra, fbo->tex, color, &p->dst_rect); } p->hwdec_overlay->driver->overlay_frame(p->hwdec_overlay, frame->current, @@ -3364,43 +3394,41 @@ void gl_video_render_frame(struct gl_video *p, struct vo_frame *frame, // For the non-interpolation case, we draw to a single "cache" // texture to speed up subsequent re-draws (if any exist) - struct ra_fbo dest_fbo = fbo; bool repeats = frame->num_vsyncs > 1 && frame->display_synced; + bool r = false; if ((repeats || frame->still) && !p->dumb_mode && - (p->ra->caps & RA_CAP_BLIT) && fbo.tex->params.blit_dst) + (p->ra->caps & RA_CAP_BLIT) && fbo->tex->params.blit_dst) { // Attempt to use the same format as the destination FBO // if possible. Some RAs use a wrapped dummy format here, // so fall back to the fbo_format in that case. - const struct ra_format *fmt = fbo.tex->params.format; + const struct ra_format *fmt = fbo->tex->params.format; if (fmt->dummy_format) fmt = p->fbo_format; - - bool r = ra_tex_resize(p->ra, p->log, &p->output_tex, - fbo.tex->params.w, fbo.tex->params.h, - fmt); - if (r) { - dest_fbo = (struct ra_fbo) { p->output_tex }; - p->output_tex_valid = true; - } + r = ra_tex_resize(p->ra, p->log, &p->output_tex, + fbo->tex->params.w, fbo->tex->params.h, + fmt); } + const struct ra_fbo *dest_fbo = r ? &(struct ra_fbo) { p->output_tex } : fbo; + p->output_tex_valid = r; pass_draw_to_screen(p, dest_fbo, flags); } // "output tex valid" and "output tex needed" are equivalent - if (p->output_tex_valid && fbo.tex->params.blit_dst) { + if (p->output_tex_valid && fbo->tex->params.blit_dst) { pass_info_reset(p, true); pass_describe(p, "redraw cached frame"); struct mp_rect src = p->dst_rect; struct mp_rect dst = src; - if (fbo.flip) { - dst.y0 = fbo.tex->params.h - src.y0; - dst.y1 = fbo.tex->params.h - src.y1; + if (fbo->flip) { + dst.y0 = fbo->tex->params.h - src.y0; + dst.y1 = fbo->tex->params.h - src.y1; } timer_pool_start(p->blit_timer); - p->ra->fns->blit(p->ra, fbo.tex, p->output_tex, &dst, &src); + p->ra->fns->blit(p->ra, fbo->tex, p->output_tex, &dst, &src); timer_pool_stop(p->blit_timer); - pass_record(p, timer_pool_measure(p->blit_timer)); + struct mp_pass_perf perf = timer_pool_measure(p->blit_timer); + pass_record(p, &perf); } } } @@ -3431,7 +3459,7 @@ done: // Make the screen solid blue to make it visually clear that an // error has occurred float color[4] = {0.0, 0.05, 0.5, 1.0}; - p->ra->fns->clear(p->ra, fbo.tex, color, &target_rc); + p->ra->fns->clear(p->ra, fbo->tex, color, &target_rc); } p->frames_rendered++; @@ -3522,7 +3550,7 @@ void gl_video_screenshot(struct gl_video *p, struct vo_frame *frame, flags |= RENDER_FRAME_OSD; if (args->scaled) flags |= RENDER_SCREEN_COLOR; - gl_video_render_frame(p, nframe, (struct ra_fbo){target}, flags); + gl_video_render_frame(p, nframe, &(struct ra_fbo){target}, flags); res = mp_image_alloc(mpfmt, params.w, params.h); if (!res) @@ -3636,7 +3664,8 @@ static bool pass_upload_image(struct gl_video *p, struct mp_image *mpi, uint64_t timer_pool_start(p->upload_timer); bool ok = ra_hwdec_mapper_map(p->hwdec_mapper, vimg->mpi) >= 0; timer_pool_stop(p->upload_timer); - pass_record(p, timer_pool_measure(p->upload_timer)); + struct mp_pass_perf perf = timer_pool_measure(p->upload_timer); + pass_record(p, &perf); vimg->hwdec_mapped = true; if (ok) { @@ -3708,7 +3737,8 @@ static bool pass_upload_image(struct gl_video *p, struct mp_image *mpi, uint64_t bool using_pbo = p->ra->use_pbo || !(p->ra->caps & RA_CAP_DIRECT_UPLOAD); const char *mode = p->using_dr_path ? "DR" : using_pbo ? "PBO" : "naive"; pass_describe(p, "upload frame (%s)", mode); - pass_record(p, timer_pool_measure(p->upload_timer)); + struct mp_pass_perf perf = timer_pool_measure(p->upload_timer); + pass_record(p, &perf); return true; @@ -3806,9 +3836,8 @@ static void check_gl_features(struct gl_video *p) p->opts.dither_algo = DITHER_NONE; MP_WARN(p, "Disabling dithering (no gl_FragCoord).\n"); } - if (!have_fragcoord && p->opts.alpha_mode == ALPHA_BLEND_TILES) { - p->opts.alpha_mode = ALPHA_BLEND; - // Verbose, since this is the default setting + if (!have_fragcoord && p->opts.background == BACKGROUND_TILES) { + p->opts.background = BACKGROUND_COLOR; MP_VERBOSE(p, "Disabling alpha checkerboard (no gl_FragCoord).\n"); } if (!have_fbo && have_compute) { @@ -3864,9 +3893,9 @@ static void check_gl_features(struct gl_video *p) .gamma_auto = p->opts.gamma_auto, .pbo = p->opts.pbo, .fbo_format = p->opts.fbo_format, - .alpha_mode = p->opts.alpha_mode, - .use_rectangle = p->opts.use_rectangle, .background = p->opts.background, + .use_rectangle = p->opts.use_rectangle, + .background_color = p->opts.background_color, .dither_algo = p->opts.dither_algo, .dither_depth = p->opts.dither_depth, .dither_size = p->opts.dither_size, @@ -3913,8 +3942,8 @@ static void check_gl_features(struct gl_video *p) } } - int use_cms = p->opts.target_prim != MP_CSP_PRIM_AUTO || - p->opts.target_trc != MP_CSP_TRC_AUTO || p->use_lut_3d; + int use_cms = p->opts.target_prim != PL_COLOR_PRIM_UNKNOWN || + p->opts.target_trc != PL_COLOR_TRC_UNKNOWN || p->use_lut_3d; // mix() is needed for some gamma functions if (!have_mglsl && (p->opts.linear_downscaling || @@ -3926,8 +3955,8 @@ static void check_gl_features(struct gl_video *p) MP_WARN(p, "Disabling linear/sigmoid scaling (GLSL version too old).\n"); } if (!have_mglsl && use_cms) { - p->opts.target_prim = MP_CSP_PRIM_AUTO; - p->opts.target_trc = MP_CSP_TRC_AUTO; + p->opts.target_prim = PL_COLOR_PRIM_UNKNOWN; + p->opts.target_trc = PL_COLOR_TRC_UNKNOWN; p->use_lut_3d = false; MP_WARN(p, "Disabling color management (GLSL version too old).\n"); } @@ -4022,7 +4051,7 @@ void gl_video_config(struct gl_video *p, struct mp_image_params *params) unmap_overlay(p); unref_current_image(p); - if (!mp_image_params_equal(&p->real_image_params, params)) { + if (!mp_image_params_static_equal(&p->real_image_params, params)) { uninit_video(p); p->real_image_params = *params; p->image_params = *params; @@ -4117,7 +4146,7 @@ static void reinit_from_options(struct gl_video *p) p->opts = *(struct gl_video_opts *)p->opts_cache->opts; if (!p->force_clear_color) - p->clear_color = p->opts.background; + p->clear_color = p->opts.background_color; check_gl_features(p); uninit_rendering(p); @@ -4362,3 +4391,8 @@ void gl_video_load_hwdecs_for_img_fmt(struct gl_video *p, struct mp_hwdec_device assert(p->hwdec_ctx.ra_ctx); ra_hwdec_ctx_load_fmt(&p->hwdec_ctx, devs, params); } + +struct mp_image_params *gl_video_get_target_params_ptr(struct gl_video *p) +{ + return &p->target_params; +} |