Compare commits

...

9 Commits

Author SHA1 Message Date
Matthias Clasen
32335feb48 Add another shader node demo
This one does stack-like transitions, using
some examples from gl-transitions.com.
2020-09-21 23:12:03 -04:00
Alexander Larsson
5ddebaad4d gtk-demo: Add GskGLShaderNode demo 2020-09-21 21:05:31 +02:00
Alexander Larsson
461ce87b52 gl backend: Add line numbers to source in glsl compilation errors
Almost always the source is created by combining various sources, which
means the line numbers in the error messages are hard to use. Adding
the line numbers to the source in the error message helps with this.
2020-09-21 21:05:31 +02:00
Alexander Larsson
84add08c36 Support GLShaderNode in backends
For vulkan/broadway this just means to ignore it, but for the gl
backend we support (with up to 4 texture inputs, which is similar to
what shadertoy does, so should be widely supported).
2020-09-21 21:05:31 +02:00
Alexander Larsson
f7978c784f GtkSnapshot: Add gtk_snapshot_push_glshader()
This is a helper to create GskGLShader nodes
2020-09-21 21:05:31 +02:00
Alexander Larsson
61ae25d55e Add GskGLShaderNode
This is a rendernode that is supposed to run a GLSL fragment
shader with a set of inputs and produce outputs.
The inputs are:
 * a vec4 args that can be used however the shader wants
 * a list of render nodes that are rendered to textures
 * A glsl string as a GRefString, which will be compiled

Additionally there is a fallback node which is used in case
OpenGL is not supported or there is some kind of failure
with the shader code.
2020-09-21 21:05:31 +02:00
Alexander Larsson
a70addb8a5 gl: Add some namespacing to the preamble symbols
This adds a gsk prefix to the stuff in the preamble, as we want to
avoid it conflicting with things in the main shader. Especially once
we start allow some customization of shaders.
2020-09-21 10:19:52 +02:00
Alexander Larsson
235de57cd8 gl: Properly report error if shader linking fails
In gsk_gl_shader_builder_create_program(), if linking fails we
need to return -1 to indicate error, rather than the old deleted
program id.
2020-09-21 10:01:33 +02:00
Alexander Larsson
975146fc79 glrenderer: Move ProgramState into Program
There is no real reason to have this on the side indexed via the
index, as it is stored next to each other anyway. Plus, storing them
together lets use use `Program` structures not in the array.
2020-09-21 10:01:29 +02:00
49 changed files with 2070 additions and 223 deletions

View File

@@ -133,6 +133,17 @@
<file>cogs.glsl</file>
<file>glowingstars.glsl</file>
</gresource>
<gresource prefix="/glshader">
<file>fire.glsl</file>
</gresource>
<gresource prefix="/gltransition">
<file>gtkshaderstack.c</file>
<file>gtkshaderstack.h</file>
<file>transition1.glsl</file>
<file>transition2.glsl</file>
<file>transition3.glsl</file>
<file>transition4.glsl</file>
</gresource>
<gresource prefix="/iconscroll">
<file>iconscroll.ui</file>
</gresource>
@@ -247,6 +258,8 @@
<file>gears.c</file>
<file>gestures.c</file>
<file>glarea.c</file>
<file>glshader.c</file>
<file>gltransition.c</file>
<file>headerbar.c</file>
<file>hypertext.c</file>
<file>iconscroll.c</file>

72
demos/gtk-demo/fire.glsl Normal file
View File

@@ -0,0 +1,72 @@
/* 2D -> [0..1] random number generator */
float random(vec2 st) {
return fract(sin(dot(st.xy,
vec2(12.9898,78.233))) *
43758.5453123);
}
/* Generate a smoothed 2d noise based on random() */
float noise(vec2 v) {
/* Round point v to integer grid grid */
vec2 grid_point = floor(v);
/* Randomize in grid corners */
float corner1 = random(grid_point);
float corner2 = random(grid_point + vec2(1, 0));
float corner3 = random(grid_point + vec2(0, 1));
float corner4 = random(grid_point + vec2(1, 1));
/* Interpolate smoothly between grid points */
vec2 fraction = smoothstep(vec2(0.0), vec2(1.0), fract(v));
return mix(mix(corner1, corner2, fraction.x),
mix(corner3, corner4, fraction.x),
fraction.y);
}
/* fractal brownian motion noice, see https://www.iquilezles.org/www/articles/fbm/fbm.htm */
float fbm(in vec2 x)
{
const float octaveScale = 1.9;
const float G = 0.5;
float f = 1.0;
float a = 1.0;
float t = 0.0;
int numOctaves = 5;
for (int i = 0; i < numOctaves; i++) {
t += a*noise(f*x);
f *= octaveScale;
a *= G;
}
return t;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord, in vec2 resolution, in vec2 uv)
{
float time = u_args.x;
vec2 xy = fragCoord / resolution;
float zoom = 3.0 - sin(time*0.5)*0.3;
// Normalize coord to height of widget
vec2 p = (vec2 (-resolution.x/2 + fragCoord.x, resolution.y - fragCoord.y) / resolution.yy)* zoom;
// Use recursive incantations of fbm
float q1 = fbm(p - vec2(0.8, 0.3) * time);
float q2 = fbm(p - vec2(0.5, 1.3) * time);
float r = fbm(2.0*p + vec2(q1,q2) - vec2(0.0, 1.0)*time*10.0 *0.4);
// Compute intensity, mostly on the bottom
float w = 2 * r * p.y;
// Smooth out left/right side and fade in at start
w /= smoothstep(0.0,0.1, xy.x)* smoothstep(0.0,0.1, 1.0-xy.x) * smoothstep(0.0,0.4, time);
// Compute colors
vec3 c = vec3(1.0,.2,.05);
vec3 color = 1.0 / (w*w/c + 1.0);
// Mix in widget
vec4 widget = texture(u_source,uv);
fragColor = mix(vec4(color,1), widget, 1.0-color.x);
}

84
demos/gtk-demo/glshader.c Normal file
View File

@@ -0,0 +1,84 @@
/* OpenGL/glshader
* #Keywords: OpenGL, shader
*
* Generate pixels using a custom fragment shader.
*
* The names of the uniforms are compatible with the shaders on shadertoy.com, so
* many of the shaders there work here too.
*/
#include <math.h>
#include <gtk/gtk.h>
#include "gtkshaderbin.h"
static GtkWidget *demo_window = NULL;
static void
close_window (GtkWidget *widget)
{
/* Reset the state */
demo_window = NULL;
}
static GtkWidget *
fire_bin_new (void)
{
GtkWidget *bin = gtk_shader_bin_new ();
GBytes *shader_b;
GskGLShader *shader;
shader_b = g_resources_lookup_data ("/glshader/fire.glsl", 0, NULL);
shader = gsk_glshader_new ((const char *)g_bytes_get_data (shader_b, NULL));
gtk_shader_bin_add_shader (GTK_SHADER_BIN (bin), shader, GTK_STATE_FLAG_PRELIGHT, GTK_STATE_FLAG_PRELIGHT);
return bin;
}
static GtkWidget *
create_glshader_window (GtkWidget *do_widget)
{
GtkWidget *window, *box, *button, *bin;
window = gtk_window_new ();
gtk_window_set_display (GTK_WINDOW (window), gtk_widget_get_display (do_widget));
gtk_window_set_title (GTK_WINDOW (window), "glshader");
g_signal_connect (window, "destroy", G_CALLBACK (close_window), NULL);
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, FALSE);
gtk_widget_set_margin_start (box, 12);
gtk_widget_set_margin_end (box, 12);
gtk_widget_set_margin_top (box, 12);
gtk_widget_set_margin_bottom (box, 12);
gtk_box_set_spacing (GTK_BOX (box), 6);
gtk_window_set_child (GTK_WINDOW (window), box);
bin = fire_bin_new ();
gtk_box_append (GTK_BOX (box), bin);
button = gtk_button_new_with_label ("Click me");
gtk_widget_set_receives_default (button, TRUE);
gtk_shader_bin_set_child (GTK_SHADER_BIN (bin), button);
bin = fire_bin_new ();
gtk_box_append (GTK_BOX (box), bin);
button = gtk_button_new_with_label ("Or me!");
gtk_widget_set_receives_default (button, TRUE);
gtk_shader_bin_set_child (GTK_SHADER_BIN (bin), button);
return window;
}
GtkWidget *
do_glshader (GtkWidget *do_widget)
{
if (!demo_window)
demo_window = create_glshader_window (do_widget);
if (!gtk_widget_get_visible (demo_window))
gtk_widget_show (demo_window);
else
gtk_window_destroy (GTK_WINDOW (demo_window));
return demo_window;
}

View File

@@ -0,0 +1,113 @@
/* OpenGL/Transitions
* #Keywords: OpenGL, shader
*
* Create transitions between pages using a custom fragment shader.
*
* The examples here are taken from gl-transitions.com.
*/
#include <math.h>
#include <gtk/gtk.h>
#include "gtkshaderstack.h"
static GtkWidget *demo_window = NULL;
static void
close_window (GtkWidget *widget)
{
/* Reset the state */
demo_window = NULL;
}
static GskGLShader *
gsk_shader_new_from_resource (const char *resource_path)
{
GBytes *shader_b;
GskGLShader *shader;
shader_b = g_resources_lookup_data (resource_path, 0, NULL);
shader = gsk_glshader_new ((const char *)g_bytes_get_data (shader_b, NULL));
g_bytes_unref (shader_b);
return shader;
}
static GtkWidget *
make_shader_stack (const char *resource_path)
{
GtkWidget *stack, *child;
GskGLShader *shader;
stack = gtk_shader_stack_new ();
shader = gsk_shader_new_from_resource (resource_path);
gtk_shader_stack_set_shader (GTK_SHADER_STACK (stack), shader);
g_object_unref (shader);
child = gtk_picture_new_for_resource ("/css_pixbufs/background.jpg");
gtk_picture_set_can_shrink (GTK_PICTURE (child), TRUE);
gtk_shader_stack_add_child (GTK_SHADER_STACK (stack), child);
child = gtk_picture_new_for_resource ("/transparent/portland-rose.jpg");
gtk_picture_set_can_shrink (GTK_PICTURE (child), TRUE);
gtk_shader_stack_add_child (GTK_SHADER_STACK (stack), child);
child = gtk_picture_new_for_resource ("/css_blendmodes/ducky.png");
gtk_picture_set_can_shrink (GTK_PICTURE (child), TRUE);
gtk_shader_stack_add_child (GTK_SHADER_STACK (stack), child);
return stack;
}
static GtkWidget *
create_gltransition_window (GtkWidget *do_widget)
{
GtkWidget *window, *grid;
window = gtk_window_new ();
gtk_window_set_display (GTK_WINDOW (window), gtk_widget_get_display (do_widget));
gtk_window_set_title (GTK_WINDOW (window), "Transitions");
gtk_window_set_default_size (GTK_WINDOW (window), 400, 400);
g_signal_connect (window, "destroy", G_CALLBACK (close_window), NULL);
grid = gtk_grid_new ();
gtk_widget_set_halign (grid, GTK_ALIGN_CENTER);
gtk_widget_set_valign (grid, GTK_ALIGN_CENTER);
gtk_widget_set_margin_start (grid, 12);
gtk_widget_set_margin_end (grid, 12);
gtk_widget_set_margin_top (grid, 12);
gtk_widget_set_margin_bottom (grid, 12);
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
gtk_grid_set_row_homogeneous (GTK_GRID (grid), TRUE);
gtk_grid_set_column_homogeneous (GTK_GRID (grid), TRUE);
gtk_window_set_child (GTK_WINDOW (window), grid);
gtk_grid_attach (GTK_GRID (grid),
make_shader_stack ("/gltransition/transition1.glsl"),
0, 0, 1, 1);
gtk_grid_attach (GTK_GRID (grid),
make_shader_stack ("/gltransition/transition2.glsl"),
1, 0, 1, 1);
gtk_grid_attach (GTK_GRID (grid),
make_shader_stack ("/gltransition/transition3.glsl"),
0, 1, 1, 1);
gtk_grid_attach (GTK_GRID (grid),
make_shader_stack ("/gltransition/transition4.glsl"),
1, 1, 1, 1);
return window;
}
GtkWidget *
do_gltransition (GtkWidget *do_widget)
{
if (!demo_window)
demo_window = create_gltransition_window (do_widget);
if (!gtk_widget_get_visible (demo_window))
gtk_widget_show (demo_window);
else
gtk_window_destroy (GTK_WINDOW (demo_window));
return demo_window;
}

