Compare commits

...

29 Commits

Author SHA1 Message Date
Matthias Clasen
f19001d4c3 Add some more docs
Add an example to the GskGLShader docs.
2020-09-25 20:38:22 -04:00
Matthias Clasen
6ecf612677 Add another shader test
This one tests the convenience api for creating
uniform data.
2020-09-25 20:37:48 -04:00
Matthias Clasen
e0b3746655 gtk-demo: Add a shader paintable to the fishbowl
This gets around 500 instances at 60fps on my system.
2020-09-25 17:22:22 -04:00
Matthias Clasen
1bc81fac2a gtk-demo: Add a shader paintable demo
Add a shader paintable to the OpenGL transitions
demo. This is reusing one of the shadertoy examples,
tweaked slightly to work as a standalone fragment
shader.
2020-09-25 17:22:22 -04:00
Matthias Clasen
bdcc29ebef gtk-demo: Add a shader paintable
This is a GdkPaintable implementation wrapped
around a GskGLShader. It can optionally be hooked
up to a frame clock to update a time uniform.

The code is set up for this to live as public api
in GSK, if we find it useful enough.
2020-09-25 17:21:40 -04:00
Alexander Larsson
810e9c238e snapshot: Drop the varargs versions of push_gl_shader()
We can now just use gsk_gl_shader_format_args() directly, relying
on the transfer-full of the shader args argument.
2020-09-25 16:51:46 +02:00
Alexander Larsson
e4dfa5597a gtk_snapshot_push_gl_shader: Make @args transfer-full
This makes the common case of this just being created a lot easier.
2020-09-25 16:46:39 +02:00
Alexander Larsson
32aa470f68 Rename uniform_data to shader args, or just args
This makes a lot of types and function names shorter.
Also, we add a "..." version of gsk_gl_shader_format_args_va().
2020-09-25 16:38:10 +02:00
Alexander Larsson
a234ab1e55 Finish remaining renames from GLSHADER to GL_SHADER (etc)
This fixes all remaining cases in public headers.
2020-09-25 14:31:44 +02:00
Alexander Larsson
e93a51b1cc GskGLShader: Drop fallback node and add try_compile function to replace it
This removes the fallback node from GskGLShaderNode and adds
a new function gsk_gl_shader_try_compile_for() which tries to compile a
shader against a renderer. Then you can use the return value of this
both as a way to implement the fallback, and as a way to get at
the error report in a saner way.
2020-09-25 14:31:33 +02:00
Matthias Clasen
66cd15d858 Add some shader tests
These are just basic api usage tests, no rendering.
They found the issues fixed in the preceding commits.
2020-09-23 18:05:57 -04:00
Matthias Clasen
a0bc9d65da gsk: Make uniform regex more forgiving
Accept - in initializers, otherwise we don't accept
things like uniform int n = -2;
2020-09-23 18:03:52 -04:00
Matthias Clasen
a580c711e5 gsk: Make uniform regex more forgiving
Accept comments after a uniform declaration - the
very first shader example I tried had these, so we
better support it.
2020-09-23 17:48:35 -04:00
Matthias Clasen
e91d5b82ce gsk: Fix getters and setters for vector types
The types we store for the uniforms are VEC2/3/4,
even though we just store a bunch of floats.

This was found while adding some simple tests.
2020-09-23 17:42:10 -04:00
Matthias Clasen
0516c59ff5 gsk: Add more shader debug spew
Print out the full assembled shader sources when
GSK_DEBUG=shaders is given. This is very verbose,
but may be useful to see what we actually pass
to the compiler.
2020-09-23 16:17:56 -04:00
Matthias Clasen
a3ea3f94dc gsk: Add some shader debug spew
When we're done extracting uniform info from the glsl,
print out what we've found for GSK_DEBUG=shaders. If
something goes wrong, this will be useful.
2020-09-23 15:40:57 -04:00
Matthias Clasen
5e9fe2f6e8 docs: Add new snapshot apis 2020-09-23 14:59:25 -04:00
Matthias Clasen
6613c523c2 Rename gl shader snapshot apis
Rename gtk_snapshot_push_glshader and friends to
gtk_snapshot_push_gl_shader, following the similar
renaming in GSK.

Update all callers.
2020-09-23 14:53:42 -04:00
Matthias Clasen
efa0fa0bf4 gsk: Small doc fixups
Tweak the gl shader docs here and there.
2020-09-23 14:46:35 -04:00
Matthias Clasen
1b6f7917ed Add GskGLShader apis to the docs 2020-09-23 14:41:22 -04:00
Alexander Larsson
8cc18d2c82 shadertoy demo: Fix GLSL on GLES
I was getting "assignment to varying fragColor" errors
2020-09-23 17:38:03 +02:00
Alexander Larsson
d97267b897 gtk-demo: Add GskGLShaderNode demo
Add adds a demo showing off GskGLShaderNode in various ways.

