gstreamer/tests/check/libs/gstglcontext.c
Matthew Waters 518e8a3fd2 glframebuffer: rewrite for a more consistent API
Facilities are given to create fbo's and attach GL memory (renderbuffers
or textures).  It also keeps track of the renderable size for use with
effective use with glViewport().
2016-07-26 14:07:24 +10:00

648 lines
17 KiB
C

/* GStreamer
*
* Copyright (C) 2013 Matthew Waters <ystreet00@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <gst/check/gstcheck.h>
#include <gst/gl/gstglcontext.h>
#include <stdio.h>
static GstGLDisplay *display;
static void
setup (void)
{
display = gst_gl_display_new ();
}
static void
teardown (void)
{
gst_object_unref (display);
}
static GstGLMemory *gl_tex;
static GLuint vbo, vbo_indices, vao;
static GstGLFramebuffer *fbo;
static GstGLShader *shader;
static GLint shader_attr_position_loc;
static GLint shader_attr_texture_loc;
static const GLfloat vertices[] = {
/* x, y, z, s, t */
1.0f, 1.0f, 0.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f, 0.0f, 0.0f,
-1.0f, -1.0f, 0.0f, 0.0f, 1.0f,
1.0f, -1.0f, 0.0f, 1.0f, 1.0f
};
static const GLushort indices[] = { 0, 1, 2, 0, 2, 3 };
static void
init (gpointer data)
{
GstGLContext *context = data;
GError *error = NULL;
GstVideoInfo v_info;
GstGLMemoryAllocator *allocator;
GstGLVideoAllocationParams *params;
gst_video_info_set_format (&v_info, GST_VIDEO_FORMAT_RGBA, 320, 240);
allocator = gst_gl_memory_allocator_get_default (context);
params =
gst_gl_video_allocation_params_new (context, NULL, &v_info, 0, NULL,
GST_GL_TEXTURE_TARGET_2D, GST_VIDEO_GL_TEXTURE_TYPE_RGBA);
/* has to be called in the thread that is going to use the framebuffer */
fbo = gst_gl_framebuffer_new_with_default_depth (context, 320, 240);
fail_if (fbo == NULL, "failed to create framebuffer object");
gl_tex =
(GstGLMemory *) gst_gl_base_memory_alloc ((GstGLBaseMemoryAllocator *)
allocator, (GstGLAllocationParams *) params);
gst_object_unref (allocator);
gst_gl_allocation_params_free ((GstGLAllocationParams *) params);
fail_if (gl_tex == NULL, "failed to create texture");
shader = gst_gl_shader_new_default (context, &error);
fail_if (shader == NULL, "failed to create shader object: %s",
error->message);
shader_attr_position_loc =
gst_gl_shader_get_attribute_location (shader, "a_position");
shader_attr_texture_loc =
gst_gl_shader_get_attribute_location (shader, "a_texcoord");
}
static void
deinit (gpointer data)
{
GstGLContext *context = data;
GstGLFuncs *gl = context->gl_vtable;
if (vao)
gl->DeleteVertexArrays (1, &vao);
gst_object_unref (fbo);
gst_object_unref (shader);
gst_memory_unref (GST_MEMORY_CAST (gl_tex));
}
static gboolean
clear_tex (gpointer data)
{
GstGLContext *context = data;
GstGLFuncs *gl = context->gl_vtable;
static gfloat r = 0.0, g = 0.0, b = 0.0;
gl->ClearColor (r, g, b, 1.0);
gl->Clear (GL_COLOR_BUFFER_BIT);
r = r > 1.0 ? 0.0 : r + 0.03;
g = g > 1.0 ? 0.0 : g + 0.01;
b = b > 1.0 ? 0.0 : b + 0.015;
return TRUE;
}
static void
draw_tex (gpointer data)
{
gst_gl_framebuffer_draw_to_texture (fbo, gl_tex,
(GstGLFramebufferFunc) clear_tex, data);
}
static void
_bind_buffer (GstGLContext * context)
{
const GstGLFuncs *gl = context->gl_vtable;
gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, vbo_indices);
gl->BindBuffer (GL_ARRAY_BUFFER, vbo);
/* Load the vertex position */
gl->VertexAttribPointer (shader_attr_position_loc, 3, GL_FLOAT, GL_FALSE,
5 * sizeof (GLfloat), (void *) 0);
/* Load the texture coordinate */
gl->VertexAttribPointer (shader_attr_texture_loc, 2, GL_FLOAT, GL_FALSE,
5 * sizeof (GLfloat), (void *) (3 * sizeof (GLfloat)));
gl->EnableVertexAttribArray (shader_attr_position_loc);
gl->EnableVertexAttribArray (shader_attr_texture_loc);
}
static void
_unbind_buffer (GstGLContext * context)
{
const GstGLFuncs *gl = context->gl_vtable;
gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);
gl->BindBuffer (GL_ARRAY_BUFFER, 0);
gl->DisableVertexAttribArray (shader_attr_position_loc);
gl->DisableVertexAttribArray (shader_attr_texture_loc);
}
static void
init_blit (gpointer data)
{
GstGLContext *context = data;
const GstGLFuncs *gl = context->gl_vtable;
if (!vbo) {
if (gl->GenVertexArrays) {
gl->GenVertexArrays (1, &vao);
gl->BindVertexArray (vao);
}
gl->GenBuffers (1, &vbo);
gl->BindBuffer (GL_ARRAY_BUFFER, vbo);
gl->BufferData (GL_ARRAY_BUFFER, 4 * 5 * sizeof (GLfloat), vertices,
GL_STATIC_DRAW);
gl->GenBuffers (1, &vbo_indices);
gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, vbo_indices);
gl->BufferData (GL_ELEMENT_ARRAY_BUFFER, sizeof (indices), indices,
GL_STATIC_DRAW);
if (gl->GenVertexArrays) {
_bind_buffer (context);
gl->BindVertexArray (0);
}
gl->BindBuffer (GL_ARRAY_BUFFER, 0);
gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);
}
}
static void
deinit_blit (gpointer data)
{
GstGLContext *context = data;
const GstGLFuncs *gl = context->gl_vtable;
if (vbo)
gl->DeleteBuffers (1, &vbo);
vbo = 0;
if (vbo_indices)
gl->DeleteBuffers (1, &vbo_indices);
vbo_indices = 0;
if (vao)
gl->DeleteVertexArrays (1, &vao);
vao = 0;
}
static void
draw_render (gpointer data)
{
GstGLContext *context = data;
GstGLContextClass *context_class = GST_GL_CONTEXT_GET_CLASS (context);
const GstGLFuncs *gl = context->gl_vtable;
gl->Clear (GL_COLOR_BUFFER_BIT);
gst_gl_shader_use (shader);
gl->ActiveTexture (GL_TEXTURE0);
gl->BindTexture (GL_TEXTURE_2D, gst_gl_memory_get_texture_id (gl_tex));
gst_gl_shader_set_uniform_1i (shader, "s_texture", 0);
if (gl->GenVertexArrays)
gl->BindVertexArray (vao);
else
_bind_buffer (context);
gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
if (gl->GenVertexArrays)
gl->BindVertexArray (0);
else
_unbind_buffer (context);
context_class->swap_buffers (context);
}
GST_START_TEST (test_share)
{
GstGLContext *context;
GstGLWindow *window;
GstGLContext *other_context;
GstGLWindow *other_window;
GError *error = NULL;
gint i = 0;
context = gst_gl_context_new (display);
window = gst_gl_window_new (display);
gst_gl_context_set_window (context, window);
gst_gl_context_create (context, 0, &error);
fail_if (error != NULL, "Error creating master context %s\n",
error ? error->message : "Unknown Error");
other_window = gst_gl_window_new (display);
other_context = gst_gl_context_new (display);
gst_gl_context_set_window (other_context, other_window);
gst_gl_context_create (other_context, context, &error);
fail_if (error != NULL, "Error creating secondary context %s\n",
error ? error->message : "Unknown Error");
/* make the window visible */
gst_gl_window_set_preferred_size (window, 320, 240);
gst_gl_window_draw (window);
gst_gl_window_send_message (other_window, GST_GL_WINDOW_CB (init),
other_context);
gst_gl_window_send_message (window, GST_GL_WINDOW_CB (init_blit), context);
while (i < 10) {
gst_gl_window_send_message (other_window, GST_GL_WINDOW_CB (draw_tex),
context);
gst_gl_window_send_message (window, GST_GL_WINDOW_CB (draw_render),
context);
i++;
}
gst_gl_window_send_message (other_window, GST_GL_WINDOW_CB (deinit),
other_context);
gst_gl_window_send_message (window, GST_GL_WINDOW_CB (deinit_blit), context);
gst_object_unref (window);
gst_object_unref (other_window);
gst_object_unref (other_context);
gst_object_unref (context);
}
GST_END_TEST;
static void
accum_true (GstGLContext * context, gpointer data)
{
gint *i = data;
*i = 1;
}
static void
check_wrapped (gpointer data)
{
GstGLContext *wrapped_context = data;
GError *error = NULL;
gint i = 0;
gboolean ret;
/* check that scheduling on an unactivated wrapped context asserts */
ASSERT_CRITICAL (gst_gl_context_thread_add (wrapped_context,
(GstGLContextThreadFunc) accum_true, &i));
fail_if (i != 0);
/* check that scheduling on an activated context succeeds */
gst_gl_context_activate (wrapped_context, TRUE);
gst_gl_context_thread_add (wrapped_context,
(GstGLContextThreadFunc) accum_true, &i);
fail_if (i != 1);
/* check filling out the wrapped context's info */
fail_if (wrapped_context->gl_vtable->TexImage2D != NULL);
ret = gst_gl_context_fill_info (wrapped_context, &error);
fail_if (!ret, "error received %s\n",
error ? error->message : "Unknown error");
fail_if (wrapped_context->gl_vtable->TexImage2D == NULL);
gst_gl_context_activate (wrapped_context, FALSE);
}
GST_START_TEST (test_wrapped_context)
{
GstGLContext *context, *other_context, *wrapped_context;
GstGLWindow *window, *other_window;
GError *error = NULL;
gint i = 0;
guintptr handle, handle2;
GstGLPlatform platform, platform2;
GstGLAPI apis, apis2;
context = gst_gl_context_new (display);
window = gst_gl_window_new (display);
gst_gl_context_set_window (context, window);
gst_gl_context_create (context, 0, &error);
fail_if (error != NULL, "Error creating master context %s\n",
error ? error->message : "Unknown Error");
handle = gst_gl_context_get_gl_context (context);
platform = gst_gl_context_get_gl_platform (context);
apis = gst_gl_context_get_gl_api (context);
wrapped_context =
gst_gl_context_new_wrapped (display, handle, platform, apis);
handle2 = gst_gl_context_get_gl_context (wrapped_context);
platform2 = gst_gl_context_get_gl_platform (wrapped_context);
apis2 = gst_gl_context_get_gl_api (wrapped_context);
fail_if (handle != handle2);
fail_if (platform != platform2);
fail_if (apis != apis2);
other_context = gst_gl_context_new (display);
other_window = gst_gl_window_new (display);
gst_gl_context_set_window (other_context, other_window);
gst_gl_context_create (other_context, wrapped_context, &error);
fail_if (error != NULL, "Error creating secondary context %s\n",
error ? error->message : "Unknown Error");
/* make the window visible */
gst_gl_window_set_preferred_size (window, 320, 240);
gst_gl_window_draw (window);
gst_gl_window_send_message (other_window, GST_GL_WINDOW_CB (init),
other_context);
gst_gl_window_send_message (window, GST_GL_WINDOW_CB (init_blit), context);
while (i < 10) {
gst_gl_window_send_message (other_window, GST_GL_WINDOW_CB (draw_tex),
context);
gst_gl_window_send_message (window, GST_GL_WINDOW_CB (draw_render),
context);
i++;
}
gst_gl_window_send_message (window, GST_GL_WINDOW_CB (check_wrapped),
wrapped_context);
gst_gl_window_send_message (other_window, GST_GL_WINDOW_CB (deinit),
other_context);
gst_gl_window_send_message (window, GST_GL_WINDOW_CB (deinit_blit), context);
gst_object_unref (other_context);
gst_object_unref (other_window);
gst_object_unref (window);
gst_object_unref (context);
gst_object_unref (wrapped_context);
}
GST_END_TEST;
struct context_info
{
GstGLAPI api;
guint major;
guint minor;
GstGLPlatform platform;
guintptr handle;
};
static void
_fill_context_info (GstGLContext * context, struct context_info *info)
{
info->handle = gst_gl_context_get_current_gl_context (info->platform);
info->api =
gst_gl_context_get_current_gl_api (info->platform, &info->major,
&info->minor);
}
GST_START_TEST (test_current_context)
{
GstGLContext *context;
GError *error = NULL;
guintptr handle;
GstGLPlatform platform;
GstGLAPI api;
gint major, minor;
struct context_info info;
context = gst_gl_context_new (display);
gst_gl_context_create (context, 0, &error);
fail_if (error != NULL, "Error creating master context %s\n",
error ? error->message : "Unknown Error");
handle = gst_gl_context_get_gl_context (context);
platform = gst_gl_context_get_gl_platform (context);
api = gst_gl_context_get_gl_api (context);
gst_gl_context_get_gl_version (context, &major, &minor);
info.platform = platform;
gst_gl_context_thread_add (context,
(GstGLContextThreadFunc) _fill_context_info, &info);
fail_if (info.platform != platform);
fail_if (info.api != api);
fail_if (info.major != major);
fail_if (info.minor != minor);
fail_if (info.handle != handle);
gst_object_unref (context);
}
GST_END_TEST;
GST_START_TEST (test_context_can_share)
{
GstGLContext *c1, *c2, *c3;
GError *error = NULL;
c1 = gst_gl_context_new (display);
gst_gl_context_create (c1, NULL, &error);
fail_if (error != NULL, "Error creating context %s\n",
error ? error->message : "Unknown Error");
c2 = gst_gl_context_new (display);
gst_gl_context_create (c2, c1, &error);
fail_if (error != NULL, "Error creating context %s\n",
error ? error->message : "Unknown Error");
fail_unless (gst_gl_context_can_share (c1, c2));
fail_unless (gst_gl_context_can_share (c2, c1));
c3 = gst_gl_context_new (display);
gst_gl_context_create (c3, c2, &error);
fail_if (error != NULL, "Error creating context %s\n",
error ? error->message : "Unknown Error");
fail_unless (gst_gl_context_can_share (c1, c3));
fail_unless (gst_gl_context_can_share (c3, c1));
fail_unless (gst_gl_context_can_share (c2, c3));
fail_unless (gst_gl_context_can_share (c3, c2));
/* destroy the middle context */
gst_object_unref (c2);
c2 = NULL;
fail_unless (gst_gl_context_can_share (c1, c3));
fail_unless (gst_gl_context_can_share (c3, c1));
gst_object_unref (c1);
gst_object_unref (c3);
}
GST_END_TEST;
GST_START_TEST (test_is_shared)
{
GstGLContext *c1, *c2;
GError *error = NULL;
c1 = gst_gl_context_new (display);
gst_gl_context_create (c1, NULL, &error);
fail_if (error != NULL, "Error creating context %s\n",
error ? error->message : "Unknown Error");
c2 = gst_gl_context_new (display);
gst_gl_context_create (c2, c1, &error);
fail_if (error != NULL, "Error creating context %s\n",
error ? error->message : "Unknown Error");
fail_unless (gst_gl_context_is_shared (c1));
fail_unless (gst_gl_context_is_shared (c2));
gst_object_unref (c2);
c2 = NULL;
fail_unless (!gst_gl_context_is_shared (c1));
gst_object_unref (c1);
}
GST_END_TEST;
GST_START_TEST (test_display_list)
{
GstGLContext *c1, *c2;
GError *error = NULL;
c1 = gst_gl_context_new (display);
gst_gl_context_create (c1, NULL, &error);
fail_if (error != NULL, "Error creating context %s\n",
error ? error->message : "Unknown Error");
GST_OBJECT_LOCK (display);
{
/* no context added so get should return NULL */
GstGLContext *tmp =
gst_gl_display_get_gl_context_for_thread (display, NULL);
fail_unless (tmp == NULL);
}
fail_unless (gst_gl_display_add_context (display, c1));
/* re-adding the same context is a no-op */
fail_unless (gst_gl_display_add_context (display, c1));
{
GThread *thread;
GstGLContext *tmp;
thread = gst_gl_context_get_thread (c1);
fail_unless (thread != NULL);
tmp = gst_gl_display_get_gl_context_for_thread (display, thread);
fail_unless (tmp == c1);
g_thread_unref (thread);
gst_object_unref (tmp);
tmp = gst_gl_display_get_gl_context_for_thread (display, NULL);
fail_unless (tmp == c1);
gst_object_unref (tmp);
}
c2 = gst_gl_context_new (display);
gst_gl_context_create (c2, c1, &error);
fail_if (error != NULL, "Error creating context %s\n",
error ? error->message : "Unknown Error");
fail_unless (gst_gl_display_add_context (display, c2));
/* re-adding the same context is a no-op */
fail_unless (gst_gl_display_add_context (display, c2));
{
GThread *thread;
GstGLContext *tmp;
thread = gst_gl_context_get_thread (c2);
fail_unless (thread != NULL);
tmp = gst_gl_display_get_gl_context_for_thread (display, thread);
fail_unless (tmp == c2);
g_thread_unref (thread);
gst_object_unref (tmp);
/* undefined which context will be returned for the NULL thread */
tmp = gst_gl_display_get_gl_context_for_thread (display, NULL);
fail_unless (tmp != NULL);
gst_object_unref (tmp);
}
gst_object_unref (c1);
/* c1 is now dead */
{
GstGLContext *tmp;
tmp = gst_gl_display_get_gl_context_for_thread (display, NULL);
fail_unless (tmp == c2);
gst_object_unref (tmp);
}
GST_OBJECT_UNLOCK (display);
gst_object_unref (c2);
/* c2 is now dead */
{
/* no more contexts alive */
GstGLContext *tmp =
gst_gl_display_get_gl_context_for_thread (display, NULL);
fail_unless (tmp == NULL);
}
}
GST_END_TEST;
static Suite *
gst_gl_context_suite (void)
{
Suite *s = suite_create ("GstGLContext");
TCase *tc_chain = tcase_create ("general");
suite_add_tcase (s, tc_chain);
tcase_add_checked_fixture (tc_chain, setup, teardown);
tcase_add_test (tc_chain, test_share);
tcase_add_test (tc_chain, test_wrapped_context);
tcase_add_test (tc_chain, test_current_context);
tcase_add_test (tc_chain, test_context_can_share);
tcase_add_test (tc_chain, test_is_shared);
tcase_add_test (tc_chain, test_display_list);
return s;
}
GST_CHECK_MAIN (gst_gl_context);