View File

@@ -0,0 +1,219 @@
#include "gtkshaderbin.h"
typedef struct {
GskGLShader *shader;
GtkStateFlags state;
GtkStateFlags state_mask;
} ShaderInfo;
struct _GtkShaderBin
{
GtkWidget parent_instance;
GtkWidget *child;
GskGLShader *active_shader;
GPtrArray *shaders;
guint tick_id;
float time;
gint64 first_frame_time;
};
struct _GtkShaderBinClass
{
GtkWidgetClass parent_class;
};
G_DEFINE_TYPE (GtkShaderBin, gtk_shader_bin, GTK_TYPE_WIDGET)
static void
shader_info_free (ShaderInfo *info)
{
g_object_unref (info->shader);
g_free (info);
}
static void
gtk_shader_bin_finalize (GObject *object)
{
GtkShaderBin *self = GTK_SHADER_BIN (object);
g_ptr_array_free (self->shaders, TRUE);
G_OBJECT_CLASS (gtk_shader_bin_parent_class)->finalize (object);
}
static void
gtk_shader_bin_dispose (GObject *object)
{
GtkShaderBin *self = GTK_SHADER_BIN (object);
g_clear_pointer (&self->child, gtk_widget_unparent);
G_OBJECT_CLASS (gtk_shader_bin_parent_class)->dispose (object);
}
static gboolean
gtk_shader_bin_tick (GtkWidget *widget,
GdkFrameClock *frame_clock,
gpointer unused)
{
GtkShaderBin *self = GTK_SHADER_BIN (widget);
gint64 frame_time;
frame_time = gdk_frame_clock_get_frame_time (gtk_widget_get_frame_clock (widget));
if (self->first_frame_time == 0)
self->first_frame_time = frame_time;
self->time = (frame_time - self->first_frame_time) / (float)G_USEC_PER_SEC;
gtk_widget_queue_allocate (widget);
return G_SOURCE_CONTINUE;
}
static void
gtk_shader_bin_init (GtkShaderBin *self)
{
self->shaders = g_ptr_array_new_with_free_func ((GDestroyNotify)shader_info_free);
}
void
gtk_shader_bin_update_active_shader (GtkShaderBin *self)
{
GtkStateFlags new_state = gtk_widget_get_state_flags (GTK_WIDGET (self));
GskGLShader *new_shader = NULL;
for (int i = 0; i < self->shaders->len; i++)
{
ShaderInfo *info = g_ptr_array_index (self->shaders, i);
if ((info->state_mask & new_state) == info->state)
{
new_shader = info->shader;
break;
}
}
if (self->active_shader == new_shader)
return;
self->active_shader = new_shader;
self->first_frame_time = 0;
if (self->active_shader)
{
if (self->tick_id == 0)
self->tick_id = gtk_widget_add_tick_callback (GTK_WIDGET (self),
gtk_shader_bin_tick,
NULL, NULL);
}
else
{
if (self->tick_id != 0)
{
gtk_widget_remove_tick_callback (GTK_WIDGET (self), self->tick_id);
self->tick_id = 0;
}
}
gtk_widget_queue_allocate (GTK_WIDGET (self));
}
static void
gtk_shader_bin_state_flags_changed (GtkWidget *widget,
GtkStateFlags previous_state_flags)
{
GtkShaderBin *self = GTK_SHADER_BIN (widget);
gtk_shader_bin_update_active_shader (self);
}
void
gtk_shader_bin_add_shader (GtkShaderBin *self,
GskGLShader *shader,
GtkStateFlags state,
GtkStateFlags state_mask)
{
ShaderInfo *info = g_new0 (ShaderInfo, 1);
info->shader = g_object_ref (shader);
info->state = state;
info->state_mask = state_mask;
g_ptr_array_add (self->shaders, info);
gtk_shader_bin_update_active_shader (self);
}
void
gtk_shader_bin_set_child (GtkShaderBin *self,
GtkWidget *child)
{
if (self->child == child)
return;
g_clear_pointer (&self->child, gtk_widget_unparent);
if (child)
{
self->child = child;
gtk_widget_set_parent (child, GTK_WIDGET (self));
}
}
GtkWidget *
gtk_shader_bin_get_child (GtkShaderBin *self)
{
return self->child;
}
static void
gtk_shader_bin_snapshot (GtkWidget *widget,
GtkSnapshot *snapshot)
{
GtkShaderBin *self = GTK_SHADER_BIN (widget);
int width, height;
width = gtk_widget_get_width (widget);
height = gtk_widget_get_height (widget);
if (self->active_shader)
{
graphene_vec4_t args;
gtk_snapshot_push_glshader (snapshot, self->active_shader,
graphene_vec4_init (&args, self->time, 0, 0, 0),
&GRAPHENE_RECT_INIT(0, 0, width, height),
1);
gtk_widget_snapshot_child (widget, self->child, snapshot);
gtk_snapshot_pop (snapshot); /* Fallback */
gtk_widget_snapshot_child (widget, self->child, snapshot);
gtk_snapshot_pop (snapshot); /* Shader node child 1 */
}
else
{
gtk_widget_snapshot_child (widget, self->child, snapshot);
}
}
static void
gtk_shader_bin_class_init (GtkShaderBinClass *class)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->finalize = gtk_shader_bin_finalize;
object_class->dispose = gtk_shader_bin_dispose;
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
widget_class->snapshot = gtk_shader_bin_snapshot;
widget_class->state_flags_changed = gtk_shader_bin_state_flags_changed;
}
GtkWidget *
gtk_shader_bin_new (void)
{
GtkShaderBin *self;
self = g_object_new (GTK_TYPE_SHADER_BIN, NULL);
return GTK_WIDGET (self);
}

View File

@@ -0,0 +1,22 @@
#ifndef __GTK_SHADER_BIN_H__
#define __GTK_SHADER_BIN_H__
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define GTK_TYPE_SHADER_BIN (gtk_shader_bin_get_type ())
G_DECLARE_FINAL_TYPE (GtkShaderBin, gtk_shader_bin, GTK, SHADER_BIN, GtkWidget)
GtkWidget *gtk_shader_bin_new (void);
void gtk_shader_bin_add_shader (GtkShaderBin *self,
GskGLShader *shader,
GtkStateFlags state,
GtkStateFlags state_mask);
void gtk_shader_bin_set_child (GtkShaderBin *self,
GtkWidget *child);
GtkWidget *gtk_shader_bin_get_child (GtkShaderBin *self);
G_END_DECLS
#endif /* __GTK_SHADER_BIN_H__ */

View File

@@ -0,0 +1,255 @@
#include "gtkshaderstack.h"
struct _GtkShaderStack
{
GtkWidget parent_instance;
GskGLShader *shader;
GPtrArray *children;
int current;
int next;
guint tick_id;
float time;
float duration;
gint64 start_time;
};
struct _GtkShaderStackClass
{
GtkWidgetClass parent_class;
};
G_DEFINE_TYPE (GtkShaderStack, gtk_shader_stack, GTK_TYPE_WIDGET)
static void
gtk_shader_stack_finalize (GObject *object)
{
GtkShaderStack *self = GTK_SHADER_STACK (object);
g_object_unref (self->shader);
G_OBJECT_CLASS (gtk_shader_stack_parent_class)->finalize (object);
}
static gboolean
transition_cb (GtkWidget *widget,
GdkFrameClock *clock,
gpointer unused)
{
GtkShaderStack *self = GTK_SHADER_STACK (widget);
gint64 frame_time;
frame_time = gdk_frame_clock_get_frame_time (clock);
if (self->start_time == 0)
self->start_time = frame_time;
self->time = (frame_time - self->start_time) / (float)G_USEC_PER_SEC;
gtk_widget_queue_draw (widget);
if (self->time >= self->duration)
{
self->current = self->next;
self->next = -1;
return G_SOURCE_REMOVE;
}
else
return G_SOURCE_CONTINUE;
}
static void
start_transition (GtkShaderStack *self)
{
self->start_time = 0;
self->tick_id = gtk_widget_add_tick_callback (GTK_WIDGET (self),
transition_cb,
NULL, NULL);
}
static void
stop_transition (GtkShaderStack *self)
{
if (self->tick_id != 0)
{
gtk_widget_remove_tick_callback (GTK_WIDGET (self), self->tick_id);
self->tick_id = 0;
}
self->next = -1;
}
static void
gtk_shader_stack_dispose (GObject *object)
{
GtkShaderStack *self = GTK_SHADER_STACK (object);
stop_transition (self);
g_clear_pointer (&self->children, g_ptr_array_unref);
G_OBJECT_CLASS (gtk_shader_stack_parent_class)->dispose (object);
}
static void
clicked_cb (GtkGestureClick *gesture,
guint n_pressed,
double x,
double y,
gpointer data)
{
GtkShaderStack *self = GTK_SHADER_STACK (data);
stop_transition (self);
self->next = (self->current + 1) % self->children->len;
start_transition (self);
}
static void
gtk_shader_stack_init (GtkShaderStack *self)
{
GtkGesture *gesture;
self->children = g_ptr_array_new_with_free_func ((GDestroyNotify)gtk_widget_unparent);
self->current = -1;
self->next = -1;
self->duration = 1.0;
gesture = gtk_gesture_click_new ();
g_signal_connect (gesture, "pressed", G_CALLBACK (clicked_cb), self);
gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture));
}
static void
gtk_shader_stack_measure (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
GtkShaderStack *self = GTK_SHADER_STACK (widget);
int i;
*minimum = 0;
*natural = 0;
for (i = 0; i < self->children->len; i++)
{
GtkWidget *child = g_ptr_array_index (self->children, i);
int child_min, child_nat;
if (gtk_widget_get_visible (child))
{
gtk_widget_measure (child, orientation, for_size, &child_min, &child_nat, NULL, NULL);
*minimum = MAX (*minimum, child_min);
*natural = MAX (*natural, child_nat);
}
}
}
static void
gtk_shader_stack_size_allocate (GtkWidget *widget,
int width,
int height,
int baseline)
{
GtkShaderStack *self = GTK_SHADER_STACK (widget);
GtkAllocation child_allocation;
GtkWidget *child;
int i;
child_allocation.x = 0;
child_allocation.y = 0;
child_allocation.width = width;
child_allocation.height = height;
for (i = 0; i < self->children->len; i++)
{
child = g_ptr_array_index (self->children, i);
if (gtk_widget_get_visible (child))
gtk_widget_size_allocate (child, &child_allocation, -1);
}
}
static void
gtk_shader_stack_snapshot (GtkWidget *widget,
GtkSnapshot *snapshot)
{
GtkShaderStack *self = GTK_SHADER_STACK (widget);
int width, height;
GtkWidget *current, *next;
width = gtk_widget_get_width (widget);
height = gtk_widget_get_height (widget);
current = g_ptr_array_index (self->children, self->current);
if (self->next == -1)
{
gtk_widget_snapshot_child (widget, current, snapshot);
}
else
{
graphene_vec4_t args;
next = g_ptr_array_index (self->children, self->next);
gtk_snapshot_push_glshader (snapshot, self->shader,
graphene_vec4_init (&args, self->time, self->time / self->duration, 0, 0),
&GRAPHENE_RECT_INIT(0, 0, width, height),
2);
gtk_widget_snapshot_child (widget, next, snapshot);
gtk_snapshot_pop (snapshot); /* Fallback */
gtk_widget_snapshot_child (widget, current, snapshot);
gtk_snapshot_pop (snapshot); /* current child */
gtk_widget_snapshot_child (widget, next, snapshot);
gtk_snapshot_pop (snapshot); /* next child */
}
}
static void
gtk_shader_stack_class_init (GtkShaderStackClass *class)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->finalize = gtk_shader_stack_finalize;
object_class->dispose = gtk_shader_stack_dispose;
widget_class->snapshot = gtk_shader_stack_snapshot;
widget_class->measure = gtk_shader_stack_measure;
widget_class->size_allocate = gtk_shader_stack_size_allocate;
}
GtkWidget *
gtk_shader_stack_new (void)
{
return g_object_new (GTK_TYPE_SHADER_STACK, NULL);
}
void
gtk_shader_stack_set_shader (GtkShaderStack *self,
GskGLShader *shader)
{
g_set_object (&self->shader, shader);
}
void
gtk_shader_stack_add_child (GtkShaderStack *self,
GtkWidget *child)
{
g_ptr_array_add (self->children, child);
gtk_widget_set_parent (child, GTK_WIDGET (self));
gtk_widget_queue_resize (GTK_WIDGET (self));
if (self->current == -1)
self->current = 0;
}

