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, ¶ms,
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 ©_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