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 "gstglmemory.h"
28 
29 #include "gl.h"
30 #include "gstglcontext_private.h"
31 #include "gstglfuncs.h"
32 
33 /**
34  * SECTION:gstglmemory
35  * @title: GstGLMemory
36  * @short_description: memory subclass for GL textures
37  * @see_also: #GstMemory, #GstAllocator, #GstGLBufferPool
38  *
39  * GstGLMemory is a #GstGLBaseMemory subclass providing support for the mapping of
40  * OpenGL textures.
41  *
42  * #GstGLMemory is created or wrapped through gst_gl_base_memory_alloc()
43  * with #GstGLVideoAllocationParams.
44  *
45  * Data is uploaded or downloaded from the GPU as is necessary.
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 #define GL_MEM_WIDTH(gl_mem) _get_plane_width (&gl_mem->info, gl_mem->plane)
55 #define GL_MEM_HEIGHT(gl_mem) _get_plane_height (&gl_mem->info, gl_mem->plane)
56 #define GL_MEM_STRIDE(gl_mem) GST_VIDEO_INFO_PLANE_STRIDE (&gl_mem->info, gl_mem->plane)
57 
58 static GstAllocator *_gl_memory_allocator;
59 
60 GST_DEBUG_CATEGORY_STATIC (GST_CAT_GL_MEMORY);
61 #define GST_CAT_DEFAULT GST_CAT_GL_MEMORY
62 
63 /* compatability definitions... */
64 #ifndef GL_UNPACK_ROW_LENGTH
65 #define GL_UNPACK_ROW_LENGTH 0x0CF2
66 #endif
67 
68 #ifndef GL_TEXTURE_RECTANGLE
69 #define GL_TEXTURE_RECTANGLE 0x84F5
70 #endif
71 #ifndef GL_TEXTURE_EXTERNAL_OES
72 #define GL_TEXTURE_EXTERNAL_OES 0x8D65
73 #endif
74 
75 #ifndef GL_READ_FRAMEBUFFER
76 #define GL_READ_FRAMEBUFFER 0x8CA8
77 #endif
78 #ifndef GL_DRAW_FRAMEBUFFER
79 #define GL_DRAW_FRAMEBUFFER 0x8CA9
80 #endif
81 
82 G_DEFINE_TYPE (GstGLMemoryAllocator, gst_gl_memory_allocator,
83     GST_TYPE_GL_BASE_MEMORY_ALLOCATOR);
84 
85 typedef struct
86 {
87   /* in */
88   GstGLMemory *src;
89   GstGLFormat out_format;
90   guint out_width, out_height;
91   GstGLTextureTarget tex_target;
92   GstGLFormat tex_format;
93   /* inout */
94   guint tex_id;
95   /* out */
96   gboolean result;
97 } GstGLMemoryCopyParams;
98 
99 static inline guint
_get_plane_width(GstVideoInfo * info,guint plane)100 _get_plane_width (GstVideoInfo * info, guint plane)
101 {
102   if (GST_VIDEO_INFO_IS_YUV (info))
103     /* For now component width and plane width are the same and the
104      * plane-component mapping matches
105      */
106     return GST_VIDEO_INFO_COMP_WIDTH (info, plane);
107   else                          /* RGB, GRAY */
108     return GST_VIDEO_INFO_WIDTH (info);
109 }
110 
111 static inline guint
_get_plane_height(GstVideoInfo * info,guint plane)112 _get_plane_height (GstVideoInfo * info, guint plane)
113 {
114   if (GST_VIDEO_INFO_IS_YUV (info))
115     /* For now component width and plane width are the same and the
116      * plane-component mapping matches
117      */
118     return GST_VIDEO_INFO_COMP_HEIGHT (info, plane);
119   else                          /* RGB, GRAY */
120     return GST_VIDEO_INFO_HEIGHT (info);
121 }
122 
123 static inline void
_calculate_unpack_length(GstGLMemory * gl_mem,GstGLContext * context)124 _calculate_unpack_length (GstGLMemory * gl_mem, GstGLContext * context)
125 {
126   guint n_gl_bytes;
127   GstGLFormat tex_format;
128   guint tex_type;
129 
130   gl_mem->tex_scaling[0] = 1.0f;
131   gl_mem->tex_scaling[1] = 1.0f;
132   gl_mem->unpack_length = 1;
133   gl_mem->tex_width = GL_MEM_WIDTH (gl_mem);
134 
135   gst_gl_format_type_from_sized_gl_format (gl_mem->tex_format, &tex_format,
136       &tex_type);
137   n_gl_bytes = gst_gl_format_type_n_bytes (tex_format, tex_type);
138   if (n_gl_bytes == 0) {
139     GST_ERROR ("Unsupported texture type %d", gl_mem->tex_format);
140     return;
141   }
142 
143   if (USING_OPENGL (context) || USING_GLES3 (context)
144       || USING_OPENGL3 (context)) {
145     gl_mem->unpack_length = GL_MEM_STRIDE (gl_mem) / n_gl_bytes;
146   } else if (USING_GLES2 (context)) {
147     guint j = 8;
148 
149     while (j >= n_gl_bytes) {
150       /* GST_ROUND_UP_j(GL_MEM_WIDTH (gl_mem) * n_gl_bytes) */
151       guint round_up_j =
152           ((GL_MEM_WIDTH (gl_mem) * n_gl_bytes) + j - 1) & ~(j - 1);
153 
154       if (round_up_j == GL_MEM_STRIDE (gl_mem)) {
155         GST_CAT_LOG (GST_CAT_GL_MEMORY, "Found alignment of %u based on "
156             "width (with plane width:%u, plane stride:%u and pixel stride:%u. "
157             "RU%u(%u*%u) = %u)", j, GL_MEM_WIDTH (gl_mem),
158             GL_MEM_STRIDE (gl_mem), n_gl_bytes, j, GL_MEM_WIDTH (gl_mem),
159             n_gl_bytes, round_up_j);
160 
161         gl_mem->unpack_length = j;
162         break;
163       }
164       j >>= 1;
165     }
166 
167     if (j < n_gl_bytes) {
168       /* Failed to find a suitable alignment, try based on plane_stride and
169        * scale in the shader.  Useful for alignments that are greater than 8.
170        */
171       j = 8;
172 
173       while (j >= n_gl_bytes) {
174         /* GST_ROUND_UP_j((GL_MEM_STRIDE (gl_mem)) */
175         guint round_up_j = ((GL_MEM_STRIDE (gl_mem)) + j - 1) & ~(j - 1);
176 
177         if (round_up_j == (GL_MEM_STRIDE (gl_mem))) {
178           GST_CAT_LOG (GST_CAT_GL_MEMORY, "Found alignment of %u based "
179               "on stride (with plane stride:%u and pixel stride:%u. "
180               "RU%u(%u) = %u)", j, GL_MEM_STRIDE (gl_mem), n_gl_bytes, j,
181               GL_MEM_STRIDE (gl_mem), round_up_j);
182 
183           gl_mem->unpack_length = j;
184           gl_mem->tex_scaling[0] =
185               (gfloat) (GL_MEM_WIDTH (gl_mem) * n_gl_bytes) /
186               (gfloat) GL_MEM_STRIDE (gl_mem);
187           gl_mem->tex_width = GL_MEM_STRIDE (gl_mem) / n_gl_bytes;
188           break;
189         }
190         j >>= 1;
191       }
192 
193       if (j < n_gl_bytes) {
194         GST_CAT_ERROR (GST_CAT_GL_MEMORY, "Failed to find matching "
195             "alignment. Image may look corrupted. plane width:%u, "
196             "plane stride:%u and pixel stride:%u", GL_MEM_WIDTH (gl_mem),
197             GL_MEM_STRIDE (gl_mem), n_gl_bytes);
198       }
199     }
200   }
201 
202   if (gl_mem->tex_target == GST_GL_TEXTURE_TARGET_RECTANGLE) {
203     guint w_sub =
204         GST_VIDEO_FORMAT_INFO_W_SUB (gl_mem->info.finfo, gl_mem->plane);
205     guint h_sub =
206         GST_VIDEO_FORMAT_INFO_H_SUB (gl_mem->info.finfo, gl_mem->plane);
207 
208     if (w_sub)
209       gl_mem->tex_scaling[0] /= (1 << w_sub);
210     if (h_sub)
211       gl_mem->tex_scaling[1] /= (1 << h_sub);
212   }
213 }
214 
215 static guint
_new_texture(GstGLContext * context,guint target,guint internal_format,guint format,guint type,guint width,guint height)216 _new_texture (GstGLContext * context, guint target, guint internal_format,
217     guint format, guint type, guint width, guint height)
218 {
219   const GstGLFuncs *gl = context->gl_vtable;
220   guint tex_id;
221 
222   gl->GenTextures (1, &tex_id);
223   gl->BindTexture (target, tex_id);
224   if (target == GL_TEXTURE_2D || target == GL_TEXTURE_RECTANGLE)
225     gl->TexImage2D (target, 0, internal_format, width, height, 0, format, type,
226         NULL);
227 
228   gl->TexParameteri (target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
229   gl->TexParameteri (target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
230   gl->TexParameteri (target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
231   gl->TexParameteri (target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
232 
233   gl->BindTexture (target, 0);
234 
235   return tex_id;
236 }
237 
238 static gboolean
_gl_tex_create(GstGLMemory * gl_mem,GError ** error)239 _gl_tex_create (GstGLMemory * gl_mem, GError ** error)
240 {
241   GstGLContext *context = gl_mem->mem.context;
242   GstGLFormat internal_format;
243   GstGLFormat tex_format;
244   GLenum tex_type;
245 
246   internal_format = gl_mem->tex_format;
247   gst_gl_format_type_from_sized_gl_format (internal_format, &tex_format,
248       &tex_type);
249   internal_format =
250       gst_gl_sized_gl_format_from_gl_format_type (context, tex_format,
251       tex_type);
252 
253   if (!gl_mem->texture_wrapped) {
254     gl_mem->tex_id =
255         _new_texture (context, gst_gl_texture_target_to_gl (gl_mem->tex_target),
256         internal_format, tex_format, tex_type, gl_mem->tex_width,
257         GL_MEM_HEIGHT (gl_mem));
258 
259     GST_TRACE ("Generating texture id:%u format:%u type:%u dimensions:%ux%u",
260         gl_mem->tex_id, tex_format, tex_type, gl_mem->tex_width,
261         GL_MEM_HEIGHT (gl_mem));
262   }
263 
264   return TRUE;
265 }
266 
267 static void
_gst_gl_memory_start_log(GstGLMemory * gl_mem,const gchar * func_name)268 _gst_gl_memory_start_log (GstGLMemory * gl_mem, const gchar * func_name)
269 {
270   /* debugging is disabled */
271   if (!GST_GL_BASE_MEMORY_CAST (gl_mem)->query)
272     return;
273 
274   gst_gl_query_start_log (GST_GL_BASE_MEMORY_CAST (gl_mem)->query,
275       GST_CAT_GL_MEMORY, GST_LEVEL_LOG, NULL, "%s took", func_name);
276 }
277 
278 static void
_gst_gl_memory_end_log(GstGLMemory * gl_mem)279 _gst_gl_memory_end_log (GstGLMemory * gl_mem)
280 {
281   /* debugging is disabled */
282   if (!GST_GL_BASE_MEMORY_CAST (gl_mem)->query)
283     return;
284 
285   gst_gl_query_end (GST_GL_BASE_MEMORY_CAST (gl_mem)->query);
286 }
287 
288 /**
289  * gst_gl_memory_init:
290  * @mem: the #GstGLBaseMemory to initialize
291  * @allocator: the #GstAllocator to initialize with
292  * @parent: (allow-none): the parent #GstMemory to initialize with
293  * @context: the #GstGLContext to initialize with
294  * @target: the #GstGLTextureTarget for this #GstGLMemory
295  * @tex_format: the #GstGLFormat for this #GstGLMemory
296  * @params: (allow-none): the @GstAllocationParams to initialize with
297  * @info: the #GstVideoInfo for this #GstGLMemory
298  * @plane: the plane number (starting from 0) for this #GstGLMemory
299  * @valign: (allow-none): optional #GstVideoAlignment parameters
300  * @notify: (allow-none): a #GDestroyNotify
301  * @user_data: (allow-none): user data to call @notify with
302  *
303  * Initializes @mem with the required parameters.  @info is assumed to have
304  * already have been modified with gst_video_info_align().
305  *
306  * Since: 1.8
307  */
308 void
gst_gl_memory_init(GstGLMemory * mem,GstAllocator * allocator,GstMemory * parent,GstGLContext * context,GstGLTextureTarget target,GstGLFormat tex_format,GstAllocationParams * params,GstVideoInfo * info,guint plane,GstVideoAlignment * valign,gpointer user_data,GDestroyNotify notify)309 gst_gl_memory_init (GstGLMemory * mem, GstAllocator * allocator,
310     GstMemory * parent, GstGLContext * context, GstGLTextureTarget target,
311     GstGLFormat tex_format, GstAllocationParams * params,
312     GstVideoInfo * info, guint plane, GstVideoAlignment * valign,
313     gpointer user_data, GDestroyNotify notify)
314 {
315   const gchar *target_str;
316   gsize size;
317 
318   g_return_if_fail (plane < GST_VIDEO_INFO_N_PLANES (info));
319 
320   mem->info = *info;
321   if (valign)
322     mem->valign = *valign;
323   else
324     gst_video_alignment_reset (&mem->valign);
325 
326   /* double-check alignment requirements (caller should've taken care of this) */
327   if (params) {
328     guint max_align, n;
329 
330     max_align = gst_memory_alignment;
331     max_align |= params->align;
332     for (n = 0; n < GST_VIDEO_MAX_PLANES; ++n)
333       max_align |= mem->valign.stride_align[n];
334 
335     if (params->align < max_align && max_align > gst_memory_alignment) {
336       GST_WARNING ("allocation params alignment %" G_GSIZE_FORMAT " is smaller "
337           "than the max required video alignment %u", params->align, max_align);
338     }
339   }
340 
341   size = gst_gl_get_plane_data_size (info, valign, plane);
342 
343   mem->tex_target = target;
344   mem->tex_format = tex_format;
345   mem->plane = plane;
346 
347   _calculate_unpack_length (mem, context);
348 
349   gst_gl_base_memory_init ((GstGLBaseMemory *) mem, allocator, parent, context,
350       params, size, user_data, notify);
351 
352   target_str = gst_gl_texture_target_to_string (target);
353   GST_CAT_DEBUG (GST_CAT_GL_MEMORY, "new GL texture context:%"
354       GST_PTR_FORMAT " memory:%p target:%s format:%u dimensions:%ux%u "
355       "stride:%u size:%" G_GSIZE_FORMAT, context, mem, target_str,
356       mem->tex_format, mem->tex_width, GL_MEM_HEIGHT (mem), GL_MEM_STRIDE (mem),
357       mem->mem.mem.size);
358 }
359 
360 /**
361  * gst_gl_memory_read_pixels:
362  * @gl_mem: a #GstGLMemory
363  * @read_pointer: the data pointer to pass to glReadPixels
364  *
365  * Reads the texture in #GstGLMemory into @read_pointer if no buffer is bound
366  * to %GL_PIXEL_PACK_BUFFER.  Otherwise @read_pointer is the byte offset into
367  * the currently bound %GL_PIXEL_PACK_BUFFER buffer to store the result of
368  * glReadPixels.  See the OpenGL specification for glReadPixels for more
369  * details.
370  *
371  * Returns: whether theread operation succeeded
372  *
373  * Since: 1.8
374  */
375 gboolean
gst_gl_memory_read_pixels(GstGLMemory * gl_mem,gpointer read_pointer)376 gst_gl_memory_read_pixels (GstGLMemory * gl_mem, gpointer read_pointer)
377 {
378   GstGLContext *context = gl_mem->mem.context;
379   const GstGLFuncs *gl = context->gl_vtable;
380   GstGLFormat format;
381   guint type;
382   guint fbo;
383 
384   gst_gl_format_type_from_sized_gl_format (gl_mem->tex_format, &format, &type);
385 
386   /* FIXME: avoid creating a framebuffer every download/copy */
387   gl->GenFramebuffers (1, &fbo);
388   gl->BindFramebuffer (GL_FRAMEBUFFER, fbo);
389 
390   gl->FramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
391       gst_gl_texture_target_to_gl (gl_mem->tex_target), gl_mem->tex_id, 0);
392 
393   if (!gst_gl_context_check_framebuffer_status (context, GL_FRAMEBUFFER)) {
394     GST_CAT_WARNING (GST_CAT_GL_MEMORY,
395         "Could not create framebuffer to read pixels for memory %p", gl_mem);
396     gl->DeleteFramebuffers (1, &fbo);
397     return FALSE;
398   }
399 
400   if (USING_GLES2 (context) || USING_GLES3 (context)) {
401     if (format == GL_RGBA && type == GL_UNSIGNED_BYTE) {
402       /* explicitly supported */
403     } else {
404       gint supported_format, supported_type;
405 
406       gl->GetIntegerv (GL_IMPLEMENTATION_COLOR_READ_FORMAT, &supported_format);
407       gl->GetIntegerv (GL_IMPLEMENTATION_COLOR_READ_TYPE, &supported_type);
408 
409       if (supported_format != format || supported_type != type) {
410         GST_CAT_ERROR (GST_CAT_GL_MEMORY, "cannot read pixels with "
411             "unsupported format and type.  Supported format 0x%x type 0x%x",
412             supported_format, supported_type);
413         gl->BindFramebuffer (GL_FRAMEBUFFER, 0);
414         gl->DeleteFramebuffers (1, &fbo);
415         return FALSE;
416       }
417     }
418   }
419 
420   _gst_gl_memory_start_log (gl_mem, "glReadPixels");
421   gl->ReadPixels (0, 0, gl_mem->tex_width, GL_MEM_HEIGHT (gl_mem), format,
422       type, read_pointer);
423   _gst_gl_memory_end_log (gl_mem);
424 
425   gl->BindFramebuffer (GL_FRAMEBUFFER, 0);
426   gl->DeleteFramebuffers (1, &fbo);
427 
428   return TRUE;
429 }
430 
431 static gpointer
_gl_tex_download_get_tex_image(GstGLMemory * gl_mem,GstMapInfo * info,gsize size)432 _gl_tex_download_get_tex_image (GstGLMemory * gl_mem, GstMapInfo * info,
433     gsize size)
434 {
435   GstGLContext *context = gl_mem->mem.context;
436   const GstGLFuncs *gl = context->gl_vtable;
437 
438   if (size != -1 && size != ((GstMemory *) gl_mem)->maxsize)
439     return NULL;
440 
441   if (USING_GLES2 (context) || USING_GLES3 (context))
442     return NULL;
443 
444   /* taken care of by read pixels */
445   if (gl_mem->tex_format != GST_GL_LUMINANCE
446       && gl_mem->tex_format != GST_GL_LUMINANCE_ALPHA)
447     return NULL;
448 
449   if (info->flags & GST_MAP_READ
450       && GST_MEMORY_FLAG_IS_SET (gl_mem,
451           GST_GL_BASE_MEMORY_TRANSFER_NEED_DOWNLOAD)) {
452     GstGLFormat format;
453     guint type;
454     guint target;
455 
456     GST_CAT_TRACE (GST_CAT_GL_MEMORY, "attempting download of texture %u "
457         "using glGetTexImage", gl_mem->tex_id);
458 
459     gst_gl_format_type_from_sized_gl_format (gl_mem->tex_format, &format,
460         &type);
461 
462     target = gst_gl_texture_target_to_gl (gl_mem->tex_target);
463     gl->BindTexture (target, gl_mem->tex_id);
464     _gst_gl_memory_start_log (gl_mem, "glGetTexImage");
465     gl->GetTexImage (target, 0, format, type, gl_mem->mem.data);
466     _gst_gl_memory_end_log (gl_mem);
467     gl->BindTexture (target, 0);
468   }
469 
470   return gl_mem->mem.data;
471 }
472 
473 static gpointer
_gl_tex_download_read_pixels(GstGLMemory * gl_mem,GstMapInfo * info,gsize size)474 _gl_tex_download_read_pixels (GstGLMemory * gl_mem, GstMapInfo * info,
475     gsize size)
476 {
477   if (size != -1 && size != ((GstMemory *) gl_mem)->maxsize)
478     return NULL;
479 
480   if (info->flags & GST_MAP_READ
481       && GST_MEMORY_FLAG_IS_SET (gl_mem,
482           GST_GL_BASE_MEMORY_TRANSFER_NEED_DOWNLOAD)) {
483     GST_CAT_TRACE (GST_CAT_GL_MEMORY,
484         "attempting download of texture %u " "using glReadPixels",
485         gl_mem->tex_id);
486     if (!gst_gl_memory_read_pixels (gl_mem, gl_mem->mem.data))
487       return NULL;
488   }
489 
490   return gl_mem->mem.data;
491 }
492 
493 static gpointer
_gl_tex_map_cpu_access(GstGLMemory * gl_mem,GstMapInfo * info,gsize size)494 _gl_tex_map_cpu_access (GstGLMemory * gl_mem, GstMapInfo * info, gsize size)
495 {
496   gpointer data = NULL;
497 
498   if (!gst_gl_base_memory_alloc_data (GST_GL_BASE_MEMORY_CAST (gl_mem)))
499     return NULL;
500 
501   if (!data)
502     data = _gl_tex_download_get_tex_image (gl_mem, info, size);
503 
504   if (!data)
505     data = _gl_tex_download_read_pixels (gl_mem, info, size);
506 
507   return data;
508 }
509 
510 static void
_upload_cpu_write(GstGLMemory * gl_mem,GstMapInfo * info,gsize maxsize)511 _upload_cpu_write (GstGLMemory * gl_mem, GstMapInfo * info, gsize maxsize)
512 {
513   gst_gl_memory_texsubimage (gl_mem, gl_mem->mem.data);
514 }
515 
516 /**
517  * gst_gl_memory_texsubimage:
518  * @gl_mem: a #GstGLMemory
519  * @read_pointer: the data pointer to pass to glTexSubImage
520  *
521  * See gst_gl_memory_read_pixels() for what @read_pointer signifies.
522  *
523  * Since: 1.8
524  */
525 void
gst_gl_memory_texsubimage(GstGLMemory * gl_mem,gpointer read_pointer)526 gst_gl_memory_texsubimage (GstGLMemory * gl_mem, gpointer read_pointer)
527 {
528   GstGLContext *context = gl_mem->mem.context;
529   const GstGLFuncs *gl;
530   GstGLFormat gl_format;
531   GLenum gl_type, gl_target;
532   gpointer data;
533   gsize plane_start;
534 
535   if (!GST_MEMORY_FLAG_IS_SET (gl_mem, GST_GL_BASE_MEMORY_TRANSFER_NEED_UPLOAD))
536     return;
537 
538   gl = context->gl_vtable;
539 
540   gst_gl_format_type_from_sized_gl_format (gl_mem->tex_format, &gl_format,
541       &gl_type);
542 
543   gl_target = gst_gl_texture_target_to_gl (gl_mem->tex_target);
544 
545   if (USING_OPENGL (context) || USING_GLES3 (context)
546       || USING_OPENGL3 (context)) {
547     gl->PixelStorei (GL_UNPACK_ROW_LENGTH, gl_mem->unpack_length);
548   } else if (USING_GLES2 (context)) {
549     gl->PixelStorei (GL_UNPACK_ALIGNMENT, gl_mem->unpack_length);
550   }
551 
552   GST_CAT_LOG (GST_CAT_GL_MEMORY, "upload for texture id:%u, %ux%u",
553       gl_mem->tex_id, gl_mem->tex_width, GL_MEM_HEIGHT (gl_mem));
554 
555   /* find the start of the plane data including padding */
556   plane_start =
557       gst_gl_get_plane_start (&gl_mem->info, &gl_mem->valign,
558       gl_mem->plane) + gl_mem->mem.mem.offset;
559 
560   data = (gpointer) ((gintptr) plane_start + (gintptr) read_pointer);
561 
562   gl->BindTexture (gl_target, gl_mem->tex_id);
563   _gst_gl_memory_start_log (gl_mem, "glTexSubImage");
564   gl->TexSubImage2D (gl_target, 0, 0, 0, gl_mem->tex_width,
565       GL_MEM_HEIGHT (gl_mem), gl_format, gl_type, data);
566   _gst_gl_memory_end_log (gl_mem);
567 
568   /* Reset to default values */
569   if (USING_OPENGL (context) || USING_GLES3 (context)
570       || USING_OPENGL3 (context)) {
571     gl->PixelStorei (GL_UNPACK_ROW_LENGTH, 0);
572   } else if (USING_GLES2 (context)) {
573     gl->PixelStorei (GL_UNPACK_ALIGNMENT, 4);
574   }
575 
576   gl->BindTexture (gl_target, 0);
577 }
578 
579 static gpointer
_default_gl_tex_map(GstGLMemory * gl_mem,GstMapInfo * info,gsize size)580 _default_gl_tex_map (GstGLMemory * gl_mem, GstMapInfo * info, gsize size)
581 {
582   if ((info->flags & GST_MAP_GL) == GST_MAP_GL) {
583     _upload_cpu_write (gl_mem, info, size);
584     return &gl_mem->tex_id;
585   } else {
586     return _gl_tex_map_cpu_access (gl_mem, info, size);
587   }
588 }
589 
590 static gpointer
_gl_tex_map(GstGLMemory * gl_mem,GstMapInfo * info,gsize maxsize)591 _gl_tex_map (GstGLMemory * gl_mem, GstMapInfo * info, gsize maxsize)
592 {
593   GstGLMemoryAllocatorClass *alloc_class;
594   gpointer data;
595 
596   alloc_class = GST_GL_MEMORY_ALLOCATOR_GET_CLASS (gl_mem->mem.mem.allocator);
597 
598   if ((info->flags & GST_MAP_GL) == GST_MAP_GL) {
599     if (gl_mem->tex_target == GST_GL_TEXTURE_TARGET_EXTERNAL_OES)
600       return &gl_mem->tex_id;
601   } else {                      /* not GL */
602     if (gl_mem->tex_target == GST_GL_TEXTURE_TARGET_EXTERNAL_OES) {
603       GST_CAT_ERROR (GST_CAT_GL_MEMORY, "Cannot map External OES textures");
604       return NULL;
605     }
606   }
607 
608   g_return_val_if_fail (alloc_class->map != NULL, NULL);
609   data = alloc_class->map (GST_GL_BASE_MEMORY_CAST (gl_mem), info, maxsize);
610 
611   return data;
612 }
613 
614 static void
_default_gl_tex_unmap(GstGLMemory * gl_mem,GstMapInfo * info)615 _default_gl_tex_unmap (GstGLMemory * gl_mem, GstMapInfo * info)
616 {
617 }
618 
619 static void
_gl_tex_unmap(GstGLMemory * gl_mem,GstMapInfo * info)620 _gl_tex_unmap (GstGLMemory * gl_mem, GstMapInfo * info)
621 {
622   GstGLMemoryAllocatorClass *alloc_class;
623 
624   alloc_class = GST_GL_MEMORY_ALLOCATOR_GET_CLASS (gl_mem->mem.mem.allocator);
625   g_return_if_fail (alloc_class->unmap != NULL);
626 
627   alloc_class->unmap (GST_GL_BASE_MEMORY_CAST (gl_mem), info);
628 }
629 
630 /**
631  * gst_gl_memory_copy_teximage:
632  * @src: the source #GstGLMemory
633  * @tex_id: the destination texture id
634  * @out_target: the destination #GstGLTextureTarget
635  * @out_tex_format: the destination #GstGLFormat
636  * @out_width: the destination width
637  * @out_height: the destination height
638  *
639  * Copies the texture in #GstGLMemory into the texture specified by @tex_id,
640  * @out_target, @out_tex_format, @out_width and @out_height.
641  *
642  * Returns: whether the copy succeeded.
643  *
644  * Since: 1.8
645  */
646 gboolean
gst_gl_memory_copy_teximage(GstGLMemory * src,guint tex_id,GstGLTextureTarget out_target,GstGLFormat out_tex_format,gint out_width,gint out_height)647 gst_gl_memory_copy_teximage (GstGLMemory * src, guint tex_id,
648     GstGLTextureTarget out_target, GstGLFormat out_tex_format,
649     gint out_width, gint out_height)
650 {
651   const GstGLFuncs *gl;
652   guint out_tex_target;
653   GstMapInfo sinfo;
654   guint src_tex_id;
655   guint fbo[2];
656   guint n_fbos;
657 
658   gl = src->mem.context->gl_vtable;
659   out_tex_target = gst_gl_texture_target_to_gl (out_target);
660 
661   if (!gl->GenFramebuffers) {
662     GST_CAT_ERROR (GST_CAT_GL_MEMORY, "Framebuffer objects not supported");
663     goto error;
664   }
665 
666   if (USING_GLES2 (src->mem.context)
667       && (src->tex_format == GST_GL_LUMINANCE
668           || src->tex_format == GST_GL_LUMINANCE_ALPHA)) {
669     GST_CAT_FIXME (GST_CAT_GL_MEMORY,
670         "Cannot copy Luminance/Luminance Alpha textures in GLES");
671     goto error;
672   }
673 
674   if (!gst_memory_map (GST_MEMORY_CAST (src), &sinfo,
675           GST_MAP_READ | GST_MAP_GL)) {
676     GST_CAT_ERROR (GST_CAT_GL_MEMORY,
677         "Failed to map source memory for copying");
678     goto error;
679   }
680 
681   src_tex_id = *(guint *) sinfo.data;
682 
683   GST_CAT_LOG (GST_CAT_GL_MEMORY, "copying memory %p, tex %u into "
684       "texture %i", src, src_tex_id, tex_id);
685 
686   /* FIXME: try and avoid creating and destroying fbo's every copy... */
687   if (!gl->BlitFramebuffer || (!gl->DrawBuffer && !gl->DrawBuffers)
688       || !gl->ReadBuffer) {
689     /* create a framebuffer object */
690     n_fbos = 1;
691     gl->GenFramebuffers (n_fbos, &fbo[0]);
692     gl->BindFramebuffer (GL_FRAMEBUFFER, fbo[0]);
693 
694     gl->FramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
695         gst_gl_texture_target_to_gl (src->tex_target), src_tex_id, 0);
696 
697     if (!gst_gl_context_check_framebuffer_status (src->mem.context,
698             GL_FRAMEBUFFER))
699       goto fbo_error;
700 
701     gl->BindTexture (out_tex_target, tex_id);
702     _gst_gl_memory_start_log (src, "CopyTexImage2D");
703     gl->CopyTexImage2D (out_tex_target, 0, out_tex_format, 0, 0, out_width,
704         out_height, 0);
705     _gst_gl_memory_end_log (src);
706 
707     gl->BindTexture (out_tex_target, 0);
708     gl->BindFramebuffer (GL_FRAMEBUFFER, 0);
709 
710     gl->DeleteFramebuffers (n_fbos, &fbo[0]);
711   } else {
712     GLenum multipleRT[] = {
713       GL_COLOR_ATTACHMENT0,
714       GL_COLOR_ATTACHMENT1,
715       GL_COLOR_ATTACHMENT2
716     };
717 
718     /* create a framebuffer object */
719     n_fbos = 2;
720     gl->GenFramebuffers (n_fbos, &fbo[0]);
721 
722     gl->BindFramebuffer (GL_READ_FRAMEBUFFER, fbo[0]);
723     gl->FramebufferTexture2D (GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
724         gst_gl_texture_target_to_gl (src->tex_target), src_tex_id, 0);
725 
726     if (!gst_gl_context_check_framebuffer_status (src->mem.context,
727             GL_READ_FRAMEBUFFER))
728       goto fbo_error;
729 
730     gl->BindFramebuffer (GL_DRAW_FRAMEBUFFER, fbo[1]);
731 
732     gl->FramebufferTexture2D (GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
733         gst_gl_texture_target_to_gl (src->tex_target), tex_id, 0);
734 
735     if (!gst_gl_context_check_framebuffer_status (src->mem.context,
736             GL_DRAW_FRAMEBUFFER))
737       goto fbo_error;
738 
739     gl->BindTexture (out_tex_target, tex_id);
740     _gst_gl_memory_start_log (src, "BlitFramebuffer");
741     gl->ReadBuffer (GL_COLOR_ATTACHMENT0);
742     if (gl->DrawBuffers)
743       gl->DrawBuffers (1, multipleRT);
744     else
745       gl->DrawBuffer (GL_COLOR_ATTACHMENT0);
746     gl->BlitFramebuffer (0, 0, out_width, out_height,
747         0, 0, out_width, out_height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
748     _gst_gl_memory_end_log (src);
749 
750     gl->BindTexture (out_tex_target, 0);
751     gl->BindFramebuffer (GL_DRAW_FRAMEBUFFER, 0);
752     gl->BindFramebuffer (GL_READ_FRAMEBUFFER, 0);
753 
754     gl->DeleteFramebuffers (n_fbos, &fbo[0]);
755 
756     if (gl->DrawBuffer)
757       gl->DrawBuffer (GL_BACK);
758   }
759 
760   gst_memory_unmap (GST_MEMORY_CAST (src), &sinfo);
761 
762   return TRUE;
763 
764 fbo_error:
765   {
766     gl->BindTexture (out_tex_target, 0);
767     if (!gl->BlitFramebuffer) {
768       gl->BindFramebuffer (GL_FRAMEBUFFER, 0);
769     } else {
770       gl->BindFramebuffer (GL_DRAW_FRAMEBUFFER, 0);
771       gl->BindFramebuffer (GL_READ_FRAMEBUFFER, 0);
772     }
773 
774     gl->DeleteFramebuffers (n_fbos, &fbo[0]);
775 
776     gst_memory_unmap (GST_MEMORY_CAST (src), &sinfo);
777   }
778 error:
779   return FALSE;
780 }
781 
782 static void
_gl_tex_copy_thread(GstGLContext * context,gpointer data)783 _gl_tex_copy_thread (GstGLContext * context, gpointer data)
784 {
785   GstGLMemoryCopyParams *copy_params;
786 
787   copy_params = (GstGLMemoryCopyParams *) data;
788 
789   if (!copy_params->tex_id) {
790     GstGLFormat internal_format, out_gl_format;
791     guint out_gl_type, out_tex_target;
792 
793     out_tex_target = gst_gl_texture_target_to_gl (copy_params->tex_target);
794     internal_format = copy_params->src->tex_format;
795     gst_gl_format_type_from_sized_gl_format (internal_format, &out_gl_format,
796         &out_gl_type);
797     internal_format =
798         gst_gl_sized_gl_format_from_gl_format_type (context, out_gl_format,
799         out_gl_type);
800 
801     copy_params->tex_id =
802         _new_texture (context, out_tex_target,
803         internal_format, out_gl_format, out_gl_type, copy_params->out_width,
804         copy_params->out_height);
805   }
806 
807   copy_params->result = gst_gl_memory_copy_teximage (copy_params->src,
808       copy_params->tex_id, copy_params->tex_target, copy_params->tex_format,
809       copy_params->out_width, copy_params->out_height);
810 }
811 
812 static GstMemory *
_default_gl_tex_copy(GstGLMemory * src,gssize offset,gssize size)813 _default_gl_tex_copy (GstGLMemory * src, gssize offset, gssize size)
814 {
815   GstAllocationParams params = { 0, GST_MEMORY_CAST (src)->align, 0, 0 };
816   GstGLBaseMemoryAllocator *base_mem_allocator;
817   GstAllocator *allocator;
818   GstGLMemory *dest = NULL;
819 
820   allocator = GST_MEMORY_CAST (src)->allocator;
821   base_mem_allocator = (GstGLBaseMemoryAllocator *) allocator;
822 
823   if (src->tex_target == GST_GL_TEXTURE_TARGET_EXTERNAL_OES) {
824     GST_CAT_ERROR (GST_CAT_GL_MEMORY, "Cannot copy External OES textures");
825     return NULL;
826   }
827 
828   /* If not doing a full copy, then copy to sysmem, the 2D represention of the
829    * texture would become wrong */
830   if (offset > 0 || size < GST_MEMORY_CAST (src)->size) {
831     return base_mem_allocator->fallback_mem_copy (GST_MEMORY_CAST (src), offset,
832         size);
833   }
834 
835   dest = g_new0 (GstGLMemory, 1);
836 
837   gst_gl_memory_init (dest, allocator, NULL, src->mem.context, src->tex_target,
838       src->tex_format, &params, &src->info, src->plane, &src->valign, NULL,
839       NULL);
840 
841   if (!GST_MEMORY_FLAG_IS_SET (src, GST_GL_BASE_MEMORY_TRANSFER_NEED_UPLOAD)) {
842     GstMapInfo dinfo;
843 
844     if (!gst_memory_map (GST_MEMORY_CAST (dest), &dinfo,
845             GST_MAP_WRITE | GST_MAP_GL)) {
846       GST_CAT_WARNING (GST_CAT_GL_MEMORY,
847           "Failed not map destination for writing");
848       gst_memory_unref (GST_MEMORY_CAST (dest));
849       return NULL;
850     }
851 
852     if (!gst_gl_memory_copy_into ((GstGLMemory *) src,
853             ((GstGLMemory *) dest)->tex_id, src->tex_target,
854             src->tex_format, src->tex_width, GL_MEM_HEIGHT (src))) {
855       GST_CAT_WARNING (GST_CAT_GL_MEMORY, "Could not copy GL Memory");
856       gst_memory_unmap (GST_MEMORY_CAST (dest), &dinfo);
857       goto memcpy;
858     }
859 
860     gst_memory_unmap (GST_MEMORY_CAST (dest), &dinfo);
861   } else {
862   memcpy:
863     if (!gst_gl_base_memory_memcpy ((GstGLBaseMemory *) src,
864             (GstGLBaseMemory *) dest, offset, size)) {
865       GST_CAT_WARNING (GST_CAT_GL_MEMORY, "Could not copy GL Memory");
866       gst_memory_unref (GST_MEMORY_CAST (dest));
867       return NULL;
868     }
869   }
870 
871   return (GstMemory *) dest;
872 }
873 
874 static GstMemory *
_gl_tex_copy(GstGLMemory * src,gssize offset,gssize size)875 _gl_tex_copy (GstGLMemory * src, gssize offset, gssize size)
876 {
877   GstGLMemoryAllocatorClass *alloc_class;
878 
879   alloc_class = GST_GL_MEMORY_ALLOCATOR_GET_CLASS (src->mem.mem.allocator);
880 
881   if (src->tex_target == GST_GL_TEXTURE_TARGET_EXTERNAL_OES) {
882     GST_CAT_ERROR (GST_CAT_GL_MEMORY, "Cannot copy External OES textures");
883     return NULL;
884   }
885 
886   g_return_val_if_fail (alloc_class->copy, NULL);
887   return (GstMemory *) alloc_class->copy (GST_GL_BASE_MEMORY_CAST (src), offset,
888       size);
889 }
890 
891 static GstMemory *
_gl_tex_alloc(GstAllocator * allocator,gsize size,GstAllocationParams * params)892 _gl_tex_alloc (GstAllocator * allocator, gsize size,
893     GstAllocationParams * params)
894 {
895   g_warning ("Use gst_gl_base_memory_alloc to allocate from this allocator");
896 
897   return NULL;
898 }
899 
900 static void
_gl_tex_destroy(GstGLMemory * gl_mem)901 _gl_tex_destroy (GstGLMemory * gl_mem)
902 {
903   const GstGLFuncs *gl = gl_mem->mem.context->gl_vtable;
904 
905   if (gl_mem->tex_id && !gl_mem->texture_wrapped)
906     gl->DeleteTextures (1, &gl_mem->tex_id);
907 }
908 
909 static GstGLMemory *
_default_gl_tex_alloc(GstGLMemoryAllocator * allocator,GstGLVideoAllocationParams * params)910 _default_gl_tex_alloc (GstGLMemoryAllocator * allocator,
911     GstGLVideoAllocationParams * params)
912 {
913   guint alloc_flags = params->parent.alloc_flags;
914   GstGLMemory *mem;
915 
916   g_return_val_if_fail (alloc_flags & GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_VIDEO,
917       NULL);
918 
919   mem = g_new0 (GstGLMemory, 1);
920 
921   if (alloc_flags & GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_WRAP_GPU_HANDLE) {
922     mem->tex_id = GPOINTER_TO_UINT (params->parent.gl_handle);
923     mem->texture_wrapped = TRUE;
924   }
925 
926   gst_gl_memory_init (mem, GST_ALLOCATOR_CAST (allocator), NULL,
927       params->parent.context, params->target, params->tex_format,
928       params->parent.alloc_params, params->v_info, params->plane,
929       params->valign, params->parent.user_data, params->parent.notify);
930 
931   if (alloc_flags & GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_WRAP_GPU_HANDLE) {
932     GST_MINI_OBJECT_FLAG_SET (mem, GST_GL_BASE_MEMORY_TRANSFER_NEED_DOWNLOAD);
933   }
934   if (alloc_flags & GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_WRAP_SYSMEM) {
935     mem->mem.data = params->parent.wrapped_data;
936     GST_MINI_OBJECT_FLAG_SET (mem, GST_GL_BASE_MEMORY_TRANSFER_NEED_UPLOAD);
937   }
938 
939   return mem;
940 }
941 
942 static void
gst_gl_memory_allocator_class_init(GstGLMemoryAllocatorClass * klass)943 gst_gl_memory_allocator_class_init (GstGLMemoryAllocatorClass * klass)
944 {
945   GstGLBaseMemoryAllocatorClass *gl_base;
946   GstAllocatorClass *allocator_class;
947 
948   gl_base = (GstGLBaseMemoryAllocatorClass *) klass;
949   allocator_class = (GstAllocatorClass *) klass;
950 
951   klass->map = (GstGLBaseMemoryAllocatorMapFunction) _default_gl_tex_map;
952   klass->unmap = (GstGLBaseMemoryAllocatorUnmapFunction) _default_gl_tex_unmap;
953   klass->copy = (GstGLBaseMemoryAllocatorCopyFunction) _default_gl_tex_copy;
954 
955   gl_base->alloc =
956       (GstGLBaseMemoryAllocatorAllocFunction) _default_gl_tex_alloc;
957   gl_base->create = (GstGLBaseMemoryAllocatorCreateFunction) _gl_tex_create;
958   gl_base->map = (GstGLBaseMemoryAllocatorMapFunction) _gl_tex_map;
959   gl_base->unmap = (GstGLBaseMemoryAllocatorUnmapFunction) _gl_tex_unmap;
960   gl_base->copy = (GstGLBaseMemoryAllocatorCopyFunction) _gl_tex_copy;
961   gl_base->destroy = (GstGLBaseMemoryAllocatorDestroyFunction) _gl_tex_destroy;
962 
963   allocator_class->alloc = _gl_tex_alloc;
964 }
965 
966 static void
gst_gl_memory_allocator_init(GstGLMemoryAllocator * allocator)967 gst_gl_memory_allocator_init (GstGLMemoryAllocator * allocator)
968 {
969   GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
970 
971   alloc->mem_type = GST_GL_MEMORY_ALLOCATOR_NAME;
972 
973   GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC);
974 }
975 
976 /**
977  * gst_gl_memory_copy_into:
978  * @gl_mem:a #GstGLMemory
979  * @tex_id:OpenGL texture id
980  * @target: the #GstGLTextureTarget
981  * @tex_format: the #GstGLFormat
982  * @width: width of @tex_id
983  * @height: height of @tex_id
984  *
985  * Copies @gl_mem into the texture specfified by @tex_id.  The format of @tex_id
986  * is specified by @tex_format, @width and @height.
987  *
988  * Returns: Whether the copy suceeded
989  *
990  * Since: 1.8
991  */
992 gboolean
gst_gl_memory_copy_into(GstGLMemory * gl_mem,guint tex_id,GstGLTextureTarget target,GstGLFormat tex_format,gint width,gint height)993 gst_gl_memory_copy_into (GstGLMemory * gl_mem, guint tex_id,
994     GstGLTextureTarget target, GstGLFormat tex_format, gint width, gint height)
995 {
996   GstGLMemoryCopyParams copy_params;
997 
998   copy_params.src = gl_mem;
999   copy_params.tex_id = tex_id;
1000   copy_params.tex_target = target;
1001   copy_params.tex_format = tex_format;
1002   copy_params.out_width = width;
1003   copy_params.out_height = height;
1004 
1005   gst_gl_context_thread_add (gl_mem->mem.context, _gl_tex_copy_thread,
1006       &copy_params);
1007 
1008   return copy_params.result;
1009 }
1010 
1011 /**
1012  * gst_gl_memory_get_texture_width:
1013  * @gl_mem: a #GstGLMemory
1014  *
1015  * Returns: the texture width of @gl_mem
1016  *
1017  * Since: 1.8
1018  */
1019 gint
gst_gl_memory_get_texture_width(GstGLMemory * gl_mem)1020 gst_gl_memory_get_texture_width (GstGLMemory * gl_mem)
1021 {
1022   g_return_val_if_fail (gst_is_gl_memory ((GstMemory *) gl_mem), 0);
1023 
1024   return gl_mem->tex_width;
1025 }
1026 
1027 /**
1028  * gst_gl_memory_get_texture_height:
1029  * @gl_mem: a #GstGLMemory
1030  *
1031  * Returns: the texture height of @gl_mem
1032  *
1033  * Since: 1.8
1034  */
1035 gint
gst_gl_memory_get_texture_height(GstGLMemory * gl_mem)1036 gst_gl_memory_get_texture_height (GstGLMemory * gl_mem)
1037 {
1038   g_return_val_if_fail (gst_is_gl_memory ((GstMemory *) gl_mem), 0);
1039 
1040   return _get_plane_height (&gl_mem->info, gl_mem->plane);
1041 }
1042 
1043 /**
1044  * gst_gl_memory_get_texture_format:
1045  * @gl_mem: a #GstGLMemory
1046  *
1047  * Returns: the #GstGLFormat of @gl_mem
1048  *
1049  * Since: 1.12
1050  */
1051 GstGLFormat
gst_gl_memory_get_texture_format(GstGLMemory * gl_mem)1052 gst_gl_memory_get_texture_format (GstGLMemory * gl_mem)
1053 {
1054   g_return_val_if_fail (gst_is_gl_memory ((GstMemory *) gl_mem), 0);
1055 
1056   return gl_mem->tex_format;
1057 }
1058 
1059 /**
1060  * gst_gl_memory_get_texture_target:
1061  * @gl_mem: a #GstGLMemory
1062  *
1063  * Returns: the #GstGLTextureTarget of @gl_mem
1064  *
1065  * Since: 1.8
1066  */
1067 GstGLTextureTarget
gst_gl_memory_get_texture_target(GstGLMemory * gl_mem)1068 gst_gl_memory_get_texture_target (GstGLMemory * gl_mem)
1069 {
1070   g_return_val_if_fail (gst_is_gl_memory ((GstMemory *) gl_mem), 0);
1071 
1072   return gl_mem->tex_target;
1073 }
1074 
1075 /**
1076  * gst_gl_memory_get_texture_id:
1077  * @gl_mem: a #GstGLMemory
1078  *
1079  * Returns: the OpenGL texture handle of @gl_mem
1080  *
1081  * Since: 1.8
1082  */
1083 guint
gst_gl_memory_get_texture_id(GstGLMemory * gl_mem)1084 gst_gl_memory_get_texture_id (GstGLMemory * gl_mem)
1085 {
1086   g_return_val_if_fail (gst_is_gl_memory ((GstMemory *) gl_mem), 0);
1087 
1088   return gl_mem->tex_id;
1089 }
1090 
1091 /**
1092  * gst_gl_memory_init_once:
1093  *
1094  * Initializes the GL Base Texture allocator. It is safe to call this function
1095  * multiple times.  This must be called before any other GstGLMemory operation.
1096  *
1097  * Since: 1.4
1098  */
1099 void
gst_gl_memory_init_once(void)1100 gst_gl_memory_init_once (void)
1101 {
1102   static volatile gsize _init = 0;
1103 
1104   if (g_once_init_enter (&_init)) {
1105     gst_gl_base_memory_init_once ();
1106 
1107     GST_DEBUG_CATEGORY_INIT (GST_CAT_GL_MEMORY, "glbasetexture", 0,
1108         "OpenGL Base Texture Memory");
1109 
1110     _gl_memory_allocator = g_object_new (GST_TYPE_GL_MEMORY_ALLOCATOR, NULL);
1111     gst_object_ref_sink (_gl_memory_allocator);
1112 
1113     gst_allocator_register (GST_GL_MEMORY_ALLOCATOR_NAME, _gl_memory_allocator);
1114 
1115     g_once_init_leave (&_init, 1);
1116   }
1117 }
1118 
1119 /**
1120  * gst_is_gl_memory:
1121  * @mem:a #GstMemory
1122  *
1123  * Returns: whether the memory at @mem is a #GstGLMemory
1124  *
1125  * Since: 1.4
1126  */
1127 gboolean
gst_is_gl_memory(GstMemory * mem)1128 gst_is_gl_memory (GstMemory * mem)
1129 {
1130   return mem != NULL && mem->allocator != NULL
1131       && g_type_is_a (G_OBJECT_TYPE (mem->allocator),
1132       GST_TYPE_GL_MEMORY_ALLOCATOR);
1133 }
1134 
1135 G_DEFINE_BOXED_TYPE (GstGLVideoAllocationParams, gst_gl_video_allocation_params,
1136     (GBoxedCopyFunc) gst_gl_allocation_params_copy,
1137     (GBoxedFreeFunc) gst_gl_allocation_params_free);
1138 
1139 static void
_gst_gl_video_allocation_params_set_video_alignment(GstGLVideoAllocationParams * params,GstVideoAlignment * valign)1140 _gst_gl_video_allocation_params_set_video_alignment (GstGLVideoAllocationParams
1141     * params, GstVideoAlignment * valign)
1142 {
1143   g_return_if_fail (params != NULL);
1144 
1145   if (!params->valign)
1146     params->valign = g_new0 (GstVideoAlignment, 1);
1147 
1148   if (valign) {
1149     *params->valign = *valign;
1150   } else {
1151     gst_video_alignment_reset (params->valign);
1152   }
1153 }
1154 
1155 /**
1156  * gst_gl_video_allocation_params_init_full: (skip)
1157  * @params: a #GstGLVideoAllocationParams to initialize
1158  * @struct_size: the size of the struct in @params
1159  * @alloc_flags: some allocation flags
1160  * @copy: a copy function
1161  * @free: a free function
1162  * @context: a #GstGLContext
1163  * @alloc_params: (allow-none): the #GstAllocationParams for @wrapped_data
1164  * @v_info: the #GstVideoInfo for @wrapped_data
1165  * @plane: the video plane @wrapped_data represents
1166  * @valign: (allow-none): any #GstVideoAlignment applied to symem mappings of @wrapped_data
1167  * @target: the #GstGLTextureTarget
1168  * @tex_format: the #GstGLFormat
1169  * @wrapped_data: (allow-none): the optional data pointer to wrap
1170  * @gl_handle: the optional OpenGL handle to wrap or 0
1171  * @user_data: (allow-none): user data to call @notify with
1172  * @notify: (allow-none): a #GDestroyNotify
1173  *
1174  * Intended for subclass usage
1175  *
1176  * Returns: initializes @params with the parameters specified
1177  *
1178  * Since: 1.8
1179  */
1180 gboolean
gst_gl_video_allocation_params_init_full(GstGLVideoAllocationParams * params,gsize struct_size,guint alloc_flags,GstGLAllocationParamsCopyFunc copy,GstGLAllocationParamsFreeFunc free,GstGLContext * context,GstAllocationParams * alloc_params,GstVideoInfo * v_info,guint plane,GstVideoAlignment * valign,GstGLTextureTarget target,GstGLFormat tex_format,gpointer wrapped_data,gpointer gl_handle,gpointer user_data,GDestroyNotify notify)1181 gst_gl_video_allocation_params_init_full (GstGLVideoAllocationParams * params,
1182     gsize struct_size, guint alloc_flags, GstGLAllocationParamsCopyFunc copy,
1183     GstGLAllocationParamsFreeFunc free, GstGLContext * context,
1184     GstAllocationParams * alloc_params, GstVideoInfo * v_info,
1185     guint plane, GstVideoAlignment * valign, GstGLTextureTarget target,
1186     GstGLFormat tex_format, gpointer wrapped_data, gpointer gl_handle,
1187     gpointer user_data, GDestroyNotify notify)
1188 {
1189   guint i;
1190 
1191   g_return_val_if_fail (params != NULL, FALSE);
1192   g_return_val_if_fail (copy != NULL, FALSE);
1193   g_return_val_if_fail (free != NULL, FALSE);
1194   g_return_val_if_fail (GST_IS_GL_CONTEXT (context), FALSE);
1195   g_return_val_if_fail (v_info != NULL, FALSE);
1196 
1197   memset (params, 0, sizeof (*params));
1198 
1199   if (!gst_gl_allocation_params_init ((GstGLAllocationParams *) params,
1200           struct_size, alloc_flags, copy, free, context, 0, alloc_params,
1201           wrapped_data, gl_handle, user_data, notify))
1202     return FALSE;
1203 
1204   params->v_info = g_new0 (GstVideoInfo, 1);
1205   *params->v_info = *v_info;
1206   for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) {
1207     params->v_info->offset[i] = v_info->offset[i];
1208     params->v_info->stride[i] = v_info->stride[i];
1209   }
1210   _gst_gl_video_allocation_params_set_video_alignment (params, valign);
1211   params->target = target;
1212   params->tex_format = tex_format;
1213   params->plane = plane;
1214 
1215   return TRUE;
1216 }
1217 
1218 /**
1219  * gst_gl_video_allocation_params_new:
1220  * @context: a #GstGLContext
1221  * @alloc_params: (allow-none): the #GstAllocationParams for sysmem mappings of the texture
1222  * @v_info: the #GstVideoInfo for the texture
1223  * @plane: the video plane of @v_info to allocate
1224  * @valign: (allow-none): any #GstVideoAlignment applied to symem mappings of the texture
1225  * @target: the #GstGLTextureTarget for the created textures
1226  * @tex_format: the #GstGLFormat for the created textures
1227  *
1228  * Returns: a new #GstGLVideoAllocationParams for allocating #GstGLMemory's
1229  *
1230  * Since: 1.8
1231  */
1232 GstGLVideoAllocationParams *
gst_gl_video_allocation_params_new(GstGLContext * context,GstAllocationParams * alloc_params,GstVideoInfo * v_info,guint plane,GstVideoAlignment * valign,GstGLTextureTarget target,GstGLFormat tex_format)1233 gst_gl_video_allocation_params_new (GstGLContext * context,
1234     GstAllocationParams * alloc_params, GstVideoInfo * v_info, guint plane,
1235     GstVideoAlignment * valign, GstGLTextureTarget target,
1236     GstGLFormat tex_format)
1237 {
1238   GstGLVideoAllocationParams *params = g_new0 (GstGLVideoAllocationParams, 1);
1239 
1240   if (!gst_gl_video_allocation_params_init_full (params,
1241           sizeof (GstGLVideoAllocationParams),
1242           GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_ALLOC |
1243           GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_VIDEO,
1244           (GstGLAllocationParamsCopyFunc)
1245           gst_gl_video_allocation_params_copy_data,
1246           (GstGLAllocationParamsFreeFunc)
1247           gst_gl_video_allocation_params_free_data, context, alloc_params,
1248           v_info, plane, valign, target, tex_format, NULL, 0, NULL, NULL)) {
1249     g_free (params);
1250     return NULL;
1251   }
1252 
1253   return params;
1254 }
1255 
1256 /**
1257  * gst_gl_video_allocation_params_new_wrapped_data:
1258  * @context: a #GstGLContext
1259  * @alloc_params: (allow-none): the #GstAllocationParams for @wrapped_data
1260  * @v_info: the #GstVideoInfo for @wrapped_data
1261  * @plane: the video plane @wrapped_data represents
1262  * @valign: (allow-none): any #GstVideoAlignment applied to symem mappings of @wrapped_data
1263  * @target: the #GstGLTextureTarget for @wrapped_data
1264  * @tex_format: the #GstGLFormat for @wrapped_data
1265  * @wrapped_data: the data pointer to wrap
1266  * @user_data: (allow-none): user data to call @notify with
1267  * @notify: (allow-none): a #GDestroyNotify
1268  *
1269  * Returns: a new #GstGLVideoAllocationParams for wrapping @wrapped_data
1270  *
1271  * Since: 1.8
1272  */
1273 GstGLVideoAllocationParams *
gst_gl_video_allocation_params_new_wrapped_data(GstGLContext * context,GstAllocationParams * alloc_params,GstVideoInfo * v_info,guint plane,GstVideoAlignment * valign,GstGLTextureTarget target,GstGLFormat tex_format,gpointer wrapped_data,gpointer user_data,GDestroyNotify notify)1274 gst_gl_video_allocation_params_new_wrapped_data (GstGLContext * context,
1275     GstAllocationParams * alloc_params, GstVideoInfo * v_info, guint plane,
1276     GstVideoAlignment * valign, GstGLTextureTarget target,
1277     GstGLFormat tex_format, gpointer wrapped_data, gpointer user_data,
1278     GDestroyNotify notify)
1279 {
1280   GstGLVideoAllocationParams *params = g_new0 (GstGLVideoAllocationParams, 1);
1281 
1282   if (!gst_gl_video_allocation_params_init_full (params,
1283           sizeof (GstGLVideoAllocationParams),
1284           GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_WRAP_SYSMEM |
1285           GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_VIDEO,
1286           (GstGLAllocationParamsCopyFunc)
1287           gst_gl_video_allocation_params_copy_data,
1288           (GstGLAllocationParamsFreeFunc)
1289           gst_gl_video_allocation_params_free_data, context, alloc_params,
1290           v_info, plane, valign, target, tex_format, wrapped_data, 0, user_data,
1291           notify)) {
1292     g_free (params);
1293     return NULL;
1294   }
1295 
1296   return params;
1297 }
1298 
1299 /**
1300  * gst_gl_video_allocation_params_new_wrapped_gl_handle:
1301  * @context: a #GstGLContext
1302  * @alloc_params: (allow-none): the #GstAllocationParams for @tex_id
1303  * @v_info: the #GstVideoInfo for @tex_id
1304  * @plane: the video plane @tex_id represents
1305  * @valign: (allow-none): any #GstVideoAlignment applied to symem mappings of @tex_id
1306  * @target: the #GstGLTextureTarget for @tex_id
1307  * @tex_format: the #GstGLFormat for @tex_id
1308  * @gl_handle: the GL handle to wrap
1309  * @user_data: (allow-none): user data to call @notify with
1310  * @notify: (allow-none): a #GDestroyNotify
1311  *
1312  * @gl_handle is defined by the specific OpenGL handle being wrapped
1313  * For #GstGLMemory and #GstGLMemoryPBO it is an OpenGL texture id.
1314  * Other memory types may define it to require a different type of parameter.
1315  *
1316  * Returns: a new #GstGLVideoAllocationParams for wrapping @gl_handle
1317  *
1318  * Since: 1.8
1319  */
1320 GstGLVideoAllocationParams *
gst_gl_video_allocation_params_new_wrapped_gl_handle(GstGLContext * context,GstAllocationParams * alloc_params,GstVideoInfo * v_info,guint plane,GstVideoAlignment * valign,GstGLTextureTarget target,GstGLFormat tex_format,gpointer gl_handle,gpointer user_data,GDestroyNotify notify)1321 gst_gl_video_allocation_params_new_wrapped_gl_handle (GstGLContext * context,
1322     GstAllocationParams * alloc_params, GstVideoInfo * v_info, guint plane,
1323     GstVideoAlignment * valign, GstGLTextureTarget target,
1324     GstGLFormat tex_format, gpointer gl_handle, gpointer user_data,
1325     GDestroyNotify notify)
1326 {
1327   GstGLVideoAllocationParams *params = g_new0 (GstGLVideoAllocationParams, 1);
1328 
1329   if (!gst_gl_video_allocation_params_init_full (params,
1330           sizeof (GstGLVideoAllocationParams),
1331           GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_WRAP_GPU_HANDLE |
1332           GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_VIDEO,
1333           (GstGLAllocationParamsCopyFunc)
1334           gst_gl_video_allocation_params_copy_data,
1335           (GstGLAllocationParamsFreeFunc)
1336           gst_gl_video_allocation_params_free_data, context, alloc_params,
1337           v_info, plane, valign, target, tex_format, NULL, gl_handle, user_data,
1338           notify)) {
1339     g_free (params);
1340     return NULL;
1341   }
1342 
1343   return params;
1344 }
1345 
1346 /**
1347  * gst_gl_video_allocation_params_new_wrapped_texture:
1348  * @context: a #GstGLContext
1349  * @alloc_params: (allow-none): the #GstAllocationParams for @tex_id
1350  * @v_info: the #GstVideoInfo for @tex_id
1351  * @plane: the video plane @tex_id represents
1352  * @valign: (allow-none): any #GstVideoAlignment applied to symem mappings of @tex_id
1353  * @target: the #GstGLTextureTarget for @tex_id
1354  * @tex_format: the #GstGLFormat for @tex_id
1355  * @tex_id: the GL texture to wrap
1356  * @user_data: (allow-none): user data to call @notify with
1357  * @notify: (allow-none): a #GDestroyNotify
1358  *
1359  * Returns: a new #GstGLVideoAllocationParams for wrapping @tex_id
1360  *
1361  * Since: 1.8
1362  */
1363 GstGLVideoAllocationParams *
gst_gl_video_allocation_params_new_wrapped_texture(GstGLContext * context,GstAllocationParams * alloc_params,GstVideoInfo * v_info,guint plane,GstVideoAlignment * valign,GstGLTextureTarget target,GstGLFormat tex_format,guint tex_id,gpointer user_data,GDestroyNotify notify)1364 gst_gl_video_allocation_params_new_wrapped_texture (GstGLContext * context,
1365     GstAllocationParams * alloc_params, GstVideoInfo * v_info, guint plane,
1366     GstVideoAlignment * valign, GstGLTextureTarget target,
1367     GstGLFormat tex_format, guint tex_id, gpointer user_data,
1368     GDestroyNotify notify)
1369 {
1370   return gst_gl_video_allocation_params_new_wrapped_gl_handle (context,
1371       alloc_params, v_info, plane, valign, target, tex_format,
1372       GUINT_TO_POINTER (tex_id), user_data, notify);
1373 }
1374 
1375 /**
1376  * gst_gl_video_allocation_params_free_data:
1377  * @params: a #GstGLVideoAllocationParams
1378  *
1379  * Unset and free any dynamically allocated resources.  Intended for subclass
1380  * usage only to chain up at the end of a subclass free function.
1381  *
1382  * Since: 1.8
1383  */
1384 void
gst_gl_video_allocation_params_free_data(GstGLVideoAllocationParams * params)1385 gst_gl_video_allocation_params_free_data (GstGLVideoAllocationParams * params)
1386 {
1387   g_free (params->v_info);
1388   g_free (params->valign);
1389 
1390   gst_gl_allocation_params_free_data (&params->parent);
1391 }
1392 
1393 /**
1394  * gst_gl_video_allocation_params_copy_data:
1395  * @src_vid: source #GstGLVideoAllocationParams to copy from
1396  * @dest_vid: destination #GstGLVideoAllocationParams to copy into
1397  *
1398  * Copy and set any dynamically allocated resources in @dest_vid.  Intended
1399  * for subclass usage only to chain up at the end of a subclass copy function.
1400  *
1401  * Since: 1.8
1402  */
1403 void
gst_gl_video_allocation_params_copy_data(GstGLVideoAllocationParams * src_vid,GstGLVideoAllocationParams * dest_vid)1404 gst_gl_video_allocation_params_copy_data (GstGLVideoAllocationParams * src_vid,
1405     GstGLVideoAllocationParams * dest_vid)
1406 {
1407   GstGLAllocationParams *src = (GstGLAllocationParams *) src_vid;
1408   GstGLAllocationParams *dest = (GstGLAllocationParams *) dest_vid;
1409   guint i;
1410 
1411   gst_gl_allocation_params_copy_data (src, dest);
1412 
1413   dest_vid->v_info = g_new0 (GstVideoInfo, 1);
1414   *dest_vid->v_info = *src_vid->v_info;
1415   for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) {
1416     dest_vid->v_info->offset[i] = src_vid->v_info->offset[i];
1417     dest_vid->v_info->stride[i] = src_vid->v_info->stride[i];
1418   }
1419   _gst_gl_video_allocation_params_set_video_alignment (dest_vid,
1420       src_vid->valign);
1421   dest_vid->target = src_vid->target;
1422   dest_vid->tex_format = src_vid->tex_format;
1423   dest_vid->plane = src_vid->plane;
1424 }
1425 
1426 /**
1427  * gst_gl_memory_setup_buffer: (skip)
1428  * @allocator: the @GstGLMemoryAllocator to allocate from
1429  * @buffer: a #GstBuffer to setup
1430  * @params: the #GstGLVideoAllocationParams to allocate with
1431  * @tex_formats: (allow-none) (array length=n_wrapped_pointers):
1432  *     a list of #GstGLFormat's to allocate with.
1433  * @wrapped_data: (array length=n_wrapped_pointers) (element-type gpointer):
1434  *     a list of wrapped data pointers
1435  * @n_wrapped_pointers: the number of elements in @tex_formats and @wrapped_data
1436  *
1437  * Returns: whether the buffer was correctly setup
1438  *
1439  * Since: 1.8
1440  */
1441 gboolean
gst_gl_memory_setup_buffer(GstGLMemoryAllocator * allocator,GstBuffer * buffer,GstGLVideoAllocationParams * params,GstGLFormat * tex_formats,gpointer * wrapped_data,gsize n_wrapped_pointers)1442 gst_gl_memory_setup_buffer (GstGLMemoryAllocator * allocator,
1443     GstBuffer * buffer, GstGLVideoAllocationParams * params,
1444     GstGLFormat * tex_formats, gpointer * wrapped_data,
1445     gsize n_wrapped_pointers)
1446 {
1447   GstGLBaseMemoryAllocator *base_allocator;
1448   guint n_mem, i, v, views;
1449   guint alloc_flags = params->parent.alloc_flags;
1450 
1451   g_return_val_if_fail (params != NULL, FALSE);
1452   g_return_val_if_fail (alloc_flags & GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_VIDEO,
1453       FALSE);
1454 
1455   base_allocator = GST_GL_BASE_MEMORY_ALLOCATOR (allocator);
1456   n_mem = GST_VIDEO_INFO_N_PLANES (params->v_info);
1457 
1458   if (GST_VIDEO_INFO_MULTIVIEW_MODE (params->v_info) ==
1459       GST_VIDEO_MULTIVIEW_MODE_SEPARATED)
1460     views = params->v_info->views;
1461   else
1462     views = 1;
1463 
1464   if (n_wrapped_pointers == views)
1465     n_mem = 1;
1466 
1467   /* Sanity check for the code below; there should be as many pointers as the
1468    * number of memory we are going to create */
1469   g_return_val_if_fail (!wrapped_data
1470       || n_mem * views == n_wrapped_pointers, FALSE);
1471 
1472   for (v = 0; v < views; v++) {
1473     for (i = 0; i < n_mem; i++) {
1474       GstGLMemory *gl_mem;
1475 
1476       if (tex_formats) {
1477         params->tex_format = tex_formats[i];
1478       } else {
1479         params->tex_format =
1480             gst_gl_format_from_video_info (params->parent.context,
1481             params->v_info, i);
1482       }
1483 
1484       params->plane = i;
1485       if (alloc_flags & GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_WRAP_SYSMEM) {
1486         g_return_val_if_fail (wrapped_data != NULL, FALSE);
1487         params->parent.wrapped_data = wrapped_data[i];
1488       } else if (alloc_flags &
1489           GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_WRAP_GPU_HANDLE) {
1490         g_return_val_if_fail (wrapped_data != NULL, FALSE);
1491         params->parent.gl_handle = wrapped_data[i];
1492       }
1493 
1494       if (!(gl_mem = (GstGLMemory *) gst_gl_base_memory_alloc (base_allocator,
1495                   (GstGLAllocationParams *) params)))
1496         return FALSE;
1497 
1498       gst_buffer_append_memory (buffer, (GstMemory *) gl_mem);
1499     }
1500 
1501     gst_buffer_add_video_meta_full (buffer, v,
1502         GST_VIDEO_INFO_FORMAT (params->v_info),
1503         GST_VIDEO_INFO_WIDTH (params->v_info),
1504         GST_VIDEO_INFO_HEIGHT (params->v_info), n_mem, params->v_info->offset,
1505         params->v_info->stride);
1506   }
1507 
1508   return TRUE;
1509 }
1510 
1511 /**
1512  * gst_gl_memory_allocator_get_default:
1513  * @context: a #GstGLContext
1514  *
1515  * Returns: (transfer full): the default #GstGLMemoryAllocator supported by
1516  *          @context
1517  *
1518  * Since: 1.8
1519  */
1520 GstGLMemoryAllocator *
gst_gl_memory_allocator_get_default(GstGLContext * context)1521 gst_gl_memory_allocator_get_default (GstGLContext * context)
1522 {
1523   GstGLMemoryAllocator *allocator = NULL;
1524 
1525   g_return_val_if_fail (GST_IS_GL_CONTEXT (context), NULL);
1526 
1527   /* we can only use the pbo allocator with GL > 3.0 contexts */
1528   if (gst_gl_context_check_gl_version (context,
1529           GST_GL_API_OPENGL | GST_GL_API_OPENGL3 | GST_GL_API_GLES2, 3, 0)) {
1530     allocator = (GstGLMemoryAllocator *)
1531         gst_allocator_find (GST_GL_MEMORY_PBO_ALLOCATOR_NAME);
1532   } else {
1533     allocator = (GstGLMemoryAllocator *)
1534         gst_allocator_find (GST_GL_MEMORY_ALLOCATOR_NAME);
1535   }
1536 
1537   return allocator;
1538 }
1539