View File

@@ -0,0 +1,19 @@
#ifndef __GTK_SHADER_STACK_H__
#define __GTK_SHADER_STACK_H__
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define GTK_TYPE_SHADER_STACK (gtk_shader_stack_get_type ())
G_DECLARE_FINAL_TYPE (GtkShaderStack, gtk_shader_stack, GTK, SHADER_STACK, GtkWidget)
GtkWidget * gtk_shader_stack_new (void);
void gtk_shader_stack_set_shader (GtkShaderStack *self,
GskGLShader *shader);
void gtk_shader_stack_add_child (GtkShaderStack *self,
GtkWidget *child);
G_END_DECLS
#endif /* __GTK_SHADER_STACK_H__ */

View File

@@ -32,6 +32,8 @@ demos = files([
'gears.c',
'gestures.c',
'glarea.c',
'glshader.c',
'gltransition.c',
'headerbar.c',
'hypertext.c',
'iconscroll.c',
@@ -102,7 +104,9 @@ extra_demo_sources = files(['main.c',
'gtkfishbowl.c',
'fontplane.c',
'gtkgears.c',
'gtkshaderbin.c',
'gtkshadertoy.c',
'gtkshaderstack.c',
'puzzlepiece.c',
'bluroverlay.c',
'demoimage.c',

View File

@@ -0,0 +1,32 @@
float progress = u_args.y;
vec4 getFromColor(vec2 uv) {
return texture(u_source, uv);
}
vec4 getToColor(vec2 uv) {
return texture(u_source2, uv);
}
// Source: https://gl-transitions.com/editor/wind
// Author: gre
// License: MIT
uniform float size = 0.2;
float rand(vec2 co) {
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
vec4 transition(vec2 p) {
float r = rand(vec2(0, p.y));
float m = smoothstep(0.0, -size, p.x*(1.0-size) + size*r - (progress * (1.0 + size)));
return mix(getFromColor(p), getToColor(p), m);
}
void mainImage(out vec4 fragColor, in vec2 fragCoord, in vec2 resolution, in vec2 uv)
{
fragColor = transition(uv);
}

View File

@@ -0,0 +1,33 @@
float progress = u_args.y;
vec4 getFromColor (vec2 uv) {
return texture(u_source, uv);
}
vec4 getToColor (vec2 uv) {
return texture(u_source2, uv);
}
// Source: https://gl-transitions.com/editor/Radial
// License: MIT
// Author: Xaychru
uniform float smoothness = 1.0;
const float PI = 3.141592653589;
vec4 transition(vec2 p) {
vec2 rp = p*2.-1.;
return mix(
getToColor(p),
getFromColor(p),
smoothstep(0., smoothness, atan(rp.y,rp.x) - (progress-.5) * PI * 2.5)
);
}
void mainImage(out vec4 fragColor, in vec2 fragCoord, in vec2 resolution, in vec2 uv)
{
fragColor = transition(uv);
}

View File

@@ -0,0 +1,26 @@
float progress = u_args.y;
vec4 getFromColor (vec2 uv) {
return texture(u_source, uv);
}
vec4 getToColor (vec2 uv) {
return texture(u_source2, uv);
}
// Source: https://gl-transitions.com/editor/crosswarp
// Author: Eke Péter <peterekepeter@gmail.com>
// License: MIT
vec4 transition(vec2 p) {
float x = progress;
x=smoothstep(.0,1.0,(x*2.0+p.x-1.0));
return mix(getFromColor((p-.5)*(1.-x)+.5), getToColor((p-.5)*x+.5), x);
}
void mainImage(out vec4 fragColor, in vec2 fragCoord, in vec2 resolution, in vec2 uv)
{
fragColor = transition(uv);
}

View File

@@ -0,0 +1,40 @@
float progress = u_args.y;
vec4 getFromColor (vec2 uv) {
return texture(u_source, uv);
}
vec4 getToColor (vec2 uv) {
return texture(u_source2, uv);
}
// Source: https://gl-transitions.com/editor/kaleidoscope
// Author: nwoeanhinnogaehr
// License: MIT
uniform float speed = 1.0;
uniform float angle = 1.0;
uniform float power = 1.5;
vec4 transition(vec2 uv) {
vec2 p = uv.xy / vec2(1.0).xy;
vec2 q = p;
float t = pow(progress, power)*speed;
p = p -0.5;
for (int i = 0; i < 7; i++) {
p = vec2(sin(t)*p.x + cos(t)*p.y, sin(t)*p.y - cos(t)*p.x);
t += angle;
p = abs(mod(p, 2.0) - 1.0);
}
abs(mod(p, 1.0));
return mix(
mix(getFromColor(q), getToColor(q), progress),
mix(getFromColor(p), getToColor(p), progress), 1.0 - 2.0*abs(progress - 0.5));
}
void mainImage(out vec4 fragColor, in vec2 fragCoord, in vec2 resolution, in vec2 uv)
{
fragColor = transition(uv);
}

View File

@@ -272,6 +272,11 @@ collect_reused_child_nodes (GskRenderer *renderer,
/* Bin nodes */
case GSK_GLSHADER_NODE:
collect_reused_node (renderer,
gsk_glshader_node_get_fallback_child (node));
break;
case GSK_SHADOW_NODE:
collect_reused_node (renderer,
gsk_shadow_node_get_child (node));
@@ -792,6 +797,11 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer,
}
return;
case GSK_GLSHADER_NODE:
gsk_broadway_renderer_add_node (renderer,
gsk_glshader_node_get_fallback_child (node), offset_x, offset_y, clip_bounds);
return;
/* Generic nodes */
case GSK_CONTAINER_NODE:

View File

@@ -64,6 +64,11 @@
glGetUniformLocation(program_ptr->id, "u_" #uniform_basename);\
}G_STMT_END
static Program *gsk_gl_renderer_lookup_custom_program (GskGLRenderer *self,
GskGLShader *shader);
static Program *gsk_gl_renderer_create_custom_program (GskGLRenderer *self,
GskGLShader *shader);
typedef enum
{
FORCE_OFFSCREEN = 1 << 0,
@@ -130,6 +135,13 @@ print_render_node_tree (GskRenderNode *root, int level)
print_render_node_tree (gsk_shadow_node_get_child (root), level + 1);
break;
case GSK_GLSHADER_NODE:
g_print ("%*s GLShader\n", level * INDENT, " ");
print_render_node_tree (gsk_glshader_node_get_fallback_child (root), level + 1);
for (i = 0; i < gsk_glshader_node_get_n_children (root); i++)
print_render_node_tree (gsk_glshader_node_get_child (root, i), level + 1);
break;
case GSK_TEXTURE_NODE:
g_print ("%*s Texture %p\n", level * INDENT, " ", gsk_texture_node_get_texture (root));
break;
@@ -495,6 +507,40 @@ struct _GskGLRendererClass
G_DEFINE_TYPE (GskGLRenderer, gsk_gl_renderer, GSK_TYPE_RENDERER)
static void
init_shader_builder (GskGLRenderer *self,
GskGLShaderBuilder *shader_builder)
{
#ifdef G_ENABLE_DEBUG
if (GSK_RENDERER_DEBUG_CHECK (GSK_RENDERER (self), SHADERS))
shader_builder->debugging = TRUE;
#endif
if (gdk_gl_context_get_use_es (self->gl_context))
{
gsk_gl_shader_builder_set_glsl_version (shader_builder, SHADER_VERSION_GLES);
shader_builder->gles = TRUE;
}
else if (gdk_gl_context_is_legacy (self->gl_context))
{
int maj, min;
gdk_gl_context_get_version (self->gl_context, &maj, &min);
if (maj == 3)
gsk_gl_shader_builder_set_glsl_version (shader_builder, SHADER_VERSION_GL3_LEGACY);
else
gsk_gl_shader_builder_set_glsl_version (shader_builder, SHADER_VERSION_GL2_LEGACY);
shader_builder->legacy = TRUE;
}
else
{
gsk_gl_shader_builder_set_glsl_version (shader_builder, SHADER_VERSION_GL3);
shader_builder->gl3 = TRUE;
}
}
static void G_GNUC_UNUSED
add_rect_ops (RenderOpBuilder *builder,
const graphene_rect_t *r)
@@ -1006,6 +1052,101 @@ render_texture_node (GskGLRenderer *self,
}
}
static inline void
render_glshader_node (GskGLRenderer *self,
GskRenderNode *node,
RenderOpBuilder *builder)
{
GskGLShader *shader = gsk_glshader_node_get_shader (node);
Program *program = gsk_gl_renderer_lookup_custom_program (self, shader);
GskRenderNode *fallback = gsk_glshader_node_get_fallback_child (node);
int n_children = gsk_glshader_node_get_n_children (node);
if (program == NULL)
{
GskGLShaderBuilder shader_builder;
const char *shader_source;
GError *error = NULL;
program = gsk_gl_renderer_create_custom_program (self, shader);
shader_source = gsk_glshader_peek_source (shader);
gsk_gl_shader_builder_init (&shader_builder,
"/org/gtk/libgsk/glsl/preamble.glsl",
"/org/gtk/libgsk/glsl/preamble.vs.glsl",
"/org/gtk/libgsk/glsl/preamble.fs.glsl");
init_shader_builder (self, &shader_builder);
program->id = gsk_gl_shader_builder_create_program (&shader_builder,
"/org/gtk/libgsk/glsl/custom.glsl",
shader_source,
&error);
if (program->id >= 0)
{
INIT_COMMON_UNIFORM_LOCATION (program, alpha);
INIT_COMMON_UNIFORM_LOCATION (program, source);
INIT_COMMON_UNIFORM_LOCATION (program, clip_rect);
INIT_COMMON_UNIFORM_LOCATION (program, viewport);
INIT_COMMON_UNIFORM_LOCATION (program, projection);
INIT_COMMON_UNIFORM_LOCATION (program, modelview);
program->glshader.size_location = glGetUniformLocation(program->id, "u_size");
program->glshader.args_location = glGetUniformLocation(program->id, "u_args");
program->glshader.extra_source_locations[0] = glGetUniformLocation(program->id, "u_source2");
program->glshader.extra_source_locations[1] = glGetUniformLocation(program->id, "u_source3");
program->glshader.extra_source_locations[2] = glGetUniformLocation(program->id, "u_source4");
}
else
{
g_warning ("Failed to compile gl shader: %s\n", error->message);
g_error_free (error);
}
gsk_gl_shader_builder_finish (&shader_builder);
}
if (program->id >= 0 && n_children <= 4)
{
graphene_vec4_t args;
graphene_vec2_t size;
TextureRegion regions[4];
gboolean is_offscreen[4];
for (guint i = 0; i < n_children; i++)
{
GskRenderNode *child = gsk_glshader_node_get_child (node, i);
if (!add_offscreen_ops (self, builder,
&node->bounds,
child,
&regions[i], &is_offscreen[i],
FORCE_OFFSCREEN | RESET_CLIP | RESET_OPACITY))
{
if (fallback)
gsk_gl_renderer_add_render_ops (self, fallback, builder);
return;
}
}
gsk_glshader_node_get_args (node, &args);
ops_set_program (builder, program);
graphene_vec2_init (&size, node->bounds.size.width, node->bounds.size.height);
ops_set_glshader_args (builder, &size, &args);
if (n_children >= 0)
ops_set_texture (builder, regions[0].texture_id);
for (guint i = 1; i < n_children; i++)
ops_set_extra_texture (builder, regions[i].texture_id, i-1);
load_offscreen_vertex_data (ops_draw (builder, NULL), node, builder);
}
else
{
if (fallback)
gsk_gl_renderer_add_render_ops (self, fallback, builder);
}
}
/* Returns TRUE is applying transform to bounds
* yields an axis-aligned rectangle
*/
@@ -2671,6 +2812,18 @@ apply_source_texture_op (const Program *program,
glBindTexture (GL_TEXTURE_2D, op->texture_id);
}
static inline void
apply_source_extra_texture_op (const Program *program,
const OpExtraTexture *op)
{
g_assert(op->texture_id != 0);
OP_PRINT (" -> New extra texture %d: %d", op->idx, op->texture_id);
/* Use texture unit 1 + op->idx for the source */
glUniform1i (program->glshader.extra_source_locations[op->idx], 1 + op->idx);
glActiveTexture (GL_TEXTURE0 + 1 + op->idx);
glBindTexture (GL_TEXTURE_2D, op->texture_id);
}
static inline void
apply_color_matrix_op (const Program *program,
const OpColorMatrix *op)
@@ -2815,6 +2968,16 @@ apply_border_op (const Program *program,
glUniform4fv (program->border.outline_rect_location, 3, (float *)&op->outline.bounds);
}
static inline void
apply_glshader_args_op (const Program *program,
const OpGLShader *op)
{
OP_PRINT (" -> Custom Args");
glUniform2fv (program->glshader.size_location, 1, op->size);
glUniform4fv (program->glshader.args_location, 1, op->args);
}
static inline void
apply_border_width_op (const Program *program,
const OpBorder *op)
@@ -2886,6 +3049,28 @@ gsk_gl_renderer_dispose (GObject *gobject)
G_OBJECT_CLASS (gsk_gl_renderer_parent_class)->dispose (gobject);
}
static void
program_init (Program *program)
{
program->index = -1;
program->state.opacity = 1.0f;
}
static void
program_finalize (Program *program)
{
if (program->id > 0)
glDeleteProgram (program->id);
gsk_transform_unref (program->state.modelview);
}
static void
program_free (Program *program)
{
program_finalize (program);
g_free (program);
}
static GskGLRendererPrograms *
gsk_gl_renderer_programs_new (void)
{
@@ -2895,9 +3080,11 @@ gsk_gl_renderer_programs_new (void)
programs = g_new0 (GskGLRendererPrograms, 1);
programs->ref_count = 1;
for (i = 0; i < GL_N_PROGRAMS; i ++)
{
programs->state[i].opacity = 1.0f;
}
program_init (&programs->programs[i]);
/* We use direct hash for performance, not string hash on the source, because we assume each caller
* reuses a single GskGLShader for all uses and different callers will use different source content. */
programs->custom_programs = g_hash_table_new_full (g_direct_hash, g_direct_equal, (GDestroyNotify)g_object_unref, (GDestroyNotify)program_free);
return programs;
}
@@ -2918,15 +3105,33 @@ gsk_gl_renderer_programs_unref (GskGLRendererPrograms *programs)
if (programs->ref_count == 0)
{
for (i = 0; i < GL_N_PROGRAMS; i ++)
{
if (programs->programs[i].id > 0)
glDeleteProgram (programs->programs[i].id);
gsk_transform_unref (programs->state[i].modelview);
}
program_finalize (&programs->programs[i]);
g_hash_table_destroy (programs->custom_programs);
g_free (programs);
}
}
static Program *
gsk_gl_renderer_lookup_custom_program (GskGLRenderer *self,
GskGLShader *shader)
{
return g_hash_table_lookup (self->programs->custom_programs, shader);
}
static Program *
gsk_gl_renderer_create_custom_program (GskGLRenderer *self,
GskGLShader *shader)
{
Program *program = g_new0 (Program, 1);
program_init (program);
g_hash_table_insert (self->programs->custom_programs, g_object_ref (shader), program);
return program;
}
static GskGLRendererPrograms *
gsk_gl_renderer_create_programs (GskGLRenderer *self,
GError **error)
@@ -2961,35 +3166,7 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self,
g_assert (G_N_ELEMENTS (program_definitions) == GL_N_PROGRAMS);
#ifdef G_ENABLE_DEBUG
if (GSK_RENDERER_DEBUG_CHECK (GSK_RENDERER (self), SHADERS))
shader_builder.debugging = TRUE;
#endif
if (gdk_gl_context_get_use_es (self->gl_context))
{
gsk_gl_shader_builder_set_glsl_version (&shader_builder, SHADER_VERSION_GLES);
shader_builder.gles = TRUE;
}
else if (gdk_gl_context_is_legacy (self->gl_context))
{
int maj, min;
gdk_gl_context_get_version (self->gl_context, &maj, &min);
if (maj == 3)
gsk_gl_shader_builder_set_glsl_version (&shader_builder, SHADER_VERSION_GL3_LEGACY);
else
gsk_gl_shader_builder_set_glsl_version (&shader_builder, SHADER_VERSION_GL2_LEGACY);
shader_builder.legacy = TRUE;
}
else
{
gsk_gl_shader_builder_set_glsl_version (&shader_builder, SHADER_VERSION_GL3);
shader_builder.gl3 = TRUE;
}
init_shader_builder (self, &shader_builder);
programs = gsk_gl_renderer_programs_new ();
@@ -3000,7 +3177,7 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self,
prog->index = i;
prog->id = gsk_gl_shader_builder_create_program (&shader_builder,
program_definitions[i].resource_path,
error);
NULL, error);
if (prog->id < 0)
{
g_clear_pointer (&programs, gsk_gl_renderer_programs_unref);
@@ -3445,6 +3622,10 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer *self,
render_repeat_node (self, node, builder);
break;
case GSK_GLSHADER_NODE:
render_glshader_node (self, node, builder);
break;
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
case GSK_REPEATING_RADIAL_GRADIENT_NODE:
case GSK_CAIRO_NODE:
@@ -3727,6 +3908,10 @@ gsk_gl_renderer_render_ops (GskGLRenderer *self)
apply_source_texture_op (program, ptr);
break;
case OP_CHANGE_EXTRA_SOURCE_TEXTURE:
apply_source_extra_texture_op (program, ptr);
break;
case OP_CHANGE_CROSS_FADE:
g_assert (program == &self->programs->cross_fade_program);
apply_cross_fade_op (program, ptr);
@@ -3773,6 +3958,10 @@ gsk_gl_renderer_render_ops (GskGLRenderer *self)
apply_repeat_op (program, ptr);
break;
case OP_CHANGE_GLSHADER_ARGS:
apply_glshader_args_op (program, ptr);
break;
case OP_DRAW:
{
const OpDraw *op = ptr;

View File

@@ -60,7 +60,7 @@ get_current_program_state (RenderOpBuilder *builder)
if (!builder->current_program)
return NULL;
return &builder->programs->state[builder->current_program->index];
return &builder->current_program->state;
}
void
@@ -218,10 +218,10 @@ ops_free (RenderOpBuilder *builder)
void
ops_set_program (RenderOpBuilder *builder,
const Program *program)
Program *program)
{
OpProgram *op;
ProgramState *program_state;
ProgramState *program_state = NULL;
if (builder->current_program == program)
return;
@@ -231,7 +231,7 @@ ops_set_program (RenderOpBuilder *builder,
builder->current_program = program;
program_state = &builder->programs->state[program->index];
program_state = &program->state;
if (memcmp (&builder->current_projection, &program_state->projection, sizeof (graphene_matrix_t)) != 0)
{
@@ -558,6 +558,18 @@ ops_set_texture (RenderOpBuilder *builder,
builder->current_texture = texture_id;
}
void
ops_set_extra_texture (RenderOpBuilder *builder,
int texture_id,
int idx)
{
OpExtraTexture *op;
op = ops_begin (builder, OP_CHANGE_EXTRA_SOURCE_TEXTURE);
op->texture_id = texture_id;
op->idx = idx;
}
int
ops_set_render_target (RenderOpBuilder *builder,
int render_target_id)
@@ -621,6 +633,26 @@ ops_set_color (RenderOpBuilder *builder,
op->rgba = color;
}
void
ops_set_glshader_args (RenderOpBuilder *builder,
const graphene_vec2_t *size,
const graphene_vec4_t *args)
{
ProgramState *current_program_state = get_current_program_state (builder);
OpGLShader *op;
if (graphene_vec2_equal (size, &current_program_state->custom.size) &&
graphene_vec4_equal (args, &current_program_state->custom.args))
return;
current_program_state->custom.size = *size;
current_program_state->custom.args = *args;
op = ops_begin (builder, OP_CHANGE_GLSHADER_ARGS);
graphene_vec4_to_float (args, &op->args[0]);
graphene_vec2_to_float (size, &op->size[0]);
}
void
ops_set_color_matrix (RenderOpBuilder *builder,
const graphene_matrix_t *matrix,

View File

@@ -31,6 +31,61 @@ typedef struct
OpsMatrixMetadata metadata;
} MatrixStackEntry;
typedef struct
{
GskTransform *modelview;
GskRoundedRect clip;
graphene_matrix_t projection;
int source_texture;
graphene_rect_t viewport;
float opacity;
/* Per-program state */
union {
GdkRGBA color;
struct {
graphene_matrix_t matrix;
graphene_vec4_t offset;
} color_matrix;
struct {
float widths[4];
GdkRGBA color;
GskRoundedRect outline;
} border;
struct {
GskRoundedRect outline;
float dx;
float dy;
float spread;
GdkRGBA color;
} inset_shadow;
struct {
GskRoundedRect outline;
float dx;
float dy;
float spread;
GdkRGBA color;
} unblurred_outset_shadow;
struct {
int n_color_stops;
GskColorStop color_stops[MAX_GRADIENT_STOPS];
float start_point[2];
float end_point[2];
} linear_gradient;
struct {
int n_color_stops;
GskColorStop color_stops[MAX_GRADIENT_STOPS];
float center[2];
float start;
float end;
float radius[2]; /* h/v */
} radial_gradient;
struct {
graphene_vec2_t size;
graphene_vec4_t args;
} custom;
};
} ProgramState;
struct _Program
{
int index; /* Into the renderer's program array */
@@ -108,59 +163,15 @@ struct _Program
int child_bounds_location;
int texture_rect_location;
} repeat;
struct {
int size_location;
int args_location;
int extra_source_locations[3];
} glshader;
};
};
typedef struct
{
GskTransform *modelview;
GskRoundedRect clip;
graphene_matrix_t projection;
int source_texture;
graphene_rect_t viewport;
float opacity;
/* Per-program state */
union {
GdkRGBA color;
struct {
graphene_matrix_t matrix;
graphene_vec4_t offset;
} color_matrix;
struct {
float widths[4];
GdkRGBA color;
GskRoundedRect outline;
} border;
struct {
GskRoundedRect outline;
float dx;
float dy;
float spread;
GdkRGBA color;
} inset_shadow;
struct {
GskRoundedRect outline;
float dx;
float dy;
float spread;
GdkRGBA color;
} unblurred_outset_shadow;
struct {
int n_color_stops;
GskColorStop color_stops[MAX_GRADIENT_STOPS];
float start_point[2];
float end_point[2];
} linear_gradient;
struct {
int n_color_stops;
GskColorStop color_stops[MAX_GRADIENT_STOPS];
float center[2];
float start;
float end;
float radius[2]; /* h/v */
} radial_gradient;
};
} ProgramState;
ProgramState state;
};
typedef struct {
int ref_count;
@@ -183,13 +194,13 @@ typedef struct {
Program unblurred_outset_shadow_program;
};
};
ProgramState state[GL_N_PROGRAMS];
GHashTable *custom_programs; /* GskGLShader -> Program* */
} GskGLRendererPrograms;
typedef struct
{
GskGLRendererPrograms *programs;
const Program *current_program;
Program *current_program;
int current_render_target;
int current_texture;
@@ -236,7 +247,7 @@ void ops_pop_modelview (RenderOpBuilder *builder);
float ops_get_scale (const RenderOpBuilder *builder);
void ops_set_program (RenderOpBuilder *builder,
const Program *program);
Program *program);
void ops_push_clip (RenderOpBuilder *builder,
const GskRoundedRect *clip);
@@ -255,6 +266,9 @@ graphene_rect_t ops_set_viewport (RenderOpBuilder *builder,
void ops_set_texture (RenderOpBuilder *builder,
int texture_id);
void ops_set_extra_texture (RenderOpBuilder *builder,
int texture_id,
int idx);
int ops_set_render_target (RenderOpBuilder *builder,
int render_target_id);
@@ -281,6 +295,9 @@ void ops_set_inset_shadow (RenderOpBuilder *self,
const GdkRGBA *color,
float dx,
float dy);
void ops_set_glshader_args (RenderOpBuilder *builder,
const graphene_vec2_t *size,
const graphene_vec4_t *args);
void ops_set_unblurred_outset_shadow (RenderOpBuilder *self,
const GskRoundedRect outline,
float spread,

View File

@@ -47,7 +47,9 @@ check_shader_error (int shader_id,
int log_len;
char *buffer;
int code_len;
char *code;
int line;
char *code, *p;
GString *s;
glGetShaderiv (shader_id, GL_COMPILE_STATUS, &status);
@@ -62,11 +64,29 @@ check_shader_error (int shader_id,
code = g_malloc0 (code_len + 1);
glGetShaderSource (shader_id, code_len, NULL, code);
s = g_string_new ("");
p = code;
line = 1;
while (*p)
{
char *end = strchr (p, '\n');
if (end)
end = end + 1; /* Include newline */
else
end = p + strlen (p);
g_string_append_printf (s, "%3d| ", line++);
g_string_append_len (s, p, end - p);
p = end;
}
g_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_COMPILATION_FAILED,
"Compilation failure in shader.\nError message: %s\n\nSource code:\n%s\n\n",
buffer,
code);
s->str);
g_string_free (s, TRUE);
g_free (buffer);
g_free (code);
@@ -76,6 +96,7 @@ check_shader_error (int shader_id,
int
gsk_gl_shader_builder_create_program (GskGLShaderBuilder *self,
const char *resource_path,
const char *extra_fragment_snippet,
GError **error)
{
@@ -136,7 +157,7 @@ gsk_gl_shader_builder_create_program (GskGLShaderBuilder *self,
}
fragment_id = glCreateShader (GL_FRAGMENT_SHADER);
glShaderSource (fragment_id, 8,
glShaderSource (fragment_id, 9,
(const char *[]) {
version_buffer,
self->debugging ? "#define GSK_DEBUG 1\n" : "",
@@ -145,7 +166,8 @@ gsk_gl_shader_builder_create_program (GskGLShaderBuilder *self,
self->gles ? "#define GSK_GLES 1\n" : "",
g_bytes_get_data (self->preamble, NULL),
g_bytes_get_data (self->fs_preamble, NULL),
fragment_shader_start
fragment_shader_start,
extra_fragment_snippet ? extra_fragment_snippet : ""
},
(int[]) {
-1,
@@ -156,6 +178,7 @@ gsk_gl_shader_builder_create_program (GskGLShaderBuilder *self,
-1,
-1,
-1,
-1,
});
glCompileShader (fragment_id);
@@ -188,6 +211,7 @@ gsk_gl_shader_builder_create_program (GskGLShaderBuilder *self,
g_free (buffer);
glDeleteProgram (program_id);
program_id = -1;
goto out;
}

View File

@@ -33,6 +33,7 @@ void gsk_gl_shader_builder_set_glsl_version (GskGLShaderBuilder *self,
int gsk_gl_shader_builder_create_program (GskGLShaderBuilder *self,
const char *resource_path,
const char *extra_fragment_snippet,
GError **error);
G_END_DECLS

View File

@@ -31,6 +31,8 @@ static guint op_sizes[OP_LAST] = {
sizeof (OpDebugGroup),
0,
sizeof (OpBlend),
sizeof (OpGLShader),
sizeof (OpExtraTexture),
};
void

View File

@@ -39,6 +39,8 @@ typedef enum
OP_PUSH_DEBUG_GROUP = 25,
OP_POP_DEBUG_GROUP = 26,
OP_CHANGE_BLEND = 27,
OP_CHANGE_GLSHADER_ARGS = 28,
OP_CHANGE_EXTRA_SOURCE_TEXTURE = 29,
OP_LAST
} OpKind;
@@ -124,6 +126,12 @@ typedef struct
int texture_id;
} OpTexture;
typedef struct
{
int texture_id;
int idx;
} OpExtraTexture;
typedef struct
{
gsize vao_offset;
@@ -198,6 +206,12 @@ typedef struct
float texture_rect[4];
} OpRepeat;
typedef struct
{
float size[2];
float args[4];
} OpGLShader;
void op_buffer_init (OpBuffer *buffer);
void op_buffer_destroy (OpBuffer *buffer);
void op_buffer_clear (OpBuffer *buffer);

View File

@@ -75,7 +75,8 @@ typedef enum {
GSK_CROSS_FADE_NODE,
GSK_TEXT_NODE,
GSK_BLUR_NODE,
GSK_DEBUG_NODE
GSK_DEBUG_NODE,
GSK_GLSHADER_NODE
} GskRenderNodeType;
/**

View File

@@ -53,6 +53,11 @@ struct _GskShadow
float radius;
};
#define GSK_TYPE_GLSHADER (gsk_glshader_get_type ())
GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GskGLShader, gsk_glshader, GSK, GLSHADER, GObject)
/**
* GskParseErrorFunc:
* @section: the #GtkCssSection where the error occurred
@@ -72,6 +77,12 @@ GType gsk_render_node_get_type (void) G_GNUC_CO
GDK_AVAILABLE_IN_ALL
GQuark gsk_serialization_error_quark (void);
GDK_AVAILABLE_IN_ALL
GskGLShader * gsk_glshader_new (const char *source);
GDK_AVAILABLE_IN_ALL
const char * gsk_glshader_peek_source (GskGLShader *shader);
GDK_AVAILABLE_IN_ALL
GskRenderNode * gsk_render_node_ref (GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
@@ -122,6 +133,7 @@ GskRenderNode * gsk_render_node_deserialize (GBytes
#define GSK_TYPE_CROSS_FADE_NODE (gsk_cross_fade_node_get_type())
#define GSK_TYPE_TEXT_NODE (gsk_text_node_get_type())
#define GSK_TYPE_BLUR_NODE (gsk_blur_node_get_type())
#define GSK_TYPE_GLSHADER_NODE (gsk_glshader_node_get_type())
typedef struct _GskDebugNode GskDebugNode;
typedef struct _GskColorNode GskColorNode;
@@ -146,6 +158,7 @@ typedef struct _GskBlendNode GskBlendNode;
typedef struct _GskCrossFadeNode GskCrossFadeNode;
typedef struct _GskTextNode GskTextNode;
typedef struct _GskBlurNode GskBlurNode;
typedef struct _GskGLShaderNode GskGLShaderNode;
GDK_AVAILABLE_IN_ALL
GType gsk_debug_node_get_type (void) G_GNUC_CONST;
@@ -451,6 +464,29 @@ GskRenderNode * gsk_blur_node_get_child (GskRenderNode
GDK_AVAILABLE_IN_ALL
float gsk_blur_node_get_radius (GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
GType gsk_glshader_node_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GskRenderNode * gsk_glshader_node_new (GskGLShader *shader,
const graphene_vec4_t *args,
const graphene_rect_t *bounds,
GskRenderNode *fallback,
GskRenderNode **children,
int n_children);
GDK_AVAILABLE_IN_ALL
GskRenderNode * gsk_glshader_node_get_fallback_child (GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
guint gsk_glshader_node_get_n_children (GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
GskRenderNode * gsk_glshader_node_get_child (GskRenderNode *node,
int idx);
GDK_AVAILABLE_IN_ALL
void gsk_glshader_node_get_args (GskRenderNode *node,
graphene_vec4_t *out_args);
GDK_AVAILABLE_IN_ALL
GskGLShader * gsk_glshader_node_get_shader (GskRenderNode *node);
G_END_DECLS
#endif /* __GSK_RENDER_NODE_H__ */

View File

@@ -4470,6 +4470,323 @@ gsk_debug_node_get_message (GskRenderNode *node)
return self->message;
}
/*** GSK_GLSHADER_NODE ***/
struct _GskGLShader
{
GObject parent_instance;
char *source;
};
G_DEFINE_TYPE (GskGLShader, gsk_glshader, G_TYPE_OBJECT)
enum {
GLSHADER_PROP_0,
GLSHADER_PROP_SOURCE,
GLSHADER_N_PROPS
};
static GParamSpec *gsk_glshader_properties[GLSHADER_N_PROPS];
static void
gsk_glshader_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GskGLShader *shader = GSK_GLSHADER (object);
switch (prop_id)
{
case GLSHADER_PROP_SOURCE:
g_value_set_string (value, shader->source);
break;
default:
g_assert_not_reached ();
}
}
static void
gsk_glshader_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GskGLShader *shader = GSK_GLSHADER (object);
switch (prop_id)
{
case GLSHADER_PROP_SOURCE:
g_free (shader->source);
shader->source = g_value_dup_string (value);
break;
default:
g_assert_not_reached ();
}
}
static void
gsk_glshader_finalize (GObject *object)
{
GskGLShader *shader = GSK_GLSHADER (object);
g_free (shader->source);
G_OBJECT_CLASS (gsk_glshader_parent_class)->finalize (object);
}
static void
gsk_glshader_class_init (GskGLShaderClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->get_property = gsk_glshader_get_property;
object_class->set_property = gsk_glshader_set_property;
object_class->finalize = gsk_glshader_finalize;
/**
* GskGLShader:source:
*
* The source code for the shader.
*/
gsk_glshader_properties[GLSHADER_PROP_SOURCE] =
g_param_spec_string ("source",
"Source",
"The source code for the shader",
NULL,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, GLSHADER_N_PROPS, gsk_glshader_properties);
}
static void
gsk_glshader_init (GskGLShader *shader)
{
}
GskGLShader *
gsk_glshader_new (const char *source)
{
GskGLShader *shader = g_object_new (GSK_TYPE_GLSHADER,
"source", source,
NULL);
return shader;
}
const char *
gsk_glshader_peek_source (GskGLShader *shader)
{
return shader->source;
}
struct _GskGLShaderNode
{
GskRenderNode render_node;
GskGLShader *shader;
graphene_vec4_t args;
GskRenderNode *fallback;
guint n_children;
GskRenderNode **children;
};
static void
gsk_glshader_node_finalize (GskRenderNode *node)
{
GskGLShaderNode *self = (GskGLShaderNode *) node;
GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_GLSHADER_NODE));
for (guint i = 0; i < self->n_children; i++)
gsk_render_node_unref (self->children[i]);
g_free (self->children);
gsk_render_node_unref (self->fallback);
g_object_unref (self->shader);
parent_class->finalize (node);
}
static void
gsk_glshader_node_draw (GskRenderNode *node,
cairo_t *cr)
{
GskGLShaderNode *self = (GskGLShaderNode *) node;
gsk_render_node_draw (self->fallback, cr);
}
static void
gsk_glshader_node_diff (GskRenderNode *node1,
GskRenderNode *node2,
cairo_region_t *region)
{
GskGLShaderNode *self1 = (GskGLShaderNode *) node1;
GskGLShaderNode *self2 = (GskGLShaderNode *) node2;
if (graphene_rect_equal (&node1->bounds, &node2->bounds) &&
self1->shader == self2->shader &&
graphene_vec4_equal (&self1->args, &self2->args) &&
self1->n_children == self2->n_children)
{
gsk_render_node_diff (self1->fallback, self2->fallback, region);
for (guint i = 0; i < self1->n_children; i++)
{
if (self1->children[i] != self2->children[i])
{
gsk_render_node_diff_impossible (node1, node2, region);
break;
}
}
}
else
{
gsk_render_node_diff_impossible (node1, node2, region);
}
}
/**
* gsk_glshader_node_new:
* @shader: the shader glsl code
* @args: a vec4 argument to the shader
* @bounds: the rectangle to render the glshader into
* @fallback: Render node to use if OpenGL is not supported
* @children: List of child nodes, these will be rendered to textures and used as input.
* @n_children: Length of @children (currenly the GL backend only supports max 4 children)
*
* Creates a #GskRenderNode that will render the given @gl_program into the area given by @bounds.
*
* Returns: (transfer full) (type GskGLShaderNode): A new #GskRenderNode
*/
GskRenderNode *
gsk_glshader_node_new (GskGLShader *shader,
const graphene_vec4_t *args,
const graphene_rect_t *bounds,
GskRenderNode *fallback,
GskRenderNode **children,
int n_children)
{
GskGLShaderNode *self;
GskRenderNode *node;
g_return_val_if_fail (bounds != NULL, NULL);
self = gsk_render_node_alloc (GSK_GLSHADER_NODE);
node = (GskRenderNode *) self;
graphene_rect_init_from_rect (&node->bounds, bounds);
self->shader = g_object_ref (shader);
self->args = *args;
self->fallback = gsk_render_node_ref (fallback);
self->n_children = n_children;
if (n_children > 0)
{
self->children = g_malloc_n (n_children, sizeof (GskRenderNode *));
for (guint i = 0; i < n_children; i++)
self->children[i] = gsk_render_node_ref (children[i]);
}
return node;
}
/**
* gsk_glshader_node_get_fallback_child:
* @node: (type GskGLShaderNode): a #GskRenderNode for a gl shader
*
* Gets the fallback child node
*
* Returns: (transfer none): The fallback node
*/
GskRenderNode *
gsk_glshader_node_get_fallback_child (GskRenderNode *node)
{
GskGLShaderNode *self = (GskGLShaderNode *) node;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_GLSHADER_NODE), NULL);
return self->fallback;
}
/**
* gsk_glshader_node_get_n_children:
* @node: (type GskGLShaderNode): a #GskRenderNode for a gl shader
*
* Returns the number of (non-fallback) children
*
* Returns: The number of children
*/
guint
gsk_glshader_node_get_n_children (GskRenderNode *node)
{
GskGLShaderNode *self = (GskGLShaderNode *) node;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_GLSHADER_NODE), 0);
return self->n_children;
}
/**
* gsk_glshader_node_get_child:
* @node: (type GskGLShaderNode): a #GskRenderNode for a gl shader
* @idx: the position of the child to get
*
* Gets one of the (non-fallback) children.
*
* Returns: (transfer none): the @idx'th child of @node
*/
GskRenderNode *
gsk_glshader_node_get_child (GskRenderNode *node,
int idx)
{
GskGLShaderNode *self = (GskGLShaderNode *) node;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_GLSHADER_NODE), NULL);
g_return_val_if_fail (idx < self->n_children, NULL);
return self->children[idx];
}
/**
* gsk_glshader_node_get_shader:
* @node: (type GskGLShaderNode): a #GskRenderNode for a gl shader
*
* Gets shader code for the node.
*
* Returns: (transfer none): the #GskGLShader shader
*/
GskGLShader *
gsk_glshader_node_get_shader (GskRenderNode *node)
{
GskGLShaderNode *self = (GskGLShaderNode *) node;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_GLSHADER_NODE), 0);
return self->shader;
}
/**
* gsk_glshader_node_get_args:
* @node: (type GskGLShaderNode): a #GskRenderNode for a gl shader
* @out_args: Location to write results to.
*
* Gets args for the node.
*/
void
gsk_glshader_node_get_args (GskRenderNode *node,
graphene_vec4_t *out_args)
{
GskGLShaderNode *self = (GskGLShaderNode *) node;
g_return_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_GLSHADER_NODE));
*out_args = self->args;
}
GType gsk_render_node_types[GSK_RENDER_NODE_TYPE_N_TYPES];
#ifndef I_
@@ -4506,6 +4823,7 @@ GSK_DEFINE_RENDER_NODE_TYPE (gsk_blend_node, GSK_BLEND_NODE)
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_glshader_node, GSK_GLSHADER_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_debug_node, GSK_DEBUG_NODE)
static void
@@ -4863,6 +5181,22 @@ gsk_render_node_init_types_once (void)
gsk_render_node_types[GSK_BLUR_NODE] = node_type;
}
{
const GskRenderNodeTypeInfo node_info =
{
GSK_GLSHADER_NODE,
sizeof (GskGLShaderNode),
NULL,
gsk_glshader_node_finalize,
gsk_glshader_node_draw,
NULL,
gsk_glshader_node_diff,
};
GType node_type = gsk_render_node_type_register_static (I_("GskGLShaderNode"), &node_info);
gsk_render_node_types[GSK_GLSHADER_NODE] = node_type;
}
{
const GskRenderNodeTypeInfo node_info =
{

View File

@@ -1089,6 +1089,15 @@ parse_inset_shadow_node (GtkCssParser *parser)
return gsk_inset_shadow_node_new (&outline, &color, dx, dy, spread, blur);
}
static GskRenderNode *
parse_glshader_node (GtkCssParser *parser)
{
/* TODO */
gtk_css_parser_error_syntax (parser, "glshader node parsing not implemented yet");
return NULL;
}
static GskRenderNode *
parse_border_node (GtkCssParser *parser)
{
@@ -1603,6 +1612,7 @@ parse_node (GtkCssParser *parser,
{ "text", parse_text_node },
{ "texture", parse_texture_node },
{ "transform", parse_transform_node },
{ "glshader", parse_glshader_node },
};
GskRenderNode **node_p = out_node;
guint i;
@@ -1837,6 +1847,19 @@ append_point (GString *str,
string_append_double (str, p->y);
}
static void
append_string (GString *str,
const char *value)
{
char *escaped = g_strescape (value, NULL);
g_string_append_c (str, '"');
g_string_append (str, escaped);
g_string_append_c (str, '"');
g_free (escaped);
}
static void
append_vec4 (GString *str,
const graphene_vec4_t *v)
@@ -1914,6 +1937,18 @@ append_point_param (Printer *p,
g_string_append_c (p->str, '\n');
}
static void
append_string_param (Printer *p,
const char *param_name,
const char *value)
{
_indent (p);
g_string_append_printf (p->str, "%s: ", param_name);
append_string (p->str, value);
g_string_append_c (p->str, ';');
g_string_append_c (p->str, '\n');
}
static void
append_vec4_param (Printer *p,
const char *param_name,
@@ -2441,6 +2476,27 @@ render_node_print (Printer *p,
}
break;
case GSK_GLSHADER_NODE:
{
start_node (p, "glshader");
GskGLShader *shader = gsk_glshader_node_get_shader (node);
append_string_param (p, "shader", gsk_glshader_peek_source (shader));
graphene_vec4_t args;
gsk_glshader_node_get_args (node, &args);
append_vec4_param (p, "args", &args);
append_node_param (p, "fallback", gsk_glshader_node_get_fallback_child (node));
for (guint i = 0; i < gsk_glshader_node_get_n_children (node); i ++)
{
GskRenderNode *child = gsk_glshader_node_get_child (node, i);
_indent (p);
render_node_print (p, child);
}
end_node (p);
}
break;
case GSK_REPEAT_NODE:
{
GskRenderNode *child = gsk_repeat_node_get_child (node);

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_DEBUG_NODE + 1)
#define GSK_RENDER_NODE_TYPE_N_TYPES (GSK_GLSHADER_NODE + 1)
extern GType gsk_render_node_types[];

View File

@@ -16,6 +16,7 @@ gsk_private_gl_shaders = [
'resources/glsl/cross_fade.glsl',
'resources/glsl/blend.glsl',
'resources/glsl/repeat.glsl',
'resources/glsl/custom.glsl',
]
gsk_public_sources = files([

View File

@@ -267,8 +267,8 @@ luminosity (vec4 Cs, vec4 Cb)
}
void main() {
vec4 bottom_color = Texture(u_source, vUv);
vec4 top_color = Texture(u_source2, vUv);
vec4 bottom_color = GskTexture(u_source, vUv);
vec4 top_color = GskTexture(u_source2, vUv);
vec4 result;
if (u_mode == 0)
@@ -306,5 +306,5 @@ void main() {
else
discard;
setOutputColor(result * u_alpha);
gskSetOutputColor(result * u_alpha);
}

View File

@@ -7,7 +7,7 @@ void main() {
// FRAGMENT_SHADER:
void main() {
vec4 diffuse = Texture(u_source, vUv);
vec4 diffuse = GskTexture(u_source, vUv);
setOutputColor(diffuse * u_alpha);
gskSetOutputColor(diffuse * u_alpha);
}

View File

@@ -39,14 +39,14 @@ void main() {
vec3 incrementalGaussian = initial_gaussian;
float coefficientSum = 0.0;
vec4 sum = Texture(u_source, vUv) * incrementalGaussian.x;
vec4 sum = GskTexture(u_source, vUv) * incrementalGaussian.x;
coefficientSum += incrementalGaussian.x;
incrementalGaussian.xy *= incrementalGaussian.yz;
vec2 p = pixel_step;
for (int i = 1; i <= int(pixels_per_side); i++) {
sum += Texture(u_source, vUv - p) * incrementalGaussian.x;
sum += Texture(u_source, vUv + p) * incrementalGaussian.x;
sum += GskTexture(u_source, vUv - p) * incrementalGaussian.x;
sum += GskTexture(u_source, vUv + p) * incrementalGaussian.x;
coefficientSum += 2.0 * incrementalGaussian.x;
incrementalGaussian.xy *= incrementalGaussian.yz;
@@ -54,5 +54,5 @@ void main() {
p += pixel_step;
}
setOutputColor(sum / coefficientSum);
gskSetOutputColor(sum / coefficientSum);
}

View File

@@ -4,37 +4,37 @@ uniform vec4 u_widths;
uniform vec4[3] u_outline_rect;
_OUT_ vec4 final_color;
_OUT_ _ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
_OUT_ _ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
void main() {
gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
final_color = premultiply(u_color) * u_alpha;
final_color = gsk_premultiply(u_color) * u_alpha;
RoundedRect outside = create_rect(u_outline_rect);
RoundedRect inside = rounded_rect_shrink (outside, u_widths);
GskRoundedRect outside = gsk_create_rect(u_outline_rect);
GskRoundedRect inside = gsk_rounded_rect_shrink (outside, u_widths);
rounded_rect_transform(outside, u_modelview);
rounded_rect_transform(inside, u_modelview);
gsk_rounded_rect_transform(outside, u_modelview);
gsk_rounded_rect_transform(inside, u_modelview);
rounded_rect_encode(outside, transformed_outside_outline);
rounded_rect_encode(inside, transformed_inside_outline);
gsk_rounded_rect_encode(outside, transformed_outside_outline);
gsk_rounded_rect_encode(inside, transformed_inside_outline);
}
// FRAGMENT_SHADER:
uniform vec4[3] u_outline_rect;
_IN_ vec4 final_color;
_IN_ _ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
_IN_ _ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
void main() {
vec2 frag = get_frag_coord();
vec2 frag = gsk_get_frag_coord();
float alpha = clamp(rounded_rect_coverage(decode_rect(transformed_outside_outline), frag) -
rounded_rect_coverage(decode_rect(transformed_inside_outline), frag),
float alpha = clamp(gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outside_outline), frag) -
gsk_rounded_rect_coverage(gsk_decode_rect(transformed_inside_outline), frag),
0.0, 1.0);
setOutputColor(final_color * alpha);
gskSetOutputColor(final_color * alpha);
}

View File

@@ -6,13 +6,13 @@ _OUT_ vec4 final_color;
void main() {
gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
final_color = premultiply(u_color) * u_alpha;
final_color = gsk_premultiply(u_color) * u_alpha;
}
// FRAGMENT_SHADER:
_IN_ vec4 final_color;
void main() {
setOutputColor(final_color);
gskSetOutputColor(final_color);
}

View File

@@ -10,7 +10,7 @@ uniform mat4 u_color_matrix;
uniform vec4 u_color_offset;
void main() {
vec4 color = Texture(u_source, vUv);
vec4 color = GskTexture(u_source, vUv);
// Un-premultilpy
if (color.a != 0.0)
@@ -21,5 +21,5 @@ void main() {
color.rgb *= color.a;
setOutputColor(color * u_alpha);
gskSetOutputColor(color * u_alpha);
}

View File

@@ -8,7 +8,7 @@ void main() {
vUv = vec2(aUv.x, aUv.y);
final_color = premultiply(u_color) * u_alpha;
final_color = gsk_premultiply(u_color) * u_alpha;
}
// FRAGMENT_SHADER:
@@ -16,7 +16,7 @@ void main() {
_IN_ vec4 final_color;
void main() {
vec4 diffuse = Texture(u_source, vUv);
vec4 diffuse = GskTexture(u_source, vUv);
setOutputColor(final_color * diffuse.a);
gskSetOutputColor(final_color * diffuse.a);
}

View File

@@ -10,11 +10,11 @@ uniform float u_progress;
uniform sampler2D u_source2;
void main() {
vec4 source1 = Texture(u_source, vUv); // start child
vec4 source2 = Texture(u_source2, vUv); // end child
vec4 source1 = GskTexture(u_source, vUv); // start child
vec4 source2 = GskTexture(u_source2, vUv); // end child
float p_start = (1.0 - u_progress) * u_alpha;
float p_end = u_progress * u_alpha;
vec4 color = (p_start * source1) + (p_end * source2);
setOutputColor(color);
gskSetOutputColor(color);
}

View File

@@ -0,0 +1,20 @@
// VERTEX_SHADER:
void main() {
gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
vUv = vec2(aUv.x, aUv.y);
}
// FRAGMENT_SHADER:
// The shader supplies:
void mainImage(out vec4 fragColor, in vec2 fragCoord, in vec2 resolution, in vec2 uv);
uniform vec2 u_size;
uniform vec4 u_args;
uniform sampler2D u_source2;
void main() {
vec4 fragColor;
vec2 fragCoord = vec2(vUv.x * u_size.x, (1.0-vUv.y) * u_size.y);
mainImage(fragColor, fragCoord, u_size, vUv);
gskSetOutputColor(fragColor);
}

View File

@@ -5,8 +5,8 @@ uniform vec2 u_offset;
uniform vec4[3] u_outline_rect;
_OUT_ vec4 final_color;
_OUT_ _ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
_OUT_ _ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
void main() {
gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
@@ -15,29 +15,29 @@ void main() {
final_color.rgb *= final_color.a;
final_color *= u_alpha;
RoundedRect outside = create_rect(u_outline_rect);
RoundedRect inside = rounded_rect_shrink(outside, vec4(u_spread));
GskRoundedRect outside = gsk_create_rect(u_outline_rect);
GskRoundedRect inside = gsk_rounded_rect_shrink(outside, vec4(u_spread));
rounded_rect_offset(inside, u_offset);
gsk_rounded_rect_offset(inside, u_offset);
rounded_rect_transform(outside, u_modelview);
rounded_rect_transform(inside, u_modelview);
gsk_rounded_rect_transform(outside, u_modelview);
gsk_rounded_rect_transform(inside, u_modelview);
rounded_rect_encode(outside, transformed_outside_outline);
rounded_rect_encode(inside, transformed_inside_outline);
gsk_rounded_rect_encode(outside, transformed_outside_outline);
gsk_rounded_rect_encode(inside, transformed_inside_outline);
}
// FRAGMENT_SHADER:
_IN_ vec4 final_color;
_IN_ _ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
_IN_ _ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
void main() {
vec2 frag = get_frag_coord();
vec2 frag = gsk_get_frag_coord();
float alpha = clamp(rounded_rect_coverage(decode_rect(transformed_outside_outline), frag) -
rounded_rect_coverage(decode_rect(transformed_inside_outline), frag),
float alpha = clamp(gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outside_outline), frag) -
gsk_rounded_rect_coverage(gsk_decode_rect(transformed_inside_outline), frag),
0.0, 1.0);
setOutputColor(final_color * alpha);
gskSetOutputColor(final_color * alpha);
}

View File

@@ -25,10 +25,10 @@ void main() {
for (int i = 0; i < u_num_color_stops; i ++) {
color_offsets[i] = u_color_stops[(i * 5) + 0];
color_stops[i] = premultiply(vec4(u_color_stops[(i * 5) + 1],
u_color_stops[(i * 5) + 2],
u_color_stops[(i * 5) + 3],
u_color_stops[(i * 5) + 4]));
color_stops[i] = gsk_premultiply(vec4(u_color_stops[(i * 5) + 1],
u_color_stops[(i * 5) + 2],
u_color_stops[(i * 5) + 3],
u_color_stops[(i * 5) + 4]));
}
}
@@ -49,7 +49,7 @@ _IN_ float color_offsets[8];
void main() {
// Position relative to startPoint
vec2 pos = get_frag_coord() - startPoint;
vec2 pos = gsk_get_frag_coord() - startPoint;
// Current pixel, projected onto the line between the start point and the end point
// The projection will be relative to the start point!
@@ -66,5 +66,5 @@ void main() {
}
}
setOutputColor(color * u_alpha);
gskSetOutputColor(color * u_alpha);
}

View File

@@ -3,31 +3,31 @@ uniform vec4 u_color;
uniform vec4[3] u_outline_rect;
_OUT_ vec4 final_color;
_OUT_ _ROUNDED_RECT_UNIFORM_ transformed_outline;
_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outline;
void main() {
gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
vUv = vec2(aUv.x, aUv.y);
final_color = premultiply(u_color) * u_alpha;
final_color = gsk_premultiply(u_color) * u_alpha;
RoundedRect outline = create_rect(u_outline_rect);
rounded_rect_transform(outline, u_modelview);
rounded_rect_encode(outline, transformed_outline);
GskRoundedRect outline = gsk_create_rect(u_outline_rect);
gsk_rounded_rect_transform(outline, u_modelview);
gsk_rounded_rect_encode(outline, transformed_outline);
}
// FRAGMENT_SHADER:
_IN_ vec4 final_color;
_IN_ _ROUNDED_RECT_UNIFORM_ transformed_outline;
_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outline;
void main() {
vec2 frag = get_frag_coord();
vec2 frag = gsk_get_frag_coord();
float alpha = Texture(u_source, vUv).a;
alpha *= (1.0 - clamp(rounded_rect_coverage(decode_rect(transformed_outline), frag), 0.0, 1.0));
float alpha = GskTexture(u_source, vUv).a;
alpha *= (1.0 - clamp(gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outline), frag), 0.0, 1.0));
vec4 color = final_color * alpha;
setOutputColor(color);
gskSetOutputColor(color);
}

View File

@@ -16,17 +16,17 @@ _IN_ vec2 vUv;
RoundedRect decode_rect(_ROUNDED_RECT_UNIFORM_ r)
GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r)
{
#if defined(GSK_GLES) || defined(GSK_LEGACY)
return RoundedRect(r[0], r[1], r[2]);
return GskRoundedRect(r[0], r[1], r[2]);
#else
return r;
#endif
}
float
ellipsis_dist (vec2 p, vec2 radius)
gsk_ellipsis_dist (vec2 p, vec2 radius)
{
if (radius == vec2(0, 0))
return 0.0;
@@ -38,14 +38,14 @@ ellipsis_dist (vec2 p, vec2 radius)
}
float
ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius)
{
float d = ellipsis_dist (point - center, radius);
float d = gsk_ellipsis_dist (point - center, radius);
return clamp (0.5 - d, 0.0, 1.0);
}
float
rounded_rect_coverage (RoundedRect r, vec2 p)
gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p)
{
if (p.x < r.bounds.x || p.y < r.bounds.y ||
p.x >= r.bounds.z || p.y >= r.bounds.w)
@@ -61,10 +61,10 @@ rounded_rect_coverage (RoundedRect r, vec2 p)
vec2 ref_br = r.corner_points2.xy;
vec2 ref_bl = r.corner_points2.zw;
float d_tl = ellipsis_coverage(p, ref_tl, rad_tl);
float d_tr = ellipsis_coverage(p, ref_tr, rad_tr);
float d_br = ellipsis_coverage(p, ref_br, rad_br);
float d_bl = ellipsis_coverage(p, ref_bl, rad_bl);
float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl);
float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr);
float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br);
float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl);
vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl);
@@ -76,7 +76,7 @@ rounded_rect_coverage (RoundedRect r, vec2 p)
return 1.0 - dot(vec4(is_out), corner_coverages);
}
vec4 Texture(sampler2D sampler, vec2 texCoords) {
vec4 GskTexture(sampler2D sampler, vec2 texCoords) {
#if defined(GSK_GLES) || defined(GSK_LEGACY)
return texture2D(sampler, texCoords);
#else
@@ -88,7 +88,7 @@ vec4 Texture(sampler2D sampler, vec2 texCoords) {
layout(origin_upper_left) in vec4 gl_FragCoord;
#endif
vec2 get_frag_coord() {
vec2 gsk_get_frag_coord() {
vec2 fc = gl_FragCoord.xy;
#ifdef GSK_GL3
@@ -101,15 +101,15 @@ vec2 get_frag_coord() {
return fc;
}
void setOutputColor(vec4 color) {
vec2 f = get_frag_coord();
void gskSetOutputColor(vec4 color) {
vec2 f = gsk_get_frag_coord();
// We do *NOT* transform the clip rect here since we already
// need to do that on the CPU.
#if defined(GSK_GLES) || defined(GSK_LEGACY)
gl_FragColor = color * rounded_rect_coverage(create_rect(u_clip_rect), f);
gl_FragColor = color * gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect), f);
#else
outputColor = color * rounded_rect_coverage(create_rect(u_clip_rect), f);
outputColor = color * gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect), f);
#endif
/*outputColor = color;*/
}

View File

@@ -5,15 +5,15 @@ precision highp float;
#if defined(GSK_GLES) || defined(GSK_LEGACY)
#define _OUT_ varying
#define _IN_ varying
#define _ROUNDED_RECT_UNIFORM_ vec4[3]
#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
#else
#define _OUT_ out
#define _IN_ in
#define _ROUNDED_RECT_UNIFORM_ RoundedRect
#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
#endif
struct RoundedRect
struct GskRoundedRect
{
vec4 bounds;
// Look, arrays can't be in structs if you want to return the struct
@@ -22,9 +22,9 @@ struct RoundedRect
vec4 corner_points2; // xy = bottom right, zw = bottom left
};
// Transform from a GskRoundedRect to a RoundedRect as we need it.
RoundedRect
create_rect(vec4[3] data)
// Transform from a C GskRoundedRect to what we need.
GskRoundedRect
gsk_create_rect(vec4[3] data)
{
vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw);
@@ -33,9 +33,9 @@ create_rect(vec4[3] data)
vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)),
bounds.xw + vec2(data[2].zw * vec2(1, -1)));
return RoundedRect(bounds, corner_points1, corner_points2);
return GskRoundedRect(bounds, corner_points1, corner_points2);
}
vec4 premultiply(vec4 c) {
vec4 gsk_premultiply(vec4 c) {
return vec4(c.rgb * c.a, c.a);
}

View File

@@ -13,8 +13,8 @@ _OUT_ vec2 vUv;
#endif
// amount is: top, right, bottom, left
RoundedRect
rounded_rect_shrink (RoundedRect r, vec4 amount)
GskRoundedRect
gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount)
{
vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz;
vec4 new_corner_points1 = r.corner_points1;
@@ -25,11 +25,11 @@ rounded_rect_shrink (RoundedRect r, vec4 amount)
if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw;
if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw;
return RoundedRect (new_bounds, new_corner_points1, new_corner_points2);
return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2);
}
void
rounded_rect_offset(inout RoundedRect r, vec2 offset)
gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset)
{
r.bounds.xy += offset;
r.bounds.zw += offset;
@@ -39,7 +39,7 @@ rounded_rect_offset(inout RoundedRect r, vec2 offset)
r.corner_points2.zw += offset;
}
void rounded_rect_transform(inout RoundedRect r, mat4 mat)
void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat)
{
r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy;
r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy;
@@ -53,9 +53,9 @@ void rounded_rect_transform(inout RoundedRect r, mat4 mat)
#if defined(GSK_LEGACY)
// Can't have out or inout array parameters...
#define rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2;
#else
void rounded_rect_encode(RoundedRect r, out _ROUNDED_RECT_UNIFORM_ out_r)
void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r)
{
#if defined(GSK_GLES)
out_r[0] = r.bounds;

View File

@@ -21,10 +21,10 @@ void main() {
for (int i = 0; i < u_num_color_stops; i ++) {
color_offsets[i] = u_color_stops[(i * 5) + 0];
color_stops[i] = premultiply(vec4(u_color_stops[(i * 5) + 1],
u_color_stops[(i * 5) + 2],
u_color_stops[(i * 5) + 3],
u_color_stops[(i * 5) + 4]));
color_stops[i] = gsk_premultiply(vec4(u_color_stops[(i * 5) + 1],
u_color_stops[(i * 5) + 2],
u_color_stops[(i * 5) + 3],
u_color_stops[(i * 5) + 4]));
}
}
@@ -51,17 +51,17 @@ float abs_offset(float offset) {
}
void main() {
vec2 pixel = get_frag_coord();
vec2 pixel = gsk_get_frag_coord();
vec2 rel = (center - pixel) / (u_radius);
float d = sqrt(dot(rel, rel));
if (d < abs_offset (color_offsets[0])) {
setOutputColor(color_stops[0] * u_alpha);
gskSetOutputColor(color_stops[0] * u_alpha);
return;
}
if (d > end) {
setOutputColor(color_stops[u_num_color_stops - 1] * u_alpha);
gskSetOutputColor(color_stops[u_num_color_stops - 1] * u_alpha);
return;
}
@@ -80,5 +80,5 @@ void main() {
}
}
setOutputColor(color * u_alpha);
gskSetOutputColor(color * u_alpha);
}

