vlc | branch: master | Romain Vimont <ro...@videolabs.io> | Tue Mar 30 13:14:10 2021 +0200| [606ece75d5c18ef0739e3c1770d010fd94d086bf] | committer: Alexandre Janniaux
opengl: add support for planes filtering If config.filter_planes is set on a filter, generate one output texture for each input texture, and call draw() for each plane separately. Signed-off-by: Alexandre Janniaux <aja...@videolabs.io> > http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=606ece75d5c18ef0739e3c1770d010fd94d086bf --- modules/video_output/opengl/filter.c | 8 +- modules/video_output/opengl/filter.h | 18 +++ modules/video_output/opengl/filter_priv.h | 12 +- modules/video_output/opengl/filters.c | 235 +++++++++++++++++++++--------- 4 files changed, 196 insertions(+), 77 deletions(-) diff --git a/modules/video_output/opengl/filter.c b/modules/video_output/opengl/filter.c index 819f5078aa..b7357c7b20 100644 --- a/modules/video_output/opengl/filter.c +++ b/modules/video_output/opengl/filter.c @@ -45,7 +45,7 @@ vlc_gl_filter_New(vlc_object_t *parent, const struct vlc_gl_api *api) priv->size_out.width = 0; priv->size_out.height = 0; - priv->has_framebuffer_out = false; + priv->tex_count = 0; struct vlc_gl_filter *filter = &priv->filter; filter->api = api; @@ -112,10 +112,10 @@ vlc_gl_filter_Delete(struct vlc_gl_filter *filter) const opengl_vtable_t *vt = &filter->api->vt; - if (priv->has_framebuffer_out) + if (priv->tex_count) { - vt->DeleteFramebuffers(1, &priv->framebuffer_out); - vt->DeleteTextures(1, &priv->texture_out); + vt->DeleteFramebuffers(priv->tex_count, priv->framebuffers_out); + vt->DeleteTextures(priv->tex_count, priv->textures_out); } if (filter->config.msaa_level) diff --git a/modules/video_output/opengl/filter.h b/modules/video_output/opengl/filter.h index 399087d68f..f73887d081 100644 --- a/modules/video_output/opengl/filter.h +++ b/modules/video_output/opengl/filter.h @@ -35,6 +35,7 @@ struct vlc_gl_tex_size { struct vlc_gl_input_meta { vlc_tick_t pts; + unsigned plane; }; typedef int @@ -63,6 +64,9 @@ struct vlc_gl_filter_owner_ops { * Successive calls to this function for the same filter is guaranteed to * always return the same sampler. * + * Important: filter->config must be initialized *before* getting the + * sampler, since the sampler behavior may depend on it. + * * \param filter the filter * \return sampler the sampler, NULL on error */ @@ -80,9 +84,21 @@ struct vlc_gl_filter { const struct vlc_gl_api *api; struct { + /** + * An OpenGL filter may either operate on the input RGBA picture, or on + * individual input planes (without chroma conversion) separately. + * + * In practice, this is useful for deinterlace filters. + * + * This flag must be set by the filter module (default is false). + */ + bool filter_planes; + /** * A blend filter draws over the input picture (without reading it). * + * Meaningless if filter_planes is true. + * * This flag must be set by the filter module (default is false). */ bool blend; @@ -93,6 +109,8 @@ struct vlc_gl_filter { * This value must be set by the filter module (default is 0, which * means disabled). * + * Meaningless if filter_planes is true. + * * The actual MSAA level may be overwritten to 0 if multisampling is * not supported, or to a higher value if another filter rendering on * the same framebuffer requested a higher MSAA level. diff --git a/modules/video_output/opengl/filter_priv.h b/modules/video_output/opengl/filter_priv.h index f4c983ce85..e28bd99b61 100644 --- a/modules/video_output/opengl/filter_priv.h +++ b/modules/video_output/opengl/filter_priv.h @@ -24,6 +24,7 @@ #include <vlc_common.h> #include <vlc_list.h> +#include <vlc_picture.h> #include "filter.h" #include "sampler.h" @@ -38,9 +39,14 @@ struct vlc_gl_filter_priv { /* Only meaningful for non-blend filters { */ struct vlc_gl_sampler *sampler; /* owned */ - bool has_framebuffer_out; - GLuint framebuffer_out; /* owned (this filter must delete it) */ - GLuint texture_out; /* owned (attached to framebuffer_out) */ + /* owned (this filter must delete it) */ + GLuint framebuffers_out[PICTURE_PLANE_MAX]; + + /* owned (each attached to framebuffers_out[i]) */ + GLuint textures_out[PICTURE_PLANE_MAX]; + GLsizei tex_widths[PICTURE_PLANE_MAX]; + GLsizei tex_heights[PICTURE_PLANE_MAX]; + unsigned tex_count; /* For multisampling, if msaa_level != 0 */ GLuint framebuffer_msaa; /* owned */ diff --git a/modules/video_output/opengl/filters.c b/modules/video_output/opengl/filters.c index 7db8b5a685..c8e99a9029 100644 --- a/modules/video_output/opengl/filters.c +++ b/modules/video_output/opengl/filters.c @@ -170,17 +170,17 @@ vlc_gl_filters_Delete(struct vlc_gl_filters *filters) } static int -InitFramebufferOut(struct vlc_gl_filter_priv *priv) +InitPlane(struct vlc_gl_filter_priv *priv, unsigned plane, GLsizei width, + GLsizei height) { - assert(priv->size_out.width > 0 && priv->size_out.height > 0); - const opengl_vtable_t *vt = &priv->filter.api->vt; - /* Create a texture having the expected size */ - vt->GenTextures(1, &priv->texture_out); - vt->BindTexture(GL_TEXTURE_2D, priv->texture_out); - vt->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, priv->size_out.width, - priv->size_out.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + GLuint framebuffer = priv->framebuffers_out[plane]; + GLuint texture = priv->textures_out[plane]; + + vt->BindTexture(GL_TEXTURE_2D, texture); + vt->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, + GL_UNSIGNED_BYTE, NULL); vt->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); vt->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); @@ -189,12 +189,9 @@ InitFramebufferOut(struct vlc_gl_filter_priv *priv) vt->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); /* Create a framebuffer and attach the texture */ - vt->GenFramebuffers(1, &priv->framebuffer_out); - vt->BindFramebuffer(GL_FRAMEBUFFER, priv->framebuffer_out); + vt->BindFramebuffer(GL_FRAMEBUFFER, framebuffer); vt->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, priv->texture_out, 0); - - priv->has_framebuffer_out = true; + GL_TEXTURE_2D, texture, 0); GLenum status = vt->CheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) @@ -203,6 +200,57 @@ InitFramebufferOut(struct vlc_gl_filter_priv *priv) return VLC_SUCCESS; } +static int +InitFramebuffersOut(struct vlc_gl_filter_priv *priv) +{ + assert(priv->size_out.width > 0 && priv->size_out.height > 0); + + const opengl_vtable_t *vt = &priv->filter.api->vt; + + struct vlc_gl_filter *filter = &priv->filter; + if (filter->config.filter_planes) + { + struct vlc_gl_sampler *sampler = vlc_gl_filter_GetSampler(filter); + if (!sampler) + return VLC_EGENERIC; + + priv->tex_count = sampler->tex_count; + vt->GenFramebuffers(priv->tex_count, priv->framebuffers_out); + vt->GenTextures(priv->tex_count, priv->textures_out); + + for (unsigned i = 0; i < sampler->tex_count; ++i) + { + priv->tex_widths[i] = priv->size_out.width * sampler->tex_widths[i] + / sampler->tex_widths[0]; + priv->tex_heights[i] = priv->size_out.height * sampler->tex_heights[i] + / sampler->tex_heights[0]; + /* Init one framebuffer and texture for each plane */ + int ret = + InitPlane(priv, i, priv->tex_widths[i], priv->tex_heights[i]); + if (ret != VLC_SUCCESS) + return ret; + } + } + else + { + priv->tex_count = 1; + + /* Create a texture having the expected size */ + + vt->GenFramebuffers(1, priv->framebuffers_out); + vt->GenTextures(1, priv->textures_out); + + priv->tex_widths[0] = priv->size_out.width; + priv->tex_heights[0] = priv->size_out.height; + + int ret = InitPlane(priv, 0, priv->tex_widths[0], priv->tex_heights[0]); + if (ret != VLC_SUCCESS) + return ret; + } + + return VLC_SUCCESS; +} + static int InitFramebufferMSAA(struct vlc_gl_filter_priv *priv, unsigned msaa_level) { @@ -241,18 +289,27 @@ GetSampler(struct vlc_gl_filter *filter) struct vlc_gl_filters *filters = priv->filters; struct vlc_gl_filter_priv *prev_filter = priv->prev_filter; + bool expose_planes = filter->config.filter_planes; struct vlc_gl_sampler *sampler; if (!priv->prev_filter) - sampler = vlc_gl_sampler_NewFromInterop(filters->interop, false); + sampler = vlc_gl_sampler_NewFromInterop(filters->interop, + expose_planes); else { video_format_t fmt; - video_format_Init(&fmt, VLC_CODEC_RGBA); + + /* If the previous filter operated on planes, then its output chroma is + * the same as its input chroma. Otherwise, it's RGBA. */ + vlc_fourcc_t chroma = prev_filter->filter.config.filter_planes + ? prev_filter->sampler->fmt.i_chroma + : VLC_CODEC_RGBA; + + video_format_Init(&fmt, chroma); fmt.i_width = fmt.i_visible_width = prev_filter->size_out.width; fmt.i_height = fmt.i_visible_height = prev_filter->size_out.height; sampler = vlc_gl_sampler_NewFromTexture2D(filters->gl, filters->api, - &fmt, false); + &fmt, expose_planes); } priv->sampler = sampler; @@ -312,6 +369,12 @@ vlc_gl_filters_Append(struct vlc_gl_filters *filters, const char *name, || (priv->size_out.width == size_in.width && priv->size_out.height == size_in.height)); + /* A filter operating on planes may not blend. */ + assert(!filter->config.filter_planes || !filter->config.blend); + + /* A filter operating on planes may not use anti-aliasing. */ + assert(!filter->config.filter_planes || !filter->config.msaa_level); + /* A blend filter may not read its input, so it is an error if a sampler * has been requested. * @@ -323,7 +386,7 @@ vlc_gl_filters_Append(struct vlc_gl_filters *filters, const char *name, if (filter->config.blend) { - if (!prev_filter) + if (!prev_filter || prev_filter->filter.config.filter_planes) { /* We cannot blend with nothing, so insert a "draw" filter to draw * the input picture to blend with. */ @@ -398,10 +461,15 @@ vlc_gl_filters_InitFramebuffers(struct vlc_gl_filters *filters) /* "priv" is the last filter */ assert(priv); /* There is at least one filter */ - if (priv->filter.config.msaa_level) - { + bool insert_draw = /* Resolving multisampling to the default framebuffer might fail, - * because its format may be different. So insert a "draw" filter. */ + * because its format may be different. */ + priv->filter.config.msaa_level || + /* A filter operating on planes may produce several textures. + * They need to be chroma-converted to a single RGBA texture. */ + priv->filter.config.filter_planes; + if (insert_draw) + { struct vlc_gl_filter *draw = vlc_gl_filters_Append(filters, "draw", NULL); if (!draw) @@ -422,11 +490,11 @@ vlc_gl_filters_InitFramebuffers(struct vlc_gl_filters *filters) if (!is_last) { /* It was the last non-blend filter before we append this one */ - assert(!priv->has_framebuffer_out); + assert(priv->tex_count == 0); /* Every non-blend filter needs its own framebuffer, except the last * one */ - int ret = InitFramebufferOut(priv); + int ret = InitFramebuffersOut(priv); if (ret != VLC_SUCCESS) return ret; } @@ -467,6 +535,7 @@ vlc_gl_filters_Draw(struct vlc_gl_filters *filters) struct vlc_gl_input_meta meta = { .pts = filters->pic.pts, + .plane = 0, }; struct vlc_gl_filter_priv *priv; @@ -478,11 +547,10 @@ vlc_gl_filters_Draw(struct vlc_gl_filters *filters) if (previous) { /* Read from the output of the previous filter */ - GLuint tex = previous->texture_out; - GLsizei width = previous->size_out.width; - GLsizei height = previous->size_out.height; - int ret = vlc_gl_sampler_UpdateTextures(priv->sampler, &tex, &width, - &height); + int ret = vlc_gl_sampler_UpdateTextures(priv->sampler, + previous->textures_out, + previous->tex_widths, + previous->tex_heights); if (ret != VLC_SUCCESS) { msg_Err(filters->gl, "Could not update sampler texture"); @@ -490,59 +558,86 @@ vlc_gl_filters_Draw(struct vlc_gl_filters *filters) } } - unsigned msaa_level = priv->filter.config.msaa_level; - GLuint draw_fb; - if (msaa_level) - draw_fb = priv->framebuffer_msaa; - else - draw_fb = priv->has_framebuffer_out ? priv->framebuffer_out - : draw_framebuffer; - - vt->BindFramebuffer(GL_DRAW_FRAMEBUFFER, draw_fb); + struct vlc_gl_filter *filter = &priv->filter; - if (vlc_list_is_last(&priv->node, &filters->list)) + if (filter->config.filter_planes) { - /* The output viewport must be applied on the last filter */ - struct vlc_gl_filters_viewport *vp = &filters->viewport; - vt->Viewport(vp->x, vp->y, vp->width, vp->height); - } - else - vt->Viewport(0, 0, priv->size_out.width, priv->size_out.height); + for (unsigned i = 0; i < priv->tex_count; ++i) + { + meta.plane = i; - struct vlc_gl_filter *filter = &priv->filter; - int ret = filter->ops->draw(filter, &meta); - if (ret != VLC_SUCCESS) - return ret; + /* Select the output texture associated to this plane */ + GLuint draw_fb = priv->framebuffers_out[i]; + vt->BindFramebuffer(GL_DRAW_FRAMEBUFFER, draw_fb); - /* Draw blend subfilters */ - struct vlc_gl_filter_priv *subfilter_priv; - vlc_list_foreach(subfilter_priv, &priv->blend_subfilters, node) + assert(!vlc_list_is_last(&priv->node, &filters->list)); + vt->Viewport(0, 0, priv->tex_widths[i], priv->tex_heights[i]); + + vlc_gl_sampler_SelectPlane(priv->sampler, i); + int ret = filter->ops->draw(filter, &meta); + if (ret != VLC_SUCCESS) + return ret; + } + } + else { - /* Reset the draw buffer, in case it has been changed from a filter - * draw() callback */ + assert(priv->tex_count <= 1); + unsigned msaa_level = priv->filter.config.msaa_level; + GLuint draw_fb; + if (msaa_level) + draw_fb = priv->framebuffer_msaa; + else + draw_fb = priv->tex_count > 0 ? priv->framebuffers_out[0] + : draw_framebuffer; + vt->BindFramebuffer(GL_DRAW_FRAMEBUFFER, draw_fb); - struct vlc_gl_filter *subfilter = &subfilter_priv->filter; - ret = subfilter->ops->draw(subfilter, &meta); + if (vlc_list_is_last(&priv->node, &filters->list)) + { + /* The output viewport must be applied on the last filter */ + struct vlc_gl_filters_viewport *vp = &filters->viewport; + vt->Viewport(vp->x, vp->y, vp->width, vp->height); + } + else + vt->Viewport(0, 0, priv->tex_widths[0], priv->tex_heights[0]); + + meta.plane = 0; + int ret = filter->ops->draw(filter, &meta); if (ret != VLC_SUCCESS) return ret; - } - if (filter->config.msaa_level) - { - /* Never resolve multisampling to the default framebuffer */ - assert(priv->has_framebuffer_out); - assert(priv->framebuffer_out != draw_framebuffer); - - /* Resolve the MSAA into the target framebuffer */ - vt->BindFramebuffer(GL_READ_FRAMEBUFFER, priv->framebuffer_msaa); - vt->BindFramebuffer(GL_DRAW_FRAMEBUFFER, priv->framebuffer_out); - - GLint width = priv->size_out.width; - GLint height = priv->size_out.height; - vt->BlitFramebuffer(0, 0, width, height, - 0, 0, width, height, - GL_COLOR_BUFFER_BIT, GL_NEAREST); + /* Draw blend subfilters */ + struct vlc_gl_filter_priv *subfilter_priv; + vlc_list_foreach(subfilter_priv, &priv->blend_subfilters, node) + { + /* Reset the draw buffer, in case it has been changed from a + * filter draw() callback */ + vt->BindFramebuffer(GL_DRAW_FRAMEBUFFER, draw_fb); + + struct vlc_gl_filter *subfilter = &subfilter_priv->filter; + ret = subfilter->ops->draw(subfilter, &meta); + if (ret != VLC_SUCCESS) + return ret; + } + + if (filter->config.msaa_level) + { + /* Never resolve multisampling to the default framebuffer */ + assert(priv->tex_count == 1); + assert(priv->framebuffers_out[0] != draw_framebuffer); + + /* Resolve the MSAA into the target framebuffer */ + vt->BindFramebuffer(GL_READ_FRAMEBUFFER, + priv->framebuffer_msaa); + vt->BindFramebuffer(GL_DRAW_FRAMEBUFFER, + priv->framebuffers_out[0]); + + GLint width = priv->size_out.width; + GLint height = priv->size_out.height; + vt->BlitFramebuffer(0, 0, width, height, + 0, 0, width, height, + GL_COLOR_BUFFER_BIT, GL_NEAREST); + } } } _______________________________________________ vlc-commits mailing list vlc-commits@videolan.org https://mailman.videolan.org/listinfo/vlc-commits