• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  * This function will internally create an OpenGL framebuffer object and must
175  * be called on @context's OpenGL thread.
176  *
177  * Returns: (transfer full): a new #GstGLFramebuffer
178  *
179  * Since: 1.10
180  */
181 GstGLFramebuffer *
gst_gl_framebuffer_new(GstGLContext * context)182 gst_gl_framebuffer_new (GstGLContext * context)
183 {
184   GstGLFramebuffer *fb;
185   const GstGLFuncs *gl;
186 
187   g_return_val_if_fail (GST_IS_GL_CONTEXT (context), NULL);
188   g_return_val_if_fail (gst_gl_context_get_current () == context, NULL);
189 
190   gl = context->gl_vtable;
191 
192   if (!gl->GenFramebuffers) {
193     GST_ERROR_OBJECT (context, "Framebuffers are not supported!");
194     return NULL;
195   }
196 
197   fb = g_object_new (GST_TYPE_GL_FRAMEBUFFER, NULL);
198   fb->context = gst_object_ref (context);
199   gl->GenFramebuffers (1, &fb->fbo_id);
200   gst_object_ref_sink (fb);
201 
202   return fb;
203 }
204 
205 /**
206  * gst_gl_framebuffer_new_with_default_depth:
207  * @context: a #GstGLContext
208  * @width: width for the depth buffer
209  * @height: for the depth buffer
210  *
211  * This function will internally create an OpenGL framebuffer object and must
212  * be called on @context's OpenGL thread.
213  *
214  * Returns: a new #GstGLFramebuffer with a depth buffer of @width and @height
215  *
216  * Since: 1.10
217  */
218 GstGLFramebuffer *
gst_gl_framebuffer_new_with_default_depth(GstGLContext * context,guint width,guint height)219 gst_gl_framebuffer_new_with_default_depth (GstGLContext * context, guint width,
220     guint height)
221 {
222   GstGLFramebuffer *fb = gst_gl_framebuffer_new (context);
223   GstGLBaseMemoryAllocator *render_alloc;
224   GstGLAllocationParams *params;
225   GstGLBaseMemory *renderbuffer;
226   guint attach_point, attach_type;
227 
228   if (!fb)
229     return NULL;
230 
231   if (gst_gl_context_get_gl_api (fb->context) & (GST_GL_API_OPENGL |
232           GST_GL_API_OPENGL3)) {
233     attach_point = GL_DEPTH_STENCIL_ATTACHMENT;
234     attach_type = GST_GL_DEPTH24_STENCIL8;
235   } else if (gst_gl_context_get_gl_api (fb->context) & GST_GL_API_GLES2) {
236     attach_point = GL_DEPTH_ATTACHMENT;
237     attach_type = GST_GL_DEPTH_COMPONENT16;
238   } else {
239     g_assert_not_reached ();
240     return NULL;
241   }
242 
243   render_alloc = (GstGLBaseMemoryAllocator *)
244       gst_allocator_find (GST_GL_RENDERBUFFER_ALLOCATOR_NAME);
245   params = (GstGLAllocationParams *)
246       gst_gl_renderbuffer_allocation_params_new (context, NULL, attach_type,
247       width, height);
248 
249   renderbuffer = gst_gl_base_memory_alloc (render_alloc, params);
250   gst_gl_allocation_params_free (params);
251   gst_object_unref (render_alloc);
252 
253   gst_gl_framebuffer_bind (fb);
254   gst_gl_framebuffer_attach (fb, attach_point, renderbuffer);
255   gst_gl_context_clear_framebuffer (fb->context);
256   gst_memory_unref (GST_MEMORY_CAST (renderbuffer));
257 
258   return fb;
259 }
260 
261 /**
262  * gst_gl_framebuffer_draw_to_texture:
263  * @fb: a #GstGLFramebuffer
264  * @mem: the #GstGLMemory to draw to
265  * @func: (scope call): the function to run
266  * @user_data: data to pass to @func
267  *
268  * Perform the steps necessary to have the output of a glDraw* command in
269  * @func update the contents of @mem.
270  *
271  * Note: this function does not map @mem for writing with OpenGL and that must
272  * be done manually by the caller using any of the mapping functions such as
273  * gst_memory_map() with the map flags %GST_MAP_WRITE | %GST_MAP_GL.
274  *
275  * Must be called with the same OpenGL context current that @fb was created
276  * with.
277  *
278  * Returns: the result of executing @func
279  *
280  * Since: 1.10
281  */
282 gboolean
gst_gl_framebuffer_draw_to_texture(GstGLFramebuffer * fb,GstGLMemory * mem,GstGLFramebufferFunc func,gpointer user_data)283 gst_gl_framebuffer_draw_to_texture (GstGLFramebuffer * fb, GstGLMemory * mem,
284     GstGLFramebufferFunc func, gpointer user_data)
285 {
286   const GstGLFuncs *gl;
287   gboolean ret;
288 
289   g_return_val_if_fail (GST_IS_GL_FRAMEBUFFER (fb), FALSE);
290   g_return_val_if_fail (gst_is_gl_memory (GST_MEMORY_CAST (mem)), FALSE);
291   g_return_val_if_fail (gst_gl_context_get_current () == fb->context, FALSE);
292 
293   gl = fb->context->gl_vtable;
294 
295   GST_TRACE_OBJECT (fb, "drawing to texture %u, dimensions %ix%i", mem->tex_id,
296       gst_gl_memory_get_texture_width (mem),
297       gst_gl_memory_get_texture_height (mem));
298 
299   gst_gl_framebuffer_bind (fb);
300   gst_gl_framebuffer_attach (fb, GL_COLOR_ATTACHMENT0, (GstGLBaseMemory *) mem);
301 
302   gl->Viewport (0, 0, fb->priv->effective_width, fb->priv->effective_height);
303   if (gst_gl_context_get_gl_api (fb->context) & (GST_GL_API_OPENGL |
304           GST_GL_API_OPENGL3))
305     gl->DrawBuffer (GL_COLOR_ATTACHMENT0);
306 
307   ret = func (user_data);
308 
309   if (gst_gl_context_get_gl_api (fb->context) & (GST_GL_API_OPENGL |
310           GST_GL_API_OPENGL3))
311     gl->DrawBuffer (GL_COLOR_ATTACHMENT0);
312   gst_gl_context_clear_framebuffer (fb->context);
313 
314   return ret;
315 }
316 
317 /**
318  * gst_gl_framebuffer_bind:
319  * @fb: a #GstGLFramebuffer
320  *
321  * Bind @fb into the current thread
322  *
323  * Must be called with the same OpenGL context current that @fb was created
324  * with.
325  *
326  * Since: 1.10
327  */
328 void
gst_gl_framebuffer_bind(GstGLFramebuffer * fb)329 gst_gl_framebuffer_bind (GstGLFramebuffer * fb)
330 {
331   const GstGLFuncs *gl;
332 
333   g_return_if_fail (GST_IS_GL_FRAMEBUFFER (fb));
334   g_return_if_fail (gst_gl_context_get_current () == fb->context);
335   g_return_if_fail (fb->fbo_id != 0);
336 
337   gl = fb->context->gl_vtable;
338 
339   gl->BindFramebuffer (GL_FRAMEBUFFER, fb->fbo_id);
340 }
341 
342 /**
343  * gst_gl_context_clear_framebuffer:
344  * @context: a #GstGLContext
345  *
346  * Unbind the current framebuffer
347  *
348  * Since: 1.10
349  */
350 void
gst_gl_context_clear_framebuffer(GstGLContext * context)351 gst_gl_context_clear_framebuffer (GstGLContext * context)
352 {
353   const GstGLFuncs *gl;
354 
355   g_return_if_fail (GST_IS_GL_CONTEXT (context));
356 
357   gl = context->gl_vtable;
358 
359   gl->BindFramebuffer (GL_FRAMEBUFFER, 0);
360 }
361 
362 static void
_update_effective_dimensions(GstGLFramebuffer * fb)363 _update_effective_dimensions (GstGLFramebuffer * fb)
364 {
365   int i;
366   guint min_width = -1, min_height = -1;
367 
368   /* remove the previous attachment */
369   for (i = 0; i < fb->attachments->len; i++) {
370     struct fbo_attachment *attach;
371     int width, height;
372 
373     attach = &g_array_index (fb->attachments, struct fbo_attachment, i);
374 
375     if (gst_is_gl_memory (GST_MEMORY_CAST (attach->mem))) {
376       GstGLMemory *mem = (GstGLMemory *) attach->mem;
377 
378       width = gst_gl_memory_get_texture_width (mem);
379       height = gst_gl_memory_get_texture_height (mem);
380     } else if (gst_is_gl_renderbuffer (GST_MEMORY_CAST (attach->mem))) {
381       GstGLRenderbuffer *mem = (GstGLRenderbuffer *) attach->mem;
382 
383       width = mem->width;
384       height = mem->height;
385     } else {
386       g_assert_not_reached ();
387     }
388 
389     if (width < min_width)
390       min_width = width;
391     if (height < min_height)
392       min_height = height;
393   }
394 
395   fb->priv->effective_width = min_width;
396   fb->priv->effective_height = min_height;
397 }
398 
399 static gboolean
_is_valid_attachment_point(guint attachment_point)400 _is_valid_attachment_point (guint attachment_point)
401 {
402   /* all 31 possible color attachments */
403   if (attachment_point >= 0x8CE0 && attachment_point <= 0x8CFF)
404     return TRUE;
405 
406   /* depth-stencil attachment */
407   if (attachment_point == 0x821A)
408     return TRUE;
409 
410   /* depth attachment */
411   if (attachment_point == 0x8D00)
412     return TRUE;
413 
414   /* stencil attachment */
415   if (attachment_point == 0x8D20)
416     return TRUE;
417 
418   return FALSE;
419 }
420 
421 static void
_attach_gl_memory(GstGLFramebuffer * fb,guint attachment_point,GstGLMemory * mem)422 _attach_gl_memory (GstGLFramebuffer * fb, guint attachment_point,
423     GstGLMemory * mem)
424 {
425   struct fbo_attachment attach;
426   const GstGLFuncs *gl = fb->context->gl_vtable;
427   guint gl_target = gst_gl_texture_target_to_gl (mem->tex_target);
428 
429   gst_gl_framebuffer_bind (fb);
430 
431   gl->FramebufferTexture2D (GL_FRAMEBUFFER, attachment_point, gl_target,
432       mem->tex_id, 0);
433 
434   _fbo_attachment_init (&attach, attachment_point, (GstGLBaseMemory *) mem);
435   fb->attachments = g_array_append_val (fb->attachments, attach);
436 }
437 
438 static void
_attach_renderbuffer(GstGLFramebuffer * fb,guint attachment_point,GstGLRenderbuffer * rb)439 _attach_renderbuffer (GstGLFramebuffer * fb, guint attachment_point,
440     GstGLRenderbuffer * rb)
441 {
442   struct fbo_attachment attach;
443   const GstGLFuncs *gl = fb->context->gl_vtable;
444 
445   gst_gl_framebuffer_bind (fb);
446   gl->BindRenderbuffer (GL_RENDERBUFFER, rb->renderbuffer_id);
447 
448   gl->FramebufferRenderbuffer (GL_FRAMEBUFFER, attachment_point,
449       GL_RENDERBUFFER, rb->renderbuffer_id);
450 
451   _fbo_attachment_init (&attach, attachment_point, (GstGLBaseMemory *) rb);
452   fb->attachments = g_array_append_val (fb->attachments, attach);
453 }
454 
455 /**
456  * gst_gl_framebuffer_attach:
457  * @fb: a #GstGLFramebuffer
458  * @attachment_point: the OpenGL attachment point to bind @mem to
459  * @mem: the memory object to bind to @attachment_point
460  *
461  * attach @mem to @attachment_point
462  *
463  * Must be called with the same OpenGL context current that @fb was created
464  * with.
465  *
466  * Since: 1.10
467  */
468 void
gst_gl_framebuffer_attach(GstGLFramebuffer * fb,guint attachment_point,GstGLBaseMemory * mem)469 gst_gl_framebuffer_attach (GstGLFramebuffer * fb, guint attachment_point,
470     GstGLBaseMemory * mem)
471 {
472   int i;
473 
474   g_return_if_fail (GST_IS_GL_FRAMEBUFFER (fb));
475   g_return_if_fail (gst_gl_context_get_current () == fb->context);
476   g_return_if_fail (_is_valid_attachment_point (attachment_point));
477 
478   /* remove the previous attachment */
479   for (i = 0; i < fb->attachments->len; i++) {
480     struct fbo_attachment *attach;
481 
482     attach = &g_array_index (fb->attachments, struct fbo_attachment, i);
483 
484     if (attach->attachment_point == attachment_point) {
485       g_array_remove_index_fast (fb->attachments, i);
486       break;
487     }
488   }
489 
490   if (gst_is_gl_memory (GST_MEMORY_CAST (mem))) {
491     _attach_gl_memory (fb, attachment_point, (GstGLMemory *) mem);
492   } else if (gst_is_gl_renderbuffer (GST_MEMORY_CAST (mem))) {
493     _attach_renderbuffer (fb, attachment_point, (GstGLRenderbuffer *) mem);
494   } else {
495     g_assert_not_reached ();
496     return;
497   }
498 
499   _update_effective_dimensions (fb);
500 }
501 
502 /**
503  * gst_gl_framebuffer_get_effective_dimensions:
504  * @fb: a #GstGLFramebuffer
505  * @width: (out) (allow-none): output width
506  * @height: (out) (allow-none): output height
507  *
508  * Retrieve the effective dimensions from the current attachments attached to
509  * @fb.
510  *
511  * Since: 1.10
512  */
513 void
gst_gl_framebuffer_get_effective_dimensions(GstGLFramebuffer * fb,guint * width,guint * height)514 gst_gl_framebuffer_get_effective_dimensions (GstGLFramebuffer * fb,
515     guint * width, guint * height)
516 {
517   g_return_if_fail (GST_IS_GL_FRAMEBUFFER (fb));
518 
519   if (width)
520     *width = fb->priv->effective_width;
521   if (height)
522     *height = fb->priv->effective_height;
523 }
524 
525 /**
526  * gst_gl_context_check_framebuffer_status:
527  * @context: a #GstGLContext
528  * @fbo_target: the GL value of the framebuffer target, GL_FRAMEBUFFER,
529  *              GL_READ_FRAMEBUFFER, GL_DRAW_FRAMEBUFFER
530  *
531  * Must be called with @context current.
532  *
533  * Returns: whether whether the current framebuffer is complete
534  *
535  * Since: 1.10
536  */
537 gboolean
gst_gl_context_check_framebuffer_status(GstGLContext * context,guint fbo_target)538 gst_gl_context_check_framebuffer_status (GstGLContext * context,
539     guint fbo_target)
540 {
541   GLenum ret;
542 
543   g_return_val_if_fail (GST_IS_GL_CONTEXT (context), FALSE);
544   g_return_val_if_fail (gst_gl_context_get_current () == context, FALSE);
545 
546   if (fbo_target != GL_FRAMEBUFFER && fbo_target != GL_READ_FRAMEBUFFER
547       && fbo_target != GL_DRAW_FRAMEBUFFER) {
548     GST_ERROR_OBJECT (context, "fbo target is invalid");
549     return FALSE;
550   }
551 
552   /* Don't do expensive framebuffer checks when debugging is disabled */
553   if (!_gst_gl_context_debug_is_enabled (context))
554     return TRUE;
555 
556   ret = context->gl_vtable->CheckFramebufferStatus (fbo_target);
557   switch (ret) {
558     case GL_FRAMEBUFFER_COMPLETE:
559       return TRUE;
560       break;
561     case GL_FRAMEBUFFER_UNSUPPORTED:
562       GST_WARNING_OBJECT (context, "GL_FRAMEBUFFER_UNSUPPORTED");
563       break;
564     case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
565       GST_WARNING_OBJECT (context, "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT");
566       break;
567     case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
568       GST_WARNING_OBJECT (context,
569           "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT");
570       break;
571     case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
572       GST_WARNING_OBJECT (context, "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS");
573       break;
574 #if GST_GL_HAVE_OPENGL
575     case GL_FRAMEBUFFER_UNDEFINED:
576       GST_WARNING_OBJECT (context, "GL_FRAMEBUFFER_UNDEFINED");
577       break;
578 #endif
579     default:
580       GST_WARNING_OBJECT (context, "Unknown FBO error: %d (0x%x)", ret, ret);
581       break;
582   }
583 
584   return FALSE;
585 }
586 
587 /**
588  * gst_gl_framebuffer_get_id:
589  * @fb: a #GstGLFramebuffer
590  *
591  * Returns: the OpenGL id for @fb
592  *
593  * Since: 1.10
594  */
595 guint
gst_gl_framebuffer_get_id(GstGLFramebuffer * fb)596 gst_gl_framebuffer_get_id (GstGLFramebuffer * fb)
597 {
598   g_return_val_if_fail (GST_IS_GL_FRAMEBUFFER (fb), 0);
599 
600   return fb->fbo_id;
601 }
602