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