• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * GStreamer
3  * Copyright (C) 2012 Matthew Waters <ystreet00@gmail.com>
4  * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include <string.h>
27 
28 #include "gstglmemorypbo.h"
29 
30 #include "gstglbuffer.h"
31 #include "gstglcontext.h"
32 #include "gstglfuncs.h"
33 #include "gstglutils.h"
34 
35 /**
36  * SECTION:gstglmemorypbo
37  * @title: GstGLMemoryPBO
38  * @short_description: memory subclass for GL textures
39  * @see_also: #GstMemory, #GstAllocator, #GstGLBufferPool
40  *
41  * #GstGLMemoryPBO is created or wrapped through gst_gl_base_memory_alloc()
42  * with #GstGLVideoAllocationParams.
43  *
44  * Data is uploaded or downloaded from the GPU as is necessary.
45  */
46 
47 /* Implementation notes
48  *
49  * PBO transfer's are implemented using GstGLBuffer.  We just need to
50  * ensure that the texture data is written/read to/from before/after calling
51  * map (mem->pbo, READ) which performs the pbo buffer transfer.
52  */
53 
54 #define USING_OPENGL(context) (gst_gl_context_check_gl_version (context, GST_GL_API_OPENGL, 1, 0))
55 #define USING_OPENGL3(context) (gst_gl_context_check_gl_version (context, GST_GL_API_OPENGL3, 3, 1))
56 #define USING_GLES(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES, 1, 0))
57 #define USING_GLES2(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 2, 0))
58 #define USING_GLES3(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 3, 0))
59 
60 #define GL_MEM_HEIGHT(gl_mem) _get_plane_height (&gl_mem->mem.info, gl_mem->mem.plane)
61 #define GL_MEM_STRIDE(gl_mem) GST_VIDEO_INFO_PLANE_STRIDE (&gl_mem->mem.info, gl_mem->mem.plane)
62 
63 #define CONTEXT_SUPPORTS_PBO_UPLOAD(context) \
64     (gst_gl_context_check_gl_version (context, \
65         GST_GL_API_OPENGL | GST_GL_API_OPENGL3, 2, 1) \
66         || gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 3, 0))
67 #define CONTEXT_SUPPORTS_PBO_DOWNLOAD(context) \
68     (gst_gl_context_check_gl_version (context, \
69         GST_GL_API_OPENGL | GST_GL_API_OPENGL3 | GST_GL_API_GLES2, 3, 0))
70 
71 GST_DEBUG_CATEGORY_STATIC (GST_CAT_GL_MEMORY);
72 #define GST_CAT_DEFAULT GST_CAT_GL_MEMORY
73 
74 static GstAllocator *_gl_allocator;
75 
76 /* compatibility definitions... */
77 #ifndef GL_PIXEL_PACK_BUFFER
78 #define GL_PIXEL_PACK_BUFFER 0x88EB
79 #endif
80 #ifndef GL_PIXEL_UNPACK_BUFFER
81 #define GL_PIXEL_UNPACK_BUFFER 0x88EC
82 #endif
83 #ifndef GL_STREAM_READ
84 #define GL_STREAM_READ 0x88E1
85 #endif
86 #ifndef GL_STREAM_DRAW
87 #define GL_STREAM_DRAW 0x88E0
88 #endif
89 #ifndef GL_STREAM_COPY
90 #define GL_STREAM_COPY 0x88E2
91 #endif
92 #ifndef GL_UNPACK_ROW_LENGTH
93 #define GL_UNPACK_ROW_LENGTH 0x0CF2
94 #endif
95 
96 #ifndef GL_TEXTURE_RECTANGLE
97 #define GL_TEXTURE_RECTANGLE 0x84F5
98 #endif
99 #ifndef GL_TEXTURE_EXTERNAL_OES
100 #define GL_TEXTURE_EXTERNAL_OES 0x8D65
101 #endif
102 
103 #define parent_class gst_gl_memory_pbo_allocator_parent_class
104 G_DEFINE_TYPE (GstGLMemoryPBOAllocator, gst_gl_memory_pbo_allocator,
105     GST_TYPE_GL_MEMORY_ALLOCATOR);
106 
107 GST_DEFINE_MINI_OBJECT_TYPE (GstGLMemoryPBO, gst_gl_memory_pbo);
108 
109 typedef struct
110 {
111   /* in */
112   GstGLMemoryPBO *src;
113   GstGLFormat out_format;
114   guint out_width, out_height;
115   guint out_stride;
116   gboolean respecify;
117   GstGLTextureTarget tex_target;
118   /* inout */
119   guint tex_id;
120   /* out */
121   gboolean result;
122 } GstGLMemoryPBOCopyParams;
123 
124 static inline guint
_get_plane_height(const GstVideoInfo * info,guint plane)125 _get_plane_height (const GstVideoInfo * info, guint plane)
126 {
127   if (GST_VIDEO_INFO_IS_YUV (info))
128     /* For now component width and plane width are the same and the
129      * plane-component mapping matches
130      */
131     return GST_VIDEO_INFO_COMP_HEIGHT (info, plane);
132   else                          /* RGB, GRAY */
133     return GST_VIDEO_INFO_HEIGHT (info);
134 }
135 
136 static void
_upload_pbo_memory(GstGLMemoryPBO * gl_mem,GstMapInfo * info,GstGLBuffer * pbo,GstMapInfo * pbo_info)137 _upload_pbo_memory (GstGLMemoryPBO * gl_mem, GstMapInfo * info,
138     GstGLBuffer * pbo, GstMapInfo * pbo_info)
139 {
140   GstGLContext *context = gl_mem->mem.mem.context;
141   const GstGLFuncs *gl;
142   guint pbo_id;
143 
144   if (!GST_MEMORY_FLAG_IS_SET (gl_mem, GST_GL_BASE_MEMORY_TRANSFER_NEED_UPLOAD))
145     return;
146 
147   g_return_if_fail (CONTEXT_SUPPORTS_PBO_UPLOAD (context));
148 
149   gl = context->gl_vtable;
150   pbo_id = *(guint *) pbo_info->data;
151 
152   GST_CAT_LOG (GST_CAT_GL_MEMORY, "upload for texture id:%u, with pbo %u %ux%u",
153       gl_mem->mem.tex_id, pbo_id, gl_mem->mem.tex_width,
154       GL_MEM_HEIGHT (gl_mem));
155 
156   gl->BindBuffer (GL_PIXEL_UNPACK_BUFFER, pbo_id);
157   gst_gl_memory_texsubimage (GST_GL_MEMORY_CAST (gl_mem), NULL);
158   gl->BindBuffer (GL_PIXEL_UNPACK_BUFFER, 0);
159 }
160 
161 static guint
_new_texture(GstGLContext * context,guint target,guint internal_format,guint format,guint type,guint width,guint height)162 _new_texture (GstGLContext * context, guint target, guint internal_format,
163     guint format, guint type, guint width, guint height)
164 {
165   const GstGLFuncs *gl = context->gl_vtable;
166   guint tex_id;
167 
168   gl->GenTextures (1, &tex_id);
169   gl->BindTexture (target, tex_id);
170   if (target == GL_TEXTURE_2D || target == GL_TEXTURE_RECTANGLE)
171     gl->TexImage2D (target, 0, internal_format, width, height, 0, format, type,
172         NULL);
173 
174   gl->TexParameteri (target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
175   gl->TexParameteri (target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
176   gl->TexParameteri (target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
177   gl->TexParameteri (target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
178 
179   gl->BindTexture (target, 0);
180 
181   return tex_id;
182 }
183 
184 static gboolean
_gl_mem_create(GstGLMemoryPBO * gl_mem,GError ** error)185 _gl_mem_create (GstGLMemoryPBO * gl_mem, GError ** error)
186 {
187   GstGLContext *context = gl_mem->mem.mem.context;
188   GstGLBaseMemoryAllocatorClass *alloc_class;
189 
190   alloc_class = GST_GL_BASE_MEMORY_ALLOCATOR_CLASS (parent_class);
191   if (!alloc_class->create ((GstGLBaseMemory *) gl_mem, error))
192     return FALSE;
193 
194   if (CONTEXT_SUPPORTS_PBO_DOWNLOAD (context)
195       || CONTEXT_SUPPORTS_PBO_UPLOAD (context)) {
196     GstAllocationParams alloc_params =
197         { 0, GST_MEMORY_CAST (gl_mem)->align, 0, 0 };
198     GstGLBaseMemoryAllocator *buf_allocator;
199     GstGLBufferAllocationParams *params;
200     GstMapInfo pbo_map;
201 
202     buf_allocator =
203         GST_GL_BASE_MEMORY_ALLOCATOR (gst_allocator_find
204         (GST_GL_BUFFER_ALLOCATOR_NAME));
205     params =
206         gst_gl_buffer_allocation_params_new (context,
207         GST_MEMORY_CAST (gl_mem)->size, &alloc_params, GL_PIXEL_UNPACK_BUFFER,
208         GL_STREAM_DRAW);
209 
210     /* FIXME: lazy init this for resource constrained platforms
211      * Will need to fix pbo detection based on the existence of the mem.id then */
212     gl_mem->pbo = (GstGLBuffer *) gst_gl_base_memory_alloc (buf_allocator,
213         (GstGLAllocationParams *) params);
214 
215     gst_object_unref (buf_allocator);
216 
217     /* if we are wrapping an existing data pointer, and glbuffer is using
218      * GL_EXT/ARB_buffer_storage or OpenGL 4.4 then we need to copy into the GL
219      * provided data pointer.
220      */
221     if (GST_MEMORY_FLAG_IS_SET (gl_mem,
222             GST_GL_BASE_MEMORY_TRANSFER_NEED_UPLOAD)) {
223       GST_MINI_OBJECT_FLAG_SET (gl_mem->pbo,
224           GST_GL_BASE_MEMORY_TRANSFER_NEED_UPLOAD);
225       gl_mem->pbo->mem.data = params->parent.wrapped_data;
226 
227       if (!gst_memory_map ((GstMemory *) gl_mem->pbo, &pbo_map,
228               GST_MAP_READWRITE)) {
229         gst_gl_allocation_params_free ((GstGLAllocationParams *) params);
230         GST_CAT_WARNING (GST_CAT_GL_MEMORY, "Failed to map pbo memory");
231         return FALSE;
232       }
233 
234       if (pbo_map.data != params->parent.wrapped_data) {
235         memcpy (pbo_map.data, gl_mem->mem.mem.data,
236             GST_MEMORY_CAST (gl_mem)->size);
237       }
238 
239       gst_memory_unmap ((GstMemory *) gl_mem->pbo, &pbo_map);
240     }
241     gst_gl_allocation_params_free ((GstGLAllocationParams *) params);
242 
243     GST_CAT_LOG (GST_CAT_GL_MEMORY, "generated pbo %u", gl_mem->pbo->id);
244   }
245 
246   return TRUE;
247 }
248 
249 static gboolean
_read_pixels_to_pbo(GstGLMemoryPBO * gl_mem)250 _read_pixels_to_pbo (GstGLMemoryPBO * gl_mem)
251 {
252   if (!gl_mem->pbo || !CONTEXT_SUPPORTS_PBO_DOWNLOAD (gl_mem->mem.mem.context)
253       || gl_mem->mem.tex_format == GST_GL_LUMINANCE
254       || gl_mem->mem.tex_format == GST_GL_LUMINANCE_ALPHA)
255     /* unsupported */
256     return FALSE;
257 
258   if (GST_MEMORY_FLAG_IS_SET (gl_mem,
259           GST_GL_BASE_MEMORY_TRANSFER_NEED_DOWNLOAD)) {
260     /* copy texture data into into the pbo and map that */
261     gsize plane_start;
262     GstMapInfo pbo_info;
263 
264     plane_start =
265         gst_gl_get_plane_start (&gl_mem->mem.info, &gl_mem->mem.valign,
266         gl_mem->mem.plane) + GST_MEMORY_CAST (gl_mem)->offset;
267 
268     gl_mem->pbo->target = GL_PIXEL_PACK_BUFFER;
269     if (!gst_memory_map (GST_MEMORY_CAST (gl_mem->pbo), &pbo_info,
270             GST_MAP_WRITE | GST_MAP_GL)) {
271       GST_CAT_ERROR (GST_CAT_GL_MEMORY, "Failed to map pbo for writing");
272       return FALSE;
273     }
274 
275     if (!gst_gl_memory_read_pixels ((GstGLMemory *) gl_mem,
276             (gpointer) plane_start)) {
277       gst_memory_unmap (GST_MEMORY_CAST (gl_mem->pbo), &pbo_info);
278       return FALSE;
279     }
280 
281     gst_memory_unmap (GST_MEMORY_CAST (gl_mem->pbo), &pbo_info);
282   }
283 
284   return TRUE;
285 }
286 
287 static gpointer
_pbo_download_transfer(GstGLMemoryPBO * gl_mem,GstMapInfo * info,gsize size)288 _pbo_download_transfer (GstGLMemoryPBO * gl_mem, GstMapInfo * info, gsize size)
289 {
290   GstMapInfo *pbo_info;
291 
292   gl_mem->pbo->target = GL_PIXEL_PACK_BUFFER;
293   /* texture -> pbo */
294   if (info->flags & GST_MAP_READ
295       && GST_MEMORY_FLAG_IS_SET (gl_mem,
296           GST_GL_BASE_MEMORY_TRANSFER_NEED_DOWNLOAD)) {
297     GstMapInfo info;
298 
299     GST_CAT_TRACE (GST_CAT_GL_MEMORY,
300         "attempting download of texture %u " "using pbo %u", gl_mem->mem.tex_id,
301         gl_mem->pbo->id);
302 
303     if (!gst_memory_map (GST_MEMORY_CAST (gl_mem->pbo), &info,
304             GST_MAP_WRITE | GST_MAP_GL)) {
305       GST_CAT_WARNING (GST_CAT_GL_MEMORY, "Failed to write to PBO");
306       return NULL;
307     }
308 
309     if (!_read_pixels_to_pbo (gl_mem)) {
310       gst_memory_unmap (GST_MEMORY_CAST (gl_mem->pbo), &info);
311       return NULL;
312     }
313 
314     gst_memory_unmap (GST_MEMORY_CAST (gl_mem->pbo), &info);
315   }
316 
317   pbo_info = g_new0 (GstMapInfo, 1);
318 
319   /* pbo -> data */
320   /* get a cpu accessible mapping from the pbo */
321   if (!gst_memory_map (GST_MEMORY_CAST (gl_mem->pbo), pbo_info, info->flags)) {
322     GST_CAT_ERROR (GST_CAT_GL_MEMORY, "Failed to map pbo");
323     g_free (pbo_info);
324     return NULL;
325   }
326   info->user_data[0] = pbo_info;
327 
328   return pbo_info->data;
329 }
330 
331 static gpointer
_gl_mem_map_cpu_access(GstGLMemoryPBO * gl_mem,GstMapInfo * info,gsize size)332 _gl_mem_map_cpu_access (GstGLMemoryPBO * gl_mem, GstMapInfo * info, gsize size)
333 {
334   gpointer data = NULL;
335 
336   gst_gl_base_memory_alloc_data ((GstGLBaseMemory *) gl_mem);
337 
338   if (!data && gl_mem->pbo
339       && CONTEXT_SUPPORTS_PBO_DOWNLOAD (gl_mem->mem.mem.context))
340     data = _pbo_download_transfer (gl_mem, info, size);
341 
342   if (!data) {
343     GstGLMemoryAllocatorClass *alloc_class;
344 
345     alloc_class = GST_GL_MEMORY_ALLOCATOR_CLASS (parent_class);
346 
347     data = alloc_class->map ((GstGLBaseMemory *) gl_mem, info, size);
348   }
349 
350   return data;
351 }
352 
353 static gpointer
_gl_mem_map_gpu_access(GstGLMemoryPBO * gl_mem,GstMapInfo * info,gsize size)354 _gl_mem_map_gpu_access (GstGLMemoryPBO * gl_mem, GstMapInfo * info, gsize size)
355 {
356   gpointer data = &gl_mem->mem.tex_id;
357 
358   if ((info->flags & GST_MAP_READ) == GST_MAP_READ) {
359     if (gl_mem->pbo && CONTEXT_SUPPORTS_PBO_UPLOAD (gl_mem->mem.mem.context)) {
360       GstMapInfo pbo_info;
361 
362       /* data -> pbo */
363       if (!gst_memory_map (GST_MEMORY_CAST (gl_mem->pbo), &pbo_info,
364               GST_MAP_READ | GST_MAP_GL)) {
365         GST_CAT_ERROR (GST_CAT_GL_MEMORY, "Failed to map pbo");
366         return NULL;
367       }
368 
369       /* pbo -> texture */
370       _upload_pbo_memory (gl_mem, info, gl_mem->pbo, &pbo_info);
371 
372       gst_memory_unmap (GST_MEMORY_CAST (gl_mem->pbo), &pbo_info);
373     } else {
374       GstGLMemoryAllocatorClass *alloc_class;
375 
376       alloc_class = GST_GL_MEMORY_ALLOCATOR_CLASS (parent_class);
377 
378       data = alloc_class->map ((GstGLBaseMemory *) gl_mem, info, size);
379     }
380   }
381 
382   return data;
383 }
384 
385 static gpointer
_gl_mem_map(GstGLMemoryPBO * gl_mem,GstMapInfo * info,gsize maxsize)386 _gl_mem_map (GstGLMemoryPBO * gl_mem, GstMapInfo * info, gsize maxsize)
387 {
388   gpointer data;
389 
390   if ((info->flags & GST_MAP_GL) == GST_MAP_GL) {
391     if (gl_mem->mem.tex_target == GST_GL_TEXTURE_TARGET_EXTERNAL_OES)
392       return &gl_mem->mem.tex_id;
393 
394     data = _gl_mem_map_gpu_access (gl_mem, info, maxsize);
395   } else {                      /* not GL */
396     if (gl_mem->mem.tex_target == GST_GL_TEXTURE_TARGET_EXTERNAL_OES) {
397       GST_CAT_ERROR (GST_CAT_GL_MEMORY, "Cannot map External OES textures");
398       return NULL;
399     }
400 
401     data = _gl_mem_map_cpu_access (gl_mem, info, maxsize);
402   }
403 
404   return data;
405 }
406 
407 static void
_gl_mem_unmap_cpu_access(GstGLMemoryPBO * gl_mem,GstMapInfo * info)408 _gl_mem_unmap_cpu_access (GstGLMemoryPBO * gl_mem, GstMapInfo * info)
409 {
410   if (!gl_mem->pbo || !CONTEXT_SUPPORTS_PBO_DOWNLOAD (gl_mem->mem.mem.context))
411     /* PBO's not supported */
412     return;
413 
414   gl_mem->pbo->target = GL_PIXEL_PACK_BUFFER;
415   gst_memory_unmap (GST_MEMORY_CAST (gl_mem->pbo),
416       (GstMapInfo *) info->user_data[0]);
417   g_free (info->user_data[0]);
418 }
419 
420 static void
_gl_mem_unmap(GstGLMemoryPBO * gl_mem,GstMapInfo * info)421 _gl_mem_unmap (GstGLMemoryPBO * gl_mem, GstMapInfo * info)
422 {
423   if ((info->flags & GST_MAP_GL) == 0) {
424     _gl_mem_unmap_cpu_access (gl_mem, info);
425   }
426 }
427 
428 static void
_gl_mem_copy_thread(GstGLContext * context,gpointer data)429 _gl_mem_copy_thread (GstGLContext * context, gpointer data)
430 {
431   const GstGLFuncs *gl;
432   GstGLMemoryPBOCopyParams *copy_params;
433   GstGLMemoryPBO *src;
434   guint tex_id;
435   guint out_tex_target;
436   GLuint fboId;
437   gsize out_width, out_height, out_stride;
438   GstGLFormat out_gl_format, in_gl_format;
439   GLuint out_gl_type, in_gl_type;
440   gsize in_size, out_size;
441 
442   copy_params = (GstGLMemoryPBOCopyParams *) data;
443   src = copy_params->src;
444   tex_id = copy_params->tex_id;
445   out_tex_target = gst_gl_texture_target_to_gl (copy_params->tex_target);
446   out_width = copy_params->out_width;
447   out_height = copy_params->out_height;
448   out_stride = copy_params->out_stride;
449 
450   gl = context->gl_vtable;
451 
452   gst_gl_format_type_from_sized_gl_format (copy_params->out_format,
453       &out_gl_format, &out_gl_type);
454   gst_gl_format_type_from_sized_gl_format (src->mem.tex_format, &in_gl_format,
455       &in_gl_type);
456 
457   if (!gl->GenFramebuffers) {
458     GST_CAT_ERROR (GST_CAT_GL_MEMORY,
459         "Context, EXT_framebuffer_object not supported");
460     goto error;
461   }
462 
463   in_size = GL_MEM_HEIGHT (src) * GL_MEM_STRIDE (src);
464   out_size = out_height * out_stride;
465 
466   if (copy_params->respecify) {
467     if (in_size != out_size) {
468       GST_CAT_ERROR (GST_CAT_GL_MEMORY, "Cannot copy between textures with "
469           "backing data of different sizes. input %" G_GSIZE_FORMAT " output %"
470           G_GSIZE_FORMAT, in_size, out_size);
471       goto error;
472     }
473   }
474 
475   if (!tex_id) {
476     tex_id =
477         _new_texture (context, out_tex_target,
478         copy_params->out_format, out_gl_format, out_gl_type,
479         copy_params->out_width, copy_params->out_height);
480   }
481 
482   if (!tex_id) {
483     GST_WARNING ("Could not create GL texture with context:%p", context);
484   }
485 
486   GST_LOG ("copying memory %p, tex %u into texture %i",
487       src, src->mem.tex_id, tex_id);
488 
489   /* FIXME: try and avoid creating and destroying fbo's every copy... */
490   /* create a framebuffer object */
491   gl->GenFramebuffers (1, &fboId);
492   gl->BindFramebuffer (GL_FRAMEBUFFER, fboId);
493 
494   gl->FramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
495       gst_gl_texture_target_to_gl (src->mem.tex_target), src->mem.tex_id, 0);
496 
497 //  if (!gst_gl_context_check_framebuffer_status (src->mem.mem.context))
498 //    goto fbo_error;
499 
500   gl->BindTexture (out_tex_target, tex_id);
501   if (copy_params->respecify) {
502     GstMapInfo pbo_info;
503 
504     if (!gl->GenBuffers || !src->pbo) {
505       GST_CAT_ERROR (GST_CAT_GL_MEMORY, "Cannot reinterpret texture contents "
506           "without pixel buffer objects");
507       gl->BindTexture (out_tex_target, 0);
508       goto fbo_error;
509     }
510 
511     if (gst_gl_context_get_gl_api (context) & GST_GL_API_GLES2
512         && (in_gl_format != GL_RGBA || in_gl_type != GL_UNSIGNED_BYTE)) {
513       GST_CAT_ERROR (GST_CAT_GL_MEMORY, "Cannot copy non RGBA/UNSIGNED_BYTE "
514           "textures on GLES2");
515       gl->BindTexture (out_tex_target, 0);
516       goto fbo_error;
517     }
518 
519     GST_TRACE ("copying texture data with size of %u*%u*%u",
520         gst_gl_format_type_n_bytes (in_gl_format, in_gl_type),
521         src->mem.tex_width, GL_MEM_HEIGHT (src));
522 
523     /* copy tex */
524     _read_pixels_to_pbo (src);
525 
526     src->pbo->target = GL_PIXEL_UNPACK_BUFFER;
527     if (!gst_memory_map (GST_MEMORY_CAST (src->pbo), &pbo_info,
528             GST_MAP_READ | GST_MAP_GL)) {
529       GST_CAT_ERROR (GST_CAT_GL_MEMORY, "Failed to map pbo for reading");
530       goto fbo_error;
531     }
532     gl->TexSubImage2D (out_tex_target, 0, 0, 0, out_width, out_height,
533         out_gl_format, out_gl_type, 0);
534     gst_memory_unmap (GST_MEMORY_CAST (src->pbo), &pbo_info);
535   } else {                      /* different sizes */
536     gst_gl_memory_copy_teximage (GST_GL_MEMORY_CAST (src),
537         tex_id, copy_params->tex_target, copy_params->out_format, out_width,
538         out_height);
539   }
540 
541   gl->BindTexture (out_tex_target, 0);
542   gl->BindFramebuffer (GL_FRAMEBUFFER, 0);
543 
544   gl->DeleteFramebuffers (1, &fboId);
545 
546   copy_params->tex_id = tex_id;
547   copy_params->result = TRUE;
548 
549   return;
550 
551 /* ERRORS */
552 fbo_error:
553   {
554     gl->DeleteFramebuffers (1, &fboId);
555 
556     copy_params->tex_id = 0;
557     copy_params->result = FALSE;
558     return;
559   }
560 
561 error:
562   {
563     copy_params->result = FALSE;
564     return;
565   }
566 }
567 
568 static GstMemory *
_gl_mem_copy(GstGLMemoryPBO * src,gssize offset,gssize size)569 _gl_mem_copy (GstGLMemoryPBO * src, gssize offset, gssize size)
570 {
571   GstAllocationParams params = { 0, GST_MEMORY_CAST (src)->align, 0, 0 };
572   GstGLBaseMemoryAllocator *base_mem_allocator;
573   GstAllocator *allocator;
574   GstMemory *dest = NULL;
575 
576   allocator = GST_MEMORY_CAST (src)->allocator;
577   base_mem_allocator = (GstGLBaseMemoryAllocator *) allocator;
578 
579   if (src->mem.tex_target == GST_GL_TEXTURE_TARGET_EXTERNAL_OES) {
580     GST_CAT_ERROR (GST_CAT_GL_MEMORY, "Cannot copy External OES textures");
581     return NULL;
582   }
583 
584   /* If not doing a full copy, then copy to sysmem, the 2D represention of the
585    * texture would become wrong */
586   if (offset > 0 || size < GST_MEMORY_CAST (src)->size) {
587     return base_mem_allocator->fallback_mem_copy (GST_MEMORY_CAST (src), offset,
588         size);
589   }
590 
591   dest = (GstMemory *) g_new0 (GstGLMemoryPBO, 1);
592   gst_gl_memory_init (GST_GL_MEMORY_CAST (dest), allocator, NULL,
593       src->mem.mem.context, src->mem.tex_target, src->mem.tex_format, &params,
594       &src->mem.info, src->mem.plane, &src->mem.valign, NULL, NULL);
595 
596   if (!GST_MEMORY_FLAG_IS_SET (src, GST_GL_BASE_MEMORY_TRANSFER_NEED_UPLOAD)) {
597     GstMapInfo dinfo;
598 
599     if (!gst_memory_map (GST_MEMORY_CAST (dest), &dinfo,
600             GST_MAP_WRITE | GST_MAP_GL)) {
601       GST_CAT_WARNING (GST_CAT_GL_MEMORY,
602           "Failed not map destination " "for writing");
603       gst_memory_unref (GST_MEMORY_CAST (dest));
604       return NULL;
605     }
606 
607     if (!gst_gl_memory_copy_into ((GstGLMemory *) src,
608             ((GstGLMemory *) dest)->tex_id, src->mem.tex_target,
609             src->mem.tex_format, src->mem.tex_width, GL_MEM_HEIGHT (src))) {
610       GST_CAT_WARNING (GST_CAT_GL_MEMORY, "Could not copy GL Memory");
611       gst_memory_unmap (GST_MEMORY_CAST (dest), &dinfo);
612       goto memcpy;
613     }
614 
615     gst_memory_unmap (GST_MEMORY_CAST (dest), &dinfo);
616   } else {
617   memcpy:
618     if (!gst_gl_base_memory_memcpy ((GstGLBaseMemory *) src,
619             (GstGLBaseMemory *) dest, offset, size)) {
620       GST_CAT_WARNING (GST_CAT_GL_MEMORY, "Could not copy GL Memory");
621       gst_memory_unref (GST_MEMORY_CAST (dest));
622       return NULL;
623     }
624   }
625 
626   return dest;
627 }
628 
629 static GstMemory *
_gl_mem_alloc(GstAllocator * allocator,gsize size,GstAllocationParams * params)630 _gl_mem_alloc (GstAllocator * allocator, gsize size,
631     GstAllocationParams * params)
632 {
633   g_warning ("Use gst_gl_base_memory_alloc () to allocate from this "
634       "GstGLMemoryPBO allocator");
635 
636   return NULL;
637 }
638 
639 static void
_gl_mem_destroy(GstGLMemoryPBO * gl_mem)640 _gl_mem_destroy (GstGLMemoryPBO * gl_mem)
641 {
642   if (gl_mem->pbo)
643     gst_memory_unref (GST_MEMORY_CAST (gl_mem->pbo));
644   gl_mem->pbo = NULL;
645 
646   GST_GL_BASE_MEMORY_ALLOCATOR_CLASS (parent_class)->destroy ((GstGLBaseMemory
647           *) gl_mem);
648 }
649 
650 static GstGLMemoryPBO *
_gl_mem_pbo_alloc(GstGLBaseMemoryAllocator * allocator,GstGLVideoAllocationParams * params)651 _gl_mem_pbo_alloc (GstGLBaseMemoryAllocator * allocator,
652     GstGLVideoAllocationParams * params)
653 {
654   GstGLMemoryPBO *mem;
655   GstAllocationParams alloc_params = { 0, };
656   guint alloc_flags;
657 
658   alloc_flags = params->parent.alloc_flags;
659   if (params->parent.alloc_params)
660     alloc_params = *params->parent.alloc_params;
661 
662   g_return_val_if_fail (alloc_flags & GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_VIDEO,
663       NULL);
664 
665   mem = g_new0 (GstGLMemoryPBO, 1);
666 
667   if (alloc_flags & GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_WRAP_GPU_HANDLE) {
668     mem->mem.tex_id = GPOINTER_TO_UINT (params->parent.gl_handle);
669     mem->mem.texture_wrapped = TRUE;
670     alloc_params.flags |= GST_GL_BASE_MEMORY_TRANSFER_NEED_DOWNLOAD;
671   }
672   if (alloc_flags & GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_WRAP_SYSMEM) {
673     alloc_params.flags |= GST_GL_BASE_MEMORY_TRANSFER_NEED_UPLOAD;
674     mem->mem.mem.data = params->parent.wrapped_data;
675   }
676 
677   gst_gl_memory_init (GST_GL_MEMORY_CAST (mem), GST_ALLOCATOR_CAST (allocator),
678       NULL, params->parent.context, params->target, params->tex_format,
679       &alloc_params, params->v_info, params->plane, params->valign,
680       params->parent.user_data, params->parent.notify);
681 
682   if (alloc_flags & GST_GL_ALLOCATION_PARAMS_ALLOC_FLAG_WRAP_SYSMEM) {
683     mem->mem.mem.data = params->parent.wrapped_data;
684   }
685 
686   return mem;
687 }
688 
689 static void
gst_gl_memory_pbo_allocator_class_init(GstGLMemoryPBOAllocatorClass * klass)690 gst_gl_memory_pbo_allocator_class_init (GstGLMemoryPBOAllocatorClass * klass)
691 {
692   GstGLBaseMemoryAllocatorClass *gl_base;
693   GstGLMemoryAllocatorClass *gl_tex;
694   GstAllocatorClass *allocator_class;
695 
696   gl_tex = (GstGLMemoryAllocatorClass *) klass;
697   gl_base = (GstGLBaseMemoryAllocatorClass *) klass;
698   allocator_class = (GstAllocatorClass *) klass;
699 
700   gl_base->alloc = (GstGLBaseMemoryAllocatorAllocFunction) _gl_mem_pbo_alloc;
701   gl_base->create = (GstGLBaseMemoryAllocatorCreateFunction) _gl_mem_create;
702   gl_tex->map = (GstGLBaseMemoryAllocatorMapFunction) _gl_mem_map;
703   gl_tex->unmap = (GstGLBaseMemoryAllocatorUnmapFunction) _gl_mem_unmap;
704   gl_tex->copy = (GstGLBaseMemoryAllocatorCopyFunction) _gl_mem_copy;
705   gl_base->destroy = (GstGLBaseMemoryAllocatorDestroyFunction) _gl_mem_destroy;
706 
707   allocator_class->alloc = _gl_mem_alloc;
708 }
709 
710 static void
gst_gl_memory_pbo_allocator_init(GstGLMemoryPBOAllocator * allocator)711 gst_gl_memory_pbo_allocator_init (GstGLMemoryPBOAllocator * allocator)
712 {
713   GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
714 
715   alloc->mem_type = GST_GL_MEMORY_PBO_ALLOCATOR_NAME;
716 
717   GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC);
718 }
719 
720 /**
721  * gst_gl_memory_pbo_copy_into_texture:
722  * @gl_mem:a #GstGLMemoryPBO
723  * @tex_id: the destination texture id
724  * @target: the destination #GstGLTextureTarget
725  * @tex_format: the destination #GstGLFormat
726  * @width: width of @tex_id
727  * @height: height of @tex_id
728  * @stride: stride of the backing texture data
729  * @respecify: whether to copy the data or copy per texel
730  *
731  * Copies @gl_mem into the texture specified by @tex_id.  The format of @tex_id
732  * is specified by @tex_format, @width and @height.
733  *
734  * If @respecify is %TRUE, then the copy is performed in terms of the texture
735  * data.  This is useful for splitting RGBA textures into RG or R textures or
736  * vice versa. The requirement for this to succeed is that the backing texture
737  * data must be the same size, i.e. say a RGBA8 texture is converted into a RG8
738  * texture, then the RG texture must have twice as many pixels available for
739  * output as the RGBA texture.
740  *
741  * Otherwise, if @respecify is %FALSE, then the copy is performed per texel
742  * using glCopyTexImage.  See the OpenGL specification for details on the
743  * mappings between texture formats.
744  *
745  * Returns: Whether the copy succeeded
746  *
747  * Since: 1.8
748  */
749 gboolean
gst_gl_memory_pbo_copy_into_texture(GstGLMemoryPBO * gl_mem,guint tex_id,GstGLTextureTarget target,GstGLFormat tex_format,gint width,gint height,gint stride,gboolean respecify)750 gst_gl_memory_pbo_copy_into_texture (GstGLMemoryPBO * gl_mem, guint tex_id,
751     GstGLTextureTarget target, GstGLFormat tex_format, gint width,
752     gint height, gint stride, gboolean respecify)
753 {
754   GstGLMemoryPBOCopyParams copy_params;
755 
756   copy_params.src = gl_mem;
757   copy_params.tex_target = target;
758   copy_params.tex_id = tex_id;
759   copy_params.out_format = tex_format;
760   copy_params.out_width = width;
761   copy_params.out_height = height;
762   copy_params.out_stride = stride;
763   copy_params.respecify = respecify;
764 
765   gst_gl_context_thread_add (gl_mem->mem.mem.context, _gl_mem_copy_thread,
766       &copy_params);
767 
768   return copy_params.result;
769 }
770 
771 static void
_download_transfer(GstGLContext * context,GstGLMemoryPBO * gl_mem)772 _download_transfer (GstGLContext * context, GstGLMemoryPBO * gl_mem)
773 {
774   GstGLBaseMemory *mem = (GstGLBaseMemory *) gl_mem;
775 
776   g_mutex_lock (&mem->lock);
777   if (_read_pixels_to_pbo (gl_mem)) {
778     GST_CAT_TRACE (GST_CAT_GL_MEMORY, "optimistic download of texture %u "
779         "using pbo %u", gl_mem->mem.tex_id, gl_mem->pbo->id);
780     GST_MEMORY_FLAG_UNSET (gl_mem, GST_GL_BASE_MEMORY_TRANSFER_NEED_DOWNLOAD);
781   }
782   g_mutex_unlock (&mem->lock);
783 }
784 
785 /**
786  * gst_gl_memory_pbo_download_transfer:
787  * @gl_mem: a #GstGLMemoryPBO
788  *
789  * Transfer the texture data from the texture into the PBO if necessary.
790  *
791  * Since: 1.8
792  */
793 void
gst_gl_memory_pbo_download_transfer(GstGLMemoryPBO * gl_mem)794 gst_gl_memory_pbo_download_transfer (GstGLMemoryPBO * gl_mem)
795 {
796   g_return_if_fail (gst_is_gl_memory ((GstMemory *) gl_mem));
797 
798   gst_gl_context_thread_add (gl_mem->mem.mem.context,
799       (GstGLContextThreadFunc) _download_transfer, gl_mem);
800 }
801 
802 static void
_upload_transfer(GstGLContext * context,GstGLMemoryPBO * gl_mem)803 _upload_transfer (GstGLContext * context, GstGLMemoryPBO * gl_mem)
804 {
805   GstGLBaseMemory *mem = (GstGLBaseMemory *) gl_mem;
806   GstMapInfo info;
807 
808   g_mutex_lock (&mem->lock);
809   gl_mem->pbo->target = GL_PIXEL_UNPACK_BUFFER;
810   if (!gst_memory_map (GST_MEMORY_CAST (gl_mem->pbo), &info,
811           GST_MAP_READ | GST_MAP_GL)) {
812     GST_CAT_WARNING (GST_CAT_GL_MEMORY, "Failed to map pbo for reading");
813   } else {
814     gst_memory_unmap (GST_MEMORY_CAST (gl_mem->pbo), &info);
815   }
816   g_mutex_unlock (&mem->lock);
817 }
818 
819 /**
820  * gst_gl_memory_pbo_upload_transfer:
821  * @gl_mem: a #GstGLMemoryPBO
822  *
823  * Transfer the texture data from the PBO into the texture if necessary.
824  *
825  * Since: 1.8
826  */
827 void
gst_gl_memory_pbo_upload_transfer(GstGLMemoryPBO * gl_mem)828 gst_gl_memory_pbo_upload_transfer (GstGLMemoryPBO * gl_mem)
829 {
830   g_return_if_fail (gst_is_gl_memory ((GstMemory *) gl_mem));
831 
832   if (gl_mem->pbo && CONTEXT_SUPPORTS_PBO_UPLOAD (gl_mem->mem.mem.context))
833     gst_gl_context_thread_add (gl_mem->mem.mem.context,
834         (GstGLContextThreadFunc) _upload_transfer, gl_mem);
835 }
836 
837 /**
838  * gst_gl_memory_pbo_init:
839  *
840  * Initializes the GL Memory allocator. It is safe to call this function
841  * multiple times.  This must be called before any other GstGLMemoryPBO operation.
842  */
843 void
gst_gl_memory_pbo_init_once(void)844 gst_gl_memory_pbo_init_once (void)
845 {
846   static gsize _init = 0;
847 
848   if (g_once_init_enter (&_init)) {
849     gst_gl_memory_init_once ();
850 
851     GST_DEBUG_CATEGORY_INIT (GST_CAT_GL_MEMORY, "glmemory", 0, "OpenGL Memory");
852 
853     _gl_allocator = g_object_new (GST_TYPE_GL_MEMORY_PBO_ALLOCATOR, NULL);
854     gst_object_ref_sink (_gl_allocator);
855     /* The allocator is never unreffed */
856     GST_OBJECT_FLAG_SET (_gl_allocator, GST_OBJECT_FLAG_MAY_BE_LEAKED);
857 
858     gst_allocator_register (GST_GL_MEMORY_PBO_ALLOCATOR_NAME,
859         gst_object_ref (_gl_allocator));
860     g_once_init_leave (&_init, 1);
861   }
862 }
863 
864 /**
865  * gst_is_gl_memory_pbo:
866  * @mem:a #GstMemory
867  *
868  * Returns: whether the memory at @mem is a #GstGLMemoryPBO
869  *
870  * Since: 1.8
871  */
872 gboolean
gst_is_gl_memory_pbo(GstMemory * mem)873 gst_is_gl_memory_pbo (GstMemory * mem)
874 {
875   return mem != NULL && mem->allocator != NULL
876       && g_type_is_a (G_OBJECT_TYPE (mem->allocator),
877       GST_TYPE_GL_MEMORY_PBO_ALLOCATOR);
878 }
879