From a8656215195ad3403f8eda727a9116194351ac96 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Mon, 23 Apr 2018 17:28:55 +0200 Subject: [PATCH] gdk: Move begin/end_frame() functions As they require a draw context and the draw context is already bound to the surface, it makes much more sense and reduces abiguity by moving these APIs to the draw context. As a side effect, we simplify GdkSurface APIs to a point where GdkSurface now does not concern itself with drawing anymore at all, apart from being the object that creates draw contexts. --- docs/reference/gdk/gdk4-sections.txt | 6 +- gdk/gdkcairocontext.c | 6 +- gdk/gdkdrawcontext.c | 170 ++++++++++++++++++--------- gdk/gdkdrawcontext.h | 11 +- gdk/gdkdrawcontextprivate.h | 5 - gdk/gdkglcontext.c | 4 +- gdk/gdksurface.c | 101 ---------------- gdk/gdksurface.h | 7 -- gdk/gdkvulkancontext.c | 10 +- gsk/gl/gskglrenderer.c | 5 +- gsk/gskbroadwayrenderer.c | 4 +- gsk/gskcairorenderer.c | 8 +- gsk/vulkan/gskvulkanrenderer.c | 9 +- 13 files changed, 142 insertions(+), 204 deletions(-) diff --git a/docs/reference/gdk/gdk4-sections.txt b/docs/reference/gdk/gdk4-sections.txt index cc56a592b9..18ba178c77 100644 --- a/docs/reference/gdk/gdk4-sections.txt +++ b/docs/reference/gdk/gdk4-sections.txt @@ -233,10 +233,6 @@ gdk_surface_set_opaque_region gdk_surface_create_gl_context gdk_surface_create_vulkan_context - -gdk_surface_begin_draw_frame -gdk_surface_end_draw_frame - gdk_surface_invalidate_rect gdk_surface_invalidate_region @@ -1106,6 +1102,8 @@ gdk_frame_timings_get_type GdkDrawContext gdk_draw_context_get_display gdk_draw_context_get_surface +gdk_draw_context_begin_frame +gdk_draw_context_end_frame gdk_draw_context_get_frame_region diff --git a/gdk/gdkcairocontext.c b/gdk/gdkcairocontext.c index 9308af8a67..34d3c7fa17 100644 --- a/gdk/gdkcairocontext.c +++ b/gdk/gdkcairocontext.c @@ -196,7 +196,7 @@ gdk_cairo_context_end_frame (GdkDrawContext *draw_context, surface = gdk_draw_context_get_surface (draw_context); if (surface->current_paint.surface == NULL) { - g_warning (G_STRLOC": no preceding call to gdk_surface_begin_draw_frame(), see documentation"); + g_warning (G_STRLOC": no preceding call to gdk_draw_context_end_frame(), see documentation"); return; } @@ -255,11 +255,11 @@ gdk_cairo_context_init (GdkCairoContext *self) * @context: a #GdkCairoContext that is currently drawing * * Retrieves a Cairo context to be used to draw on the #GdkSurface - * of @context. A call to gdk_surface_begin_draw_frame() with this + * of @context. A call to gdk_draw_context_begin_frame() with this * @context must have been done or this function will return %NULL. * * The returned context is guaranteed to be valid until - * gdk_surface_end_draw_frame() is called. + * gdk_draw_context_end_frame() is called. * * Returns: (transfer full) (nullable): a Cairo context to be used * to draw the contents of the #GdkSurface. %NULL is returned diff --git a/gdk/gdkdrawcontext.c b/gdk/gdkdrawcontext.c index 45a353b086..dcedc540ca 100644 --- a/gdk/gdkdrawcontext.c +++ b/gdk/gdkdrawcontext.c @@ -197,63 +197,6 @@ gdk_draw_context_is_drawing (GdkDrawContext *context) return priv->is_drawing; } -/*< private > - * gdk_draw_context_begin_frame: - * @context: a #GdkDrawContext - * @region: (inout): The clip region that needs to be repainted - * - * Sets up @context and @drawing for a new drawing. - * - * The @context is free to update @region to the size that actually needs to - * be repainted. Contexts that do not support partial blits for example may - * want to invalidate the whole surface instead. - * - * The function does not clear the background. Clearing the backgroud is the - * job of the renderer. The contents of the backbuffer are undefined after this - * function call. - */ -void -gdk_draw_context_begin_frame (GdkDrawContext *context, - cairo_region_t *region) -{ - GdkDrawContextPrivate *priv; - - g_return_if_fail (GDK_IS_DRAW_CONTEXT (context)); - g_return_if_fail (region != NULL); - - priv = gdk_draw_context_get_instance_private (context); - priv->is_drawing = TRUE; - - GDK_DRAW_CONTEXT_GET_CLASS (context)->begin_frame (context, region); -} - -/*< private > - * gdk_draw_context_end_frame: - * @context: a #GdkDrawContext - * @painted: The area that has been redrawn this frame - * @damage: The area that we know is actually different from the last frame - * - * Copies the back buffer to the front buffer. - * - * This function may call `glFlush()` implicitly before returning; it - * is not recommended to call `glFlush()` explicitly before calling - * this function. - */ -void -gdk_draw_context_end_frame (GdkDrawContext *context, - cairo_region_t *painted, - cairo_region_t *damage) -{ - GdkDrawContextPrivate *priv; - - g_return_if_fail (GDK_IS_DRAW_CONTEXT (context)); - - GDK_DRAW_CONTEXT_GET_CLASS (context)->end_frame (context, painted, damage); - - priv = gdk_draw_context_get_instance_private (context); - priv->is_drawing = FALSE; -} - /*< private > * gdk_draw_context_surface_resized: * @context: a #GdkDrawContext @@ -303,6 +246,119 @@ gdk_draw_context_get_surface (GdkDrawContext *context) return priv->surface; } +/** + * gdk_draw_context_begin_frame: + * @context: the context used to draw the frame + * @region: minimum region that should be drawn + * + * Indicates that you are beginning the process of redrawing @region + * on the @context's surface. + * + * Calling this function begins a drawing operation using @context on the + * surface that @context was created from. The actual requirements and + * guarantees for the drawing operation vary for different implementations + * of drawing, so a #GdkCairoContext and a #GdkGLContext need to be treated + * differently. + * + * A call to this function is a requirement for drawing and must be followed + * by a call to gdk_draw_context_end_frame(), which will complete the + * drawing operation and ensure the contents become visible on screen. + * + * Note that the @region passed to this function is the minimum region that + * needs to be drawn and depending on implementation, windowing system and + * hardware in use, it might be necessary to draw a larger region. Drawing + * implementation must use gdk_draw_context_get_frame_region() to query the + * region that must be drawn. + * + * When using GTK+, the widget system automatically places calls to + * gdk_draw_context_begin_frame() and gdk_draw_context_end_frame() via the + * use of #GskRenderers, so application code does not need to call these + * functions explicitly. + */ +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 (region != NULL); + + if (GDK_SURFACE_DESTROYED (priv->surface)) + return; + + if (priv->surface->paint_context != NULL) + { + if (priv->surface->paint_context == context) + { + g_critical ("The surface %p is already drawing. You must finish the " + "previous drawing operation with gdk_draw_context_end_frame() first.", + priv->surface); + } + else + { + g_critical ("The surface %p is already being drawn by %s %p. " + "You cannot draw s surface wih multiple contexts at the same time.", + priv->surface, + G_OBJECT_TYPE_NAME (priv->surface->paint_context), priv->surface->paint_context); + } + return; + } + + context->frame_region = cairo_region_copy (region); + priv->is_drawing = TRUE; + priv->surface->paint_context = g_object_ref (context); + + GDK_DRAW_CONTEXT_GET_CLASS (context)->begin_frame (context, context->frame_region); +} + +/** + * gdk_draw_context_end_frame: + * @context: a #GdkDrawContext + * + * Ends a drawing operation started with gdk_draw_context_begin_frame() + * and makes the drawing available on screen. See that function for more + * details about drawing. + * + * When using a #GdkGLContext, this function may call `glFlush()` + * implicitly before returning; it is not recommended to call `glFlush()` + * explicitly before calling this function. + */ +void +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)); + + if (GDK_SURFACE_DESTROYED (priv->surface)) + return; + + if (priv->surface->paint_context == NULL) + { + g_critical ("The surface %p has no drawing context. You must call " + "gdk_draw_context_begin_frame() before calling " + "gdk_draw_context_end_frame().", priv->surface); + return; + } + else if (priv->surface->paint_context != context) + { + g_critical ("The surface %p is not drawn by this context but by %s %p.", + priv->surface, + G_OBJECT_TYPE_NAME (priv->surface->paint_context), priv->surface->paint_context); + return; + } + + GDK_DRAW_CONTEXT_GET_CLASS (context)->end_frame (context, + context->frame_region, + priv->surface->active_update_area); + + + priv->is_drawing = FALSE; + g_clear_pointer (&context->frame_region, cairo_region_destroy); + g_clear_object (&priv->surface->paint_context); +} + /** * gdk_draw_context_get_frame_region: * @context: a #GdkDrawContext diff --git a/gdk/gdkdrawcontext.h b/gdk/gdkdrawcontext.h index 5b542667cd..4fdc2898d7 100644 --- a/gdk/gdkdrawcontext.h +++ b/gdk/gdkdrawcontext.h @@ -38,12 +38,17 @@ GDK_AVAILABLE_IN_ALL GType gdk_draw_context_get_type (void) G_GNUC_CONST; GDK_AVAILABLE_IN_ALL -GdkDisplay * gdk_draw_context_get_display (GdkDrawContext *context); +GdkDisplay * gdk_draw_context_get_display (GdkDrawContext *context); GDK_AVAILABLE_IN_ALL -GdkSurface * gdk_draw_context_get_surface (GdkDrawContext *context); +GdkSurface * gdk_draw_context_get_surface (GdkDrawContext *context); GDK_AVAILABLE_IN_ALL -const cairo_region_t * gdk_draw_context_get_frame_region (GdkDrawContext *context); +void gdk_draw_context_begin_frame (GdkDrawContext *context, + const cairo_region_t *region); +GDK_AVAILABLE_IN_ALL +void gdk_draw_context_end_frame (GdkDrawContext *context); +GDK_AVAILABLE_IN_ALL +const cairo_region_t * gdk_draw_context_get_frame_region (GdkDrawContext *context); G_END_DECLS diff --git a/gdk/gdkdrawcontextprivate.h b/gdk/gdkdrawcontextprivate.h index 242919698f..39f3b96631 100644 --- a/gdk/gdkdrawcontextprivate.h +++ b/gdk/gdkdrawcontextprivate.h @@ -51,11 +51,6 @@ struct _GdkDrawContextClass }; gboolean gdk_draw_context_is_drawing (GdkDrawContext *context); -void gdk_draw_context_begin_frame (GdkDrawContext *context, - cairo_region_t *region); -void gdk_draw_context_end_frame (GdkDrawContext *context, - cairo_region_t *painted, - cairo_region_t *damage); void gdk_draw_context_surface_resized (GdkDrawContext *context); diff --git a/gdk/gdkglcontext.c b/gdk/gdkglcontext.c index d60f8e2b4c..0b9f93d795 100644 --- a/gdk/gdkglcontext.c +++ b/gdk/gdkglcontext.c @@ -301,7 +301,7 @@ gdk_gl_context_real_begin_frame (GdkDrawContext *draw_context, shared = gdk_gl_context_get_shared_context (context); if (shared) { - gdk_draw_context_begin_frame (GDK_DRAW_CONTEXT (shared), region); + GDK_DRAW_CONTEXT_GET_CLASS (GDK_DRAW_CONTEXT (shared))->begin_frame (GDK_DRAW_CONTEXT (shared), region); return; } @@ -335,7 +335,7 @@ gdk_gl_context_real_end_frame (GdkDrawContext *draw_context, shared = gdk_gl_context_get_shared_context (context); if (shared) { - gdk_draw_context_end_frame (GDK_DRAW_CONTEXT (shared), painted, damage); + GDK_DRAW_CONTEXT_GET_CLASS (GDK_DRAW_CONTEXT (shared))->end_frame (GDK_DRAW_CONTEXT (shared), painted, damage); return; } diff --git a/gdk/gdksurface.c b/gdk/gdksurface.c index b4e0d26493..f5234bcc33 100644 --- a/gdk/gdksurface.c +++ b/gdk/gdksurface.c @@ -1541,107 +1541,6 @@ gdk_surface_create_vulkan_context (GdkSurface *surface, NULL); } -/** - * gdk_surface_begin_draw_frame: - * @surface: a #GdkSurface - * @context: (allow-none): the context used to draw the frame - * @region: a Cairo region - * - * Indicates that you are beginning the process of redrawing @region - * on @surface. - * - * If @surface is a top level #GdkSurface, backed by a native surface - * implementation, a backing store (offscreen buffer) large enough to - * contain @region will be created. The backing store will be initialized - * with the background color or background surface for @surface. Then, all - * drawing operations performed on @surface will be diverted to the - * backing store. When you call gdk_surface_end_frame(), the contents of - * the backing store will be copied to @surface, making it visible - * on screen. Only the part of @surface contained in @region will be - * modified; that is, drawing operations are clipped to @region. - * - * The net result of all this is to remove flicker, because the user - * sees the finished product appear all at once when you call - * gdk_surface_end_draw_frame(). If you draw to @surface directly without - * calling gdk_surface_begin_draw_frame(), the user may see flicker - * as individual drawing operations are performed in sequence. - * - * When using GTK+, the widget system automatically places calls to - * gdk_surface_begin_draw_frame() and gdk_surface_end_draw_frame() around - * emissions of the `GtkWidget::draw` signal. That is, if you’re - * drawing the contents of the widget yourself, you can assume that the - * widget has a cleared background, is already set as the clip region, - * and already has a backing store. Therefore in most cases, application - * code in GTK does not need to call gdk_surface_begin_draw_frame() - * explicitly. - */ -void -gdk_surface_begin_draw_frame (GdkSurface *surface, - GdkDrawContext *draw_context, - const cairo_region_t *region) -{ - g_return_if_fail (GDK_IS_SURFACE (surface)); - g_return_if_fail (gdk_surface_has_native (surface)); - g_return_if_fail (gdk_surface_is_toplevel (surface)); - g_return_if_fail (GDK_IS_DRAW_CONTEXT (draw_context)); - g_return_if_fail (gdk_draw_context_get_surface (draw_context) == surface); - g_return_if_fail (region != NULL); - - if (GDK_SURFACE_DESTROYED (surface)) - return; - - if (surface->paint_context != NULL) - { - g_critical ("The surface %p already has a drawing context. You cannot " - "call gdk_surface_begin_draw_frame() without calling " - "gdk_surface_end_draw_frame() first.", surface); - return; - } - - draw_context->frame_region = cairo_region_copy (region); - - gdk_draw_context_begin_frame (draw_context, draw_context->frame_region); - - surface->paint_context = g_object_ref (draw_context); -} - -/** - * gdk_surface_end_draw_frame: - * @surface: a #GdkSurface - * - * Indicates that the drawing of the contents of @surface started with - * gdk_surface_begin_frame() has been completed. - * - * It is an error to call this function without a matching - * gdk_surface_begin_frame() first. - */ -void -gdk_surface_end_draw_frame (GdkSurface *surface) -{ - GdkDrawContext *paint_context; - - g_return_if_fail (GDK_IS_SURFACE (surface)); - - if (GDK_SURFACE_DESTROYED (surface)) - return; - - if (surface->paint_context == NULL) - { - g_critical ("The surface %p has no drawing context. You must call " - "gdk_surface_begin_draw_frame() before calling " - "gdk_surface_end_draw_frame().", surface); - return; - } - - paint_context = g_steal_pointer (&surface->paint_context); - gdk_draw_context_end_frame (paint_context, - paint_context->frame_region, - surface->active_update_area); - - g_clear_pointer (&paint_context->frame_region, cairo_region_destroy); - g_object_unref (paint_context); -} - /*< private > * gdk_surface_get_current_paint_region: * @surface: a #GdkSurface diff --git a/gdk/gdksurface.h b/gdk/gdksurface.h index c0709d1dda..ddae4f2128 100644 --- a/gdk/gdksurface.h +++ b/gdk/gdksurface.h @@ -575,13 +575,6 @@ void gdk_surface_set_geometry_hints (GdkSurface *surface, const GdkGeometry *geometry, GdkSurfaceHints geom_mask); -GDK_AVAILABLE_IN_ALL -void gdk_surface_begin_draw_frame (GdkSurface *surface, - GdkDrawContext *context, - const cairo_region_t *region); -GDK_AVAILABLE_IN_ALL -void gdk_surface_end_draw_frame (GdkSurface *surface); - GDK_AVAILABLE_IN_ALL void gdk_surface_set_title (GdkSurface *surface, const gchar *title); diff --git a/gdk/gdkvulkancontext.c b/gdk/gdkvulkancontext.c index ef5f0d808f..dae1de9632 100644 --- a/gdk/gdkvulkancontext.c +++ b/gdk/gdkvulkancontext.c @@ -707,9 +707,8 @@ gdk_vulkan_context_get_image (GdkVulkanContext *context, * * Gets the index of the image that is currently being drawn. * - * This function can only be used between gdk_surface_begin_draw_frame() and - * gdk_surface_end_draw_frame() calls for the toplevel surface that the - * @context is associated with. + * This function can only be used between gdk_cairo_context_begin_frame() and + * gdk_draw_context_end_frame() calls. * * Returns: the index of the images that is being drawn */ @@ -731,9 +730,8 @@ gdk_vulkan_context_get_draw_index (GdkVulkanContext *context) * Gets the Vulkan semaphore that protects access to the image that is * currently being drawn. * - * This function can only be used between gdk_surface_begin_draw_frame() and - * gdk_surface_end_draw_frame() calls for the toplevel surface that the - * @context is associated with. + * This function can only be used between gdk_cairo_context_begin_frame() and + * gdk_draw_context_end_frame() calls. * * Returns: (transfer none): the VkSemaphore */ diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c index e76c1f8eed..2a11cb53a5 100644 --- a/gsk/gl/gskglrenderer.c +++ b/gsk/gl/gskglrenderer.c @@ -2577,8 +2577,7 @@ gsk_gl_renderer_render (GskRenderer *renderer, gdk_surface_get_height (surface) * self->scale_factor }; - gdk_surface_begin_draw_frame (surface, - GDK_DRAW_CONTEXT (self->gl_context), + gdk_draw_context_begin_frame (GDK_DRAW_CONTEXT (self->gl_context), update_area); damage = gdk_draw_context_get_frame_region (GDK_DRAW_CONTEXT (self->gl_context)); @@ -2612,7 +2611,7 @@ gsk_gl_renderer_render (GskRenderer *renderer, gdk_gl_context_make_current (self->gl_context); gsk_gl_renderer_clear_tree (self); - gdk_surface_end_draw_frame (surface); + gdk_draw_context_end_frame (GDK_DRAW_CONTEXT (self->gl_context)); g_clear_pointer (&self->render_region, cairo_region_destroy); } diff --git a/gsk/gskbroadwayrenderer.c b/gsk/gskbroadwayrenderer.c index ee2a45d6dd..0c118859cc 100644 --- a/gsk/gskbroadwayrenderer.c +++ b/gsk/gskbroadwayrenderer.c @@ -681,7 +681,7 @@ gsk_broadway_renderer_render (GskRenderer *self, gdk_surface_get_width (surface), gdk_surface_get_height (surface) }); - gdk_surface_begin_draw_frame (surface, NULL, whole); + gdk_draw_context_begin_frame (NULL, whole); cairo_region_destroy (whole); nodes = g_array_new (FALSE, FALSE, sizeof(guint32)); @@ -691,7 +691,7 @@ gsk_broadway_renderer_render (GskRenderer *self, g_array_unref (nodes); g_ptr_array_unref (node_textures); - gdk_surface_end_draw_frame (surface); + gdk_draw_context_end_frame (NULL); } static void diff --git a/gsk/gskcairorenderer.c b/gsk/gskcairorenderer.c index 22e723fdb9..3d37e838cb 100644 --- a/gsk/gskcairorenderer.c +++ b/gsk/gskcairorenderer.c @@ -108,11 +108,9 @@ gsk_cairo_renderer_render (GskRenderer *renderer, const cairo_region_t *region) { GskCairoRenderer *self = GSK_CAIRO_RENDERER (renderer); - GdkSurface *surface = gsk_renderer_get_surface (renderer); cairo_t *cr; - gdk_surface_begin_draw_frame (surface, - GDK_DRAW_CONTEXT (self->cairo_context), + gdk_draw_context_begin_frame (GDK_DRAW_CONTEXT (self->cairo_context), region); cr = gdk_cairo_context_cairo_create (self->cairo_context); @@ -121,6 +119,8 @@ gsk_cairo_renderer_render (GskRenderer *renderer, #ifdef G_ENABLE_DEBUG if (GSK_RENDERER_DEBUG_CHECK (renderer, GEOMETRY)) { + GdkSurface *surface = gsk_renderer_get_surface (renderer); + cairo_save (cr); cairo_set_operator (cr, CAIRO_OPERATOR_OVER); cairo_rectangle (cr, @@ -136,7 +136,7 @@ gsk_cairo_renderer_render (GskRenderer *renderer, cairo_destroy (cr); - gdk_surface_end_draw_frame (surface); + gdk_draw_context_end_frame (GDK_DRAW_CONTEXT (self->cairo_context)); } static void diff --git a/gsk/vulkan/gskvulkanrenderer.c b/gsk/vulkan/gskvulkanrenderer.c index c80c4abb13..f948027ec3 100644 --- a/gsk/vulkan/gskvulkanrenderer.c +++ b/gsk/vulkan/gskvulkanrenderer.c @@ -217,15 +217,12 @@ gsk_vulkan_renderer_render (GskRenderer *renderer, { GskVulkanRenderer *self = GSK_VULKAN_RENDERER (renderer); GskVulkanRender *render; - GdkSurface *surface; const cairo_region_t *clip; #ifdef G_ENABLE_DEBUG GskProfiler *profiler; gint64 cpu_time; #endif - surface = gsk_renderer_get_surface (renderer); - #ifdef G_ENABLE_DEBUG profiler = gsk_renderer_get_profiler (renderer); gsk_profiler_counter_set (profiler, self->profile_counters.fallback_pixels, 0); @@ -234,9 +231,7 @@ gsk_vulkan_renderer_render (GskRenderer *renderer, gsk_profiler_timer_begin (profiler, self->profile_timers.cpu_time); #endif - gdk_surface_begin_draw_frame (surface, - GDK_DRAW_CONTEXT (self->vulkan), - region); + gdk_draw_context_begin_frame (GDK_DRAW_CONTEXT (self->vulkan), region); render = self->render; clip = gdk_draw_context_get_frame_region (GDK_DRAW_CONTEXT (self->vulkan)); @@ -257,7 +252,7 @@ gsk_vulkan_renderer_render (GskRenderer *renderer, gsk_profiler_push_samples (profiler); #endif - gdk_surface_end_draw_frame (surface); + gdk_draw_context_end_frame (GDK_DRAW_CONTEXT (self->vulkan)); } static void -- 2.30.2