Compare commits

...

12 Commits

Author SHA1 Message Date
Matthias Clasen
50725b67ac Add another shader node demo
This one does stack-like transitions, using
some examples from gl-transitions.com.
2020-09-23 00:54:28 -04:00
Matthias Clasen
aeae3522b6 Complete serialization for GskGLShaderNode
Make the node serialization and parsing handle
shader nodes.
2020-09-23 00:54:28 -04:00
Matthias Clasen
3e3df6ba1e inspector: Show data of glshader nodes
Show the required sources and the values of the uniforms
in the recorder.
2020-09-23 00:54:28 -04:00
Matthias Clasen
f2a046449f Add a missing file
This was reconstructed from build failures.
2020-09-22 15:46:49 -04:00
Alexander Larsson
122304d34a gtk-demo: Add GskGLShaderNode demo 2020-09-22 16:06:54 +02:00
Alexander Larsson
6c13f89f91 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-22 16:06:54 +02:00
Alexander Larsson
5f7bf0a23e GtkSnapshot: Add gtk_snapshot_push_glshader()
This is a helper to create GskGLShader nodes
2020-09-22 16:06:54 +02:00
Alexander Larsson
adbd434d8c 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 GskGLShader object with the source and definitions of the uniforms
 * A the data for the unifors, formated according to the GskGLShader
 * a list of render nodes that are rendered to textures

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-22 16:06:54 +02:00
Alexander Larsson
0693deb02f 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-22 12:50:10 +02:00
Alexander Larsson
ffd39c257a 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-22 12:50:10 +02:00
Alexander Larsson
ec0ad5738c 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-22 09:40:16 +02:00
Alexander Larsson
c958fd8e86 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-22 09:40:16 +02:00
52 changed files with 3446 additions and 224 deletions

View File

