gsk: Rework GskRenderer and GskRenderNode semantics

This commit changes the way GskRenderer and GskRenderNode interact and
are meant to be used.

GskRenderNode should represent a transient tree of rendering nodes,
which are submitted to the GskRenderer at render time; this allows the
renderer to take ownership of the render tree. Once the toolkit and
application code have finished assembling it, the render tree ownership
is transferred to the renderer.
This commit is contained in:
Emmanuele Bassi 2016-06-22 17:30:36 +01:00
parent d99f91f5fd
commit 074c77e7ac
10 changed files with 359 additions and 1281 deletions

View File

@ -123,22 +123,29 @@ gsk_cairo_renderer_render_node (GskCairoRenderer *self,
} }
static void static void
gsk_cairo_renderer_resize_viewport (GskRenderer *renderer, gsk_cairo_renderer_render (GskRenderer *renderer,
const graphene_rect_t *viewport) GskRenderNode *root,
GdkDrawingContext *context)
{ {
GskCairoRenderer *self = GSK_CAIRO_RENDERER (renderer); GskCairoRenderer *self = GSK_CAIRO_RENDERER (renderer);
cairo_t *cr = gdk_drawing_context_get_cairo_context (context);
self->viewport = *viewport; gsk_renderer_get_viewport (renderer, &self->viewport);
if (gsk_renderer_get_auto_clear (renderer))
{
cairo_save (cr);
cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
if (gsk_renderer_get_use_alpha (renderer))
cairo_set_source_rgba (cr, 0, 0, 0, 0);
else
cairo_set_source_rgb (cr, 0, 0, 0);
cairo_paint (cr);
cairo_restore (cr);
} }
static void
gsk_cairo_renderer_render (GskRenderer *renderer)
{
GskCairoRenderer *self = GSK_CAIRO_RENDERER (renderer);
cairo_surface_t *target = gsk_renderer_get_surface (renderer);
GskRenderNode *root = gsk_renderer_get_root_node (renderer);
cairo_t *cr = cairo_create (target);
if (GSK_RENDER_MODE_CHECK (GEOMETRY)) if (GSK_RENDER_MODE_CHECK (GEOMETRY))
{ {
cairo_save (cr); cairo_save (cr);
@ -154,26 +161,6 @@ gsk_cairo_renderer_render (GskRenderer *renderer)
} }
gsk_cairo_renderer_render_node (self, root, cr); gsk_cairo_renderer_render_node (self, root, cr);
cairo_destroy (cr);
}
static void
gsk_cairo_renderer_clear (GskRenderer *renderer)
{
cairo_surface_t *surface = gsk_renderer_get_surface (renderer);
cairo_t *cr = cairo_create (surface);
cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
if (gsk_renderer_get_use_alpha (renderer))
cairo_set_source_rgba (cr, 0, 0, 0, 0);
else
cairo_set_source_rgb (cr, 0, 0, 0);
cairo_paint (cr);
cairo_destroy (cr);
} }
static void static void
@ -183,8 +170,6 @@ gsk_cairo_renderer_class_init (GskCairoRendererClass *klass)
renderer_class->realize = gsk_cairo_renderer_realize; renderer_class->realize = gsk_cairo_renderer_realize;
renderer_class->unrealize = gsk_cairo_renderer_unrealize; renderer_class->unrealize = gsk_cairo_renderer_unrealize;
renderer_class->resize_viewport = gsk_cairo_renderer_resize_viewport;
renderer_class->clear = gsk_cairo_renderer_clear;
renderer_class->render = gsk_cairo_renderer_render; renderer_class->render = gsk_cairo_renderer_render;
} }

View File

