Bitmap: use thread local GdkDisplay to fix glitches on X11

GdkDisplay is not thread safe on X11. We can't even use the default
display from main thread, because a GTK render thread may run concurrently.
This commit is contained in:
Julian Winkler
2025-10-24 11:36:25 +02:00
parent 162ce00ba4
commit 7941a04ab3

View File

@@ -19,14 +19,20 @@ JNIEXPORT jlong JNICALL Java_android_graphics_Bitmap_native_1create_1snapshot(JN
JNIEXPORT jlong JNICALL Java_android_graphics_Bitmap_native_1create_1texture(JNIEnv *env, jclass class, jlong snapshot_ptr, jint width, jint height, jint stride, jint format) JNIEXPORT jlong JNICALL Java_android_graphics_Bitmap_native_1create_1texture(JNIEnv *env, jclass class, jlong snapshot_ptr, jint width, jint height, jint stride, jint format)
{ {
static GType renderer_type = 0; static GType renderer_type = 0;
/* GdkDisplay is not thread safe on X11. We can't even use the default
* display from main thread, because a GTK render thread may run concurrently.*/
static GPrivate off_screen_display_thread_local = G_PRIVATE_INIT(g_object_unref);
GtkSnapshot *snapshot = _PTR(snapshot_ptr); GtkSnapshot *snapshot = _PTR(snapshot_ptr);
GskRenderNode *node = snapshot ? gtk_snapshot_free_to_node(snapshot) : NULL; GskRenderNode *node = snapshot ? gtk_snapshot_free_to_node(snapshot) : NULL;
GdkTexture *texture = NULL; GdkTexture *texture = NULL;
if (node) { if (node) {
graphene_rect_t bounds = GRAPHENE_RECT_INIT(0, 0, width, height); graphene_rect_t bounds = GRAPHENE_RECT_INIT(0, 0, width, height);
GdkDisplay *off_screen_display = g_private_get(&off_screen_display_thread_local);
if (!off_screen_display)
g_private_set(&off_screen_display_thread_local, off_screen_display = gdk_display_open(NULL));
if (!renderer_type) { if (!renderer_type) {
// Use same renderer type as for onscreen rendering. // Create and destroy a dummy surface to get the renderer type
GdkSurface *surface = gdk_surface_new_toplevel(gdk_display_get_default()); GdkSurface *surface = gdk_surface_new_toplevel(off_screen_display);
GskRenderer *renderer = gsk_renderer_new_for_surface(surface); GskRenderer *renderer = gsk_renderer_new_for_surface(surface);
renderer_type = G_OBJECT_TYPE(renderer); renderer_type = G_OBJECT_TYPE(renderer);
gsk_renderer_unrealize(renderer); gsk_renderer_unrealize(renderer);
@@ -34,7 +40,7 @@ JNIEXPORT jlong JNICALL Java_android_graphics_Bitmap_native_1create_1texture(JNI
gdk_surface_destroy(surface); gdk_surface_destroy(surface);
} }
GskRenderer *renderer = g_object_new(renderer_type, NULL); GskRenderer *renderer = g_object_new(renderer_type, NULL);
gsk_renderer_realize(renderer, NULL, NULL); gsk_renderer_realize_for_display(renderer, off_screen_display, NULL);
texture = gsk_renderer_render_texture(renderer, node, &bounds); texture = gsk_renderer_render_texture(renderer, node, &bounds);
gsk_render_node_unref(node); gsk_render_node_unref(node);
gsk_renderer_unrealize(renderer); gsk_renderer_unrealize(renderer);