gstreamer/tests/check/libs/gstglcontext.c
Sebastian Dröge acc098a736 gl: Only unbind buffers/vertex attrib arrays if we can't directly bind the vertex array to 0
Binding the vertex array to 0 will unbind everything else already.

In the previous order older versions of the Intel GL driver caused
errors to be printed for every single call when disabling the vertex
attrib arrays after binding the vertex array to 0.
2019-01-16 14:09:18 +02:00

664 lines
18 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/gl.h>
#include <gst/gl/gstglfuncs.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, *gl_tex2;
static GLuint vbo, vbo_indices, vao;
static GstGLFramebuffer *fbo, *fbo2;
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_GL_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);
gl_tex2 =
(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));
gst_memory_unref (GST_MEMORY_CAST (gl_tex2));
}
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);
}
/* has to be called in the thread that is going to use the framebuffer */
fbo2 = gst_gl_framebuffer_new_with_default_depth (context, 320, 240);
fail_if (fbo2 == NULL, "failed to create framebuffer object");
}
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;
gst_object_unref (fbo2);
fbo2 = NULL;
}
static gboolean
blit_tex (gpointer data)
{
GstGLContext *context = data;
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);
_bind_buffer (context);
gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
if (gl->GenVertexArrays)
gl->BindVertexArray (0);
else
_unbind_buffer (context);
return TRUE;
}
static void
draw_render (gpointer data)
{
gst_gl_framebuffer_draw_to_texture (fbo2, gl_tex2,
(GstGLFramebufferFunc) blit_tex, data);
}
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");
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);