@@ -133,6 +133,19 @@
<file>cogs.glsl</file>
<file>glowingstars.glsl</file>
</gresource>
<gresource prefix="/glshader">
<file>fire.glsl</file>
<file>gtkshaderbin.h</file>
<file>gtkshaderbin.c</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 +260,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 @@
uniform float u_time;
/* 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)
{
vec2 xy = fragCoord / resolution;
float zoom = 3.0 - sin(u_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) * u_time);
float q2 = fbm(p - vec2(0.5, 1.3) * u_time);
float r = fbm(2.0*p + vec2(q1,q2) - vec2(0.0, 1.0)*u_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, u_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);
}

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

@@ -0,0 +1,85 @@
/* OpenGL/GLShader
* #Keywords: OpenGL, shader
*
* Demonstrates using GskGLShaderNodes to integrate GLSL fragment shaders
* with the Gtk widget rendering.
*/
#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));
gsk_glshader_add_uniform (shader, "u_time", GSK_GLUNIFORM_TYPE_FLOAT);
gtk_shader_bin_add_shader (GTK_SHADER_BIN (bin), shader, GTK_STATE_FLAG_PRELIGHT, GTK_STATE_FLAG_PRELIGHT);
g_bytes_unref (shader_b);
g_object_unref (shader);
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,250 @@
/* OpenGL/Transitions
* #Keywords: OpenGL, shader
*
* Create transitions between pages using a custom fragment shader.
* The examples here are taken from gl-transitions.com.
*
* Click to start a transition.
*/
#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));
gsk_glshader_set_n_required_sources (shader, 2);
gsk_glshader_add_uniform (shader, "progress", GSK_GLUNIFORM_TYPE_FLOAT);
g_bytes_unref (shader_b);
return shader;
}
static void
text_changed (GtkTextBuffer *buffer,
GtkWidget *button)
{
gtk_widget_show (button);
}
static void
apply_text (GtkWidget *button,
GtkTextBuffer *buffer)
{
GtkWidget *stack;
GskGLShader *shader;
GtkTextIter start, end;
char *text;
stack = gtk_widget_get_ancestor (button, GTK_TYPE_SHADER_STACK);
gtk_text_buffer_get_bounds (buffer, &start, &end);
text = gtk_text_buffer_get_text (buffer, &start, &end, TRUE);
shader = gsk_glshader_new (text);
gsk_glshader_set_n_required_sources (shader, 2);
gsk_glshader_add_uniform (shader, "progress", GSK_GLUNIFORM_TYPE_FLOAT);
gtk_shader_stack_set_shader (GTK_SHADER_STACK (stack), shader);
g_object_unref (shader);
g_free (text);
gtk_widget_hide (button);
}
static void
clicked_cb (GtkGestureClick *gesture,
guint n_pressed,
double x,
double y,
gpointer data)
{
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
}
static GtkWidget *
make_shader_stack (const char *name,
const char *resource_path,
GtkWidget *scale)
{
GtkWidget *stack, *child, *widget;
GtkWidget *label, *button, *tv;
GskGLShader *shader;
GObjectClass *class;
GParamSpecFloat *pspec;
GtkAdjustment *adjustment;
GtkTextBuffer *buffer;
GBytes *bytes;
GtkEventController *controller;
GtkCssProvider *provider;
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);
child = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
widget = gtk_center_box_new ();
label = gtk_label_new (name);
gtk_widget_add_css_class (label, "title-4");
gtk_widget_set_size_request (label, -1, 26);
gtk_center_box_set_center_widget (GTK_CENTER_BOX (widget), label);
button = gtk_button_new_from_icon_name ("view-refresh-symbolic");
provider = gtk_css_provider_new ();
gtk_css_provider_load_from_data (provider, "button.small { padding: 0; }", -1);
gtk_style_context_add_provider (gtk_widget_get_style_context (button),
GTK_STYLE_PROVIDER (provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
g_object_unref (provider);
gtk_widget_set_halign (button, GTK_ALIGN_CENTER);
gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
gtk_widget_add_css_class (button, "small");
gtk_widget_hide (button);
gtk_center_box_set_end_widget (GTK_CENTER_BOX (widget), button);
gtk_box_append (GTK_BOX (child), widget);
class = g_type_class_ref (GTK_TYPE_SHADER_STACK);
pspec = G_PARAM_SPEC_FLOAT (g_object_class_find_property (class, "duration"));
adjustment = gtk_range_get_adjustment (GTK_RANGE (scale));
if (gtk_adjustment_get_lower (adjustment) == 0.0 &&
gtk_adjustment_get_upper (adjustment) == 0.0)
{
gtk_adjustment_configure (adjustment,
pspec->default_value,
pspec->minimum,
pspec->maximum,
0.1, 0.5, 0);
}
g_type_class_unref (class);
g_object_bind_property (adjustment, "value",
stack, "duration",
G_BINDING_DEFAULT);
widget = gtk_scrolled_window_new ();
gtk_scrolled_window_set_has_frame (GTK_SCROLLED_WINDOW (widget), TRUE);
gtk_widget_set_hexpand (widget, TRUE);
gtk_widget_set_vexpand (widget, TRUE);
controller = GTK_EVENT_CONTROLLER (gtk_gesture_click_new ());
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (controller), 0);
g_signal_connect (controller, "released", G_CALLBACK (clicked_cb), NULL);
gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_BUBBLE);
gtk_widget_add_controller (GTK_WIDGET (widget), controller);
tv = gtk_text_view_new ();
gtk_text_view_set_left_margin (GTK_TEXT_VIEW (tv), 4);
gtk_text_view_set_right_margin (GTK_TEXT_VIEW (tv), 4);
gtk_text_view_set_top_margin (GTK_TEXT_VIEW (tv), 4);
gtk_text_view_set_bottom_margin (GTK_TEXT_VIEW (tv), 4);
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
bytes = g_resources_lookup_data (resource_path, 0, NULL);
gtk_text_buffer_set_text (buffer,
(const char *)g_bytes_get_data (bytes, NULL),
g_bytes_get_size (bytes));
g_bytes_unref (bytes);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (widget), tv);
g_signal_connect (buffer, "changed", G_CALLBACK (text_changed), button);
g_signal_connect (button, "clicked", G_CALLBACK (apply_text), buffer);
gtk_box_append (GTK_BOX (child), widget);
gtk_shader_stack_add_child (GTK_SHADER_STACK (stack), child);
return stack;
}
static GtkWidget *
create_gltransition_window (GtkWidget *do_widget)
{
GtkWidget *window, *headerbar, *scale, *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");
headerbar = gtk_header_bar_new ();
scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, NULL);
gtk_scale_set_draw_value (GTK_SCALE (scale), FALSE);
gtk_widget_set_size_request (scale, 100, -1);
gtk_header_bar_pack_end (GTK_HEADER_BAR (headerbar), scale);
gtk_window_set_titlebar (GTK_WINDOW (window), headerbar);
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_window_set_child (GTK_WINDOW (window), grid);
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_grid_attach (GTK_GRID (grid),
make_shader_stack ("Wind", "/gltransition/transition1.glsl", scale),
0, 0, 1, 1);
gtk_grid_attach (GTK_GRID (grid),
make_shader_stack ("Radial", "/gltransition/transition2.glsl", scale),
1, 0, 1, 1);
gtk_grid_attach (GTK_GRID (grid),
make_shader_stack ("Crosswarp", "/gltransition/transition3.glsl", scale),
0, 1, 1, 1);
gtk_grid_attach (GTK_GRID (grid),
make_shader_stack ("Kaleidoscope", "/gltransition/transition4.glsl", scale),
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,220 @@
#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 (frame_clock);
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_draw (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_draw (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)
{
gtk_snapshot_push_glshader (snapshot, self->active_shader,
&GRAPHENE_RECT_INIT(0, 0, width, height),
1,
"u_time", &self->time,
NULL
);
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,356 @@
#include "gtkshaderstack.h"
struct _GtkShaderStack
{
GtkWidget parent_instance;
GskGLShader *shader;
GPtrArray *children;
int current;
int next;
gboolean backwards;
guint tick_id;
float time;
float duration;
gint64 start_time;
};
struct _GtkShaderStackClass
{
GtkWidgetClass parent_class;
};
enum {
PROP_DURATION = 1,
NUM_PROPERTIES
};
static GParamSpec *properties[NUM_PROPERTIES] = { NULL };
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 void
update_child_visible (GtkShaderStack *self)
{
int i;
for (i = 0; i < self->children->len; i++)
{
GtkWidget *child = g_ptr_array_index (self->children, i);
gtk_widget_set_child_visible (child,
i == self->current || i == self->next);
}
}
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;
update_child_visible (self);
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;
update_child_visible (self);
}
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);
guint button;
button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
stop_transition (self);
self->backwards = button == GDK_BUTTON_SECONDARY;
if (self->backwards)
self->next = (self->current + self->children->len - 1) % self->children->len;
else
self->next = (self->current + 1) % self->children->len;
update_child_visible (self);
start_transition (self);
}
static void
gtk_shader_stack_init (GtkShaderStack *self)
{
GtkEventController *controller;
self->children = g_ptr_array_new_with_free_func ((GDestroyNotify)gtk_widget_unparent);
self->current = -1;
self->next = -1;
self->backwards = FALSE;
self->duration = 1.0;
controller = GTK_EVENT_CONTROLLER (gtk_gesture_click_new ());
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (controller), 0);
g_signal_connect (controller, "released", G_CALLBACK (clicked_cb), self);
gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_BUBBLE);
gtk_widget_add_controller (GTK_WIDGET (self), controller);
}
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
{
float progress;
next = g_ptr_array_index (self->children, self->next);
progress = self->time / self->duration;
if (self->backwards)
{
GtkWidget *tmp = next;
next = current;
current = tmp;
progress = 1. - progress;
}
gtk_snapshot_push_glshader (snapshot,
self->shader,
&GRAPHENE_RECT_INIT(0, 0, width, height),
2,
"progress", &progress,
NULL);
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_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkShaderStack *self = GTK_SHADER_STACK (object);
switch (prop_id)
{
case PROP_DURATION:
g_value_set_float (value, self->duration);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_shader_stack_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkShaderStack *self = GTK_SHADER_STACK (object);
switch (prop_id)
{
case PROP_DURATION:
self->duration = g_value_get_float (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
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;
object_class->get_property = gtk_shader_stack_get_property;
object_class->set_property = gtk_shader_stack_set_property;
widget_class->snapshot = gtk_shader_stack_snapshot;
widget_class->measure = gtk_shader_stack_measure;
widget_class->size_allocate = gtk_shader_stack_size_allocate;
properties[PROP_DURATION] =
g_param_spec_float ("duration", "Duration", "Duration",
0.1, 3.0, 1.0,
G_PARAM_READWRITE);
g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
}
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;
else
gtk_widget_set_child_visible (child, FALSE);
}

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 @@
uniform float progress;
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 @@
uniform float progress;
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 @@
uniform float progress;
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 @@
uniform float progress;
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

@@ -19,6 +19,7 @@
#include "gskglnodesampleprivate.h"
#include "gsktransform.h"
#include "glutilsprivate.h"
#include "gskglshaderprivate.h"
#include "gskprivate.h"
@@ -64,6 +65,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 +136,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 +508,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 +1053,128 @@ 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;
int n_uniforms;
const GskGLUniform *uniforms;
int n_required_sources = gsk_glshader_get_n_required_sources (shader);
/* We always create the program, so that any compiler warnings or other is only reported once */
program = gsk_gl_renderer_create_custom_program (self, shader);
shader_source = gsk_glshader_get_sourcecode (shader);
uniforms = gsk_glshader_get_uniforms (shader, &n_uniforms);
if (n_uniforms > G_N_ELEMENTS (program->glshader.args_locations))
{
g_set_error (&error, GDK_GL_ERROR, GDK_GL_ERROR_UNSUPPORTED_FORMAT,
"GLShaderNode supports max %d custom uniforms", (int)G_N_ELEMENTS (program->glshader.args_locations));
}
else if (n_required_sources > 1 + G_N_ELEMENTS (program->glshader.extra_source_locations))
{
g_set_error (&error, GDK_GL_ERROR, GDK_GL_ERROR_UNSUPPORTED_FORMAT,
"GLShaderNode supports max %d texture sources", (int)(1 + G_N_ELEMENTS (program->glshader.extra_source_locations)));
}
else
{
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);
gsk_gl_shader_builder_finish (&shader_builder);
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.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");
for (int i = 0; i < G_N_ELEMENTS (program->glshader.args_locations); i++)
{
if (i < n_uniforms)
{
program->glshader.args_locations[i] = glGetUniformLocation(program->id, uniforms[i].name);
if (program->glshader.args_locations[i] == -1)
g_warning ("Expected uniform `%s` not found in shader", uniforms[i].name);
}
else
program->glshader.args_locations[i] = -1;
}
}
}
if (program->id <= 0)
{
g_warning ("Failed to compile gl shader: %s\n", error->message);
g_error_free (error);
}
}
if (program->id >= 0 && n_children <= 4)
{
const guchar *uniform_data;
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;
}
}
uniform_data = gsk_glshader_node_get_uniform_data (node);
ops_set_program (builder, program);
ops_set_glshader_args (builder, shader, node->bounds.size.width, node->bounds.size.height, uniform_data);
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 +2840,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 +2996,51 @@ 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)
{
int n_uniforms, i;
const GskGLUniform *uniforms;
OP_PRINT (" -> GLShader Args");
glUniform2fv (program->glshader.size_location, 1, op->size);
uniforms = gsk_glshader_get_uniforms (op->shader, &n_uniforms);
for (i = 0; i < n_uniforms; i++)
{
const GskGLUniform *u = &uniforms[i];
const guchar *data = op->uniform_data + u->offset;
switch (u->type)
{
default:
case GSK_GLUNIFORM_TYPE_NONE:
break;
case GSK_GLUNIFORM_TYPE_FLOAT:
glUniform1fv (program->glshader.args_locations[i], 1, (const float *)data);
break;
case GSK_GLUNIFORM_TYPE_INT:
glUniform1iv (program->glshader.args_locations[i], 1, (const gint32 *)data);
break;
case GSK_GLUNIFORM_TYPE_UINT:
case GSK_GLUNIFORM_TYPE_BOOL:
glUniform1uiv (program->glshader.args_locations[i], 1, (const guint32 *) data);
break;
case GSK_GLUNIFORM_TYPE_VEC2:
glUniform2fv (program->glshader.args_locations[i], 1, (const float *)data);
break;
case GSK_GLUNIFORM_TYPE_VEC3:
glUniform3fv (program->glshader.args_locations[i], 1, (const float *)data);
break;
case GSK_GLUNIFORM_TYPE_VEC4:
glUniform4fv (program->glshader.args_locations[i], 1, (const float *)data);
break;
}
}
}
static inline void
apply_border_width_op (const Program *program,
const OpBorder *op)
@@ -2886,6 +3112,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 +3143,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 +3168,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 +3229,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 +3240,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 +3685,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 +3971,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 +4021,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,22 @@ ops_set_color (RenderOpBuilder *builder,
op->rgba = color;
}
void
ops_set_glshader_args (RenderOpBuilder *builder,
GskGLShader *shader,
float width,
float height,
const guchar *uniform_data)
{
OpGLShader *op;
op = ops_begin (builder, OP_CHANGE_GLSHADER_ARGS);
op->shader = shader;
op->size[0] = width;
op->size[1] = height;
op->uniform_data = uniform_data;
}
void
ops_set_color_matrix (RenderOpBuilder *builder,
const graphene_matrix_t *matrix,

View File

@@ -31,6 +31,57 @@ 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;
};
} ProgramState;
struct _Program
{
int index; /* Into the renderer's program array */
@@ -108,59 +159,15 @@ struct _Program
int child_bounds_location;
int texture_rect_location;
} repeat;
struct {
int size_location;
int args_locations[8];
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 +190,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 +243,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 +262,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 +291,11 @@ void ops_set_inset_shadow (RenderOpBuilder *self,
const GdkRGBA *color,
float dx,
float dy);
void ops_set_glshader_args (RenderOpBuilder *builder,
GskGLShader *shader,
float width,
float height,
const guchar *uniform_data);
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,13 @@ typedef struct
float texture_rect[4];
} OpRepeat;
typedef struct
{
float size[2];
GskGLShader *shader;
const guchar *uniform_data;
} 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;
/**
@@ -218,4 +219,32 @@ typedef enum
GSK_TRANSFORM_CATEGORY_IDENTITY
} GskTransformCategory;
/**
* GskGLUniformType:
* @GSK_GLUNIFORM_TYPE_NONE: No type, used for uninitialized or unspecified values.
* @GSK_GLUNIFORM_TYPE_FLOAT: A float uniform
* @GSK_GLUNIFORM_TYPE_INT: A GLSL int / gint32 uniform
* @GSK_GLUNIFORM_TYPE_UINT: A GLSL uint / guint32 uniform
* @GSK_GLUNIFORM_TYPE_BOOL: A GLSL bool / gboolean uniform
* @GSK_GLUNIFORM_TYPE_VEC2: A GLSL vec2 / graphene_vec2_t uniform
* @GSK_GLUNIFORM_TYPE_VEC3: A GLSL vec3 / graphene_vec3_t uniform
* @GSK_GLUNIFORM_TYPE_VEC4: A GLSL vec4 / graphene_vec4_t uniform
*
* This defines the types of the uniforms that #GskGLShaders
* declare. It defines both what the type is called in the GLSL shader
* code, and what the corresponding C type is on the Gtk side.
*/
typedef enum
{
GSK_GLUNIFORM_TYPE_NONE,
GSK_GLUNIFORM_TYPE_FLOAT,
GSK_GLUNIFORM_TYPE_INT,
GSK_GLUNIFORM_TYPE_UINT,
GSK_GLUNIFORM_TYPE_BOOL,
GSK_GLUNIFORM_TYPE_VEC2,
GSK_GLUNIFORM_TYPE_VEC3,
GSK_GLUNIFORM_TYPE_VEC4,
} GskGLUniformType;
#endif /* __GSK_TYPES_H__ */

