diff options
author | Alexander Larsson <alexl@redhat.com> | 2020-09-18 17:56:02 +0200 |
---|---|---|
committer | Alexander Larsson <alexl@redhat.com> | 2020-09-29 09:51:16 +0200 |
commit | 950cc41e15cc8b92c75f49e5e31fbff5e6d6efa2 (patch) | |
tree | f2ff7f1582d4a7ed3bf55b973c89b4617b89dd8b | |
parent | 7ea755e206c36960db05f0e0660c6f5f20b692c6 (diff) | |
download | gtk+-950cc41e15cc8b92c75f49e5e31fbff5e6d6efa2.tar.gz |
GtkSnapshot: Add gtk_snapshot_push_glshader()
-rw-r--r-- | docs/reference/gtk/gtk4-sections.txt | 1 | ||||
-rw-r--r-- | gtk/gtksnapshot.c | 199 | ||||
-rw-r--r-- | gtk/gtksnapshot.h | 8 |
3 files changed, 207 insertions, 1 deletions
diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt index b0bbd9cb1d..352c9d26e1 100644 --- a/docs/reference/gtk/gtk4-sections.txt +++ b/docs/reference/gtk/gtk4-sections.txt @@ -4299,6 +4299,7 @@ gtk_snapshot_push_blend gtk_snapshot_push_blur gtk_snapshot_push_shadow gtk_snapshot_push_debug +gtk_snapshot_push_gl_shader gtk_snapshot_pop gtk_snapshot_save gtk_snapshot_restore diff --git a/gtk/gtksnapshot.c b/gtk/gtksnapshot.c index 89e4ea9d39..ab19aa8ac1 100644 --- a/gtk/gtksnapshot.c +++ b/gtk/gtksnapshot.c @@ -92,6 +92,18 @@ struct _GtkSnapshotState { graphene_rect_t bounds; } clip; struct { + GskGLShader *shader; + GBytes *args; + graphene_rect_t bounds; + GskRenderNode **nodes; + GskRenderNode *internal_nodes[4]; + } glshader; + struct { + graphene_rect_t bounds; + int node_idx; + int n_children; + } glshader_texture; + struct { GskRoundedRect bounds; } rounded_clip; struct { @@ -230,6 +242,18 @@ gtk_snapshot_get_previous_state (const GtkSnapshot *snapshot) return gtk_snapshot_states_get (&snapshot->state_stack, size - 2); } +/* n == 0 => current, n == 1, previous, etc */ +static GtkSnapshotState * +gtk_snapshot_get_nth_previous_state (const GtkSnapshot *snapshot, + int n) +{ + gsize size = gtk_snapshot_states_get_size (&snapshot->state_stack); + + g_assert (size > n); + + return gtk_snapshot_states_get (&snapshot->state_stack, size - (1 + n)); +} + static void gtk_snapshot_state_clear (GtkSnapshotState *state) { @@ -826,6 +850,149 @@ gtk_snapshot_push_clip (GtkSnapshot *snapshot, } static GskRenderNode * +gtk_snapshot_collect_gl_shader (GtkSnapshot *snapshot, + GtkSnapshotState *state, + GskRenderNode **collected_nodes, + guint n_collected_nodes) +{ + GskRenderNode *shader_node = NULL; + GskRenderNode **nodes; + int n_children; + + n_children = gsk_gl_shader_get_n_textures (state->data.glshader.shader); + shader_node = NULL; + + if (n_collected_nodes != 0) + g_warning ("Unexpected children when poping gl shader."); + + if (state->data.glshader.nodes) + nodes = state->data.glshader.nodes; + else + nodes = &state->data.glshader.internal_nodes[0]; + + if (state->data.glshader.bounds.size.width != 0 && + state->data.glshader.bounds.size.height != 0) + shader_node = gsk_gl_shader_node_new (state->data.glshader.shader, + &state->data.glshader.bounds, + state->data.glshader.args, + nodes, n_children); + + g_object_unref (state->data.glshader.shader); + g_bytes_unref (state->data.glshader.args); + + for (guint i = 0; i < n_children; i++) + gsk_render_node_unref (nodes[i]); + + g_free (state->data.glshader.nodes); + + return shader_node; +} + +static GskRenderNode * +gtk_snapshot_collect_gl_shader_texture (GtkSnapshot *snapshot, + GtkSnapshotState *state, + GskRenderNode **nodes, + guint n_nodes) +{ + GskRenderNode *child_node; + GdkRGBA transparent = { 0, 0, 0, 0 }; + int n_children, node_idx; + GtkSnapshotState *glshader_state; + GskRenderNode **out_nodes; + + child_node = gtk_snapshot_collect_default (snapshot, state, nodes, n_nodes); + + if (child_node == NULL) + child_node = gsk_color_node_new (&transparent, &state->data.glshader_texture.bounds); + + n_children = state->data.glshader_texture.n_children; + node_idx = state->data.glshader_texture.node_idx; + + glshader_state = gtk_snapshot_get_nth_previous_state (snapshot, n_children - node_idx); + g_assert (glshader_state->collect_func == gtk_snapshot_collect_gl_shader); + + if (glshader_state->data.glshader.nodes) + out_nodes = glshader_state->data.glshader.nodes; + else + out_nodes = &glshader_state->data.glshader.internal_nodes[0]; + + out_nodes[node_idx] = child_node; + + return NULL; +} + +/** + * gtk_snapshot_push_gl_shader: + * @snapshot: a #GtkSnapshot + * @shader: The code to run + * @bounds: the rectangle to render into + * @take_args: (transfer full): Data block with arguments for the shader. + * + * Push a #GskGLShaderNode with a specific #GskGLShader and a set of uniform values + * to use while rendering. Additionally this takes a list of @n_children other nodes + * which will be passed to the #GskGLShaderNode. + * + * The @take_args argument is a block of data to use for uniform + * arguments, as per types and offsets defined by the @shader. Normally this is + * generated by gsk_gl_shader_format_args() or #GskGLShaderArgBuilder. + * The snapshotter takes ownership of @take_args, so the caller should not free it + * after this. + * + * If the renderer doesn't support GL shaders, or if there is any problem when + * compiling the shader, then the node will draw pink. You should use + * gsk_gl_shader_compile() to ensure the @shader will work for the renderer + * before using it. + * + * If the shader requires textures (see gsk_gl_shader_get_n_textures()), then it is + * expected that you call gtk_snapshot_gl_shader_pop_texture() the number of times that are + * required. Each of these calls will generate a node that is added as a child to the gl shader + * node, which in turn will render these offscreen and pass as a texture to the shader. + * + * Once all textures (if any) are pop:ed, you must call the regular gtk_snapshot_pop(). + * + * If you want to use pre-existing textures as input to the shader rather than + * rendering new ones, use gtk_snapshot_append_texture() to push a texture node. These + * will be used directly rather than being re-rendered. + * + * For details on how to write shaders, see #GskGLShader. + */ +void +gtk_snapshot_push_gl_shader (GtkSnapshot *snapshot, + GskGLShader *shader, + const graphene_rect_t *bounds, + GBytes *take_args) +{ + GtkSnapshotState *state; + float scale_x, scale_y, dx, dy; + graphene_rect_t transformed_bounds; + int n_children = gsk_gl_shader_get_n_textures (shader); + + gtk_snapshot_ensure_affine (snapshot, &scale_x, &scale_y, &dx, &dy); + + state = gtk_snapshot_push_state (snapshot, + gtk_snapshot_get_current_state (snapshot)->transform, + gtk_snapshot_collect_gl_shader); + gtk_graphene_rect_scale_affine (bounds, scale_x, scale_y, dx, dy, &transformed_bounds); + state->data.glshader.bounds = transformed_bounds; + state->data.glshader.shader = g_object_ref (shader); + state->data.glshader.args = take_args; /* Takes ownership */ + if (n_children <= G_N_ELEMENTS (state->data.glshader.internal_nodes)) + state->data.glshader.nodes = NULL; + else + state->data.glshader.nodes = g_new (GskRenderNode *, n_children); + + for (int i = 0; i < n_children; i++) + { + state = gtk_snapshot_push_state (snapshot, + gtk_snapshot_get_current_state (snapshot)->transform, + gtk_snapshot_collect_gl_shader_texture); + state->data.glshader_texture.bounds = transformed_bounds; + state->data.glshader_texture.node_idx = n_children - 1 - i;/* We pop in reverse order */ + state->data.glshader_texture.n_children = n_children; + } +} + +static GskRenderNode * gtk_snapshot_collect_rounded_clip (GtkSnapshot *snapshot, GtkSnapshotState *state, GskRenderNode **nodes, @@ -1325,8 +1492,12 @@ gtk_snapshot_to_paintable (GtkSnapshot *snapshot, void gtk_snapshot_pop (GtkSnapshot *snapshot) { + GtkSnapshotState *state = gtk_snapshot_get_current_state (snapshot); GskRenderNode *node; + if (state->collect_func == gtk_snapshot_collect_gl_shader_texture) + g_warning ("Not enough calls to gtk_snapshot_gl_shader_pop_texture()."); + node = gtk_snapshot_pop_internal (snapshot); if (node) @@ -1334,6 +1505,34 @@ gtk_snapshot_pop (GtkSnapshot *snapshot) } /** + * gtk_snapshot_gl_shader_pop_texture: + * @snapshot: a #GtkSnapshot + * + * Removes the top element from the stack of render nodes and + * adds it to the nearest GskGLShaderNode below it. This must be called the + * same number of times as the number of textures is needed for the + * shader in gtk_snapshot_push_gl_shader(). + */ +void +gtk_snapshot_gl_shader_pop_texture (GtkSnapshot *snapshot) +{ + GtkSnapshotState *state = gtk_snapshot_get_current_state (snapshot); + GskRenderNode *node; + + if (state->collect_func != gtk_snapshot_collect_gl_shader_texture) + { + g_warning ("Too many calls to gtk_snapshot_gl_shader_pop_texture()."); + return; + } + + g_assert (state->collect_func == gtk_snapshot_collect_gl_shader_texture); + + node = gtk_snapshot_pop_internal (snapshot); + g_assert (node == NULL); +} + + +/** * gtk_snapshot_save: * @snapshot: a #GtkSnapshot * diff --git a/gtk/gtksnapshot.h b/gtk/gtksnapshot.h index e3c0e17e65..19e0045b17 100644 --- a/gtk/gtksnapshot.h +++ b/gtk/gtksnapshot.h @@ -99,8 +99,14 @@ GDK_AVAILABLE_IN_ALL void gtk_snapshot_push_cross_fade (GtkSnapshot *snapshot, double progress); GDK_AVAILABLE_IN_ALL +void gtk_snapshot_push_gl_shader (GtkSnapshot *snapshot, + GskGLShader *shader, + const graphene_rect_t *bounds, + GBytes *take_args); +GDK_AVAILABLE_IN_ALL +void gtk_snapshot_gl_shader_pop_texture (GtkSnapshot *snapshot); +GDK_AVAILABLE_IN_ALL void gtk_snapshot_pop (GtkSnapshot *snapshot); - GDK_AVAILABLE_IN_ALL void gtk_snapshot_save (GtkSnapshot *snapshot); GDK_AVAILABLE_IN_ALL |