diff --git a/video/out/opengl/osd.c b/video/out/opengl/osd.c index 967b81e535..f7c325d1db 100644 --- a/video/out/opengl/osd.c +++ b/video/out/opengl/osd.c @@ -54,7 +54,6 @@ struct mpgl_osd_part { enum sub_bitmap_format format; int change_id; struct ra_tex *texture; - struct tex_upload pbo; int w, h; int num_subparts; int prev_num_subparts; @@ -71,7 +70,6 @@ struct mpgl_osd { const struct ra_format *fmt_table[SUBBITMAP_COUNT]; bool formats[SUBBITMAP_COUNT]; bool change_flag; // for reporting to API user only - bool want_pbo; // temporary int stereo_mode; struct mp_osd_res osd_res; @@ -79,7 +77,7 @@ struct mpgl_osd { }; struct mpgl_osd *mpgl_osd_init(struct ra *ra, struct mp_log *log, - struct osd_state *osd, bool want_pbo) + struct osd_state *osd) { struct mpgl_osd *ctx = talloc_ptrtype(NULL, ctx); *ctx = (struct mpgl_osd) { @@ -88,7 +86,6 @@ struct mpgl_osd *mpgl_osd_init(struct ra *ra, struct mp_log *log, .ra = ra, .change_flag = true, .scratch = talloc_zero_size(ctx, 1), - .want_pbo = want_pbo, }; ctx->fmt_table[SUBBITMAP_LIBASS] = ra_find_unorm_format(ra, 1, 1); @@ -111,7 +108,6 @@ void mpgl_osd_destroy(struct mpgl_osd *ctx) for (int n = 0; n < MAX_OSD_PARTS; n++) { struct mpgl_osd_part *p = ctx->parts[n]; ra_tex_free(ctx->ra, &p->texture); - tex_upload_uninit(ctx->ra, &p->pbo); } talloc_free(ctx); } @@ -180,7 +176,7 @@ static bool upload_osd(struct mpgl_osd *ctx, struct mpgl_osd_part *osd, .stride = imgs->packed->stride[0], }; - ok = tex_upload(ra, &osd->pbo, ctx->want_pbo, ¶ms); + ok = ra->fns->tex_upload(ra, ¶ms); done: return ok; diff --git a/video/out/opengl/osd.h b/video/out/opengl/osd.h index b5618ce5f0..6c2b886de3 100644 --- a/video/out/opengl/osd.h +++ b/video/out/opengl/osd.h @@ -9,7 +9,7 @@ #include "sub/osd.h" struct mpgl_osd *mpgl_osd_init(struct ra *ra, struct mp_log *log, - struct osd_state *osd, bool want_pbo); + struct osd_state *osd); void mpgl_osd_destroy(struct mpgl_osd *ctx); void mpgl_osd_generate(struct mpgl_osd *ctx, struct mp_osd_res res, double pts, diff --git a/video/out/opengl/ra.h b/video/out/opengl/ra.h index eee1728bba..8658a0737c 100644 --- a/video/out/opengl/ra.h +++ b/video/out/opengl/ra.h @@ -30,6 +30,11 @@ struct ra { // formats should have a lower index. (E.g. GLES3 should put rg8 before la.) struct ra_format **formats; int num_formats; + + // Accelerate texture uploads via an extra PBO even when + // RA_CAP_DIRECT_UPLOAD is supported. This is basically only relevant for + // OpenGL. Set by the RA user. + bool use_pbo; }; enum { diff --git a/video/out/opengl/ra_gl.c b/video/out/opengl/ra_gl.c index f63bbe3ea5..e31948c2b1 100644 --- a/video/out/opengl/ra_gl.c +++ b/video/out/opengl/ra_gl.c @@ -15,6 +15,7 @@ struct ra_gl { // For ra_tex.priv struct ra_tex_gl { + struct ra_buf_pool pbo; // for ra.use_pbo bool own_objects; GLenum target; GLuint texture; // 0 if no texture data associated @@ -218,6 +219,8 @@ static void gl_tex_destroy(struct ra *ra, struct ra_tex *tex) GL *gl = ra_gl_get(ra); struct ra_tex_gl *tex_gl = tex->priv; + ra_buf_pool_uninit(ra, &tex_gl->pbo); + if (tex_gl->own_objects) { if (tex_gl->fbo) gl->DeleteFramebuffers(1, &tex_gl->fbo); @@ -435,6 +438,9 @@ static bool gl_tex_upload(struct ra *ra, assert(tex->params.host_mutable); assert(!params->buf || !params->src); + if (ra->use_pbo && !params->buf) + return ra_tex_upload_pbo(ra, &tex_gl->pbo, params); + const void *src = params->src; if (buf) { gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, buf_gl->buffer); diff --git a/video/out/opengl/utils.c b/video/out/opengl/utils.c index 5ef4349fff..e7fce62662 100644 --- a/video/out/opengl/utils.c +++ b/video/out/opengl/utils.c @@ -39,6 +39,91 @@ void gl_transform_ortho_fbodst(struct gl_transform *t, struct fbodst fbo) gl_transform_ortho(t, 0, fbo.tex->params.w, 0, fbo.tex->params.h * y_dir); } +void ra_buf_pool_uninit(struct ra *ra, struct ra_buf_pool *pool) +{ + for (int i = 0; i < pool->num_buffers; i++) + ra_buf_free(ra, &pool->buffers[i]); + + talloc_free(pool->buffers); + *pool = (struct ra_buf_pool){0}; +} + +static bool ra_buf_params_compatible(const struct ra_buf_params *new, + const struct ra_buf_params *old) +{ + return new->type == old->type && + new->size <= old->size && + new->host_mapped == old->host_mapped && + new->host_mutable == old->host_mutable; +} + +static bool ra_buf_pool_grow(struct ra *ra, struct ra_buf_pool *pool) +{ + struct ra_buf *buf = ra_buf_create(ra, &pool->current_params); + if (!buf) + return false; + + MP_TARRAY_INSERT_AT(NULL, pool->buffers, pool->num_buffers, pool->index, buf); + MP_VERBOSE(ra, "Resized buffer pool to size %d\n", pool->num_buffers); + return true; +} + +struct ra_buf *ra_buf_pool_get(struct ra *ra, struct ra_buf_pool *pool, + const struct ra_buf_params *params) +{ + assert(!params->initial_data); + + if (!ra_buf_params_compatible(params, &pool->current_params)) { + ra_buf_pool_uninit(ra, pool); + pool->current_params = *params; + } + + // Make sure we have at least one buffer available + if (!pool->buffers && !ra_buf_pool_grow(ra, pool)) + return NULL; + + // Make sure the next buffer is available for use + if (!ra->fns->buf_poll(ra, pool->buffers[pool->index]) && + !ra_buf_pool_grow(ra, pool)) + { + return NULL; + } + + struct ra_buf *buf = pool->buffers[pool->index++]; + pool->index %= pool->num_buffers; + + return buf; +} + +bool ra_tex_upload_pbo(struct ra *ra, struct ra_buf_pool *pbo, + const struct ra_tex_upload_params *params) +{ + if (params->buf) + return ra->fns->tex_upload(ra, params); + + struct ra_tex *tex = params->tex; + size_t row_size = tex->params.dimensions == 2 ? params->stride : + tex->params.w * tex->params.format->pixel_size; + + struct ra_buf_params bufparams = { + .type = RA_BUF_TYPE_TEX_UPLOAD, + .size = row_size * tex->params.h * tex->params.d, + .host_mutable = true, + }; + + struct ra_buf *buf = ra_buf_pool_get(ra, pbo, &bufparams); + if (!buf) + return false; + + ra->fns->buf_update(ra, buf, 0, params->src, bufparams.size); + + struct ra_tex_upload_params newparams = *params; + newparams.buf = buf; + newparams.src = NULL; + + return ra->fns->tex_upload(ra, &newparams); +} + // Create a texture and a FBO using the texture as color attachments. // fmt: texture internal format // If the parameters are the same as the previous call, do not touch it. @@ -120,63 +205,6 @@ void fbotex_uninit(struct fbotex *fbo) } } -bool tex_upload(struct ra *ra, struct tex_upload *pbo, bool want_pbo, - const struct ra_tex_upload_params *params) -{ - if (!(ra->caps & RA_CAP_DIRECT_UPLOAD)) - want_pbo = true; - - if (!want_pbo || params->buf) - return ra->fns->tex_upload(ra, params); - - struct ra_tex *tex = params->tex; - size_t row_size = tex->params.dimensions == 2 ? params->stride : - tex->params.w * tex->params.format->pixel_size; - size_t needed_size = row_size * tex->params.h * tex->params.d; - - if (needed_size > pbo->buffer_size) - tex_upload_uninit(ra, pbo); - - if (!pbo->buffers[0]) { - struct ra_buf_params bufparams = { - .type = RA_BUF_TYPE_TEX_UPLOAD, - .size = needed_size, - .host_mutable = true, - }; - - pbo->buffer_size = bufparams.size; - for (int i = 0; i < NUM_PBO_BUFFERS; i++) { - pbo->buffers[i] = ra_buf_create(ra, &bufparams); - if (!pbo->buffers[i]) - return false; - } - } - - struct ra_buf *buf = pbo->buffers[pbo->index++]; - pbo->index %= NUM_PBO_BUFFERS; - - if (!ra->fns->buf_poll(ra, buf)) { - MP_WARN(ra, "Texture upload buffer was not free to use! Try " - "increasing NUM_PBO_BUFFERS.\n"); - return false; - } - - ra->fns->buf_update(ra, buf, 0, params->src, needed_size); - - struct ra_tex_upload_params newparams = *params; - newparams.buf = buf; - newparams.src = NULL; - - return ra->fns->tex_upload(ra, &newparams); -} - -void tex_upload_uninit(struct ra *ra, struct tex_upload *pbo) -{ - for (int i = 0; i < NUM_PBO_BUFFERS; i++) - ra_buf_free(ra, &pbo->buffers[i]); - *pbo = (struct tex_upload){0}; -} - struct timer_pool { struct ra *ra; ra_timer *timer; diff --git a/video/out/opengl/utils.h b/video/out/opengl/utils.h index aaaf4bd12e..8c52680f14 100644 --- a/video/out/opengl/utils.h +++ b/video/out/opengl/utils.h @@ -69,6 +69,25 @@ struct fbodst { void gl_transform_ortho_fbodst(struct gl_transform *t, struct fbodst fbo); +// A pool of buffers, which can grow as needed +struct ra_buf_pool { + struct ra_buf_params current_params; + struct ra_buf **buffers; + int num_buffers; + int index; +}; + +void ra_buf_pool_uninit(struct ra *ra, struct ra_buf_pool *pool); + +// Note: params->initial_data is *not* supported +struct ra_buf *ra_buf_pool_get(struct ra *ra, struct ra_buf_pool *pool, + const struct ra_buf_params *params); + +// Helper that wraps ra_tex_upload using texture upload buffers to ensure that +// params->buf is always set. This is intended for RA-internal usage. +bool ra_tex_upload_pbo(struct ra *ra, struct ra_buf_pool *pbo, + const struct ra_tex_upload_params *params); + struct fbotex { struct ra *ra; struct ra_tex *tex; @@ -83,21 +102,6 @@ bool fbotex_change(struct fbotex *fbo, struct ra *ra, struct mp_log *log, #define FBOTEX_FUZZY_H 2 #define FBOTEX_FUZZY (FBOTEX_FUZZY_W | FBOTEX_FUZZY_H) -#define NUM_PBO_BUFFERS 3 - -// A wrapper around tex_upload that uses PBOs internally if requested or -// required -struct tex_upload { - size_t buffer_size; - struct ra_buf *buffers[NUM_PBO_BUFFERS]; - int index; -}; - -bool tex_upload(struct ra *ra, struct tex_upload *pbo, bool want_pbo, - const struct ra_tex_upload_params *params); - -void tex_upload_uninit(struct ra *ra, struct tex_upload *pbo); - // A wrapper around ra_timer that does result pooling, averaging etc. struct timer_pool; diff --git a/video/out/opengl/video.c b/video/out/opengl/video.c index e8ff23f2b4..bb3730022a 100644 --- a/video/out/opengl/video.c +++ b/video/out/opengl/video.c @@ -84,7 +84,6 @@ static const struct ra_renderpass_input vertex_vao[] = { struct texplane { struct ra_tex *tex; - struct tex_upload pbo; int w, h; bool flipped; }; @@ -494,7 +493,7 @@ static void reinit_osd(struct gl_video *p) mpgl_osd_destroy(p->osd); p->osd = NULL; if (p->osd_state) - p->osd = mpgl_osd_init(p->ra, p->log, p->osd_state, p->opts.pbo); + p->osd = mpgl_osd_init(p->ra, p->log, p->osd_state); } static void uninit_rendering(struct gl_video *p) @@ -987,7 +986,6 @@ static void uninit_video(struct gl_video *p) for (int n = 0; n < p->plane_count; n++) { struct texplane *plane = &vimg->planes[n]; ra_tex_free(p->ra, &plane->tex); - tex_upload_uninit(p->ra, &plane->pbo); } *vimg = (struct video_image){0}; @@ -3291,7 +3289,7 @@ static bool pass_upload_image(struct gl_video *p, struct mp_image *mpi, uint64_t MP_VERBOSE(p, "DR enabled: %s\n", p->using_dr_path ? "yes" : "no"); } - if (!tex_upload(p->ra, &plane->pbo, p->opts.pbo, ¶ms)) { + if (!p->ra->fns->tex_upload(p->ra, ¶ms)) { timer_pool_stop(p->upload_timer); goto error; } @@ -3300,7 +3298,9 @@ static bool pass_upload_image(struct gl_video *p, struct mp_image *mpi, uint64_t mapped->mpi = mp_image_new_ref(mpi); } timer_pool_stop(p->upload_timer); - const char *mode = p->using_dr_path ? "DR" : p->opts.pbo ? "PBO" : "naive"; + + 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)); @@ -3639,6 +3639,7 @@ static void reinit_from_options(struct gl_video *p) check_gl_features(p); uninit_rendering(p); gl_sc_set_cache_dir(p->sc, p->opts.shader_cache_dir); + p->ra->use_pbo = p->opts.pbo; gl_video_setup_hooks(p); reinit_osd(p);