529
gsk/gskglshader.c Normal file
View File

@@ -0,0 +1,529 @@
/* GSK - The GTK Scene Kit
*
* Copyright 2020, Red Hat Inc
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* SECTION:GskGLShader
* @Title: GskGLShader
* @Short_description: A description of GskGLShader
*
* A #GskGLShader is a snippet of GLSL that is meant to run in the
* fragment shader of the rendering pipeline. A fragment shaader it
* gets the coordinates being rendered as input and produces a the
* pixel values for that particular pixel. Additionally the
* shader can declare a set of other input arguments, called
* uniforms (as they are uniform over all the calls to your shader in
* each instance of use). A shader can also receieve up to 4
* textures that it can use as input when producing the pixel data.
*
* The typical way a #GskGLShader is used is as an argument to a
* #GskGLShaderNode in the rendering hierarchy, and then the textures
* it gets as input are constructed by rendering the child nodes to
* textures before rendering the shader node itself. (Although you can
* also pass texture nodes as children if you want to directly use a
* texture as input). Note that the #GskGLShaderNode API is a bit
* lowlevel, and highlevel code normally uses
* gtk_snapshot_push_glshader() to produce the nodes.
*
* The actual shader code is GLSL code that gets combined with
* some other code into the fragment shader. Since the exact
* capabilities of the GPU driver differs between different OpenGL
* drivers and hardware Gtk adds some defines that you can use
* to ensure your GLSL code runs on as many drivers as it can.
*
* If the OpenGL driver is GLES, then the shader language version
* is set to 100, and GSK_GLES will be defined in the shader.
*
* Otherwise, if the OpenGL driver does not support the 3.2 core profile,
* then the shader will run with language version 110 for GL2 and 130 for GL3,
* and GSK_LEGACY will be defined in the shader.
*
* If the OpenGL driver supports the 3.2 code profile, it will be used,
* the shader language version is set to 150, and GSK_GL3 will be defined
* in the shader.
*
* The main function the shader should implement is:
*
* |[<!-- language="plain" -->
* void mainImage(out vec4 fragColor,
* in vec2 fragCoord,
* in vec2 resolution,
* in vec2 uv)
* ]|
*
* Where the input @fragCoord is the coordinate of the pixel we're
* currently rendering, relative to the boundary rectangle that was
* specified in the #GskGLShaderNode, and @resolution is the width and
* height of that rectangle. This is in the typical Gtk+ coordinate
* system with the origin in the top left. @uv contains the u and v
* coordinates that can be used to index a texture at the
* corresponding point. These coordinates are in the [0..1]x[0..1]
* region, with 0, 0 being in the lower left cornder (which is typical
* for OpenGL).
*
* The output @fragColor should be a RGBA color (and alpha) that will
* be used as the output for the specified pixel location. Note that
* this output will be automatically clipped to the clip region of the
* glshader node.
*
* In addition to the function arguments the shader can use one of the
* pre-defined uniforms for tetxure sources (u_source, u_source2,
* u_source3 or u_source4), as well as any custom uniforms you
* declare.
*
* Gtk uses the the "gsk" namespace in the symbols it uses in the
* shader, so your code should not use any symbols with the prefix gsk
* or GSK. There are some helper functions declared that you can use:
*
* |[<!-- language="plain" -->
* vec4 GskTexture(sampler2D sampler, vec2 texCoords);
* ]|
*
* This samples a texture (e.g. u_source) at the specified
* coordinates, and containes some helper ifdefs to ensure that
* it works on all OpenGL versions.
*/
#include "config.h"
#include "gskglshader.h"
#include "gskglshaderprivate.h"
struct _GskGLShader
{
GObject parent_instance;
char *sourcecode;
int n_required_sources;
int uniforms_size;
GArray *uniforms;
};
G_DEFINE_TYPE (GskGLShader, gsk_glshader, G_TYPE_OBJECT)
enum {
GLSHADER_PROP_0,
GLSHADER_PROP_SOURCECODE,
GLSHADER_PROP_N_REQUIRED_SOURCES,
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_SOURCECODE:
g_value_set_string (value, shader->sourcecode);
break;
case GLSHADER_PROP_N_REQUIRED_SOURCES:
g_value_set_int (value, shader->n_required_sources);
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_SOURCECODE:
g_free (shader->sourcecode);
shader->sourcecode = g_value_dup_string (value);
break;
case GLSHADER_PROP_N_REQUIRED_SOURCES:
gsk_glshader_set_n_required_sources (shader, g_value_get_int (value));
break;
default:
g_assert_not_reached ();
}
}
static void
gsk_glshader_finalize (GObject *object)
{
GskGLShader *shader = GSK_GLSHADER (object);
g_free (shader->sourcecode);
for (int i = 0; i < shader->uniforms->len; i ++)
g_free (g_array_index (shader->uniforms, GskGLUniform, i).name);
g_array_free (shader->uniforms, TRUE);
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:sourcecode:
*
* The source code for the shader.
*/
gsk_glshader_properties[GLSHADER_PROP_SOURCECODE] =
g_param_spec_string ("sourcecode",
"Sourcecode",
"The sourcecode code for the shader",
NULL,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
/**
* GskGLShader:n-required-sources:
*
* The number of texture sources (u_source*) that are required to be set for the
* shader to work.
*/
gsk_glshader_properties[GLSHADER_PROP_N_REQUIRED_SOURCES] =
g_param_spec_int ("n-required-sources",
"Number of required sources",
"Minimum number of source textures required for the shader to work",
0, 4, 0,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, GLSHADER_N_PROPS, gsk_glshader_properties);
}
static void
gsk_glshader_init (GskGLShader *shader)
{
shader->uniforms = g_array_new (FALSE, FALSE, sizeof (GskGLUniform));
}
/**
* gsk_glshader_new:
* @sourcecode: The sourcecode for the shader
*
* Creates a #GskGLShader that will render pixels using the specified code.
*
* Returns: (transfer full): A new #GskGLShader
*/
GskGLShader *
gsk_glshader_new (const char *sourcecode)
{
GskGLShader *shader = g_object_new (GSK_TYPE_GLSHADER,
"sourcecode", sourcecode,
NULL);
return shader;
}
/**
* gsk_glshader_get_sourcecode:
* @shader: A #GskGLShader
*
* Get the source code being used to render this shader.
*
* Returns: (transfer none): The source code for the shader
*/
const char *
gsk_glshader_get_sourcecode (GskGLShader *shader)
{
return shader->sourcecode;
}
/**
* gsk_glshader_get_n_required_sources:
* @shader: A #GskGLShader
*
* Returns the number of texture sources that the shader requires. This can be set
* with gsk_glshader_set_n_required_sources() and can be used to check that the
* a passed shader works in your usecase.
*
* Returns: (transfer none): The set nr of texture sources this shader requires.
*/
int
gsk_glshader_get_n_required_sources (GskGLShader *shader)
{
return shader->n_required_sources;
}
/**
* gsk_glshader_set_n_required_sources:
* @shader: A #GskGLShader
* @n_required_sources: The number of sources.
*
* Sets the number of texture sources that the shader requires. This can be used
* to signal to other users that this shader needs this much input.
*/
void
gsk_glshader_set_n_required_sources (GskGLShader *shader,
int n_required_sources)
{
shader->n_required_sources = n_required_sources;
}
static int
uniform_type_size (GskGLUniformType type)
{
switch (type)
{
case GSK_GLUNIFORM_TYPE_FLOAT:
return sizeof (float);
case GSK_GLUNIFORM_TYPE_INT:
return sizeof (gint32);
case GSK_GLUNIFORM_TYPE_UINT:
case GSK_GLUNIFORM_TYPE_BOOL:
return sizeof (guint32);
case GSK_GLUNIFORM_TYPE_VEC2:
return sizeof (float) * 2;
case GSK_GLUNIFORM_TYPE_VEC3:
return sizeof (float) * 3;
case GSK_GLUNIFORM_TYPE_VEC4:
return sizeof (float) * 4;
case GSK_GLUNIFORM_TYPE_NONE:
default:
g_assert_not_reached ();
return 0;
}
}
/**
* gsk_glshader_add_uniform:
* @shader: A #GskGLShader
* @name: Then name of the uniform.
* @type: The uniform type
*
* This declares that the shader uses a uniform of a particular @name
* and @type. You need to declare each uniform that your shader
* uses or you will not be able to pass data to it.
*/
void
gsk_glshader_add_uniform (GskGLShader *shader,
const char *name,
GskGLUniformType type)
{
GskGLUniform uniform = {
g_strdup (name),
type,
shader->uniforms_size
};
shader->uniforms_size += uniform_type_size (type);
g_array_append_val (shader->uniforms, uniform);
}
/**
* gsk_glshader_get_n_uniforms:
* @shader: A #GskGLShader
*
* Get the number of declared uniforms for this shader.
*
* Returns: The nr of declared uniforms
*/
int
gsk_glshader_get_n_uniforms (GskGLShader *shader)
{
return shader->uniforms->len;
}
/**
* gsk_glshader_get_uniform_name:
* @shader: A #GskGLShader
* @idx: A zero-based index of the uniforms
*
* Get the name of a declared uniforms for this shader at index @indx.
*
* Returns: (transfer none): The name of the declared uniform
*/
const char *
gsk_glshader_get_uniform_name (GskGLShader *shader,
int idx)
{
return g_array_index (shader->uniforms, GskGLUniform, idx).name;
}
/**
* gsk_glshader_get_uniform_type:
* @shader: A #GskGLShader
* @idx: A zero-based index of the uniforms
*
* Get the type of a declared uniforms for this shader at index @indx.
*
* Returns: The type of the declared uniform
*/
GskGLUniformType
gsk_glshader_get_uniform_type (GskGLShader *shader,
int idx)
{
return g_array_index (shader->uniforms, GskGLUniform, idx).type;
}
/**
* gsk_glshader_get_uniform_offset:
* @shader: A #GskGLShader
* @idx: A zero-based index of the uniforms
*
* Get the offset into the data block where data for this uniforms is stored.
*
* Returns: The data offset
*/
int
gsk_glshader_get_uniform_offset (GskGLShader *shader,
int idx)
{
return g_array_index (shader->uniforms, GskGLUniform, idx).offset;
}
const GskGLUniform *
gsk_glshader_get_uniforms (GskGLShader *shader,
int *n_uniforms)
{
*n_uniforms = shader->uniforms->len;
return &g_array_index (shader->uniforms, GskGLUniform, 0);
}
/**
* gsk_glshader_get_uniforms_size:
* @shader: A #GskGLShader
*
* Get the size of the data block used to specify uniform data for this shader.
*
* Returns: The size of the data block
*/
int
gsk_glshader_get_uniforms_size (GskGLShader *shader)
{
return shader->uniforms_size;
}
static const GskGLUniform *
gsk_glshader_find_uniform (GskGLShader *shader,
const char *name)
{
int i;
for (i = 0; i < shader->uniforms->len; i++)
{
const GskGLUniform *u = &g_array_index (shader->uniforms, GskGLUniform, i);
if (strcmp (u->name, name) == 0)
return u;
}
return NULL;
}
/**
* gsk_glshader_format_uniform_data_va:
* @shader: A #GskGLShader
* @uniforms: %NULL terminated va_list with (name, pointer-to-data) arguments pairs
*
* Formats the uniform data as needed for feeding the named uniforms values into the shader.
* The argument list is a list of pairs of names, and pointers to data of the types
* that match the declared uniforms (i.e. `float *` for float uniforms and `graphene_vec4_t *` f
* or vec3 uniforms).
*
* Returns: (transfer full): A newly allocated block of data which can be passed to gsk_glshader_node_new().
*/
guchar *
gsk_glshader_format_uniform_data_va (GskGLShader *shader,
va_list uniforms)
{
guchar *args = g_malloc0 (shader->uniforms_size);
const char *name;
while ((name = va_arg (uniforms, const char *)) != NULL)
{
const GskGLUniform *u;
gpointer value = va_arg (uniforms, gpointer);
guchar *args_dest;
u = gsk_glshader_find_uniform (shader, name);
if (u == NULL)
{
/* This isn't really an error, because we can easily imaging
a shader interface that have input which isn't needed for
a particular shader */
g_debug ("No uniform named `%s` in shader", name);
continue;
}
args_dest = args + u->offset;
/* We use pointers-to-value so that all values are the same
size, otherwise we couldn't handle the missing uniform case above */
switch (u->type)
{
case GSK_GLUNIFORM_TYPE_FLOAT:
*(float *)args_dest = *(float *)value;
break;
case GSK_GLUNIFORM_TYPE_INT:
*(gint32 *)args_dest = *(gint32 *)value;
break;
case GSK_GLUNIFORM_TYPE_UINT:
*(guint32 *)args_dest = *(guint32 *)value;
break;
case GSK_GLUNIFORM_TYPE_BOOL:
*(guint32 *)args_dest = *(gboolean *)value;
break;
case GSK_GLUNIFORM_TYPE_VEC2:
graphene_vec2_to_float ((const graphene_vec2_t *)value,
(float *)args_dest);
break;
case GSK_GLUNIFORM_TYPE_VEC3:
graphene_vec3_to_float ((const graphene_vec3_t *)value,
(float *)args_dest);
break;
case GSK_GLUNIFORM_TYPE_VEC4:
graphene_vec4_to_float ((const graphene_vec4_t *)value,
(float *)args_dest);
break;
case GSK_GLUNIFORM_TYPE_NONE:
default:
g_assert_not_reached ();
}
}
return args;
}

70
gsk/gskglshader.h Normal file
View File

@@ -0,0 +1,70 @@
/* GSK - The GTK Scene Kit
*
* Copyright 2020 Red Hat Inc
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GSK_GLSHADER_H__
#define __GSK_GLSHADER_H__
#if !defined (__GSK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gsk/gsk.h> can be included directly."
#endif
#include <stdarg.h>
#include <gsk/gsktypes.h>
#include <gsk/gskenums.h>
G_BEGIN_DECLS
#define GSK_TYPE_GLSHADER (gsk_glshader_get_type ())
GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GskGLShader, gsk_glshader, GSK, GLSHADER, GObject)
GDK_AVAILABLE_IN_ALL
GskGLShader * gsk_glshader_new (const char *sourcecode);
GDK_AVAILABLE_IN_ALL
const char * gsk_glshader_get_sourcecode (GskGLShader *shader);
GDK_AVAILABLE_IN_ALL
int gsk_glshader_get_n_required_sources (GskGLShader *shader);
GDK_AVAILABLE_IN_ALL
void gsk_glshader_set_n_required_sources (GskGLShader *shader,
int n_required_sources);
GDK_AVAILABLE_IN_ALL
void gsk_glshader_add_uniform (GskGLShader *shader,
const char *name,
GskGLUniformType type);
GDK_AVAILABLE_IN_ALL
int gsk_glshader_get_n_uniforms (GskGLShader *shader);
GDK_AVAILABLE_IN_ALL
const char * gsk_glshader_get_uniform_name (GskGLShader *shader,
int idx);
GDK_AVAILABLE_IN_ALL
GskGLUniformType gsk_glshader_get_uniform_type (GskGLShader *shader,
int idx);
GDK_AVAILABLE_IN_ALL
int gsk_glshader_get_uniform_offset (GskGLShader *shader,
int idx);
GDK_AVAILABLE_IN_ALL
int gsk_glshader_get_uniforms_size (GskGLShader *shader);
GDK_AVAILABLE_IN_ALL
guchar * gsk_glshader_format_uniform_data_va (GskGLShader *shader,
va_list uniforms);
G_END_DECLS
#endif /* __GSK_GLSHADER_H__ */

17
gsk/gskglshaderprivate.h Normal file
View File

@@ -0,0 +1,17 @@
#ifndef __GSK_GL_UNIFORM_H__
#define __GSK_GL_UNIFORM_H__
#include "gskglshader.h"
typedef struct
{
char *name;
GskGLUniformType type;
int offset;
} GskGLUniform;
const GskGLUniform *gsk_glshader_get_uniforms (GskGLShader *shader,
int *n_uniforms);
#endif

View File

@@ -25,6 +25,7 @@
#include <gsk/gskroundedrect.h>
#include <gsk/gsktypes.h>
#include <gsk/gskglshader.h>
#include <gtk/css/gtkcss.h>
G_BEGIN_DECLS
@@ -122,6 +123,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 +148,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 +454,28 @@ 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_rect_t *bounds,
guchar *uniform_data,
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
const guchar * gsk_glshader_node_get_uniform_data (GskRenderNode *node);
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,244 @@ gsk_debug_node_get_message (GskRenderNode *node)
return self->message;
}
/*** GSK_GLSHADER_NODE ***/
struct _GskGLShaderNode
{
GskRenderNode render_node;
GskGLShader *shader;
union {
guchar inlined[8]; /* Most shaders have few args, so avoid extra alloc */
guchar *external;
} args;
GskRenderNode *fallback;
GskRenderNode **children;
guint n_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));
int uniforms_size;
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);
uniforms_size = gsk_glshader_get_uniforms_size (self->shader);
if (uniforms_size > sizeof (self->args.inlined))
g_free (self->args.external);
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;
int uniforms_size = gsk_glshader_get_uniforms_size (self1->shader);
if (graphene_rect_equal (&node1->bounds, &node2->bounds) &&
self1->shader == self2->shader &&
((uniforms_size <= sizeof (self1->args.inlined)) ?
memcmp (&self1->args.inlined[0], &self2->args.inlined[0], uniforms_size) == 0:
memcmp (self1->args.external, self2->args.external, uniforms_size) == 0) &&
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 #GskGLShader
* @bounds: the rectangle to render the shader into
* @uniform_data: Data for the uniforms
* @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.
* The @uniform_data is a block of data to use for uniform input, as per types and offsets
* defined by the @shader. Normally this is generated by gsk_glshader_format_uniform_data_va().
*
* See #GskGLShader for details about how the shader should be written.
*
* All the children will be rendered into textures, if they aren't already #GskTextureNode:s
* then they will be used directly. These textures will be sent as input to the shader.
*
* If the backend doesn't support GL shaders, or if there is any problem when compiling
* the shader, then the fallback shader node will be used instead.
*
* Returns: (transfer full) (type GskGLShaderNode): A new #GskRenderNode
*/
GskRenderNode *
gsk_glshader_node_new (GskGLShader *shader,
const graphene_rect_t *bounds,
guchar *uniform_data,
GskRenderNode *fallback,
GskRenderNode **children,
int n_children)
{
GskGLShaderNode *self;
GskRenderNode *node;
int uniforms_size;
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);
uniforms_size = gsk_glshader_get_uniforms_size (shader);
if (uniforms_size <= sizeof (self->args.inlined))
memcpy (self->args.inlined, uniform_data, uniforms_size);
else
self->args.external = g_memdup (uniform_data, uniforms_size);
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_uniform_data:
* @node: (type GskGLShaderNode): a #GskRenderNode for a gl shader
*
* Gets args for the node.
*/
const guchar *
gsk_glshader_node_get_uniform_data (GskRenderNode *node)
{
GskGLShaderNode *self = (GskGLShaderNode *) node;
int uniforms_size;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_GLSHADER_NODE), NULL);
uniforms_size = gsk_glshader_get_uniforms_size (self->shader);
if (uniforms_size > sizeof (self->args.inlined))
return self->args.external;
else
return &self->args.inlined[0];
}
GType gsk_render_node_types[GSK_RENDER_NODE_TYPE_N_TYPES];
#ifndef I_
@@ -4506,6 +4744,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 +5102,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

