summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Larsson <alexl@redhat.com>2020-09-18 17:56:02 +0200
committerAlexander Larsson <alexl@redhat.com>2020-09-29 09:51:16 +0200
commit950cc41e15cc8b92c75f49e5e31fbff5e6d6efa2 (patch)
treef2ff7f1582d4a7ed3bf55b973c89b4617b89dd8b
parent7ea755e206c36960db05f0e0660c6f5f20b692c6 (diff)
downloadgtk+-950cc41e15cc8b92c75f49e5e31fbff5e6d6efa2.tar.gz
GtkSnapshot: Add gtk_snapshot_push_glshader()
-rw-r--r--docs/reference/gtk/gtk4-sections.txt1
-rw-r--r--gtk/gtksnapshot.c199
-rw-r--r--gtk/gtksnapshot.h8
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