diff --git a/demos/node-editor/node-editor-window.c b/demos/node-editor/node-editor-window.c index 4a22a0b9d8..995d2969f6 100644 --- a/demos/node-editor/node-editor-window.c +++ b/demos/node-editor/node-editor-window.c @@ -646,7 +646,6 @@ create_cairo_texture (NodeEditorWindow *self) GskRenderer *renderer; GskRenderNode *node; GdkTexture *texture; - GdkSurface *surface; paintable = gtk_picture_get_paintable (GTK_PICTURE (self->picture)); if (paintable == NULL || @@ -659,9 +658,8 @@ create_cairo_texture (NodeEditorWindow *self) if (node == NULL) return NULL; - surface = gtk_native_get_surface (gtk_widget_get_native (GTK_WIDGET (self))); renderer = gsk_cairo_renderer_new (); - gsk_renderer_realize (renderer, surface, NULL); + gsk_renderer_realize (renderer, NULL, NULL); texture = gsk_renderer_render_texture (renderer, node, NULL); gsk_render_node_unref (node); @@ -839,16 +837,18 @@ node_editor_window_add_renderer (NodeEditorWindow *self, GskRenderer *renderer, const char *description) { - GdkSurface *surface; GdkPaintable *paintable; - surface = gtk_native_get_surface (GTK_NATIVE (self)); - g_assert (surface != NULL); - - if (renderer != NULL && !gsk_renderer_realize (renderer, surface, NULL)) + if (!gsk_renderer_realize (renderer, NULL, NULL)) { - g_object_unref (renderer); - return; + GdkSurface *surface = gtk_native_get_surface (GTK_NATIVE (self)); + g_assert (surface != NULL); + + if (!gsk_renderer_realize (renderer, surface, NULL)) + { + g_object_unref (renderer); + return; + } } paintable = gtk_renderer_paintable_new (renderer, gtk_picture_get_paintable (GTK_PICTURE (self->picture))); diff --git a/gdk/gdkdisplay.c b/gdk/gdkdisplay.c index 075342a26b..d5defbeb82 100644 --- a/gdk/gdkdisplay.c +++ b/gdk/gdkdisplay.c @@ -1325,6 +1325,38 @@ gdk_display_prepare_gl (GdkDisplay *self, } } +/** + * gdk_display_create_gl_context: + * @self: a `GdkDisplay` + * @error: return location for an error + * + * Creates a new `GdkGLContext` for the `GdkDisplay`. + * + * The context is disconnected from any particular surface or surface + * and cannot be used to draw to any surface. It can only be used to + * draw to non-surface framebuffers like textures. + * + * If the creation of the `GdkGLContext` failed, @error will be set. + * Before using the returned `GdkGLContext`, you will need to + * call [method@Gdk.GLContext.make_current] or [method@Gdk.GLContext.realize]. + * + * Returns: (transfer full) (nullable): the newly created `GdkGLContext` + * + * Since: 4.6 + */ +GdkGLContext * +gdk_display_create_gl_context (GdkDisplay *self, + GError **error) +{ + g_return_val_if_fail (GDK_IS_DISPLAY (self), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + if (!gdk_display_prepare_gl (self, error)) + return NULL; + + return gdk_gl_context_new (self, NULL); +} + /*< private > * gdk_display_get_gl_context: * @self: the `GdkDisplay` diff --git a/gdk/gdkdisplay.h b/gdk/gdkdisplay.h index 9fabac6b50..52aa502247 100644 --- a/gdk/gdkdisplay.h +++ b/gdk/gdkdisplay.h @@ -71,6 +71,9 @@ gboolean gdk_display_supports_input_shapes (GdkDisplay *display); GDK_AVAILABLE_IN_4_4 gboolean gdk_display_prepare_gl (GdkDisplay *self, GError **error); +GDK_AVAILABLE_IN_4_6 +GdkGLContext *gdk_display_create_gl_context(GdkDisplay *self, + GError **error); GDK_AVAILABLE_IN_ALL GdkDisplay *gdk_display_get_default (void); diff --git a/gdk/gdkdrawcontext.c b/gdk/gdkdrawcontext.c index b51e1139dd..8ab405e25b 100644 --- a/gdk/gdkdrawcontext.c +++ b/gdk/gdkdrawcontext.c @@ -276,7 +276,8 @@ gdk_draw_context_get_surface (GdkDrawContext *context) /** * gdk_draw_context_begin_frame: - * @context: the `GdkDrawContext` used to draw the frame + * @context: the `GdkDrawContext` used to draw the frame. The context must + * have a surface. * @region: minimum region that should be drawn * * Indicates that you are beginning the process of redrawing @region @@ -308,7 +309,10 @@ void gdk_draw_context_begin_frame (GdkDrawContext *context, const cairo_region_t *region) { + GdkDrawContextPrivate *priv = gdk_draw_context_get_instance_private (context); + g_return_if_fail (GDK_IS_DRAW_CONTEXT (context)); + g_return_if_fail (priv->surface != NULL); g_return_if_fail (region != NULL); gdk_draw_context_begin_frame_full (context, FALSE, region); @@ -411,6 +415,7 @@ gdk_draw_context_end_frame (GdkDrawContext *context) GdkDrawContextPrivate *priv = gdk_draw_context_get_instance_private (context); g_return_if_fail (GDK_IS_DRAW_CONTEXT (context)); + g_return_if_fail (priv->surface != NULL); if (GDK_SURFACE_DESTROYED (priv->surface)) return; diff --git a/gdk/gdkglcontext.c b/gdk/gdkglcontext.c index 812a3cd9d8..422e0a189e 100644 --- a/gdk/gdkglcontext.c +++ b/gdk/gdkglcontext.c @@ -722,15 +722,19 @@ gdk_gl_context_init (GdkGLContext *self) /* Must have called gdk_display_prepare_gl() before */ GdkGLContext * -gdk_gl_context_new_for_surface (GdkSurface *surface) +gdk_gl_context_new (GdkDisplay *display, + GdkSurface *surface) { - GdkDisplay *display = gdk_surface_get_display (surface); - GdkGLContext *shared = gdk_display_get_gl_context (display); + GdkGLContext *shared; + + g_assert (surface == NULL || display == gdk_surface_get_display (surface)); /* assert gdk_display_prepare_gl() had been called */ + shared = gdk_display_get_gl_context (display); g_assert (shared); return g_object_new (G_OBJECT_TYPE (shared), + "display", display, "surface", surface, NULL); } diff --git a/gdk/gdkglcontextprivate.h b/gdk/gdkglcontextprivate.h index 56fe04f903..e07420f1c1 100644 --- a/gdk/gdkglcontextprivate.h +++ b/gdk/gdkglcontextprivate.h @@ -99,7 +99,8 @@ gboolean gdk_gl_backend_can_be_used (GdkGLBackend GError **error); void gdk_gl_backend_use (GdkGLBackend backend_type); -GdkGLContext * gdk_gl_context_new_for_surface (GdkSurface *surface); +GdkGLContext * gdk_gl_context_new (GdkDisplay *display, + GdkSurface *surface); gboolean gdk_gl_context_is_api_allowed (GdkGLContext *self, GdkGLAPI api, diff --git a/gdk/gdksurface.c b/gdk/gdksurface.c index 40d9c21b8f..5b0c947ed3 100644 --- a/gdk/gdksurface.c +++ b/gdk/gdksurface.c @@ -1171,7 +1171,7 @@ gdk_surface_get_paint_gl_context (GdkSurface *surface, * Before using the returned `GdkGLContext`, you will need to * call [method@Gdk.GLContext.make_current] or [method@Gdk.GLContext.realize]. * - * Returns: (transfer full) (nullable): the newly created `GdkGLContext` + * Returns: (transfer full): the newly created `GdkGLContext` */ GdkGLContext * gdk_surface_create_gl_context (GdkSurface *surface, @@ -1183,7 +1183,7 @@ gdk_surface_create_gl_context (GdkSurface *surface, if (!gdk_display_prepare_gl (surface->display, error)) return NULL; - return gdk_gl_context_new_for_surface (surface); + return gdk_gl_context_new (surface->display, surface); } /** diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c index 0b1e9eddda..b222e2b07a 100644 --- a/gsk/gl/gskglrenderer.c +++ b/gsk/gl/gskglrenderer.c @@ -90,12 +90,10 @@ gsk_gl_renderer_realize (GskRenderer *renderer, GskGLRenderer *self = (GskGLRenderer *)renderer; GdkGLContext *context = NULL; GskGLDriver *driver = NULL; + GdkDisplay *display; gboolean ret = FALSE; gboolean debug_shaders = FALSE; - g_assert (GSK_IS_GL_RENDERER (self)); - g_assert (GDK_IS_SURFACE (surface)); - if (self->context != NULL) return TRUE; @@ -103,8 +101,18 @@ gsk_gl_renderer_realize (GskRenderer *renderer, g_assert (self->context == NULL); g_assert (self->command_queue == NULL); - if (!(context = gdk_surface_create_gl_context (surface, error)) || - !gdk_gl_context_realize (context, error)) + if (surface == NULL) + { + display = gdk_display_get_default (); /* FIXME: allow different displays somehow ? */ + context = gdk_display_create_gl_context (display, error); + } + else + { + display = gdk_surface_get_display (surface); + context = gdk_surface_create_gl_context (surface, error); + } + + if (!context || !gdk_gl_context_realize (context, error)) goto failure; #ifdef G_ENABLE_DEBUG @@ -112,7 +120,7 @@ gsk_gl_renderer_realize (GskRenderer *renderer, debug_shaders = TRUE; #endif - if (!(driver = gsk_gl_driver_for_display (gdk_surface_get_display (surface), debug_shaders, error))) + if (!(driver = gsk_gl_driver_for_display (display, debug_shaders, error))) goto failure; self->command_queue = gsk_gl_driver_create_command_queue (driver, context); diff --git a/gsk/gskcairorenderer.c b/gsk/gskcairorenderer.c index 074b54b97f..3135acfdb1 100644 --- a/gsk/gskcairorenderer.c +++ b/gsk/gskcairorenderer.c @@ -59,7 +59,8 @@ gsk_cairo_renderer_realize (GskRenderer *renderer, { GskCairoRenderer *self = GSK_CAIRO_RENDERER (renderer); - self->cairo_context = gdk_surface_create_cairo_context (surface); + if (surface) + self->cairo_context = gdk_surface_create_cairo_context (surface); return TRUE; } diff --git a/gsk/gskrenderer.c b/gsk/gskrenderer.c index 7c35b051f9..ff4ff976ce 100644 --- a/gsk/gskrenderer.c +++ b/gsk/gskrenderer.c @@ -103,7 +103,7 @@ static GParamSpec *gsk_renderer_properties[N_PROPS]; static gboolean gsk_renderer_real_realize (GskRenderer *self, - GdkSurface *surface, + GdkSurface *surface, GError **error) { GSK_RENDERER_WARN_NOT_IMPLEMENTED_METHOD (self, realize); @@ -189,12 +189,12 @@ gsk_renderer_class_init (GskRendererClass *klass) /** * GskRenderer:realized: (attributes org.gtk.Property.get=gsk_renderer_is_realized) * - * Whether the renderer has been associated with a surface. + * Whether the renderer has been associated with a surface or draw context. */ gsk_renderer_properties[PROP_REALIZED] = g_param_spec_boolean ("realized", "Realized", - "The renderer has been associated with a surface", + "The renderer has been associated with a surface or draw context", FALSE, G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); @@ -281,11 +281,14 @@ gsk_renderer_is_realized (GskRenderer *renderer) /** * gsk_renderer_realize: * @renderer: a `GskRenderer` - * @surface: the `GdkSurface` renderer will be used on + * @surface: (nullable): the `GdkSurface` renderer will be used on * @error: return location for an error * * Creates the resources needed by the @renderer to render the scene * graph. + * + * Since GTK 4.6, the surface may be `NULL`, which allows using + * renderers without having to create a surface. */ gboolean gsk_renderer_realize (GskRenderer *renderer, @@ -296,10 +299,11 @@ gsk_renderer_realize (GskRenderer *renderer, g_return_val_if_fail (GSK_IS_RENDERER (renderer), FALSE); g_return_val_if_fail (!gsk_renderer_is_realized (renderer), FALSE); - g_return_val_if_fail (GDK_IS_SURFACE (surface), FALSE); + g_return_val_if_fail (surface == NULL || GDK_IS_SURFACE (surface), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - priv->surface = g_object_ref (surface); + if (surface) + priv->surface = g_object_ref (surface); if (!GSK_RENDERER_GET_CLASS (renderer)->realize (renderer, surface, error)) { @@ -399,13 +403,15 @@ gsk_renderer_render_texture (GskRenderer *renderer, /** * gsk_renderer_render: - * @renderer: a `GskRenderer` + * @renderer: a realized `GskRenderer` * @root: a `GskRenderNode` * @region: (nullable): the `cairo_region_t` that must be redrawn or %NULL * for the whole window * - * Renders the scene graph, described by a tree of `GskRenderNode` instances, - * ensuring that the given @region gets redrawn. + * Renders the scene graph, described by a tree of `GskRenderNode` instances + * to the renderer's surface, ensuring that the given @region gets redrawn. + * + * If the renderer has no associated surface, this function does nothing. * * Renderers must ensure that changes of the contents given by the @root * node as well as the area given by @region are redrawn. They are however @@ -428,6 +434,9 @@ gsk_renderer_render (GskRenderer *renderer, g_return_if_fail (GSK_IS_RENDER_NODE (root)); g_return_if_fail (priv->root_node == NULL); + if (priv->surface == NULL) + return; + if (region == NULL || priv->prev_node == NULL || GSK_RENDERER_DEBUG_CHECK (renderer, FULL_REDRAW)) { clip = cairo_region_create_rectangle (&(GdkRectangle) { diff --git a/gsk/vulkan/gskvulkanrenderer.c b/gsk/vulkan/gskvulkanrenderer.c index c91b14a384..86f7984805 100644 --- a/gsk/vulkan/gskvulkanrenderer.c +++ b/gsk/vulkan/gskvulkanrenderer.c @@ -114,12 +114,19 @@ gsk_vulkan_renderer_update_images_cb (GdkVulkanContext *context, static gboolean gsk_vulkan_renderer_realize (GskRenderer *renderer, - GdkSurface *window, + GdkSurface *surface, GError **error) { GskVulkanRenderer *self = GSK_VULKAN_RENDERER (renderer); - self->vulkan = gdk_surface_create_vulkan_context (window, error); + if (surface == NULL) + { + g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_UNSUPPORTED, + "The Vulkan renderer does not support surfaceless rendering yet."); + return FALSE; + } + + self->vulkan = gdk_surface_create_vulkan_context (surface, error); if (self->vulkan == NULL) return FALSE; diff --git a/testsuite/gdk/memorytexture.c b/testsuite/gdk/memorytexture.c index 03f8634c3f..712ff951cb 100644 --- a/testsuite/gdk/memorytexture.c +++ b/testsuite/gdk/memorytexture.c @@ -765,7 +765,6 @@ add_test (const char *name, int main (int argc, char *argv[]) { - GdkSurface *surface; int result; gtk_test_init (&argc, &argv, NULL); @@ -776,12 +775,10 @@ main (int argc, char *argv[]) add_test ("/memorytexture/download_float_1x1", test_download_float_1x1); add_test ("/memorytexture/download_float_4x4", test_download_float_4x4); - surface = gdk_surface_new_toplevel (gdk_display_get_default()); gl_renderer = gsk_gl_renderer_new (); - if (!gsk_renderer_realize (gl_renderer, surface, NULL)) + if (!gsk_renderer_realize (gl_renderer, NULL, NULL)) { g_clear_object (&gl_renderer); - g_clear_object (&surface); } result = g_test_run (); @@ -791,7 +788,6 @@ main (int argc, char *argv[]) gsk_renderer_unrealize (gl_renderer); g_clear_object (&gl_renderer); } - g_clear_object (&surface); return result; } diff --git a/testsuite/gdk/texture-threads.c b/testsuite/gdk/texture-threads.c index 5ca16e0ec5..17236070cf 100644 --- a/testsuite/gdk/texture-threads.c +++ b/testsuite/gdk/texture-threads.c @@ -64,7 +64,6 @@ texture_download_thread (GTask *task, static void texture_threads (void) { - GdkSurface *surface; GskRenderer *gl_renderer; GskRenderNode *node; GMainLoop *loop; @@ -73,15 +72,13 @@ texture_threads (void) GError *error = NULL; /* 1. Get a GL renderer */ - surface = gdk_surface_new_toplevel (gdk_display_get_default()); gl_renderer = gsk_gl_renderer_new (); - if (!gsk_renderer_realize (gl_renderer, surface, &error)) + if (!gsk_renderer_realize (gl_renderer, NULL, &error)) { g_test_skip (error->message); g_clear_error (&error); g_clear_object (&gl_renderer); - g_clear_object (&surface); return; } @@ -116,7 +113,6 @@ texture_threads (void) gsk_renderer_unrealize (gl_renderer); g_clear_pointer (&loop, g_main_loop_unref); g_clear_object (&gl_renderer); - g_clear_object (&surface); g_main_context_release (NULL); }