@ -432,16 +432,9 @@ gsk_gl_renderer_realize (GskRenderer *renderer)
gdk_gl_context_make_current (self->context); gdk_gl_context_make_current (self->context);
GSK_NOTE (OPENGL, g_print ("Creating buffers and programs\n")); GSK_NOTE (OPENGL, g_print ("Creating buffers and programs\n"));
gsk_gl_renderer_create_buffers (self); gsk_gl_renderer_create_buffers (self);
gsk_gl_renderer_create_program (self); gsk_gl_renderer_create_program (self);
self->opaque_render_items = g_array_sized_new (FALSE, FALSE, sizeof (RenderItem), 16);
g_array_set_clear_func (self->opaque_render_items, render_item_clear);
self->transparent_render_items = g_array_sized_new (FALSE, FALSE, sizeof (RenderItem), 16);
g_array_set_clear_func (self->opaque_render_items, render_item_clear);
return TRUE; return TRUE;
} }
@ -466,18 +459,22 @@ gsk_gl_renderer_unrealize (GskRenderer *renderer)
} }
static void static void
gsk_gl_renderer_resize_viewport (GskRenderer *renderer, gsk_gl_renderer_resize_viewport (GskGLRenderer *self,
const graphene_rect_t *viewport) const graphene_rect_t *viewport)
{ {
GSK_NOTE (OPENGL, g_print ("glViewport(0, 0, %g, %g)\n",
viewport->size.width,
viewport->size.height));
glViewport (viewport->origin.x, viewport->origin.y,
viewport->size.width, viewport->size.height);
} }
static void static void
gsk_gl_renderer_update (GskRenderer *renderer, gsk_gl_renderer_update_frustum (GskGLRenderer *self,
const graphene_matrix_t *modelview, const graphene_matrix_t *modelview,
const graphene_matrix_t *projection) const graphene_matrix_t *projection)
{ {
GskGLRenderer *self = GSK_GL_RENDERER (renderer);
GSK_NOTE (OPENGL, g_print ("Updating the modelview/projection\n")); GSK_NOTE (OPENGL, g_print ("Updating the modelview/projection\n"));
graphene_matrix_multiply (modelview, projection, &self->mvp); graphene_matrix_multiply (modelview, projection, &self->mvp);
@ -662,6 +659,7 @@ get_gl_scaling_filters (GskRenderer *renderer,
} }
} }
#if 0
static gboolean static gboolean
check_in_frustum (const graphene_frustum_t *frustum, check_in_frustum (const graphene_frustum_t *frustum,
RenderItem *item) RenderItem *item)
@ -673,6 +671,7 @@ check_in_frustum (const graphene_frustum_t *frustum,
return graphene_frustum_intersects_box (frustum, &aabb); return graphene_frustum_intersects_box (frustum, &aabb);
} }
#endif
static float static float
project_item (const graphene_matrix_t *projection, project_item (const graphene_matrix_t *projection,
@ -748,19 +747,6 @@ gsk_gl_renderer_add_render_item (GskGLRenderer *self,
gsk_renderer_get_projection (GSK_RENDERER (self), &projection); gsk_renderer_get_projection (GSK_RENDERER (self), &projection);
item.z = project_item (&projection, &mv); item.z = project_item (&projection, &mv);
/* Discard the item if it's outside of the frustum as determined by the
* viewport and the projection matrix
*/
#if 0
if (!check_in_frustum (&self->frustum, &item))
{
GSK_NOTE (OPENGL, g_print ("Node <%s>[%p] culled by frustum\n",
node->name != NULL ? node->name : "unnamed",
node));
return;
}
#endif
/* TODO: This should really be an asset atlas, to avoid uploading a ton /* TODO: This should really be an asset atlas, to avoid uploading a ton
* of textures. Ideally we could use a single Cairo surface to get around * of textures. Ideally we could use a single Cairo surface to get around
* the GL texture limits and reorder the texture data on the CPU side and * the GL texture limits and reorder the texture data on the CPU side and
@ -787,7 +773,7 @@ gsk_gl_renderer_add_render_item (GskGLRenderer *self,
if (gsk_render_node_is_opaque (node) && gsk_render_node_get_opacity (node) == 1.f) if (gsk_render_node_is_opaque (node) && gsk_render_node_get_opacity (node) == 1.f)
g_array_append_val (self->opaque_render_items, item); g_array_append_val (self->opaque_render_items, item);
else else
g_array_append_val (self->transparent_render_items, item); g_array_prepend_val (self->transparent_render_items, item);
recurse_children: recurse_children:
gsk_render_node_iter_init (&iter, node); gsk_render_node_iter_init (&iter, node);
@ -795,198 +781,72 @@ recurse_children:
gsk_gl_renderer_add_render_item (self, child); gsk_gl_renderer_add_render_item (self, child);
} }
static int static gboolean
opaque_item_cmp (gconstpointer _a, gsk_gl_renderer_validate_tree (GskGLRenderer *self,
gconstpointer _b)
{
const RenderItem *a = _a;
const RenderItem *b = _b;
if (a->z != b->z)
{
if (a->z > b->z)
return 1;
return -1;
}
if (a != b)
{
if ((gsize) a > (gsize) b)
return 1;
return -1;
}
return 0;
}
static int
transparent_item_cmp (gconstpointer _a,
gconstpointer _b)
{
const RenderItem *a = _a;
const RenderItem *b = _b;
if (a->z != b->z)
{
if (a->z < b->z)
return 1;
return -1;
}
if (a != b)
{
if ((gsize) a < (gsize) b)
return 1;
return -1;
}
return 0;
}
static void
gsk_gl_renderer_validate_tree (GskRenderer *renderer,
GskRenderNode *root) GskRenderNode *root)
{ {
GskGLRenderer *self = GSK_GL_RENDERER (renderer); int n_children;
gboolean clear_items = FALSE;
int i;
if (self->context == NULL) if (self->context == NULL)
return; return FALSE;
n_children = gsk_render_node_get_n_children (root);
if (n_children == 0)
return FALSE;
gdk_gl_context_make_current (self->context); gdk_gl_context_make_current (self->context);
if (self->opaque_render_items->len > 0 || self->transparent_render_items->len > 0) self->opaque_render_items = g_array_sized_new (FALSE, FALSE, sizeof (RenderItem), n_children);
{ self->transparent_render_items = g_array_sized_new (FALSE, FALSE, sizeof (RenderItem), n_children);
/* If we only changed the opacity and transformations then there is no
* reason to clear the render items
*/
for (i = 0; i < self->opaque_render_items->len; i++)
{
RenderItem *item = &g_array_index (self->opaque_render_items, RenderItem, i);
GskRenderNodeChanges changes = gsk_render_node_get_last_state (item->node);
if (changes == 0) g_array_set_clear_func (self->opaque_render_items, render_item_clear);
continue; g_array_set_clear_func (self->transparent_render_items, render_item_clear);
if ((changes & GSK_RENDER_NODE_CHANGES_UPDATE_OPACITY) != 0)
{
item->opaque = gsk_render_node_is_opaque (item->node);
item->opacity = gsk_render_node_get_opacity (item->node);
changes &= ~GSK_RENDER_NODE_CHANGES_UPDATE_OPACITY;
}
if (changes & GSK_RENDER_NODE_CHANGES_UPDATE_TRANSFORM)
{
gsk_render_node_get_world_matrix (item->node, &item->mvp);
changes &= ~ GSK_RENDER_NODE_CHANGES_UPDATE_TRANSFORM;
}
if (changes != 0)
{
clear_items = TRUE;
break;
}
}
for (i = 0; i < self->transparent_render_items->len; i++)
{
RenderItem *item = &g_array_index (self->transparent_render_items, RenderItem, i);
GskRenderNodeChanges changes = gsk_render_node_get_last_state (item->node);
if (changes == 0)
continue;
if ((changes & GSK_RENDER_NODE_CHANGES_UPDATE_OPACITY) != 0)
{
item->opaque = gsk_render_node_is_opaque (item->node);
item->opacity = gsk_render_node_get_opacity (item->node);
changes &= ~GSK_RENDER_NODE_CHANGES_UPDATE_OPACITY;
}
if (changes & GSK_RENDER_NODE_CHANGES_UPDATE_TRANSFORM)
{
gsk_render_node_get_world_matrix (item->node, &item->mvp);
changes &= ~ GSK_RENDER_NODE_CHANGES_UPDATE_TRANSFORM;
}
if (changes != 0)
{
clear_items = TRUE;
break;
}
}
}
else
clear_items = TRUE;
if (!clear_items)
{
GSK_NOTE (OPENGL, g_print ("Tree is still valid\n"));
goto out;
}
for (i = 0; i < self->opaque_render_items->len; i++)
render_item_clear (&g_array_index (self->opaque_render_items, RenderItem, i));
for (i = 0; i < self->transparent_render_items->len; i++)
render_item_clear (&g_array_index (self->transparent_render_items, RenderItem, i));
g_array_set_size (self->opaque_render_items, 0);
g_array_set_size (self->transparent_render_items, 0);
GSK_NOTE (OPENGL, g_print ("RenderNode -> RenderItem\n")); GSK_NOTE (OPENGL, g_print ("RenderNode -> RenderItem\n"));
gsk_gl_renderer_add_render_item (self, gsk_renderer_get_root_node (renderer)); gsk_gl_renderer_add_render_item (self, root);
GSK_NOTE (OPENGL, g_print ("Sorting render nodes\n"));
g_array_sort (self->opaque_render_items, opaque_item_cmp);
g_array_sort (self->transparent_render_items, transparent_item_cmp);
out:
GSK_NOTE (OPENGL, g_print ("Total render items: %d (opaque:%d, transparent:%d)\n", GSK_NOTE (OPENGL, g_print ("Total render items: %d (opaque:%d, transparent:%d)\n",
self->opaque_render_items->len + self->transparent_render_items->len, self->opaque_render_items->len + self->transparent_render_items->len,
self->opaque_render_items->len, self->opaque_render_items->len,
self->transparent_render_items->len)); self->transparent_render_items->len));
return TRUE;
} }
static void static void
gsk_gl_renderer_clear_tree (GskRenderer *renderer, gsk_gl_renderer_clear_tree (GskGLRenderer *self)
GskRenderNode *root_node)
{ {
GskGLRenderer *self = GSK_GL_RENDERER (renderer);
if (self->context == NULL) if (self->context == NULL)
return; return;
gdk_gl_context_make_current (self->context);
g_clear_pointer (&self->opaque_render_items, g_array_unref); g_clear_pointer (&self->opaque_render_items, g_array_unref);
g_clear_pointer (&self->transparent_render_items, g_array_unref); g_clear_pointer (&self->transparent_render_items, g_array_unref);
if (gsk_renderer_is_realized (renderer))
{
self->opaque_render_items = g_array_sized_new (FALSE, FALSE, sizeof (RenderItem), 16);
g_array_set_clear_func (self->opaque_render_items, render_item_clear);
self->transparent_render_items = g_array_sized_new (FALSE, FALSE, sizeof (RenderItem), 16);
g_array_set_clear_func (self->opaque_render_items, render_item_clear);
}
} }
static void static void
gsk_gl_renderer_clear (GskRenderer *renderer) gsk_gl_renderer_clear (GskGLRenderer *self)
{ {
int clear_bits = GL_COLOR_BUFFER_BIT;
if (self->has_depth_buffer)
clear_bits |= GL_DEPTH_BUFFER_BIT;
if (self->has_stencil_buffer)
clear_bits |= GL_STENCIL_BUFFER_BIT;
GSK_NOTE (OPENGL, g_print ("Clearing viewport\n"));
glClearColor (0, 0, 0, 0);
glClear (clear_bits);
} }
static void static void
gsk_gl_renderer_render (GskRenderer *renderer) gsk_gl_renderer_render (GskRenderer *renderer,
GskRenderNode *root,
GdkDrawingContext *context)
{ {
GskGLRenderer *self = GSK_GL_RENDERER (renderer); GskGLRenderer *self = GSK_GL_RENDERER (renderer);
graphene_matrix_t modelview, projection;
graphene_rect_t viewport; graphene_rect_t viewport;
int scale, status, clear_bits; int status;
guint i; guint i;
if (self->context == NULL) if (self->context == NULL)
@ -1008,22 +868,16 @@ gsk_gl_renderer_render (GskRenderer *renderer)
/* Ensure that the viewport is up to date */ /* Ensure that the viewport is up to date */
status = glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT); status = glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT);
if (status == GL_FRAMEBUFFER_COMPLETE_EXT) if (status == GL_FRAMEBUFFER_COMPLETE_EXT)
{ gsk_gl_renderer_resize_viewport (self, &viewport);
GSK_NOTE (OPENGL, g_print ("glViewport(0, 0, %g, %g)\n",
viewport.size.width,
viewport.size.height));
glViewport (0, 0, viewport.size.width, viewport.size.height);
}
clear_bits = GL_COLOR_BUFFER_BIT; gsk_gl_renderer_clear (self);
if (self->has_depth_buffer)
clear_bits |= GL_DEPTH_BUFFER_BIT;
if (self->has_stencil_buffer)
clear_bits |= GL_STENCIL_BUFFER_BIT;
GSK_NOTE (OPENGL, g_print ("Clearing viewport\n")); gsk_renderer_get_modelview (renderer, &modelview);
glClearColor (0, 0, 0, 0); gsk_renderer_get_projection (renderer, &projection);
glClear (clear_bits); gsk_gl_renderer_update_frustum (self, &modelview, &projection);
if (!gsk_gl_renderer_validate_tree (self, root))
goto out;
/* Opaque pass: front-to-back */ /* Opaque pass: front-to-back */
GSK_NOTE (OPENGL, g_print ("Rendering %u opaque items\n", self->opaque_render_items->len)); GSK_NOTE (OPENGL, g_print ("Rendering %u opaque items\n", self->opaque_render_items->len));
@ -1050,15 +904,18 @@ gsk_gl_renderer_render (GskRenderer *renderer)
/* Draw the output of the GL rendering to the window */ /* Draw the output of the GL rendering to the window */
GSK_NOTE (OPENGL, g_print ("Drawing GL content on Cairo surface using a %s\n", GSK_NOTE (OPENGL, g_print ("Drawing GL content on Cairo surface using a %s\n",
self->texture_id != 0 ? "texture" : "renderbuffer")); self->texture_id != 0 ? "texture" : "renderbuffer"));
scale = 1;
gdk_cairo_draw_from_gl (gsk_renderer_get_draw_context (renderer), out:
gsk_renderer_get_window (renderer), gdk_cairo_draw_from_gl (gdk_drawing_context_get_cairo_context (context),
gdk_drawing_context_get_window (context),
self->texture_id != 0 ? self->texture_id : self->render_buffer, self->texture_id != 0 ? self->texture_id : self->render_buffer,
self->texture_id != 0 ? GL_TEXTURE : GL_RENDERBUFFER, self->texture_id != 0 ? GL_TEXTURE : GL_RENDERBUFFER,
scale, gsk_renderer_get_scale_factor (renderer),
0, 0, viewport.size.width, viewport.size.height); 0, 0, viewport.size.width, viewport.size.height);
gdk_gl_context_make_current (self->context); gdk_gl_context_make_current (self->context);
gsk_gl_renderer_clear_tree (self);
} }
static void static void
@ -1071,11 +928,6 @@ gsk_gl_renderer_class_init (GskGLRendererClass *klass)
renderer_class->realize = gsk_gl_renderer_realize; renderer_class->realize = gsk_gl_renderer_realize;
renderer_class->unrealize = gsk_gl_renderer_unrealize; renderer_class->unrealize = gsk_gl_renderer_unrealize;
renderer_class->resize_viewport = gsk_gl_renderer_resize_viewport;
renderer_class->update = gsk_gl_renderer_update;
renderer_class->clear = gsk_gl_renderer_clear;
renderer_class->validate_tree = gsk_gl_renderer_validate_tree;
renderer_class->clear_tree = gsk_gl_renderer_clear_tree;
renderer_class->render = gsk_gl_renderer_render; renderer_class->render = gsk_gl_renderer_render;
} }
@ -1089,27 +941,3 @@ gsk_gl_renderer_init (GskGLRenderer *self)
self->has_depth_buffer = TRUE; self->has_depth_buffer = TRUE;
self->has_stencil_buffer = TRUE; self->has_stencil_buffer = TRUE;
} }
void
gsk_gl_renderer_set_context (GskGLRenderer *renderer,
GdkGLContext *context)
{
g_return_if_fail (GSK_IS_GL_RENDERER (renderer));
g_return_if_fail (context == NULL || GDK_IS_GL_CONTEXT (context));
if (gsk_renderer_is_realized (GSK_RENDERER (renderer)))
return;
if (gdk_gl_context_get_display (context) != gsk_renderer_get_display (GSK_RENDERER (renderer)))
return;
g_set_object (&renderer->context, context);
}
GdkGLContext *
gsk_gl_renderer_get_context (GskGLRenderer *renderer)
{
g_return_val_if_fail (GSK_IS_GL_RENDERER (renderer), NULL);
return renderer->context;
}

View File

