1#include <gtk/gtk.h>
2
3#include "gsk/gl/gskglrenderer.h"
4
5/* This function will be called from a thread and/or the main loop.
6 * Textures are threadsafe after all. */
7static void
8ensure_texture_access (GdkTexture *texture)
9{
10 /* Make sure to initialize the pixel to anything but red */
11 guint32 pixel = 0;
12
13 g_test_message (format: "Checking texture access in thread %p...", g_thread_self());
14 /* Just to be sure */
15 g_assert_cmpint (gdk_texture_get_width (texture), ==, 1);
16 g_assert_cmpint (gdk_texture_get_height (texture), ==, 1);
17
18 /* download the pixel */
19 gdk_texture_download (texture, data: (guchar *) &pixel, stride: 4);
20
21 /* check the pixel is now red */
22 g_assert_cmphex (pixel, ==, 0xFFFF0000);
23
24 g_test_message (format: "...done in thread %p", g_thread_self());
25}
26
27static void
28texture_download_done (GObject *texture,
29 GAsyncResult *res,
30 gpointer loop)
31{
32 ensure_texture_access (GDK_TEXTURE (texture));
33
34 g_main_loop_quit (loop);
35}
36
37static void
38texture_download_thread (GTask *task,
39 gpointer texture,
40 gpointer unused,
41 GCancellable *cancellable)
42{
43 g_test_message (format: "Starting thread %p.", g_thread_self());
44 /* not sure this can happen, but if it does, we
45 * should clear_current() here. */
46 g_assert_null (gdk_gl_context_get_current ());
47
48 ensure_texture_access (GDK_TEXTURE (texture));
49
50 /* Makes sure the GL context is still NULL, because all the
51 * GL stuff should have happened in the main thread. */
52 g_assert_null (gdk_gl_context_get_current ());
53 g_test_message (format: "Returning from thread %p.", g_thread_self());
54
55 g_task_return_boolean (task, TRUE);
56}
57
58static void
59texture_threads (void)
60{
61 GskRenderer *gl_renderer;
62 GskRenderNode *node;
63 GMainLoop *loop;
64 GdkTexture *texture;
65 GTask *task;
66 GError *error = NULL;
67
68 /* 1. Get a GL renderer */
69 gl_renderer = gsk_gl_renderer_new ();
70 if (!gsk_renderer_realize (renderer: gl_renderer, NULL, error: &error))
71 {
72 g_test_skip (msg: error->message);
73
74 g_clear_error (err: &error);
75 g_clear_object (&gl_renderer);
76 return;
77 }
78
79 /* 2. Get a GL texture */
80 node = gsk_color_node_new (rgba: &(GdkRGBA) { 1, 0, 0, 1 }, bounds: &GRAPHENE_RECT_INIT(0, 0, 1, 1));
81 texture = gsk_renderer_render_texture (renderer: gl_renderer, root: node, viewport: &GRAPHENE_RECT_INIT(0, 0, 1, 1));
82 gsk_render_node_unref (node);
83
84 /* 3. This is a bit fishy, but we want to make sure that
85 * the texture's GL context is current in the main thread.
86 *
87 * If we had access to the context, we'd make_current() here.
88 */
89 ensure_texture_access (texture);
90 g_assert_nonnull (gdk_gl_context_get_current ());
91
92 /* 4. Acquire the main loop, so the run_in_thread() doesn't
93 * try to acquire it if it manages to outrace this thread.
94 */
95 g_assert_true (g_main_context_acquire (NULL));
96
97 /* 5. Run a thread trying to download the texture */
98 loop = g_main_loop_new (NULL, TRUE);
99 task = g_task_new (source_object: texture, NULL, callback: texture_download_done, callback_data: loop);
100 g_task_run_in_thread (task, task_func: texture_download_thread);
101 g_clear_object (&task);
102
103 /* 6. Run the main loop waiting for the thread to return */
104 g_main_loop_run (loop);
105
106 /* 7. All good */
107 gsk_renderer_unrealize (renderer: gl_renderer);
108 g_clear_pointer (&loop, g_main_loop_unref);
109 g_clear_object (&gl_renderer);
110 g_main_context_release (NULL);
111 gdk_gl_context_clear_current ();
112}
113
114int
115main (int argc, char *argv[])
116{
117 gtk_test_init (argcp: &argc, argvp: &argv, NULL);
118
119 g_test_add_func (testpath: "/texture-threads", test_func: texture_threads);
120
121 return g_test_run ();
122}
123

source code of gtk/testsuite/gdk/texture-threads.c