Broadway: Add id for nodes and reuse old ones

When sending render nodes from the client to the daemon we add an id,
and whenever we're about to re-send the entire tree node we instead
send the old id. We track all the nodes for the previous frame
of the surface this way.

Having the id on the daemon side will allow us do to much better deltas.
This commit is contained in:
Alexander Larsson 2019-03-25 14:15:49 +01:00
parent d59d8b5dd4
commit fbefec52a5
7 changed files with 588 additions and 389 deletions

View File

@ -24,6 +24,7 @@ typedef enum { /* Sync changes with broadway.js */
BROADWAY_NODE_KEEP_THIS = 12,
BROADWAY_NODE_TRANSLATE = 13,
BROADWAY_NODE_DEBUG = 14,
BROADWAY_NODE_REUSE = 15,
} BroadwayNodeType;
static const char *broadway_node_type_names[] G_GNUC_UNUSED = {

View File

@ -35,7 +35,6 @@
#include <string.h>
#endif
typedef struct {
int id;
guint32 tag;
@ -126,23 +125,56 @@ struct BroadwaySurface {
gint32 transient_for;
guint32 texture;
BroadwayNode *nodes;
GHashTable *node_lookup;
};
struct _BroadwayTexture {
grefcount refcount;
guint32 id;
GBytes *bytes;
};
static void broadway_server_resync_surfaces (BroadwayServer *server);
static void send_outstanding_roundtrips (BroadwayServer *server);
static void broadway_server_ref_texture (BroadwayServer *server,
guint32 id);
static GType broadway_server_get_type (void);
G_DEFINE_TYPE (BroadwayServer, broadway_server, G_TYPE_OBJECT)
static void
broadway_node_free (BroadwayNode *node)
broadway_texture_free (BroadwayTexture *texture)
{
g_bytes_unref (texture->bytes);
g_free (texture);
}
static void
broadway_node_unref (BroadwayServer *server,
BroadwayNode *node)
{
int i;
for (i = 0; i < node->n_children; i++)
broadway_node_free (node->children[i]);
g_free (node);
if (g_ref_count_dec (&node->refcount))
{
for (i = 0; i < node->n_children; i++)
broadway_node_unref (server, node->children[i]);
if (node->texture_id)
broadway_server_release_texture (server, node->texture_id);
g_free (node);
}
}
static BroadwayNode *
broadway_node_ref (BroadwayNode *node)
{
g_ref_count_inc (&node->refcount);
return node;
}
gboolean
@ -204,7 +236,7 @@ broadway_server_init (BroadwayServer *server)
server->surface_id_hash = g_hash_table_new (NULL, NULL);
server->id_counter = 0;
server->textures = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
(GDestroyNotify)g_bytes_unref);
(GDestroyNotify)broadway_texture_free);
root = g_new0 (BroadwaySurface, 1);
root->id = server->id_counter++;
@ -241,10 +273,12 @@ broadway_server_class_init (BroadwayServerClass * class)
}
static void
broadway_surface_free (BroadwaySurface *surface)
broadway_surface_free (BroadwayServer *server,
BroadwaySurface *surface)
{
if (surface->nodes)
broadway_node_free (surface->nodes);
broadway_node_unref (server, surface->nodes);
g_hash_table_unref (surface->node_lookup);
g_free (surface);
}
@ -1477,7 +1511,7 @@ broadway_server_destroy_surface (BroadwayServer *server,
server->surfaces = g_list_remove (server->surfaces, surface);
g_hash_table_remove (server->surface_id_hash,
GINT_TO_POINTER (id));
broadway_surface_free (surface);
broadway_surface_free (server, surface);
}
}
@ -1605,53 +1639,232 @@ broadway_server_has_client (BroadwayServer *server)
return server->output != NULL;
}
#define NODE_SIZE_COLOR 1
#define NODE_SIZE_FLOAT 1
#define NODE_SIZE_POINT 2
#define NODE_SIZE_SIZE 2
#define NODE_SIZE_RECT (NODE_SIZE_POINT + NODE_SIZE_SIZE)
#define NODE_SIZE_RRECT (NODE_SIZE_RECT + 4 * NODE_SIZE_SIZE)
#define NODE_SIZE_COLOR_STOP (NODE_SIZE_FLOAT + NODE_SIZE_COLOR)
#define NODE_SIZE_SHADOW (NODE_SIZE_COLOR + 3 * NODE_SIZE_FLOAT)
static guint32
rotl (guint32 value, int shift)
{
if ((shift &= 32 - 1) == 0)
return value;
return (value << shift) | (value >> (32 - shift));
}
static BroadwayNode *
decode_nodes (BroadwayServer *server,
BroadwaySurface *surface,
int len,
guint32 data[],
GHashTable *client_texture_map,
int *pos)
{
BroadwayNode *node;
guint32 type, id;
guint32 i, n_stops, n_shadows, n_chars;
guint32 size, n_children;
gint32 texture_offset;
guint32 hash;
g_assert (*pos < len);
size = 0;
n_children = 0;
texture_offset = -1;
type = data[(*pos)++];
id = data[(*pos)++];
switch (type) {
case BROADWAY_NODE_REUSE:
node = g_hash_table_lookup (surface->node_lookup, GINT_TO_POINTER(id));
g_assert (node != NULL);
return broadway_node_ref (node);
break;
case BROADWAY_NODE_COLOR:
size = NODE_SIZE_RECT + NODE_SIZE_COLOR;
break;
case BROADWAY_NODE_BORDER:
size = NODE_SIZE_RRECT + 4 * NODE_SIZE_FLOAT + 4 * NODE_SIZE_COLOR;
break;
case BROADWAY_NODE_INSET_SHADOW:
case BROADWAY_NODE_OUTSET_SHADOW:
size = NODE_SIZE_RRECT + NODE_SIZE_COLOR + 4 * NODE_SIZE_FLOAT;
break;
case BROADWAY_NODE_TEXTURE:
texture_offset = 4;
size = 5;
break;
case BROADWAY_NODE_CONTAINER:
size = 1;
n_children = data[*pos];
break;
case BROADWAY_NODE_ROUNDED_CLIP:
size = NODE_SIZE_RRECT;
n_children = 1;
break;
case BROADWAY_NODE_CLIP:
size = NODE_SIZE_RECT;
n_children = 1;
break;
case BROADWAY_NODE_TRANSLATE:
size = NODE_SIZE_POINT;
n_children = 1;
break;
case BROADWAY_NODE_LINEAR_GRADIENT:
size = NODE_SIZE_RECT + 2 * NODE_SIZE_POINT;
n_stops = data[*pos + size++];
size += n_stops * NODE_SIZE_COLOR_STOP;
break;
case BROADWAY_NODE_SHADOW:
size = 1;
n_shadows = data[*pos];
size += n_shadows * NODE_SIZE_SHADOW;
n_children = 1;
break;
case BROADWAY_NODE_OPACITY:
size = NODE_SIZE_FLOAT;
n_children = 1;
break;
case BROADWAY_NODE_DEBUG:
n_chars = data[*pos];
size = 1 + (n_chars + 3) / 4;
n_children = 1;
break;
default:
g_assert_not_reached ();
}
node = g_malloc (sizeof(BroadwayNode) + (size - 1) * sizeof(guint32) + n_children * sizeof (BroadwayNode *));
g_ref_count_init (&node->refcount);
node->type = type;
node->id = id;
node->texture_id = 0;
node->n_children = n_children;
node->children = (BroadwayNode **)((char *)node + sizeof(BroadwayNode) + (size - 1) * sizeof(guint32));
node->n_data = size;
for (i = 0; i < size; i++)
{
node->data[i] = data[(*pos)++];
if (i == texture_offset)
{
node->texture_id = GPOINTER_TO_INT (g_hash_table_lookup (client_texture_map, GINT_TO_POINTER (node->data[i])));
broadway_server_ref_texture (server, node->texture_id);
node->data[i] = node->texture_id;
}
}
for (i = 0; i < n_children; i++)
node->children[i] = decode_nodes (server, surface, len, data, client_texture_map, pos);
hash = node->type << 16;
for (i = 0; i < size; i++)
hash ^= rotl (node->data[i], i);
for (i = 0; i < n_children; i++)
hash ^= rotl (node->children[i]->hash, i);
node->hash = hash;
return node;
}
static void
init_node_lookup (BroadwaySurface *surface,
BroadwayNode *node)
{
int i;
g_hash_table_insert (surface->node_lookup, GINT_TO_POINTER(node->id), node);
for (i = 0; i < node->n_children; i++)
init_node_lookup (surface, node->children[i]);
}
/* passes ownership of nodes */
void
broadway_server_surface_set_nodes (BroadwayServer *server,
gint id,
BroadwayNode *root)
broadway_server_surface_update_nodes (BroadwayServer *server,
gint id,
guint32 data[],
int len,
GHashTable *client_texture_map)
{
BroadwaySurface *surface;
int pos = 0;
BroadwayNode *root;
surface = broadway_server_lookup_surface (server, id);
if (surface == NULL)
return;
root = decode_nodes (server, surface, len, data, client_texture_map, &pos);
if (server->output != NULL)
broadway_output_surface_set_nodes (server->output, surface->id,
root,
surface->nodes);
if (surface->nodes)
broadway_node_free (surface->nodes);
broadway_node_unref (server, surface->nodes);
surface->nodes = root;
g_hash_table_remove_all (surface->node_lookup);
init_node_lookup (surface, surface->nodes);
}
guint32
broadway_server_upload_texture (BroadwayServer *server,
GBytes *texture)
GBytes *bytes)
{
guint32 id;
BroadwayTexture *texture;
texture = g_new0 (BroadwayTexture, 1);
g_ref_count_init (&texture->refcount);
texture->id = ++server->next_texture_id;
texture->bytes = g_bytes_ref (bytes);
id = ++server->next_texture_id;
g_hash_table_replace (server->textures,
GINT_TO_POINTER (id),
g_bytes_ref (texture));
GINT_TO_POINTER (texture->id),
texture);
if (server->output)
broadway_output_upload_texture (server->output, id, texture);
broadway_output_upload_texture (server->output, texture->id, texture->bytes);
return id;
return texture->id;
}
static void
broadway_server_ref_texture (BroadwayServer *server,
guint32 id)
{
BroadwayTexture *texture;
texture = g_hash_table_lookup (server->textures, GINT_TO_POINTER (id));
if (texture)
g_ref_count_inc (&texture->refcount);
}
void
broadway_server_release_texture (BroadwayServer *server,
guint32 id)
{
g_hash_table_remove (server->textures, GINT_TO_POINTER (id));
BroadwayTexture *texture;
if (server->output)
broadway_output_release_texture (server->output, id);
texture = g_hash_table_lookup (server->textures, GINT_TO_POINTER (id));
if (texture && g_ref_count_dec (&texture->refcount))
{
g_hash_table_remove (server->textures, GINT_TO_POINTER (id));
if (server->output)
broadway_output_release_texture (server->output, id);
}
}
gboolean
@ -1801,6 +2014,7 @@ broadway_server_new_surface (BroadwayServer *server,
surface->width = width;
surface->height = height;
surface->is_temp = is_temp;
surface->node_lookup = g_hash_table_new (g_direct_hash, g_direct_equal);
g_hash_table_insert (server->surface_id_hash,
GINT_TO_POINTER (surface->id),
@ -1835,9 +2049,12 @@ broadway_server_resync_surfaces (BroadwayServer *server)
/* First upload all textures */
g_hash_table_iter_init (&iter, server->textures);
while (g_hash_table_iter_next (&iter, &key, &value))
broadway_output_upload_texture (server->output,
GPOINTER_TO_INT (key),
(GBytes *)value);
{
BroadwayTexture *texture = value;
broadway_output_upload_texture (server->output,
GPOINTER_TO_INT (key),
texture->bytes);
}
/* Then create all surfaces */
for (l = server->surfaces; l != NULL; l = l->next)

View File

@ -19,12 +19,16 @@ typedef struct _BroadwayServerClass BroadwayServerClass;
#define BROADWAY_SERVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), BROADWAY_TYPE_SERVER, BroadwayServerClass))
typedef struct _BroadwayNode BroadwayNode;
typedef struct _BroadwayTexture BroadwayTexture;
struct _BroadwayNode {
grefcount refcount;
guint32 type;
guint32 id;
guint32 hash; /* deep hash */
guint32 n_children;
BroadwayNode **children;
guint32 texture_id;
guint32 n_data;
guint32 data[1];
};
@ -99,9 +103,11 @@ void broadway_server_release_texture (BroadwayServer *
guint32 id);
cairo_surface_t * broadway_server_create_surface (int width,
int height);
void broadway_server_surface_set_nodes (BroadwayServer *server,
void broadway_server_surface_update_nodes (BroadwayServer *server,
gint id,
BroadwayNode *root);
guint32 data[],
int len,
GHashTable *client_texture_map);
gboolean broadway_server_surface_move_resize (BroadwayServer *server,
gint id,
gboolean with_move,

View File

@ -215,125 +215,6 @@ get_client_serial (BroadwayClient *client, guint32 daemon_serial)
return client_serial;
}
#define NODE_SIZE_COLOR 1
#define NODE_SIZE_FLOAT 1
#define NODE_SIZE_POINT 2
#define NODE_SIZE_SIZE 2
#define NODE_SIZE_RECT (NODE_SIZE_POINT + NODE_SIZE_SIZE)
#define NODE_SIZE_RRECT (NODE_SIZE_RECT + 4 * NODE_SIZE_SIZE)
#define NODE_SIZE_COLOR_STOP (NODE_SIZE_FLOAT + NODE_SIZE_COLOR)
#define NODE_SIZE_SHADOW (NODE_SIZE_COLOR + 3 * NODE_SIZE_FLOAT)
static guint32
rotl (guint32 value, int shift)
{
if ((shift &= 32 - 1) == 0)
return value;
return (value << shift) | (value >> (32 - shift));
}
static BroadwayNode *
decode_nodes (BroadwayClient *client,
int len, guint32 data[], int *pos)
{
BroadwayNode *node;
guint32 type;
guint32 i, n_stops, n_shadows, n_chars;
guint32 size, n_children;
gint32 texture_offset;
guint32 hash;
g_assert (*pos < len);
size = 0;
n_children = 0;
texture_offset = -1;
type = data[(*pos)++];
switch (type) {
case BROADWAY_NODE_COLOR:
size = NODE_SIZE_RECT + NODE_SIZE_COLOR;
break;
case BROADWAY_NODE_BORDER:
size = NODE_SIZE_RRECT + 4 * NODE_SIZE_FLOAT + 4 * NODE_SIZE_COLOR;
break;
case BROADWAY_NODE_INSET_SHADOW:
case BROADWAY_NODE_OUTSET_SHADOW:
size = NODE_SIZE_RRECT + NODE_SIZE_COLOR + 4 * NODE_SIZE_FLOAT;
break;
case BROADWAY_NODE_TEXTURE:
texture_offset = 4;
size = 5;
break;
case BROADWAY_NODE_CONTAINER:
size = 1;
n_children = data[*pos];
break;
case BROADWAY_NODE_ROUNDED_CLIP:
size = NODE_SIZE_RRECT;
n_children = 1;
break;
case BROADWAY_NODE_CLIP:
size = NODE_SIZE_RECT;
n_children = 1;
break;
case BROADWAY_NODE_TRANSLATE:
size = NODE_SIZE_POINT;
n_children = 1;
break;
case BROADWAY_NODE_LINEAR_GRADIENT:
size = NODE_SIZE_RECT + 2 * NODE_SIZE_POINT;
n_stops = data[*pos + size++];
size += n_stops * NODE_SIZE_COLOR_STOP;
break;
case BROADWAY_NODE_SHADOW:
size = 1;
n_shadows = data[*pos];
size += n_shadows * NODE_SIZE_SHADOW;
n_children = 1;
break;
case BROADWAY_NODE_OPACITY:
size = NODE_SIZE_FLOAT;
n_children = 1;
break;
case BROADWAY_NODE_DEBUG:
n_chars = data[*pos];
size = 1 + (n_chars + 3) / 4;
n_children = 1;
break;
default:
g_assert_not_reached ();
}
node = g_malloc (sizeof(BroadwayNode) + (size - 1) * sizeof(guint32) + n_children * sizeof (BroadwayNode *));
node->type = type;
node->n_children = n_children;
node->children = (BroadwayNode **)((char *)node + sizeof(BroadwayNode) + (size - 1) * sizeof(guint32));
node->n_data = size;
for (i = 0; i < size; i++)
{
node->data[i] = data[(*pos)++];
if (i == texture_offset)
node->data[i] = GPOINTER_TO_INT (g_hash_table_lookup (client->textures,
GINT_TO_POINTER (node->data[i])));
}
for (i = 0; i < n_children; i++)
node->children[i] = decode_nodes (client, len, data, pos);
hash = node->type << 16;
for (i = 0; i < size; i++)
hash ^= rotl (node->data[i], i);
for (i = 0; i < n_children; i++)
hash ^= rotl (node->children[i]->hash, i);
node->hash = hash;
return node;
}
static void
client_handle_request (BroadwayClient *client,
BroadwayRequest *request)
@ -409,13 +290,10 @@ client_handle_request (BroadwayClient *client,
{
gsize array_size = request->base.size - sizeof (BroadwayRequestSetNodes) + sizeof(guint32);
int n_data = array_size / sizeof(guint32);
int pos = 0;
BroadwayNode *node;
node = decode_nodes (client, n_data, request->set_nodes.data, &pos);
broadway_server_surface_set_nodes (server, request->set_nodes.id,
node);
broadway_server_surface_update_nodes (server, request->set_nodes.id,
request->set_nodes.data, n_data,
client->textures);
}
break;
case BROADWAY_REQUEST_UPLOAD_TEXTURE:

View File

@ -380,7 +380,7 @@ gdk_broadway_display_ensure_texture (GdkDisplay *display,
data = g_new0 (BroadwayTextureData, 1);
data->id = id;
data->display = g_object_ref (display);
g_object_set_data_full (G_OBJECT (texture), "broadway-data", data, (GDestroyNotify)broadway_texture_data_free);
g_object_set_data_full (G_OBJECT (texture), "broadway-data", data, (GDestroyNotify)broadway_texture_data_free);
}
return data->id;

View File

@ -64,6 +64,8 @@ gdk_broadway_draw_context_end_frame (GdkDrawContext *draw_context,
g_array_unref (self->nodes);
self->nodes = NULL;
/* We now sent all new texture refs to the daemon via the nodes, so we can drop them here */
g_ptr_array_unref (self->node_textures);
self->node_textures = NULL;
}

View File

@ -13,8 +13,16 @@ struct _GskBroadwayRenderer
{
GskRenderer parent_instance;
GdkBroadwayDrawContext *draw_context;
GHashTable *fallback_cache;
guint32 frame_nr;
guint32 next_node_id;
/* Set during rendering */
GArray *nodes; /* Owned by draw_contex */
GPtrArray *node_textures; /* Owned by draw_contex */
GHashTable *node_lookup;
/* Kept from last frame */
GHashTable *last_node_lookup;
GskRenderNode *last_root; /* Owning refs to the things in last_node_lookup */
};
struct _GskBroadwayRendererClass
@ -179,46 +187,132 @@ add_string (GArray *nodes, const char *str)
add_uint32 (nodes, v);
}
typedef struct {
GskRenderNode *node;
GdkTexture *texture;
float off_x;
float off_y;
int used_in_frame;
} FallbackCacheElement;
static void
collect_reused_child_nodes (GskRenderer *renderer,
GskRenderNode *node);
static void
fallback_cache_element_free (FallbackCacheElement *element)
collect_reused_node (GskRenderer *renderer,
GskRenderNode *node)
{
gsk_render_node_unref (element->node);
g_object_unref (element->texture);
g_free (element);
GskBroadwayRenderer *self = GSK_BROADWAY_RENDERER (renderer);
guint32 old_id;
if (self->last_node_lookup &&
(old_id = GPOINTER_TO_INT(g_hash_table_lookup (self->last_node_lookup, node))) != 0)
{
g_hash_table_insert (self->node_lookup, node, GINT_TO_POINTER (old_id));
collect_reused_child_nodes (renderer, node);
}
}
static GdkTexture *
node_texture_fallback (GskRenderNode *node,
float *off_x,
float *off_y)
static void
collect_reused_child_nodes (GskRenderer *renderer,
GskRenderNode *node)
{
cairo_surface_t *surface;
cairo_t *cr;
int x = floorf (node->bounds.origin.x);
int y = floorf (node->bounds.origin.y);
int width = ceil (node->bounds.origin.x + node->bounds.size.width) - x;
int height = ceil (node->bounds.origin.y + node->bounds.size.height) - y;
GdkTexture *texture;
guint i;
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
cr = cairo_create (surface);
cairo_translate (cr, -x, -y);
gsk_render_node_draw (node, cr);
cairo_destroy (cr);
switch (gsk_render_node_get_node_type (node))
{
case GSK_NOT_A_RENDER_NODE:
g_assert_not_reached ();
return;
texture = gdk_texture_new_for_surface (surface);
*off_x = x - node->bounds.origin.x;
*off_y = y - node->bounds.origin.y;
/* Leaf nodes */
return texture;
case GSK_TEXTURE_NODE:
case GSK_CAIRO_NODE:
case GSK_COLOR_NODE:
case GSK_BORDER_NODE:
case GSK_OUTSET_SHADOW_NODE:
case GSK_INSET_SHADOW_NODE:
case GSK_LINEAR_GRADIENT_NODE:
/* Fallbacks (=> leaf for now */
case GSK_COLOR_MATRIX_NODE:
case GSK_TEXT_NODE:
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
case GSK_REPEAT_NODE:
case GSK_BLEND_NODE:
case GSK_CROSS_FADE_NODE:
case GSK_BLUR_NODE:
default:
break;
/* Bin nodes */
case GSK_SHADOW_NODE:
collect_reused_node (renderer,
gsk_shadow_node_get_child (node));
break;
case GSK_OPACITY_NODE:
collect_reused_node (renderer,
gsk_opacity_node_get_child (node));
break;
case GSK_ROUNDED_CLIP_NODE:
collect_reused_node (renderer,
gsk_rounded_clip_node_get_child (node));
break;
case GSK_CLIP_NODE:
collect_reused_node (renderer,
gsk_clip_node_get_child (node));
break;
case GSK_TRANSFORM_NODE:
collect_reused_node (renderer,
gsk_transform_node_get_child (node));
break;
case GSK_DEBUG_NODE:
collect_reused_node (renderer,
gsk_debug_node_get_child (node));
break;
/* Generic nodes */
case GSK_CONTAINER_NODE:
for (i = 0; i < gsk_container_node_get_n_children (node); i++)
collect_reused_node (renderer,
gsk_container_node_get_child (node, i));
break;
break; /* Fallback */
}
}
static gboolean
add_new_node (GskRenderer *renderer,
GskRenderNode *node,
BroadwayNodeType type)
{
GskBroadwayRenderer *self = GSK_BROADWAY_RENDERER (renderer);
guint32 id, old_id;
if (self->last_node_lookup &&
(old_id = GPOINTER_TO_INT (g_hash_table_lookup (self->last_node_lookup, node))) != 0)
{
add_uint32 (self->nodes, BROADWAY_NODE_REUSE);
add_uint32 (self->nodes, old_id);
g_hash_table_insert (self->node_lookup, node, GINT_TO_POINTER(old_id));
collect_reused_child_nodes (renderer, node);
return FALSE;
}
id = ++self->next_node_id;
g_hash_table_insert (self->node_lookup, node, GINT_TO_POINTER(id));
add_uint32 (self->nodes, type);
add_uint32 (self->nodes, id);
return TRUE;
}
/* Note: This tracks the offset so that we can convert
@ -227,14 +321,13 @@ node_texture_fallback (GskRenderNode *node,
which is good for re-using subtrees. */
static void
gsk_broadway_renderer_add_node (GskRenderer *renderer,
GArray *nodes,
GPtrArray *node_textures,
GskRenderNode *node,
float offset_x,
float offset_y)
{
GdkDisplay *display = gdk_surface_get_display (gsk_renderer_get_surface (renderer));
GskBroadwayRenderer *self = GSK_BROADWAY_RENDERER (renderer);
GArray *nodes = self->nodes;
switch (gsk_render_node_get_node_type (node))
{
@ -245,214 +338,218 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer,
/* Leaf nodes */
case GSK_TEXTURE_NODE:
{
GdkTexture *texture = gsk_texture_node_get_texture (node);
guint32 texture_id;
if (add_new_node (renderer, node, BROADWAY_NODE_TEXTURE))
{
GdkTexture *texture = gsk_texture_node_get_texture (node);
guint32 texture_id;
g_ptr_array_add (node_textures, g_object_ref (texture)); /* Transfers ownership to node_textures */
texture_id = gdk_broadway_display_ensure_texture (display, texture);
/* No need to add to self->node_textures here, the node will keep it alive until end of frame. */
add_uint32 (nodes, BROADWAY_NODE_TEXTURE);
add_rect (nodes, &node->bounds, offset_x, offset_y);
add_uint32 (nodes, texture_id);
}
texture_id = gdk_broadway_display_ensure_texture (display, texture);
add_rect (nodes, &node->bounds, offset_x, offset_y);
add_uint32 (nodes, texture_id);
}
return;
case GSK_CAIRO_NODE:
{
cairo_surface_t *surface = (cairo_surface_t *)gsk_cairo_node_peek_surface (node);
cairo_surface_t *image_surface = NULL;
GdkTexture *texture;
guint32 texture_id;
if (add_new_node (renderer, node, BROADWAY_NODE_TEXTURE))
{
cairo_surface_t *surface = (cairo_surface_t *)gsk_cairo_node_peek_surface (node);
cairo_surface_t *image_surface = NULL;
GdkTexture *texture;
guint32 texture_id;
if (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE)
image_surface = cairo_surface_reference (surface);
else
{
cairo_t *cr;
image_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
ceilf (node->bounds.size.width),
ceilf (node->bounds.size.height));
cr = cairo_create (image_surface);
cairo_set_source_surface (cr, surface, 0, 0);
cairo_rectangle (cr, 0, 0, node->bounds.size.width, node->bounds.size.height);
cairo_fill (cr);
cairo_destroy (cr);
}
if (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE)
image_surface = cairo_surface_reference (surface);
else
{
cairo_t *cr;
image_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
ceilf (node->bounds.size.width),
ceilf (node->bounds.size.height));
cr = cairo_create (image_surface);
cairo_set_source_surface (cr, surface, 0, 0);
cairo_rectangle (cr, 0, 0, node->bounds.size.width, node->bounds.size.height);
cairo_fill (cr);
cairo_destroy (cr);
}
texture = gdk_texture_new_for_surface (image_surface);
g_ptr_array_add (node_textures, g_object_ref (texture)); /* Transfers ownership to node_textures */
texture_id = gdk_broadway_display_ensure_texture (display, texture);
texture = gdk_texture_new_for_surface (image_surface);
g_ptr_array_add (self->node_textures, g_object_ref (texture)); /* Transfers ownership to node_textures */
texture_id = gdk_broadway_display_ensure_texture (display, texture);
add_uint32 (nodes, BROADWAY_NODE_TEXTURE);
add_rect (nodes, &node->bounds, offset_x, offset_y);
add_uint32 (nodes, texture_id);
add_rect (nodes, &node->bounds, offset_x, offset_y);
add_uint32 (nodes, texture_id);
cairo_surface_destroy (image_surface);
}
cairo_surface_destroy (image_surface);
}
return;
case GSK_COLOR_NODE:
{
add_uint32 (nodes, BROADWAY_NODE_COLOR);
add_rect (nodes, &node->bounds, offset_x, offset_y);
add_rgba (nodes, gsk_color_node_peek_color (node));
}
if (add_new_node (renderer, node, BROADWAY_NODE_COLOR))
{
add_rect (nodes, &node->bounds, offset_x, offset_y);
add_rgba (nodes, gsk_color_node_peek_color (node));
}
return;
case GSK_BORDER_NODE:
{
int i;
add_uint32 (nodes, BROADWAY_NODE_BORDER);
add_rounded_rect (nodes, gsk_border_node_peek_outline (node), offset_x, offset_y);
for (i = 0; i < 4; i++)
add_float (nodes, gsk_border_node_peek_widths (node)[i]);
for (i = 0; i < 4; i++)
add_rgba (nodes, &gsk_border_node_peek_colors (node)[i]);
}
if (add_new_node (renderer, node, BROADWAY_NODE_BORDER))
{
int i;
add_rounded_rect (nodes, gsk_border_node_peek_outline (node), offset_x, offset_y);
for (i = 0; i < 4; i++)
add_float (nodes, gsk_border_node_peek_widths (node)[i]);
for (i = 0; i < 4; i++)
add_rgba (nodes, &gsk_border_node_peek_colors (node)[i]);
}
return;
case GSK_OUTSET_SHADOW_NODE:
{
add_uint32 (nodes, BROADWAY_NODE_OUTSET_SHADOW);
add_rounded_rect (nodes, gsk_outset_shadow_node_peek_outline (node), offset_x, offset_y);
add_rgba (nodes, gsk_outset_shadow_node_peek_color (node));
add_float (nodes, gsk_outset_shadow_node_get_dx (node));
add_float (nodes, gsk_outset_shadow_node_get_dy (node));
add_float (nodes, gsk_outset_shadow_node_get_spread (node));
add_float (nodes, gsk_outset_shadow_node_get_blur_radius (node));
}
if (add_new_node (renderer, node, BROADWAY_NODE_OUTSET_SHADOW))
{
add_rounded_rect (nodes, gsk_outset_shadow_node_peek_outline (node), offset_x, offset_y);
add_rgba (nodes, gsk_outset_shadow_node_peek_color (node));
add_float (nodes, gsk_outset_shadow_node_get_dx (node));
add_float (nodes, gsk_outset_shadow_node_get_dy (node));
add_float (nodes, gsk_outset_shadow_node_get_spread (node));
add_float (nodes, gsk_outset_shadow_node_get_blur_radius (node));
}
return;
case GSK_INSET_SHADOW_NODE:
{
add_uint32 (nodes, BROADWAY_NODE_INSET_SHADOW);
add_rounded_rect (nodes, gsk_inset_shadow_node_peek_outline (node), offset_x, offset_y);
add_rgba (nodes, gsk_inset_shadow_node_peek_color (node));
add_float (nodes, gsk_inset_shadow_node_get_dx (node));
add_float (nodes, gsk_inset_shadow_node_get_dy (node));
add_float (nodes, gsk_inset_shadow_node_get_spread (node));
add_float (nodes, gsk_inset_shadow_node_get_blur_radius (node));
}
if (add_new_node (renderer, node, BROADWAY_NODE_INSET_SHADOW))
{
add_rounded_rect (nodes, gsk_inset_shadow_node_peek_outline (node), offset_x, offset_y);
add_rgba (nodes, gsk_inset_shadow_node_peek_color (node));
add_float (nodes, gsk_inset_shadow_node_get_dx (node));
add_float (nodes, gsk_inset_shadow_node_get_dy (node));
add_float (nodes, gsk_inset_shadow_node_get_spread (node));
add_float (nodes, gsk_inset_shadow_node_get_blur_radius (node));
}
return;
case GSK_LINEAR_GRADIENT_NODE:
{
guint i, n;
if (add_new_node (renderer, node, BROADWAY_NODE_LINEAR_GRADIENT))
{
guint i, n;
add_uint32 (nodes, BROADWAY_NODE_LINEAR_GRADIENT);
add_rect (nodes, &node->bounds, offset_x, offset_y);
add_point (nodes, gsk_linear_gradient_node_peek_start (node), offset_x, offset_y);
add_point (nodes, gsk_linear_gradient_node_peek_end (node), offset_x, offset_y);
n = gsk_linear_gradient_node_get_n_color_stops (node);
add_uint32 (nodes, n);
for (i = 0; i < n; i++)
add_color_stop (nodes, &gsk_linear_gradient_node_peek_color_stops (node)[i]);
}
add_rect (nodes, &node->bounds, offset_x, offset_y);
add_point (nodes, gsk_linear_gradient_node_peek_start (node), offset_x, offset_y);
add_point (nodes, gsk_linear_gradient_node_peek_end (node), offset_x, offset_y);
n = gsk_linear_gradient_node_get_n_color_stops (node);
add_uint32 (nodes, n);
for (i = 0; i < n; i++)
add_color_stop (nodes, &gsk_linear_gradient_node_peek_color_stops (node)[i]);
}
return;
/* Bin nodes */
case GSK_SHADOW_NODE:
{
gsize i, n_shadows = gsk_shadow_node_get_n_shadows (node);
add_uint32 (nodes, BROADWAY_NODE_SHADOW);
add_uint32 (nodes, n_shadows);
for (i = 0; i < n_shadows; i++)
{
const GskShadow *shadow = gsk_shadow_node_peek_shadow (node, i);
add_rgba (nodes, &shadow->color);
add_float (nodes, shadow->dx);
add_float (nodes, shadow->dy);
add_float (nodes, shadow->radius);
}
gsk_broadway_renderer_add_node (renderer, nodes, node_textures,
gsk_shadow_node_get_child (node),
offset_x, offset_y);
}
if (add_new_node (renderer, node, BROADWAY_NODE_SHADOW))
{
gsize i, n_shadows = gsk_shadow_node_get_n_shadows (node);
add_uint32 (nodes, n_shadows);
for (i = 0; i < n_shadows; i++)
{
const GskShadow *shadow = gsk_shadow_node_peek_shadow (node, i);
add_rgba (nodes, &shadow->color);
add_float (nodes, shadow->dx);
add_float (nodes, shadow->dy);
add_float (nodes, shadow->radius);
}
gsk_broadway_renderer_add_node (renderer,
gsk_shadow_node_get_child (node),
offset_x, offset_y);
}
return;
case GSK_OPACITY_NODE:
{
add_uint32 (nodes, BROADWAY_NODE_OPACITY);
add_float (nodes, gsk_opacity_node_get_opacity (node));
gsk_broadway_renderer_add_node (renderer, nodes, node_textures,
gsk_opacity_node_get_child (node),
offset_x, offset_y);
}
if (add_new_node (renderer, node, BROADWAY_NODE_OPACITY))
{
add_float (nodes, gsk_opacity_node_get_opacity (node));
gsk_broadway_renderer_add_node (renderer,
gsk_opacity_node_get_child (node),
offset_x, offset_y);
}
return;
case GSK_ROUNDED_CLIP_NODE:
{
const GskRoundedRect *rclip = gsk_rounded_clip_node_peek_clip (node);
add_uint32 (nodes, BROADWAY_NODE_ROUNDED_CLIP);
add_rounded_rect (nodes, rclip, offset_x, offset_y);
gsk_broadway_renderer_add_node (renderer, nodes, node_textures,
gsk_rounded_clip_node_get_child (node),
rclip->bounds.origin.x,
rclip->bounds.origin.y);
}
if (add_new_node (renderer, node, BROADWAY_NODE_ROUNDED_CLIP))
{
const GskRoundedRect *rclip = gsk_rounded_clip_node_peek_clip (node);
add_rounded_rect (nodes, rclip, offset_x, offset_y);
gsk_broadway_renderer_add_node (renderer,
gsk_rounded_clip_node_get_child (node),
rclip->bounds.origin.x,
rclip->bounds.origin.y);
}
return;
case GSK_CLIP_NODE:
{
const graphene_rect_t *clip = gsk_clip_node_peek_clip (node);
add_uint32 (nodes, BROADWAY_NODE_CLIP);
add_rect (nodes, clip, offset_x, offset_y);
gsk_broadway_renderer_add_node (renderer, nodes, node_textures,
gsk_clip_node_get_child (node),
clip->origin.x,
clip->origin.y);
}
if (add_new_node (renderer, node, BROADWAY_NODE_CLIP))
{
const graphene_rect_t *clip = gsk_clip_node_peek_clip (node);
add_rect (nodes, clip, offset_x, offset_y);
gsk_broadway_renderer_add_node (renderer,
gsk_clip_node_get_child (node),
clip->origin.x,
clip->origin.y);
}
return;
case GSK_TRANSFORM_NODE:
{
GskTransform *transform = gsk_transform_node_get_transform (node);
GskTransformCategory category = gsk_transform_get_category (transform);
float dx, dy;
if (category >= GSK_TRANSFORM_CATEGORY_2D_TRANSLATE)
{
gsk_transform_to_translate (transform, &dx, &dy);
add_uint32 (nodes, BROADWAY_NODE_TRANSLATE);
add_xy (nodes, dx, dy, offset_x, offset_y);
gsk_broadway_renderer_add_node (renderer, nodes, node_textures,
gsk_transform_node_get_child (node),
0, 0);
if (add_new_node (renderer, node, BROADWAY_NODE_TRANSLATE)) {
float dx, dy;
gsk_transform_to_translate (transform, &dx, &dy);
add_xy (nodes, dx, dy, offset_x, offset_y);
gsk_broadway_renderer_add_node (renderer,
gsk_transform_node_get_child (node),
0, 0);
}
}
else
{
/* Fallback to texture for now */
break;
}
}
return;
case GSK_DEBUG_NODE:
{
const char *message = gsk_debug_node_get_message (node);
add_uint32 (nodes, BROADWAY_NODE_DEBUG);
add_string (nodes, message);
gsk_broadway_renderer_add_node (renderer, nodes, node_textures,
gsk_debug_node_get_child (node), offset_x, offset_y);
}
if (add_new_node (renderer, node, BROADWAY_NODE_DEBUG))
{
const char *message = gsk_debug_node_get_message (node);
add_string (nodes, message);
gsk_broadway_renderer_add_node (renderer,
gsk_debug_node_get_child (node), offset_x, offset_y);
}
return;
/* Generic nodes */
case GSK_CONTAINER_NODE:
{
guint i;
if (add_new_node (renderer, node, BROADWAY_NODE_CONTAINER))
{
guint i;
add_uint32 (nodes, BROADWAY_NODE_CONTAINER);
add_uint32 (nodes, gsk_container_node_get_n_children (node));
for (i = 0; i < gsk_container_node_get_n_children (node); i++)
gsk_broadway_renderer_add_node (renderer, nodes, node_textures,
gsk_container_node_get_child (node, i), offset_x, offset_y);
}
add_uint32 (nodes, gsk_container_node_get_n_children (node));
for (i = 0; i < gsk_container_node_get_n_children (node); i++)
gsk_broadway_renderer_add_node (renderer,
gsk_container_node_get_child (node, i), offset_x, offset_y);
}
return;
case GSK_COLOR_MATRIX_NODE:
@ -466,58 +563,33 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer,
break; /* Fallback */
}
{
GdkTexture *texture;
guint32 texture_id;
FallbackCacheElement *hit;
float t_off_x = 0, t_off_y = 0;
if (add_new_node (renderer, node, BROADWAY_NODE_TEXTURE))
{
GdkTexture *texture;
cairo_surface_t *surface;
cairo_t *cr;
guint32 texture_id;
int x = floorf (node->bounds.origin.x);
int y = floorf (node->bounds.origin.y);
int width = ceil (node->bounds.origin.x + node->bounds.size.width) - x;
int height = ceil (node->bounds.origin.y + node->bounds.size.height) - y;
hit = g_hash_table_lookup (self->fallback_cache, node);
if (hit)
{
texture = g_object_ref (hit->texture);
t_off_x = hit->off_x;
t_off_y = hit->off_y;
hit->used_in_frame = self->frame_nr;
}
else
{
FallbackCacheElement *element;
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
cr = cairo_create (surface);
cairo_translate (cr, -x, -y);
gsk_render_node_draw (node, cr);
cairo_destroy (cr);
texture = node_texture_fallback (node, &t_off_x, &t_off_y);
#if 0
g_print ("Fallback %p for %s\n", texture, node->node_class->type_name);
#endif
element = g_new0 (FallbackCacheElement, 1);
element->texture = g_object_ref (texture);
element->node = gsk_render_node_ref (node);
element->off_x = t_off_x;
element->off_y = t_off_y;
element->used_in_frame = self->frame_nr;
g_hash_table_insert (self->fallback_cache, element->node, element);
}
texture = gdk_texture_new_for_surface (surface);
g_ptr_array_add (self->node_textures, texture); /* Transfers ownership to node_textures */
g_ptr_array_add (node_textures, texture); /* Transfers ownership to node_textures */
texture_id = gdk_broadway_display_ensure_texture (display, texture);
add_uint32 (nodes, BROADWAY_NODE_TEXTURE);
add_float (nodes, node->bounds.origin.x + t_off_x - offset_x);
add_float (nodes, node->bounds.origin.y + t_off_y - offset_y);
add_float (nodes, gdk_texture_get_width (texture));
add_float (nodes, gdk_texture_get_height (texture));
add_uint32 (nodes, texture_id);
}
}
static gboolean
clean_old_fallbacks (gpointer key,
gpointer value,
gpointer user_data)
{
GskBroadwayRenderer *self = GSK_BROADWAY_RENDERER (user_data);
FallbackCacheElement *element = value;
/* Remove cached fallbacks not used for 5 frames */
return self->frame_nr - element->used_in_frame > 5;
texture_id = gdk_broadway_display_ensure_texture (display, texture);
add_float (nodes, x - offset_x);
add_float (nodes, y - offset_y);
add_float (nodes, gdk_texture_get_width (texture));
add_float (nodes, gdk_texture_get_height (texture));
add_uint32 (nodes, texture_id);
}
}
static void
@ -527,13 +599,40 @@ gsk_broadway_renderer_render (GskRenderer *renderer,
{
GskBroadwayRenderer *self = GSK_BROADWAY_RENDERER (renderer);
self->frame_nr++;
self->node_lookup = g_hash_table_new (g_direct_hash, g_direct_equal);
gdk_draw_context_begin_frame (GDK_DRAW_CONTEXT (self->draw_context), update_area);
gsk_broadway_renderer_add_node (renderer, self->draw_context->nodes, self->draw_context->node_textures, root, 0, 0);
/* These are owned by the draw context between begin and end, but
cache them here for easier access during the render */
self->nodes = self->draw_context->nodes;
self->node_textures = self->draw_context->node_textures;
gsk_broadway_renderer_add_node (renderer, root, 0, 0);
self->nodes = NULL;
self->node_textures = NULL;
gdk_draw_context_end_frame (GDK_DRAW_CONTEXT (self->draw_context));
g_hash_table_foreach_remove (self->fallback_cache, clean_old_fallbacks, renderer);
if (self->last_node_lookup)
g_hash_table_unref (self->last_node_lookup);
self->last_node_lookup = self->node_lookup;
self->node_lookup = NULL;
if (self->last_root)
gsk_render_node_unref (self->last_root);
self->last_root = gsk_render_node_ref (root);
if (self->next_node_id > G_MAXUINT32 / 2)
{
/* We're "near" a wrap of the ids, lets avoid reusing any of
* these nodes next frame, then we can reset the id counter
* without risk of any old nodes sticking around and conflicting. */
g_hash_table_remove_all (self->last_node_lookup);
self->next_node_id = 0;
}
}
static void
@ -550,8 +649,4 @@ gsk_broadway_renderer_class_init (GskBroadwayRendererClass *klass)
static void
gsk_broadway_renderer_init (GskBroadwayRenderer *self)
{
self->fallback_cache = g_hash_table_new_full ((GHashFunc)g_direct_hash,
(GEqualFunc)g_direct_equal,
NULL,
(GDestroyNotify)fallback_cache_element_free);
}