It has a transistion widget, using some examples from
gl-transitions.com, with child widgets being both images, a GL area
and real widgets (that let you edit the transition shaders
themselves.

It also has a fancy fire effect on hove on the buttons.
2020-09-23 17:28:23 +02:00
Alexander Larsson
21e0ad0561 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-23 17:01:17 +02:00
Alexander Larsson
94cccc5e71 GtkSnapshot: Add gtk_snapshot_push_glshader() 2020-09-23 17:01:13 +02:00
Alexander Larsson
111dfdf3f5 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 uniforms definitions
   computed from the source.
 * A the data for the uniforms, 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-23 17:01:08 +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
63 changed files with 5202 additions and 227 deletions

229
demos/gtk-demo/cogs2.glsl Normal file
View File

@@ -0,0 +1,229 @@
uniform float iTime;
in vec2 fragCoord;
out vec4 vFragColor;
// Originally from: https://www.shadertoy.com/view/3ljyDD
// License CC0: Hexagonal tiling + cog wheels
// Nothing fancy, just hexagonal tiling + cog wheels
#define PI 3.141592654
#define TAU (2.0*PI)
#define MROT(a) mat2(cos(a), sin(a), -sin(a), cos(a))
float hash(in vec2 co) {
return fract(sin(dot(co.xy ,vec2(12.9898,58.233))) * 13758.5453);
}
float pcos(float a) {
return 0.5 + 0.5*cos(a);
}
void rot(inout vec2 p, float a) {
float c = cos(a);
float s = sin(a);
p = vec2(c*p.x + s*p.y, -s*p.x + c*p.y);
}
float modPolar(inout vec2 p, float repetitions) {
float angle = 2.0*PI/repetitions;
float a = atan(p.y, p.x) + angle/2.;
float r = length(p);
float c = floor(a/angle);
a = mod(a,angle) - angle/2.;
p = vec2(cos(a), sin(a))*r;
// For an odd number of repetitions, fix cell index of the cell in -x direction
// (cell index would be e.g. -5 and 5 in the two halves of the cell):
if (abs(c) >= (repetitions/2.0)) c = abs(c);
return c;
}
float pmin(float a, float b, float k) {
float h = clamp( 0.5+0.5*(b-a)/k, 0.0, 1.0 );
return mix( b, a, h ) - k*h*(1.0-h);
}
const vec2 sz = vec2(1.0, sqrt(3.0));
const vec2 hsz = 0.5*sz;
const float smallCount = 16.0;
vec2 hextile(inout vec2 p) {
// See Art of Code: Hexagonal Tiling Explained!
// https://www.youtube.com/watch?v=VmrIDyYiJBA
vec2 p1 = mod(p, sz)-hsz;
vec2 p2 = mod(p - hsz*1.0, sz)-hsz;
vec2 p3 = mix(p2, p1, vec2(length(p1) < length(p2)));
vec2 n = p3 - p;
p = p3;
return n;
}
float circle(vec2 p, float r) {
return length(p) - r;
}
float box(vec2 p, vec2 b) {
vec2 d = abs(p)-b;
return length(max(d,0.0)) + min(max(d.x,d.y),0.0);
}
float unevenCapsule(vec2 p, float r1, float r2, float h) {
p.x = abs(p.x);
float b = (r1-r2)/h;
float a = sqrt(1.0-b*b);
float k = dot(p,vec2(-b,a));
if( k < 0.0 ) return length(p) - r1;
if( k > a*h ) return length(p-vec2(0.0,h)) - r2;
return dot(p, vec2(a,b) ) - r1;
}
float cogwheel(vec2 p, float innerRadius, float outerRadius, float cogs, float holes) {
float cogWidth = 0.25*innerRadius*TAU/cogs;
float d0 = circle(p, innerRadius);
vec2 icp = p;
modPolar(icp, holes);
icp -= vec2(innerRadius*0.55, 0.0);
float d1 = circle(icp, innerRadius*0.25);
vec2 cp = p;
modPolar(cp, cogs);
cp -= vec2(innerRadius, 0.0);
float d2 = unevenCapsule(cp.yx, cogWidth, cogWidth*0.75, (outerRadius-innerRadius));
float d3 = circle(p, innerRadius*0.20);
float d = 1E6;
d = min(d, d0);
d = pmin(d, d2, 0.5*cogWidth);
d = min(d, d2);
d = max(d, -d1);
d = max(d, -d3);
return d;
}
float ccell1(vec2 p, float r) {
float d = 1E6;
const float bigCount = 60.0;
vec2 cp0 = p;
rot(cp0, -iTime*TAU/bigCount);
float d0 = cogwheel(cp0, 0.36, 0.38, bigCount, 5.0);
vec2 cp1 = p;
float nm = modPolar(cp1, 6.0);
cp1 -= vec2(0.5, 0.0);
rot(cp1, 0.2+TAU*nm/2.0 + iTime*TAU/smallCount);
float d1 = cogwheel(cp1, 0.11, 0.125, smallCount, 5.0);
d = min(d, d0);
d = min(d, d1);
return d;
}
float ccell2(vec2 p, float r) {
float d = 1E6;
vec2 cp0 = p;
float nm = modPolar(cp0, 6.0);
vec2 cp1 = cp0;
const float off = 0.275;
const float count = smallCount + 2.0;
cp0 -= vec2(off, 0.0);
rot(cp0, 0.+TAU*nm/2.0 - iTime*TAU/count);
float d0 = cogwheel(cp0, 0.09, 0.105, count, 5.0);
cp1 -= vec2(0.5, 0.0);
rot(cp1, 0.2+TAU*nm/2.0 + iTime*TAU/smallCount);
float d1 = cogwheel(cp1, 0.11, 0.125, smallCount, 5.0);
float l = length(p);
float d2 = l - (off+0.055);
float d3 = d2 + 0.020;;
vec2 tp0 = p;
modPolar(tp0, 60.0);
tp0.x -= off;
float d4 = box(tp0, vec2(0.0125, 0.005));
float ctime = -(iTime*0.05 + r)*TAU;
vec2 tp1 = p;
rot(tp1, ctime*12.0);
tp1.x -= 0.13;
float d5 = box(tp1, vec2(0.125, 0.005));
vec2 tp2 = p;
rot(tp2, ctime);
tp2.x -= 0.13*0.5;
float d6 = box(tp2, vec2(0.125*0.5, 0.0075));
float d7 = l - 0.025;
float d8 = l - 0.0125;
d = min(d, d0);
d = min(d, d1);
d = min(d, d2);
d = max(d, -d3);
d = min(d, d4);
d = min(d, d5);
d = min(d, d6);
d = min(d, d7);
d = max(d, -d8);
return d;
}
float df(vec2 p, float scale, inout vec2 nn) {
p /= scale;
nn = hextile(p);
nn = round(nn);
float r = hash(nn);
float d;;
if (r < 0.5) {
d = ccell1(p, r);
} else {
d = ccell2(p, r);
}
return d*scale;
}
vec3 postProcess(vec3 col, vec2 q) {
//col = saturate(col);
col=pow(clamp(col,0.0,1.0),vec3(0.75));
col=col*0.6+0.4*col*col*(3.0-2.0*col); // contrast
col=mix(col, vec3(dot(col, vec3(0.33))), -0.4); // satuation
col*=0.5+0.5*pow(19.0*q.x*q.y*(1.0-q.x)*(1.0-q.y),0.7); // vigneting
return col;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord, in vec2 resolution, in vec2 uv) {
vec2 q = fragCoord/resolution.xy;
vec2 p = -1.0 + 2.0*q;
p.x *= resolution.x/resolution.y;
float tm = iTime*0.1;
p += vec2(cos(tm), sin(tm*sqrt(0.5)));
float z = mix(0.5, 1.0, pcos(tm*sqrt(0.3)));
float aa = 4.0 / resolution.y;
vec2 nn = vec2(0.0);
float d = df(p, z, nn);
vec3 col = vec3(160.0)/vec3(255.0);
vec3 baseCol = vec3(0.3);
vec4 logoCol = vec4(baseCol, 1.0)*smoothstep(-aa, 0.0, -d);
col = mix(col, logoCol.xyz, pow(logoCol.w, 8.0));
col += 0.4*pow(abs(sin(20.0*d)), 0.6);
col = postProcess(col, q);
fragColor = vec4(col, 1.0);
}

View File

@@ -133,6 +133,18 @@
<file>cogs.glsl</file>
<file>glowingstars.glsl</file>
</gresource>
<gresource prefix="/gltransition">
<file>gtkshaderstack.c</file>
<file>gtkshaderstack.h</file>
<file>gtkshaderbin.h</file>
<file>gtkshaderbin.c</file>
<file>fire.glsl</file>
<file>transition1.glsl</file>
<file>transition2.glsl</file>
<file>transition3.glsl</file>
<file>transition4.glsl</file>
<file>cogs2.glsl</file>
</gresource>
<gresource prefix="/iconscroll">
<file>iconscroll.ui</file>
</gresource>
@@ -247,6 +259,7 @@
<file>gears.c</file>
<file>gestures.c</file>
<file>glarea.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;
uniform sampler2D u_texture1;
/* 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.0 + 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.0 * 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 = GskTexture(u_texture1,uv);
fragColor = mix(vec4(color,1), widget, 1.0-color.x);
}

View File

@@ -9,6 +9,7 @@
#include "gtkfishbowl.h"
#include "gtkgears.h"
#include "gskshaderpaintable.h"
const char *const css =
".blurred-button {"
@@ -149,6 +150,37 @@ create_switch (void)
return w;
}
static gboolean
update_paintable (GtkWidget *widget,
GdkFrameClock *frame_clock,
gpointer user_data)
{
GskShaderPaintable *paintable;
gint64 frame_time;
paintable = GSK_SHADER_PAINTABLE (gtk_picture_get_paintable (GTK_PICTURE (widget)));
frame_time = gdk_frame_clock_get_frame_time (frame_clock);
gsk_shader_paintable_update_time (paintable, 0, frame_time);
return G_SOURCE_CONTINUE;
}
static GtkWidget *
create_cogs (void)
{
GtkWidget *picture;
GskGLShader *shader;
GdkPaintable *paintable;
shader = gsk_gl_shader_new_from_resource ("/gltransition/cogs2.glsl");
paintable = gsk_shader_paintable_new (shader, NULL);
picture = gtk_picture_new_for_paintable (paintable);
gtk_widget_set_size_request (picture, 150, 75);
gtk_widget_add_tick_callback (picture, update_paintable, NULL, NULL);
return picture;
}
static void
mapped (GtkWidget *w)
{
@@ -185,6 +217,7 @@ static const struct {
{ "Gears", create_gears },
{ "Switch", create_switch },
{ "Menubutton", create_menu_button },
{ "Shader", create_cogs },
};
static int selected_widget_type = -1;

View File

@@ -0,0 +1,330 @@
/* OpenGL/Transitions
* #Keywords: OpenGL, shader
*
* Create transitions between pages using a custom fragment shader.
* The examples here are taken from gl-transitions.com, and you
* can edit the transision code itself on the last page of the stack.
*
* It also shows some sample fire effects on the buttons.
*/
#include <math.h>
#include <gtk/gtk.h>
#include "gtkshaderstack.h"
#include "gtkshaderbin.h"
#include "gtkshadertoy.h"
#include "gskshaderpaintable.h"
static GtkWidget *demo_window = NULL;
static void
close_window (GtkWidget *widget)
{
/* Reset the state */
demo_window = NULL;
}
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 = g_object_get_data (G_OBJECT (button), "the-stack");
gtk_text_buffer_get_bounds (buffer, &start, &end);
text = gtk_text_buffer_get_text (buffer, &start, &end, TRUE);
GBytes *bytes = g_bytes_new_take (text, strlen (text));
shader = gsk_gl_shader_new_from_bytes (bytes);
gtk_shader_stack_set_shader (GTK_SHADER_STACK (stack), shader);
g_object_unref (shader);
g_bytes_unref (bytes);
gtk_widget_hide (button);
}
static void
go_back (GtkWidget *button,
GtkWidget *stack)
{
gtk_shader_stack_transition (GTK_SHADER_STACK (stack), FALSE);
}
static void
go_forward (GtkWidget *button,
GtkWidget *stack)
{
gtk_shader_stack_transition (GTK_SHADER_STACK (stack), TRUE);
}
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 *
fire_bin_new (void)
{
GtkWidget *bin = gtk_shader_bin_new ();
static GskGLShader *shader = NULL;
if (shader == NULL)
shader = gsk_gl_shader_new_from_resource ("/gltransition/fire.glsl");
gtk_shader_bin_add_shader (GTK_SHADER_BIN (bin), shader, GTK_STATE_FLAG_PRELIGHT, GTK_STATE_FLAG_PRELIGHT);
return bin;
}
static GtkWidget *
new_shadertoy (const char *path)
{
GBytes *shader;
GtkWidget *toy;
toy = gtk_shadertoy_new ();
shader = g_resources_lookup_data (path, 0, NULL);
gtk_shadertoy_set_image_shader (GTK_SHADERTOY (toy),
g_bytes_get_data (shader, NULL));
g_bytes_unref (shader);
return toy;
}
static gboolean
update_paintable (GtkWidget *widget,
GdkFrameClock *frame_clock,
gpointer user_data)
{
GskShaderPaintable *paintable;
gint64 frame_time;
paintable = GSK_SHADER_PAINTABLE (gtk_picture_get_paintable (GTK_PICTURE (widget)));
frame_time = gdk_frame_clock_get_frame_time (frame_clock);
gsk_shader_paintable_update_time (paintable, 0, frame_time);
return G_SOURCE_CONTINUE;
}
static GtkWidget *
make_shader_stack (const char *name,
const char *resource_path,
GtkWidget *scale,
GdkFrameClock *frame_clock)
{
GtkWidget *stack, *child, *widget, *vbox, *hbox, *bin;
GtkWidget *label, *button, *tv;
GskGLShader *shader;
GObjectClass *class;
GParamSpecFloat *pspec;
GtkAdjustment *adjustment;
GtkTextBuffer *buffer;
GBytes *bytes;
GtkEventController *controller;
GtkCssProvider *provider;
GdkPaintable *paintable;
stack = gtk_shader_stack_new ();
shader = gsk_gl_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);
shader = gsk_gl_shader_new_from_resource ("/gltransition/cogs2.glsl");
paintable = gsk_shader_paintable_new (shader, NULL);
child = gtk_picture_new_for_paintable (paintable);
gtk_widget_add_tick_callback (child, update_paintable, NULL, NULL);
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 = new_shadertoy ("/shadertoy/neon.glsl");
gtk_shader_stack_add_child (GTK_SHADER_STACK (stack), child);
child = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
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);
gtk_box_append (GTK_BOX (child), widget);
gtk_shader_stack_add_child (GTK_SHADER_STACK (stack), child);
vbox = 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");
g_signal_connect (buffer, "changed", G_CALLBACK (text_changed), button);
g_object_set_data (G_OBJECT (button), "the-stack", stack);
g_signal_connect (button, "clicked", G_CALLBACK (apply_text), buffer);
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 (vbox), widget);
gtk_box_append (GTK_BOX (vbox), stack);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
gtk_widget_set_halign (hbox, GTK_ALIGN_CENTER);
gtk_box_append (GTK_BOX (vbox), hbox);
button = gtk_button_new_from_icon_name ("go-previous");
g_signal_connect (button, "clicked", G_CALLBACK (go_back), stack);
bin = fire_bin_new ();
gtk_shader_bin_set_child (GTK_SHADER_BIN (bin), button);
gtk_box_append (GTK_BOX (hbox), bin);
button = gtk_button_new_from_icon_name ("go-next");
g_signal_connect (button, "clicked", G_CALLBACK (go_forward), stack);
bin = fire_bin_new ();
gtk_shader_bin_set_child (GTK_SHADER_BIN (bin), button);
gtk_box_append (GTK_BOX (hbox), bin);
return vbox;
}
static GtkWidget *
create_gltransition_window (GtkWidget *do_widget)
{
GtkWidget *window, *headerbar, *scale, *grid;
GdkFrameClock *frame_clock;
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), 800, 600);
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_widget_realize (window);
frame_clock = gtk_widget_get_frame_clock (window);
gtk_grid_attach (GTK_GRID (grid),
make_shader_stack ("Wind", "/gltransition/transition1.glsl", scale, frame_clock),
0, 0, 1, 1);
gtk_grid_attach (GTK_GRID (grid),
make_shader_stack ("Radial", "/gltransition/transition2.glsl", scale, frame_clock),
1, 0, 1, 1);
gtk_grid_attach (GTK_GRID (grid),
make_shader_stack ("Crosswarp", "/gltransition/transition3.glsl", scale, frame_clock),
0, 1, 1, 1);
gtk_grid_attach (GTK_GRID (grid),
make_shader_stack ("Kaleidoscope", "/gltransition/transition4.glsl", scale, frame_clock),
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,349 @@
/*
* 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.1 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/>.
*
* Authors: Matthias Clasen <mclasen@redhat.com>
*/
#include "config.h"
#include <gtk/gtk.h>
#include "gskshaderpaintable.h"
/**
* SECTION:gskshaderpaintable
* @Short_description: Drawing with shaders
* @Title: GskShaderPaintable
* @see_also: #GdkPaintable
*
* GskShaderPaintable is an implementation of the #GdkPaintable interface
* that uses a #GskGLShader to create pixels.
*
* You can set the uniform data that the shader needs for rendering
* using gsk_shader_paintable_set_uniform_data(). This function can
* be called repeatedly to change the uniform data for the next
* snapshot.
*
* Commonly, time is passed to shaders as a float uniform containing
* the elapsed time in seconds. The convenience API
* gsk_shader_paintable_update_time() can be called from a #GtkTickCallback
* to update the time based on the frame time of the frame clock.
*/
struct _GskShaderPaintable
{
GObject parent_instance;
GskGLShader *shader;
GBytes *uniform_data;
gint64 start_time;
};
struct _GskShaderPaintableClass
{
GObjectClass parent_class;
};
enum {
PROP_0,
PROP_SHADER,
PROP_UNIFORM_DATA,
N_PROPS,
};
static GParamSpec *properties[N_PROPS] = { NULL, };
static void
gsk_shader_paintable_paintable_snapshot (GdkPaintable *paintable,
GdkSnapshot *snapshot,
double width,
double height)
{
GskShaderPaintable *self = GSK_SHADER_PAINTABLE (paintable);
/* FIXME: We have to add a pointless extra child here to
* keep GtkSnapshot from blowing up. We really want n_children = 0,
* but then we would pop() 0 times, and... no pop, no node.
*/
gtk_snapshot_push_gl_shader (snapshot, self->shader, &GRAPHENE_RECT_INIT(0, 0, width, height), g_bytes_ref (self->uniform_data), 1);
gtk_snapshot_append_color (snapshot, &(GdkRGBA){1.0, 0.5, 0.6, 1.0}, &GRAPHENE_RECT_INIT(0, 0, width, height));
gtk_snapshot_pop (snapshot);
}
static void
gsk_shader_paintable_paintable_init (GdkPaintableInterface *iface)
{
iface->snapshot = gsk_shader_paintable_paintable_snapshot;
}
G_DEFINE_TYPE_EXTENDED (GskShaderPaintable, gsk_shader_paintable, G_TYPE_OBJECT, 0,
G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
gsk_shader_paintable_paintable_init))
static void
gsk_shader_paintable_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GskShaderPaintable *self = GSK_SHADER_PAINTABLE (object);
switch (prop_id)
{
case PROP_SHADER:
gsk_shader_paintable_set_shader (self, g_value_get_object (value));
break;
case PROP_UNIFORM_DATA:
gsk_shader_paintable_set_uniform_data (self, g_value_get_boxed (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gsk_shader_paintable_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GskShaderPaintable *self = GSK_SHADER_PAINTABLE (object);
switch (prop_id)
{
case PROP_SHADER:
g_value_set_object (value, self->shader);
break;
case PROP_UNIFORM_DATA:
g_value_set_boxed (value, self->uniform_data);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gsk_shader_paintable_finalize (GObject *object)
{
GskShaderPaintable *self = GSK_SHADER_PAINTABLE (object);
g_clear_pointer (&self->uniform_data, g_bytes_unref);
g_clear_object (&self->shader);
G_OBJECT_CLASS (gsk_shader_paintable_parent_class)->finalize (object);
}
static void
gsk_shader_paintable_class_init (GskShaderPaintableClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->get_property = gsk_shader_paintable_get_property;
gobject_class->set_property = gsk_shader_paintable_set_property;
gobject_class->finalize = gsk_shader_paintable_finalize;
properties[PROP_SHADER] =
g_param_spec_object ("shader", "Shader", "The shader",
GSK_TYPE_GL_SHADER,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
properties[PROP_UNIFORM_DATA] =
g_param_spec_boxed ("uniform-data", "Uniform data", "The uniform data",
G_TYPE_BYTES,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
}
static void
gsk_shader_paintable_init (GskShaderPaintable *self)
{
}
/**
* gsk_shader_paintable_new:
* @shader: (transfer full) (nullable): the shader to use
* @data: (transfer full) (nullable): uniform data
*
* Creates a paintable that uses the @shader to create
* pixels. The shader must not require input textures.
* If @data is %NULL, all uniform values are set to zero.
*
* Returns: (transfer full): a new #GskShaderPaintable
*/
GdkPaintable *
gsk_shader_paintable_new (GskGLShader *shader,
GBytes *data)
{
GdkPaintable *ret;
g_return_val_if_fail (shader == NULL || GSK_IS_GL_SHADER (shader), NULL);
if (shader && !data)
{
int size = gsk_gl_shader_get_args_size (shader);
data = g_bytes_new_take (g_new0 (guchar, size), size);
}
ret = g_object_new (GSK_TYPE_SHADER_PAINTABLE,
"shader", shader,
"uniform-data", data,
NULL);
g_clear_object (&shader);
g_clear_pointer (&data, g_bytes_unref);
return ret;
}
/**
* gsk_shader_paintable_set_shader:
* @self: a #GskShaderPaintable
* @shader: the #GskGLShader to use
*
* Sets the shader that the paintable will use
* to create pixels. The shader must not require
* input textures.
*/
void
gsk_shader_paintable_set_shader (GskShaderPaintable *self,
GskGLShader *shader)
{
g_return_if_fail (GSK_IS_SHADER_PAINTABLE (self));
g_return_if_fail (shader == NULL || GSK_IS_GL_SHADER (shader));
g_return_if_fail (shader == NULL || gsk_gl_shader_get_n_required_textures (shader) == 0);
if (!g_set_object (&self->shader, shader))
return;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SHADER]);
gdk_paintable_invalidate_contents (GDK_PAINTABLE (self));
g_clear_pointer (&self->uniform_data, g_bytes_unref);
}
/**
* gsk_shader_paintable_get_shader:
* @self: a #GskShaderPaintable
*
* Returns the shader that the paintable is using.
*
* Returns: (transfer none): the #GskGLShader that is used
*/
GskGLShader *
gsk_shader_paintable_get_shader (GskShaderPaintable *self)
{
g_return_val_if_fail (GSK_IS_SHADER_PAINTABLE (self), NULL);
return self->shader;
}
/**
* gsk_shader_paintable_set_uniform_data:
* @self: a #GskShaderPaintable
* @data: Data block with uniform data for the shader
*
* Sets the uniform data that will be passed to the
* shader when rendering. The @data will typically
* be produced by a #GskUniformDataBuilder.
*
* Note that the @data should be considered immutable
* after it has been passed to this function.
*/
void
gsk_shader_paintable_set_uniform_data (GskShaderPaintable *self,
GBytes *data)
{
g_return_if_fail (GSK_IS_SHADER_PAINTABLE (self));
g_return_if_fail (data == NULL || g_bytes_get_size (data) == gsk_gl_shader_get_args_size (self->shader));
g_clear_pointer (&self->uniform_data, g_bytes_unref);
if (data)
self->uniform_data = g_bytes_ref (data);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_UNIFORM_DATA]);
gdk_paintable_invalidate_contents (GDK_PAINTABLE (self));
}
/**
* gsk_shader_paintable_get_uniform_data:
* @self: a #GskShaderPaintable
*
* Returns the uniform data set with
* gsk_shader_paintable_get_uniform_data().
*
* Returns: (transfer none): the uniform data
*/
GBytes *
gsk_shader_paintable_get_uniform_data (GskShaderPaintable *self)
{
g_return_val_if_fail (GSK_IS_SHADER_PAINTABLE (self), NULL);
return self->uniform_data;
}
/**
* gsk_shader_paintable_update_time:
* @self: a #GskShaderPaintable
* @time_idx: the index of the uniform for time in seconds as float
* @frame_time: the current frame time, as returned by #GdkFrameClock
*
* This function is a convenience wrapper for
* gsk_shader_paintable_set_uniform_data() that leaves all
* uniform values unchanged, except for the uniform with
* index @time_idx, which will be set to the elapsed time
* in seconds, since the first call to this function.
*
* This function is usually called from a #GtkTickCallback.
*/
void
gsk_shader_paintable_update_time (GskShaderPaintable *self,
int time_idx,
gint64 frame_time)
{
int size;
int offset;
guchar *data;
float time;
GBytes *uniform_data;
size = gsk_gl_shader_get_args_size (self->shader);
offset = gsk_gl_shader_get_uniform_offset (self->shader, time_idx);
data = g_new0 (guchar, size);
memcpy (data, g_bytes_get_data (self->uniform_data, NULL), size);
if (self->start_time == 0)
self->start_time = frame_time;
time = (frame_time - self->start_time) / (float)G_TIME_SPAN_SECOND;
*(float*)(data + offset) = time;
uniform_data = g_bytes_new_take (data, size);
gsk_shader_paintable_set_uniform_data (self, uniform_data);
g_bytes_unref (uniform_data);
}

View File

@@ -0,0 +1,53 @@
/*
* 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.1 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/>.
*
* Authors: Matthias Clasen <mclasen@redhat.com>
*/
#ifndef __GSK_SHADER_PAINTABLE_H__
#define __GSK_SHADER_PAINTABLE_H__
#include <gdk/gdk.h>
#include <gsk/gsk.h>
G_BEGIN_DECLS
#define GSK_TYPE_SHADER_PAINTABLE (gsk_shader_paintable_get_type ())
GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GskShaderPaintable, gsk_shader_paintable, GSK, SHADER_PAINTABLE, GObject)
GDK_AVAILABLE_IN_ALL
GdkPaintable * gsk_shader_paintable_new (GskGLShader *shader,
GBytes *data);
GDK_AVAILABLE_IN_ALL
GskGLShader * gsk_shader_paintable_get_shader (GskShaderPaintable *self);
GDK_AVAILABLE_IN_ALL
void gsk_shader_paintable_set_shader (GskShaderPaintable *self,
GskGLShader *shader);
GDK_AVAILABLE_IN_ALL
GBytes * gsk_shader_paintable_get_uniform_data (GskShaderPaintable *self);
GDK_AVAILABLE_IN_ALL
void gsk_shader_paintable_set_uniform_data (GskShaderPaintable *self,
GBytes *data);
GDK_AVAILABLE_IN_ALL
void gsk_shader_paintable_update_time (GskShaderPaintable *self,
int time_idx,
gint64 frame_time);
G_END_DECLS
#endif /* __GSK_SHADER_PAINTABLE_H__ */

View File

@@ -0,0 +1,241 @@
#include "gtkshaderbin.h"
typedef struct {
GskGLShader *shader;
GtkStateFlags state;
GtkStateFlags state_mask;
gboolean compiled;
gboolean compiled_ok;
} ShaderInfo;
struct _GtkShaderBin
{
GtkWidget parent_instance;
GtkWidget *child;
ShaderInfo *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));
ShaderInfo *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;
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)
{
if (!self->active_shader->compiled)
{
GtkNative *native = gtk_widget_get_native (widget);
GskRenderer *renderer = gtk_native_get_renderer (native);
GError *error = NULL;
self->active_shader->compiled = TRUE;
self->active_shader->compiled_ok =
gsk_gl_shader_try_compile_for (self->active_shader->shader,
renderer, &error);
if (!self->active_shader->compiled_ok)
{
g_warning ("GtkShaderBin failed to compile shader: %s", error->message);
g_error_free (error);
}
}
if (self->active_shader->compiled_ok)
{
gtk_snapshot_push_gl_shader (snapshot, self->active_shader->shader,
&GRAPHENE_RECT_INIT(0, 0, width, height),
gsk_gl_shader_format_args (self->active_shader->shader,
"u_time", &self->time,
NULL),
1);
gtk_widget_snapshot_child (widget, self->child, snapshot);
gtk_snapshot_pop (snapshot);
return;
}
}
/* Non-shader fallback */
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,353 @@
#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;
}
if (self->next != -1)
self->current = self->next;
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);
}
void
gtk_shader_stack_transition (GtkShaderStack *self,
gboolean forward)
{
stop_transition (self);
self->backwards = !forward;
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)
{
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;
}
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
{
GtkNative *native = gtk_widget_get_native (widget);
GskRenderer *renderer = gtk_native_get_renderer (native);
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;
}
if (gsk_gl_shader_try_compile_for (self->shader,
renderer, NULL))
{
gtk_snapshot_push_gl_shader (snapshot,
self->shader,
&GRAPHENE_RECT_INIT(0, 0, width, height),
gsk_gl_shader_format_args (self->shader,
"progress", &progress,
NULL),
2);
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 */
}
else
{
/* Non-shader fallback */
gtk_widget_snapshot_child (widget, current, snapshot);
}
}
}
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,21 @@
#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);
void gtk_shader_stack_transition (GtkShaderStack *self,
gboolean forward);
G_END_DECLS
#endif /* __GTK_SHADER_STACK_H__ */