View File

@@ -35,7 +35,7 @@ void main() {
tp.x = u_texture_rect.x + (wrapped_x * tw);
tp.y = u_texture_rect.y + (wrapped_y * th);
vec4 diffuse = Texture(u_source, tp);
vec4 diffuse = GskTexture(u_source, tp);
setOutputColor(diffuse * u_alpha);
gskSetOutputColor(diffuse * u_alpha);
}

View File

@@ -5,8 +5,8 @@ uniform vec2 u_offset;
uniform vec4[3] u_outline_rect;
_OUT_ vec4 final_color;
_OUT_ _ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
_OUT_ _ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
void main() {
gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
@@ -15,30 +15,30 @@ void main() {
final_color.rgb *= final_color.a;
final_color *= u_alpha;
RoundedRect inside = create_rect(u_outline_rect);
RoundedRect outside = rounded_rect_shrink(inside, vec4(- u_spread));
GskRoundedRect inside = gsk_create_rect(u_outline_rect);
GskRoundedRect outside = gsk_rounded_rect_shrink(inside, vec4(- u_spread));
rounded_rect_offset(outside, u_offset);
gsk_rounded_rect_offset(outside, u_offset);
rounded_rect_transform(outside, u_modelview);
rounded_rect_transform(inside, u_modelview);
gsk_rounded_rect_transform(outside, u_modelview);
gsk_rounded_rect_transform(inside, u_modelview);
rounded_rect_encode(outside, transformed_outside_outline);
rounded_rect_encode(inside, transformed_inside_outline);
gsk_rounded_rect_encode(outside, transformed_outside_outline);
gsk_rounded_rect_encode(inside, transformed_inside_outline);
}
// FRAGMENT_SHADER:
_IN_ vec4 final_color;
_IN_ _ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
_IN_ _ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline;
_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline;
void main() {
vec2 frag = get_frag_coord();
vec2 frag = gsk_get_frag_coord();
float alpha = clamp(rounded_rect_coverage(decode_rect(transformed_outside_outline), frag) -
rounded_rect_coverage(decode_rect(transformed_inside_outline), frag),
float alpha = clamp(gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outside_outline), frag) -
gsk_rounded_rect_coverage(gsk_decode_rect(transformed_inside_outline), frag),
0.0, 1.0);
setOutputColor(final_color * alpha);
gskSetOutputColor(final_color * alpha);
}

