1 /* GStreamer
2  *
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 #ifdef HAVE_CONFIG_H
22 #  include "config.h"
23 #endif
24 
25 #include <gst/check/gstcheck.h>
26 
27 #include <gst/gl/gl.h>
28 #include <gst/gl/gstglfuncs.h>
29 
30 #include <stdio.h>
31 
32 static GstGLDisplay *display;
33 
34 static void
setup(void)35 setup (void)
36 {
37   display = gst_gl_display_new ();
38 }
39 
40 static void
teardown(void)41 teardown (void)
42 {
43   gst_object_unref (display);
44 }
45 
46 static GstGLMemory *gl_tex, *gl_tex2;
47 static GLuint vbo, vbo_indices, vao;
48 static GstGLFramebuffer *fbo, *fbo2;
49 static GstGLShader *shader;
50 static GLint shader_attr_position_loc;
51 static GLint shader_attr_texture_loc;
52 
53 static const GLfloat vertices[] = {
54   /* x, y, z, s, t */
55   1.0f, 1.0f, 0.0f, 1.0f, 0.0f,
56   -1.0f, 1.0f, 0.0f, 0.0f, 0.0f,
57   -1.0f, -1.0f, 0.0f, 0.0f, 1.0f,
58   1.0f, -1.0f, 0.0f, 1.0f, 1.0f
59 };
60 
61 static const GLushort indices[] = { 0, 1, 2, 0, 2, 3 };
62 
63 static void
init(gpointer data)64 init (gpointer data)
65 {
66   GstGLContext *context = data;
67   GError *error = NULL;
68   GstVideoInfo v_info;
69   GstGLMemoryAllocator *allocator;
70   GstGLVideoAllocationParams *params;
71 
72   gst_video_info_set_format (&v_info, GST_VIDEO_FORMAT_RGBA, 320, 240);
73   allocator = gst_gl_memory_allocator_get_default (context);
74   params =
75       gst_gl_video_allocation_params_new (context, NULL, &v_info, 0, NULL,
76       GST_GL_TEXTURE_TARGET_2D, GST_GL_RGBA);
77 
78   /* has to be called in the thread that is going to use the framebuffer */
79   fbo = gst_gl_framebuffer_new_with_default_depth (context, 320, 240);
80 
81   fail_if (fbo == NULL, "failed to create framebuffer object");
82 
83   gl_tex =
84       (GstGLMemory *) gst_gl_base_memory_alloc ((GstGLBaseMemoryAllocator *)
85       allocator, (GstGLAllocationParams *) params);
86   gl_tex2 =
87       (GstGLMemory *) gst_gl_base_memory_alloc ((GstGLBaseMemoryAllocator *)
88       allocator, (GstGLAllocationParams *) params);
89   gst_object_unref (allocator);
90   gst_gl_allocation_params_free ((GstGLAllocationParams *) params);
91   fail_if (gl_tex == NULL, "failed to create texture");
92 
93   shader = gst_gl_shader_new_default (context, &error);
94   fail_if (shader == NULL, "failed to create shader object: %s",
95       error->message);
96 
97   shader_attr_position_loc =
98       gst_gl_shader_get_attribute_location (shader, "a_position");
99   shader_attr_texture_loc =
100       gst_gl_shader_get_attribute_location (shader, "a_texcoord");
101 }
102 
103 static void
deinit(gpointer data)104 deinit (gpointer data)
105 {
106   GstGLContext *context = data;
107   GstGLFuncs *gl = context->gl_vtable;
108   if (vao)
109     gl->DeleteVertexArrays (1, &vao);
110   gst_object_unref (fbo);
111   gst_object_unref (shader);
112   gst_memory_unref (GST_MEMORY_CAST (gl_tex));
113   gst_memory_unref (GST_MEMORY_CAST (gl_tex2));
114 }
115 
116 static gboolean
clear_tex(gpointer data)117 clear_tex (gpointer data)
118 {
119   GstGLContext *context = data;
120   GstGLFuncs *gl = context->gl_vtable;
121   static gfloat r = 0.0, g = 0.0, b = 0.0;
122 
123   gl->ClearColor (r, g, b, 1.0);
124   gl->Clear (GL_COLOR_BUFFER_BIT);
125 
126   r = r > 1.0 ? 0.0 : r + 0.03;
127   g = g > 1.0 ? 0.0 : g + 0.01;
128   b = b > 1.0 ? 0.0 : b + 0.015;
129 
130   return TRUE;
131 }
132 
133 static void
draw_tex(gpointer data)134 draw_tex (gpointer data)
135 {
136   gst_gl_framebuffer_draw_to_texture (fbo, gl_tex,
137       (GstGLFramebufferFunc) clear_tex, data);
138 }
139 
140 static void
_bind_buffer(GstGLContext * context)141 _bind_buffer (GstGLContext * context)
142 {
143   const GstGLFuncs *gl = context->gl_vtable;
144 
145   gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, vbo_indices);
146   gl->BindBuffer (GL_ARRAY_BUFFER, vbo);
147 
148   /* Load the vertex position */
149   gl->VertexAttribPointer (shader_attr_position_loc, 3, GL_FLOAT, GL_FALSE,
150       5 * sizeof (GLfloat), (void *) 0);
151 
152   /* Load the texture coordinate */
153   gl->VertexAttribPointer (shader_attr_texture_loc, 2, GL_FLOAT, GL_FALSE,
154       5 * sizeof (GLfloat), (void *) (3 * sizeof (GLfloat)));
155 
156   gl->EnableVertexAttribArray (shader_attr_position_loc);
157   gl->EnableVertexAttribArray (shader_attr_texture_loc);
158 }
159 
160 static void
_unbind_buffer(GstGLContext * context)161 _unbind_buffer (GstGLContext * context)
162 {
163   const GstGLFuncs *gl = context->gl_vtable;
164 
165   gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);
166   gl->BindBuffer (GL_ARRAY_BUFFER, 0);
167 
168   gl->DisableVertexAttribArray (shader_attr_position_loc);
169   gl->DisableVertexAttribArray (shader_attr_texture_loc);
170 }
171 
172 static void
init_blit(gpointer data)173 init_blit (gpointer data)
174 {
175   GstGLContext *context = data;
176   const GstGLFuncs *gl = context->gl_vtable;
177 
178   if (!vbo) {
179     if (gl->GenVertexArrays) {
180       gl->GenVertexArrays (1, &vao);
181       gl->BindVertexArray (vao);
182     }
183 
184     gl->GenBuffers (1, &vbo);
185     gl->BindBuffer (GL_ARRAY_BUFFER, vbo);
186     gl->BufferData (GL_ARRAY_BUFFER, 4 * 5 * sizeof (GLfloat), vertices,
187         GL_STATIC_DRAW);
188 
189     gl->GenBuffers (1, &vbo_indices);
190     gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, vbo_indices);
191     gl->BufferData (GL_ELEMENT_ARRAY_BUFFER, sizeof (indices), indices,
192         GL_STATIC_DRAW);
193 
194     if (gl->GenVertexArrays) {
195       _bind_buffer (context);
196       gl->BindVertexArray (0);
197     }
198 
199     gl->BindBuffer (GL_ARRAY_BUFFER, 0);
200     gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);
201   }
202   /* has to be called in the thread that is going to use the framebuffer */
203   fbo2 = gst_gl_framebuffer_new_with_default_depth (context, 320, 240);
204 
205   fail_if (fbo2 == NULL, "failed to create framebuffer object");
206 }
207 
208 static void
deinit_blit(gpointer data)209 deinit_blit (gpointer data)
210 {
211   GstGLContext *context = data;
212   const GstGLFuncs *gl = context->gl_vtable;
213 
214   if (vbo)
215     gl->DeleteBuffers (1, &vbo);
216   vbo = 0;
217   if (vbo_indices)
218     gl->DeleteBuffers (1, &vbo_indices);
219   vbo_indices = 0;
220   if (vao)
221     gl->DeleteVertexArrays (1, &vao);
222   vao = 0;
223   gst_object_unref (fbo2);
224   fbo2 = NULL;
225 }
226 
227 static gboolean
blit_tex(gpointer data)228 blit_tex (gpointer data)
229 {
230   GstGLContext *context = data;
231   const GstGLFuncs *gl = context->gl_vtable;
232 
233   gl->Clear (GL_COLOR_BUFFER_BIT);
234 
235   gst_gl_shader_use (shader);
236 
237   gl->ActiveTexture (GL_TEXTURE0);
238   gl->BindTexture (GL_TEXTURE_2D, gst_gl_memory_get_texture_id (gl_tex));
239   gst_gl_shader_set_uniform_1i (shader, "s_texture", 0);
240 
241   if (gl->GenVertexArrays)
242     gl->BindVertexArray (vao);
243   _bind_buffer (context);
244 
245   gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
246 
247   if (gl->GenVertexArrays)
248     gl->BindVertexArray (0);
249   else
250     _unbind_buffer (context);
251 
252   return TRUE;
253 }
254 
255 static void
draw_render(gpointer data)256 draw_render (gpointer data)
257 {
258   gst_gl_framebuffer_draw_to_texture (fbo2, gl_tex2,
259       (GstGLFramebufferFunc) blit_tex, data);
260 }
261 
GST_START_TEST(test_share)262 GST_START_TEST (test_share)
263 {
264   GstGLContext *context;
265   GstGLWindow *window;
266   GstGLContext *other_context;
267   GstGLWindow *other_window;
268   GError *error = NULL;
269   gint i = 0;
270 
271   context = gst_gl_context_new (display);
272 
273   window = gst_gl_window_new (display);
274   gst_gl_context_set_window (context, window);
275 
276   gst_gl_context_create (context, 0, &error);
277 
278   fail_if (error != NULL, "Error creating master context %s\n",
279       error ? error->message : "Unknown Error");
280 
281   other_window = gst_gl_window_new (display);
282 
283   other_context = gst_gl_context_new (display);
284   gst_gl_context_set_window (other_context, other_window);
285 
286   gst_gl_context_create (other_context, context, &error);
287 
288   fail_if (error != NULL, "Error creating secondary context %s\n",
289       error ? error->message : "Unknown Error");
290 
291   /* make the window visible */
292   gst_gl_window_set_preferred_size (window, 320, 240);
293   gst_gl_window_draw (window);
294 
295   gst_gl_window_send_message (other_window, GST_GL_WINDOW_CB (init),
296       other_context);
297   gst_gl_window_send_message (window, GST_GL_WINDOW_CB (init_blit), context);
298 
299   while (i < 10) {
300     gst_gl_window_send_message (other_window, GST_GL_WINDOW_CB (draw_tex),
301         context);
302     gst_gl_window_send_message (window, GST_GL_WINDOW_CB (draw_render),
303         context);
304     i++;
305   }
306 
307   gst_gl_window_send_message (other_window, GST_GL_WINDOW_CB (deinit),
308       other_context);
309   gst_gl_window_send_message (window, GST_GL_WINDOW_CB (deinit_blit), context);
310 
311   gst_object_unref (window);
312   gst_object_unref (other_window);
313   gst_object_unref (other_context);
314   gst_object_unref (context);
315 }
316 
317 GST_END_TEST;
318 
319 static void
accum_true(GstGLContext * context,gpointer data)320 accum_true (GstGLContext * context, gpointer data)
321 {
322   gint *i = data;
323   *i = 1;
324 }
325 
326 static void
check_wrapped(gpointer data)327 check_wrapped (gpointer data)
328 {
329   GstGLContext *wrapped_context = data;
330   GError *error = NULL;
331   gint i = 0;
332   gboolean ret;
333 
334   /* check that scheduling on an unactivated wrapped context asserts */
335   ASSERT_CRITICAL (gst_gl_context_thread_add (wrapped_context,
336           (GstGLContextThreadFunc) accum_true, &i));
337   fail_if (i != 0);
338 
339   /* check that scheduling on an activated context succeeds */
340   gst_gl_context_activate (wrapped_context, TRUE);
341   gst_gl_context_thread_add (wrapped_context,
342       (GstGLContextThreadFunc) accum_true, &i);
343   fail_if (i != 1);
344 
345   /* check filling out the wrapped context's info */
346   fail_if (wrapped_context->gl_vtable->TexImage2D != NULL);
347   ret = gst_gl_context_fill_info (wrapped_context, &error);
348   fail_if (!ret, "error received %s\n",
349       error ? error->message : "Unknown error");
350   fail_if (wrapped_context->gl_vtable->TexImage2D == NULL);
351   gst_gl_context_activate (wrapped_context, FALSE);
352 }
353 
GST_START_TEST(test_wrapped_context)354 GST_START_TEST (test_wrapped_context)
355 {
356   GstGLContext *context, *other_context, *wrapped_context;
357   GstGLWindow *window, *other_window;
358   GError *error = NULL;
359   gint i = 0;
360   guintptr handle, handle2;
361   GstGLPlatform platform, platform2;
362   GstGLAPI apis, apis2;
363 
364   context = gst_gl_context_new (display);
365 
366   window = gst_gl_window_new (display);
367   gst_gl_context_set_window (context, window);
368 
369   gst_gl_context_create (context, 0, &error);
370 
371   fail_if (error != NULL, "Error creating master context %s\n",
372       error ? error->message : "Unknown Error");
373 
374   handle = gst_gl_context_get_gl_context (context);
375   platform = gst_gl_context_get_gl_platform (context);
376   apis = gst_gl_context_get_gl_api (context);
377 
378   wrapped_context =
379       gst_gl_context_new_wrapped (display, handle, platform, apis);
380 
381   handle2 = gst_gl_context_get_gl_context (wrapped_context);
382   platform2 = gst_gl_context_get_gl_platform (wrapped_context);
383   apis2 = gst_gl_context_get_gl_api (wrapped_context);
384 
385   fail_if (handle != handle2);
386   fail_if (platform != platform2);
387   fail_if (apis != apis2);
388 
389   other_context = gst_gl_context_new (display);
390   other_window = gst_gl_window_new (display);
391   gst_gl_context_set_window (other_context, other_window);
392 
393   gst_gl_context_create (other_context, wrapped_context, &error);
394 
395   fail_if (error != NULL, "Error creating secondary context %s\n",
396       error ? error->message : "Unknown Error");
397 
398   gst_gl_window_set_preferred_size (window, 320, 240);
399   gst_gl_window_draw (window);
400 
401   gst_gl_window_send_message (other_window, GST_GL_WINDOW_CB (init),
402       other_context);
403   gst_gl_window_send_message (window, GST_GL_WINDOW_CB (init_blit), context);
404 
405   while (i < 10) {
406     gst_gl_window_send_message (other_window, GST_GL_WINDOW_CB (draw_tex),
407         context);
408     gst_gl_window_send_message (window, GST_GL_WINDOW_CB (draw_render),
409         context);
410     i++;
411   }
412 
413   gst_gl_window_send_message (window, GST_GL_WINDOW_CB (check_wrapped),
414       wrapped_context);
415 
416   gst_gl_window_send_message (other_window, GST_GL_WINDOW_CB (deinit),
417       other_context);
418   gst_gl_window_send_message (window, GST_GL_WINDOW_CB (deinit_blit), context);
419 
420   gst_object_unref (other_context);
421   gst_object_unref (other_window);
422   gst_object_unref (window);
423   gst_object_unref (context);
424   gst_object_unref (wrapped_context);
425 }
426 
427 GST_END_TEST;
428 
429 struct context_info
430 {
431   GstGLAPI api;
432   guint major;
433   guint minor;
434   GstGLPlatform platform;
435   guintptr handle;
436 };
437 
438 static void
_fill_context_info(GstGLContext * context,struct context_info * info)439 _fill_context_info (GstGLContext * context, struct context_info *info)
440 {
441   info->handle = gst_gl_context_get_current_gl_context (info->platform);
442   info->api =
443       gst_gl_context_get_current_gl_api (info->platform, &info->major,
444       &info->minor);
445 }
446 
GST_START_TEST(test_current_context)447 GST_START_TEST (test_current_context)
448 {
449   GstGLContext *context;
450   GError *error = NULL;
451   guintptr handle;
452   GstGLPlatform platform;
453   GstGLAPI api;
454   gint major, minor;
455   struct context_info info;
456 
457   context = gst_gl_context_new (display);
458 
459   gst_gl_context_create (context, 0, &error);
460 
461   fail_if (error != NULL, "Error creating master context %s\n",
462       error ? error->message : "Unknown Error");
463 
464   handle = gst_gl_context_get_gl_context (context);
465   platform = gst_gl_context_get_gl_platform (context);
466   api = gst_gl_context_get_gl_api (context);
467   gst_gl_context_get_gl_version (context, &major, &minor);
468 
469   info.platform = platform;
470 
471   gst_gl_context_thread_add (context,
472       (GstGLContextThreadFunc) _fill_context_info, &info);
473 
474   fail_if (info.platform != platform);
475   fail_if (info.api != api);
476   fail_if (info.major != major);
477   fail_if (info.minor != minor);
478   fail_if (info.handle != handle);
479 
480   gst_object_unref (context);
481 }
482 
483 GST_END_TEST;
484 
GST_START_TEST(test_context_can_share)485 GST_START_TEST (test_context_can_share)
486 {
487   GstGLContext *c1, *c2, *c3;
488   GError *error = NULL;
489 
490   c1 = gst_gl_context_new (display);
491   gst_gl_context_create (c1, NULL, &error);
492   fail_if (error != NULL, "Error creating context %s\n",
493       error ? error->message : "Unknown Error");
494 
495   c2 = gst_gl_context_new (display);
496   gst_gl_context_create (c2, c1, &error);
497   fail_if (error != NULL, "Error creating context %s\n",
498       error ? error->message : "Unknown Error");
499 
500   fail_unless (gst_gl_context_can_share (c1, c2));
501   fail_unless (gst_gl_context_can_share (c2, c1));
502 
503   c3 = gst_gl_context_new (display);
504   gst_gl_context_create (c3, c2, &error);
505   fail_if (error != NULL, "Error creating context %s\n",
506       error ? error->message : "Unknown Error");
507 
508   fail_unless (gst_gl_context_can_share (c1, c3));
509   fail_unless (gst_gl_context_can_share (c3, c1));
510   fail_unless (gst_gl_context_can_share (c2, c3));
511   fail_unless (gst_gl_context_can_share (c3, c2));
512 
513   /* destroy the middle context */
514   gst_object_unref (c2);
515   c2 = NULL;
516 
517   fail_unless (gst_gl_context_can_share (c1, c3));
518   fail_unless (gst_gl_context_can_share (c3, c1));
519 
520   gst_object_unref (c1);
521   gst_object_unref (c3);
522 }
523 
524 GST_END_TEST;
525 
GST_START_TEST(test_is_shared)526 GST_START_TEST (test_is_shared)
527 {
528   GstGLContext *c1, *c2;
529   GError *error = NULL;
530 
531   c1 = gst_gl_context_new (display);
532   gst_gl_context_create (c1, NULL, &error);
533   fail_if (error != NULL, "Error creating context %s\n",
534       error ? error->message : "Unknown Error");
535 
536   c2 = gst_gl_context_new (display);
537   gst_gl_context_create (c2, c1, &error);
538   fail_if (error != NULL, "Error creating context %s\n",
539       error ? error->message : "Unknown Error");
540 
541   fail_unless (gst_gl_context_is_shared (c1));
542   fail_unless (gst_gl_context_is_shared (c2));
543 
544   gst_object_unref (c2);
545   c2 = NULL;
546 
547   fail_unless (!gst_gl_context_is_shared (c1));
548 
549   gst_object_unref (c1);
550 }
551 
552 GST_END_TEST;
553 
GST_START_TEST(test_display_list)554 GST_START_TEST (test_display_list)
555 {
556   GstGLContext *c1, *c2;
557   GError *error = NULL;
558 
559   c1 = gst_gl_context_new (display);
560   gst_gl_context_create (c1, NULL, &error);
561   fail_if (error != NULL, "Error creating context %s\n",
562       error ? error->message : "Unknown Error");
563 
564   GST_OBJECT_LOCK (display);
565   {
566     /* no context added so get should return NULL */
567     GstGLContext *tmp =
568         gst_gl_display_get_gl_context_for_thread (display, NULL);
569     fail_unless (tmp == NULL);
570   }
571 
572   fail_unless (gst_gl_display_add_context (display, c1));
573   /* re-adding the same context is a no-op */
574   fail_unless (gst_gl_display_add_context (display, c1));
575 
576   {
577     GThread *thread;
578     GstGLContext *tmp;
579 
580     thread = gst_gl_context_get_thread (c1);
581     fail_unless (thread != NULL);
582 
583     tmp = gst_gl_display_get_gl_context_for_thread (display, thread);
584     fail_unless (tmp == c1);
585     g_thread_unref (thread);
586     gst_object_unref (tmp);
587 
588     tmp = gst_gl_display_get_gl_context_for_thread (display, NULL);
589     fail_unless (tmp == c1);
590     gst_object_unref (tmp);
591   }
592 
593   c2 = gst_gl_context_new (display);
594   gst_gl_context_create (c2, c1, &error);
595   fail_if (error != NULL, "Error creating context %s\n",
596       error ? error->message : "Unknown Error");
597 
598   fail_unless (gst_gl_display_add_context (display, c2));
599   /* re-adding the same context is a no-op */
600   fail_unless (gst_gl_display_add_context (display, c2));
601 
602   {
603     GThread *thread;
604     GstGLContext *tmp;
605 
606     thread = gst_gl_context_get_thread (c2);
607     fail_unless (thread != NULL);
608 
609     tmp = gst_gl_display_get_gl_context_for_thread (display, thread);
610     fail_unless (tmp == c2);
611     g_thread_unref (thread);
612     gst_object_unref (tmp);
613 
614     /* undefined which context will be returned for the NULL thread */
615     tmp = gst_gl_display_get_gl_context_for_thread (display, NULL);
616     fail_unless (tmp != NULL);
617     gst_object_unref (tmp);
618   }
619 
620   gst_object_unref (c1);
621   /* c1 is now dead */
622 
623   {
624     GstGLContext *tmp;
625 
626     tmp = gst_gl_display_get_gl_context_for_thread (display, NULL);
627     fail_unless (tmp == c2);
628     gst_object_unref (tmp);
629   }
630   GST_OBJECT_UNLOCK (display);
631 
632   gst_object_unref (c2);
633   /* c2 is now dead */
634 
635   {
636     /* no more contexts alive */
637     GstGLContext *tmp =
638         gst_gl_display_get_gl_context_for_thread (display, NULL);
639     fail_unless (tmp == NULL);
640   }
641 }
642 
643 GST_END_TEST;
644 
645 static Suite *
gst_gl_context_suite(void)646 gst_gl_context_suite (void)
647 {
648   Suite *s = suite_create ("GstGLContext");
649   TCase *tc_chain = tcase_create ("general");
650 
651   suite_add_tcase (s, tc_chain);
652   tcase_add_checked_fixture (tc_chain, setup, teardown);
653   tcase_add_test (tc_chain, test_share);
654   tcase_add_test (tc_chain, test_wrapped_context);
655   tcase_add_test (tc_chain, test_current_context);
656   tcase_add_test (tc_chain, test_context_can_share);
657   tcase_add_test (tc_chain, test_is_shared);
658   tcase_add_test (tc_chain, test_display_list);
659 
660   return s;
661 }
662 
663 GST_CHECK_MAIN (gst_gl_context);
664