View File

@@ -56,13 +56,15 @@ const char *fragment_prefix =
"uniform float iSampleRate; // sound sample rate (i.e., 44100)\n"
"\n"
"in vec2 fragCoord;\n"
"out vec4 fragColor;\n";
"out vec4 vFragColor;\n";
// Fragment shader suffix
const char *fragment_suffix =
" void main() {\n"
" mainImage(fragColor, fragCoord);\n"
" vec4 c;\n"
" mainImage(c, fragCoord);\n"
" vFragColor = c;\n"
" }\n";
typedef struct {

View File

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

View File

@@ -0,0 +1,33 @@
uniform float progress;
uniform sampler2D u_texture1;
uniform sampler2D u_texture2;
vec4 getFromColor (vec2 uv) {
return GskTexture(u_texture1, uv);
}
vec4 getToColor (vec2 uv) {
return GskTexture(u_texture2, uv);
}
// Source: https://gl-transitions.com/editor/wind
// Author: gre
// License: MIT
const 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,34 @@
uniform float progress;
uniform sampler2D u_texture1;
uniform sampler2D u_texture2;
vec4 getFromColor (vec2 uv) {
return GskTexture(u_texture1, uv);
}
vec4 getToColor (vec2 uv) {
return GskTexture(u_texture2, uv);
}
// Source: https://gl-transitions.com/editor/Radial
// License: MIT
// Author: Xaychru
const 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,27 @@
uniform float progress;
uniform sampler2D u_texture1;
uniform sampler2D u_texture2;
vec4 getFromColor (vec2 uv) {
return GskTexture(u_texture1, uv);
}
vec4 getToColor (vec2 uv) {
return GskTexture(u_texture2, 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,41 @@
uniform float progress;
uniform sampler2D u_texture1;
uniform sampler2D u_texture2;
vec4 getFromColor (vec2 uv) {
return GskTexture(u_texture1, uv);
}
vec4 getToColor (vec2 uv) {
return GskTexture(u_texture2, uv);
}
// Source: https://gl-transitions.com/editor/kaleidoscope
// Author: nwoeanhinnogaehr
// License: MIT
const float speed = 1.0;
const float angle = 1.0;
const 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

@@ -20,6 +20,7 @@
<xi:include href="xml/GskRenderNode.xml" />
<xi:include href="xml/GskRoundedRect.xml" />
<xi:include href="xml/GskTransform.xml" />
<xi:include href="xml/GskGLShader.xml" />
</reference>
<index id="api-index-full">

View File

@@ -48,6 +48,7 @@ GskShadowNode
GskTextNode
GskTextureNode
GskTransformNode
GskGLShaderNode
gsk_render_node_ref
gsk_render_node_unref
GskRenderNodeType
@@ -152,6 +153,12 @@ gsk_blur_node_get_radius
gsk_debug_node_new
gsk_debug_node_get_child
gsk_debug_node_get_message
gsk_gl_shader_node_new
gsk_gl_shader_node_get_fallback_child
gsk_gl_shader_node_get_n_children
gsk_gl_shader_node_get_child
gsk_gl_shader_node_get_uniform_data
gsk_gl_shader_node_get_shader
<SUBSECTION Standard>
GSK_IS_RENDER_NODE
GSK_RENDER_NODE
@@ -177,6 +184,7 @@ GSK_TYPE_SHADOW_NODE
GSK_TYPE_TEXT_NODE
GSK_TYPE_TEXTURE_NODE
GSK_TYPE_TRANSFORM_NODE
GSK_TYPE_GLSHADER_NODE
GskRenderNodeClass
gsk_blend_node_get_type
gsk_blur_node_get_type
@@ -202,6 +210,7 @@ gsk_shadow_node_get_type
gsk_text_node_get_type
gsk_texture_node_get_type
gsk_transform_node_get_type
gsk_gl_shader_node_get_type
GSK_TYPE_BLEND_MODE
<SUBSECTION Standard>
gsk_serialization_error_quark
@@ -266,3 +275,44 @@ gsk_transform_get_type
gsk_transform_new
</SECTION>
<SECTION>
<FILE>GskGLShader</FILE>
GskGLShader
gsk_gl_shader_new_from_bytes
gsk_gl_shader_new_from_resource
gsk_gl_shader_get_bytes
gsk_gl_shader_get_n_required_textures
gsk_gl_shader_get_n_uniforms
gsk_gl_shader_get_uniform_name
gsk_gl_shader_find_uniform_by_name
gsk_gl_shader_get_uniform_type
gsk_gl_shader_get_uniform_offset
gsk_gl_shader_get_args_size
<SUBSECTION Uniform Data>
gsk_gl_shader_get_arg_float
gsk_gl_shader_get_arg_int
gsk_gl_shader_get_arg_uint
gsk_gl_shader_get_arg_bool
gsk_gl_shader_get_arg_vec2
gsk_gl_shader_get_arg_vec3
gsk_gl_shader_get_arg_vec4
gsk_gl_shader_format_args_va
gsk_gl_shader_format_args
<SUBSECTION Shader Args Builder>
GskShaderArgsBuilder
gsk_gl_shader_build_args
gsk_shader_args_builder_finish
gsk_shader_args_builder_free
gsk_shader_args_builder_copy
gsk_shader_args_builder_set_float
gsk_shader_args_builder_set_int
gsk_shader_args_builder_set_uint
gsk_shader_args_builder_set_bool
gsk_shader_args_builder_set_vec2
gsk_shader_args_builder_set_vec3
gsk_shader_args_builder_set_vec4
</SECTION>

View File

@@ -1,2 +1,3 @@
gsk_render_node_get_type
gsk_renderer_get_type
gsk_gl_shader_get_type

View File

@@ -4311,6 +4311,7 @@ gtk_snapshot_push_blend
gtk_snapshot_push_blur
gtk_snapshot_push_shadow
gtk_snapshot_push_debug
gtk_snapshot_push_gl_shader
gtk_snapshot_pop
gtk_snapshot_save
gtk_snapshot_restore

View File

@@ -258,6 +258,7 @@ collect_reused_child_nodes (GskRenderer *renderer,
case GSK_LINEAR_GRADIENT_NODE:
/* Fallbacks (=> leaf for now */
case GSK_GL_SHADER_NODE:
case GSK_COLOR_MATRIX_NODE:
case GSK_TEXT_NODE:
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
@@ -847,6 +848,7 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer,
case GSK_BLEND_NODE:
case GSK_CROSS_FADE_NODE:
case GSK_BLUR_NODE:
case GSK_GL_SHADER_NODE:
default:
break; /* Fallback */
}

View File

@@ -1,6 +1,6 @@
#include "config.h"
#include "gskglrenderer.h"
#include "gskglrendererprivate.h"
#include "gskdebugprivate.h"
#include "gskenums.h"
@@ -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,12 @@ print_render_node_tree (GskRenderNode *root, int level)
print_render_node_tree (gsk_shadow_node_get_child (root), level + 1);
break;
case GSK_GL_SHADER_NODE:
g_print ("%*s GL Shader\n", level * INDENT, " ");
for (i = 0; i < gsk_gl_shader_node_get_n_children (root); i++)
print_render_node_tree (gsk_gl_shader_node_get_child (root, i), level + 1);
break;
case GSK_TEXTURE_NODE:
g_print ("%*s Texture %p\n", level * INDENT, " ", gsk_texture_node_get_texture (root));
break;
@@ -495,6 +507,40 @@ struct _GskGLRendererClass
G_DEFINE_TYPE (GskGLRenderer, gsk_gl_renderer, GSK_TYPE_RENDERER)
static void
init_shader_builder (GskGLRenderer *self,
GskGLShaderBuilder *shader_builder)
{
#ifdef G_ENABLE_DEBUG
if (GSK_RENDERER_DEBUG_CHECK (GSK_RENDERER (self), SHADERS))
shader_builder->debugging = TRUE;
#endif
if (gdk_gl_context_get_use_es (self->gl_context))
{
gsk_gl_shader_builder_set_glsl_version (shader_builder, SHADER_VERSION_GLES);
shader_builder->gles = TRUE;
}
else if (gdk_gl_context_is_legacy (self->gl_context))
{
int maj, min;
gdk_gl_context_get_version (self->gl_context, &maj, &min);
if (maj == 3)
gsk_gl_shader_builder_set_glsl_version (shader_builder, SHADER_VERSION_GL3_LEGACY);
else
gsk_gl_shader_builder_set_glsl_version (shader_builder, SHADER_VERSION_GL2_LEGACY);
shader_builder->legacy = TRUE;
}
else
{
gsk_gl_shader_builder_set_glsl_version (shader_builder, SHADER_VERSION_GL3);
shader_builder->gl3 = TRUE;
}
}
static void G_GNUC_UNUSED
add_rect_ops (RenderOpBuilder *builder,
const graphene_rect_t *r)
@@ -1006,6 +1052,178 @@ render_texture_node (GskGLRenderer *self,
}
}
static Program *
compile_glshader (GskGLRenderer *self,
GskGLShader *shader,
GError **error)
{
GskGLShaderBuilder shader_builder;
const char *shader_source;
gsize shader_source_len;
int n_uniforms;
const GskGLUniform *uniforms;
GBytes *bytes;
int n_required_textures = gsk_gl_shader_get_n_required_textures (shader);
int program_id;
Program *program;
bytes = gsk_gl_shader_get_bytes (shader);
shader_source = g_bytes_get_data (bytes, &shader_source_len);
uniforms = gsk_gl_shader_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));
return NULL;
}
if (n_required_textures > G_N_ELEMENTS (program->glshader.texture_locations))
{
g_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_UNSUPPORTED_FORMAT,
"GLShaderNode supports max %d texture sources", (int)(G_N_ELEMENTS (program->glshader.texture_locations)));
return NULL;
}
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, shader_source_len,
error);
gsk_gl_shader_builder_finish (&shader_builder);
if (program_id < 0)
return NULL;
program = gsk_gl_renderer_create_custom_program (self, shader);
program->id = program_id;
INIT_COMMON_UNIFORM_LOCATION (program, alpha);
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.texture_locations[0] = glGetUniformLocation(program->id, "u_texture1");
program->glshader.texture_locations[1] = glGetUniformLocation(program->id, "u_texture2");
program->glshader.texture_locations[2] = glGetUniformLocation(program->id, "u_texture3");
program->glshader.texture_locations[3] = glGetUniformLocation(program->id, "u_texture4");
/* We use u_textue1 for the texture 0 in the glshaders, so alias it here so we can use the regular setters */
program->source_location = program->glshader.texture_locations[0];
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);
/* This isn't necessary a hard error, you might declare uniforms that are not actually
always used, for instance if you have an "API" in uniforms for multiple shaders. */
if (program->glshader.args_locations[i] == -1)
g_debug ("Declared uniform `%s` not found in GskGLShader", uniforms[i].name);
}
else
program->glshader.args_locations[i] = -1;
}
return program;
}
gboolean
gsk_gl_render_try_compile_gl_shader (GskGLRenderer *self,
GskGLShader *shader,
GError **error)
{
Program *program;
gdk_gl_context_make_current (self->gl_context);
/* Maybe we tried to compile it already? */
program = gsk_gl_renderer_lookup_custom_program (self, shader);
if (program != NULL)
{
if (program->id > 0)
return TRUE;
else
{
g_propagate_error (error, g_error_copy (program->glshader.compile_error));
return FALSE;
}
}
program = compile_glshader (self, shader, error);
return program != NULL;
}
static inline void
render_gl_shader_node (GskGLRenderer *self,
GskRenderNode *node,
RenderOpBuilder *builder)
{
GskGLShader *shader = gsk_gl_shader_node_get_shader (node);
Program *program = gsk_gl_renderer_lookup_custom_program (self, shader);
int n_children = gsk_gl_shader_node_get_n_children (node);
if (program == NULL)
{
GError *error = NULL;
program = compile_glshader (self, shader, &error);
if (program == NULL)
{
/* We create the program anyway (in a failed state), so that any compiler warnings or other are only reported once */
program = gsk_gl_renderer_create_custom_program (self, shader);
program->id = -1;
program->glshader.compile_error = error;
g_warning ("Failed to compile gl shader: %s", error->message);
}
}
if (program->id >= 0 && n_children <= G_N_ELEMENTS (program->glshader.texture_locations))
{
GBytes *args;
TextureRegion regions[4];
gboolean is_offscreen[4];
for (guint i = 0; i < n_children; i++)
{
GskRenderNode *child = gsk_gl_shader_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))
return;
}
args = gsk_gl_shader_node_get_args (node);
ops_set_program (builder, program);
ops_set_gl_shader_args (builder, shader, node->bounds.size.width, node->bounds.size.height, g_bytes_get_data (args, NULL));
for (guint i = 0; i < n_children; i++)
{
if (i == 0)
ops_set_texture (builder, regions[i].texture_id);
else
ops_set_extra_texture (builder, regions[i].texture_id, i);
}
load_offscreen_vertex_data (ops_draw (builder, NULL), node, builder);
}
else
{
static GdkRGBA pink = { 255 / 255., 105 / 255., 180 / 255., 1.0 };
ops_set_program (builder, &self->programs->color_program);
ops_set_color (builder, &pink);
load_vertex_data (ops_draw (builder, NULL), node, builder);
}
}
/* Returns TRUE is applying transform to bounds
* yields an axis-aligned rectangle
*/
@@ -2671,6 +2889,17 @@ 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);
glUniform1i (program->glshader.texture_locations[op->idx], op->idx);
glActiveTexture (GL_TEXTURE0 + op->idx);
glBindTexture (GL_TEXTURE_2D, op->texture_id);
}
static inline void
apply_color_matrix_op (const Program *program,
const OpColorMatrix *op)
@@ -2815,6 +3044,51 @@ apply_border_op (const Program *program,
glUniform4fv (program->border.outline_rect_location, 3, (float *)&op->outline.bounds);
}
static inline void
apply_gl_shader_args_op (const Program *program,
const OpGLShader *op)
{
int n_uniforms, i;
const GskGLUniform *uniforms;
OP_PRINT (" -> GL Shader Args");
glUniform2fv (program->glshader.size_location, 1, op->size);
uniforms = gsk_gl_shader_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_GL_UNIFORM_TYPE_NONE:
break;
case GSK_GL_UNIFORM_TYPE_FLOAT:
glUniform1fv (program->glshader.args_locations[i], 1, (const float *)data);
break;
case GSK_GL_UNIFORM_TYPE_INT:
glUniform1iv (program->glshader.args_locations[i], 1, (const gint32 *)data);
break;
case GSK_GL_UNIFORM_TYPE_UINT:
case GSK_GL_UNIFORM_TYPE_BOOL:
glUniform1uiv (program->glshader.args_locations[i], 1, (const guint32 *) data);
break;
case GSK_GL_UNIFORM_TYPE_VEC2:
glUniform2fv (program->glshader.args_locations[i], 1, (const float *)data);
break;
case GSK_GL_UNIFORM_TYPE_VEC3:
glUniform3fv (program->glshader.args_locations[i], 1, (const float *)data);
break;
case GSK_GL_UNIFORM_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 +3160,32 @@ 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);
if (program->index == -1 &&
program->glshader.compile_error != NULL)
g_error_free (program->glshader.compile_error);
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 +3195,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 +3220,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 +3281,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 +3292,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, 0, error);
if (prog->id < 0)
{
g_clear_pointer (&programs, gsk_gl_renderer_programs_unref);
@@ -3445,6 +3737,10 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer *self,
render_repeat_node (self, node, builder);
break;
case GSK_GL_SHADER_NODE:
render_gl_shader_node (self, node, builder);
break;
case GSK_REPEATING_LINEAR_GRADIENT_NODE:
case GSK_REPEATING_RADIAL_GRADIENT_NODE:
case GSK_CAIRO_NODE:
@@ -3727,6 +4023,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 +4073,10 @@ gsk_gl_renderer_render_ops (GskGLRenderer *self)
apply_repeat_op (program, ptr);
break;
case OP_CHANGE_GL_SHADER_ARGS:
apply_gl_shader_args_op (program, ptr);
break;
case OP_DRAW:
{
const OpDraw *op = ptr;

View File

@@ -0,0 +1,14 @@
#ifndef __GSK_GL_RENDERER_PRIVATE_H__
#define __GSK_GL_RENDERER_PRIVATE_H__
#include "gskglrenderer.h"
G_BEGIN_DECLS
gboolean gsk_gl_render_try_compile_gl_shader (GskGLRenderer *self,
GskGLShader *shader,
GError **error);
G_END_DECLS
#endif /* __GSK_GL_RENDERER_PRIVATE_H__ */

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_gl_shader_args (RenderOpBuilder *builder,
GskGLShader *shader,
float width,
float height,
const guchar *uniform_data)
{
OpGLShader *op;
op = ops_begin (builder, OP_CHANGE_GL_SHADER_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,9 +31,60 @@ 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 */
int index; /* Into the renderer's program array -1 for custom */
int id;
/* Common locations (gl_common)*/
@@ -108,59 +159,16 @@ struct _Program
int child_bounds_location;
int texture_rect_location;
} repeat;
struct {
int size_location;
int args_locations[8];
int texture_locations[4];
GError *compile_error;
} 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 +191,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 +244,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 +263,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 +292,11 @@ void ops_set_inset_shadow (RenderOpBuilder *self,
const GdkRGBA *color,
float dx,
float dy);
void ops_set_gl_shader_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

@@ -39,6 +39,30 @@ gsk_gl_shader_builder_set_glsl_version (GskGLShaderBuilder *self,
self->version = version;
}
static void
prepend_line_numbers (char *code,
GString *s)
{
char *p;
int line;
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;
}
}
static gboolean
check_shader_error (int shader_id,
GError **error)
@@ -48,6 +72,7 @@ check_shader_error (int shader_id,
char *buffer;
int code_len;
char *code;
GString *s;
glGetShaderiv (shader_id, GL_COMPILE_STATUS, &status);
@@ -62,20 +87,50 @@ check_shader_error (int shader_id,
code = g_malloc0 (code_len + 1);
glGetShaderSource (shader_id, code_len, NULL, code);
s = g_string_new ("");
prepend_line_numbers (code, s);
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);
return FALSE;
}
static void
print_shader_info (const char *prefix,
int shader_id,
const char *resource_path)
{
if (GSK_DEBUG_CHECK(SHADERS))
{
int code_len;
char *code;
GString *s;
glGetShaderiv (shader_id, GL_SHADER_SOURCE_LENGTH, &code_len);
code = g_malloc0 (code_len + 1);
glGetShaderSource (shader_id, code_len, NULL, code);
s = g_string_new ("");
prepend_line_numbers (code, s);
g_message ("%s %d, %s:\n%s", prefix, shader_id, resource_path, s->str);
g_string_free (s, TRUE);
g_free (code);
}
}
int
gsk_gl_shader_builder_create_program (GskGLShaderBuilder *self,
const char *resource_path,
const char *extra_fragment_snippet,
gsize extra_fragment_length,
GError **error)
{
@@ -135,8 +190,10 @@ gsk_gl_shader_builder_create_program (GskGLShaderBuilder *self,
goto out;
}
print_shader_info ("Vertex shader", vertex_id, resource_path);
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 +202,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 +214,7 @@ gsk_gl_shader_builder_create_program (GskGLShaderBuilder *self,
-1,
-1,
-1,
extra_fragment_length,
});
glCompileShader (fragment_id);
@@ -165,6 +224,8 @@ gsk_gl_shader_builder_create_program (GskGLShaderBuilder *self,
goto out;
}
print_shader_info ("Fragment shader", vertex_id, resource_path);
program_id = glCreateProgram ();
glAttachShader (program_id, vertex_id);
glAttachShader (program_id, fragment_id);
@@ -188,6 +249,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,8 @@ 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,
gsize extra_fragment_length,
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_GL_SHADER_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

@@ -48,6 +48,7 @@
* @GSK_TEXT_NODE: A node containing a glyph string
* @GSK_BLUR_NODE: A node that applies a blur
* @GSK_DEBUG_NODE: Debug information that does not affect the rendering
* @GSK_GL_SHADER_NODE: A node that uses OpenGL fragment shaders to render
* The type of a node determines what the node is rendering.
*/
@@ -75,7 +76,8 @@ typedef enum {
GSK_CROSS_FADE_NODE,
GSK_TEXT_NODE,
GSK_BLUR_NODE,
GSK_DEBUG_NODE
GSK_DEBUG_NODE,
GSK_GL_SHADER_NODE
} GskRenderNodeType;
/**
@@ -218,4 +220,32 @@ typedef enum
GSK_TRANSFORM_CATEGORY_IDENTITY
} GskTransformCategory;
/**
* GskGLUniformType:
* @GSK_GL_UNIFORM_TYPE_NONE: No type, used for uninitialized or unspecified values.
* @GSK_GL_UNIFORM_TYPE_FLOAT: A float uniform
* @GSK_GL_UNIFORM_TYPE_INT: A GLSL int / gint32 uniform
* @GSK_GL_UNIFORM_TYPE_UINT: A GLSL uint / guint32 uniform
* @GSK_GL_UNIFORM_TYPE_BOOL: A GLSL bool / gboolean uniform
* @GSK_GL_UNIFORM_TYPE_VEC2: A GLSL vec2 / graphene_vec2_t uniform
* @GSK_GL_UNIFORM_TYPE_VEC3: A GLSL vec3 / graphene_vec3_t uniform
* @GSK_GL_UNIFORM_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_GL_UNIFORM_TYPE_NONE,
GSK_GL_UNIFORM_TYPE_FLOAT,
GSK_GL_UNIFORM_TYPE_INT,
GSK_GL_UNIFORM_TYPE_UINT,
GSK_GL_UNIFORM_TYPE_BOOL,
GSK_GL_UNIFORM_TYPE_VEC2,
GSK_GL_UNIFORM_TYPE_VEC3,
GSK_GL_UNIFORM_TYPE_VEC4,
} GskGLUniformType;
#endif /* __GSK_TYPES_H__ */

1266
gsk/gskglshader.c Normal file

File diff suppressed because it is too large Load Diff

158
gsk/gskglshader.h Normal file
View File

@@ -0,0 +1,158 @@
/* 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_GL_SHADER_H__
#define __GSK_GL_SHADER_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_SHADER_ARGS_BUILDER (gsk_shader_args_builder_get_type ())
typedef struct _GskShaderArgsBuilder GskShaderArgsBuilder;
#define GSK_TYPE_GL_SHADER (gsk_gl_shader_get_type ())
GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GskGLShader, gsk_gl_shader, GSK, GL_SHADER, GObject)
GDK_AVAILABLE_IN_ALL
GskGLShader * gsk_gl_shader_new_from_bytes (GBytes *sourcecode);
GDK_AVAILABLE_IN_ALL
GskGLShader * gsk_gl_shader_new_from_resource (const char *resource_path);
GDK_AVAILABLE_IN_ALL
gboolean gsk_gl_shader_try_compile_for (GskGLShader *shader,
GskRenderer *renderer,
GError **error);
GDK_AVAILABLE_IN_ALL
GBytes * gsk_gl_shader_get_bytes (GskGLShader *shader);
GDK_AVAILABLE_IN_ALL
int gsk_gl_shader_get_n_required_textures (GskGLShader *shader);
GDK_AVAILABLE_IN_ALL
int gsk_gl_shader_get_n_uniforms (GskGLShader *shader);
GDK_AVAILABLE_IN_ALL
const char * gsk_gl_shader_get_uniform_name (GskGLShader *shader,
int idx);
GDK_AVAILABLE_IN_ALL
int gsk_gl_shader_find_uniform_by_name (GskGLShader *shader,
const char *name);
GDK_AVAILABLE_IN_ALL
GskGLUniformType gsk_gl_shader_get_uniform_type (GskGLShader *shader,
int idx);
GDK_AVAILABLE_IN_ALL
int gsk_gl_shader_get_uniform_offset (GskGLShader *shader,
int idx);
GDK_AVAILABLE_IN_ALL
int gsk_gl_shader_get_args_size (GskGLShader *shader);
/* Helpers for managing shader args */
GDK_AVAILABLE_IN_ALL
GBytes * gsk_gl_shader_format_args_va (GskGLShader *shader,
va_list uniforms);
GDK_AVAILABLE_IN_ALL
GBytes * gsk_gl_shader_format_args (GskGLShader *shader,
...) G_GNUC_NULL_TERMINATED;
GDK_AVAILABLE_IN_ALL
float gsk_gl_shader_get_arg_float (GskGLShader *shader,
GBytes *args,
int idx);
GDK_AVAILABLE_IN_ALL
gint32 gsk_gl_shader_get_arg_int (GskGLShader *shader,
GBytes *args,
int idx);
GDK_AVAILABLE_IN_ALL
guint32 gsk_gl_shader_get_arg_uint (GskGLShader *shader,
GBytes *args,
int idx);
GDK_AVAILABLE_IN_ALL
gboolean gsk_gl_shader_get_arg_bool (GskGLShader *shader,
GBytes *args,
int idx);
GDK_AVAILABLE_IN_ALL
void gsk_gl_shader_get_arg_vec2 (GskGLShader *shader,
GBytes *args,
int idx,
graphene_vec2_t *out_value);
GDK_AVAILABLE_IN_ALL
void gsk_gl_shader_get_arg_vec3 (GskGLShader *shader,
GBytes *args,
int idx,
graphene_vec3_t *out_value);
GDK_AVAILABLE_IN_ALL
void gsk_gl_shader_get_arg_vec4 (GskGLShader *shader,
GBytes *args,
int idx,
graphene_vec4_t *out_value);
GDK_AVAILABLE_IN_ALL
GskShaderArgsBuilder *gsk_gl_shader_build_args (GskGLShader *shader);
GDK_AVAILABLE_IN_ALL
GType gsk_shader_args_builder_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GBytes * gsk_shader_args_builder_finish (GskShaderArgsBuilder *builder);
GDK_AVAILABLE_IN_ALL
void gsk_shader_args_builder_free (GskShaderArgsBuilder *builder);
GDK_AVAILABLE_IN_ALL
GskShaderArgsBuilder *gsk_shader_args_builder_copy (GskShaderArgsBuilder *builder);
GDK_AVAILABLE_IN_ALL
void gsk_shader_args_builder_set_float (GskShaderArgsBuilder *builder,
int idx,
float value);
GDK_AVAILABLE_IN_ALL
void gsk_shader_args_builder_set_int (GskShaderArgsBuilder *builder,
int idx,
gint32 value);
GDK_AVAILABLE_IN_ALL
void gsk_shader_args_builder_set_uint (GskShaderArgsBuilder *builder,
int idx,
guint32 value);
GDK_AVAILABLE_IN_ALL
void gsk_shader_args_builder_set_bool (GskShaderArgsBuilder *builder,
int idx,
gboolean value);
GDK_AVAILABLE_IN_ALL
void gsk_shader_args_builder_set_vec2 (GskShaderArgsBuilder *builder,
int idx,
const graphene_vec2_t *value);
GDK_AVAILABLE_IN_ALL
void gsk_shader_args_builder_set_vec3 (GskShaderArgsBuilder *builder,
int idx,
const graphene_vec3_t *value);
GDK_AVAILABLE_IN_ALL
void gsk_shader_args_builder_set_vec4 (GskShaderArgsBuilder *builder,
int idx,
const graphene_vec4_t *value);
G_END_DECLS
#endif /* __GSK_GL_SHADER_H__ */

19
gsk/gskglshaderprivate.h Normal file
View File

@@ -0,0 +1,19 @@
#ifndef __GSK_GLSHADER_PRIVATE_H__
#define __GSK_GLSHADER_PRIVATE_H__
#include <gsk/gskglshader.h>
G_BEGIN_DECLS
typedef struct {
char *name;
GskGLUniformType type;
gsize offset;
} GskGLUniform;
const GskGLUniform *gsk_gl_shader_get_uniforms (GskGLShader *shader,
int *n_uniforms);
G_END_DECLS
#endif /* __GSK_GLSHADER_PRIVATE_H__ */

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_GL_SHADER_NODE (gsk_gl_shader_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,24 @@ 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_gl_shader_node_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GskRenderNode * gsk_gl_shader_node_new (GskGLShader *shader,
const graphene_rect_t *bounds,
GBytes *args,
GskRenderNode **children,
int n_children);
GDK_AVAILABLE_IN_ALL
guint gsk_gl_shader_node_get_n_children (GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
GskRenderNode * gsk_gl_shader_node_get_child (GskRenderNode *node,
int idx);
GDK_AVAILABLE_IN_ALL
GBytes * gsk_gl_shader_node_get_args (GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
GskGLShader * gsk_gl_shader_node_get_shader (GskRenderNode *node);
G_END_DECLS
#endif /* __GSK_RENDER_NODE_H__ */

View File

@@ -4470,6 +4470,208 @@ gsk_debug_node_get_message (GskRenderNode *node)
return self->message;
}
/*** GSK_GL_SHADER_NODE ***/
struct _GskGLShaderNode
{
GskRenderNode render_node;
GskGLShader *shader;
GBytes *args;
GskRenderNode **children;
guint n_children;
};
static void
gsk_gl_shader_node_finalize (GskRenderNode *node)
{
GskGLShaderNode *self = (GskGLShaderNode *) node;
GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_GL_SHADER_NODE));
for (guint i = 0; i < self->n_children; i++)
gsk_render_node_unref (self->children[i]);
g_free (self->children);
g_bytes_unref (self->args);
g_object_unref (self->shader);
parent_class->finalize (node);
}
static void
gsk_gl_shader_node_draw (GskRenderNode *node,
cairo_t *cr)
{
cairo_set_source_rgb (cr, 255 / 255., 105 / 255., 180 / 255.);
gsk_cairo_rectangle (cr, &node->bounds);
cairo_fill (cr);
}
static void
gsk_gl_shader_node_diff (GskRenderNode *node1,
GskRenderNode *node2,
cairo_region_t *region)
{
GskGLShaderNode *self1 = (GskGLShaderNode *) node1;
GskGLShaderNode *self2 = (GskGLShaderNode *) node2;
if (graphene_rect_equal (&node1->bounds, &node2->bounds) &&
self1->shader == self2->shader &&
g_bytes_compare (self1->args, self2->args) == 0 &&
self1->n_children == self2->n_children)
{
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_gl_shader_node_new:
* @shader: the #GskGLShader
* @bounds: the rectangle to render the shader into
* @args: Arguments for the uniforms
* @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 @shader into the area
* given by @bounds. The @args 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_gl_shader_format_args() or #GskGLShaderArgBuilder.
*
* See #GskGLShader for details about how the shader should be written.
*
* All the children will be rendered into textures (if they aren't already
* #GskTextureNodes, which will be used directly). These textures will be
* sent as input to the shader.
*
* If the renderer doesn't support GL shaders, or if there is any problem when
* compiling the shader, then the node will draw pink. You should use
* gsk_gl_shader_try_compile_for() to ensure the @shader will work for the renderer
* before using it.
*
* Returns: (transfer full) (type GskGLShaderNode): A new #GskRenderNode
*/
GskRenderNode *
gsk_gl_shader_node_new (GskGLShader *shader,
const graphene_rect_t *bounds,
GBytes *args,
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_GL_SHADER_NODE);
node = (GskRenderNode *) self;
graphene_rect_init_from_rect (&node->bounds, bounds);
self->shader = g_object_ref (shader);
uniforms_size = gsk_gl_shader_get_args_size (shader);
g_assert (g_bytes_get_size (args) == uniforms_size);
self->args = g_bytes_ref (args);
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_gl_shader_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_gl_shader_node_get_n_children (GskRenderNode *node)
{
GskGLShaderNode *self = (GskGLShaderNode *) node;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_GL_SHADER_NODE), 0);
return self->n_children;
}
/**
* gsk_gl_shader_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_gl_shader_node_get_child (GskRenderNode *node,
int idx)
{
GskGLShaderNode *self = (GskGLShaderNode *) node;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_GL_SHADER_NODE), NULL);
g_return_val_if_fail (idx < self->n_children, NULL);
return self->children[idx];
}
/**
* gsk_gl_shader_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_gl_shader_node_get_shader (GskRenderNode *node)
{
GskGLShaderNode *self = (GskGLShaderNode *) node;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_GL_SHADER_NODE), 0);
return self->shader;
}
/**
* gsk_gl_shader_node_get_args:
* @node: (type GskGLShaderNode): a #GskRenderNode for a gl shader
*
* Gets args for the node.
*
* Returns: (transfer none): A #GBytes with the uniform arguments
*/
GBytes *
gsk_gl_shader_node_get_args (GskRenderNode *node)
{
GskGLShaderNode *self = (GskGLShaderNode *) node;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_GL_SHADER_NODE), NULL);
return self->args;
}
GType gsk_render_node_types[GSK_RENDER_NODE_TYPE_N_TYPES];
#ifndef I_
@@ -4506,6 +4708,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_gl_shader_node, GSK_GL_SHADER_NODE)
GSK_DEFINE_RENDER_NODE_TYPE (gsk_debug_node, GSK_DEBUG_NODE)
static void
@@ -4863,6 +5066,22 @@ gsk_render_node_init_types_once (void)
gsk_render_node_types[GSK_BLUR_NODE] = node_type;
}
{
const GskRenderNodeTypeInfo node_info =
{
GSK_GL_SHADER_NODE,
sizeof (GskGLShaderNode),
NULL,
gsk_gl_shader_node_finalize,
gsk_gl_shader_node_draw,
NULL,
gsk_gl_shader_node_diff,
};
GType node_type = gsk_render_node_type_register_static (I_("GskGLShaderNode"), &node_info);
gsk_render_node_types[GSK_GL_SHADER_NODE] = node_type;
}
{
const GskRenderNodeTypeInfo node_info =
{

View File

@@ -824,9 +824,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 +1089,233 @@ parse_inset_shadow_node (GtkCssParser *parser)
return gsk_inset_shadow_node_new (&outline, &color, dx, dy, spread, blur);
}
typedef union {
gint32 i;
double v[4];
} UniformValue;
typedef struct {
GskGLShader *shader;
GArray *uniform_values;
} ShaderInfo;
static void
clear_shader_info (gpointer data)
{
ShaderInfo *info = data;
g_array_set_size (info->uniform_values, 0);
}
static gboolean
parse_shader (GtkCssParser *parser,
gpointer out_shader_info)
{
ShaderInfo *shader_info = out_shader_info;
char *sourcecode = NULL;
GBytes *bytes;
GskGLShader *shader;
if (!parse_string (parser, &sourcecode))
return FALSE;
bytes = g_bytes_new_take (sourcecode, strlen (sourcecode));
shader = gsk_gl_shader_new_from_bytes (bytes);
g_bytes_unref (bytes);
shader_info->shader = shader;
return TRUE;
}
static gboolean
parse_uniform_value (GtkCssParser *parser,
GskGLUniformType uniform_type,
UniformValue *value)
{
switch (uniform_type)
{
case GSK_GL_UNIFORM_TYPE_FLOAT:
if (!gtk_css_parser_consume_number (parser, &value->v[0]))
return FALSE;
break;
case GSK_GL_UNIFORM_TYPE_INT:
if (!gtk_css_parser_consume_integer (parser, &value->i))
return FALSE;
break;
case GSK_GL_UNIFORM_TYPE_UINT:
if (!gtk_css_parser_consume_integer (parser, &value->i) ||
value->i < 0)
return FALSE;
break;
case GSK_GL_UNIFORM_TYPE_BOOL:
if (!gtk_css_parser_consume_integer (parser, &value->i) ||
(value->i != 0 && value->i != 1))
return FALSE;
break;
case GSK_GL_UNIFORM_TYPE_VEC2:
if (!gtk_css_parser_consume_number (parser, &value->v[0]) ||
!gtk_css_parser_consume_number (parser, &value->v[1]))
return FALSE;
break;
case GSK_GL_UNIFORM_TYPE_VEC3:
if (!gtk_css_parser_consume_number (parser, &value->v[0]) ||
!gtk_css_parser_consume_number (parser, &value->v[1]) ||
!gtk_css_parser_consume_number (parser, &value->v[2]))
return FALSE;
break;
case GSK_GL_UNIFORM_TYPE_VEC4:
if (!gtk_css_parser_consume_number (parser, &value->v[0]) ||
!gtk_css_parser_consume_number (parser, &value->v[1]) ||
!gtk_css_parser_consume_number (parser, &value->v[2]) ||
!gtk_css_parser_consume_number (parser, &value->v[3]))
return FALSE;
break;
case GSK_GL_UNIFORM_TYPE_NONE:
default:
g_assert_not_reached ();
break;
}
gtk_css_parser_try_token (parser, GTK_CSS_TOKEN_COMMA);
return TRUE;
}
static gboolean
parse_shader_args (GtkCssParser *parser, gpointer data)
{
ShaderInfo *shader_info = data;
int n_uniforms = gsk_gl_shader_get_n_uniforms (shader_info->shader);
int i;
g_array_set_size (shader_info->uniform_values, n_uniforms);
for (i = 0; i < n_uniforms; i++)
{
GskGLUniformType uniform_type = gsk_gl_shader_get_uniform_type (shader_info->shader, i);
UniformValue *uniform_value = &g_array_index (shader_info->uniform_values, UniformValue, i);
if (!parse_uniform_value (parser, uniform_type, uniform_value))
return FALSE;
}
return TRUE;
}
static GskRenderNode *
parse_glshader_node (GtkCssParser *parser)
{
graphene_rect_t bounds = GRAPHENE_RECT_INIT (0, 0, 50, 50);
GskRenderNode *child[4] = { NULL, };
ShaderInfo shader_info = {
NULL,
g_array_new (FALSE, FALSE, sizeof (UniformValue))
};
const Declaration declarations[] = {
{ "bounds", parse_rect, NULL, &bounds },
{ "sourcecode", parse_shader, NULL, &shader_info },
{ "uniform-data", parse_shader_args, clear_shader_info, &shader_info },
{ "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;
GskShaderArgsBuilder *builder;
GBytes *args = NULL;
int len, i;
parse_declarations (parser, declarations, G_N_ELEMENTS(declarations));
for (len = 0; len < 4; len++)
{
if (child[len] == NULL)
break;
}
shader = shader_info.shader;
builder = gsk_gl_shader_build_args (shader);
for (i = 0; i < shader_info.uniform_values->len; i++)
{
GskGLUniformType uniform_type = gsk_gl_shader_get_uniform_type (shader, i);
UniformValue *value = &g_array_index (shader_info.uniform_values, UniformValue, i);
switch (uniform_type)
{
case GSK_GL_UNIFORM_TYPE_FLOAT:
gsk_shader_args_builder_set_float (builder, i, value->v[0]);
break;
case GSK_GL_UNIFORM_TYPE_INT:
gsk_shader_args_builder_set_int (builder, i, value->i);
break;
case GSK_GL_UNIFORM_TYPE_UINT:
gsk_shader_args_builder_set_uint (builder, i, value->i);
break;
case GSK_GL_UNIFORM_TYPE_BOOL:
gsk_shader_args_builder_set_bool (builder, i, value->i);
break;
case GSK_GL_UNIFORM_TYPE_VEC2:
{
graphene_vec2_t v;
graphene_vec2_init (&v, value->v[0], value->v[1]);
gsk_shader_args_builder_set_vec2 (builder, i, &v);
}
break;
case GSK_GL_UNIFORM_TYPE_VEC3:
{
graphene_vec3_t v;
graphene_vec3_init (&v, value->v[0], value->v[1], value->v[2]);
gsk_shader_args_builder_set_vec3 (builder, i, &v);
}
break;
case GSK_GL_UNIFORM_TYPE_VEC4:
{
graphene_vec4_t v;
graphene_vec4_init (&v, value->v[0], value->v[1], value->v[2], value->v[3]);
gsk_shader_args_builder_set_vec4 (builder, i, &v);
}
break;
case GSK_GL_UNIFORM_TYPE_NONE:
default:
g_assert_not_reached ();
}
}
args = gsk_shader_args_builder_finish (builder);
gsk_shader_args_builder_free (builder);
node = gsk_gl_shader_node_new (shader, &bounds, args,
child, len);
g_array_unref (shader_info.uniform_values);
g_bytes_unref (args);
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 +1830,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 +2065,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)
@@ -1914,6 +2187,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 +2726,124 @@ render_node_print (Printer *p,
}
break;
case GSK_GL_SHADER_NODE:
{
GskGLShader *shader = gsk_gl_shader_node_get_shader (node);
GBytes *args = gsk_gl_shader_node_get_args (node);
start_node (p, "glshader");
append_rect_param (p, "bounds", &node->bounds);
GBytes *bytes = gsk_gl_shader_get_bytes (shader);
/* Ensure we are zero-terminated */
char *sourcecode = g_strndup (g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes));
append_string_param (p, "sourcecode", sourcecode);
g_free (sourcecode);
if (gsk_gl_shader_get_n_uniforms (shader) > 0)
{
GString *data = g_string_new ("");
for (guint i = 0; i < gsk_gl_shader_get_n_uniforms (shader); i++)
{
if (i > 0)
g_string_append (data, ", ");
switch (gsk_gl_shader_get_uniform_type (shader, i))
{
case GSK_GL_UNIFORM_TYPE_NONE:
default:
g_assert_not_reached ();
break;
case GSK_GL_UNIFORM_TYPE_FLOAT:
{
float value = gsk_gl_shader_get_arg_float (shader, args, i);
string_append_double (data, value);
}
break;
case GSK_GL_UNIFORM_TYPE_INT:
{
gint32 value = gsk_gl_shader_get_arg_int (shader, args, i);
g_string_append_printf (data, "%d", value);
}
break;
case GSK_GL_UNIFORM_TYPE_UINT:
{
guint32 value = gsk_gl_shader_get_arg_uint (shader, args, i);
g_string_append_printf (data, "%u", value);
}
break;
case GSK_GL_UNIFORM_TYPE_BOOL:
{
gboolean value = gsk_gl_shader_get_arg_bool (shader, args, i);
g_string_append_printf (data, "%d", value);
}
break;
case GSK_GL_UNIFORM_TYPE_VEC2:
{
graphene_vec2_t value;
gsk_gl_shader_get_arg_vec2 (shader, args, i,
&value);
string_append_double (data, graphene_vec2_get_x (&value));
g_string_append (data, " ");
string_append_double (data, graphene_vec2_get_y (&value));
}
break;
case GSK_GL_UNIFORM_TYPE_VEC3:
{
graphene_vec3_t value;
gsk_gl_shader_get_arg_vec3 (shader, args, i,
&value);
string_append_double (data, graphene_vec3_get_x (&value));
g_string_append (data, " ");
string_append_double (data, graphene_vec3_get_y (&value));
g_string_append (data, " ");
string_append_double (data, graphene_vec3_get_z (&value));
}
break;
case GSK_GL_UNIFORM_TYPE_VEC4:
{
graphene_vec4_t value;
gsk_gl_shader_get_arg_vec4 (shader, args, i,
&value);
string_append_double (data, graphene_vec4_get_x (&value));
g_string_append (data, " ");
string_append_double (data, graphene_vec4_get_y (&value));
g_string_append (data, " ");
string_append_double (data, graphene_vec4_get_z (&value));
g_string_append (data, " ");
string_append_double (data, graphene_vec4_get_w (&value));
}
break;
}
}
_indent (p);
g_string_append_printf (p->str, "uniform-data: %s;\n", data->str);
g_string_free (data, TRUE);
}
for (guint i = 0; i < gsk_gl_shader_node_get_n_children (node); i ++)
{
GskRenderNode *child = gsk_gl_shader_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_GL_SHADER_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(gsk_premultiply(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

@@ -256,6 +256,7 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self,
case GSK_NOT_A_RENDER_NODE:
g_assert_not_reached ();
return;
case GSK_GL_SHADER_NODE:
case GSK_SHADOW_NODE:
case GSK_RADIAL_GRADIENT_NODE:
case GSK_REPEATING_RADIAL_GRADIENT_NODE:

View File

@@ -91,6 +91,14 @@ struct _GtkSnapshotState {
struct {
graphene_rect_t bounds;
} clip;
struct {
GskGLShader *shader;
GBytes *args;
graphene_rect_t bounds;
int n_children;
int node_idx;
GskRenderNode **nodes;
} glshader;
struct {
GskRoundedRect bounds;
} rounded_clip;
@@ -812,6 +820,119 @@ gtk_snapshot_push_clip (GtkSnapshot *snapshot,
gtk_graphene_rect_scale_affine (bounds, scale_x, scale_y, dx, dy, &state->data.clip.bounds);
}
static GskRenderNode *
gtk_snapshot_collect_gl_shader (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 - 1)
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)
{
shader_node = gsk_gl_shader_node_new (state->data.glshader.shader,
&state->data.glshader.bounds,
state->data.glshader.args,
&state->data.glshader.nodes[0],
state->data.glshader.n_children);
}
g_object_unref (state->data.glshader.shader);
g_bytes_unref (state->data.glshader.args);
for (guint i = 0; i < state->data.glshader.n_children; i++)
gsk_render_node_unref (state->data.glshader.nodes[i]);
g_free (state->data.glshader.nodes);
return shader_node;
}
/**
* gtk_snapshot_push_gl_shader:
* @snapshot: a #GtkSnapshot
* @shader: The code to run
* @bounds: the rectangle to render into
* @take_args: (transfer full): Data block with arguments for the shader.
* @n_children: The number of extra nodes given as argument to the shader as textures.
*
* Push a #GskGLShaderNode with a specific #GskGLShader and a set of uniform values
* to use while rendering. Additionally this takes a list of @n_children other nodes
* which will be passed to the #GskGLShaderNode.
*
* The @take_args argument is a block of data to use for uniform
* input, as per types and offsets defined by the @shader. Normally this is
* generated by gsk_gl_shader_format_args() or #GskGLShaderArgBuilder.
* The snapshotter takes ownership of @take_args, so the caller should not free it
* after this.
*
* If the renderer doesn't support GL shaders, or if there is any problem when
* compiling the shader, then the node will draw pink. You should use
* gsk_gl_shader_try_compile_for() to ensure the @shader will work for the renderer
* before using it.
*
* 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 added as a child to the gl shader 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_gl_shader (GtkSnapshot *snapshot,
GskGLShader *shader,
const graphene_rect_t *bounds,
GBytes *take_args,
int n_children)
{
GtkSnapshotState *state;
float scale_x, scale_y, dx, dy;
GskRenderNode **nodes;
int node_idx;
graphene_rect_t transformed_bounds;
gtk_snapshot_ensure_affine (snapshot, &scale_x, &scale_y, &dx, &dy);
state = gtk_snapshot_push_state (snapshot,
gtk_snapshot_get_current_state (snapshot)->transform,
gtk_snapshot_collect_gl_shader);
gtk_graphene_rect_scale_affine (bounds, scale_x, scale_y, dx, dy, &transformed_bounds);
state->data.glshader.bounds = transformed_bounds;
state->data.glshader.shader = g_object_ref (shader);
state->data.glshader.args = take_args; /* Takes ownership */
state->data.glshader.n_children = n_children;
nodes = g_new0 (GskRenderNode *, n_children);
node_idx = n_children-1; /* We pop in reverse order */
state->data.glshader.node_idx = node_idx--;
state->data.glshader.nodes = nodes;
for (int i = 0; i < n_children-1; i++)
{
state = gtk_snapshot_push_state (snapshot,
gtk_snapshot_get_current_state (snapshot)->transform,
gtk_snapshot_collect_gl_shader);
state->data.glshader.node_idx = node_idx--;
state->data.glshader.n_children = n_children;
state->data.glshader.nodes = nodes;
state->data.glshader.bounds = transformed_bounds;
}
}
static GskRenderNode *
gtk_snapshot_collect_rounded_clip (GtkSnapshot *snapshot,
GtkSnapshotState *state,

View File

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

View File

@@ -171,6 +171,25 @@ create_list_model_for_render_node (GskRenderNode *node)
return create_render_node_list_model ((GskRenderNode *[2]) { gsk_cross_fade_node_get_start_child (node),
gsk_cross_fade_node_get_end_child (node) }, 2);
case GSK_GL_SHADER_NODE:
{
GListStore *store = g_list_store_new (GDK_TYPE_PAINTABLE);
for (guint i = 0; i < gsk_gl_shader_node_get_n_children (node); i++)
{
GskRenderNode *child = gsk_gl_shader_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_GL_SHADER_NODE:
return "GL Shader";
}
}
@@ -301,6 +322,7 @@ node_name (GskRenderNode *node)
case GSK_CROSS_FADE_NODE:
case GSK_TEXT_NODE:
case GSK_BLUR_NODE:
case GSK_GL_SHADER_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,92 @@ populate_render_node_properties (GtkListStore *store,
add_float_row (store, "Radius", gsk_blur_node_get_radius (node));
break;
case GSK_GL_SHADER_NODE:
{
GskGLShader *shader = gsk_gl_shader_node_get_shader (node);
GBytes *args = gsk_gl_shader_node_get_args (node);
int i;
add_int_row (store, "Required textures", gsk_gl_shader_get_n_required_textures (shader));
for (i = 0; i < gsk_gl_shader_get_n_uniforms (shader); i++)
{
const char *name;
char *title;
name = gsk_gl_shader_get_uniform_name (shader, i);
title = g_strdup_printf ("Uniform %s", name);
switch (gsk_gl_shader_get_uniform_type (shader, i))
{
case GSK_GL_UNIFORM_TYPE_NONE:
default:
g_assert_not_reached ();
break;
case GSK_GL_UNIFORM_TYPE_FLOAT:
add_float_row (store, title,
gsk_gl_shader_get_arg_float (shader, args, i));
break;
case GSK_GL_UNIFORM_TYPE_INT:
add_int_row (store, title,
gsk_gl_shader_get_arg_int (shader, args, i));
break;
case GSK_GL_UNIFORM_TYPE_UINT:
add_uint_row (store, title,
gsk_gl_shader_get_arg_uint (shader, args, i));
break;
case GSK_GL_UNIFORM_TYPE_BOOL:
add_boolean_row (store, title,
gsk_gl_shader_get_arg_bool (shader, args, i));
break;
case GSK_GL_UNIFORM_TYPE_VEC2:
{
graphene_vec2_t v;
gsk_gl_shader_get_arg_vec2 (shader, args, i, &v);
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_GL_UNIFORM_TYPE_VEC3:
{
graphene_vec3_t v;
gsk_gl_shader_get_arg_vec3 (shader, args, i, &v);
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_GL_UNIFORM_TYPE_VEC4:
{
graphene_vec4_t v;
gsk_gl_shader_get_arg_vec4 (shader, args, i, &v);
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);

View File

@@ -188,6 +188,7 @@ endforeach
tests = [
['rounded-rect'],
['transform'],
['shader'],
]
test_cargs = []

215
testsuite/gsk/shader.c Normal file
View File

@@ -0,0 +1,215 @@
/*
* 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.1 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/>.
*
* Authors: Matthias Clasen <mclasen@redhat.com>
*/
#include <gtk/gtk.h>
/* shader fragment as found in nature */
const char shader0[] =
"// author: bobylito\n"
"// license: MIT\n"
"const float SQRT_2 = 1.414213562373;"
"uniform float dots;// = 20.0;"
"uniform vec2 center; //= vec2(0, 0);"
""
"vec4 transition(vec2 uv) {"
" bool nextImage = distance(fract(uv * dots), vec2(0.5, 0.5)) < ( progress / distance(uv, center));"
" return nextImage ? getToColor(uv) : getFromColor(uv);"
"}";
/* Same shader, with our preamble added, and with newlines
* to make the regex happy. Added a variety of uniforms to
* exercise the parser.
*/
const char shader1[] =
"uniform float progress;\n"
"uniform sampler2D u_texture1;\n"
"uniform sampler2D u_texture2;\n"
""
"vec4 getFromColor (vec2 uv) {\n"
" return GskTexture(u_texture1, uv);\n"
"}\n"
"\n"
"vec4 getToColor (vec2 uv) {\n"
" return GskTexture(u_texture2, uv);\n"
"}\n"
"\n"
"// author: bobylito\n"
"// license: MIT\n"
"const float SQRT_2 = 1.414213562373;\n"
"uniform float dots;// = 20.0;\n"
"uniform vec2 center; //= vec2(0, 0);\n"
"\n"
"uniform int test1 = -2;\n"
"uniform uint test2 = 2; \n"
"uniform bool test3;\n"
"uniform vec3 test4;\n"
"uniform vec4 test5;\n"
"\n"
"vec4 transition(vec2 uv) {\n"
" bool nextImage = distance(fract(uv * dots), vec2(0.5, 0.5)) < ( progress / distance(uv, center));\n"
" return nextImage ? getToColor(uv) : getFromColor(uv);\n"
"}\n"
"\n"
"void mainImage(out vec4 fragColor, in vec2 fragCoord, in vec2 resolution, in vec2 uv)\n"
"{\n"
" fragColor = transition(uv);\n"
"}\n";
static void
test_create_simple (void)
{
GBytes *bytes;
GskGLShader *shader;
bytes = g_bytes_new_static (shader1, sizeof (shader1));
shader = gsk_gl_shader_new_from_bytes (bytes);
g_assert_nonnull (shader);
g_assert_cmpint (gsk_gl_shader_get_n_required_textures (shader), ==, 2);
g_assert_cmpint (gsk_gl_shader_get_n_uniforms (shader), ==, 8);
g_assert_cmpstr (gsk_gl_shader_get_uniform_name (shader, 0), ==, "progress");
g_assert_cmpint (gsk_gl_shader_get_uniform_type (shader, 0), ==, GSK_GL_UNIFORM_TYPE_FLOAT);
g_assert_cmpstr (gsk_gl_shader_get_uniform_name (shader, 1), ==, "dots");
g_assert_cmpint (gsk_gl_shader_get_uniform_type (shader, 1), ==, GSK_GL_UNIFORM_TYPE_FLOAT);
g_assert_cmpstr (gsk_gl_shader_get_uniform_name (shader, 2), ==, "center");
g_assert_cmpint (gsk_gl_shader_get_uniform_type (shader, 2), ==, GSK_GL_UNIFORM_TYPE_VEC2);
g_assert_cmpstr (gsk_gl_shader_get_uniform_name (shader, 3), ==, "test1");
g_assert_cmpint (gsk_gl_shader_get_uniform_type (shader, 3), ==, GSK_GL_UNIFORM_TYPE_INT);
g_assert_cmpstr (gsk_gl_shader_get_uniform_name (shader, 4), ==, "test2");
g_assert_cmpint (gsk_gl_shader_get_uniform_type (shader, 4), ==, GSK_GL_UNIFORM_TYPE_UINT);
g_assert_cmpstr (gsk_gl_shader_get_uniform_name (shader, 5), ==, "test3");
g_assert_cmpint (gsk_gl_shader_get_uniform_type (shader, 5), ==, GSK_GL_UNIFORM_TYPE_BOOL);
g_assert_cmpstr (gsk_gl_shader_get_uniform_name (shader, 6), ==, "test4");
g_assert_cmpint (gsk_gl_shader_get_uniform_type (shader, 6), ==, GSK_GL_UNIFORM_TYPE_VEC3);
g_assert_cmpstr (gsk_gl_shader_get_uniform_name (shader, 7), ==, "test5");
g_assert_cmpint (gsk_gl_shader_get_uniform_type (shader, 7), ==, GSK_GL_UNIFORM_TYPE_VEC4);
g_object_unref (shader);
g_bytes_unref (bytes);
}
static void
test_create_data (void)
{
GBytes *bytes;
GskGLShader *shader;
GskShaderArgsBuilder *builder;
graphene_vec2_t v2, vv2;
graphene_vec3_t v3, vv3;
graphene_vec4_t v4, vv4;
bytes = g_bytes_new_static (shader1, sizeof (shader1));
shader = gsk_gl_shader_new_from_bytes (bytes);
g_assert_nonnull (shader);
g_clear_pointer (&bytes, g_bytes_unref);
builder = gsk_gl_shader_build_args (shader);
g_assert_nonnull (builder);
graphene_vec2_init (&v2, 20, 30);
graphene_vec3_init (&v3, -1, -2, -3);
graphene_vec4_init (&v4, 100, 0, -100, 10);
gsk_shader_args_builder_set_float (builder, 0, 0.5);
gsk_shader_args_builder_set_float (builder, 1, 20.0);
gsk_shader_args_builder_set_vec2 (builder, 2, &v2);
gsk_shader_args_builder_set_int (builder, 3, -99);
gsk_shader_args_builder_set_uint (builder, 4, 99);
gsk_shader_args_builder_set_bool (builder, 5, 1);
gsk_shader_args_builder_set_vec3 (builder, 6, &v3);
gsk_shader_args_builder_set_vec4 (builder, 7, &v4);
bytes = gsk_shader_args_builder_finish (builder);
g_assert_cmpfloat (gsk_gl_shader_get_arg_float (shader, bytes, 0), ==, 0.5);
g_assert_cmpfloat (gsk_gl_shader_get_arg_float (shader, bytes, 1), ==, 20.0);
gsk_gl_shader_get_arg_vec2 (shader, bytes, 2, &vv2);
g_assert_true (graphene_vec2_equal (&v2, &vv2));
g_assert_cmpint (gsk_gl_shader_get_arg_int (shader, bytes, 3), ==, -99);
g_assert_cmpuint (gsk_gl_shader_get_arg_uint (shader, bytes, 4), ==, 99);
g_assert_cmpint (gsk_gl_shader_get_arg_bool (shader, bytes, 5), ==, 1);
gsk_gl_shader_get_arg_vec3 (shader, bytes, 6, &vv3);
g_assert_true (graphene_vec3_equal (&v3, &vv3));
gsk_gl_shader_get_arg_vec4 (shader, bytes, 7, &vv4);
g_assert_true (graphene_vec4_equal (&v4, &vv4));
g_bytes_unref (bytes);
gsk_shader_args_builder_free (builder);
g_object_unref (shader);
}
static void
test_format_args (void)
{
GBytes *bytes;
GskGLShader *shader;
graphene_vec2_t v2, vv2;
graphene_vec3_t v3, vv3;
graphene_vec4_t v4, vv4;
float f1, f2;
GBytes *args;
bytes = g_bytes_new_static (shader1, sizeof (shader1));
shader = gsk_gl_shader_new_from_bytes (bytes);
g_assert_nonnull (shader);
g_clear_pointer (&bytes, g_bytes_unref);
graphene_vec2_init (&v2, 20, 30);
graphene_vec3_init (&v3, -1, -2, -3);
graphene_vec4_init (&v4, 100, 0, -100, 10);
f1 = 0.5;
f2 = 20.0;
args = gsk_gl_shader_format_args (shader,
"progress", &f1,
"dots", &f2,
"center", &v2,
"test4", &v3,
"test5", &v4,
NULL);
g_assert_cmpfloat (gsk_gl_shader_get_arg_float (shader, args, 0), ==, 0.5);
g_assert_cmpfloat (gsk_gl_shader_get_arg_float (shader, args, 1), ==, 20.0);
gsk_gl_shader_get_arg_vec2 (shader, args, 2, &vv2);
g_assert_true (graphene_vec2_equal (&v2, &vv2));
gsk_gl_shader_get_arg_vec3 (shader, args, 6, &vv3);
g_assert_true (graphene_vec3_equal (&v3, &vv3));
gsk_gl_shader_get_arg_vec4 (shader, args, 7, &vv4);
g_assert_true (graphene_vec4_equal (&v4, &vv4));
g_bytes_unref (args);
g_object_unref (shader);
}
int
main (int argc,
char *argv[])
{
gtk_test_init (&argc, &argv, NULL);
g_test_add_func ("/shader/create/simple", test_create_simple);
g_test_add_func ("/shader/create/data", test_create_data);
g_test_add_func ("/shader/format-args", test_format_args);
return g_test_run ();
}