View File

@@ -539,6 +539,10 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self,
}
return;
case GSK_GLSHADER_NODE:
gsk_vulkan_render_pass_add_node (self, render, constants, gsk_glshader_node_get_fallback_child (node));
return;
case GSK_DEBUG_NODE:
gsk_vulkan_render_pass_add_node (self, render, constants, gsk_debug_node_get_child (node));
return;

View File

@@ -91,6 +91,14 @@ struct _GtkSnapshotState {
struct {
graphene_rect_t bounds;
} clip;
struct {
GskGLShader *shader;
graphene_vec4_t args;
graphene_rect_t bounds;
int n_children;
int node_idx;
GskRenderNode **nodes;
} glshader;
struct {
GskRoundedRect bounds;
} rounded_clip;
@@ -812,6 +820,122 @@ gtk_snapshot_push_clip (GtkSnapshot *snapshot,
gtk_graphene_rect_scale_affine (bounds, scale_x, scale_y, dx, dy, &state->data.clip.bounds);
}
static GskRenderNode *
maybe_clip (GskRenderNode *node,
const graphene_rect_t *bounds)
{
if (node &&
!graphene_rect_contains_rect (bounds, &node->bounds))
{
return gsk_clip_node_new (node, bounds);
}
return gsk_render_node_ref (node);
}
static GskRenderNode *
gtk_snapshot_collect_glshader (GtkSnapshot *snapshot,
GtkSnapshotState *state,
GskRenderNode **nodes,
guint n_nodes)
{
GskRenderNode *shader_node = NULL, *child_node;
GdkRGBA transparent = { 0, 0, 0, 0 };
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.bounds);
state->data.glshader.nodes[state->data.glshader.node_idx] = child_node;
if (state->data.glshader.node_idx != state->data.glshader.n_children)
return NULL; /* Not last */
/* This is the last pop */
shader_node = NULL;
if (state->data.glshader.bounds.size.width != 0 &&
state->data.glshader.bounds.size.height != 0)
{
GskRenderNode *fallback_node = maybe_clip (state->data.glshader.nodes[0],
&state->data.glshader.bounds);
shader_node = gsk_glshader_node_new (state->data.glshader.shader,
&state->data.glshader.args,
&state->data.glshader.bounds,
fallback_node,
&state->data.glshader.nodes[1],
state->data.glshader.n_children);
gsk_render_node_unref (fallback_node);
}
g_object_unref (state->data.glshader.shader);
for (guint i = 0; i < state->data.glshader.n_children + 1; i++)
gsk_render_node_unref (state->data.glshader.nodes[i]);
g_free (state->data.glshader.nodes);
return shader_node;
}
/**
* gtk_snapshot_push_glshader:
* @snapshot: a #GtkSnapshot
* @shader: The code to run
* @args: A vec4 argument given to the shader
* @bounds: the rectangle to render into
* @n_children: The number of extra nodes given as argument to the shader as textures.
*
* Renders a rectagle with output from a GLSL shader. If shaders are not supported
* the fallback will be used instead. The fallback is pushed to the snapshot
* until the first call to gtk_snapshot_pop(). After that the extra nodes
* pushed, and @n_children mode gtk_snapshot_pop() calls are expected for
* these.
*/
void
gtk_snapshot_push_glshader (GtkSnapshot *snapshot,
GskGLShader *shader,
const graphene_vec4_t *args,
const graphene_rect_t *bounds,
int n_children)
{
GtkSnapshotState *state;
float scale_x, scale_y, dx, dy;
GskRenderNode **nodes;
int node_idx;
graphene_rect_t transformed_bounds;
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_glshader);
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);
if (args)
state->data.glshader.args = *args;
else
graphene_vec4_init (&state->data.glshader.args, 0, 0, 0, 0);
state->data.glshader.n_children = n_children;
nodes = g_new0 (GskRenderNode *, n_children + 1);
node_idx = n_children; /* We pop in reverse order */
state->data.glshader.node_idx = node_idx--;
state->data.glshader.nodes = nodes;
for (int i = 0; i < n_children; i++)
{
state = gtk_snapshot_push_state (snapshot,
gtk_snapshot_get_current_state (snapshot)->transform,
gtk_snapshot_collect_glshader);
state->data.glshader.node_idx = node_idx--;
state->data.glshader.n_children = n_children;
state->data.glshader.nodes = nodes;
state->data.glshader.bounds = transformed_bounds;
}
}
static GskRenderNode *
gtk_snapshot_collect_rounded_clip (GtkSnapshot *snapshot,
GtkSnapshotState *state,

View File

@@ -99,6 +99,12 @@ GDK_AVAILABLE_IN_ALL
void gtk_snapshot_push_cross_fade (GtkSnapshot *snapshot,
double progress);
GDK_AVAILABLE_IN_ALL
void gtk_snapshot_push_glshader (GtkSnapshot *snapshot,
GskGLShader *shader,
const graphene_vec4_t *args,
const graphene_rect_t *bounds,
int n_children);
GDK_AVAILABLE_IN_ALL
void gtk_snapshot_pop (GtkSnapshot *snapshot);
GDK_AVAILABLE_IN_ALL

View File

@@ -171,6 +171,25 @@ create_list_model_for_render_node (GskRenderNode *node)
return create_render_node_list_model ((GskRenderNode *[2]) { gsk_cross_fade_node_get_start_child (node),
gsk_cross_fade_node_get_end_child (node) }, 2);
case GSK_GLSHADER_NODE:
{
GListStore *store = G_LIST_STORE (create_render_node_list_model ((GskRenderNode *[1]) { gsk_glshader_node_get_fallback_child (node) }, 1));
for (guint i = 0; i < gsk_glshader_node_get_n_children (node); i++)
{
GskRenderNode *child = gsk_glshader_node_get_child (node, i);
graphene_rect_t bounds;
GdkPaintable *paintable;
gsk_render_node_get_bounds (child, &bounds);
paintable = gtk_render_node_paintable_new (child, &bounds);
g_list_store_append (store, paintable);
g_object_unref (paintable);
}
return G_LIST_MODEL (store);
}
case GSK_CONTAINER_NODE:
{
GListStore *store;
@@ -270,6 +289,8 @@ node_type_name (GskRenderNodeType type)
return "Text";
case GSK_BLUR_NODE:
return "Blur";
case GSK_GLSHADER_NODE:
return "GLShader";
}
}
@@ -301,6 +322,7 @@ node_name (GskRenderNode *node)
case GSK_CROSS_FADE_NODE:
case GSK_TEXT_NODE:
case GSK_BLUR_NODE:
case GSK_GLSHADER_NODE:
return g_strdup (node_type_name (gsk_render_node_get_node_type (node)));
case GSK_DEBUG_NODE:
@@ -521,6 +543,20 @@ add_float_row (GtkListStore *store,
g_free (text);
}
static void
add_vec4_row (GtkListStore *store,
const char *name,
graphene_vec4_t *value)
{
char *text = g_strdup_printf ("[%.2f, %.2f, %.2f, %.2f]",
graphene_vec4_get_x (value),
graphene_vec4_get_y (value),
graphene_vec4_get_z (value),
graphene_vec4_get_w (value));
add_text_row (store, name, text);
g_free (text);
}
static void
populate_render_node_properties (GtkListStore *store,
GskRenderNode *node)
@@ -759,6 +795,14 @@ populate_render_node_properties (GtkListStore *store,
add_float_row (store, "Radius", gsk_blur_node_get_radius (node));
break;
case GSK_GLSHADER_NODE:
{
graphene_vec4_t args;
gsk_glshader_node_get_args (node, &args);
add_vec4_row (store, "Args", &args);
}
break;
case GSK_INSET_SHADOW_NODE:
{
const GdkRGBA *color = gsk_inset_shadow_node_peek_color (node);