@@ -358,6 +358,13 @@ parse_double (GtkCssParser *parser,
return gtk_css_parser_consume_number (parser, out_double);
}
static gboolean
parse_int (GtkCssParser *parser,
gpointer out_int)
{
return gtk_css_parser_consume_integer (parser, out_int);
}
static gboolean
parse_point (GtkCssParser *parser,
gpointer out_point)
@@ -824,9 +831,9 @@ clear_node (gpointer inout_node)
static GskRenderNode *
parse_container_node (GtkCssParser *parser)
{
GskRenderNode *node;
GPtrArray *nodes;
const GtkCssToken *token;
GskRenderNode *node;
nodes = g_ptr_array_new_with_free_func ((GDestroyNotify) gsk_render_node_unref);
@@ -1089,6 +1096,251 @@ parse_inset_shadow_node (GtkCssParser *parser)
return gsk_inset_shadow_node_new (&outline, &color, dx, dy, spread, blur);
}
typedef struct {
GskGLUniformType type;
char *name;
union {
int i;
double v[4];
} value;
} Uniform;
static gboolean
parse_uniform_decl (GtkCssParser *parser, Uniform *decl)
{
Uniform decls[] = {
{ GSK_GLUNIFORM_TYPE_FLOAT, (char *) "float" },
{ GSK_GLUNIFORM_TYPE_INT, (char *) "int" },
{ GSK_GLUNIFORM_TYPE_UINT, (char*) "uint" },
{ GSK_GLUNIFORM_TYPE_BOOL, (char *) "bool" },
{ GSK_GLUNIFORM_TYPE_VEC2, (char *) "vec2" },
{ GSK_GLUNIFORM_TYPE_VEC3, (char *) "vec3" },
{ GSK_GLUNIFORM_TYPE_VEC4, (char *) "vec4" },
};
int i;
for (i = 0; i < G_N_ELEMENTS (decls); i++)
{
if (gtk_css_parser_try_ident (parser, decls[i].name))
{
decl->type = decls[i].type;
decl->name = gtk_css_parser_consume_ident (parser);
gtk_css_parser_try_token (parser, GTK_CSS_TOKEN_COMMA);
return TRUE;
}
}
return FALSE;
}
static void
clear_uniform_decl (gpointer data)
{
Uniform *decl = data;
g_free (decl->name);
}
static gboolean
parse_uniforms (GtkCssParser *parser, gpointer data)
{
GArray *uniforms = data;
Uniform decl;
while (parse_uniform_decl (parser, &decl))
g_array_append_val (uniforms, decl);
return TRUE;
}
static void
clear_array (gpointer data)
{
GArray *array = data;
g_array_set_size (array, 0);
}
static gboolean
parse_uniform_value (GtkCssParser *parser, Uniform *uniform)
{
switch (uniform->type)
{
case GSK_GLUNIFORM_TYPE_FLOAT:
if (!gtk_css_parser_consume_number (parser, &uniform->value.v[0]))
return FALSE;
break;
case GSK_GLUNIFORM_TYPE_INT:
if (!gtk_css_parser_consume_integer (parser, &uniform->value.i))
return FALSE;
break;
case GSK_GLUNIFORM_TYPE_UINT:
if (!gtk_css_parser_consume_integer (parser, &uniform->value.i) ||
uniform->value.i < 0)
return FALSE;
break;
case GSK_GLUNIFORM_TYPE_BOOL:
if (!gtk_css_parser_consume_integer (parser, &uniform->value.i) ||
(uniform->value.i != 0 && uniform->value.i != 1))
return FALSE;
break;
case GSK_GLUNIFORM_TYPE_VEC2:
if (!gtk_css_parser_consume_number (parser, &uniform->value.v[0]) ||
!gtk_css_parser_consume_number (parser, &uniform->value.v[1]))
return FALSE;
break;
case GSK_GLUNIFORM_TYPE_VEC3:
if (!gtk_css_parser_consume_number (parser, &uniform->value.v[0]) ||
!gtk_css_parser_consume_number (parser, &uniform->value.v[1]) ||
!gtk_css_parser_consume_number (parser, &uniform->value.v[2]))
return FALSE;
break;
case GSK_GLUNIFORM_TYPE_VEC4:
if (!gtk_css_parser_consume_number (parser, &uniform->value.v[0]) ||
!gtk_css_parser_consume_number (parser, &uniform->value.v[1]) ||
!gtk_css_parser_consume_number (parser, &uniform->value.v[2]) ||
!gtk_css_parser_consume_number (parser, &uniform->value.v[3]))
return FALSE;
break;
case GSK_GLUNIFORM_TYPE_NONE:
default:
g_assert_not_reached ();
break;
}
gtk_css_parser_try_token (parser, GTK_CSS_TOKEN_COMMA);
return TRUE;
}
static gboolean
parse_uniform_data (GtkCssParser *parser, gpointer data)
{
GArray *uniforms = data;
int i;
for (i = 0; i < uniforms->len; i++)
{
Uniform *uniform = &g_array_index (uniforms, Uniform, i);
if (!parse_uniform_value (parser, uniform))
return FALSE;
}
return TRUE;
}
static GskRenderNode *
parse_glshader_node (GtkCssParser *parser)
{
graphene_rect_t bounds = GRAPHENE_RECT_INIT (0, 0, 50, 50);
char *sourcecode = NULL;
int required_sources = 0;
GskRenderNode *fallback = NULL;
GskRenderNode *child[4] = { NULL, };
GArray *uniforms = g_array_new (FALSE, FALSE, sizeof (Uniform));
const Declaration declarations[] = {
{ "bounds", parse_rect, NULL, &bounds },
{ "sourcecode", parse_string, NULL, &sourcecode },
{ "required-sources", parse_int, NULL, &required_sources },
{ "uniforms", parse_uniforms, clear_array, uniforms },
{ "uniform-data", parse_uniform_data, clear_array, uniforms },
{ "fallback", parse_node, clear_node, &fallback },
{ "child1", parse_node, clear_node, &child[0] },
{ "child2", parse_node, clear_node, &child[1] },
{ "child3", parse_node, clear_node, &child[2] },
{ "child4", parse_node, clear_node, &child[3] },
};
GskGLShader *shader;
GskRenderNode *node;
guchar *uniform_data;
int len, i;
g_array_set_clear_func (uniforms, clear_uniform_decl);
parse_declarations (parser, declarations, G_N_ELEMENTS(declarations));
for (len = 0; len < 4; len++)
{
if (child[len] == NULL)
break;
}
shader = gsk_glshader_new (sourcecode);
gsk_glshader_set_n_required_sources (shader, required_sources);
for (i = 0; i < uniforms->len; i++)
{
Uniform *uniform = &g_array_index (uniforms, Uniform, i);
gsk_glshader_add_uniform (shader, uniform->name, uniform->type);
}
uniform_data = g_new (guchar, gsk_glshader_get_uniforms_size (shader));
for (i = 0; i < uniforms->len; i++)
{
Uniform *uniform = &g_array_index (uniforms, Uniform, i);
guchar *dest;
dest = uniform_data + gsk_glshader_get_uniform_offset (shader, i);
switch (uniform->type)
{
case GSK_GLUNIFORM_TYPE_FLOAT:
*(float *)dest = uniform->value.v[0];
break;
case GSK_GLUNIFORM_TYPE_INT:
*(gint32 *)dest = uniform->value.i;
break;
case GSK_GLUNIFORM_TYPE_UINT:
*(guint32 *)dest = uniform->value.i;
break;
case GSK_GLUNIFORM_TYPE_BOOL:
*(guint32 *)dest = uniform->value.i;
break;
case GSK_GLUNIFORM_TYPE_VEC4:
((float *)dest)[3] = uniform->value.v[3];
G_GNUC_FALLTHROUGH;
case GSK_GLUNIFORM_TYPE_VEC3:
((float *)dest)[2] = uniform->value.v[2];
G_GNUC_FALLTHROUGH;
case GSK_GLUNIFORM_TYPE_VEC2:
((float *)dest)[1] = uniform->value.v[1];
((float *)dest)[0] = uniform->value.v[0];
break;
case GSK_GLUNIFORM_TYPE_NONE:
default:
g_assert_not_reached ();
}
}
node = gsk_glshader_node_new (shader, &bounds, uniform_data,
fallback, child, len);
g_array_unref (uniforms);
g_free (uniform_data);
g_object_unref (shader);
for (i = 0; i < 4; i++)
{
if (child[i])
gsk_render_node_unref (child[i]);
}
return node;
}
static GskRenderNode *
parse_border_node (GtkCssParser *parser)
{
@@ -1603,6 +1855,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 +2090,51 @@ append_point (GString *str,
string_append_double (str, p->y);
}
static void
append_string (GString *str,
const char *string)
{
gsize len;
g_return_if_fail (str != NULL);
g_return_if_fail (string != NULL);
g_string_append_c (str, '"');
do {
len = strcspn (string, "\\\"\n\r\f");
g_string_append_len (str, string, len);
string += len;
switch (*string)
{
case '\0':
goto out;
case '\n':
g_string_append (str, "\\A ");
break;
case '\r':
g_string_append (str, "\\D ");
break;
case '\f':
g_string_append (str, "\\C ");
break;
case '\"':
g_string_append (str, "\\\"");
break;
case '\\':
g_string_append (str, "\\\\");
break;
default:
g_assert_not_reached ();
break;
}
string++;
} while (*string);
out:
g_string_append_c (str, '"');
}
static void
append_vec4 (GString *str,
const graphene_vec4_t *v)
@@ -1866,6 +2164,19 @@ append_float_param (Printer *p,
g_string_append (p->str, ";\n");
}
static void
append_int_param (Printer *p,
const char *param_name,
int value,
int default_value)
{
if (value == default_value)
return;
_indent (p);
g_string_append_printf (p->str, "%s: %d;\n", param_name, value);
}
static void
append_rgba_param (Printer *p,
const char *param_name,
@@ -1914,6 +2225,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 +2764,131 @@ render_node_print (Printer *p,
}
break;
case GSK_GLSHADER_NODE:
{
GskGLShader *shader = gsk_glshader_node_get_shader (node);
const guchar *uniform_data = gsk_glshader_node_get_uniform_data (node);
start_node (p, "glshader");
append_rect_param (p, "bounds", &node->bounds);
append_string_param (p, "sourcecode", gsk_glshader_get_sourcecode (shader));
append_int_param (p, "required-sources", gsk_glshader_get_n_required_sources (shader), 0);
if (gsk_glshader_get_n_uniforms (shader) > 0)
{
GString *decl = g_string_new ("");
GString *data = g_string_new ("");
for (guint i = 0; i < gsk_glshader_get_n_uniforms (shader); i++)
{
const char *name = gsk_glshader_get_uniform_name (shader, i);
int offset = gsk_glshader_get_uniform_offset (shader, i);
if (i > 0)
{
g_string_append (decl, ", ");
g_string_append (data, ", ");
}
switch (gsk_glshader_get_uniform_type (shader, i))
{
case GSK_GLUNIFORM_TYPE_NONE:
default:
g_assert_not_reached ();
break;
case GSK_GLUNIFORM_TYPE_FLOAT:
{
float value = *(float *)(uniform_data + offset);
g_string_append_printf (decl, "float %s", name);
g_string_append_printf (data, "%f", value);
}
break;
case GSK_GLUNIFORM_TYPE_INT:
{
gint32 value = *(gint32 *)(uniform_data + offset);
g_string_append_printf (decl, "int %s", name);
g_string_append_printf (data, "%d", value);
}
break;
case GSK_GLUNIFORM_TYPE_UINT:
{
guint32 value = *(guint32 *)(uniform_data + offset);
g_string_append_printf (decl, "uint %s", name);
g_string_append_printf (data, "%u", value);
}
break;
case GSK_GLUNIFORM_TYPE_BOOL:
{
gboolean value = *(gboolean *)(uniform_data + offset);
g_string_append_printf (decl, "bool %s", name);
g_string_append_printf (data, "%d", value);
}
break;
case GSK_GLUNIFORM_TYPE_VEC2:
{
graphene_vec2_t *v = (graphene_vec2_t *)(uniform_data + offset);
g_string_append_printf (decl, "vec2 %s", name);
g_string_append_printf (data, "%f %f",
graphene_vec2_get_x (v),
graphene_vec2_get_y (v));
}
break;
case GSK_GLUNIFORM_TYPE_VEC3:
{
graphene_vec3_t *v = (graphene_vec3_t *)(uniform_data + offset);
g_string_append_printf (decl, "vec3 %s", name);
g_string_append_printf (data, "%f %f %f",
graphene_vec3_get_x (v),
graphene_vec3_get_y (v),
graphene_vec3_get_z (v));
}
break;
case GSK_GLUNIFORM_TYPE_VEC4:
{
graphene_vec4_t *v = (graphene_vec4_t *)(uniform_data + offset);
g_string_append_printf (decl, "vec4 %s", name);
g_string_append_printf (data, "%f %f %f %f",
graphene_vec4_get_x (v),
graphene_vec4_get_y (v),
graphene_vec4_get_z (v),
graphene_vec4_get_w (v));
}
break;
}
}
_indent (p);
g_string_append_printf (p->str, "uniforms: %s;\n", decl->str);
_indent (p);
g_string_append_printf (p->str, "uniform-data: %s;\n", data->str);
g_string_free (decl, TRUE);
g_string_free (data, TRUE);
}
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);
char *name;
name = g_strdup_printf ("child%d", i + 1);
append_node_param (p, name, child);
g_free (name);
}
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,11 +16,13 @@ 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([
'gskdiff.c',
'gskcairorenderer.c',
'gskglshader.c',
'gskrenderer.c',
'gskrendernode.c',
'gskrendernodeimpl.c',
@@ -52,6 +54,7 @@ gsk_private_sources = files([
gsk_public_headers = files([
'gskcairorenderer.h',
'gskenums.h',
'gskglshader.h',
'gskrenderer.h',
'gskrendernode.h',
'gskroundedrect.h',

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,21 @@
// 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 sampler2D u_source2;
uniform sampler2D u_source3;
uniform sampler2D u_source4;
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;
guchar *args;
graphene_rect_t bounds;
int n_children;
int node_idx;
GskRenderNode **nodes;
} glshader;
struct {
GskRoundedRect bounds;
} rounded_clip;
@@ -812,6 +820,154 @@ 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.bounds,
state->data.glshader.args,
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_va:
* @snapshot: a #GtkSnapshot
* @shader: The code to run
* @bounds: the rectangle to render into
* @n_children: The number of extra nodes given as argument to the shader as textures.
* @uniforms: Values for the uniforms of the shader in this instance, ending with NULL
*
* Renders a rectangle with output from a GLSL shader. This is the same as
* gtk_snapshot_push_glshader() but with a va_list instead of a varargs argument,
* so see that for details.
*/
void
gtk_snapshot_push_glshader_va (GtkSnapshot *snapshot,
GskGLShader *shader,
const graphene_rect_t *bounds,
int n_children,
va_list uniforms)
{
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);
state->data.glshader.args = gsk_glshader_format_uniform_data_va (shader, uniforms);
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;
}
}
/**
* gtk_snapshot_push_glshader:
* @snapshot: a #GtkSnapshot
* @shader: The code to run
* @bounds: the rectangle to render into
* @n_children: The number of extra nodes given as argument to the shader as textures.
* @uniforms: Values for the uniforms of the shader in this instance, ending with NULL
*
* Push a #GskGLShaderNode with a specific #GskGLShader and a set of uniform values
* to use while rendering. Additionally this takes a fallback node and a list of
* @n_children other nodes which will be passed to the #GskGLShaderNode.
*
* The fallback node is used if GLSL shaders are not supported by the backend, or if
* there is any problem compiling the shader. The fallback node needs to be pushed
* directly after the gtk_snapshot_push_glshader() call up until the first call to gtk_snapshot_pop().
*
* If @n_children > 0, then it is expected that you (after the fallback call gtk_snapshot_pop()
* @n_children times. Each of these will generate a node that is passed as a child to the
* glshader node, which in turn will render these to textures and pass as arguments to the
* shader.
*
* For details on how to write shaders, see #GskGLShader.
*/
void
gtk_snapshot_push_glshader (GtkSnapshot *snapshot,
GskGLShader *shader,
const graphene_rect_t *bounds,
int n_children,
...)
{
va_list args;
va_start (args, n_children);
gtk_snapshot_push_glshader_va (snapshot, shader, bounds, n_children, args);
va_end (args);
}
static GskRenderNode *
gtk_snapshot_collect_rounded_clip (GtkSnapshot *snapshot,
GtkSnapshotState *state,

View File

@@ -99,6 +99,18 @@ 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_rect_t *bounds,
int n_children,
...);
GDK_AVAILABLE_IN_ALL
void gtk_snapshot_push_glshader_va (GtkSnapshot *snapshot,
GskGLShader *shader,
const graphene_rect_t *bounds,
int n_children,
va_list uniforms);
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:
@@ -511,6 +533,34 @@ add_color_row (GtkListStore *store,
g_object_unref (texture);
}
static void
add_int_row (GtkListStore *store,
const char *name,
int value)
{
char *text = g_strdup_printf ("%d", value);
add_text_row (store, name, text);
g_free (text);
}
static void
add_uint_row (GtkListStore *store,
const char *name,
guint value)
{
char *text = g_strdup_printf ("%u", value);
add_text_row (store, name, text);
g_free (text);
}
static void
add_boolean_row (GtkListStore *store,
const char *name,
gboolean value)
{
add_text_row (store, name, value ? "TRUE" : "FALSE");
}
static void
add_float_row (GtkListStore *store,
const char *name,
@@ -759,6 +809,88 @@ populate_render_node_properties (GtkListStore *store,
add_float_row (store, "Radius", gsk_blur_node_get_radius (node));
break;
case GSK_GLSHADER_NODE:
{
GskGLShader *shader = gsk_glshader_node_get_shader (node);
const guchar *uniform_data = gsk_glshader_node_get_uniform_data (node);
int i;
add_int_row (store, "Required sources", gsk_glshader_get_n_required_sources (shader));
for (i = 0; i < gsk_glshader_get_n_uniforms (shader); i++)
{
const char *name;
int offset;
char *title;
name = gsk_glshader_get_uniform_name (shader, i);
title = g_strdup_printf ("Uniform %s", name);
offset = gsk_glshader_get_uniform_offset (shader, i);
switch (gsk_glshader_get_uniform_type (shader, i))
{
case GSK_GLUNIFORM_TYPE_NONE:
default:
g_assert_not_reached ();
break;
case GSK_GLUNIFORM_TYPE_FLOAT:
add_float_row (store, title, *(float *)(uniform_data + offset));
break;
case GSK_GLUNIFORM_TYPE_INT:
add_int_row (store, title, *(gint32 *)(uniform_data + offset));
break;
case GSK_GLUNIFORM_TYPE_UINT:
add_uint_row (store, title, *(guint32 *)(uniform_data + offset));
break;
case GSK_GLUNIFORM_TYPE_BOOL:
add_boolean_row (store, title, *(gboolean *)(uniform_data + offset));
break;
case GSK_GLUNIFORM_TYPE_VEC2:
{
graphene_vec2_t *v = (graphene_vec2_t *)(uniform_data + offset);
float x = graphene_vec2_get_x (v);
float y = graphene_vec2_get_x (v);
char *s = g_strdup_printf ("%.2f %.2f", x, y);
add_text_row (store, title, s);
g_free (s);
}
break;
case GSK_GLUNIFORM_TYPE_VEC3:
{
graphene_vec3_t *v = (graphene_vec3_t *)(uniform_data + offset);
float x = graphene_vec3_get_x (v);
float y = graphene_vec3_get_y (v);
float z = graphene_vec3_get_z (v);
char *s = g_strdup_printf ("%.2f %.2f %.2f", x, y, z);
add_text_row (store, title, s);
g_free (s);
}
break;
case GSK_GLUNIFORM_TYPE_VEC4:
{
graphene_vec4_t *v = (graphene_vec4_t *)(uniform_data + offset);
float x = graphene_vec4_get_x (v);
float y = graphene_vec4_get_y (v);
float z = graphene_vec4_get_z (v);
float w = graphene_vec4_get_w (v);
char *s = g_strdup_printf ("%.2f %.2f %.2f %.2f", x, y, z, w);
add_text_row (store, title, s);
g_free (s);
}
break;
}
g_free (title);
}
}
break;
case GSK_INSET_SHADOW_NODE:
{
const GdkRGBA *color = gsk_inset_shadow_node_peek_color (node);