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.
This commit is contained in:
Benjamin Otte 2018-04-23 17:28:55 +02:00
parent 48fc18c37b
commit a865621519
13 changed files with 142 additions and 204 deletions

View File

@ -233,10 +233,6 @@ gdk_surface_set_opaque_region
gdk_surface_create_gl_context
gdk_surface_create_vulkan_context
<SUBSECTION>
gdk_surface_begin_draw_frame
gdk_surface_end_draw_frame
<SUBSECTION>
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
<SUBSECTION Standard>

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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 youre
* 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

View File

@ -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);

View File

@ -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
*/

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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