1 /*
2  * GStreamer
3  * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include <string.h>
26 
27 #include "gstglrenderbuffer.h"
28 
29 #include "gstglcontext.h"
30 #include "gstglfuncs.h"
31 #include "gstglmemory.h"
32 
33 /**
34  * SECTION:gstglrenderbuffer
35  * @title: GstGLRenderBuffer
36  * @short_description: memory subclass for GL renderbuffer objects
37  * @see_also: #GstMemory, #GstAllocator
38  *
39  * GstGLRenderbuffer is a #GstGLBaseMemory subclass providing support for
40  * OpenGL renderbuffers.
41  *
42  * #GstGLRenderbuffer is created or wrapped through gst_gl_base_memory_alloc()
43  * with #GstGLRenderbufferAllocationParams.
44  *
45  * Since: 1.10
46  */
47 
48 #define USING_OPENGL(context) (gst_gl_context_check_gl_version (context, GST_GL_API_OPENGL, 1, 0))
49 #define USING_OPENGL3(context) (gst_gl_context_check_gl_version (context, GST_GL_API_OPENGL3, 3, 1))
50 #define USING_GLES(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES, 1, 0))
51 #define USING_GLES2(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 2, 0))
52 #define USING_GLES3(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 3, 0))
53 
54 static GstAllocator *_gl_renderbuffer_allocator;
55 
56 GST_DEBUG_CATEGORY_STATIC (GST_CAT_GL_RENDERBUFFER);
57 #define GST_CAT_DEFAULT GST_CAT_GL_RENDERBUFFER
58 
59 G_DEFINE_TYPE (GstGLRenderbufferAllocator, gst_gl_renderbuffer_allocator,
60     GST_TYPE_GL_BASE_MEMORY_ALLOCATOR);
61 
62 static guint
_new_renderbuffer(GstGLContext * context,guint format,guint width,guint height)63 _new_renderbuffer (GstGLContext * context, guint format, guint width,
64     guint height)
65 {
66   const GstGLFuncs *gl = context->gl_vtable;
67   guint rbo_id;
68 
69   gl->GenRenderbuffers (1, &rbo_id);
70   gl->BindRenderbuffer (GL_RENDERBUFFER, rbo_id);
71 
72   gl->RenderbufferStorage (GL_RENDERBUFFER, format, width, height);
73 
74   gl->BindRenderbuffer (GL_RENDERBUFFER, 0);
75 
76   return rbo_id;
77 }
78 
79 static gboolean
_gl_rbo_create(GstGLRenderbuffer * gl_mem,GError ** error)80 _gl_rbo_create (GstGLRenderbuffer * gl_mem, GError ** error)
81 {
82   if (!gl_mem->renderbuffer_wrapped) {
83     GstGLContext *context = gl_mem->mem.context;
84     GLenum internal_format;
85     GLenum tex_format;
86     GLenum renderbuffer_type;
87 
88     tex_format = gl_mem->renderbuffer_format;
89     renderbuffer_type = GL_UNSIGNED_BYTE;
90     if (gl_mem->renderbuffer_format == GST_GL_RGB565) {
91       tex_format = GST_GL_RGB;
92       renderbuffer_type = GL_UNSIGNED_SHORT_5_6_5;
93     }
94 
95     internal_format =
96         gst_gl_sized_gl_format_from_gl_format_type (context, tex_format,
97         renderbuffer_type);
98 
99     gl_mem->renderbuffer_id =
100         _new_renderbuffer (context, internal_format,
101         gst_gl_renderbuffer_get_width (gl_mem),
102         gst_gl_renderbuffer_get_height (gl_mem));
103 
104     GST_CAT_TRACE (GST_CAT_GL_RENDERBUFFER, "Generating renderbuffer id:%u "
105         "format:%u dimensions:%ux%u", gl_mem->renderbuffer_id, internal_format,
106         gst_gl_renderbuffer_get_width (gl_mem),
107         gst_gl_renderbuffer_get_height (gl_mem));
108   }
109 
110   return TRUE;
111 }
112 
113 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)114 gst_gl_renderbuffer_init (GstGLRenderbuffer * mem, GstAllocator * allocator,
115     GstMemory * parent, GstGLContext * context,
116     GstGLFormat renderbuffer_format, GstAllocationParams * params,
117     guint width, guint height, gpointer user_data, GDestroyNotify notify)
118 {
119   gsize size;
120   guint tex_type;
121 
122   tex_type = GL_UNSIGNED_BYTE;
123   if (renderbuffer_format == GST_GL_RGB565)
124     tex_type = GL_UNSIGNED_SHORT_5_6_5;
125   size =
126       gst_gl_format_type_n_bytes (renderbuffer_format,
127       tex_type) * width * height;
128 
129   mem->renderbuffer_format = renderbuffer_format;
130   mem->width = width;
131   mem->height = height;
132 
133   gst_gl_base_memory_init ((GstGLBaseMemory *) mem, allocator, parent, context,
134       params, size, user_data, notify);
135 
136   GST_CAT_DEBUG (GST_CAT_GL_RENDERBUFFER, "new GL renderbuffer context:%"
137       GST_PTR_FORMAT " memory:%p format:%u dimensions:%ux%u ", context, mem,
138       mem->renderbuffer_format, gst_gl_renderbuffer_get_width (mem),
139       gst_gl_renderbuffer_get_height (mem));
140 }
141 
142 static gpointer
_gl_rbo_map(GstGLRenderbuffer * gl_mem,GstMapInfo * info,gsize maxsize)143 _gl_rbo_map (GstGLRenderbuffer * gl_mem, GstMapInfo * info, gsize maxsize)
144 {
145   GST_CAT_WARNING (GST_CAT_GL_RENDERBUFFER, "Renderbuffer's cannot be mapped");
146 
147   return NULL;
148 }
149 
150 static void
_gl_rbo_unmap(GstGLRenderbuffer * gl_mem,GstMapInfo * info)151 _gl_rbo_unmap (GstGLRenderbuffer * gl_mem, GstMapInfo * info)
152 {
153 }
154 
155 static GstMemory *
_gl_rbo_copy(GstGLRenderbuffer * src,gssize offset,gssize size)156 _gl_rbo_copy (GstGLRenderbuffer * src, gssize offset, gssize size)
157 {
158   GST_CAT_WARNING (GST_CAT_GL_RENDERBUFFER, "Renderbuffer's cannot be copied");
159 
160   return NULL;
161 }
162 
163 static GstMemory *
_gl_rbo_alloc(GstAllocator * allocator,gsize size,GstAllocationParams * params)164 _gl_rbo_alloc (GstAllocator * allocator, gsize size,
165     GstAllocationParams * params)
166 {
167   g_warning ("Use gst_gl_base_memory_alloc to allocate from this allocator");
168 
169   return NULL;
170 }
171 
172 static void
_gl_rbo_destroy(GstGLRenderbuffer * gl_mem)173 _gl_rbo_destroy (GstGLRenderbuffer * gl_mem)
174 {
175   const GstGLFuncs *gl = gl_mem->mem.context->gl_vtable;
176 
177   if (gl_mem->renderbuffer_id && !gl_mem->renderbuffer_wrapped)
178     gl->DeleteRenderbuffers (1, &gl_mem->renderbuffer_id);
179 }
180 
181 static GstGLRenderbuffer *
_default_gl_rbo_alloc(GstGLRenderbufferAllocator * allocator,GstGLRenderbufferAllocationParams * params)182 _default_gl_rbo_alloc (GstGLRenderbufferAllocator * allocator,
183     GstGLRenderbufferAllocationParams * params)
184 {
185   guint alloc_flags = params->parent.alloc_flags;
186   GstGLRenderbuffer *mem;
187 
188   g_return_val_if_fail ((alloc_flags &
189           GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_WRAP_SYSMEM) == 0, NULL);
190 
191   mem = g_new0 (GstGLRenderbuffer, 1);
192 
193   if (alloc_flags & GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_WRAP_GPU_HANDLE) {
194     mem->renderbuffer_id = GPOINTER_TO_UINT (params->parent.gl_handle);
195     mem->renderbuffer_wrapped = TRUE;
196   }
197 
198   gst_gl_renderbuffer_init (mem, GST_ALLOCATOR_CAST (allocator), NULL,
199       params->parent.context, params->renderbuffer_format,
200       params->parent.alloc_params, params->width, params->height,
201       params->parent.user_data, params->parent.notify);
202 
203   return mem;
204 }
205 
206 static void
gst_gl_renderbuffer_allocator_class_init(GstGLRenderbufferAllocatorClass * klass)207 gst_gl_renderbuffer_allocator_class_init (GstGLRenderbufferAllocatorClass *
208     klass)
209 {
210   GstGLBaseMemoryAllocatorClass *gl_base;
211   GstAllocatorClass *allocator_class;
212 
213   gl_base = (GstGLBaseMemoryAllocatorClass *) klass;
214   allocator_class = (GstAllocatorClass *) klass;
215 
216   gl_base->alloc =
217       (GstGLBaseMemoryAllocatorAllocFunction) _default_gl_rbo_alloc;
218   gl_base->create = (GstGLBaseMemoryAllocatorCreateFunction) _gl_rbo_create;
219   gl_base->destroy = (GstGLBaseMemoryAllocatorDestroyFunction) _gl_rbo_destroy;
220 
221   allocator_class->alloc = _gl_rbo_alloc;
222 }
223 
224 static void
gst_gl_renderbuffer_allocator_init(GstGLRenderbufferAllocator * allocator)225 gst_gl_renderbuffer_allocator_init (GstGLRenderbufferAllocator * allocator)
226 {
227   GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
228 
229   alloc->mem_type = GST_GL_RENDERBUFFER_ALLOCATOR_NAME;
230 
231   alloc->mem_map_full = (GstMemoryMapFullFunction) _gl_rbo_map;
232   alloc->mem_unmap_full = (GstMemoryUnmapFullFunction) _gl_rbo_unmap;
233   alloc->mem_copy = (GstMemoryCopyFunction) _gl_rbo_copy;
234 
235   GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC);
236 }
237 
238 /**
239  * gst_gl_renderbuffer_get_width:
240  * @gl_mem: a #GstGLRenderbuffer
241  *
242  * Returns: the configured width of @gl_mem
243  *
244  * Since: 1.10
245  */
246 gint
gst_gl_renderbuffer_get_width(GstGLRenderbuffer * gl_mem)247 gst_gl_renderbuffer_get_width (GstGLRenderbuffer * gl_mem)
248 {
249   g_return_val_if_fail (gst_is_gl_renderbuffer ((GstMemory *) gl_mem), 0);
250 
251   return gl_mem->width;
252 }
253 
254 /**
255  * gst_gl_renderbuffer_get_height:
256  * @gl_mem: a #GstGLRenderbuffer
257  *
258  * Returns: the configured height of @gl_mem
259  *
260  * Since: 1.10
261  */
262 gint
gst_gl_renderbuffer_get_height(GstGLRenderbuffer * gl_mem)263 gst_gl_renderbuffer_get_height (GstGLRenderbuffer * gl_mem)
264 {
265   g_return_val_if_fail (gst_is_gl_renderbuffer ((GstMemory *) gl_mem), 0);
266 
267   return gl_mem->height;
268 }
269 
270 /**
271  * gst_gl_renderbuffer_get_format:
272  * @gl_mem: a #GstGLRenderbuffer
273  *
274  * Returns: the #GstGLFormat of @gl_mem
275  *
276  * Since: 1.12
277  */
278 GstGLFormat
gst_gl_renderbuffer_get_format(GstGLRenderbuffer * gl_mem)279 gst_gl_renderbuffer_get_format (GstGLRenderbuffer * gl_mem)
280 {
281   g_return_val_if_fail (gst_is_gl_renderbuffer ((GstMemory *) gl_mem), 0);
282 
283   return gl_mem->renderbuffer_format;
284 }
285 
286 /**
287  * gst_gl_renderbuffer_get_id:
288  * @gl_mem: a #GstGLRenderbuffer
289  *
290  * Returns: the OpenGL renderbuffer handle of @gl_mem
291  *
292  * Since: 1.10
293  */
294 guint
gst_gl_renderbuffer_get_id(GstGLRenderbuffer * gl_mem)295 gst_gl_renderbuffer_get_id (GstGLRenderbuffer * gl_mem)
296 {
297   g_return_val_if_fail (gst_is_gl_renderbuffer ((GstMemory *) gl_mem), 0);
298 
299   return gl_mem->renderbuffer_id;
300 }
301 
302 /**
303  * gst_gl_renderbuffer_init_once:
304  *
305  * Initializes the GL Base Texture allocator. It is safe to call this function
306  * multiple times.  This must be called before any other GstGLRenderbuffer operation.
307  *
308  * Since: 1.10
309  */
310 void
gst_gl_renderbuffer_init_once(void)311 gst_gl_renderbuffer_init_once (void)
312 {
313   static volatile gsize _init = 0;
314 
315   if (g_once_init_enter (&_init)) {
316     gst_gl_base_memory_init_once ();
317 
318     GST_DEBUG_CATEGORY_INIT (GST_CAT_GL_RENDERBUFFER, "glrenderbuffermemory", 0,
319         "OpenGL Renderbuffer memory");
320 
321     _gl_renderbuffer_allocator =
322         g_object_new (GST_TYPE_GL_RENDERBUFFER_ALLOCATOR, NULL);
323     gst_object_ref_sink (_gl_renderbuffer_allocator);
324     GST_OBJECT_FLAG_SET (_gl_renderbuffer_allocator,
325         GST_OBJECT_FLAG_MAY_BE_LEAKED);
326 
327     gst_allocator_register (GST_GL_RENDERBUFFER_ALLOCATOR_NAME,
328         _gl_renderbuffer_allocator);
329 
330     g_once_init_leave (&_init, 1);
331   }
332 }
333 
334 /**
335  * gst_is_gl_renderbuffer:
336  * @mem:a #GstMemory
337  *
338  * Returns: whether the memory at @mem is a #GstGLRenderbuffer
339  *
340  * Since: 1.10
341  */
342 gboolean
gst_is_gl_renderbuffer(GstMemory * mem)343 gst_is_gl_renderbuffer (GstMemory * mem)
344 {
345   return mem != NULL && mem->allocator != NULL
346       && g_type_is_a (G_OBJECT_TYPE (mem->allocator),
347       GST_TYPE_GL_RENDERBUFFER_ALLOCATOR);
348 }
349 
350 G_DEFINE_BOXED_TYPE (GstGLRenderbufferAllocationParams,
351     gst_gl_renderbuffer_allocation_params,
352     (GBoxedCopyFunc) gst_gl_allocation_params_copy,
353     (GBoxedFreeFunc) gst_gl_allocation_params_free);
354 
355 static void
_gst_gl_rb_alloc_params_free_data(GstGLRenderbufferAllocationParams * params)356 _gst_gl_rb_alloc_params_free_data (GstGLRenderbufferAllocationParams * params)
357 {
358   gst_gl_allocation_params_free_data (&params->parent);
359 }
360 
361 static void
_gst_gl_rb_alloc_params_copy_data(GstGLRenderbufferAllocationParams * src_vid,GstGLRenderbufferAllocationParams * dest_vid)362 _gst_gl_rb_alloc_params_copy_data (GstGLRenderbufferAllocationParams * src_vid,
363     GstGLRenderbufferAllocationParams * dest_vid)
364 {
365   GstGLAllocationParams *src = (GstGLAllocationParams *) src_vid;
366   GstGLAllocationParams *dest = (GstGLAllocationParams *) dest_vid;
367 
368   gst_gl_allocation_params_copy_data (src, dest);
369 
370   dest_vid->renderbuffer_format = src_vid->renderbuffer_format;
371   dest_vid->width = src_vid->width;
372   dest_vid->height = src_vid->height;
373 }
374 
375 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)376     _gst_gl_renderbuffer_allocation_params_init_full
377     (GstGLRenderbufferAllocationParams * params, gsize struct_size,
378     guint alloc_flags, GstGLAllocationParamsCopyFunc copy,
379     GstGLAllocationParamsFreeFunc free, GstGLContext * context,
380     GstAllocationParams * alloc_params, guint width, guint height,
381     GstGLFormat renderbuffer_format, gpointer wrapped_data,
382     gpointer gl_handle, gpointer user_data, GDestroyNotify notify)
383 {
384   g_return_val_if_fail (params != NULL, FALSE);
385   g_return_val_if_fail (copy != NULL, FALSE);
386   g_return_val_if_fail (free != NULL, FALSE);
387   g_return_val_if_fail (GST_IS_GL_CONTEXT (context), FALSE);
388 
389   memset (params, 0, sizeof (*params));
390 
391   if (!gst_gl_allocation_params_init ((GstGLAllocationParams *) params,
392           struct_size, alloc_flags, copy, free, context, 0, alloc_params,
393           wrapped_data, gl_handle, user_data, notify))
394     return FALSE;
395 
396   params->renderbuffer_format = renderbuffer_format;
397   params->width = width;
398   params->height = height;
399 
400   return TRUE;
401 }
402 
403 /**
404  * gst_gl_renderbuffer_allocation_params_new:
405  * @context: a #GstGLContext
406  * @alloc_params: (allow-none): the #GstAllocationParams for sysmem mappings of the texture
407  * @width: the width of the renderbuffer
408  * @height: the height of the renderbuffer
409  * @renderbuffer_format: the #GstGLFormat for the created textures
410  *
411  * Returns: a new #GstGLRenderbufferAllocationParams for allocating #GstGLRenderbuffer's
412  *
413  * Since: 1.10
414  */
415 GstGLRenderbufferAllocationParams *
gst_gl_renderbuffer_allocation_params_new(GstGLContext * context,GstAllocationParams * alloc_params,GstGLFormat renderbuffer_format,guint width,guint height)416 gst_gl_renderbuffer_allocation_params_new (GstGLContext * context,
417     GstAllocationParams * alloc_params, GstGLFormat renderbuffer_format,
418     guint width, guint height)
419 {
420   GstGLRenderbufferAllocationParams *params =
421       g_new0 (GstGLRenderbufferAllocationParams, 1);
422 
423   if (!_gst_gl_renderbuffer_allocation_params_init_full (params,
424           sizeof (GstGLRenderbufferAllocationParams),
425           GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_ALLOC |
426           GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_VIDEO,
427           (GstGLAllocationParamsCopyFunc) _gst_gl_rb_alloc_params_copy_data,
428           (GstGLAllocationParamsFreeFunc) _gst_gl_rb_alloc_params_free_data,
429           context, alloc_params, width, height, renderbuffer_format, NULL, 0,
430           NULL, NULL)) {
431     g_free (params);
432     return NULL;
433   }
434 
435   return params;
436 }
437 
438 /**
439  * gst_gl_renderbuffer_allocation_params_new_wrapped:
440  * @context: a #GstGLContext
441  * @alloc_params: (allow-none): the #GstAllocationParams for @tex_id
442  * @width: the width of the renderbuffer
443  * @height: the height of the renderbuffer
444  * @renderbuffer_format: the #GstGLFormat for @tex_id
445  * @gl_handle: the GL handle to wrap
446  * @user_data: (allow-none): user data to call @notify with
447  * @notify: (allow-none): a #GDestroyNotify
448  *
449  * Returns: a new #GstGLRenderbufferAllocationParams for wrapping @gl_handle as a
450  *          renderbuffer
451  *
452  * Since: 1.10
453  */
454 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)455 gst_gl_renderbuffer_allocation_params_new_wrapped (GstGLContext * context,
456     GstAllocationParams * alloc_params, GstGLFormat renderbuffer_format,
457     guint width, guint height, gpointer gl_handle, gpointer user_data,
458     GDestroyNotify notify)
459 {
460   GstGLRenderbufferAllocationParams *params =
461       g_new0 (GstGLRenderbufferAllocationParams, 1);
462 
463   if (!_gst_gl_renderbuffer_allocation_params_init_full (params,
464           sizeof (GstGLRenderbufferAllocationParams),
465           GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_WRAP_GPU_HANDLE |
466           GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_VIDEO,
467           (GstGLAllocationParamsCopyFunc) _gst_gl_rb_alloc_params_copy_data,
468           (GstGLAllocationParamsFreeFunc) _gst_gl_rb_alloc_params_free_data,
469           context, alloc_params, width, height, renderbuffer_format, NULL,
470           gl_handle, user_data, notify)) {
471     g_free (params);
472     return NULL;
473   }
474 
475   return params;
476 }
477