1 /*
2 * GStreamer
3 * Copyright (C) 2013 Matthew Waters <ystreet00@gmail.com>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 /**
22 * SECTION:gstglframebuffer
23 * @short_description: OpenGL framebuffer abstraction
24 * @title: GstGLFramebuffer
25 * @see_also: #GstGLBaseMemory, #GstGLMemory, #GstGLContext
26 *
27 * A #GstGLFramebuffer represents and holds an OpenGL framebuffer object with
28 * it's associated attachments.
29 *
30 * A #GstGLFramebuffer can be created with gst_gl_framebuffer_new() or
31 * gst_gl_framebuffer_new_with_default_depth() and bound with
32 * gst_gl_framebuffer_bind(). Other resources can be bound with
33 * gst_gl_framebuffer_attach()
34 *
35 * Note: OpenGL framebuffers are not shareable resources so cannot be used
36 * between multiple OpenGL contexts.
37 *
38 * Since: 1.10
39 */
40
41 #ifdef HAVE_CONFIG_H
42 #include "config.h"
43 #endif
44
45 #include "gstglframebuffer.h"
46
47 #include "gstglcontext.h"
48 #include "gstglcontext_private.h"
49 #include "gstglfuncs.h"
50 #include "gstglmemory.h"
51 #include "gstglrenderbuffer.h"
52
53 #ifndef GL_FRAMEBUFFER_UNDEFINED
54 #define GL_FRAMEBUFFER_UNDEFINED 0x8219
55 #endif
56 #ifndef GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT
57 #define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6
58 #endif
59 #ifndef GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT
60 #define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7
61 #endif
62 #ifndef GL_FRAMEBUFFER_UNSUPPORTED
63 #define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD
64 #endif
65 #ifndef GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS
66 #define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS 0x8CD9
67 #endif
68
69 #ifndef GL_DEPTH_STENCIL_ATTACHMENT
70 #define GL_DEPTH_STENCIL_ATTACHMENT 0x821A
71 #endif
72
73 #ifndef GL_READ_FRAMEBUFFER
74 #define GL_READ_FRAMEBUFFER 0x8CA8
75 #endif
76 #ifndef GL_DRAW_FRAMEBUFFER
77 #define GL_DRAW_FRAMEBUFFER 0x8CA9
78 #endif
79
80 GST_DEBUG_CATEGORY_STATIC (gst_gl_framebuffer_debug);
81 #define GST_CAT_DEFAULT gst_gl_framebuffer_debug
82
83 static void gst_gl_framebuffer_finalize (GObject * object);
84
85 struct _GstGLFramebufferPrivate
86 {
87 guint effective_width;
88 guint effective_height;
89 };
90
91 #define DEBUG_INIT \
92 GST_DEBUG_CATEGORY_INIT (gst_gl_framebuffer_debug, "glframebuffer", 0, "GL Framebuffer");
93
94 G_DEFINE_TYPE_WITH_CODE (GstGLFramebuffer, gst_gl_framebuffer, GST_TYPE_OBJECT,
95 G_ADD_PRIVATE (GstGLFramebuffer) DEBUG_INIT);
96
97 struct fbo_attachment
98 {
99 guint attachment_point;
100 GstGLBaseMemory *mem;
101 };
102
103 static void
_fbo_attachment_init(struct fbo_attachment * attach,guint point,GstGLBaseMemory * mem)104 _fbo_attachment_init (struct fbo_attachment *attach, guint point,
105 GstGLBaseMemory * mem)
106 {
107 attach->attachment_point = point;
108 attach->mem = (GstGLBaseMemory *) gst_memory_ref (GST_MEMORY_CAST (mem));
109 }
110
111 static void
_fbo_attachment_unset(struct fbo_attachment * attach)112 _fbo_attachment_unset (struct fbo_attachment *attach)
113 {
114 if (!attach)
115 return;
116
117 if (attach->mem)
118 gst_memory_unref (GST_MEMORY_CAST (attach->mem));
119 attach->mem = NULL;
120 }
121
122 static void
gst_gl_framebuffer_class_init(GstGLFramebufferClass * klass)123 gst_gl_framebuffer_class_init (GstGLFramebufferClass * klass)
124 {
125 G_OBJECT_CLASS (klass)->finalize = gst_gl_framebuffer_finalize;
126 }
127
128 static void
gst_gl_framebuffer_init(GstGLFramebuffer * fb)129 gst_gl_framebuffer_init (GstGLFramebuffer * fb)
130 {
131 fb->priv = gst_gl_framebuffer_get_instance_private (fb);
132
133 fb->attachments =
134 g_array_new (FALSE, FALSE, (sizeof (struct fbo_attachment)));
135 g_array_set_clear_func (fb->attachments,
136 (GDestroyNotify) _fbo_attachment_unset);
137 }
138
139 static void
_delete_fbo_gl(GstGLContext * context,GstGLFramebuffer * fb)140 _delete_fbo_gl (GstGLContext * context, GstGLFramebuffer * fb)
141 {
142 const GstGLFuncs *gl = context->gl_vtable;
143
144 if (fb->fbo_id)
145 gl->DeleteFramebuffers (1, &fb->fbo_id);
146 fb->fbo_id = 0;
147 }
148
149 static void
gst_gl_framebuffer_finalize(GObject * object)150 gst_gl_framebuffer_finalize (GObject * object)
151 {
152 GstGLFramebuffer *fb = GST_GL_FRAMEBUFFER (object);
153
154 if (fb->context) {
155 if (fb->fbo_id)
156 gst_gl_context_thread_add (fb->context,
157 (GstGLContextThreadFunc) _delete_fbo_gl, fb);
158
159 gst_object_unref (fb->context);
160 fb->context = NULL;
161 }
162
163 if (fb->attachments)
164 g_array_free (fb->attachments, TRUE);
165 fb->attachments = NULL;
166
167 G_OBJECT_CLASS (gst_gl_framebuffer_parent_class)->finalize (object);
168 }
169
170 /**
171 * gst_gl_framebuffer_new:
172 * @context: a #GstGLContext
173 *
174 * Returns: (transfer full): a new #GstGLFramebuffer
175 *
176 * Since: 1.10
177 */
178 GstGLFramebuffer *
gst_gl_framebuffer_new(GstGLContext * context)179 gst_gl_framebuffer_new (GstGLContext * context)
180 {
181 GstGLFramebuffer *fb;
182 const GstGLFuncs *gl;
183
184 g_return_val_if_fail (GST_IS_GL_CONTEXT (context), NULL);
185 g_return_val_if_fail (gst_gl_context_get_current () == context, NULL);
186
187 gl = context->gl_vtable;
188
189 if (!gl->GenFramebuffers) {
190 GST_ERROR_OBJECT (context, "Framebuffers are not supported!");
191 return NULL;
192 }
193
194 fb = g_object_new (GST_TYPE_GL_FRAMEBUFFER, NULL);
195 fb->context = gst_object_ref (context);
196 gl->GenFramebuffers (1, &fb->fbo_id);
197 gst_object_ref_sink (fb);
198
199 return fb;
200 }
201
202 /**
203 * gst_gl_framebuffer_new_with_default_depth:
204 * @context: a #GstGLContext
205 * @width: width for the depth buffer
206 * @height: for the depth buffer
207 *
208 * Returns: a new #GstGLFramebuffer with a depth buffer of @width and @height
209 *
210 * Since: 1.10
211 */
212 GstGLFramebuffer *
gst_gl_framebuffer_new_with_default_depth(GstGLContext * context,guint width,guint height)213 gst_gl_framebuffer_new_with_default_depth (GstGLContext * context, guint width,
214 guint height)
215 {
216 GstGLFramebuffer *fb = gst_gl_framebuffer_new (context);
217 GstGLBaseMemoryAllocator *render_alloc;
218 GstGLAllocationParams *params;
219 GstGLBaseMemory *renderbuffer;
220 guint attach_point, attach_type;
221
222 if (!fb)
223 return NULL;
224
225 if (gst_gl_context_get_gl_api (fb->context) & (GST_GL_API_OPENGL |
226 GST_GL_API_OPENGL3)) {
227 attach_point = GL_DEPTH_STENCIL_ATTACHMENT;
228 attach_type = GST_GL_DEPTH24_STENCIL8;
229 } else if (gst_gl_context_get_gl_api (fb->context) & GST_GL_API_GLES2) {
230 attach_point = GL_DEPTH_ATTACHMENT;
231 attach_type = GST_GL_DEPTH_COMPONENT16;
232 } else {
233 g_assert_not_reached ();
234 return NULL;
235 }
236
237 render_alloc = (GstGLBaseMemoryAllocator *)
238 gst_allocator_find (GST_GL_RENDERBUFFER_ALLOCATOR_NAME);
239 params = (GstGLAllocationParams *)
240 gst_gl_renderbuffer_allocation_params_new (context, NULL, attach_type,
241 width, height);
242
243 renderbuffer = gst_gl_base_memory_alloc (render_alloc, params);
244 gst_gl_allocation_params_free (params);
245 gst_object_unref (render_alloc);
246
247 gst_gl_framebuffer_bind (fb);
248 gst_gl_framebuffer_attach (fb, attach_point, renderbuffer);
249 gst_gl_context_clear_framebuffer (fb->context);
250 gst_memory_unref (GST_MEMORY_CAST (renderbuffer));
251
252 return fb;
253 }
254
255 /**
256 * gst_gl_framebuffer_draw_to_texture:
257 * @fb: a #GstGLFramebuffer
258 * @mem: the #GstGLMemory to draw to
259 * @func: (scope call): the function to run
260 * @user_data: data to pass to @func
261 *
262 * Perform the steps necessary to have the output of a glDraw* command in
263 * @func update the contents of @mem.
264 *
265 * Returns: the result of executing @func
266 *
267 * Since: 1.10
268 */
269 gboolean
gst_gl_framebuffer_draw_to_texture(GstGLFramebuffer * fb,GstGLMemory * mem,GstGLFramebufferFunc func,gpointer user_data)270 gst_gl_framebuffer_draw_to_texture (GstGLFramebuffer * fb, GstGLMemory * mem,
271 GstGLFramebufferFunc func, gpointer user_data)
272 {
273 const GstGLFuncs *gl;
274 gboolean ret;
275
276 g_return_val_if_fail (GST_IS_GL_FRAMEBUFFER (fb), FALSE);
277 g_return_val_if_fail (gst_is_gl_memory (GST_MEMORY_CAST (mem)), FALSE);
278
279 gl = fb->context->gl_vtable;
280
281 GST_TRACE_OBJECT (fb, "drawing to texture %u, dimensions %ix%i", mem->tex_id,
282 gst_gl_memory_get_texture_width (mem),
283 gst_gl_memory_get_texture_height (mem));
284
285 gst_gl_framebuffer_bind (fb);
286 gst_gl_framebuffer_attach (fb, GL_COLOR_ATTACHMENT0, (GstGLBaseMemory *) mem);
287
288 gl->Viewport (0, 0, fb->priv->effective_width, fb->priv->effective_height);
289 if (gst_gl_context_get_gl_api (fb->context) & (GST_GL_API_OPENGL |
290 GST_GL_API_OPENGL3))
291 gl->DrawBuffer (GL_COLOR_ATTACHMENT0);
292
293 ret = func (user_data);
294
295 if (gst_gl_context_get_gl_api (fb->context) & (GST_GL_API_OPENGL |
296 GST_GL_API_OPENGL3))
297 gl->DrawBuffer (GL_COLOR_ATTACHMENT0);
298 gst_gl_context_clear_framebuffer (fb->context);
299
300 return ret;
301 }
302
303 /**
304 * gst_gl_framebuffer_bind:
305 * @fb: a #GstGLFramebuffer
306 *
307 * Bind @fb into the current thread
308 *
309 * Since: 1.10
310 */
311 void
gst_gl_framebuffer_bind(GstGLFramebuffer * fb)312 gst_gl_framebuffer_bind (GstGLFramebuffer * fb)
313 {
314 const GstGLFuncs *gl;
315
316 g_return_if_fail (GST_IS_GL_FRAMEBUFFER (fb));
317 g_return_if_fail (gst_gl_context_get_current () == fb->context);
318 g_return_if_fail (fb->fbo_id != 0);
319
320 gl = fb->context->gl_vtable;
321
322 gl->BindFramebuffer (GL_FRAMEBUFFER, fb->fbo_id);
323 }
324
325 /**
326 * gst_gl_context_clear_framebuffer:
327 * @context: a #GstGLContext
328 *
329 * Unbind the current framebuffer
330 *
331 * Since: 1.10
332 */
333 void
gst_gl_context_clear_framebuffer(GstGLContext * context)334 gst_gl_context_clear_framebuffer (GstGLContext * context)
335 {
336 const GstGLFuncs *gl;
337
338 g_return_if_fail (GST_IS_GL_CONTEXT (context));
339
340 gl = context->gl_vtable;
341
342 gl->BindFramebuffer (GL_FRAMEBUFFER, 0);
343 }
344
345 static void
_update_effective_dimensions(GstGLFramebuffer * fb)346 _update_effective_dimensions (GstGLFramebuffer * fb)
347 {
348 int i;
349 guint min_width = -1, min_height = -1;
350
351 /* remove the previous attachment */
352 for (i = 0; i < fb->attachments->len; i++) {
353 struct fbo_attachment *attach;
354 int width, height;
355
356 attach = &g_array_index (fb->attachments, struct fbo_attachment, i);
357
358 if (gst_is_gl_memory (GST_MEMORY_CAST (attach->mem))) {
359 GstGLMemory *mem = (GstGLMemory *) attach->mem;
360
361 width = gst_gl_memory_get_texture_width (mem);
362 height = gst_gl_memory_get_texture_height (mem);
363 } else if (gst_is_gl_renderbuffer (GST_MEMORY_CAST (attach->mem))) {
364 GstGLRenderbuffer *mem = (GstGLRenderbuffer *) attach->mem;
365
366 width = mem->width;
367 height = mem->height;
368 } else {
369 g_assert_not_reached ();
370 }
371
372 if (width < min_width)
373 min_width = width;
374 if (height < min_height)
375 min_height = height;
376 }
377
378 fb->priv->effective_width = min_width;
379 fb->priv->effective_height = min_height;
380 }
381
382 static gboolean
_is_valid_attachment_point(guint attachment_point)383 _is_valid_attachment_point (guint attachment_point)
384 {
385 /* all 31 possible color attachments */
386 if (attachment_point >= 0x8CE0 && attachment_point <= 0x8CFF)
387 return TRUE;
388
389 /* depth-stencil attachment */
390 if (attachment_point == 0x821A)
391 return TRUE;
392
393 /* depth attachment */
394 if (attachment_point == 0x8D00)
395 return TRUE;
396
397 /* stencil attachment */
398 if (attachment_point == 0x8D20)
399 return TRUE;
400
401 return FALSE;
402 }
403
404 static void
_attach_gl_memory(GstGLFramebuffer * fb,guint attachment_point,GstGLMemory * mem)405 _attach_gl_memory (GstGLFramebuffer * fb, guint attachment_point,
406 GstGLMemory * mem)
407 {
408 struct fbo_attachment attach;
409 const GstGLFuncs *gl = fb->context->gl_vtable;
410 guint gl_target = gst_gl_texture_target_to_gl (mem->tex_target);
411
412 gst_gl_framebuffer_bind (fb);
413
414 gl->FramebufferTexture2D (GL_FRAMEBUFFER, attachment_point, gl_target,
415 mem->tex_id, 0);
416
417 _fbo_attachment_init (&attach, attachment_point, (GstGLBaseMemory *) mem);
418 fb->attachments = g_array_append_val (fb->attachments, attach);
419 }
420
421 static void
_attach_renderbuffer(GstGLFramebuffer * fb,guint attachment_point,GstGLRenderbuffer * rb)422 _attach_renderbuffer (GstGLFramebuffer * fb, guint attachment_point,
423 GstGLRenderbuffer * rb)
424 {
425 struct fbo_attachment attach;
426 const GstGLFuncs *gl = fb->context->gl_vtable;
427
428 gst_gl_framebuffer_bind (fb);
429 gl->BindRenderbuffer (GL_RENDERBUFFER, rb->renderbuffer_id);
430
431 gl->FramebufferRenderbuffer (GL_FRAMEBUFFER, attachment_point,
432 GL_RENDERBUFFER, rb->renderbuffer_id);
433
434 _fbo_attachment_init (&attach, attachment_point, (GstGLBaseMemory *) rb);
435 fb->attachments = g_array_append_val (fb->attachments, attach);
436 }
437
438 /**
439 * gst_gl_framebuffer_attach:
440 * @fb: a #GstGLFramebuffer
441 * @attachment_point: the OpenGL attachment point to bind @mem to
442 * @mem: the memory object to bind to @attachment_point
443 *
444 * attach @mem to @attachment_point
445 *
446 * Since: 1.10
447 */
448 void
gst_gl_framebuffer_attach(GstGLFramebuffer * fb,guint attachment_point,GstGLBaseMemory * mem)449 gst_gl_framebuffer_attach (GstGLFramebuffer * fb, guint attachment_point,
450 GstGLBaseMemory * mem)
451 {
452 int i;
453
454 g_return_if_fail (GST_IS_GL_FRAMEBUFFER (fb));
455 g_return_if_fail (gst_gl_context_get_current () == fb->context);
456 g_return_if_fail (_is_valid_attachment_point (attachment_point));
457
458 /* remove the previous attachment */
459 for (i = 0; i < fb->attachments->len; i++) {
460 struct fbo_attachment *attach;
461
462 attach = &g_array_index (fb->attachments, struct fbo_attachment, i);
463
464 if (attach->attachment_point == attachment_point) {
465 g_array_remove_index_fast (fb->attachments, i);
466 break;
467 }
468 }
469
470 if (gst_is_gl_memory (GST_MEMORY_CAST (mem))) {
471 _attach_gl_memory (fb, attachment_point, (GstGLMemory *) mem);
472 } else if (gst_is_gl_renderbuffer (GST_MEMORY_CAST (mem))) {
473 _attach_renderbuffer (fb, attachment_point, (GstGLRenderbuffer *) mem);
474 } else {
475 g_assert_not_reached ();
476 return;
477 }
478
479 _update_effective_dimensions (fb);
480 }
481
482 /**
483 * gst_gl_framebuffer_get_effective_dimensions:
484 * @fb: a #GstGLFramebuffer
485 * @width: (out) (allow-none): output width
486 * @height: (out) (allow-none): output height
487 *
488 * Retreive the effective dimensions from the current attachments attached to
489 * @fb.
490 *
491 * Since: 1.10
492 */
493 void
gst_gl_framebuffer_get_effective_dimensions(GstGLFramebuffer * fb,guint * width,guint * height)494 gst_gl_framebuffer_get_effective_dimensions (GstGLFramebuffer * fb,
495 guint * width, guint * height)
496 {
497 g_return_if_fail (GST_IS_GL_FRAMEBUFFER (fb));
498
499 if (width)
500 *width = fb->priv->effective_width;
501 if (height)
502 *height = fb->priv->effective_height;
503 }
504
505 /**
506 * gst_gl_context_check_framebuffer_status:
507 * @context: a #GstGLContext
508 * @fbo_target: the GL value of the framebuffer target, GL_FRAMEBUFFER,
509 * GL_READ_FRAMEBUFFER, GL_DRAW_FRAMEBUFFER
510 *
511 * Returns: whether whether the current framebuffer is complete
512 *
513 * Since: 1.10
514 */
515 gboolean
gst_gl_context_check_framebuffer_status(GstGLContext * context,guint fbo_target)516 gst_gl_context_check_framebuffer_status (GstGLContext * context,
517 guint fbo_target)
518 {
519 g_return_val_if_fail (GST_IS_GL_CONTEXT (context), FALSE);
520
521 if (fbo_target != GL_FRAMEBUFFER && fbo_target != GL_READ_FRAMEBUFFER
522 && fbo_target != GL_DRAW_FRAMEBUFFER) {
523 GST_ERROR_OBJECT (context, "fbo target is invalid");
524 return FALSE;
525 }
526
527 /* Don't do expensive framebuffer checks when debugging is disabled */
528 if (!_gst_gl_context_debug_is_enabled (context))
529 return TRUE;
530
531 switch (context->gl_vtable->CheckFramebufferStatus (fbo_target)) {
532 case GL_FRAMEBUFFER_COMPLETE:
533 return TRUE;
534 break;
535 case GL_FRAMEBUFFER_UNSUPPORTED:
536 GST_WARNING_OBJECT (context, "GL_FRAMEBUFFER_UNSUPPORTED");
537 break;
538 case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
539 GST_WARNING_OBJECT (context, "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT");
540 break;
541 case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
542 GST_WARNING_OBJECT (context,
543 "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT");
544 break;
545 case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
546 GST_WARNING_OBJECT (context, "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS");
547 break;
548 #if GST_GL_HAVE_OPENGL
549 case GL_FRAMEBUFFER_UNDEFINED:
550 GST_WARNING_OBJECT (context, "GL_FRAMEBUFFER_UNDEFINED");
551 break;
552 #endif
553 default:
554 GST_WARNING_OBJECT (context, "Unknown FBO error");
555 break;
556 }
557
558 return FALSE;
559 }
560
561 /**
562 * gst_gl_framebuffer_get_id:
563 * @fb: a #GstGLFramebuffer
564 *
565 * Returns: the OpenGL id for @fb
566 *
567 * Since: 1.10
568 */
569 guint
gst_gl_framebuffer_get_id(GstGLFramebuffer * fb)570 gst_gl_framebuffer_get_id (GstGLFramebuffer * fb)
571 {
572 g_return_val_if_fail (GST_IS_GL_FRAMEBUFFER (fb), 0);
573
574 return fb->fbo_id;
575 }
576