gsk: Add the ability to create fallback renderers

While porting GTK to GskRenderer we noticed that the current fallback
code for widgets using Cairo to draw is not enough to cover all the
possible cases.

For instance, if a container widget still uses GtkWidget::draw to render
its children, and at least one of them has been ported to using render
nodes instead, the container won't know how to draw it.

For this reason we want to provide to layers above GSK the ability to
create a "fallback" renderer instance, created using a "parent"
GskRenderer instance, but using a Cairo context as the rendering target
instead of a GdkDrawingContext.

GTK will use this inside the gtk_widget_draw() implementation, if a
widget implements GtkWidgetClass.get_render_node().
This commit is contained in:
Emmanuele Bassi 2016-08-25 11:31:56 +01:00
parent a9bbb020aa
commit dace0791a9
6 changed files with 149 additions and 26 deletions

View File

@ -135,7 +135,11 @@ gsk_cairo_renderer_render (GskRenderer *renderer,
GdkDrawingContext *context = gsk_renderer_get_drawing_context (renderer);
cairo_t *cr;
cr = gdk_drawing_context_get_cairo_context (context);
if (context != NULL)
cr = gdk_drawing_context_get_cairo_context (context);
else
cr = gsk_renderer_get_cairo_context (renderer);
if (cr == NULL)
return;

View File

@ -920,9 +920,17 @@ out:
GdkWindow *window;
cairo_t *cr;
/* XXX: Add GdkDrawingContext API */
cr = gdk_drawing_context_get_cairo_context (context);
window = gdk_drawing_context_get_window (context);
if (context != NULL)
{
/* XXX: Add GdkDrawingContext API */
cr = gdk_drawing_context_get_cairo_context (context);
window = gdk_drawing_context_get_window (context);
}
else
{
cr = gsk_renderer_get_cairo_context (renderer);
window = gsk_renderer_get_window (renderer);
}
gdk_cairo_draw_from_gl (cr, window,
self->texture_id,

View File

@ -37,6 +37,7 @@
#include "gskrendererprivate.h"
#include "gskcairorendererprivate.h"
#include "gskdebugprivate.h"
#include "gskglrendererprivate.h"
#include "gskprofilerprivate.h"
@ -68,6 +69,7 @@ typedef struct
GdkDrawingContext *drawing_context;
GskRenderNode *root_node;
GdkDisplay *display;
cairo_t *cairo_context;
GskProfiler *profiler;
@ -121,6 +123,7 @@ gsk_renderer_dispose (GObject *gobject)
gsk_renderer_unrealize (self);
g_clear_pointer (&priv->cairo_context, cairo_destroy);
g_clear_object (&priv->window);
g_clear_object (&priv->display);
@ -603,6 +606,16 @@ gsk_renderer_render (GskRenderer *renderer,
if (context != NULL)
priv->drawing_context = g_object_ref (context);
else
{
if (priv->cairo_context == NULL)
{
g_critical ("The given GskRenderer instance was not created using "
"gsk_renderer_create_fallback(), but you forgot to pass "
"a GdkDrawingContext.");
return;
}
}
priv->root_node = gsk_render_node_ref (root);
gsk_render_node_make_immutable (priv->root_node);
@ -695,7 +708,7 @@ gsk_renderer_get_for_display (GdkDisplay *display)
}
if (use_software[0] != '0')
return NULL;
return g_object_new (GSK_TYPE_CAIRO_RENDERER, "display", display, NULL);
#ifdef GDK_WINDOWING_X11
if (GDK_IS_X11_DISPLAY (display))
@ -707,7 +720,7 @@ gsk_renderer_get_for_display (GdkDisplay *display)
renderer_type = GSK_TYPE_GL_RENDERER;
else
#endif
return NULL;
renderer_type = GSK_TYPE_CAIRO_RENDERER;
GSK_NOTE (RENDERER, g_print ("Creating renderer of type '%s' for display '%s'\n",
g_type_name (renderer_type),
@ -717,3 +730,65 @@ gsk_renderer_get_for_display (GdkDisplay *display)
return g_object_new (renderer_type, "display", display, NULL);
}
static void
gsk_renderer_set_cairo_context (GskRenderer *renderer,
cairo_t *cr)
{
GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
g_clear_pointer (&priv->cairo_context, cairo_destroy);
if (cr != NULL)
priv->cairo_context = cairo_reference (cr);
}
cairo_t *
gsk_renderer_get_cairo_context (GskRenderer *renderer)
{
GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
return priv->cairo_context;
}
/**
* gsk_renderer_create_fallback:
* @renderer: a #GskRenderer
* @viewport: the viewport for the fallback renderer
* @cr: a Cairo context
*
* Creates a fallback #GskRenderer using the same display and window of
* the given @renderer, and instructs it to render to a given Cairo
* context.
*
* Typically, you'll use this function to implement fallback rendering
* of #GskRenderNodes on an intermediate Cairo context, instead of using
* the drawing context associated to a #GdkWindow's rendering buffer.
*
* Returns: (transfer full): a newly created fallback #GskRenderer instance
*
* Since: 3.22
*/
GskRenderer *
gsk_renderer_create_fallback (GskRenderer *renderer,
const graphene_rect_t *viewport,
cairo_t *cr)
{
GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
GskRenderer *res;
g_return_val_if_fail (GSK_IS_RENDERER (renderer), NULL);
g_return_val_if_fail (cr != NULL, NULL);
res = g_object_new (GSK_TYPE_CAIRO_RENDERER,
"display", priv->display,
"window", priv->window,
"scale-factor", priv->scale_factor,
"viewport", viewport,
NULL);
gsk_renderer_set_cairo_context (res, cr);
gsk_renderer_realize (res);
return res;
}

View File

@ -70,6 +70,11 @@ void gsk_renderer_unrealize (GskRenderer
GDK_AVAILABLE_IN_3_22
GskRenderNode * gsk_renderer_create_render_node (GskRenderer *renderer);
GDK_AVAILABLE_IN_3_22
GskRenderer * gsk_renderer_create_fallback (GskRenderer *renderer,
const graphene_rect_t *viewport,
cairo_t *cr);
GDK_AVAILABLE_IN_3_22
void gsk_renderer_render (GskRenderer *renderer,
GskRenderNode *root,

View File

@ -48,6 +48,7 @@ gboolean gsk_renderer_is_realized (GskRenderer *renderer);
GskRenderNode * gsk_renderer_get_root_node (GskRenderer *renderer);
GdkDrawingContext * gsk_renderer_get_drawing_context (GskRenderer *renderer);
cairo_t * gsk_renderer_get_cairo_context (GskRenderer *renderer);
GskProfiler * gsk_renderer_get_profiler (GskRenderer *renderer);

View File

@ -6354,6 +6354,18 @@ gtk_cairo_set_marked_for_draw (cairo_t *cr,
cairo_set_user_data (cr, &mark_for_draw_key, NULL, NULL);
}
static GskRenderer *
gtk_widget_get_renderer (GtkWidget *widget)
{
GtkWidget *toplevel;
toplevel = _gtk_widget_get_toplevel (widget);
if (_gtk_widget_is_toplevel (toplevel))
return gtk_window_get_renderer (GTK_WINDOW (toplevel));
return NULL;
}
/**
* gtk_cairo_should_draw_window:
* @cr: a cairo context
@ -6421,6 +6433,7 @@ gtk_widget_draw_internal (GtkWidget *widget,
if (gdk_cairo_get_clip_rectangle (cr, NULL))
{
GtkWidgetClass *widget_class = GTK_WIDGET_GET_CLASS (widget);
GdkWindow *event_window = NULL;
gboolean result;
gboolean push_group;
@ -6453,17 +6466,46 @@ gtk_widget_draw_internal (GtkWidget *widget,
g_warning ("%s %p is drawn without a current allocation. This should not happen.", G_OBJECT_TYPE_NAME (widget), widget);
#endif
if (g_signal_has_handler_pending (widget, widget_signals[DRAW], 0, FALSE))
/* If the widget uses GSK render nodes then we need a fallback path to
* render on the Cairo context; otherwise we just go through the old
* GtkWidget::draw path
*/
if (widget_class->get_render_node != NULL)
{
g_signal_emit (widget, widget_signals[DRAW],
0, cr,
&result);
GskRenderer *renderer = gtk_widget_get_renderer (widget);
GskRenderer *fallback;
graphene_rect_t viewport;
GskRenderNode *node;
graphene_rect_init (&viewport,
widget->priv->clip.x,
widget->priv->clip.y,
widget->priv->clip.width,
widget->priv->clip.height);
fallback = gsk_renderer_create_fallback (renderer, &viewport, cr);
node = gtk_widget_get_render_node (widget, fallback);
if (node != NULL)
{
gsk_renderer_render (fallback, node, NULL);
gsk_render_node_unref (node);
}
g_object_unref (fallback);
}
else if (GTK_WIDGET_GET_CLASS (widget)->draw)
else
{
cairo_save (cr);
GTK_WIDGET_GET_CLASS (widget)->draw (widget, cr);
cairo_restore (cr);
if (g_signal_has_handler_pending (widget, widget_signals[DRAW], 0, FALSE))
{
g_signal_emit (widget, widget_signals[DRAW],
0, cr,
&result);
}
else if (GTK_WIDGET_GET_CLASS (widget)->draw)
{
cairo_save (cr);
GTK_WIDGET_GET_CLASS (widget)->draw (widget, cr);
cairo_restore (cr);
}
}
#ifdef G_ENABLE_DEBUG
@ -15827,18 +15869,6 @@ gtk_widget_reset_controllers (GtkWidget *widget)
}
}
GskRenderer *
gtk_widget_get_renderer (GtkWidget *widget)
{
GtkWidget *toplevel;
toplevel = _gtk_widget_get_toplevel (widget);
if (_gtk_widget_is_toplevel (toplevel))
return gtk_window_get_renderer (GTK_WINDOW (toplevel));
return NULL;
}
GskRenderNode *
gtk_widget_create_render_node (GtkWidget *widget,
GskRenderer *renderer,