From fae66eded406cb4f28fbbfe48b6e060b735595a3 Mon Sep 17 00:00:00 2001 From: Niklas Haas <git@haasn.xyz> Date: Wed, 20 May 2020 04:55:31 +0200 Subject: [PATCH] renderer: use a dynamic list of FBOs instead of hard-coding them The current approach was to pair each FBO with its use, "semantically". The intent was to minimize the number of "reallocations" that would be required if the number of passes changed dynamically (e.g. as the renderer options changed). However, this is not only an unrealistic design goal to optimize for (users can use separate pl_renderers for wildly different purposes, and for a single conceptual video stream it doesn't really matter), but also, it gets in the way of a planned refactor I have concerning `struct img`. Change this to make all FBOs dynamically allocated. The current implementation simply uses a counter, but a more advanced implementation could use a pool of textures and find ones that have matching sizes before recreating ones that don't. The API shouldn't change as a result of this. --- src/renderer.c | 181 +++++++++++++++++++++++-------------------------- 1 file changed, 84 insertions(+), 97 deletions(-) diff --git a/src/renderer.c b/src/renderer.c index 0cb1441e9..d61b53bdf 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -65,12 +65,8 @@ struct pl_renderer { struct pl_shader_obj *dither_state; struct pl_shader_obj *lut3d_state; struct pl_shader_obj *grain_state[4]; - const struct pl_tex *main_scale_fbo; - const struct pl_tex *deband_fbos[4]; - const struct pl_tex *grain_fbos[4]; - const struct pl_tex *output_fbo; - const struct pl_tex **hook_fbos; - int num_hook_fbos; + const struct pl_tex **fbos; + int num_fbos; struct sampler samplers[SCALER_COUNT]; struct sampler *osd_samplers; int num_osd_samplers; @@ -161,14 +157,8 @@ void pl_renderer_destroy(struct pl_renderer **p_rr) return; // Free all intermediate FBOs - pl_tex_destroy(rr->gpu, &rr->main_scale_fbo); - pl_tex_destroy(rr->gpu, &rr->output_fbo); - for (int i = 0; i < PL_ARRAY_SIZE(rr->deband_fbos); i++) - pl_tex_destroy(rr->gpu, &rr->deband_fbos[i]); - for (int i = 0; i < PL_ARRAY_SIZE(rr->grain_fbos); i++) - pl_tex_destroy(rr->gpu, &rr->grain_fbos[i]); - for (int i = 0; i < rr->num_hook_fbos; i++) - pl_tex_destroy(rr->gpu, &rr->hook_fbos[i]); + for (int i = 0; i < rr->num_fbos; i++) + pl_tex_destroy(rr->gpu, &rr->fbos[i]); // Free all shader resource objects pl_shader_obj_destroy(&rr->peak_detect_state); @@ -232,6 +222,29 @@ struct img { int comps; }; +struct pass_state { + // Pointer back to the renderer itself, for callbacks + struct pl_renderer *rr; + + // Represents the "current" image which we're in the process of rendering. + // This is initially set by pass_read_image, and all of the subsequent + // rendering steps will mutate this in-place. + struct img img; + + // Represents the "reference rect". Canonically, this is functionallity + // equivalent to `pl_image.src_rect`, but both guaranteed to be valid, and + // also updates as the refplane evolves (e.g. due to user hook prescalers) + struct pl_rect2df ref_rect; + + // Cached copies of the `image` / `target` for this rendering pass, + // corrected to make sure all rects etc. are properly defaulted/inferred. + struct pl_image image; + struct pl_render_target target; + + // Index into `rr->fbos` + int fbo_index; +}; + // Returns the texture parameters an `img` would have if finalized static inline struct pl_tex_params img_params(struct pl_renderer *rr, struct img *img, @@ -251,13 +264,15 @@ static inline struct pl_tex_params img_params(struct pl_renderer *rr, }; } -static const struct pl_tex *finalize_img(struct pl_renderer *rr, - struct img *img, - const struct pl_fmt *fmt, - const struct pl_tex **tex) +static const struct pl_tex *finalize_img(struct pass_state *pass, struct img *img) { - struct pl_tex_params tex_params = img_params(rr, img, fmt); - bool ok = pl_tex_recreate(rr->gpu, tex, &tex_params); + struct pl_renderer *rr = pass->rr; + if (pass->fbo_index == rr->num_fbos) + TARRAY_APPEND(rr, rr->fbos, rr->num_fbos, NULL); + + const struct pl_tex **ptex = &rr->fbos[pass->fbo_index++]; + struct pl_tex_params tex_params = img_params(rr, img, rr->fbofmt); + bool ok = pl_tex_recreate(rr->gpu, ptex, &tex_params); if (!ok) { PL_ERR(rr, "Failed creating FBO texture! Disabling advanced rendering.."); rr->fbofmt = NULL; @@ -267,7 +282,7 @@ static const struct pl_tex *finalize_img(struct pl_renderer *rr, ok = pl_dispatch_finish(rr->dp, &(struct pl_dispatch_params) { .shader = &img->sh, - .target = *tex, + .target = *ptex, }); if (!ok) { @@ -275,32 +290,9 @@ static const struct pl_tex *finalize_img(struct pl_renderer *rr, return NULL; } - return *tex; + return *ptex; } -struct pass_state { - // Pointer back to the renderer itself, for callbacks - struct pl_renderer *rr; - - // Represents the "current" image which we're in the process of rendering. - // This is initially set by pass_read_image, and all of the subsequent - // rendering steps will mutate this in-place. - struct img img; - - // Represents the "reference rect". Canonically, this is functionallity - // equivalent to `pl_image.src_rect`, but both guaranteed to be valid, and - // also updates as the refplane evolves (e.g. due to user hook prescalers) - struct pl_rect2df ref_rect; - - // Cached copies of the `image` / `target` for this rendering pass, - // corrected to make sure all rects etc. are properly defaulted/inferred. - struct pl_image image; - struct pl_render_target target; - - // Index into `rr->hook_fbos` - int hook_index; -}; - enum sampler_type { SAMPLER_DIRECT, // texture()'s built in sampling SAMPLER_BICUBIC, // fast bicubic scaling @@ -362,7 +354,7 @@ static struct sampler_info sample_src_info(struct pl_renderer *rr, return info; } -static void dispatch_sampler(struct pl_renderer *rr, struct pl_shader *sh, +static void dispatch_sampler(struct pass_state *pass, struct pl_shader *sh, struct sampler *sampler, const struct pl_render_params *params, const struct pl_sample_src *src) @@ -370,6 +362,7 @@ static void dispatch_sampler(struct pl_renderer *rr, struct pl_shader *sh, if (!sampler) goto fallback; + struct pl_renderer *rr = pass->rr; struct sampler_info info = sample_src_info(rr, src, params); struct pl_shader_obj **lut = NULL; const struct pl_tex **sep_fbo = NULL; @@ -425,7 +418,7 @@ static void dispatch_sampler(struct pl_renderer *rr, struct pl_shader *sh, }; struct pl_sample_src src2 = *src; - src2.tex = finalize_img(rr, &img, FBOFMT, sep_fbo); + src2.tex = finalize_img(pass, &img); src2.scale = 1.0; ok = src2.tex && pl_shader_sample_ortho(sh, PL_SEP_HORIZ, &src2, &fparams); } @@ -444,12 +437,13 @@ fallback: pl_shader_sample_direct(sh, src); } -static void draw_overlays(struct pl_renderer *rr, const struct pl_tex *fbo, +static void draw_overlays(struct pass_state *pass, const struct pl_tex *fbo, const struct pl_overlay *overlays, int num, struct pl_color_space color, bool use_sigmoid, struct pl_transform2x2 *scale, const struct pl_render_params *params) { + struct pl_renderer *rr = pass->rr; if (num <= 0 || rr->disable_overlay) return; @@ -497,7 +491,7 @@ static void draw_overlays(struct pl_renderer *rr, const struct pl_tex *fbo, sampler = NULL; struct pl_shader *sh = pl_dispatch_begin(rr->dp); - dispatch_sampler(rr, sh, sampler, params, &src); + dispatch_sampler(pass, sh, sampler, params, &src); GLSL("vec4 osd_color;\n"); for (int c = 0; c < src.components; c++) { @@ -561,12 +555,12 @@ static const struct pl_tex *get_hook_tex(void *priv, int width, int height) struct pass_state *pass = priv; struct pl_renderer *rr = pass->rr; - int idx = pass->hook_index; - if (idx == rr->num_hook_fbos) - TARRAY_APPEND(rr, rr->hook_fbos, rr->num_hook_fbos, NULL); + int idx = pass->fbo_index; + if (idx == rr->num_fbos) + TARRAY_APPEND(rr, rr->fbos, rr->num_fbos, NULL); pl_assert(rr->fbofmt); - bool ok = pl_tex_recreate(rr->gpu, &rr->hook_fbos[idx], &(struct pl_tex_params) { + bool ok = pl_tex_recreate(rr->gpu, &rr->fbos[idx], &(struct pl_tex_params) { .w = width, .h = height, .format = rr->fbofmt, @@ -582,18 +576,19 @@ static const struct pl_tex *get_hook_tex(void *priv, int width, int height) if (!ok) return NULL; - pass->hook_index++; - return rr->hook_fbos[idx]; + pass->fbo_index++; + return rr->fbos[idx]; } // if `ptex` is unspecified, uses `img->sh` instead // // Returns if any hook was applied (even if there were errors) -static bool pass_hook(struct pl_renderer *rr, struct pass_state *pass, - struct img *img, enum pl_hook_stage stage, +static bool pass_hook(struct pass_state *pass, struct img *img, + enum pl_hook_stage stage, const struct pl_tex **ptex, const struct pl_render_params *params) { + struct pl_renderer *rr = pass->rr; if (!rr->fbofmt || rr->disable_hooks) return false; @@ -630,11 +625,7 @@ static bool pass_hook(struct pl_renderer *rr, struct pass_state *pass, case PL_HOOK_SIG_TEX: { if (!cur_tex) { - int idx = pass->hook_index++; - if (idx == rr->num_hook_fbos) - TARRAY_APPEND(rr, rr->hook_fbos, rr->num_hook_fbos, NULL); - - cur_tex = finalize_img(rr, img, rr->fbofmt, &rr->hook_fbos[idx]); + cur_tex = finalize_img(pass, img); if (!cur_tex) { PL_ERR(rr, "Failed dispatching shader prior to hook!"); goto error; @@ -726,11 +717,7 @@ static bool pass_hook(struct pl_renderer *rr, struct pass_state *pass, if (ptex) { if (!cur_tex) { - int idx = pass->hook_index++; - if (idx == rr->num_hook_fbos) - TARRAY_APPEND(rr, rr->hook_fbos, rr->num_hook_fbos, NULL); - - cur_tex = finalize_img(rr, img, rr->fbofmt, &rr->hook_fbos[idx]); + cur_tex = finalize_img(pass, img); if (!cur_tex) { PL_ERR(rr, "Failed dispatching shader prior to hook!"); goto error; @@ -771,11 +758,12 @@ enum { DEBAND_SCALED, // debanding took care of scaling as well }; -static int deband_src(struct pl_renderer *rr, struct pl_shader *psh, - struct pl_sample_src *psrc, const struct pl_tex **fbo, +static int deband_src(struct pass_state *pass, struct pl_shader *psh, + struct pl_sample_src *psrc, const struct pl_image *image, const struct pl_render_params *params) { + struct pl_renderer *rr = pass->rr; if (rr->disable_debanding || !params->deband_params) return DEBAND_NOOP; @@ -837,7 +825,7 @@ static int deband_src(struct pl_renderer *rr, struct pl_shader *psh, .h = src->new_h, }; - const struct pl_tex *new = finalize_img(rr, &img, FBOFMT, fbo); + const struct pl_tex *new = finalize_img(pass, &img); if (!new) { PL_ERR(rr, "Failed dispatching debanding shader.. disabling debanding!"); rr->disable_debanding = true; @@ -991,12 +979,13 @@ static enum plane_type detect_plane_type(const struct plane_state *st) } // Returns true if grain was applied -static bool plane_av1_grain(struct pl_renderer *rr, int plane_idx, +static bool plane_av1_grain(struct pass_state *pass, int plane_idx, struct plane_state *st, const struct pl_tex *ref_tex, const struct pl_image *image, const struct pl_render_params *params) { + struct pl_renderer *rr = pass->rr; if (rr->disable_grain) return false; @@ -1035,8 +1024,7 @@ static bool plane_av1_grain(struct pl_renderer *rr, int plane_idx, .h = plane->texture->params.h, }; - new_tex = finalize_img(rr, &grain_img, FBOFMT, - &rr->grain_fbos[plane_idx]); + new_tex = finalize_img(pass, &grain_img); ok = !!new_tex; sh = NULL; } @@ -1054,8 +1042,7 @@ static bool plane_av1_grain(struct pl_renderer *rr, int plane_idx, } // Returns true if any user hooks were executed -static bool plane_user_hooks(struct pl_renderer *rr, struct pass_state *pass, - struct plane_state *st, const struct pl_image *image, +static bool plane_user_hooks(struct pass_state *pass, struct plane_state *st, const struct pl_render_params *params) { static const enum pl_hook_stage plane_stages[] = { @@ -1066,7 +1053,7 @@ static bool plane_user_hooks(struct pl_renderer *rr, struct pass_state *pass, [PLANE_XYZ] = PL_HOOK_XYZ_INPUT, }; - return pass_hook(rr, pass, &st->img, plane_stages[st->type], + return pass_hook(pass, &st->img, plane_stages[st->type], &st->plane.texture, params); } @@ -1184,12 +1171,12 @@ static bool pass_read_image(struct pl_renderer *rr, struct pass_state *pass, // intent of the spec (which is to apply synthesis effectively during // decoding) - if (plane_av1_grain(rr, i, st, ref_tex, image, params)) { + if (plane_av1_grain(pass, i, st, ref_tex, image, params)) { PL_TRACE(rr, "After AV1 grain:"); log_plane_info(rr, st); } - if (plane_user_hooks(rr, pass, st, image, params)) { + if (plane_user_hooks(pass, st, params)) { PL_TRACE(rr, "After user hooks:"); log_plane_info(rr, st); } @@ -1229,8 +1216,8 @@ static bool pass_read_image(struct pl_renderer *rr, struct pass_state *pass, src.rect.x1, src.rect.y1); struct pl_shader *psh = pl_dispatch_begin_ex(rr->dp, true); - if (deband_src(rr, psh, &src, &rr->deband_fbos[i], image, params) != DEBAND_SCALED) - dispatch_sampler(rr, psh, &rr->samplers[i], params, &src); + if (deband_src(pass, psh, &src, image, params) != DEBAND_SCALED) + dispatch_sampler(pass, psh, &rr->samplers[i], params, &src); ident_t sub = sh_subpass(sh, psh); if (!sub) { @@ -1279,11 +1266,11 @@ static bool pass_read_image(struct pl_renderer *rr, struct pass_state *pass, // Update the reference rect to our adjusted image coordinates pass->ref_rect = pass->img.rect; - pass_hook(rr, pass, &pass->img, PL_HOOK_NATIVE, NULL, params); + pass_hook(pass, &pass->img, PL_HOOK_NATIVE, NULL, params); // Convert the image colorspace pl_shader_decode_color(sh, &pass->img.repr, params->color_adjustment); - pass_hook(rr, pass, &pass->img, PL_HOOK_RGB, NULL, params); + pass_hook(pass, &pass->img, PL_HOOK_RGB, NULL, params); // HDR peak detection, do this as early as possible hdr_update_peak(rr, sh, pass, params); @@ -1359,17 +1346,17 @@ static bool pass_scale_main(struct pl_renderer *rr, struct pass_state *pass, if (use_linear) { pl_shader_linearize(img->sh, img->color.transfer); img->color.transfer = PL_COLOR_TRC_LINEAR; - pass_hook(rr, pass, &pass->img, PL_HOOK_LINEAR, NULL, params); + pass_hook(pass, &pass->img, PL_HOOK_LINEAR, NULL, params); } if (use_sigmoid) { pl_shader_sigmoidize(img->sh, params->sigmoid_params); - pass_hook(rr, pass, &pass->img, PL_HOOK_SIGMOID, NULL, params); + pass_hook(pass, &pass->img, PL_HOOK_SIGMOID, NULL, params); } - pass_hook(rr, pass, &pass->img, PL_HOOK_PRE_OVERLAY, NULL, params); + pass_hook(pass, &pass->img, PL_HOOK_PRE_OVERLAY, NULL, params); - src.tex = finalize_img(rr, img, FBOFMT, &rr->main_scale_fbo); + src.tex = finalize_img(pass, img); if (!src.tex) return false; @@ -1389,13 +1376,13 @@ static bool pass_scale_main(struct pl_renderer *rr, struct pass_state *pass, }; } - draw_overlays(rr, src.tex, image->overlays, image->num_overlays, + draw_overlays(pass, src.tex, image->overlays, image->num_overlays, img->color, use_sigmoid, &tf, params); - pass_hook(rr, pass, &pass->img, PL_HOOK_PRE_KERNEL, &src.tex, params); + pass_hook(pass, &pass->img, PL_HOOK_PRE_KERNEL, &src.tex, params); struct pl_shader *sh = pl_dispatch_begin_ex(rr->dp, true); - dispatch_sampler(rr, sh, &rr->samplers[SCALER_MAIN], params, &src); + dispatch_sampler(pass, sh, &rr->samplers[SCALER_MAIN], params, &src); pass->img = (struct img) { .sh = sh, .w = src.new_w, @@ -1406,12 +1393,12 @@ static bool pass_scale_main(struct pl_renderer *rr, struct pass_state *pass, .comps = img->comps, }; - pass_hook(rr, pass, &pass->img, PL_HOOK_POST_KERNEL, NULL, params); + pass_hook(pass, &pass->img, PL_HOOK_POST_KERNEL, NULL, params); if (use_sigmoid) pl_shader_unsigmoidize(sh, params->sigmoid_params); - pass_hook(rr, pass, &pass->img, PL_HOOK_SCALED, NULL, params); + pass_hook(pass, &pass->img, PL_HOOK_SCALED, NULL, params); return true; } @@ -1493,20 +1480,20 @@ fallback: bool is_comp = pl_shader_is_compute(sh); if (is_comp && !fbo->params.storable) { - bool ok = finalize_img(rr, &pass->img, FBOFMT, &rr->output_fbo); - if (!ok) { + const struct pl_tex *output_tex = finalize_img(pass, &pass->img); + if (!output_tex) { PL_ERR(rr, "Failed dispatching compute shader to intermediate FBO?"); return false; } sh = pass->img.sh = pl_dispatch_begin(rr->dp); pl_shader_sample_direct(sh, &(struct pl_sample_src) { - .tex = rr->output_fbo, + .tex = output_tex, }); } pl_shader_encode_color(sh, &target->repr); - pass_hook(rr, pass, &pass->img, PL_HOOK_OUTPUT, NULL, params); + pass_hook(pass, &pass->img, PL_HOOK_OUTPUT, NULL, params); // FIXME: What if this ends up being a compute shader?? Should we do the // is_compute unredirection *after* encode_color, or will that fuck up // the bit depth? @@ -1614,12 +1601,12 @@ bool pl_render_image(struct pl_renderer *rr, const struct pl_image *pimage, }, }; - draw_overlays(rr, target->fbo, image->overlays, image->num_overlays, + draw_overlays(&pass, target->fbo, image->overlays, image->num_overlays, target->color, false, &scale, params); } // Draw the final output overlays - draw_overlays(rr, target->fbo, target->overlays, target->num_overlays, + draw_overlays(&pass, target->fbo, target->overlays, target->num_overlays, target->color, false, NULL, params); return true; -- GitLab