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. */ |
7 | static void |
8 | ensure_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 | |
27 | static void |
28 | texture_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 | |
37 | static void |
38 | texture_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 | |
58 | static void |
59 | texture_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 | |
114 | int |
115 | main (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 | |