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