@ -29,7 +29,6 @@
#include "gskrendererprivate.h" #include "gskrendererprivate.h"
#include "gskdebugprivate.h" #include "gskdebugprivate.h"
#include "gskcairorendererprivate.h"
#include "gskglrendererprivate.h" #include "gskglrendererprivate.h"
#include "gskrendernodeprivate.h" #include "gskrendernodeprivate.h"
@ -50,9 +49,6 @@ typedef struct
{ {
GObject parent_instance; GObject parent_instance;
GdkDisplay *display;
GdkWindow *window;
graphene_rect_t viewport; graphene_rect_t viewport;
graphene_matrix_t modelview; graphene_matrix_t modelview;
graphene_matrix_t projection; graphene_matrix_t projection;
@ -60,16 +56,14 @@ typedef struct
GskScalingFilter min_filter; GskScalingFilter min_filter;
GskScalingFilter mag_filter; GskScalingFilter mag_filter;
GdkWindow *window;
GdkDrawingContext *drawing_context;
GskRenderNode *root_node; GskRenderNode *root_node;
GdkDisplay *display;
cairo_surface_t *surface; int scale_factor;
cairo_t *draw_context;
gboolean is_realized : 1; gboolean is_realized : 1;
gboolean needs_viewport_resize : 1;
gboolean needs_modelview_update : 1;
gboolean needs_projection_update : 1;
gboolean needs_tree_validation : 1;
gboolean auto_clear : 1; gboolean auto_clear : 1;
gboolean use_alpha : 1; gboolean use_alpha : 1;
} GskRendererPrivate; } GskRendererPrivate;
@ -83,11 +77,12 @@ enum {
PROP_MINIFICATION_FILTER, PROP_MINIFICATION_FILTER,
PROP_MAGNIFICATION_FILTER, PROP_MAGNIFICATION_FILTER,
PROP_AUTO_CLEAR, PROP_AUTO_CLEAR,
PROP_USE_ALPHA,
PROP_SCALE_FACTOR,
PROP_WINDOW,
PROP_ROOT_NODE, PROP_ROOT_NODE,
PROP_DISPLAY, PROP_DISPLAY,
PROP_WINDOW, PROP_DRAWING_CONTEXT,
PROP_SURFACE,
PROP_USE_ALPHA,
N_PROPS N_PROPS
}; };
@ -111,36 +106,13 @@ gsk_renderer_real_unrealize (GskRenderer *self)
} }
static void static void
gsk_renderer_real_render (GskRenderer *self) gsk_renderer_real_render (GskRenderer *self,
GskRenderNode *root,
GdkDrawingContext *context)
{ {
GSK_RENDERER_WARN_NOT_IMPLEMENTED_METHOD (self, render); GSK_RENDERER_WARN_NOT_IMPLEMENTED_METHOD (self, render);
} }
static void
gsk_renderer_real_resize_viewport (GskRenderer *self,
const graphene_rect_t *viewport)
{
}
static void
gsk_renderer_real_update (GskRenderer *self,
const graphene_matrix_t *mv,
const graphene_matrix_t *proj)
{
}
static void
gsk_renderer_real_validate_tree (GskRenderer *self,
GskRenderNode *root)
{
}
static void
gsk_renderer_real_clear_tree (GskRenderer *self,
GskRenderNode *old_root)
{
}
static void static void
gsk_renderer_dispose (GObject *gobject) gsk_renderer_dispose (GObject *gobject)
{ {
@ -149,11 +121,7 @@ gsk_renderer_dispose (GObject *gobject)
gsk_renderer_unrealize (self); gsk_renderer_unrealize (self);
g_clear_pointer (&priv->surface, cairo_surface_destroy);
g_clear_pointer (&priv->draw_context, cairo_destroy);
g_clear_object (&priv->window); g_clear_object (&priv->window);
g_clear_object (&priv->root_node);
g_clear_object (&priv->display); g_clear_object (&priv->display);
G_OBJECT_CLASS (gsk_renderer_parent_class)->dispose (gobject); G_OBJECT_CLASS (gsk_renderer_parent_class)->dispose (gobject);
@ -194,12 +162,12 @@ gsk_renderer_set_property (GObject *gobject,
gsk_renderer_set_auto_clear (self, g_value_get_boolean (value)); gsk_renderer_set_auto_clear (self, g_value_get_boolean (value));
break; break;
case PROP_ROOT_NODE: case PROP_USE_ALPHA:
gsk_renderer_set_root_node (self, g_value_get_object (value)); gsk_renderer_set_use_alpha (self, g_value_get_boolean (value));
break; break;
case PROP_SURFACE: case PROP_SCALE_FACTOR:
gsk_renderer_set_surface (self, g_value_get_boxed (value)); gsk_renderer_set_scale_factor (self, g_value_get_int (value));
break; break;
case PROP_WINDOW: case PROP_WINDOW:
@ -207,12 +175,9 @@ gsk_renderer_set_property (GObject *gobject,
break; break;
case PROP_DISPLAY: case PROP_DISPLAY:
/* Construct-only */
priv->display = g_value_dup_object (value); priv->display = g_value_dup_object (value);
break; break;
case PROP_USE_ALPHA:
gsk_renderer_set_use_alpha (self, g_value_get_boolean (value));
break;
} }
} }
@ -251,24 +216,28 @@ gsk_renderer_get_property (GObject *gobject,
g_value_set_boolean (value, priv->auto_clear); g_value_set_boolean (value, priv->auto_clear);
break; break;
case PROP_ROOT_NODE: case PROP_USE_ALPHA:
g_value_set_object (value, priv->root_node); g_value_set_boolean (value, priv->use_alpha);
break; break;
case PROP_SURFACE: case PROP_SCALE_FACTOR:
g_value_set_boxed (value, priv->surface); g_value_set_int (value, priv->scale_factor);
break;
case PROP_DISPLAY:
g_value_set_object (value, priv->display);
break; break;
case PROP_WINDOW: case PROP_WINDOW:
g_value_set_object (value, priv->window); g_value_set_object (value, priv->window);
break; break;
case PROP_USE_ALPHA: case PROP_ROOT_NODE:
g_value_set_boolean (value, priv->use_alpha); g_value_set_object (value, priv->root_node);
break;
case PROP_DRAWING_CONTEXT:
g_value_set_object (value, priv->drawing_context);
break;
case PROP_DISPLAY:
g_value_set_object (value, priv->display);
break; break;
} }
} }
@ -297,10 +266,6 @@ gsk_renderer_class_init (GskRendererClass *klass)
klass->realize = gsk_renderer_real_realize; klass->realize = gsk_renderer_real_realize;
klass->unrealize = gsk_renderer_real_unrealize; klass->unrealize = gsk_renderer_real_unrealize;
klass->resize_viewport = gsk_renderer_real_resize_viewport;
klass->update = gsk_renderer_real_update;
klass->validate_tree = gsk_renderer_real_validate_tree;
klass->clear_tree = gsk_renderer_real_clear_tree;
klass->render = gsk_renderer_real_render; klass->render = gsk_renderer_real_render;
gobject_class->constructed = gsk_renderer_constructed; gobject_class->constructed = gsk_renderer_constructed;
@ -448,27 +413,8 @@ gsk_renderer_class_init (GskRendererClass *klass)
"Root Node", "Root Node",
"The root render node to render", "The root render node to render",
GSK_TYPE_RENDER_NODE, GSK_TYPE_RENDER_NODE,
G_PARAM_READWRITE | G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS | G_PARAM_STATIC_STRINGS);
G_PARAM_EXPLICIT_NOTIFY);
/**
* GskRenderer:surface:
*
* The target rendering surface.
*
* See also: #GskRenderer:window.
*
* Since: 3.22
*/
gsk_renderer_properties[PROP_SURFACE] =
g_param_spec_boxed ("surface",
"Surface",
"The Cairo surface used to render to",
CAIRO_GOBJECT_TYPE_SURFACE,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS |
G_PARAM_EXPLICIT_NOTIFY);
/** /**
* GskRenderer:display: * GskRenderer:display:
@ -486,23 +432,47 @@ gsk_renderer_class_init (GskRendererClass *klass)
G_PARAM_CONSTRUCT_ONLY | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS); G_PARAM_STATIC_STRINGS);
/**
* GskRenderer:window:
*
* The #GdkWindow used to create a target surface, if #GskRenderer:surface
* is not explicitly set.
*
* Since: 3.22
*/
gsk_renderer_properties[PROP_WINDOW] = gsk_renderer_properties[PROP_WINDOW] =
g_param_spec_object ("window", g_param_spec_object ("window",
"Window", "Window",
"The GdkWindow associated to the renderer", "The window associated to the renderer",
GDK_TYPE_WINDOW, GDK_TYPE_WINDOW,
G_PARAM_READWRITE | G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
/**
* GskRenderer:scale-factor:
*
* The scale factor used when rendering.
*
* Since: 3.22
*/
gsk_renderer_properties[PROP_SCALE_FACTOR] =
g_param_spec_int ("scale-factor",
"Scale Factor",
"The scaling factor of the renderer",
1, G_MAXINT,
1,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS | G_PARAM_STATIC_STRINGS |
G_PARAM_EXPLICIT_NOTIFY); G_PARAM_EXPLICIT_NOTIFY);
/**
* GskRenderer:drawing-context:
*
* The drawing context used when rendering.
*
* Since: 3.22
*/
gsk_renderer_properties[PROP_DRAWING_CONTEXT] =
g_param_spec_object ("drawing-context",
"Drawing Context",
"The drawing context used by the renderer",
GDK_TYPE_DRAWING_CONTEXT,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
/** /**
* GskRenderer:use-alpha: * GskRenderer:use-alpha:
* *
@ -531,6 +501,7 @@ gsk_renderer_init (GskRenderer *self)
graphene_matrix_init_identity (&priv->projection); graphene_matrix_init_identity (&priv->projection);
priv->auto_clear = TRUE; priv->auto_clear = TRUE;
priv->scale_factor = 1;
priv->min_filter = GSK_SCALING_FILTER_LINEAR; priv->min_filter = GSK_SCALING_FILTER_LINEAR;
priv->mag_filter = GSK_SCALING_FILTER_LINEAR; priv->mag_filter = GSK_SCALING_FILTER_LINEAR;
@ -565,9 +536,6 @@ gsk_renderer_set_viewport (GskRenderer *renderer,
return; return;
graphene_rect_init_from_rect (&priv->viewport, viewport); graphene_rect_init_from_rect (&priv->viewport, viewport);
priv->needs_viewport_resize = TRUE;
priv->needs_modelview_update = TRUE;
priv->needs_projection_update = TRUE;
g_object_notify_by_pspec (G_OBJECT (renderer), gsk_renderer_properties[PROP_VIEWPORT]); g_object_notify_by_pspec (G_OBJECT (renderer), gsk_renderer_properties[PROP_VIEWPORT]);
} }
@ -618,8 +586,6 @@ gsk_renderer_set_modelview (GskRenderer *renderer,
else else
graphene_matrix_init_from_matrix (&priv->modelview, modelview); graphene_matrix_init_from_matrix (&priv->modelview, modelview);
priv->needs_modelview_update = TRUE;
g_object_notify_by_pspec (G_OBJECT (renderer), gsk_renderer_properties[PROP_MODELVIEW]); g_object_notify_by_pspec (G_OBJECT (renderer), gsk_renderer_properties[PROP_MODELVIEW]);
} }
@ -666,8 +632,6 @@ gsk_renderer_set_projection (GskRenderer *renderer,
else else
graphene_matrix_init_from_matrix (&priv->projection, projection); graphene_matrix_init_from_matrix (&priv->projection, projection);
priv->needs_projection_update = TRUE;
g_object_notify_by_pspec (G_OBJECT (renderer), gsk_renderer_properties[PROP_PROJECTION]); g_object_notify_by_pspec (G_OBJECT (renderer), gsk_renderer_properties[PROP_PROJECTION]);
} }
@ -692,67 +656,6 @@ gsk_renderer_get_projection (GskRenderer *renderer,
graphene_matrix_init_from_matrix (projection, &priv->projection); graphene_matrix_init_from_matrix (projection, &priv->projection);
} }
static void
gsk_renderer_invalidate_tree (GskRenderNode *node,
gpointer data)
{
GskRenderer *self = data;
GskRendererPrivate *priv = gsk_renderer_get_instance_private (self);
GSK_NOTE (RENDERER, g_print ("Invalidating tree.\n"));
/* Since the scene graph has changed in some way, we need to re-validate it. */
priv->needs_tree_validation = TRUE;
}
/**
* gsk_renderer_set_root_node:
* @renderer: a #GskRenderer
* @root: (nullable): a #GskRenderNode
*
* Sets the root node of the scene graph to be rendered.
*
* The #GskRenderer will acquire a reference on @root.
*
* Since: 3.22
*/
void
gsk_renderer_set_root_node (GskRenderer *renderer,
GskRenderNode *root)
{
GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
GskRenderNode *old_root;
g_return_if_fail (GSK_IS_RENDERER (renderer));
g_return_if_fail (GSK_IS_RENDER_NODE (root));
old_root = priv->root_node != NULL ? g_object_ref (priv->root_node) : NULL;
if (g_set_object (&priv->root_node, root))
{
/* We need to unset the invalidate function on the old instance */
if (old_root != NULL)
{
gsk_render_node_set_invalidate_func (old_root, NULL, NULL, NULL);
gsk_renderer_clear_tree (renderer, old_root);
g_object_unref (old_root);
}
if (priv->root_node != NULL)
gsk_render_node_set_invalidate_func (priv->root_node,
gsk_renderer_invalidate_tree,
renderer,
NULL);
/* If we don't have a root node, there's really no point in validating a
* tree that it's not going to be drawn
*/
priv->needs_tree_validation = priv->root_node != NULL;
g_object_notify_by_pspec (G_OBJECT (renderer), gsk_renderer_properties[PROP_ROOT_NODE]);
}
}
/** /**
* gsk_renderer_set_scaling_filters: * gsk_renderer_set_scaling_filters:
* @renderer: a #GskRenderer * @renderer: a #GskRenderer
@ -819,112 +722,100 @@ gsk_renderer_get_scaling_filters (GskRenderer *renderer,
*mag_filter = priv->mag_filter; *mag_filter = priv->mag_filter;
} }
/**
* gsk_renderer_set_surface:
* @renderer: a #GskRenderer
* @surface: (nullable): a Cairo surface
*
* Sets the #cairo_surface_t used as the target rendering surface.
*
* This function will acquire a reference to @surface.
*
* See also: gsk_renderer_set_window()
*
* Since: 3.22
*/
void void
gsk_renderer_set_surface (GskRenderer *renderer, gsk_renderer_set_scale_factor (GskRenderer *renderer,
cairo_surface_t *surface) int scale_factor)
{ {
GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer); GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
g_return_if_fail (GSK_IS_RENDERER (renderer)); g_return_if_fail (GSK_IS_RENDERER (renderer));
if (priv->surface == surface) if (priv->scale_factor != scale_factor)
return; {
priv->scale_factor = scale_factor;
g_clear_pointer (&priv->surface, cairo_surface_destroy); g_object_notify_by_pspec (G_OBJECT (renderer), gsk_renderer_properties[PROP_SCALE_FACTOR]);
}
if (surface != NULL)
priv->surface = cairo_surface_reference (surface);
g_object_notify_by_pspec (G_OBJECT (renderer), gsk_renderer_properties[PROP_SURFACE]);
} }
/** int
* gsk_renderer_get_surface: gsk_renderer_get_scale_factor (GskRenderer *renderer)
* @renderer: a #GskRenderer
*
* Retrieve the target rendering surface used by @renderer.
*
* If you did not use gsk_renderer_set_surface(), a compatible surface
* will be created by using the #GdkWindow passed to gsk_renderer_set_window().
*
* Returns: (transfer none) (nullable): a Cairo surface
*
* Since: 3.22
*/
cairo_surface_t *
gsk_renderer_get_surface (GskRenderer *renderer)
{ {
GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer); GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
g_return_val_if_fail (GSK_IS_RENDERER (renderer), NULL); g_return_val_if_fail (GSK_IS_RENDERER (renderer), 1);
if (priv->surface != NULL) return priv->scale_factor;
return priv->surface;
if (priv->window != NULL)
{
int scale = gdk_window_get_scale_factor (priv->window);
int width = gdk_window_get_width (priv->window);
int height = gdk_window_get_height (priv->window);
cairo_content_t content;
if (priv->use_alpha)
content = CAIRO_CONTENT_COLOR_ALPHA;
else
content = CAIRO_CONTENT_COLOR;
GSK_NOTE (RENDERER, g_print ("Creating surface from window [%p] (w:%d, h:%d, s:%d, a:%s)\n",
priv->window,
width, height, scale,
priv->use_alpha ? "y" : "n"));
priv->surface = gdk_window_create_similar_surface (priv->window,
content,
width, height);
}
return priv->surface;
} }
void void
gsk_renderer_set_draw_context (GskRenderer *renderer, gsk_renderer_set_window (GskRenderer *renderer,
cairo_t *cr) GdkWindow *window)
{ {
GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer); GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
g_return_if_fail (GSK_IS_RENDERER (renderer)); g_return_if_fail (GSK_IS_RENDERER (renderer));
g_return_if_fail (!priv->is_realized);
g_return_if_fail (window == NULL || GDK_IS_WINDOW (window));
if (priv->draw_context == cr) if (g_set_object (&priv->window, window))
return; g_object_notify_by_pspec (G_OBJECT (renderer), gsk_renderer_properties[PROP_WINDOW]);
g_clear_pointer (&priv->draw_context, cairo_destroy);
priv->draw_context = cr != NULL ? cairo_reference (cr) : NULL;
} }
cairo_t * /**
gsk_renderer_get_draw_context (GskRenderer *renderer) * gsk_renderer_get_window:
* @renderer: a #GskRenderer
*
* Retrieves the #GdkWindow set using gsk_renderer_set_window().
*
* Returns: (transfer none) (nullable): a #GdkWindow
*
* Since: 3.22
*/
GdkWindow *
gsk_renderer_get_window (GskRenderer *renderer)
{ {
GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer); GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
g_return_val_if_fail (GSK_IS_RENDERER (renderer), NULL); g_return_val_if_fail (GSK_IS_RENDERER (renderer), NULL);
if (priv->draw_context != NULL) return priv->window;
return priv->draw_context; }
return cairo_create (gsk_renderer_get_surface (renderer)); /*< private >
* gsk_renderer_get_root_node:
* @renderer: a #GskRenderer
*
* Retrieves the #GskRenderNode used by @renderer.
*
* Returns: (transfer none) (nullable): a #GskRenderNode
*/
GskRenderNode *
gsk_renderer_get_root_node (GskRenderer *renderer)
{
GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
g_return_val_if_fail (GSK_IS_RENDERER (renderer), NULL);
return priv->root_node;
}
/*< private >
* gsk_renderer_get_drawing_context:
* @renderer: a #GskRenderer
*
* Retrieves the #GdkDrawingContext used by @renderer.
*
* Returns: (transfer none) (nullable): a #GdkDrawingContext
*/
GdkDrawingContext *
gsk_renderer_get_drawing_context (GskRenderer *renderer)
{
GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
g_return_val_if_fail (GSK_IS_RENDERER (renderer), NULL);
return priv->drawing_context;
} }
/** /**
@ -947,26 +838,6 @@ gsk_renderer_get_display (GskRenderer *renderer)
return priv->display; return priv->display;
} }
/**
* gsk_renderer_get_root_node:
* @renderer: a #GskRenderer
*
* Retrieves the root node of the scene graph.
*
* Returns: (transfer none) (nullable): a #GskRenderNode
*
* Since: 3.22
*/
GskRenderNode *
gsk_renderer_get_root_node (GskRenderer *renderer)
{
GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
g_return_val_if_fail (GSK_IS_RENDERER (renderer), NULL);
return priv->root_node;
}
/*< private > /*< private >
* gsk_renderer_is_realized: * gsk_renderer_is_realized:
* @renderer: a #GskRenderer * @renderer: a #GskRenderer
@ -987,51 +858,6 @@ gsk_renderer_is_realized (GskRenderer *renderer)
return priv->is_realized; return priv->is_realized;
} }
/**
* gsk_renderer_set_window:
* @renderer: a #GskRenderer
* @window: (nullable): a #GdkWindow
*
* Sets the #GdkWindow used to create the target rendering surface.
*
* See also: gsk_renderer_set_surface()
*
* Since: 3.22
*/
void
gsk_renderer_set_window (GskRenderer *renderer,
GdkWindow *window)
{
GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
g_return_if_fail (GSK_IS_RENDERER (renderer));
g_return_if_fail (window == NULL || GDK_IS_WINDOW (window));
g_return_if_fail (!priv->is_realized);
if (g_set_object (&priv->window, window))
g_object_notify_by_pspec (G_OBJECT (renderer), gsk_renderer_properties[PROP_WINDOW]);
}
/**
* gsk_renderer_get_window:
* @renderer: a #GskRenderer
*
* Retrieves the #GdkWindow set with gsk_renderer_set_window().
*
* Returns: (transfer none) (nullable): a #GdkWindow
*
* Since: 3.22
*/
GdkWindow *
gsk_renderer_get_window (GskRenderer *renderer)
{
GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
g_return_val_if_fail (GSK_IS_RENDERER (renderer), NULL);
return priv->window;
}
/** /**
* gsk_renderer_realize: * gsk_renderer_realize:
* @renderer: a #GskRenderer * @renderer: a #GskRenderer
@ -1051,12 +877,6 @@ gsk_renderer_realize (GskRenderer *renderer)
if (priv->is_realized) if (priv->is_realized)
return TRUE; return TRUE;
if (priv->window == NULL && priv->surface == NULL)
{
g_critical ("No rendering surface has been set.");
return FALSE;
}
priv->is_realized = GSK_RENDERER_GET_CLASS (renderer)->realize (renderer); priv->is_realized = GSK_RENDERER_GET_CLASS (renderer)->realize (renderer);
return priv->is_realized; return priv->is_realized;
@ -1085,156 +905,41 @@ gsk_renderer_unrealize (GskRenderer *renderer)
priv->is_realized = FALSE; priv->is_realized = FALSE;
} }
/*< private >
* gsk_renderer_maybe_resize_viewport:
* @renderer: a #GskRenderer
*
* Optionally resize the viewport of @renderer.
*
* This function should be called by gsk_renderer_render().
*
* This function may call @GskRendererClass.resize_viewport().
*/
void
gsk_renderer_maybe_resize_viewport (GskRenderer *renderer)
{
GskRendererClass *renderer_class = GSK_RENDERER_GET_CLASS (renderer);
GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
if (priv->needs_viewport_resize)
{
renderer_class->resize_viewport (renderer, &priv->viewport);
priv->needs_viewport_resize = FALSE;
GSK_NOTE (RENDERER, g_print ("Viewport size: %g x %g\n",
priv->viewport.size.width,
priv->viewport.size.height));
/* If the target surface has been created from a window, we need
* to clear it, so that it gets recreated with the right size
*/
if (priv->window != NULL && priv->surface != NULL)
g_clear_pointer (&priv->surface, cairo_surface_destroy);
}
}
/*< private >
* gsk_renderer_maybe_update:
* @renderer: a #GskRenderer
*
* Optionally recomputes the modelview-projection matrix used by
* the @renderer.
*
* This function should be called by gsk_renderer_render().
*
* This function may call @GskRendererClass.update().
*/
void
gsk_renderer_maybe_update (GskRenderer *renderer)
{
GskRendererClass *renderer_class = GSK_RENDERER_GET_CLASS (renderer);
GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
if (priv->needs_modelview_update || priv->needs_projection_update)
{
renderer_class->update (renderer, &priv->modelview, &priv->projection);
priv->needs_modelview_update = FALSE;
priv->needs_projection_update = FALSE;
}
}
void
gsk_renderer_clear_tree (GskRenderer *renderer,
GskRenderNode *old_root)
{
GSK_RENDERER_GET_CLASS (renderer)->clear_tree (renderer, old_root);
}
/*< private >
* gsk_renderer_maybe_validate_tree:
* @renderer: a #GskRenderer
*
* Optionally validates the #GskRenderNode scene graph, and uses it
* to generate more efficient intermediate representations depending
* on the type of @renderer.
*
* This function should be called by gsk_renderer_render().
*
* This function may call @GskRendererClas.validate_tree().
*/
void
gsk_renderer_maybe_validate_tree (GskRenderer *renderer)
{
GskRendererClass *renderer_class = GSK_RENDERER_GET_CLASS (renderer);
GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
if (priv->root_node == NULL)
return;
/* Ensure that the render nodes are valid; this will change the
* needs_tree_validation flag on the renderer, if needed
*/
gsk_render_node_validate (priv->root_node);
if (priv->needs_tree_validation)
{
/* Ensure that the Renderer can update itself */
renderer_class->validate_tree (renderer, priv->root_node);
priv->needs_tree_validation = FALSE;
}
}
/*< private >
* gsk_renderer_maybe_clear:
* @renderer: a #GskRenderer
*
* Optionally calls @GskRendererClass.clear(), depending on the value
* of #GskRenderer:auto-clear.
*
* This function should be called by gsk_renderer_render().
*/
void
gsk_renderer_maybe_clear (GskRenderer *renderer)
{
GskRendererClass *renderer_class = GSK_RENDERER_GET_CLASS (renderer);
GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
if (priv->auto_clear)
renderer_class->clear (renderer);
}
/** /**
* gsk_renderer_render: * gsk_renderer_render:
* @renderer: a #GskRenderer * @renderer: a #GskRenderer
* @root: a #GskRenderNode
* @context: a #GdkDrawingContext
* *
* Renders the scene graph associated to @renderer, using the * Renders the scene graph, described by a tree of #GskRenderNode instances,
* given target surface. * using the given #GdkDrawingContext.
*
* The @renderer will acquire a reference on the #GskRenderNode tree while
* the rendering is in progress, and will make the tree immutable.
* *
* Since: 3.22 * Since: 3.22
*/ */
void void
gsk_renderer_render (GskRenderer *renderer) gsk_renderer_render (GskRenderer *renderer,
GskRenderNode *root,
GdkDrawingContext *context)
{ {
GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer); GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
g_return_if_fail (GSK_IS_RENDERER (renderer)); g_return_if_fail (GSK_IS_RENDERER (renderer));
g_return_if_fail (priv->is_realized); g_return_if_fail (priv->is_realized);
g_return_if_fail (priv->root_node != NULL); g_return_if_fail (GSK_IS_RENDER_NODE (root));
g_return_if_fail (GDK_IS_DRAWING_CONTEXT (context));
/* We need to update the viewport and the modelview, to allow renderers g_set_object (&priv->root_node, root);
* to update their clip region and/or frustum; this allows them to cull g_set_object (&priv->drawing_context, context);
* render nodes in the tree validation phase
*/
gsk_renderer_maybe_resize_viewport (renderer);
gsk_renderer_maybe_update (renderer); gsk_render_node_make_immutable (root);
gsk_renderer_maybe_validate_tree (renderer); GSK_RENDERER_GET_CLASS (renderer)->render (renderer, root, context);
/* Clear the output surface */ g_clear_object (&priv->root_node);
gsk_renderer_maybe_clear (renderer); g_clear_object (&priv->drawing_context);
GSK_RENDERER_GET_CLASS (renderer)->render (renderer);
} }
/** /**
@ -1345,7 +1050,7 @@ gsk_renderer_get_use_alpha (GskRenderer *renderer)
* *
* Creates an appropriate #GskRenderer instance for the given @display. * Creates an appropriate #GskRenderer instance for the given @display.
* *
* Returns: (transfer full): a #GskRenderer * Returns: (transfer full) (nullable): a #GskRenderer
* *
* Since: 3.22 * Since: 3.22
*/ */
@ -1364,10 +1069,7 @@ gsk_renderer_get_for_display (GdkDisplay *display)
} }
if (use_software[0] != '0') if (use_software[0] != '0')
{ return NULL;
renderer_type = GSK_TYPE_CAIRO_RENDERER;
goto out;
}
#ifdef GDK_WINDOWING_X11 #ifdef GDK_WINDOWING_X11
if (GDK_IS_X11_DISPLAY (display)) if (GDK_IS_X11_DISPLAY (display))
@ -1377,9 +1079,9 @@ gsk_renderer_get_for_display (GdkDisplay *display)
#ifdef GDK_WINDOWING_WAYLAND #ifdef GDK_WINDOWING_WAYLAND
if (GDK_IS_WAYLAND_DISPLAY (display)) if (GDK_IS_WAYLAND_DISPLAY (display))
renderer_type = GSK_TYPE_GL_RENDERER; renderer_type = GSK_TYPE_GL_RENDERER;
else
#endif #endif
renderer_type = GSK_TYPE_CAIRO_RENDERER; else
return NULL;
GSK_NOTE (RENDERER, g_print ("Creating renderer of type '%s' for display '%s'\n", GSK_NOTE (RENDERER, g_print ("Creating renderer of type '%s' for display '%s'\n",
g_type_name (renderer_type), g_type_name (renderer_type),
@ -1387,6 +1089,5 @@ gsk_renderer_get_for_display (GdkDisplay *display)
g_assert (renderer_type != G_TYPE_INVALID); g_assert (renderer_type != G_TYPE_INVALID);
out:
return g_object_new (renderer_type, "display", display, NULL); return g_object_new (renderer_type, "display", display, NULL);
} }

View File

@ -69,44 +69,37 @@ void gsk_renderer_get_scaling_filters (GskRenderer
GskScalingFilter *min_filter, GskScalingFilter *min_filter,
GskScalingFilter *mag_filter); GskScalingFilter *mag_filter);
GDK_AVAILABLE_IN_3_22 GDK_AVAILABLE_IN_3_22
void gsk_renderer_set_scale_factor (GskRenderer *renderer,
int scale_factor);
GDK_AVAILABLE_IN_3_22
int gsk_renderer_get_scale_factor (GskRenderer *renderer);
GDK_AVAILABLE_IN_3_22
void gsk_renderer_set_auto_clear (GskRenderer *renderer, void gsk_renderer_set_auto_clear (GskRenderer *renderer,
gboolean clear); gboolean clear);
GDK_AVAILABLE_IN_3_22 GDK_AVAILABLE_IN_3_22
gboolean gsk_renderer_get_auto_clear (GskRenderer *renderer); gboolean gsk_renderer_get_auto_clear (GskRenderer *renderer);
GDK_AVAILABLE_IN_3_22 GDK_AVAILABLE_IN_3_22
void gsk_renderer_set_root_node (GskRenderer *renderer, void gsk_renderer_set_use_alpha (GskRenderer *renderer,
GskRenderNode *root); gboolean use_alpha);
GDK_AVAILABLE_IN_3_22 GDK_AVAILABLE_IN_3_22
GskRenderNode * gsk_renderer_get_root_node (GskRenderer *renderer); gboolean gsk_renderer_get_use_alpha (GskRenderer *renderer);
GDK_AVAILABLE_IN_3_22
void gsk_renderer_set_surface (GskRenderer *renderer,
cairo_surface_t *surface);
GDK_AVAILABLE_IN_3_22
cairo_surface_t * gsk_renderer_get_surface (GskRenderer *renderer);
GDK_AVAILABLE_IN_3_22 GDK_AVAILABLE_IN_3_22
void gsk_renderer_set_window (GskRenderer *renderer, void gsk_renderer_set_window (GskRenderer *renderer,
GdkWindow *window); GdkWindow *window);
GDK_AVAILABLE_IN_3_22 GDK_AVAILABLE_IN_3_22
GdkWindow * gsk_renderer_get_window (GskRenderer *renderer); GdkWindow * gsk_renderer_get_window (GskRenderer *renderer);
GDK_AVAILABLE_IN_3_22
void gsk_renderer_set_draw_context (GskRenderer *renderer,
cairo_t *cr);
GDK_AVAILABLE_IN_3_22
cairo_t * gsk_renderer_get_draw_context (GskRenderer *renderer);
GDK_AVAILABLE_IN_3_22 GDK_AVAILABLE_IN_3_22
GdkDisplay * gsk_renderer_get_display (GskRenderer *renderer); GdkDisplay * gsk_renderer_get_display (GskRenderer *renderer);
GDK_AVAILABLE_IN_3_22
void gsk_renderer_set_use_alpha (GskRenderer *renderer,
gboolean use_alpha);
GDK_AVAILABLE_IN_3_22
gboolean gsk_renderer_get_use_alpha (GskRenderer *renderer);
GDK_AVAILABLE_IN_3_22 GDK_AVAILABLE_IN_3_22
gboolean gsk_renderer_realize (GskRenderer *renderer); gboolean gsk_renderer_realize (GskRenderer *renderer);
GDK_AVAILABLE_IN_3_22 GDK_AVAILABLE_IN_3_22
void gsk_renderer_unrealize (GskRenderer *renderer); void gsk_renderer_unrealize (GskRenderer *renderer);
GDK_AVAILABLE_IN_3_22 GDK_AVAILABLE_IN_3_22
void gsk_renderer_render (GskRenderer *renderer); void gsk_renderer_render (GskRenderer *renderer,
GskRenderNode *root,
GdkDrawingContext *context);
G_END_DECLS G_END_DECLS

View File

@ -39,28 +39,15 @@ struct _GskRendererClass
gboolean (* realize) (GskRenderer *renderer); gboolean (* realize) (GskRenderer *renderer);
void (* unrealize) (GskRenderer *renderer); void (* unrealize) (GskRenderer *renderer);
void (* resize_viewport) (GskRenderer *renderer, void (* render) (GskRenderer *renderer,
const graphene_rect_t *viewport); GskRenderNode *root,
void (* update) (GskRenderer *renderer, GdkDrawingContext *context);
const graphene_matrix_t *modelview,
const graphene_matrix_t *projection);
void (* validate_tree) (GskRenderer *renderer,
GskRenderNode *root);
void (* clear_tree) (GskRenderer *renderer,
GskRenderNode *old_root);
void (* clear) (GskRenderer *renderer);
void (* render) (GskRenderer *renderer);
}; };
gboolean gsk_renderer_is_realized (GskRenderer *renderer); gboolean gsk_renderer_is_realized (GskRenderer *renderer);
void gsk_renderer_clear_tree (GskRenderer *renderer, GskRenderNode * gsk_renderer_get_root_node (GskRenderer *renderer);
GskRenderNode *old_root); GdkDrawingContext * gsk_renderer_get_drawing_context (GskRenderer *renderer);
void gsk_renderer_maybe_resize_viewport (GskRenderer *renderer);
void gsk_renderer_maybe_update (GskRenderer *renderer);
void gsk_renderer_maybe_validate_tree (GskRenderer *renderer);
void gsk_renderer_maybe_clear (GskRenderer *renderer);
G_END_DECLS G_END_DECLS

