mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-10 03:19:40 +00:00
518e8a3fd2
Facilities are given to create fbo's and attach GL memory (renderbuffers or textures). It also keeps track of the renderable size for use with effective use with glViewport().
448 lines
12 KiB
C
448 lines
12 KiB
C
/*
|
|
* GStreamer
|
|
* Copyright (C) 2013 Matthew Waters <ystreet00@gmail.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "gl.h"
|
|
#include "gstglframebuffer.h"
|
|
|
|
#ifndef GL_FRAMEBUFFER_UNDEFINED
|
|
#define GL_FRAMEBUFFER_UNDEFINED 0x8219
|
|
#endif
|
|
#ifndef GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT
|
|
#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6
|
|
#endif
|
|
#ifndef GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT
|
|
#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7
|
|
#endif
|
|
#ifndef GL_FRAMEBUFFER_UNSUPPORTED
|
|
#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD
|
|
#endif
|
|
#ifndef GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS
|
|
#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS 0x8CD9
|
|
#endif
|
|
|
|
GST_DEBUG_CATEGORY_STATIC (gst_gl_framebuffer_debug);
|
|
#define GST_CAT_DEFAULT gst_gl_framebuffer_debug
|
|
|
|
#define DEBUG_INIT \
|
|
GST_DEBUG_CATEGORY_INIT (gst_gl_framebuffer_debug, "glframebuffer", 0, "GL Framebuffer");
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (GstGLFramebuffer, gst_gl_framebuffer, GST_TYPE_OBJECT,
|
|
DEBUG_INIT);
|
|
|
|
#define GST_GL_FRAMEBUFFER_GET_PRIVATE(o) \
|
|
(G_TYPE_INSTANCE_GET_PRIVATE((o), GST_TYPE_GL_FRAMEBUFFER, GstGLFramebufferPrivate))
|
|
|
|
static void gst_gl_framebuffer_finalize (GObject * object);
|
|
|
|
struct _GstGLFramebufferPrivate
|
|
{
|
|
guint effective_width;
|
|
guint effective_height;
|
|
};
|
|
|
|
struct fbo_attachment
|
|
{
|
|
guint attachment_point;
|
|
GstGLBaseMemory *mem;
|
|
};
|
|
|
|
static void
|
|
_fbo_attachment_init (struct fbo_attachment *attach, guint point,
|
|
GstGLBaseMemory * mem)
|
|
{
|
|
attach->attachment_point = point;
|
|
attach->mem = (GstGLBaseMemory *) gst_memory_ref (GST_MEMORY_CAST (mem));
|
|
}
|
|
|
|
static void
|
|
_fbo_attachment_unset (struct fbo_attachment *attach)
|
|
{
|
|
if (!attach)
|
|
return;
|
|
|
|
if (attach->mem)
|
|
gst_memory_unref (GST_MEMORY_CAST (attach->mem));
|
|
attach->mem = NULL;
|
|
}
|
|
|
|
static void
|
|
gst_gl_framebuffer_class_init (GstGLFramebufferClass * klass)
|
|
{
|
|
g_type_class_add_private (klass, sizeof (GstGLFramebufferPrivate));
|
|
|
|
G_OBJECT_CLASS (klass)->finalize = gst_gl_framebuffer_finalize;
|
|
}
|
|
|
|
static void
|
|
gst_gl_framebuffer_init (GstGLFramebuffer * fb)
|
|
{
|
|
fb->priv = GST_GL_FRAMEBUFFER_GET_PRIVATE (fb);
|
|
|
|
fb->attachments =
|
|
g_array_new (FALSE, FALSE, (sizeof (struct fbo_attachment)));
|
|
g_array_set_clear_func (fb->attachments,
|
|
(GDestroyNotify) _fbo_attachment_unset);
|
|
}
|
|
|
|
static void
|
|
_delete_fbo_gl (GstGLContext * context, GstGLFramebuffer * fb)
|
|
{
|
|
const GstGLFuncs *gl = context->gl_vtable;
|
|
|
|
if (fb->fbo_id)
|
|
gl->DeleteFramebuffers (1, &fb->fbo_id);
|
|
fb->fbo_id = 0;
|
|
}
|
|
|
|
static void
|
|
gst_gl_framebuffer_finalize (GObject * object)
|
|
{
|
|
GstGLFramebuffer *fb = GST_GL_FRAMEBUFFER (object);
|
|
|
|
if (fb->context) {
|
|
if (fb->fbo_id)
|
|
gst_gl_context_thread_add (fb->context,
|
|
(GstGLContextThreadFunc) _delete_fbo_gl, fb);
|
|
|
|
gst_object_unref (fb->context);
|
|
fb->context = NULL;
|
|
}
|
|
|
|
if (fb->attachments)
|
|
g_array_free (fb->attachments, TRUE);
|
|
fb->attachments = NULL;
|
|
|
|
G_OBJECT_CLASS (gst_gl_framebuffer_parent_class)->finalize (object);
|
|
}
|
|
|
|
GstGLFramebuffer *
|
|
gst_gl_framebuffer_new (GstGLContext * context)
|
|
{
|
|
GstGLFramebuffer *fb;
|
|
const GstGLFuncs *gl;
|
|
|
|
g_return_val_if_fail (GST_IS_GL_CONTEXT (context), NULL);
|
|
g_return_val_if_fail (gst_gl_context_get_current () == context, NULL);
|
|
|
|
gl = context->gl_vtable;
|
|
|
|
if (!gl->GenFramebuffers) {
|
|
GST_ERROR_OBJECT (context, "Framebuffers are not supported!");
|
|
return NULL;
|
|
}
|
|
|
|
fb = g_object_new (GST_TYPE_GL_FRAMEBUFFER, NULL);
|
|
fb->context = gst_object_ref (context);
|
|
gl->GenFramebuffers (1, &fb->fbo_id);
|
|
|
|
return fb;
|
|
}
|
|
|
|
GstGLFramebuffer *
|
|
gst_gl_framebuffer_new_with_default_depth (GstGLContext * context, guint width,
|
|
guint height)
|
|
{
|
|
GstGLFramebuffer *fb = gst_gl_framebuffer_new (context);
|
|
GstGLBaseMemoryAllocator *render_alloc;
|
|
GstGLAllocationParams *params;
|
|
GstGLBaseMemory *renderbuffer;
|
|
guint attach_point, attach_type;
|
|
|
|
if (!fb)
|
|
return NULL;
|
|
|
|
if (gst_gl_context_get_gl_api (fb->context) & (GST_GL_API_OPENGL |
|
|
GST_GL_API_OPENGL3)) {
|
|
attach_point = GL_DEPTH_STENCIL_ATTACHMENT;
|
|
attach_type = GST_GL_DEPTH24_STENCIL8;
|
|
} else if (gst_gl_context_get_gl_api (fb->context) & GST_GL_API_GLES2) {
|
|
attach_point = GL_DEPTH_ATTACHMENT;
|
|
attach_type = GST_GL_DEPTH_COMPONENT16;
|
|
} else {
|
|
g_assert_not_reached ();
|
|
return NULL;
|
|
}
|
|
|
|
render_alloc = (GstGLBaseMemoryAllocator *)
|
|
gst_allocator_find (GST_GL_RENDERBUFFER_ALLOCATOR_NAME);
|
|
params = (GstGLAllocationParams *)
|
|
gst_gl_renderbuffer_allocation_params_new (context, NULL, attach_type,
|
|
width, height);
|
|
|
|
renderbuffer = gst_gl_base_memory_alloc (render_alloc, params);
|
|
gst_gl_allocation_params_free (params);
|
|
gst_object_unref (render_alloc);
|
|
|
|
gst_gl_framebuffer_bind (fb);
|
|
gst_gl_framebuffer_attach (fb, attach_point, renderbuffer);
|
|
gst_gl_context_clear_framebuffer (fb->context);
|
|
gst_memory_unref (GST_MEMORY_CAST (renderbuffer));
|
|
|
|
return fb;
|
|
}
|
|
|
|
gboolean
|
|
gst_gl_framebuffer_draw_to_texture (GstGLFramebuffer * fb, GstGLMemory * mem,
|
|
GstGLFramebufferFunc func, gpointer user_data)
|
|
{
|
|
GLint viewport_dim[4] = { 0 };
|
|
const GstGLFuncs *gl;
|
|
gboolean ret;
|
|
|
|
g_return_val_if_fail (GST_IS_GL_FRAMEBUFFER (fb), FALSE);
|
|
g_return_val_if_fail (gst_is_gl_memory (GST_MEMORY_CAST (mem)), FALSE);
|
|
|
|
gl = fb->context->gl_vtable;
|
|
|
|
GST_TRACE_OBJECT (fb, "drawing to texture %u, dimensions %ix%i", mem->tex_id,
|
|
gst_gl_memory_get_texture_width (mem),
|
|
gst_gl_memory_get_texture_height (mem));
|
|
|
|
gst_gl_framebuffer_bind (fb);
|
|
gst_gl_framebuffer_attach (fb, GL_COLOR_ATTACHMENT0, (GstGLBaseMemory *) mem);
|
|
|
|
gl->GetIntegerv (GL_VIEWPORT, viewport_dim);
|
|
gl->Viewport (0, 0, fb->priv->effective_width, fb->priv->effective_height);
|
|
if (gst_gl_context_get_gl_api (fb->context) & (GST_GL_API_OPENGL |
|
|
GST_GL_API_OPENGL3))
|
|
gl->DrawBuffer (GL_COLOR_ATTACHMENT0);
|
|
|
|
ret = func (user_data);
|
|
|
|
if (gst_gl_context_get_gl_api (fb->context) & (GST_GL_API_OPENGL |
|
|
GST_GL_API_OPENGL3))
|
|
gl->DrawBuffer (GL_NONE);
|
|
gl->Viewport (viewport_dim[0], viewport_dim[1], viewport_dim[2],
|
|
viewport_dim[3]);
|
|
gst_gl_context_clear_framebuffer (fb->context);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
gst_gl_framebuffer_bind (GstGLFramebuffer * fb)
|
|
{
|
|
const GstGLFuncs *gl;
|
|
|
|
g_return_if_fail (GST_IS_GL_FRAMEBUFFER (fb));
|
|
g_return_if_fail (gst_gl_context_get_current () == fb->context);
|
|
g_return_if_fail (fb->fbo_id != 0);
|
|
|
|
gl = fb->context->gl_vtable;
|
|
|
|
gl->BindFramebuffer (GL_FRAMEBUFFER, fb->fbo_id);
|
|
}
|
|
|
|
void
|
|
gst_gl_context_clear_framebuffer (GstGLContext * context)
|
|
{
|
|
const GstGLFuncs *gl;
|
|
|
|
g_return_if_fail (GST_IS_GL_CONTEXT (context));
|
|
|
|
gl = context->gl_vtable;
|
|
|
|
gl->BindFramebuffer (GL_FRAMEBUFFER, 0);
|
|
}
|
|
|
|
static void
|
|
_update_effective_dimensions (GstGLFramebuffer * fb)
|
|
{
|
|
int i;
|
|
guint min_width = -1, min_height = -1;
|
|
|
|
/* remove the previous attachment */
|
|
for (i = 0; i < fb->attachments->len; i++) {
|
|
struct fbo_attachment *attach;
|
|
int width, height;
|
|
|
|
attach = &g_array_index (fb->attachments, struct fbo_attachment, i);
|
|
|
|
if (gst_is_gl_memory (GST_MEMORY_CAST (attach->mem))) {
|
|
GstGLMemory *mem = (GstGLMemory *) attach->mem;
|
|
|
|
width = gst_gl_memory_get_texture_width (mem);
|
|
height = gst_gl_memory_get_texture_height (mem);
|
|
} else if (gst_is_gl_renderbuffer (GST_MEMORY_CAST (attach->mem))) {
|
|
GstGLRenderbuffer *mem = (GstGLRenderbuffer *) attach->mem;
|
|
|
|
width = mem->width;
|
|
height = mem->height;
|
|
} else {
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
if (width < min_width)
|
|
min_width = width;
|
|
if (height < min_height)
|
|
min_height = height;
|
|
}
|
|
|
|
fb->priv->effective_width = min_width;
|
|
fb->priv->effective_height = min_height;
|
|
}
|
|
|
|
static gboolean
|
|
_is_valid_attachment_point (guint attachment_point)
|
|
{
|
|
/* all 31 possible color attachments */
|
|
if (attachment_point >= 0x8CE0 && attachment_point <= 0x8CFF)
|
|
return TRUE;
|
|
|
|
/* depth-stencil attachment */
|
|
if (attachment_point == 0x821A)
|
|
return TRUE;
|
|
|
|
/* depth attachment */
|
|
if (attachment_point == 0x8D00)
|
|
return TRUE;
|
|
|
|
/* stencil attachment */
|
|
if (attachment_point == 0x8D20)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
_attach_gl_memory (GstGLFramebuffer * fb, guint attachment_point,
|
|
GstGLMemory * mem)
|
|
{
|
|
struct fbo_attachment attach;
|
|
const GstGLFuncs *gl = fb->context->gl_vtable;
|
|
guint gl_target = gst_gl_texture_target_to_gl (mem->tex_target);
|
|
|
|
gst_gl_framebuffer_bind (fb);
|
|
|
|
gl->FramebufferTexture2D (GL_FRAMEBUFFER, attachment_point, gl_target,
|
|
mem->tex_id, 0);
|
|
|
|
_fbo_attachment_init (&attach, attachment_point, (GstGLBaseMemory *) mem);
|
|
fb->attachments = g_array_append_val (fb->attachments, attach);
|
|
}
|
|
|
|
static void
|
|
_attach_renderbuffer (GstGLFramebuffer * fb, guint attachment_point,
|
|
GstGLRenderbuffer * rb)
|
|
{
|
|
struct fbo_attachment attach;
|
|
const GstGLFuncs *gl = fb->context->gl_vtable;
|
|
|
|
gst_gl_framebuffer_bind (fb);
|
|
gl->BindRenderbuffer (GL_RENDERBUFFER, rb->renderbuffer_id);
|
|
|
|
gl->FramebufferRenderbuffer (GL_FRAMEBUFFER, attachment_point,
|
|
GL_RENDERBUFFER, rb->renderbuffer_id);
|
|
|
|
_fbo_attachment_init (&attach, attachment_point, (GstGLBaseMemory *) rb);
|
|
fb->attachments = g_array_append_val (fb->attachments, attach);
|
|
}
|
|
|
|
void
|
|
gst_gl_framebuffer_attach (GstGLFramebuffer * fb, guint attachment_point,
|
|
GstGLBaseMemory * mem)
|
|
{
|
|
int i;
|
|
|
|
g_return_if_fail (GST_IS_GL_FRAMEBUFFER (fb));
|
|
g_return_if_fail (gst_gl_context_get_current () == fb->context);
|
|
g_return_if_fail (_is_valid_attachment_point (attachment_point));
|
|
|
|
/* remove the previous attachment */
|
|
for (i = 0; i < fb->attachments->len; i++) {
|
|
struct fbo_attachment *attach;
|
|
|
|
attach = &g_array_index (fb->attachments, struct fbo_attachment, i);
|
|
|
|
if (attach->attachment_point == attachment_point) {
|
|
g_array_remove_index_fast (fb->attachments, i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (gst_is_gl_memory (GST_MEMORY_CAST (mem))) {
|
|
_attach_gl_memory (fb, attachment_point, (GstGLMemory *) mem);
|
|
} else if (gst_is_gl_renderbuffer (GST_MEMORY_CAST (mem))) {
|
|
_attach_renderbuffer (fb, attachment_point, (GstGLRenderbuffer *) mem);
|
|
} else {
|
|
g_assert_not_reached ();
|
|
return;
|
|
}
|
|
|
|
_update_effective_dimensions (fb);
|
|
}
|
|
|
|
void
|
|
gst_gl_framebuffer_get_effective_dimensions (GstGLFramebuffer * fb,
|
|
guint * width, guint * height)
|
|
{
|
|
g_return_if_fail (GST_IS_GL_FRAMEBUFFER (fb));
|
|
|
|
if (width)
|
|
*width = fb->priv->effective_width;
|
|
if (height)
|
|
*height = fb->priv->effective_height;
|
|
}
|
|
|
|
gboolean
|
|
gst_gl_context_check_framebuffer_status (GstGLContext * context)
|
|
{
|
|
g_return_val_if_fail (GST_IS_GL_CONTEXT (context), FALSE);
|
|
|
|
switch (context->gl_vtable->CheckFramebufferStatus (GL_FRAMEBUFFER)) {
|
|
case GL_FRAMEBUFFER_COMPLETE:
|
|
return TRUE;
|
|
break;
|
|
case GL_FRAMEBUFFER_UNSUPPORTED:
|
|
GST_WARNING_OBJECT (context, "GL_FRAMEBUFFER_UNSUPPORTED");
|
|
break;
|
|
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
|
|
GST_WARNING_OBJECT (context, "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT");
|
|
break;
|
|
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
|
|
GST_WARNING_OBJECT (context,
|
|
"GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT");
|
|
break;
|
|
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
|
|
GST_WARNING_OBJECT (context, "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS");
|
|
break;
|
|
#if GST_GL_HAVE_OPENGL
|
|
case GL_FRAMEBUFFER_UNDEFINED:
|
|
GST_WARNING_OBJECT (context, "GL_FRAMEBUFFER_UNDEFINED");
|
|
break;
|
|
#endif
|
|
default:
|
|
GST_WARNING_OBJECT (context, "Unknown FBO error");
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
guint
|
|
gst_gl_framebuffer_get_id (GstGLFramebuffer * fb)
|
|
{
|
|
g_return_val_if_fail (GST_IS_GL_FRAMEBUFFER (fb), 0);
|
|
|
|
return fb->fbo_id;
|
|
}
|