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 (¶ms->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