| /* |
| * GStreamer |
| * Copyright (C) 2015 Matthew Waters <matthew@centricular.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 <string.h> |
| |
| #include "gstglrenderbuffer.h" |
| |
| #include "gstglcontext.h" |
| #include "gstglfuncs.h" |
| #include "gstglmemory.h" |
| |
| /** |
| * SECTION:gstglrenderbuffer |
| * @title: GstGLRenderBuffer |
| * @short_description: memory subclass for GL renderbuffer objects |
| * @see_also: #GstMemory, #GstAllocator |
| * |
| * GstGLRenderbuffer is a #GstGLBaseMemory subclass providing support for |
| * OpenGL renderbuffers. |
| * |
| * #GstGLRenderbuffer is created or wrapped through gst_gl_base_memory_alloc() |
| * with #GstGLRenderbufferAllocationParams. |
| * |
| * Since: 1.10 |
| */ |
| |
| #define USING_OPENGL(context) (gst_gl_context_check_gl_version (context, GST_GL_API_OPENGL, 1, 0)) |
| #define USING_OPENGL3(context) (gst_gl_context_check_gl_version (context, GST_GL_API_OPENGL3, 3, 1)) |
| #define USING_GLES(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES, 1, 0)) |
| #define USING_GLES2(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 2, 0)) |
| #define USING_GLES3(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 3, 0)) |
| |
| static GstAllocator *_gl_renderbuffer_allocator; |
| |
| GST_DEBUG_CATEGORY_STATIC (GST_CAT_GL_RENDERBUFFER); |
| #define GST_CAT_DEFAULT GST_CAT_GL_RENDERBUFFER |
| |
| G_DEFINE_TYPE (GstGLRenderbufferAllocator, gst_gl_renderbuffer_allocator, |
| GST_TYPE_GL_BASE_MEMORY_ALLOCATOR); |
| |
| static guint |
| _new_renderbuffer (GstGLContext * context, guint format, guint width, |
| guint height) |
| { |
| const GstGLFuncs *gl = context->gl_vtable; |
| guint rbo_id; |
| |
| gl->GenRenderbuffers (1, &rbo_id); |
| gl->BindRenderbuffer (GL_RENDERBUFFER, rbo_id); |
| |
| gl->RenderbufferStorage (GL_RENDERBUFFER, format, width, height); |
| |
| gl->BindRenderbuffer (GL_RENDERBUFFER, 0); |
| |
| return rbo_id; |
| } |
| |
| static gboolean |
| _gl_rbo_create (GstGLRenderbuffer * gl_mem, GError ** error) |
| { |
| if (!gl_mem->renderbuffer_wrapped) { |
| GstGLContext *context = gl_mem->mem.context; |
| GLenum internal_format; |
| GLenum tex_format; |
| GLenum renderbuffer_type; |
| |
| tex_format = gl_mem->renderbuffer_format; |
| renderbuffer_type = GL_UNSIGNED_BYTE; |
| if (gl_mem->renderbuffer_format == GST_GL_RGB565) { |
| tex_format = GST_GL_RGB; |
| renderbuffer_type = GL_UNSIGNED_SHORT_5_6_5; |
| } |
| |
| internal_format = |
| gst_gl_sized_gl_format_from_gl_format_type (context, tex_format, |
| renderbuffer_type); |
| |
| gl_mem->renderbuffer_id = |
| _new_renderbuffer (context, internal_format, |
| gst_gl_renderbuffer_get_width (gl_mem), |
| gst_gl_renderbuffer_get_height (gl_mem)); |
| |
| GST_CAT_TRACE (GST_CAT_GL_RENDERBUFFER, "Generating renderbuffer id:%u " |
| "format:%u dimensions:%ux%u", gl_mem->renderbuffer_id, internal_format, |
| gst_gl_renderbuffer_get_width (gl_mem), |
| gst_gl_renderbuffer_get_height (gl_mem)); |
| } |
| |
| return TRUE; |
| } |
| |
| static void |
| gst_gl_renderbuffer_init (GstGLRenderbuffer * mem, GstAllocator * allocator, |
| GstMemory * parent, GstGLContext * context, |
| GstGLFormat renderbuffer_format, GstAllocationParams * params, |
| guint width, guint height, gpointer user_data, GDestroyNotify notify) |
| { |
| gsize size; |
| guint tex_type; |
| |
| tex_type = GL_UNSIGNED_BYTE; |
| if (renderbuffer_format == GST_GL_RGB565) |
| tex_type = GL_UNSIGNED_SHORT_5_6_5; |
| size = |
| gst_gl_format_type_n_bytes (renderbuffer_format, |
| tex_type) * width * height; |
| |
| mem->renderbuffer_format = renderbuffer_format; |
| mem->width = width; |
| mem->height = height; |
| |
| gst_gl_base_memory_init ((GstGLBaseMemory *) mem, allocator, parent, context, |
| params, size, user_data, notify); |
| |
| GST_CAT_DEBUG (GST_CAT_GL_RENDERBUFFER, "new GL renderbuffer context:%" |
| GST_PTR_FORMAT " memory:%p format:%u dimensions:%ux%u ", context, mem, |
| mem->renderbuffer_format, gst_gl_renderbuffer_get_width (mem), |
| gst_gl_renderbuffer_get_height (mem)); |
| } |
| |
| static gpointer |
| _gl_rbo_map (GstGLRenderbuffer * gl_mem, GstMapInfo * info, gsize maxsize) |
| { |
| GST_CAT_WARNING (GST_CAT_GL_RENDERBUFFER, "Renderbuffer's cannot be mapped"); |
| |
| return NULL; |
| } |
| |
| static void |
| _gl_rbo_unmap (GstGLRenderbuffer * gl_mem, GstMapInfo * info) |
| { |
| } |
| |
| static GstMemory * |
| _gl_rbo_copy (GstGLRenderbuffer * src, gssize offset, gssize size) |
| { |
| GST_CAT_WARNING (GST_CAT_GL_RENDERBUFFER, "Renderbuffer's cannot be copied"); |
| |
| return NULL; |
| } |
| |
| static GstMemory * |
| _gl_rbo_alloc (GstAllocator * allocator, gsize size, |
| GstAllocationParams * params) |
| { |
| g_warning ("Use gst_gl_base_memory_alloc to allocate from this allocator"); |
| |
| return NULL; |
| } |
| |
| static void |
| _gl_rbo_destroy (GstGLRenderbuffer * gl_mem) |
| { |
| const GstGLFuncs *gl = gl_mem->mem.context->gl_vtable; |
| |
| if (gl_mem->renderbuffer_id && !gl_mem->renderbuffer_wrapped) |
| gl->DeleteRenderbuffers (1, &gl_mem->renderbuffer_id); |
| } |
| |
| static GstGLRenderbuffer * |
| _default_gl_rbo_alloc (GstGLRenderbufferAllocator * allocator, |
| GstGLRenderbufferAllocationParams * params) |
| { |
| guint alloc_flags = params->parent.alloc_flags; |
| GstGLRenderbuffer *mem; |
| |
| g_return_val_if_fail ((alloc_flags & |
| GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_WRAP_SYSMEM) == 0, NULL); |
| |
| mem = g_new0 (GstGLRenderbuffer, 1); |
| |
| if (alloc_flags & GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_WRAP_GPU_HANDLE) { |
| mem->renderbuffer_id = GPOINTER_TO_UINT (params->parent.gl_handle); |
| mem->renderbuffer_wrapped = TRUE; |
| } |
| |
| gst_gl_renderbuffer_init (mem, GST_ALLOCATOR_CAST (allocator), NULL, |
| params->parent.context, params->renderbuffer_format, |
| params->parent.alloc_params, params->width, params->height, |
| params->parent.user_data, params->parent.notify); |
| |
| return mem; |
| } |
| |
| static void |
| gst_gl_renderbuffer_allocator_class_init (GstGLRenderbufferAllocatorClass * |
| klass) |
| { |
| GstGLBaseMemoryAllocatorClass *gl_base; |
| GstAllocatorClass *allocator_class; |
| |
| gl_base = (GstGLBaseMemoryAllocatorClass *) klass; |
| allocator_class = (GstAllocatorClass *) klass; |
| |
| gl_base->alloc = |
| (GstGLBaseMemoryAllocatorAllocFunction) _default_gl_rbo_alloc; |
| gl_base->create = (GstGLBaseMemoryAllocatorCreateFunction) _gl_rbo_create; |
| gl_base->destroy = (GstGLBaseMemoryAllocatorDestroyFunction) _gl_rbo_destroy; |
| |
| allocator_class->alloc = _gl_rbo_alloc; |
| } |
| |
| static void |
| gst_gl_renderbuffer_allocator_init (GstGLRenderbufferAllocator * allocator) |
| { |
| GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator); |
| |
| alloc->mem_type = GST_GL_RENDERBUFFER_ALLOCATOR_NAME; |
| |
| alloc->mem_map_full = (GstMemoryMapFullFunction) _gl_rbo_map; |
| alloc->mem_unmap_full = (GstMemoryUnmapFullFunction) _gl_rbo_unmap; |
| alloc->mem_copy = (GstMemoryCopyFunction) _gl_rbo_copy; |
| |
| GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC); |
| } |
| |
| /** |
| * gst_gl_renderbuffer_get_width: |
| * @gl_mem: a #GstGLRenderbuffer |
| * |
| * Returns: the configured width of @gl_mem |
| * |
| * Since: 1.10 |
| */ |
| gint |
| gst_gl_renderbuffer_get_width (GstGLRenderbuffer * gl_mem) |
| { |
| g_return_val_if_fail (gst_is_gl_renderbuffer ((GstMemory *) gl_mem), 0); |
| |
| return gl_mem->width; |
| } |
| |
| /** |
| * gst_gl_renderbuffer_get_height: |
| * @gl_mem: a #GstGLRenderbuffer |
| * |
| * Returns: the configured height of @gl_mem |
| * |
| * Since: 1.10 |
| */ |
| gint |
| gst_gl_renderbuffer_get_height (GstGLRenderbuffer * gl_mem) |
| { |
| g_return_val_if_fail (gst_is_gl_renderbuffer ((GstMemory *) gl_mem), 0); |
| |
| return gl_mem->height; |
| } |
| |
| /** |
| * gst_gl_renderbuffer_get_format: |
| * @gl_mem: a #GstGLRenderbuffer |
| * |
| * Returns: the #GstGLFormat of @gl_mem |
| * |
| * Since: 1.12 |
| */ |
| GstGLFormat |
| gst_gl_renderbuffer_get_format (GstGLRenderbuffer * gl_mem) |
| { |
| g_return_val_if_fail (gst_is_gl_renderbuffer ((GstMemory *) gl_mem), 0); |
| |
| return gl_mem->renderbuffer_format; |
| } |
| |
| /** |
| * gst_gl_renderbuffer_get_id: |
| * @gl_mem: a #GstGLRenderbuffer |
| * |
| * Returns: the OpenGL renderbuffer handle of @gl_mem |
| * |
| * Since: 1.10 |
| */ |
| guint |
| gst_gl_renderbuffer_get_id (GstGLRenderbuffer * gl_mem) |
| { |
| g_return_val_if_fail (gst_is_gl_renderbuffer ((GstMemory *) gl_mem), 0); |
| |
| return gl_mem->renderbuffer_id; |
| } |
| |
| /** |
| * gst_gl_renderbuffer_init_once: |
| * |
| * Initializes the GL Base Texture allocator. It is safe to call this function |
| * multiple times. This must be called before any other GstGLRenderbuffer operation. |
| * |
| * Since: 1.10 |
| */ |
| void |
| gst_gl_renderbuffer_init_once (void) |
| { |
| static volatile gsize _init = 0; |
| |
| if (g_once_init_enter (&_init)) { |
| gst_gl_base_memory_init_once (); |
| |
| GST_DEBUG_CATEGORY_INIT (GST_CAT_GL_RENDERBUFFER, "glrenderbuffermemory", 0, |
| "OpenGL Renderbuffer memory"); |
| |
| _gl_renderbuffer_allocator = |
| g_object_new (GST_TYPE_GL_RENDERBUFFER_ALLOCATOR, NULL); |
| gst_object_ref_sink (_gl_renderbuffer_allocator); |
| GST_OBJECT_FLAG_SET (_gl_renderbuffer_allocator, |
| GST_OBJECT_FLAG_MAY_BE_LEAKED); |
| |
| gst_allocator_register (GST_GL_RENDERBUFFER_ALLOCATOR_NAME, |
| _gl_renderbuffer_allocator); |
| |
| g_once_init_leave (&_init, 1); |
| } |
| } |
| |
| /** |
| * gst_is_gl_renderbuffer: |
| * @mem:a #GstMemory |
| * |
| * Returns: whether the memory at @mem is a #GstGLRenderbuffer |
| * |
| * Since: 1.10 |
| */ |
| gboolean |
| gst_is_gl_renderbuffer (GstMemory * mem) |
| { |
| return mem != NULL && mem->allocator != NULL |
| && g_type_is_a (G_OBJECT_TYPE (mem->allocator), |
| GST_TYPE_GL_RENDERBUFFER_ALLOCATOR); |
| } |
| |
| G_DEFINE_BOXED_TYPE (GstGLRenderbufferAllocationParams, |
| gst_gl_renderbuffer_allocation_params, |
| (GBoxedCopyFunc) gst_gl_allocation_params_copy, |
| (GBoxedFreeFunc) gst_gl_allocation_params_free); |
| |
| static void |
| _gst_gl_rb_alloc_params_free_data (GstGLRenderbufferAllocationParams * params) |
| { |
| gst_gl_allocation_params_free_data (¶ms->parent); |
| } |
| |
| static void |
| _gst_gl_rb_alloc_params_copy_data (GstGLRenderbufferAllocationParams * src_vid, |
| GstGLRenderbufferAllocationParams * dest_vid) |
| { |
| GstGLAllocationParams *src = (GstGLAllocationParams *) src_vid; |
| GstGLAllocationParams *dest = (GstGLAllocationParams *) dest_vid; |
| |
| gst_gl_allocation_params_copy_data (src, dest); |
| |
| dest_vid->renderbuffer_format = src_vid->renderbuffer_format; |
| dest_vid->width = src_vid->width; |
| dest_vid->height = src_vid->height; |
| } |
| |
| static gboolean |
| _gst_gl_renderbuffer_allocation_params_init_full |
| (GstGLRenderbufferAllocationParams * params, gsize struct_size, |
| guint alloc_flags, GstGLAllocationParamsCopyFunc copy, |
| GstGLAllocationParamsFreeFunc free, GstGLContext * context, |
| GstAllocationParams * alloc_params, guint width, guint height, |
| GstGLFormat renderbuffer_format, gpointer wrapped_data, |
| gpointer gl_handle, gpointer user_data, GDestroyNotify notify) |
| { |
| g_return_val_if_fail (params != NULL, FALSE); |
| g_return_val_if_fail (copy != NULL, FALSE); |
| g_return_val_if_fail (free != NULL, FALSE); |
| g_return_val_if_fail (GST_IS_GL_CONTEXT (context), FALSE); |
| |
| memset (params, 0, sizeof (*params)); |
| |
| if (!gst_gl_allocation_params_init ((GstGLAllocationParams *) params, |
| struct_size, alloc_flags, copy, free, context, 0, alloc_params, |
| wrapped_data, gl_handle, user_data, notify)) |
| return FALSE; |
| |
| params->renderbuffer_format = renderbuffer_format; |
| params->width = width; |
| params->height = height; |
| |
| return TRUE; |
| } |
| |
| /** |
| * gst_gl_renderbuffer_allocation_params_new: |
| * @context: a #GstGLContext |
| * @alloc_params: (allow-none): the #GstAllocationParams for sysmem mappings of the texture |
| * @width: the width of the renderbuffer |
| * @height: the height of the renderbuffer |
| * @renderbuffer_format: the #GstGLFormat for the created textures |
| * |
| * Returns: a new #GstGLRenderbufferAllocationParams for allocating #GstGLRenderbuffer's |
| * |
| * Since: 1.10 |
| */ |
| GstGLRenderbufferAllocationParams * |
| gst_gl_renderbuffer_allocation_params_new (GstGLContext * context, |
| GstAllocationParams * alloc_params, GstGLFormat renderbuffer_format, |
| guint width, guint height) |
| { |
| GstGLRenderbufferAllocationParams *params = |
| g_new0 (GstGLRenderbufferAllocationParams, 1); |
| |
| if (!_gst_gl_renderbuffer_allocation_params_init_full (params, |
| sizeof (GstGLRenderbufferAllocationParams), |
| GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_ALLOC | |
| GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_VIDEO, |
| (GstGLAllocationParamsCopyFunc) _gst_gl_rb_alloc_params_copy_data, |
| (GstGLAllocationParamsFreeFunc) _gst_gl_rb_alloc_params_free_data, |
| context, alloc_params, width, height, renderbuffer_format, NULL, 0, |
| NULL, NULL)) { |
| g_free (params); |
| return NULL; |
| } |
| |
| return params; |
| } |
| |
| /** |
| * gst_gl_renderbuffer_allocation_params_new_wrapped: |
| * @context: a #GstGLContext |
| * @alloc_params: (allow-none): the #GstAllocationParams for @tex_id |
| * @width: the width of the renderbuffer |
| * @height: the height of the renderbuffer |
| * @renderbuffer_format: the #GstGLFormat for @tex_id |
| * @gl_handle: the GL handle to wrap |
| * @user_data: (allow-none): user data to call @notify with |
| * @notify: (allow-none): a #GDestroyNotify |
| * |
| * Returns: a new #GstGLRenderbufferAllocationParams for wrapping @gl_handle as a |
| * renderbuffer |
| * |
| * Since: 1.10 |
| */ |
| GstGLRenderbufferAllocationParams * |
| gst_gl_renderbuffer_allocation_params_new_wrapped (GstGLContext * context, |
| GstAllocationParams * alloc_params, GstGLFormat renderbuffer_format, |
| guint width, guint height, gpointer gl_handle, gpointer user_data, |
| GDestroyNotify notify) |
| { |
| GstGLRenderbufferAllocationParams *params = |
| g_new0 (GstGLRenderbufferAllocationParams, 1); |
| |
| if (!_gst_gl_renderbuffer_allocation_params_init_full (params, |
| sizeof (GstGLRenderbufferAllocationParams), |
| GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_WRAP_GPU_HANDLE | |
| GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_VIDEO, |
| (GstGLAllocationParamsCopyFunc) _gst_gl_rb_alloc_params_copy_data, |
| (GstGLAllocationParamsFreeFunc) _gst_gl_rb_alloc_params_free_data, |
| context, alloc_params, width, height, renderbuffer_format, NULL, |
| gl_handle, user_data, notify)) { |
| g_free (params); |
| return NULL; |
| } |
| |
| return params; |
| } |