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 GST_DEFINE_MINI_OBJECT_TYPE (GstGLRenderbuffer, gst_gl_renderbuffer);
63
64 static guint
_new_renderbuffer(GstGLContext * context,guint format,guint width,guint height)65 _new_renderbuffer (GstGLContext * context, guint format, guint width,
66 guint height)
67 {
68 const GstGLFuncs *gl = context->gl_vtable;
69 guint rbo_id;
70
71 gl->GenRenderbuffers (1, &rbo_id);
72 gl->BindRenderbuffer (GL_RENDERBUFFER, rbo_id);
73
74 gl->RenderbufferStorage (GL_RENDERBUFFER, format, width, height);
75
76 gl->BindRenderbuffer (GL_RENDERBUFFER, 0);
77
78 return rbo_id;
79 }
80
81 static gboolean
_gl_rbo_create(GstGLRenderbuffer * gl_mem,GError ** error)82 _gl_rbo_create (GstGLRenderbuffer * gl_mem, GError ** error)
83 {
84 if (!gl_mem->renderbuffer_wrapped) {
85 GstGLContext *context = gl_mem->mem.context;
86 GLenum internal_format;
87 GLenum tex_format;
88 GLenum renderbuffer_type;
89
90 tex_format = gl_mem->renderbuffer_format;
91 renderbuffer_type = GL_UNSIGNED_BYTE;
92 if (gl_mem->renderbuffer_format == GST_GL_RGB565) {
93 tex_format = GST_GL_RGB;
94 renderbuffer_type = GL_UNSIGNED_SHORT_5_6_5;
95 }
96
97 internal_format =
98 gst_gl_sized_gl_format_from_gl_format_type (context, tex_format,
99 renderbuffer_type);
100
101 gl_mem->renderbuffer_id =
102 _new_renderbuffer (context, internal_format,
103 gst_gl_renderbuffer_get_width (gl_mem),
104 gst_gl_renderbuffer_get_height (gl_mem));
105
106 GST_CAT_TRACE (GST_CAT_GL_RENDERBUFFER, "Generating renderbuffer id:%u "
107 "format:%u dimensions:%ux%u", gl_mem->renderbuffer_id, internal_format,
108 gst_gl_renderbuffer_get_width (gl_mem),
109 gst_gl_renderbuffer_get_height (gl_mem));
110 }
111
112 return TRUE;
113 }
114
115 static void
gst_gl_renderbuffer_init(GstGLRenderbuffer * mem,GstAllocator * allocator,GstMemory * parent,GstGLContext * context,GstGLFormat renderbuffer_format,const GstAllocationParams * params,guint width,guint height,gpointer user_data,GDestroyNotify notify)116 gst_gl_renderbuffer_init (GstGLRenderbuffer * mem, GstAllocator * allocator,
117 GstMemory * parent, GstGLContext * context,
118 GstGLFormat renderbuffer_format, const GstAllocationParams * params,
119 guint width, guint height, gpointer user_data, GDestroyNotify notify)
120 {
121 gsize size;
122 guint tex_type;
123
124 tex_type = GL_UNSIGNED_BYTE;
125 if (renderbuffer_format == GST_GL_RGB565)
126 tex_type = GL_UNSIGNED_SHORT_5_6_5;
127 size =
128 gst_gl_format_type_n_bytes (renderbuffer_format,
129 tex_type) * width * height;
130
131 mem->renderbuffer_format = renderbuffer_format;
132 mem->width = width;
133 mem->height = height;
134
135 gst_gl_base_memory_init ((GstGLBaseMemory *) mem, allocator, parent, context,
136 params, size, user_data, notify);
137
138 GST_CAT_DEBUG (GST_CAT_GL_RENDERBUFFER, "new GL renderbuffer context:%"
139 GST_PTR_FORMAT " memory:%p format:%u dimensions:%ux%u ", context, mem,
140 mem->renderbuffer_format, gst_gl_renderbuffer_get_width (mem),
141 gst_gl_renderbuffer_get_height (mem));
142 }
143
144 static gpointer
_gl_rbo_map(GstGLRenderbuffer * gl_mem,GstMapInfo * info,gsize maxsize)145 _gl_rbo_map (GstGLRenderbuffer * gl_mem, GstMapInfo * info, gsize maxsize)
146 {
147 GST_CAT_WARNING (GST_CAT_GL_RENDERBUFFER, "Renderbuffer's cannot be mapped");
148
149 return NULL;
150 }
151
152 static void
_gl_rbo_unmap(GstGLRenderbuffer * gl_mem,GstMapInfo * info)153 _gl_rbo_unmap (GstGLRenderbuffer * gl_mem, GstMapInfo * info)
154 {
155 }
156
157 static GstMemory *
_gl_rbo_copy(GstGLRenderbuffer * src,gssize offset,gssize size)158 _gl_rbo_copy (GstGLRenderbuffer * src, gssize offset, gssize size)
159 {
160 GST_CAT_WARNING (GST_CAT_GL_RENDERBUFFER, "Renderbuffer's cannot be copied");
161
162 return NULL;
163 }
164
165 static GstMemory *
_gl_rbo_alloc(GstAllocator * allocator,gsize size,GstAllocationParams * params)166 _gl_rbo_alloc (GstAllocator * allocator, gsize size,
167 GstAllocationParams * params)
168 {
169 g_warning ("Use gst_gl_base_memory_alloc to allocate from this allocator");
170
171 return NULL;
172 }
173
174 static void
_gl_rbo_destroy(GstGLRenderbuffer * gl_mem)175 _gl_rbo_destroy (GstGLRenderbuffer * gl_mem)
176 {
177 const GstGLFuncs *gl = gl_mem->mem.context->gl_vtable;
178
179 if (gl_mem->renderbuffer_id && !gl_mem->renderbuffer_wrapped)
180 gl->DeleteRenderbuffers (1, &gl_mem->renderbuffer_id);
181 }
182
183 static GstGLRenderbuffer *
_default_gl_rbo_alloc(GstGLRenderbufferAllocator * allocator,GstGLRenderbufferAllocationParams * params)184 _default_gl_rbo_alloc (GstGLRenderbufferAllocator * allocator,
185 GstGLRenderbufferAllocationParams * params)
186 {
187 guint alloc_flags = params->parent.alloc_flags;
188 GstGLRenderbuffer *mem;
189
190 g_return_val_if_fail ((alloc_flags &
191 GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_WRAP_SYSMEM) == 0, NULL);
192
193 mem = g_new0 (GstGLRenderbuffer, 1);
194
195 if (alloc_flags & GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_WRAP_GPU_HANDLE) {
196 mem->renderbuffer_id = GPOINTER_TO_UINT (params->parent.gl_handle);
197 mem->renderbuffer_wrapped = TRUE;
198 }
199
200 gst_gl_renderbuffer_init (mem, GST_ALLOCATOR_CAST (allocator), NULL,
201 params->parent.context, params->renderbuffer_format,
202 params->parent.alloc_params, params->width, params->height,
203 params->parent.user_data, params->parent.notify);
204
205 return mem;
206 }
207
208 static void
gst_gl_renderbuffer_allocator_class_init(GstGLRenderbufferAllocatorClass * klass)209 gst_gl_renderbuffer_allocator_class_init (GstGLRenderbufferAllocatorClass *
210 klass)
211 {
212 GstGLBaseMemoryAllocatorClass *gl_base;
213 GstAllocatorClass *allocator_class;
214
215 gl_base = (GstGLBaseMemoryAllocatorClass *) klass;
216 allocator_class = (GstAllocatorClass *) klass;
217
218 gl_base->alloc =
219 (GstGLBaseMemoryAllocatorAllocFunction) _default_gl_rbo_alloc;
220 gl_base->create = (GstGLBaseMemoryAllocatorCreateFunction) _gl_rbo_create;
221 gl_base->destroy = (GstGLBaseMemoryAllocatorDestroyFunction) _gl_rbo_destroy;
222
223 allocator_class->alloc = _gl_rbo_alloc;
224 }
225
226 static void
gst_gl_renderbuffer_allocator_init(GstGLRenderbufferAllocator * allocator)227 gst_gl_renderbuffer_allocator_init (GstGLRenderbufferAllocator * allocator)
228 {
229 GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
230
231 alloc->mem_type = GST_GL_RENDERBUFFER_ALLOCATOR_NAME;
232
233 alloc->mem_map_full = (GstMemoryMapFullFunction) _gl_rbo_map;
234 alloc->mem_unmap_full = (GstMemoryUnmapFullFunction) _gl_rbo_unmap;
235 alloc->mem_copy = (GstMemoryCopyFunction) _gl_rbo_copy;
236
237 GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC);
238 }
239
240 /**
241 * gst_gl_renderbuffer_get_width:
242 * @gl_mem: a #GstGLRenderbuffer
243 *
244 * Returns: the configured width of @gl_mem
245 *
246 * Since: 1.10
247 */
248 gint
gst_gl_renderbuffer_get_width(GstGLRenderbuffer * gl_mem)249 gst_gl_renderbuffer_get_width (GstGLRenderbuffer * gl_mem)
250 {
251 g_return_val_if_fail (gst_is_gl_renderbuffer ((GstMemory *) gl_mem), 0);
252
253 return gl_mem->width;
254 }
255
256 /**
257 * gst_gl_renderbuffer_get_height:
258 * @gl_mem: a #GstGLRenderbuffer
259 *
260 * Returns: the configured height of @gl_mem
261 *
262 * Since: 1.10
263 */
264 gint
gst_gl_renderbuffer_get_height(GstGLRenderbuffer * gl_mem)265 gst_gl_renderbuffer_get_height (GstGLRenderbuffer * gl_mem)
266 {
267 g_return_val_if_fail (gst_is_gl_renderbuffer ((GstMemory *) gl_mem), 0);
268
269 return gl_mem->height;
270 }
271
272 /**
273 * gst_gl_renderbuffer_get_format:
274 * @gl_mem: a #GstGLRenderbuffer
275 *
276 * Returns: the #GstGLFormat of @gl_mem
277 *
278 * Since: 1.12
279 */
280 GstGLFormat
gst_gl_renderbuffer_get_format(GstGLRenderbuffer * gl_mem)281 gst_gl_renderbuffer_get_format (GstGLRenderbuffer * gl_mem)
282 {
283 g_return_val_if_fail (gst_is_gl_renderbuffer ((GstMemory *) gl_mem), 0);
284
285 return gl_mem->renderbuffer_format;
286 }
287
288 /**
289 * gst_gl_renderbuffer_get_id:
290 * @gl_mem: a #GstGLRenderbuffer
291 *
292 * Returns: the OpenGL renderbuffer handle of @gl_mem
293 *
294 * Since: 1.10
295 */
296 guint
gst_gl_renderbuffer_get_id(GstGLRenderbuffer * gl_mem)297 gst_gl_renderbuffer_get_id (GstGLRenderbuffer * gl_mem)
298 {
299 g_return_val_if_fail (gst_is_gl_renderbuffer ((GstMemory *) gl_mem), 0);
300
301 return gl_mem->renderbuffer_id;
302 }
303
304 /**
305 * gst_gl_renderbuffer_init_once:
306 *
307 * Initializes the GL Base Texture allocator. It is safe to call this function
308 * multiple times. This must be called before any other GstGLRenderbuffer operation.
309 *
310 * Since: 1.10
311 */
312 void
gst_gl_renderbuffer_init_once(void)313 gst_gl_renderbuffer_init_once (void)
314 {
315 static gsize _init = 0;
316
317 if (g_once_init_enter (&_init)) {
318 gst_gl_base_memory_init_once ();
319
320 GST_DEBUG_CATEGORY_INIT (GST_CAT_GL_RENDERBUFFER, "glrenderbuffermemory", 0,
321 "OpenGL Renderbuffer memory");
322
323 _gl_renderbuffer_allocator =
324 g_object_new (GST_TYPE_GL_RENDERBUFFER_ALLOCATOR, NULL);
325 gst_object_ref_sink (_gl_renderbuffer_allocator);
326 GST_OBJECT_FLAG_SET (_gl_renderbuffer_allocator,
327 GST_OBJECT_FLAG_MAY_BE_LEAKED);
328
329 gst_allocator_register (GST_GL_RENDERBUFFER_ALLOCATOR_NAME,
330 _gl_renderbuffer_allocator);
331
332 g_once_init_leave (&_init, 1);
333 }
334 }
335
336 /**
337 * gst_is_gl_renderbuffer:
338 * @mem:a #GstMemory
339 *
340 * Returns: whether the memory at @mem is a #GstGLRenderbuffer
341 *
342 * Since: 1.10
343 */
344 gboolean
gst_is_gl_renderbuffer(GstMemory * mem)345 gst_is_gl_renderbuffer (GstMemory * mem)
346 {
347 return mem != NULL && mem->allocator != NULL
348 && g_type_is_a (G_OBJECT_TYPE (mem->allocator),
349 GST_TYPE_GL_RENDERBUFFER_ALLOCATOR);
350 }
351
352 G_DEFINE_BOXED_TYPE (GstGLRenderbufferAllocationParams,
353 gst_gl_renderbuffer_allocation_params,
354 (GBoxedCopyFunc) gst_gl_allocation_params_copy,
355 (GBoxedFreeFunc) gst_gl_allocation_params_free);
356
357 static void
_gst_gl_rb_alloc_params_free_data(GstGLRenderbufferAllocationParams * params)358 _gst_gl_rb_alloc_params_free_data (GstGLRenderbufferAllocationParams * params)
359 {
360 gst_gl_allocation_params_free_data (¶ms->parent);
361 }
362
363 static void
_gst_gl_rb_alloc_params_copy_data(GstGLRenderbufferAllocationParams * src_vid,GstGLRenderbufferAllocationParams * dest_vid)364 _gst_gl_rb_alloc_params_copy_data (GstGLRenderbufferAllocationParams * src_vid,
365 GstGLRenderbufferAllocationParams * dest_vid)
366 {
367 GstGLAllocationParams *src = (GstGLAllocationParams *) src_vid;
368 GstGLAllocationParams *dest = (GstGLAllocationParams *) dest_vid;
369
370 gst_gl_allocation_params_copy_data (src, dest);
371
372 dest_vid->renderbuffer_format = src_vid->renderbuffer_format;
373 dest_vid->width = src_vid->width;
374 dest_vid->height = src_vid->height;
375 }
376
377 static gboolean
_gst_gl_renderbuffer_allocation_params_init_full(GstGLRenderbufferAllocationParams * params,gsize struct_size,guint alloc_flags,GstGLAllocationParamsCopyFunc copy,GstGLAllocationParamsFreeFunc free,GstGLContext * context,const GstAllocationParams * alloc_params,guint width,guint height,GstGLFormat renderbuffer_format,gpointer wrapped_data,gpointer gl_handle,gpointer user_data,GDestroyNotify notify)378 _gst_gl_renderbuffer_allocation_params_init_full
379 (GstGLRenderbufferAllocationParams * params, gsize struct_size,
380 guint alloc_flags, GstGLAllocationParamsCopyFunc copy,
381 GstGLAllocationParamsFreeFunc free, GstGLContext * context,
382 const GstAllocationParams * alloc_params, guint width, guint height,
383 GstGLFormat renderbuffer_format, gpointer wrapped_data,
384 gpointer gl_handle, gpointer user_data, GDestroyNotify notify)
385 {
386 g_return_val_if_fail (params != NULL, FALSE);
387 g_return_val_if_fail (copy != NULL, FALSE);
388 g_return_val_if_fail (free != NULL, FALSE);
389 g_return_val_if_fail (GST_IS_GL_CONTEXT (context), FALSE);
390
391 memset (params, 0, sizeof (*params));
392
393 if (!gst_gl_allocation_params_init ((GstGLAllocationParams *) params,
394 struct_size, alloc_flags, copy, free, context, 0, alloc_params,
395 wrapped_data, gl_handle, user_data, notify))
396 return FALSE;
397
398 params->renderbuffer_format = renderbuffer_format;
399 params->width = width;
400 params->height = height;
401
402 return TRUE;
403 }
404
405 /**
406 * gst_gl_renderbuffer_allocation_params_new:
407 * @context: a #GstGLContext
408 * @alloc_params: (allow-none): the #GstAllocationParams for sysmem mappings of the texture
409 * @width: the width of the renderbuffer
410 * @height: the height of the renderbuffer
411 * @renderbuffer_format: the #GstGLFormat for the created textures
412 *
413 * Returns: a new #GstGLRenderbufferAllocationParams for allocating #GstGLRenderbuffer's
414 *
415 * Since: 1.10
416 */
417 GstGLRenderbufferAllocationParams *
gst_gl_renderbuffer_allocation_params_new(GstGLContext * context,const GstAllocationParams * alloc_params,GstGLFormat renderbuffer_format,guint width,guint height)418 gst_gl_renderbuffer_allocation_params_new (GstGLContext * context,
419 const GstAllocationParams * alloc_params, GstGLFormat renderbuffer_format,
420 guint width, guint height)
421 {
422 GstGLRenderbufferAllocationParams *params =
423 g_new0 (GstGLRenderbufferAllocationParams, 1);
424
425 if (!_gst_gl_renderbuffer_allocation_params_init_full (params,
426 sizeof (GstGLRenderbufferAllocationParams),
427 GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_ALLOC |
428 GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_VIDEO,
429 (GstGLAllocationParamsCopyFunc) _gst_gl_rb_alloc_params_copy_data,
430 (GstGLAllocationParamsFreeFunc) _gst_gl_rb_alloc_params_free_data,
431 context, alloc_params, width, height, renderbuffer_format, NULL, 0,
432 NULL, NULL)) {
433 g_free (params);
434 return NULL;
435 }
436
437 return params;
438 }
439
440 /**
441 * gst_gl_renderbuffer_allocation_params_new_wrapped:
442 * @context: a #GstGLContext
443 * @alloc_params: (allow-none): the #GstAllocationParams for @tex_id
444 * @width: the width of the renderbuffer
445 * @height: the height of the renderbuffer
446 * @renderbuffer_format: the #GstGLFormat for @tex_id
447 * @gl_handle: the GL handle to wrap
448 * @user_data: (allow-none): user data to call @notify with
449 * @notify: (allow-none): a #GDestroyNotify
450 *
451 * Returns: a new #GstGLRenderbufferAllocationParams for wrapping @gl_handle as a
452 * renderbuffer
453 *
454 * Since: 1.10
455 */
456 GstGLRenderbufferAllocationParams *
gst_gl_renderbuffer_allocation_params_new_wrapped(GstGLContext * context,const GstAllocationParams * alloc_params,GstGLFormat renderbuffer_format,guint width,guint height,gpointer gl_handle,gpointer user_data,GDestroyNotify notify)457 gst_gl_renderbuffer_allocation_params_new_wrapped (GstGLContext * context,
458 const GstAllocationParams * alloc_params, GstGLFormat renderbuffer_format,
459 guint width, guint height, gpointer gl_handle, gpointer user_data,
460 GDestroyNotify notify)
461 {
462 GstGLRenderbufferAllocationParams *params =
463 g_new0 (GstGLRenderbufferAllocationParams, 1);
464
465 if (!_gst_gl_renderbuffer_allocation_params_init_full (params,
466 sizeof (GstGLRenderbufferAllocationParams),
467 GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_WRAP_GPU_HANDLE |
468 GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_VIDEO,
469 (GstGLAllocationParamsCopyFunc) _gst_gl_rb_alloc_params_copy_data,
470 (GstGLAllocationParamsFreeFunc) _gst_gl_rb_alloc_params_free_data,
471 context, alloc_params, width, height, renderbuffer_format, NULL,
472 gl_handle, user_data, notify)) {
473 g_free (params);
474 return NULL;
475 }
476
477 return params;
478 }
479