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