View File

@ -41,8 +41,6 @@ gsk_render_node_dispose (GObject *gobject)
GskRenderNode *self = GSK_RENDER_NODE (gobject); GskRenderNode *self = GSK_RENDER_NODE (gobject);
GskRenderNodeIter iter; GskRenderNodeIter iter;
gsk_render_node_set_invalidate_func (self, NULL, NULL, NULL);
gsk_render_node_iter_init (&iter, self); gsk_render_node_iter_init (&iter, self);
while (gsk_render_node_iter_next (&iter, NULL)) while (gsk_render_node_iter_next (&iter, NULL))
gsk_render_node_iter_remove (&iter); gsk_render_node_iter_remove (&iter);
@ -50,19 +48,12 @@ gsk_render_node_dispose (GObject *gobject)
G_OBJECT_CLASS (gsk_render_node_parent_class)->dispose (gobject); G_OBJECT_CLASS (gsk_render_node_parent_class)->dispose (gobject);
} }
static void
gsk_render_node_real_resize (GskRenderNode *node)
{
}
static void static void
gsk_render_node_class_init (GskRenderNodeClass *klass) gsk_render_node_class_init (GskRenderNodeClass *klass)
{ {
GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->dispose = gsk_render_node_dispose; gobject_class->dispose = gsk_render_node_dispose;
klass->resize = gsk_render_node_real_resize;
} }
static void static void
@ -74,6 +65,8 @@ gsk_render_node_init (GskRenderNode *self)
graphene_matrix_init_identity (&self->child_transform); graphene_matrix_init_identity (&self->child_transform);
self->opacity = 1.0; self->opacity = 1.0;
self->is_mutable = TRUE;
} }
/** /**
@ -204,6 +197,13 @@ gsk_render_node_insert_child_internal (GskRenderNode *node,
return; return;
} }
if (!node->is_mutable)
{
g_critical ("The render node of type '%s' is immutable.",
G_OBJECT_TYPE_NAME (node));
return;
}
insert_func (node, child, insert_func_data); insert_func (node, child, insert_func_data);
g_object_ref (child); g_object_ref (child);
@ -216,21 +216,6 @@ gsk_render_node_insert_child_internal (GskRenderNode *node,
node->age += 1; node->age += 1;
node->needs_world_matrix_update = TRUE; node->needs_world_matrix_update = TRUE;
/* Transfer invalidated children to the current top-level */
if (child->invalidated_descendants != NULL)
{
if (node->parent == NULL)
node->invalidated_descendants = child->invalidated_descendants;
else
{
GskRenderNode *tmp = gsk_render_node_get_toplevel (node);
tmp->invalidated_descendants = child->invalidated_descendants;
}
child->invalidated_descendants = NULL;
}
if (child->prev_sibling == NULL) if (child->prev_sibling == NULL)
node->first_child = child; node->first_child = child;
if (child->next_sibling == NULL) if (child->next_sibling == NULL)
@ -296,6 +281,62 @@ insert_child_at_pos (GskRenderNode *node,
} }
} }
/**
* gsk_render_node_append_child:
* @node: a #GskRenderNode
* @child: a #GskRenderNode
*
* Appends @child to the list of children of @node.
*
* This function acquires a reference on @child.
*
* Returns: (transfer none): the #GskRenderNode
*
* Since: 3.22
*/
GskRenderNode *
gsk_render_node_append_child (GskRenderNode *node,
GskRenderNode *child)
{
g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
g_return_val_if_fail (GSK_IS_RENDER_NODE (child), node);
g_return_val_if_fail (node->is_mutable, node);
gsk_render_node_insert_child_internal (node, child,
insert_child_at_pos,
GINT_TO_POINTER (node->n_children));
return node;
}
/**
* gsk_render_node_prepend_child:
* @node: a #GskRenderNode
* @child: a #GskRenderNode
*
* Prepends @child to the list of children of @node.
*
* This function acquires a reference on @child.
*
* Returns: (transfer none): the #GskRenderNode
*
* Since: 3.22
*/
GskRenderNode *
gsk_render_node_prepend_child (GskRenderNode *node,
GskRenderNode *child)
{
g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
g_return_val_if_fail (GSK_IS_RENDER_NODE (child), node);
g_return_val_if_fail (node->is_mutable, node);
gsk_render_node_insert_child_internal (node, child,
insert_child_at_pos,
GINT_TO_POINTER (0));
return node;
}
/** /**
* gsk_render_node_insert_child_at_pos: * gsk_render_node_insert_child_at_pos:
* @node: a #GskRenderNode * @node: a #GskRenderNode
@ -322,6 +363,7 @@ gsk_render_node_insert_child_at_pos (GskRenderNode *node,
{ {
g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL); g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
g_return_val_if_fail (GSK_IS_RENDER_NODE (child), node); g_return_val_if_fail (GSK_IS_RENDER_NODE (child), node);
g_return_val_if_fail (node->is_mutable, node);
gsk_render_node_insert_child_internal (node, child, gsk_render_node_insert_child_internal (node, child,
insert_child_at_pos, insert_child_at_pos,
@ -382,6 +424,7 @@ gsk_render_node_insert_child_before (GskRenderNode *node,
g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL); g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
g_return_val_if_fail (GSK_IS_RENDER_NODE (child), node); g_return_val_if_fail (GSK_IS_RENDER_NODE (child), node);
g_return_val_if_fail (sibling == NULL || GSK_IS_RENDER_NODE (sibling), node); g_return_val_if_fail (sibling == NULL || GSK_IS_RENDER_NODE (sibling), node);
g_return_val_if_fail (node->is_mutable, node);
gsk_render_node_insert_child_internal (node, child, insert_child_before, sibling); gsk_render_node_insert_child_internal (node, child, insert_child_before, sibling);
@ -440,6 +483,7 @@ gsk_render_node_insert_child_after (GskRenderNode *node,
g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL); g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
g_return_val_if_fail (GSK_IS_RENDER_NODE (child), node); g_return_val_if_fail (GSK_IS_RENDER_NODE (child), node);
g_return_val_if_fail (sibling == NULL || GSK_IS_RENDER_NODE (sibling), node); g_return_val_if_fail (sibling == NULL || GSK_IS_RENDER_NODE (sibling), node);
g_return_val_if_fail (node->is_mutable, node);
if (sibling != NULL) if (sibling != NULL)
g_return_val_if_fail (sibling->parent == node, node); g_return_val_if_fail (sibling->parent == node, node);
@ -500,6 +544,8 @@ gsk_render_node_replace_child (GskRenderNode *node,
g_return_val_if_fail (new_child->parent == NULL, node); g_return_val_if_fail (new_child->parent == NULL, node);
g_return_val_if_fail (old_child->parent == node, node); g_return_val_if_fail (old_child->parent == node, node);
g_return_val_if_fail (node->is_mutable, node);
clos.prev_sibling = old_child->prev_sibling; clos.prev_sibling = old_child->prev_sibling;
clos.next_sibling = old_child->next_sibling; clos.next_sibling = old_child->next_sibling;
gsk_render_node_remove_child (node, old_child); gsk_render_node_remove_child (node, old_child);
@ -529,6 +575,7 @@ gsk_render_node_remove_child (GskRenderNode *node,
g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL); g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
g_return_val_if_fail (GSK_IS_RENDER_NODE (child), node); g_return_val_if_fail (GSK_IS_RENDER_NODE (child), node);
g_return_val_if_fail (node->is_mutable, node);
if (child->parent != node) if (child->parent != node)
{ {
@ -582,6 +629,7 @@ gsk_render_node_remove_all_children (GskRenderNode *node)
GskRenderNodeIter iter; GskRenderNodeIter iter;
g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL); g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
g_return_val_if_fail (node->is_mutable, node);
if (node->n_children == 0) if (node->n_children == 0)
return node; return node;
@ -631,13 +679,12 @@ gsk_render_node_set_bounds (GskRenderNode *node,
const graphene_rect_t *bounds) const graphene_rect_t *bounds)
{ {
g_return_if_fail (GSK_IS_RENDER_NODE (node)); g_return_if_fail (GSK_IS_RENDER_NODE (node));
g_return_if_fail (node->is_mutable);
if (bounds == NULL) if (bounds == NULL)
graphene_rect_init_from_rect (&node->bounds, graphene_rect_zero ()); graphene_rect_init_from_rect (&node->bounds, graphene_rect_zero ());
else else
graphene_rect_init_from_rect (&node->bounds, bounds); graphene_rect_init_from_rect (&node->bounds, bounds);
gsk_render_node_queue_invalidate (node, GSK_RENDER_NODE_CHANGES_UPDATE_BOUNDS);
} }
/** /**
@ -673,6 +720,7 @@ gsk_render_node_set_transform (GskRenderNode *node,
const graphene_matrix_t *transform) const graphene_matrix_t *transform)
{ {
g_return_if_fail (GSK_IS_RENDER_NODE (node)); g_return_if_fail (GSK_IS_RENDER_NODE (node));
g_return_if_fail (node->is_mutable);
if (transform == NULL) if (transform == NULL)
graphene_matrix_init_identity (&node->transform); graphene_matrix_init_identity (&node->transform);
@ -680,7 +728,6 @@ gsk_render_node_set_transform (GskRenderNode *node,
graphene_matrix_init_from_matrix (&node->transform, transform); graphene_matrix_init_from_matrix (&node->transform, transform);
node->transform_set = !graphene_matrix_is_identity (&node->transform); node->transform_set = !graphene_matrix_is_identity (&node->transform);
gsk_render_node_queue_invalidate (node, GSK_RENDER_NODE_CHANGES_UPDATE_TRANSFORM);
} }
/** /**
@ -697,10 +744,8 @@ void
gsk_render_node_set_child_transform (GskRenderNode *node, gsk_render_node_set_child_transform (GskRenderNode *node,
const graphene_matrix_t *transform) const graphene_matrix_t *transform)
{ {
GskRenderNodeIter iter;
GskRenderNode *child;
g_return_if_fail (GSK_IS_RENDER_NODE (node)); g_return_if_fail (GSK_IS_RENDER_NODE (node));
g_return_if_fail (node->is_mutable);
if (transform == NULL) if (transform == NULL)
graphene_matrix_init_identity (&node->child_transform); graphene_matrix_init_identity (&node->child_transform);
@ -708,11 +753,6 @@ gsk_render_node_set_child_transform (GskRenderNode *node,
graphene_matrix_init_from_matrix (&node->child_transform, transform); graphene_matrix_init_from_matrix (&node->child_transform, transform);
node->child_transform_set = !graphene_matrix_is_identity (&node->child_transform); node->child_transform_set = !graphene_matrix_is_identity (&node->child_transform);
/* We need to invalidate the world matrix for our children */
gsk_render_node_iter_init (&iter, node);
while (gsk_render_node_iter_next (&iter, &child))
gsk_render_node_queue_invalidate (child, GSK_RENDER_NODE_CHANGES_UPDATE_TRANSFORM);
} }
/** /**
@ -730,10 +770,9 @@ gsk_render_node_set_opacity (GskRenderNode *node,
double opacity) double opacity)
{ {
g_return_if_fail (GSK_IS_RENDER_NODE (node)); g_return_if_fail (GSK_IS_RENDER_NODE (node));
g_return_if_fail (node->is_mutable);
node->opacity = CLAMP (opacity, 0.0, 1.0); node->opacity = CLAMP (opacity, 0.0, 1.0);
gsk_render_node_queue_invalidate (node, GSK_RENDER_NODE_CHANGES_UPDATE_OPACITY);
} }
/** /**
@ -770,10 +809,9 @@ gsk_render_node_set_hidden (GskRenderNode *node,
gboolean hidden) gboolean hidden)
{ {
g_return_if_fail (GSK_IS_RENDER_NODE (node)); g_return_if_fail (GSK_IS_RENDER_NODE (node));
g_return_if_fail (node->is_mutable);
node->hidden = !!hidden; node->hidden = !!hidden;
gsk_render_node_queue_invalidate (node, GSK_RENDER_NODE_CHANGES_UPDATE_VISIBILITY);
} }
/** /**
@ -813,10 +851,9 @@ gsk_render_node_set_opaque (GskRenderNode *node,
gboolean opaque) gboolean opaque)
{ {
g_return_if_fail (GSK_IS_RENDER_NODE (node)); g_return_if_fail (GSK_IS_RENDER_NODE (node));
g_return_if_fail (node->is_mutable);
node->opaque = !!opaque; node->opaque = !!opaque;
gsk_render_node_queue_invalidate (node, GSK_RENDER_NODE_CHANGES_UPDATE_OPACITY);
} }
/** /**
@ -865,31 +902,6 @@ gsk_render_node_contains (GskRenderNode *node,
return FALSE; return FALSE;
} }
/**
* gsk_render_node_set_surface:
* @node: a #GskRenderNode
* @surface: (nullable): a Cairo surface
*
* Sets the contents of the #GskRenderNode.
*
* The @node will acquire a reference on the given @surface.
*
* Since: 3.22
*/
void
gsk_render_node_set_surface (GskRenderNode *node,
cairo_surface_t *surface)
{
g_return_if_fail (GSK_IS_RENDER_NODE (node));
g_clear_pointer (&node->surface, cairo_surface_destroy);
if (surface != NULL)
node->surface = cairo_surface_reference (surface);
gsk_render_node_queue_invalidate (node, GSK_RENDER_NODE_CHANGES_UPDATE_SURFACE);
}
/*< private > /*< private >
* gsk_render_node_get_toplevel: * gsk_render_node_get_toplevel:
* @node: a #GskRenderNode * @node: a #GskRenderNode
@ -1014,168 +1026,6 @@ gsk_render_node_get_world_matrix (GskRenderNode *node,
*mv = node->world_matrix; *mv = node->world_matrix;
} }
void
gsk_render_node_set_invalidate_func (GskRenderNode *node,
GskRenderNodeInvalidateFunc invalidate_func,
gpointer func_data,
GDestroyNotify destroy_func_data)
{
if (node->parent != NULL)
{
g_critical ("Render node of type '%s' is not a root node. Only root "
"nodes can have an invalidation function.",
G_OBJECT_TYPE_NAME (node));
return;
}
if (node->invalidate_func != NULL)
{
if (node->destroy_func_data != NULL)
node->destroy_func_data (node->func_data);
}
node->invalidate_func = invalidate_func;
node->func_data = func_data;
node->destroy_func_data = destroy_func_data;
}
GskRenderNodeChanges
gsk_render_node_get_current_state (GskRenderNode *node)
{
GskRenderNodeChanges res = 0;
if (node->needs_resize)
res |= GSK_RENDER_NODE_CHANGES_UPDATE_BOUNDS;
if (node->needs_world_matrix_update)
res |= GSK_RENDER_NODE_CHANGES_UPDATE_TRANSFORM;
if (node->needs_content_update)
res |= GSK_RENDER_NODE_CHANGES_UPDATE_SURFACE;
if (node->needs_opacity_update)
res |= GSK_RENDER_NODE_CHANGES_UPDATE_OPACITY;
if (node->needs_visibility_update)
res |= GSK_RENDER_NODE_CHANGES_UPDATE_VISIBILITY;
return res;
}
GskRenderNodeChanges
gsk_render_node_get_last_state (GskRenderNode *node)
{
return node->last_state_change;
}
void
gsk_render_node_queue_invalidate (GskRenderNode *node,
GskRenderNodeChanges changes)
{
GskRenderNodeChanges cur_invalidated_bits = 0;
GskRenderNode *root;
int i;
cur_invalidated_bits = gsk_render_node_get_current_state (node);
if ((cur_invalidated_bits & changes) != 0)
return;
node->needs_resize = (changes & GSK_RENDER_NODE_CHANGES_UPDATE_BOUNDS) != 0;
node->needs_world_matrix_update = (changes & GSK_RENDER_NODE_CHANGES_UPDATE_TRANSFORM) != 0;
node->needs_content_update = (changes & GSK_RENDER_NODE_CHANGES_UPDATE_SURFACE) != 0;
node->needs_opacity_update = (changes & GSK_RENDER_NODE_CHANGES_UPDATE_OPACITY) != 0;
node->needs_visibility_update = (changes & GSK_RENDER_NODE_CHANGES_UPDATE_VISIBILITY) != 0;
if (node->parent == NULL)
{
GSK_NOTE (RENDER_NODE, g_print ("Invalid node [%p] is top-level\n", node));
return;
}
root = gsk_render_node_get_toplevel (node);
if (root->invalidated_descendants == NULL)
root->invalidated_descendants = g_ptr_array_new ();
for (i = 0; i < root->invalidated_descendants->len; i++)
{
if (node == g_ptr_array_index (root->invalidated_descendants, i))
{
GSK_NOTE (RENDER_NODE, g_print ("Node [%p] already invalidated; skipping...\n", node));
return;
}
}
GSK_NOTE (RENDER_NODE, g_print ("Adding node [%p] to list of invalid descendants of [%p]\n", node, root));
g_ptr_array_add (root->invalidated_descendants, node);
}
void
gsk_render_node_validate (GskRenderNode *node)
{
GPtrArray *invalidated_descendants;
gboolean call_invalidate_func;
int i;
node->last_state_change = gsk_render_node_get_current_state (node);
/* We call the invalidation function if our state changed, or if
* the descendants state has changed
*/
call_invalidate_func = node->last_state_change != 0 ||
node->invalidated_descendants != NULL;
gsk_render_node_maybe_resize (node);
gsk_render_node_update_world_matrix (node, FALSE);
node->needs_content_update = FALSE;
node->needs_visibility_update = FALSE;
node->needs_opacity_update = FALSE;
/* Steal the array of invalidated descendants, so that changes caused by
* the validation will not cause recursions
*/
invalidated_descendants = node->invalidated_descendants;
node->invalidated_descendants = NULL;
if (invalidated_descendants != NULL)
{
for (i = 0; i < invalidated_descendants->len; i++)
{
GskRenderNode *child = g_ptr_array_index (invalidated_descendants, i);
child->last_state_change = 0;
GSK_NOTE (RENDER_NODE, g_print ("Validating descendant node [%p] (resize:%s, transform:%s)\n",
child,
child->needs_resize ? "yes" : "no",
child->needs_world_matrix_update ? "yes" : "no"));
child->last_state_change = gsk_render_node_get_current_state (child);
gsk_render_node_maybe_resize (child);
gsk_render_node_update_world_matrix (child, FALSE);
child->needs_content_update = FALSE;
child->needs_visibility_update = FALSE;
child->needs_opacity_update = FALSE;
}
}
g_clear_pointer (&invalidated_descendants, g_ptr_array_unref);
if (call_invalidate_func && node->invalidate_func != NULL)
node->invalidate_func (node, node->func_data);
}
void
gsk_render_node_maybe_resize (GskRenderNode *node)
{
g_return_if_fail (GSK_IS_RENDER_NODE (node));
if (!node->needs_resize)
return;
GSK_RENDER_NODE_GET_CLASS (node)->resize (node);
node->needs_resize = FALSE;
}
/** /**
* gsk_render_node_set_name: * gsk_render_node_set_name:
* @node: a #GskRenderNode * @node: a #GskRenderNode
@ -1197,23 +1047,12 @@ gsk_render_node_set_name (GskRenderNode *node,
node->name = g_strdup (name); node->name = g_strdup (name);
} }
static cairo_user_data_key_t render_node_context_key;
static void
surface_invalidate (void *data)
{
GskRenderNode *node = data;
gsk_render_node_queue_invalidate (node, GSK_RENDER_NODE_CHANGES_UPDATE_SURFACE);
}
/** /**
* gsk_render_node_get_draw_context: * gsk_render_node_get_draw_context:
* @node: a #GskRenderNode * @node: a #GskRenderNode
* *
* Creates a Cairo context for drawing using the surface associated * Creates a Cairo context for drawing using the surface associated
* to the render node. If no surface has been attached to the render * to the render node.
* node, a new surface will be created as a side effect.
* *
* Returns: (transfer full): a Cairo context used for drawing; use * Returns: (transfer full): a Cairo context used for drawing; use
* cairo_destroy() when done drawing * cairo_destroy() when done drawing
@ -1226,6 +1065,7 @@ gsk_render_node_get_draw_context (GskRenderNode *node)
cairo_t *res; cairo_t *res;
g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL); g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
g_return_val_if_fail (node->is_mutable, NULL);
if (node->surface == NULL) if (node->surface == NULL)
node->surface = cairo_image_surface_create (node->opaque ? CAIRO_FORMAT_RGB24 node->surface = cairo_image_surface_create (node->opaque ? CAIRO_FORMAT_RGB24
@ -1240,7 +1080,21 @@ gsk_render_node_get_draw_context (GskRenderNode *node)
node->bounds.size.width, node->bounds.size.height); node->bounds.size.width, node->bounds.size.height);
cairo_clip (res); cairo_clip (res);
cairo_set_user_data (res, &render_node_context_key, node, surface_invalidate);
return res; return res;
} }
void
gsk_render_node_make_immutable (GskRenderNode *node)
{
GskRenderNodeIter iter;
GskRenderNode *child;
if (!node->is_mutable)
return;
node->is_mutable = FALSE;
gsk_render_node_iter_init (&iter, node);
while (gsk_render_node_iter_next (&iter, &child))
gsk_render_node_make_immutable (child);
}

View File

@ -52,6 +52,12 @@ GskRenderNode * gsk_render_node_get_next_sibling (GskRenderNode *
GDK_AVAILABLE_IN_3_22 GDK_AVAILABLE_IN_3_22
GskRenderNode * gsk_render_node_get_previous_sibling (GskRenderNode *node); GskRenderNode * gsk_render_node_get_previous_sibling (GskRenderNode *node);
GDK_AVAILABLE_IN_3_22
GskRenderNode * gsk_render_node_append_child (GskRenderNode *node,
GskRenderNode *child);
GDK_AVAILABLE_IN_3_22
GskRenderNode * gsk_render_node_prepend_child (GskRenderNode *node,
GskRenderNode *child);
GDK_AVAILABLE_IN_3_22 GDK_AVAILABLE_IN_3_22
GskRenderNode * gsk_render_node_insert_child_at_pos (GskRenderNode *node, GskRenderNode * gsk_render_node_insert_child_at_pos (GskRenderNode *node,
GskRenderNode *child, GskRenderNode *child,
@ -103,9 +109,6 @@ void gsk_render_node_set_opaque (GskRenderNode *
GDK_AVAILABLE_IN_3_22 GDK_AVAILABLE_IN_3_22
gboolean gsk_render_node_is_opaque (GskRenderNode *node); gboolean gsk_render_node_is_opaque (GskRenderNode *node);
GDK_AVAILABLE_IN_3_22 GDK_AVAILABLE_IN_3_22
void gsk_render_node_set_surface (GskRenderNode *node,
cairo_surface_t *surface);
GDK_AVAILABLE_IN_3_22
cairo_t * gsk_render_node_get_draw_context (GskRenderNode *node); cairo_t * gsk_render_node_get_draw_context (GskRenderNode *node);
GDK_AVAILABLE_IN_3_22 GDK_AVAILABLE_IN_3_22

View File

@ -10,18 +10,6 @@ G_BEGIN_DECLS
#define GSK_IS_RENDER_NODE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSK_TYPE_RENDER_NODE)) #define GSK_IS_RENDER_NODE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSK_TYPE_RENDER_NODE))
#define GSK_RENDER_NODE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSK_TYPE_RENDER_NODE, GskRenderNodeClass)) #define GSK_RENDER_NODE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSK_TYPE_RENDER_NODE, GskRenderNodeClass))
typedef enum {
GSK_RENDER_NODE_CHANGES_UPDATE_BOUNDS = 1 << 0,
GSK_RENDER_NODE_CHANGES_UPDATE_TRANSFORM = 1 << 1,
GSK_RENDER_NODE_CHANGES_UPDATE_SURFACE = 1 << 2,
GSK_RENDER_NODE_CHANGES_UPDATE_OPACITY = 1 << 3,
GSK_RENDER_NODE_CHANGES_UPDATE_VISIBILITY = 1 << 4,
GSK_RENDER_NODE_CHANEGS_UPDATE_HIERARCHY = 1 << 5
} GskRenderNodeChanges;
typedef void (* GskRenderNodeInvalidateFunc) (GskRenderNode *node,
gpointer data);
struct _GskRenderNode struct _GskRenderNode
{ {
GObject parent_instance; GObject parent_instance;
@ -59,35 +47,22 @@ struct _GskRenderNode
/* Transformations applied to the children of the node */ /* Transformations applied to the children of the node */
graphene_matrix_t child_transform; graphene_matrix_t child_transform;
/* Invalidation function for root node */
GskRenderNodeInvalidateFunc invalidate_func;
gpointer func_data;
GDestroyNotify destroy_func_data;
/* Descendants that need to be validated; only for root node */
GPtrArray *invalidated_descendants;
GskRenderNodeChanges last_state_change;
/* Bit fields; leave at the end */ /* Bit fields; leave at the end */
gboolean is_mutable : 1;
gboolean hidden : 1; gboolean hidden : 1;
gboolean opaque : 1; gboolean opaque : 1;
gboolean transform_set : 1; gboolean transform_set : 1;
gboolean child_transform_set : 1; gboolean child_transform_set : 1;
gboolean needs_resize : 1;
gboolean needs_world_matrix_update : 1; gboolean needs_world_matrix_update : 1;
gboolean needs_content_update : 1;
gboolean needs_opacity_update : 1;
gboolean needs_visibility_update : 1;
}; };
struct _GskRenderNodeClass struct _GskRenderNodeClass
{ {
GObjectClass parent_class; GObjectClass parent_class;
void (* resize) (GskRenderNode *node);
}; };
void gsk_render_node_make_immutable (GskRenderNode *node);
void gsk_render_node_get_bounds (GskRenderNode *node, void gsk_render_node_get_bounds (GskRenderNode *node,
graphene_rect_t *frame); graphene_rect_t *frame);
void gsk_render_node_get_transform (GskRenderNode *node, void gsk_render_node_get_transform (GskRenderNode *node,
@ -104,21 +79,6 @@ void gsk_render_node_update_world_matrix (GskRenderNode *node,
void gsk_render_node_get_world_matrix (GskRenderNode *node, void gsk_render_node_get_world_matrix (GskRenderNode *node,
graphene_matrix_t *mv); graphene_matrix_t *mv);
void gsk_render_node_queue_invalidate (GskRenderNode *node,
GskRenderNodeChanges changes);
void gsk_render_node_set_invalidate_func (GskRenderNode *root,
GskRenderNodeInvalidateFunc validate_func,
gpointer data,
GDestroyNotify notify);
void gsk_render_node_validate (GskRenderNode *node);
void gsk_render_node_maybe_resize (GskRenderNode *node);
GskRenderNodeChanges gsk_render_node_get_current_state (GskRenderNode *node);
GskRenderNodeChanges gsk_render_node_get_last_state (GskRenderNode *node);
G_END_DECLS G_END_DECLS
#endif /* __GSK_RENDER_NODE_PRIVATE_H__ */ #endif /* __GSK_RENDER_NODE_PRIVATE_H__ */

View File

@ -163,7 +163,6 @@ noinst_PROGRAMS = $(TEST_PROGS) \
testpopup \ testpopup \
testpopupat \ testpopupat \
testgaction \ testgaction \
testgskrenderer \
$(NULL) $(NULL)
if USE_X11 if USE_X11
@ -281,7 +280,6 @@ testtitlebar_DEPENDENCIES = $(TEST_DEPS)
testwindowsize_DEPENDENCIES = $(TEST_DEPS) testwindowsize_DEPENDENCIES = $(TEST_DEPS)
listmodel_DEPENDENCIES = $(TEST_DEPS) listmodel_DEPENDENCIES = $(TEST_DEPS)
foreigndrawing_DEPENDENCIES = $(TEST_DEPS) foreigndrawing_DEPENDENCIES = $(TEST_DEPS)
testgskrenderer_DEPENDENCIES = $(TEST_DEPS)
animated_resizing_SOURCES = \ animated_resizing_SOURCES = \
animated-resizing.c \ animated-resizing.c \
@ -490,8 +488,6 @@ listmodel_SOURCES = listmodel.c
foreigndrawing_SOURCES = foreigndrawing.c foreigndrawing_SOURCES = foreigndrawing.c
testgskrenderer_SOURCES = testgskrenderer.c
EXTRA_DIST += \ EXTRA_DIST += \
gradient1.png \ gradient1.png \
testgtk.1 \ testgtk.1 \

View File

@ -1,229 +0,0 @@
#include "config.h"
#include <graphene.h>
#include <cairo.h>
#include <gsk/gsk.h>
#include <gtk/gtk.h>
#define BOX_SIZE 50.f
#define PADDING 10.f
#define ROOT_SIZE BOX_SIZE * 2 + PADDING * 2
static void
create_color_surface (cairo_t *cr, GdkRGBA *color, int w, int h)
{
cairo_set_source_rgba (cr, color->red, color->green, color->blue, color->alpha);
cairo_rectangle (cr, 0, 0, w, h);
cairo_fill (cr);
}
static GskRenderer *
get_renderer (GtkWidget *widget)
{
GskRenderer *res;
res = g_object_get_data (G_OBJECT (widget), "-gsk-renderer");
if (res == NULL)
{
res = gsk_renderer_get_for_display (gtk_widget_get_display (widget));
g_object_set_data_full (G_OBJECT (widget), "-gsk-renderer",
res,
(GDestroyNotify) g_object_unref);
}
return res;
}
static void
create_scene (GskRenderer *renderer)
{
GskRenderNode *root, *node;
graphene_matrix_t ctm;
cairo_t *cr;
root = gsk_render_node_new ();
gsk_render_node_set_name (root, "Root node");
gsk_render_node_set_bounds (root, &(graphene_rect_t) {
.origin.x = 0.f,
.origin.y = 0.f,
.size.width = ROOT_SIZE,
.size.height = ROOT_SIZE
});
cr = gsk_render_node_get_draw_context (root);
create_color_surface (cr, &(GdkRGBA) { .red = 1, .green = 0, .blue = 0, .alpha = 1 }, ROOT_SIZE, ROOT_SIZE);
cairo_destroy (cr);
gsk_renderer_set_root_node (renderer, root);
g_object_set_data (G_OBJECT (renderer), "-gsk-renderer-root-node", root);
g_object_unref (root);
node = gsk_render_node_new ();
gsk_render_node_set_name (node, "Green node");
gsk_render_node_set_bounds (node, &(graphene_rect_t) {
.origin.x = 0.f,
.origin.y = 0.f,
.size.width = BOX_SIZE,
.size.height = BOX_SIZE
});
cr = gsk_render_node_get_draw_context (node);
create_color_surface (cr, &(GdkRGBA) { .red = 0, .green = 1, .blue = 0, .alpha = 1 }, BOX_SIZE, BOX_SIZE);
cairo_destroy (cr);
graphene_matrix_init_translate (&ctm, &(graphene_point3d_t) { .x = -0.5, .y = -0.5, .z = 0.f });
gsk_render_node_set_transform (node, &ctm);
gsk_render_node_insert_child_at_pos (root, node, 0);
g_object_unref (node);
node = gsk_render_node_new ();
gsk_render_node_set_name (node, "Blue node");
gsk_render_node_set_bounds (node, &(graphene_rect_t) {
.origin.x = 0.f,
.origin.y = 0.f,
.size.width = BOX_SIZE,
.size.height = BOX_SIZE
});
cr = gsk_render_node_get_draw_context (node);
create_color_surface (cr, &(GdkRGBA) { .red = 0, .green = 0, .blue = 1, .alpha = 1 }, BOX_SIZE, BOX_SIZE);
cairo_destroy (cr);
graphene_matrix_init_translate (&ctm, &(graphene_point3d_t) { .x = 0.5, .y = 0.5, .z = 0.f });
gsk_render_node_set_transform (node, &ctm);
gsk_render_node_insert_child_at_pos (root, node, 1);
g_object_unref (node);
}
static void
realize (GtkWidget *widget)
{
GskRenderer *renderer = get_renderer (widget);
gsk_renderer_set_window (renderer, gtk_widget_get_window (widget));
gsk_renderer_set_use_alpha (renderer, TRUE);
gsk_renderer_realize (renderer);
create_scene (renderer);
}
static void
unrealize (GtkWidget *widget)
{
g_object_set_data (G_OBJECT (widget), "-gsk-renderer", NULL);
}
static void
size_allocate (GtkWidget *widget, GtkAllocation *allocation)
{
GskRenderer *renderer = get_renderer (widget);
GskRenderNode *root;
graphene_matrix_t ctm;
gsk_renderer_set_viewport (renderer, &(graphene_rect_t) {
.origin.x = 0,
.origin.y = 0,
.size.width = allocation->width,
.size.height = allocation->height
});
graphene_matrix_init_translate (&ctm, &(graphene_point3d_t) {
allocation->x,
allocation->y,
0.f
});
gsk_renderer_set_modelview (renderer, &ctm);
root = g_object_get_data (G_OBJECT (renderer), "-gsk-renderer-root-node");
if (root == NULL)
{
create_scene (renderer);
root = g_object_get_data (G_OBJECT (renderer), "-gsk-renderer-root-node");
}
graphene_matrix_init_translate (&ctm, &(graphene_point3d_t) {
.x = 0,
.y = 0,
.z = 0
});
gsk_render_node_set_transform (root, &ctm);
}
static gboolean
draw (GtkWidget *widget, cairo_t *cr)
{
GskRenderer *renderer = get_renderer (widget);
gsk_renderer_set_draw_context (renderer, cr);
gsk_renderer_render (renderer);
return TRUE;
}
static gboolean
fade_out (GtkWidget *widget,
GdkFrameClock *frame_clock,
gpointer data)
{
static gint64 first_frame_time;
static gboolean flip = FALSE;
gint64 now = gdk_frame_clock_get_frame_time (frame_clock);
if (first_frame_time == 0)
{
first_frame_time = now;
return G_SOURCE_CONTINUE;
}
double start = first_frame_time;
double end = first_frame_time + (double) 1000000;
double progress = (now - first_frame_time) / (end - start);
if (flip)
progress = 1 - progress;
if (progress < 0 || progress >= 1)
{
first_frame_time = now;
flip = !flip;
return G_SOURCE_CONTINUE;
}
GskRenderer *renderer = get_renderer (widget);
GskRenderNode *root = gsk_renderer_get_root_node (renderer);
gsk_render_node_set_opacity (root, 1.0 - progress);
gtk_widget_queue_draw (widget);
return G_SOURCE_CONTINUE;
}
int
main (int argc, char *argv[])
{
GtkWidget *window, *area;
gtk_init (NULL, NULL);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size (GTK_WINDOW (window), 400, 400);
gtk_window_set_title (GTK_WINDOW (window), "GSK Renderer");
g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
area = gtk_drawing_area_new ();
gtk_widget_set_hexpand (area, TRUE);
gtk_widget_set_vexpand (area, TRUE);
gtk_widget_set_has_window (GTK_WIDGET (area), FALSE);
gtk_widget_set_app_paintable (GTK_WIDGET (area), TRUE);
gtk_container_add (GTK_CONTAINER (window), area);
g_signal_connect (area, "realize", G_CALLBACK (realize), NULL);
g_signal_connect (area, "unrealize", G_CALLBACK (unrealize), NULL);
g_signal_connect (area, "size-allocate", G_CALLBACK (size_allocate), NULL);
g_signal_connect (area, "draw", G_CALLBACK (draw), NULL);
gtk_widget_add_tick_callback (area, fade_out, NULL, NULL);
gtk_widget_show_all (window);
gtk_main ();
}