[vlc-devel] [PATCH v3 11/22] opengl: create and bind filters framebuffers
Romain Vimont
rom1v at videolabs.io
Mon Jul 6 12:47:04 CEST 2020
Create one framebuffer and its associate texture for each filter, and
bind them as necessary before drawing so that the output of a filter is
the input of the next one.
Co-authored-by: Alexandre Janniaux <ajanni at videolabs.io>
Co-authored-by: Maxime Meissonnier <mmeisson at outlook.fr>
---
modules/video_output/opengl/filter.c | 11 ++++
modules/video_output/opengl/filter_priv.h | 4 ++
modules/video_output/opengl/filters.c | 72 +++++++++++++++++++++++
3 files changed, 87 insertions(+)
diff --git a/modules/video_output/opengl/filter.c b/modules/video_output/opengl/filter.c
index 80a0459be9..11007edfcd 100644
--- a/modules/video_output/opengl/filter.c
+++ b/modules/video_output/opengl/filter.c
@@ -30,6 +30,7 @@
#include <vlc_common.h>
#include <vlc_modules.h>
+#include "gl_api.h"
#include "sampler_priv.h"
#undef vlc_gl_filter_New
@@ -44,6 +45,8 @@ 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;
+
struct vlc_gl_filter *filter = &priv->filter;
filter->api = api;
filter->ops = NULL;
@@ -94,8 +97,16 @@ vlc_gl_filter_Delete(struct vlc_gl_filter *filter)
module_unneed(filter, filter->module);
struct vlc_gl_filter_priv *priv = vlc_gl_filter_PRIV(filter);
+
if (priv->sampler)
vlc_gl_sampler_Delete(priv->sampler);
+ if (priv->has_framebuffer_out)
+ {
+ const opengl_vtable_t *vt = &filter->api->vt;
+ vt->DeleteFramebuffers(1, &priv->framebuffer_out);
+ vt->DeleteTextures(1, &priv->texture_out);
+ }
+
vlc_object_delete(&filter->obj);
}
diff --git a/modules/video_output/opengl/filter_priv.h b/modules/video_output/opengl/filter_priv.h
index 50134faf50..8e177908f3 100644
--- a/modules/video_output/opengl/filter_priv.h
+++ b/modules/video_output/opengl/filter_priv.h
@@ -33,6 +33,10 @@ struct vlc_gl_filter_priv {
struct vlc_gl_tex_size size_out;
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) */
+
struct vlc_list node; /**< node of vlc_gl_filters.list */
};
diff --git a/modules/video_output/opengl/filters.c b/modules/video_output/opengl/filters.c
index 47c222f533..d6762b0b56 100644
--- a/modules/video_output/opengl/filters.c
+++ b/modules/video_output/opengl/filters.c
@@ -73,6 +73,41 @@ vlc_gl_filters_Delete(struct vlc_gl_filters *filters)
free(filters);
}
+static int
+InitFramebufferOut(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;
+
+ /* 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);
+ vt->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ vt->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ /* iOS needs GL_CLAMP_TO_EDGE or power-of-two textures */
+ vt->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ 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->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D, priv->texture_out, 0);
+
+ priv->has_framebuffer_out = true;
+
+ GLenum status = vt->CheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (status != GL_FRAMEBUFFER_COMPLETE)
+ return VLC_EGENERIC;
+
+ vt->BindFramebuffer(GL_FRAMEBUFFER, 0);
+ return VLC_SUCCESS;
+}
+
struct vlc_gl_filter *
vlc_gl_filters_Append(struct vlc_gl_filters *filters, const char *name,
const config_chain_t *config)
@@ -127,6 +162,20 @@ vlc_gl_filters_Append(struct vlc_gl_filters *filters, const char *name,
return NULL;
}
+ if (prev_filter)
+ {
+ /* It was the last filter before we append this one */
+ assert(!prev_filter->has_framebuffer_out);
+
+ /* Every non-last filter needs its own framebuffer */
+ ret = InitFramebufferOut(prev_filter);
+ if (ret != VLC_SUCCESS)
+ {
+ vlc_gl_filter_Delete(filter);
+ return NULL;
+ }
+ }
+
vlc_list_append(&priv->node, &filters->list);
return filter;
@@ -150,9 +199,32 @@ vlc_gl_filters_UpdatePicture(struct vlc_gl_filters *filters,
int
vlc_gl_filters_Draw(struct vlc_gl_filters *filters)
{
+ const opengl_vtable_t *vt = &filters->api->vt;
+
struct vlc_gl_filter_priv *priv;
vlc_list_foreach(priv, &filters->list, node)
{
+ struct vlc_gl_filter_priv *previous =
+ vlc_list_prev_entry_or_null(&filters->list, priv,
+ struct vlc_gl_filter_priv, node);
+ if (previous)
+ {
+ /* Read from the output of the previous filter */
+ int ret = vlc_gl_sampler_UpdateTexture(priv->sampler,
+ previous->texture_out,
+ previous->size_out.width,
+ previous->size_out.height);
+ if (ret != VLC_SUCCESS)
+ {
+ msg_Err(filters->gl, "Could not update sampler texture");
+ return ret;
+ }
+ }
+
+ GLuint draw_fb = priv->has_framebuffer_out ? priv->framebuffer_out : 0;
+
+ vt->BindFramebuffer(GL_DRAW_FRAMEBUFFER, draw_fb);
+
struct vlc_gl_filter *filter = &priv->filter;
int ret = filter->ops->draw(filter);
if (ret != VLC_SUCCESS)
--
2.27.0
More information about the vlc-devel
mailing list