Compare commits

...

1 Commits

Author SHA1 Message Date
Christian Hergert
990102f269 gsk: add GskHintNode
The GskHintNode is meant to set flags for the renderer which can be used
to optimize batch submission. Currently, two flags are supported. One will
disable caching of textures from the hint-node to all descendants. The
second will request that the child node be cached as an offscreen texture
and used to blit on subsequent frames.
2022-03-21 14:40:59 -07:00
8 changed files with 347 additions and 6 deletions

View File

@@ -306,6 +306,11 @@ collect_reused_child_nodes (GskRenderer *renderer,
gsk_debug_node_get_child (node));
break;
case GSK_HINT_NODE:
collect_reused_node (renderer,
gsk_hint_node_get_child (node));
break;
/* Generic nodes */
case GSK_CONTAINER_NODE:
@@ -796,6 +801,11 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer,
}
return;
case GSK_HINT_NODE:
gsk_broadway_renderer_add_node (renderer,
gsk_hint_node_get_child (node), offset_x, offset_y, clip_bounds);
return;
/* Generic nodes */
case GSK_CONTAINER_NODE:

View File

@@ -173,6 +173,11 @@ struct _GskGLRenderJob
* looking at the format of the framebuffer we are rendering on.
*/
int target_format;
/* If caching render nodes is inhibited from a hint node through
* all of the descendants.
*/
int inhibit_cache_count;
};
typedef struct _GskGLRenderOffscreen
@@ -2903,6 +2908,9 @@ is_non_branching (const GskRenderNode *node)
case GSK_DEBUG_NODE:
return is_non_branching (gsk_debug_node_get_child (node));
case GSK_HINT_NODE:
return is_non_branching (gsk_hint_node_get_child (node));
case GSK_CONTAINER_NODE:
return gsk_container_node_get_n_children (node) == 1 &&
is_non_branching (gsk_container_node_get_child (node, 0));
@@ -3646,6 +3654,51 @@ gsk_gl_render_job_visit_repeat_node (GskGLRenderJob *job,
gsk_gl_render_job_end_draw (job);
}
static void
gsk_gl_render_job_visit_hint_node (GskGLRenderJob *job,
const GskRenderNode *node)
{
const GskRenderNode *child = gsk_hint_node_get_child (node);
GskRenderHints hints = gsk_hint_node_get_hints (node);
gboolean never_cache = !!(hints & GSK_RENDER_HINTS_NEVER_CACHE);
gboolean force_cache = !!(hints & GSK_RENDER_HINTS_FORCE_CACHE);
job->inhibit_cache_count += never_cache;
if (force_cache && job->inhibit_cache_count == 0)
{
GskGLRenderOffscreen offscreen = {0};
offscreen.bounds = &child->bounds;
offscreen.reset_clip = TRUE;
offscreen.force_offscreen = TRUE;
/* gsk_gl_render_job_visit_node_with_offscreen() will cache the texture
* for @child and if already cached, re-use it without further rendering.
*/
if (!gsk_gl_render_job_visit_node_with_offscreen (job, child, &offscreen))
g_assert_not_reached ();
g_assert (offscreen.texture_id);
g_assert (offscreen.was_offscreen == TRUE);
gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit));
gsk_gl_program_set_uniform_texture (job->current_program,
UNIFORM_SHARED_SOURCE, 0,
GL_TEXTURE_2D,
GL_TEXTURE0,
offscreen.texture_id);
gsk_gl_render_job_draw_offscreen (job, &node->bounds, &offscreen);
gsk_gl_render_job_end_draw (job);
}
else
{
gsk_gl_render_job_visit_node (job, child);
}
job->inhibit_cache_count -= never_cache;
}
static void
gsk_gl_render_job_visit_node (GskGLRenderJob *job,
const GskRenderNode *node)
@@ -3757,6 +3810,10 @@ gsk_gl_render_job_visit_node (GskGLRenderJob *job,
gsk_gl_render_job_visit_node (job, gsk_debug_node_get_child (node));
break;
case GSK_HINT_NODE:
gsk_gl_render_job_visit_hint_node (job, node);
break;
case GSK_GL_SHADER_NODE:
gsk_gl_render_job_visit_gl_shader_node (job, node);
break;
@@ -4000,7 +4057,7 @@ gsk_gl_render_job_visit_node_with_offscreen (GskGLRenderJob *job,
init_full_texture_region (offscreen);
if (!offscreen->do_not_cache)
if (!offscreen->do_not_cache && job->inhibit_cache_count == 0)
gsk_gl_driver_cache_texture (job->driver, &key, offscreen->texture_id);
return TRUE;

View File

@@ -50,7 +50,8 @@
* @GSK_BLUR_NODE: A node that applies a blur
* @GSK_DEBUG_NODE: Debug information that does not affect the rendering
* @GSK_GL_SHADER_NODE: A node that uses OpenGL fragment shaders to render
* @GSK_HINT_NODE: A node that provides hints to the renderer
* The type of a node determines what the node is rendering.
*/
typedef enum {
@@ -79,7 +80,8 @@ typedef enum {
GSK_TEXT_NODE,
GSK_BLUR_NODE,
GSK_DEBUG_NODE,
GSK_GL_SHADER_NODE
GSK_GL_SHADER_NODE,
GSK_HINT_NODE
} GskRenderNodeType;
/**
@@ -251,5 +253,24 @@ typedef enum
GSK_GL_UNIFORM_TYPE_VEC4,
} GskGLUniformType;
/**
* GskRenderHints:
* @GSK_RENDER_HINTS_NEVER_CACHE: Hints to the renderer that it should never
* cache the contents of this or any descendant node.
* @GSK_RENDER_HINTS_FORCE_CACHE: Hints to the renderer that it should try
* to cache the conents of the node as it is likely to be reused in future
* render requests.
*
* This defines the hints that may be provided to renderers to instruct
* preferred behavior.
*
* Since: 4.8
*/
typedef enum
{
GSK_RENDER_HINTS_NEVER_CACHE = 1 << 0,
GSK_RENDER_HINTS_FORCE_CACHE = 1 << 1,
} GskRenderHints;
#endif /* __GSK_TYPES_H__ */

View File

@@ -164,6 +164,7 @@ GskRenderNode * gsk_render_node_deserialize (GBytes
#define GSK_TYPE_TEXT_NODE (gsk_text_node_get_type())
#define GSK_TYPE_BLUR_NODE (gsk_blur_node_get_type())
#define GSK_TYPE_GL_SHADER_NODE (gsk_gl_shader_node_get_type())
#define GSK_TYPE_HINT_NODE (gsk_hint_node_get_type())
typedef struct _GskDebugNode GskDebugNode;
typedef struct _GskColorNode GskColorNode;
@@ -190,6 +191,7 @@ typedef struct _GskCrossFadeNode GskCrossFadeNode;
typedef struct _GskTextNode GskTextNode;
typedef struct _GskBlurNode GskBlurNode;
typedef struct _GskGLShaderNode GskGLShaderNode;
typedef struct _GskHintNode GskHintNode;
GDK_AVAILABLE_IN_ALL
GType gsk_debug_node_get_type (void) G_GNUC_CONST;
@@ -534,6 +536,16 @@ GBytes * gsk_gl_shader_node_get_args (const GskRender
GDK_AVAILABLE_IN_ALL
GskGLShader * gsk_gl_shader_node_get_shader (const GskRenderNode *node) G_GNUC_PURE;
GDK_AVAILABLE_IN_4_8
GType gsk_hint_node_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_4_8
GskRenderNode * gsk_hint_node_new (GskRenderNode *child,
GskRenderHints hints);
GDK_AVAILABLE_IN_4_8
GskRenderNode * gsk_hint_node_get_child (const GskRenderNode *node) G_GNUC_PURE;
GDK_AVAILABLE_IN_4_8
GskRenderHints gsk_hint_node_get_hints (const GskRenderNode *node) G_GNUC_PURE;
/**
* GSK_VALUE_HOLDS_RENDER_NODE:
* @value: a `GValue`

View File

@@ -5056,6 +5056,127 @@ gsk_debug_node_get_message (const GskRenderNode *node)
return self->message;
}
/*** GSK_HINT_NODE ***/
/**
* GskHintNode:
*
* A render node that provides hints to the renderer about how a child node
* should be processed.
*/
struct _GskHintNode
{
GskRenderNode render_node;
GskRenderNode *child;
GskRenderHints hints;
};
static void
gsk_hint_node_finalize (GskRenderNode *node)
{
GskHintNode *self = (GskHintNode *) node;
GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_HINT_NODE));
gsk_render_node_unref (self->child);
parent_class->finalize (node);
}
static void
gsk_hint_node_draw (GskRenderNode *node,
cairo_t *cr)
{
GskHintNode *self = (GskHintNode *) node;
gsk_render_node_draw (self->child, cr);
}
static gboolean
gsk_hint_node_can_diff (const GskRenderNode *node1,
const GskRenderNode *node2)
{
GskHintNode *self1 = (GskHintNode *) node1;
GskHintNode *self2 = (GskHintNode *) node2;
return gsk_render_node_can_diff (self1->child, self2->child);
}
static void
gsk_hint_node_diff (GskRenderNode *node1,
GskRenderNode *node2,
cairo_region_t *region)
{
GskHintNode *self1 = (GskHintNode *) node1;
GskHintNode *self2 = (GskHintNode *) node2;
gsk_render_node_diff (self1->child, self2->child, region);
}
/**
* gsk_hint_node_new:
* @child: The child to add hint info for
* @hints: The hints for the child
*
* Creates a `GskRenderNode` that will add hints about how the child
* should be processed by the renderer.
*
* Returns: (transfer full) (type GskHintNode): A new `GskRenderNode`
*/
GskRenderNode *
gsk_hint_node_new (GskRenderNode *child,
GskRenderHints hints)
{
GskHintNode *self;
GskRenderNode *node;
g_return_val_if_fail (GSK_IS_RENDER_NODE (child), NULL);
self = gsk_render_node_alloc (GSK_HINT_NODE);
node = (GskRenderNode *) self;
self->child = gsk_render_node_ref (child);
self->hints = hints;
graphene_rect_init_from_rect (&node->bounds, &child->bounds);
node->prefers_high_depth = gsk_render_node_prefers_high_depth (child);
return node;
}
/**
* gsk_hint_node_get_child:
* @node: (type GskHintNode): a hint `GskRenderNode`
*
* Gets the child node that is getting drawn by the given @node.
*
* Returns: (transfer none): the child `GskRenderNode`
**/
GskRenderNode *
gsk_hint_node_get_child (const GskRenderNode *node)
{
const GskHintNode *self = (const GskHintNode *) node;
return self->child;
}
/**
* gsk_hint_node_get_hints:
* @node: (type GskHintNode): a hint `GskRenderNode`
*
* Gets the hints applied to this node
*
* Returns: The hints for the node
**/
GskRenderHints
gsk_hint_node_get_hints (const GskRenderNode *node)
{
const GskHintNode *self = (const GskHintNode *) node;
return self->hints;
}
/*** GSK_GL_SHADER_NODE ***/
/**
@@ -5298,6 +5419,7 @@ GSK_DEFINE_RENDER_NODE_TYPE (gsk_cross_fade_node, GSK_CROSS_FADE_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_text_node, GSK_TEXT_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_blur_node, GSK_BLUR_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_gl_shader_node, GSK_GL_SHADER_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_hint_node, GSK_HINT_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_debug_node, GSK_DEBUG_NODE)
static void
@@ -5687,6 +5809,22 @@ gsk_render_node_init_types_once (void)
gsk_render_node_types[GSK_GL_SHADER_NODE] = node_type;
}
{
const GskRenderNodeTypeInfo node_info =
{
GSK_HINT_NODE,
sizeof (GskHintNode),
NULL,
gsk_hint_node_finalize,
gsk_hint_node_draw,
gsk_hint_node_can_diff,
gsk_hint_node_diff,
};
GType node_type = gsk_render_node_type_register_static (I_("GskHintNode"), &node_info);
gsk_render_node_types[GSK_HINT_NODE] = node_type;
}
{
const GskRenderNodeTypeInfo node_info =
{

View File

@@ -416,6 +416,29 @@ clear_string (gpointer inout_string)
g_clear_pointer ((char **) inout_string, g_free);
}
static gboolean
parse_bool (GtkCssParser *parser,
gpointer out_bool)
{
if (gtk_css_parser_try_ident (parser, "true") ||
gtk_css_parser_try_ident (parser, "True") ||
gtk_css_parser_try_ident (parser, "TRUE"))
{
*(gboolean *) out_bool = TRUE;
return TRUE;
}
if (gtk_css_parser_try_ident (parser, "false") ||
gtk_css_parser_try_ident (parser, "False") ||
gtk_css_parser_try_ident (parser, "FALSE"))
{
*(gboolean *) out_bool = FALSE;
return TRUE;
}
return FALSE;
}
static gboolean
parse_stops (GtkCssParser *parser,
gpointer out_stops)
@@ -1102,7 +1125,7 @@ parse_conic_gradient_node (GtkCssParser *parser)
g_array_append_val (stops, to);
}
result = gsk_conic_gradient_node_new (&bounds, &center, rotation,
result = gsk_conic_gradient_node_new (&bounds, &center, rotation,
(GskColorStop *) stops->data, stops->len);
g_array_free (stops, TRUE);
@@ -1388,7 +1411,7 @@ parse_cairo_node (GtkCssParser *parser)
parse_declarations (parser, declarations, G_N_ELEMENTS(declarations));
node = gsk_cairo_node_new (&bounds);
if (surface != NULL)
{
cairo_t *cr = gsk_cairo_node_get_draw_context (node);
@@ -1816,6 +1839,37 @@ parse_debug_node (GtkCssParser *parser)
return result;
}
static GskRenderNode *
parse_hint_node (GtkCssParser *parser)
{
gboolean force_cache = FALSE;
gboolean never_cache = FALSE;
GskRenderHints hints = 0;
GskRenderNode *child = NULL;
const Declaration declarations[] = {
{ "never-cache", parse_bool, NULL, &never_cache },
{ "force-cache", parse_bool, NULL, &force_cache },
{ "child", parse_node, clear_node, &child },
};
GskRenderNode *result;
parse_declarations (parser, declarations, G_N_ELEMENTS(declarations));
if (child == NULL)
child = create_default_render_node ();
if (never_cache)
hints |= GSK_RENDER_HINTS_NEVER_CACHE;
if (force_cache)
hints |= GSK_RENDER_HINTS_FORCE_CACHE;
result = gsk_hint_node_new (child, hints);
gsk_render_node_unref (child);
return result;
}
static gboolean
parse_node (GtkCssParser *parser,
gpointer out_node)
@@ -1849,6 +1903,7 @@ parse_node (GtkCssParser *parser,
{ "texture", parse_texture_node },
{ "transform", parse_transform_node },
{ "glshader", parse_glshader_node },
{ "hint", parse_hint_node },
};
GskRenderNode **node_p = out_node;
guint i;
@@ -2158,6 +2213,17 @@ append_point_param (Printer *p,
g_string_append_c (p->str, '\n');
}
static void
append_bool_param (Printer *p,
const char *param_name,
gboolean value)
{
_indent (p);
g_string_append_printf (p->str, "%s: %s", param_name, value ? "true" : "false");
g_string_append_c (p->str, ';');
g_string_append_c (p->str, '\n');
}
static void
append_string_param (Printer *p,
const char *param_name,
@@ -2765,6 +2831,24 @@ render_node_print (Printer *p,
}
break;
case GSK_HINT_NODE:
{
GskRenderHints hints = gsk_hint_node_get_hints (node);
start_node (p, "hint");
if (hints & GSK_RENDER_HINTS_NEVER_CACHE)
append_bool_param (p, "never-cache", TRUE);
if (hints & GSK_RENDER_HINTS_FORCE_CACHE)
append_bool_param (p, "force-cache", TRUE);
append_node_param (p, "child", gsk_hint_node_get_child (node));
end_node (p);
}
break;
case GSK_BLUR_NODE:
{
start_node (p, "blur");

View File

@@ -13,7 +13,7 @@ typedef struct _GskRenderNodeClass GskRenderNodeClass;
* We don't add an "n-types" value to avoid having to handle
* it in every single switch.
*/
#define GSK_RENDER_NODE_TYPE_N_TYPES (GSK_GL_SHADER_NODE + 1)
#define GSK_RENDER_NODE_TYPE_N_TYPES (GSK_HINT_NODE + 1)
extern GType gsk_render_node_types[];

View File

@@ -232,6 +232,9 @@ create_list_model_for_render_node (GskRenderNode *node)
case GSK_DEBUG_NODE:
return create_render_node_list_model ((GskRenderNode *[1]) { gsk_debug_node_get_child (node) }, 1);
case GSK_HINT_NODE:
return create_render_node_list_model ((GskRenderNode *[1]) { gsk_hint_node_get_child (node) }, 1);
}
}
@@ -310,6 +313,8 @@ node_type_name (GskRenderNodeType type)
return "Blur";
case GSK_GL_SHADER_NODE:
return "GL Shader";
case GSK_HINT_NODE:
return "Hint";
}
}
@@ -343,6 +348,7 @@ node_name (GskRenderNode *node)
case GSK_TEXT_NODE:
case GSK_BLUR_NODE:
case GSK_GL_SHADER_NODE:
case GSK_HINT_NODE:
return g_strdup (node_type_name (gsk_render_node_get_node_type (node)));
case GSK_DEBUG_NODE:
@@ -1216,6 +1222,19 @@ populate_render_node_properties (GtkListStore *store,
add_text_row (store, "Message", gsk_debug_node_get_message (node));
break;
case GSK_HINT_NODE:
{
GskRenderHints hints = gsk_hint_node_get_hints (node);
if (hints & GSK_RENDER_HINTS_NEVER_CACHE)
add_text_row (store, "Never Cache", "True");
if (hints & GSK_RENDER_HINTS_FORCE_CACHE)
add_text_row (store, "Force Cache", "True");
break;
}
case GSK_SHADOW_NODE:
{
int i;