Compare commits

...

5 Commits

Author SHA1 Message Date
Matthias Clasen
984d257d1a Add an example shader
fire.frag shows how one might do a transition
between two children.
2017-10-03 18:03:08 -04:00
Matthias Clasen
d850ca03f8 A shadertoy frontend
This adds a very preliminary shadertoy example to gtk4-demo.
You can write a shader, and start and stop running it (this
is animated, since we pass a time parameter to the shader).
2017-10-03 18:03:08 -04:00
Matthias Clasen
0b8d58ca3d Export more snapshot api
We currently don't export enough api to do custom snapshots
outside libgtk. To demonstrate this in gtk4-demo, export
the necessary api here.
2017-10-03 18:03:08 -04:00
Matthias Clasen
6d8eb2441a inspector: learn about new nodes
Whenever we add a new kind of render node, the inspector needs
to be updated, or it crashes. Somewhat annoying.
2017-10-03 18:03:08 -04:00
Matthias Clasen
2905e9dbcc Support custom shaders in vulkan
Initial work for letting widgets and eventually applications
provide their own shaders. For now, we only have a limited set
of inputs: the rectangle, a time parameter and up to two
children which are passed as textures to the shader.
2017-10-03 18:03:08 -04:00
31 changed files with 1468 additions and 25 deletions

View File

@@ -187,6 +187,7 @@
<file>scale.c</file>
<file>search_entry.c</file>
<file>search_entry2.c</file>
<file>shadertoy.c</file>
<file>shortcuts.c</file>
<file>sizegroup.c</file>
<file>sidebar.c</file>
@@ -241,4 +242,7 @@
<gresource prefix="/modelbutton">
<file>modelbutton.ui</file>
</gresource>
<gresource prefix="/shadertoy">
<file>shadertoy.ui</file>
</gresource>
</gresources>

87
demos/gtk-demo/fire.frag Normal file
View File

@@ -0,0 +1,87 @@
#version 420 core
layout(location = 0) in vec2 iPos;
layout(location = 1) in vec2 iTexCoord1;
layout(location = 2) in vec2 iTexCoord2;
layout(location = 3) in float iTime;
layout(location = 4) in vec2 iResolution;
layout(set = 0, binding = 0) uniform sampler2D iTexture1;
layout(set = 1, binding = 0) uniform sampler2D iTexture2;
layout(location = 0) out vec4 color;
// based on http://glslsandbox.com/e#35642.0
vec3 TextureSource(vec2 uv)
{
return texture(iTexture1, uv).rgb;;
}
vec3 TextureTarget(vec2 uv)
{
return texture(iTexture2, uv).rgb;;
}
float Hash( vec2 p)
{
vec3 p2 = vec3(p.xy,1.0);
return fract(sin(dot(p2,vec3(37.1,61.7, 12.4)))*3758.5453123);
}
float noise(in vec2 p)
{
vec2 i = floor(p);
vec2 f = fract(p);
f *= f * (3.0-2.0*f);
return mix(mix(Hash(i + vec2(0.,0.)), Hash(i + vec2(1.,0.)),f.x),
mix(Hash(i + vec2(0.,1.)), Hash(i + vec2(1.,1.)),f.x),
f.y);
}
float fbm(vec2 p)
{
float v = 0.0;
v += noise(p*1.)*.5;
v += noise(p*2.)*.25;
v += noise(p*4.)*.125;
return v;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = iTexCoord1;
vec3 src = TextureSource(uv);
vec3 tgt = TextureTarget(uv);
vec3 col = src;
uv.x -= 1.5;
if (iTime <= 0) {
fragColor.rgb = src.rgb;
return;
}
else if (iTime > 5) {
fragColor.rgb = tgt.rgb;
return;
}
float ctime = iTime*.5;
// burn
float d = uv.x+uv.y*0.5 + 0.5*fbm(uv*15.1) + ctime*1.3;
if (d >0.35) col = clamp(col-(d-0.35)*10.,0.0,1.0);
if (d >0.47) {
if (d < 0.5 ) col += (d-0.4)*33.0*0.5*(0.0+noise(100.*uv+vec2(-ctime*2.,0.)))*vec3(1.5,0.5,0.0);
else col += tgt; }
fragColor.rgb = col;
}
void main()
{
mainImage (color, iPos);
}

View File

@@ -54,6 +54,7 @@ demos = files([
'scale.c',
'search_entry.c',
'search_entry2.c',
'shadertoy.c',
'shortcuts.c',
'sidebar.c',
'sizegroup.c',

567
demos/gtk-demo/shadertoy.c Normal file
View File

@@ -0,0 +1,567 @@
/* Shadertoy
*
* Play with shaders. Everybody does it.
*/
#include <gtk/gtk.h>
enum {
PROP_0,
PROP_TIME,
PROP_RUNNING,
N_PROPS
};
G_DECLARE_FINAL_TYPE (GtkShadertoy, gtk_shadertoy, GTK, SHADERTOY, GtkWidget)
struct _GtkShadertoy {
GtkWidget parent_instance;
GBytes *fragment_shader;
GtkWidget *child1;
GtkWidget *child2;
guint tick;
guint64 starttime;
float time;
float base;
};
G_DEFINE_TYPE (GtkShadertoy, gtk_shadertoy, GTK_TYPE_WIDGET);
static void
gtk_shadertoy_snapshot (GtkWidget *widget,
GtkSnapshot *snapshot)
{
GtkShadertoy *self = GTK_SHADERTOY (widget);
GtkAllocation alloc;
graphene_rect_t bounds;
int offset_x, offset_y;
float time = (float)(g_get_monotonic_time () - self->starttime)/1000000.0;
GskRenderNode *node, *child1, *child2;
GtkAllocation child_alloc;
GtkSnapshot *child_snapshot;
if (!self->fragment_shader)
{
GTK_WIDGET_CLASS (gtk_shadertoy_parent_class)->snapshot (widget, snapshot);
return;
}
gtk_snapshot_get_offset (snapshot, &offset_x, &offset_y);
gtk_widget_get_allocation (widget, &alloc);
bounds.origin.x = offset_x;
bounds.origin.y = offset_y;
bounds.size.width = alloc.width;
bounds.size.height = alloc.height;
gtk_widget_get_allocation (self->child1, &child_alloc);
child_snapshot = gtk_snapshot_new (snapshot, "shadertoy child1");
gtk_snapshot_offset (child_snapshot, offset_x + child_alloc.x, offset_y + child_alloc.y);
gtk_widget_snapshot (self->child1, child_snapshot);
gtk_snapshot_offset (child_snapshot, - offset_x - child_alloc.x, - offset_y - child_alloc.y);
child1 = gtk_snapshot_free (child_snapshot);
gtk_widget_get_allocation (self->child2, &child_alloc);
child_snapshot = gtk_snapshot_new (snapshot, "shadertoy child2");
gtk_snapshot_offset (child_snapshot, offset_x + child_alloc.x, offset_y + child_alloc.y);
gtk_widget_snapshot (self->child2, child_snapshot);
gtk_snapshot_offset (child_snapshot, - offset_x - child_alloc.x, - offset_y - child_alloc.y);
child2 = gtk_snapshot_free (child_snapshot);
node = gsk_pixel_shader_node_new (&bounds,
child1, child2,
self->fragment_shader,
time);
gsk_render_node_set_name (node, "shader");
gtk_snapshot_append_node (snapshot, node);
gsk_render_node_unref (child1);
gsk_render_node_unref (child2);
}
static void
gtk_shadertoy_finalize (GObject *object)
{
GtkShadertoy *self = GTK_SHADERTOY (object);
if (self->fragment_shader)
g_bytes_unref (self->fragment_shader);
G_OBJECT_CLASS (gtk_shadertoy_parent_class)->finalize (object);
}
static void
gtk_shadertoy_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkShadertoy *self = GTK_SHADERTOY (object);
switch (prop_id)
{
case PROP_TIME:
g_value_set_float (value, self->time);
break;
case PROP_RUNNING:
g_value_set_boolean (value, self->tick != 0);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void gtk_shadertoy_set_running (GtkShadertoy *self,
gboolean running);
static void gtk_shadertoy_reset_time (GtkShadertoy *self);
static void
gtk_shadertoy_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkShadertoy *self = GTK_SHADERTOY (object);
switch (prop_id)
{
case PROP_RUNNING:
gtk_shadertoy_set_running (self, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
gtk_shadertoy_measure (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
GtkShadertoy *self = GTK_SHADERTOY (widget);
gint child1_min, child1_nat;
gint child2_min, child2_nat;
*minimum = 0;
*natural = 0;
gtk_widget_measure (self->child1, orientation, -1, &child1_min, &child1_nat, NULL, NULL);
gtk_widget_measure (self->child2, orientation, -1, &child2_min, &child2_nat, NULL, NULL);
if (orientation == GTK_ORIENTATION_HORIZONTAL)
{
*minimum = MAX (*minimum, MAX (child1_min, child2_min));
*natural = MAX (*natural, MAX (child1_nat, child2_nat));
}
else /* VERTICAL */
{
*minimum = MAX (*minimum, MAX (child1_min, child2_min));
*natural = MAX (*natural, MAX (child1_nat, child2_nat));
}
}
static void
gtk_shadertoy_size_allocate (GtkWidget *widget,
const GtkAllocation *allocation,
int baseline,
GtkAllocation *out_clip)
{
GtkShadertoy *self = GTK_SHADERTOY (widget);
GtkAllocation child_allocation;
GtkRequisition child_requisition;
gtk_widget_get_preferred_size (self->child1, &child_requisition, NULL);
gtk_widget_get_preferred_size (self->child2, &child_requisition, NULL);
child_allocation.x = 0;
child_allocation.y = 0;
child_allocation.width = allocation->width;
child_allocation.height = allocation->height;
gtk_widget_size_allocate (self->child1, &child_allocation, -1, out_clip);
gtk_widget_size_allocate (self->child2, &child_allocation, -1, out_clip);
}
static void
gtk_shadertoy_class_init (GtkShadertoyClass *klass)
{
GParamSpec *pspec;
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->finalize = gtk_shadertoy_finalize;
object_class->get_property = gtk_shadertoy_get_property;
object_class->set_property = gtk_shadertoy_set_property;
widget_class->snapshot = gtk_shadertoy_snapshot;
widget_class->measure = gtk_shadertoy_measure;
widget_class->size_allocate = gtk_shadertoy_size_allocate;
pspec = g_param_spec_float ("time", NULL, NULL,
0.0, G_MAXFLOAT, 0.0,
G_PARAM_READABLE);
g_object_class_install_property (object_class, PROP_TIME, pspec);
pspec = g_param_spec_boolean ("running", NULL, NULL,
FALSE,
G_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_RUNNING, pspec);
}
static void
gtk_shadertoy_init (GtkShadertoy *self)
{
GdkPixbuf *pixbuf;
gtk_widget_set_has_window (GTK_WIDGET (self), FALSE);
self->child2 = gtk_entry_new ();
gtk_entry_set_text (GTK_ENTRY (self->child2), "Stat rosa prisina nomine, nomina nuda tenemus");
gtk_widget_set_parent (self->child2, GTK_WIDGET (self));
pixbuf = gdk_pixbuf_new_from_file_at_scale ("tests/portland-rose.jpg", 400, 400, TRUE, NULL);
self->child1 = gtk_image_new_from_pixbuf (pixbuf);
g_object_unref (pixbuf);
gtk_widget_set_parent (self->child1, GTK_WIDGET (self));
}
static GtkWidget *
gtk_shadertoy_new (void)
{
return g_object_new (gtk_shadertoy_get_type (), NULL);
}
static void
gtk_shadertoy_set_fragment_shader (GtkShadertoy *self,
GBytes *shader)
{
if (self->fragment_shader)
g_bytes_unref (self->fragment_shader);
self->fragment_shader = shader;
if (self->fragment_shader)
g_bytes_ref (self->fragment_shader);
}
static gboolean
tick_cb (GtkWidget *widget, GdkFrameClock *clock, gpointer data)
{
GtkShadertoy *self = GTK_SHADERTOY (widget);
self->time = self->base + (g_get_monotonic_time () - self->starttime) / 1000000.0;
g_object_notify (G_OBJECT (widget), "time");
gtk_widget_queue_draw (widget);
return G_SOURCE_CONTINUE;
}
static void
gtk_shadertoy_set_running (GtkShadertoy *self,
gboolean running)
{
if (running && self->tick == 0)
{
self->tick = gtk_widget_add_tick_callback (GTK_WIDGET (self), tick_cb, NULL, NULL);
self->starttime = g_get_monotonic_time ();
self->time = 0;
}
else if (!running && self->tick != 0)
{
gtk_widget_remove_tick_callback (GTK_WIDGET (self), self->tick);
self->base += (g_get_monotonic_time () - self->starttime) / 1000000.0;
self->tick = 0;
}
else
return;
gtk_widget_queue_draw (GTK_WIDGET (self));
g_object_notify (G_OBJECT (self), "running");
}
static gboolean
gtk_shadertoy_is_running (GtkShadertoy *self)
{
return self->tick != 0;
}
static void
gtk_shadertoy_reset_time (GtkShadertoy *self)
{
self->base = 0;
self->time = 0;
self->starttime = g_get_monotonic_time ();
gtk_widget_queue_draw (GTK_WIDGET (self));
g_object_notify (G_OBJECT (self), "time");
}
static GtkWidget *window = NULL;
static GtkWidget *textview;
static GtkWidget *toy;
static const char *frag_text =
"#version 420 core\n"
"\n"
"layout(location = 0) in vec2 iPos;\n"
"layout(location = 1) in vec2 iTexCoord1;\n"
"layout(location = 2) in vec2 iTexCoord2;\n"
"layout(location = 3) in float iTime;\n"
"layout(location = 4) in vec2 iResolution;\n"
"\n"
"layout(set = 0, binding = 0) uniform sampler2D iTexture1;\n"
"layout(set = 1, binding = 0) uniform sampler2D iTexture2;\n"
"\n"
"layout(location = 0) out vec4 color;\n"
"\n"
"void\n"
"mainImage (out vec4 fragColor, in vec2 fragCoord)\n"
"{\n"
" vec2 uv = fragCoord.xy / iResolution.xy;\n"
" fragColor = texture(iTexture1,iTexCoord1*(0.3*sin(iTime) + 0.5));\n"
"}\n"
"\n"
"void main()\n"
"{\n"
" mainImage (color, iPos);\n"
"}";
static gboolean
update_shader (void)
{
GError *error = NULL;
int status;
char *spv;
gsize length;
GBytes *shader;
GtkTextBuffer *buffer;
GtkTextIter start, end;
char *text;
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview));
gtk_text_buffer_get_bounds (buffer, &start, &end);
text = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
if (!g_file_set_contents ("gtk4-demo-shader.frag", text, -1, &error))
{
g_print ("Failed to write shader file: %s\n", error->message);
g_error_free (error);
g_free (text);
return FALSE;
}
g_free (text);
if (!g_spawn_command_line_sync ("glslc -fshader-stage=fragment -DCLIP_NONE gtk4-demo-shader.frag -o gtk4-demo-shader.frag.spv", NULL, NULL, &status, &error))
{
g_print ("Running glslc failed (%d): %s\n", status, error->message);
g_error_free (error);
return FALSE;
}
if (!g_file_get_contents ("gtk4-demo-shader.frag.spv", &spv, &length, &error))
{
g_print ("Reading compiled shader failed: %s\n", error->message);
g_error_free (error);
return FALSE;
}
shader = g_bytes_new_take (spv, length);
gtk_shadertoy_set_fragment_shader (GTK_SHADERTOY (toy), shader);
g_bytes_unref (shader);
return TRUE;
}
static void
play_cb (GtkButton *button)
{
if (gtk_shadertoy_is_running (GTK_SHADERTOY (toy)))
{
gtk_shadertoy_set_running (GTK_SHADERTOY (toy), FALSE);
gtk_button_set_icon_name (button, "media-playback-start-symbolic");
}
else
{
if (update_shader ())
{
gtk_shadertoy_set_running (GTK_SHADERTOY (toy), TRUE);
gtk_button_set_icon_name (button, "media-playback-stop-symbolic");
}
}
}
static void
rewind_cb (GtkButton *button)
{
gtk_shadertoy_reset_time (GTK_SHADERTOY (toy));
}
static gboolean
format_time (GBinding *binding,
const GValue *from_value,
GValue *to_value,
gpointer data)
{
char buffer[256];
g_snprintf (buffer, sizeof (buffer), "%.2f", g_value_get_float (from_value));
g_value_set_string (to_value, buffer);
return TRUE;
}
static void
save_cb (GtkButton *button)
{
GtkFileChooserNative *fs;
int response;
fs = gtk_file_chooser_native_new ("Save Shader",
GTK_WINDOW (window),
GTK_FILE_CHOOSER_ACTION_SAVE,
"Save", "Cancel");
gtk_native_dialog_set_modal (GTK_NATIVE_DIALOG (fs), TRUE);
response = gtk_native_dialog_run (GTK_NATIVE_DIALOG (fs));
if (response == GTK_RESPONSE_ACCEPT)
{
GtkTextBuffer *buffer;
GtkTextIter start, end;
char *text;
char *filename;
GError *error = NULL;
filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fs));
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview));
gtk_text_buffer_get_bounds (buffer, &start, &end);
text = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
if (!g_file_set_contents (filename, text, -1, &error))
{
g_print ("Failed to save :-( %s\n", error->message);
g_error_free (error);
}
g_free (filename);
g_free (text);
}
g_object_unref (fs);
}
static void
load_cb (GtkButton *button)
{
GtkFileChooserNative *fs;
int response;
fs = gtk_file_chooser_native_new ("Load Shader",
GTK_WINDOW (window),
GTK_FILE_CHOOSER_ACTION_OPEN,
"Open", "Cancel");
gtk_native_dialog_set_modal (GTK_NATIVE_DIALOG (fs), TRUE);
response = gtk_native_dialog_run (GTK_NATIVE_DIALOG (fs));
if (response == GTK_RESPONSE_ACCEPT)
{
GtkTextBuffer *buffer;
char *text;
gsize len;
char *filename;
GError *error = NULL;
filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fs));
if (!g_file_get_contents (filename, &text, &len, &error))
{
g_print ("Failed to load :-( %s\n", error->message);
g_error_free (error);
}
else
{
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview));
gtk_text_buffer_set_text (buffer, text, len);
g_free (text);
}
g_free (filename);
}
g_object_unref (fs);
}
GtkWidget *
do_shadertoy (GtkWidget *do_widget)
{
if (!window)
{
GtkBuilder *builder;
GtkWidget *content;
GtkWidget *rewind;
GtkWidget *play;
GtkWidget *time;
GtkWidget *load;
GtkWidget *save;
GtkTextBuffer *buffer;
builder = gtk_builder_new_from_resource ("/shadertoy/shadertoy.ui");
window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
content = GTK_WIDGET (gtk_builder_get_object (builder, "content"));
textview = GTK_WIDGET (gtk_builder_get_object (builder, "text"));
rewind = GTK_WIDGET (gtk_builder_get_object (builder, "rewind"));
play = GTK_WIDGET (gtk_builder_get_object (builder, "play"));
time = GTK_WIDGET (gtk_builder_get_object (builder, "time"));
load = GTK_WIDGET (gtk_builder_get_object (builder, "load"));
save = GTK_WIDGET (gtk_builder_get_object (builder, "save"));
gtk_window_set_screen (GTK_WINDOW (window),
gtk_widget_get_screen (do_widget));
g_signal_connect (window, "destroy",
G_CALLBACK (gtk_widget_destroyed), &window);
toy = gtk_shadertoy_new ();
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview));
gtk_text_buffer_set_text (buffer, frag_text, -1);
gtk_widget_set_hexpand (toy, TRUE);
gtk_widget_set_vexpand (toy, TRUE);
gtk_container_add (GTK_CONTAINER (content), toy);
g_signal_connect (play, "clicked", G_CALLBACK (play_cb), NULL);
g_signal_connect (rewind, "clicked", G_CALLBACK (rewind_cb), NULL);
g_signal_connect (load, "clicked", G_CALLBACK (load_cb), NULL);
g_signal_connect (save, "clicked", G_CALLBACK (save_cb), NULL);
g_object_bind_property_full (toy, "time",
time, "label",
G_BINDING_SYNC_CREATE,
format_time,
NULL,
NULL,
NULL);
g_object_unref (builder);
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
gtk_widget_destroy (window);
return window;
}

View File

@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<!-- interface-requires gtk+ 3.6 -->
<object class="GtkWindow" id="window">
<property name="default-width">640</property>
<property name="default-height">480</property>
<child type="titlebar">
<object class="GtkHeaderBar">
<property name="title" translatable="yes">Shadertoy</property>
<property name="show-close-button">1</property>
<child>
<object class="GtkButton" id="rewind">
<property name="icon-name">media-skip-backward-symbolic</property>
</object>
</child>
<child>
<object class="GtkButton" id="play">
<property name="icon-name">media-playback-start-symbolic</property>
</object>
</child>
<child>
<object class="GtkLabel" id="time">
<property name="margin-start">20</property>
</object>
</child>
<child>
<object class="GtkButton" id="save">
<property name="icon-name">document-save-as-symbolic</property>
</object>
<packing>
<property name="pack-type">end</property>
</packing>
</child>
<child>
<object class="GtkButton" id="load">
<property name="icon-name">document-open-symbolic</property>
</object>
<packing>
<property name="pack-type">end</property>
</packing>
</child>
</object>
</child>
<child>
<object class="GtkPaned" id="content">
<property name="orientation">vertical</property>
<property name="position">240</property>
<child>
<object class="GtkScrolledWindow">
<property name="expand">1</property>
<child>
<object class="GtkTextView" id="text">
<property name="top-margin">10</property>
<property name="bottom-margin">10</property>
<property name="left-margin">10</property>
<property name="right-margin">10</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</interface>

View File

@@ -45,6 +45,7 @@
* @GSK_CROSS_FADE_NODE: A node that cross-fades between two children
* @GSK_TEXT_NODE: A node containing a glyph string
* @GSK_BLUR_NODE: A node that applies a blur
* @GSK_PIXEL_SHADER_NODE: A node that applies a custom shader
*
* The type of a node determines what the node is rendering.
*
@@ -71,7 +72,8 @@ typedef enum {
GSK_BLEND_NODE,
GSK_CROSS_FADE_NODE,
GSK_TEXT_NODE,
GSK_BLUR_NODE
GSK_BLUR_NODE,
GSK_PIXEL_SHADER_NODE
} GskRenderNodeType;
/**

View File

@@ -182,6 +182,18 @@ GDK_AVAILABLE_IN_3_92
GskRenderNode * gsk_blur_node_new (GskRenderNode *child,
double radius);
GDK_AVAILABLE_IN_3_92
GskRenderNode * gsk_pixel_shader_node_new (const graphene_rect_t *bounds,
GskRenderNode *child1,
GskRenderNode *child2,
GBytes *fragement_bytes,
float time);
GDK_AVAILABLE_IN_3_92
GskRenderNode *gsk_pixel_shader_node_get_child1 (GskRenderNode *node);
GDK_AVAILABLE_IN_3_92
GskRenderNode *gsk_pixel_shader_node_get_child2 (GskRenderNode *node);
GDK_AVAILABLE_IN_3_90
void gsk_render_node_set_scaling_filters (GskRenderNode *node,
GskScalingFilter min_filter,

View File

@@ -4414,6 +4414,223 @@ gsk_blur_node_get_radius (GskRenderNode *node)
return self->radius;
}
/*** GSK_PIXEL_SHADER_NODE ***/
typedef struct _GskPixelShaderNode GskPixelShaderNode;
struct _GskPixelShaderNode
{
GskRenderNode render_node;
GBytes *fragment_bytes;
float time;
GskRenderNode *child1;
GskRenderNode *child2;
};
static void
gsk_pixel_shader_node_finalize (GskRenderNode *node)
{
GskPixelShaderNode *self = (GskPixelShaderNode *) node;
g_bytes_unref (self->fragment_bytes);
if (self->child1)
gsk_render_node_unref (self->child1);
if (self->child2)
gsk_render_node_unref (self->child2);
}
static void
gsk_pixel_shader_node_draw (GskRenderNode *node,
cairo_t *cr)
{
GskPixelShaderNode *self = (GskPixelShaderNode *) node;
cairo_save (cr);
cairo_set_source_rgb (cr, 1, 0, 0);
cairo_paint (cr);
cairo_restore (cr);
if (self->child1)
gsk_render_node_draw (self->child1, cr);
if (self->child2)
gsk_render_node_draw (self->child2, cr);
}
#define GSK_PIXEL_SHADER_NODE_VARIANT_TYPE "(dddda(uv)ayd)"
static GVariant *
gsk_pixel_shader_node_serialize (GskRenderNode *node)
{
GskPixelShaderNode *self = (GskPixelShaderNode *) node;
GVariantBuilder builder;
g_variant_builder_init (&builder, G_VARIANT_TYPE (GSK_CONTAINER_NODE_VARIANT_TYPE));
if (self->child1)
g_variant_builder_add (&builder, "(uv)",
(guint32) gsk_render_node_get_node_type (self->child1),
gsk_render_node_serialize_node (self->child1));
if (self->child2)
g_variant_builder_add (&builder, "(uv)",
(guint32) gsk_render_node_get_node_type (self->child2),
gsk_render_node_serialize_node (self->child2));
return g_variant_new ("(dddd@ayda(uv))",
(double) node->bounds.origin.x, (double) node->bounds.origin.y,
(double) node->bounds.size.width, (double) node->bounds.size.height,
&builder,
g_variant_new_fixed_array (G_VARIANT_TYPE ("y"),
g_bytes_get_data (self->fragment_bytes, NULL),
g_bytes_get_size (self->fragment_bytes), 1),
self->time);
}
static GskRenderNode *
gsk_pixel_shader_node_deserialize (GVariant *variant,
GError **error)
{
GVariant *fragment_variant;
char *data;
double bounds[4];
double time;
gsize length;
GBytes *fragment_bytes;
GskRenderNode *node;
GVariantIter *iter;
gsize i, n_children;
guint32 child_type;
GVariant *child_variant;
GskRenderNode *children[2] = { NULL, NULL };
if (!check_variant_type (variant, GSK_PIXEL_SHADER_NODE_VARIANT_TYPE, error))
return NULL;
g_variant_get (variant, "(dddda(uv)@ayd)",
&bounds[0], &bounds[1], &bounds[2], &bounds[3],
&iter, &fragment_variant, &time);
n_children = g_variant_iter_init (iter, variant);
if (n_children > 2)
return NULL;
i = 0;
while (g_variant_iter_loop (iter, "(uv)", &child_type, &child_variant))
{
children[i] = gsk_render_node_deserialize_node (child_type, child_variant, error);
if (children[i] == NULL)
{
guint j;
for (j = 0; j < i; j++)
gsk_render_node_unref (children[j]);
g_variant_unref (child_variant);
return NULL;
}
i++;
}
data = g_variant_get_fixed_array (fragment_variant, &length, 1);
fragment_bytes = g_bytes_new (data, length);
node = gsk_pixel_shader_node_new (&GRAPHENE_RECT_INIT(bounds[0], bounds[1], bounds[2], bounds[3]),
children[0], children[1],
fragment_bytes, time);
if (children[0])
gsk_render_node_unref (children[0]);
if (children[1])
gsk_render_node_unref (children[1]);
g_bytes_unref (fragment_bytes);
g_variant_unref (fragment_variant);
return node;
}
static const GskRenderNodeClass GSK_PIXEL_SHADER_NODE_CLASS = {
GSK_PIXEL_SHADER_NODE,
sizeof (GskPixelShaderNode),
"GskPixelShaderNode",
gsk_pixel_shader_node_finalize,
gsk_pixel_shader_node_draw,
gsk_pixel_shader_node_serialize,
gsk_pixel_shader_node_deserialize
};
GskRenderNode *
gsk_pixel_shader_node_new (const graphene_rect_t *bounds,
GskRenderNode *child1,
GskRenderNode *child2,
GBytes *fragment_bytes,
float time)
{
GskPixelShaderNode *self;
self = (GskPixelShaderNode *) gsk_render_node_new (&GSK_PIXEL_SHADER_NODE_CLASS, 0);
if (child1)
self->child1 = gsk_render_node_ref (child1);
else
self->child1 = NULL;
if (child2)
self->child2 = gsk_render_node_ref (child2);
else
self->child2 = NULL;
graphene_rect_init_from_rect (&self->render_node.bounds, bounds);
self->fragment_bytes = g_bytes_ref (fragment_bytes);
self->time = time;
return &self->render_node;
}
GBytes *
gsk_pixel_shader_node_get_fragment_bytes (GskRenderNode *node)
{
GskPixelShaderNode *self = (GskPixelShaderNode *) node;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_PIXEL_SHADER_NODE), NULL);
return self->fragment_bytes;
}
float
gsk_pixel_shader_node_get_time (GskRenderNode *node)
{
GskPixelShaderNode *self = (GskPixelShaderNode *) node;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_PIXEL_SHADER_NODE), 0);
return self->time;
}
GskRenderNode *
gsk_pixel_shader_node_get_child1 (GskRenderNode *node)
{
GskPixelShaderNode *self = (GskPixelShaderNode *) node;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_PIXEL_SHADER_NODE), 0);
return self->child1;
}
GskRenderNode *
gsk_pixel_shader_node_get_child2 (GskRenderNode *node)
{
GskPixelShaderNode *self = (GskPixelShaderNode *) node;
g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_PIXEL_SHADER_NODE), 0);
return self->child2;
}
/*** ***/
static const GskRenderNodeClass *klasses[] = {
[GSK_CONTAINER_NODE] = &GSK_CONTAINER_NODE_CLASS,
[GSK_CAIRO_NODE] = &GSK_CAIRO_NODE_CLASS,
@@ -4434,7 +4651,8 @@ static const GskRenderNodeClass *klasses[] = {
[GSK_BLEND_NODE] = &GSK_BLEND_NODE_CLASS,
[GSK_CROSS_FADE_NODE] = &GSK_CROSS_FADE_NODE_CLASS,
[GSK_TEXT_NODE] = &GSK_TEXT_NODE_CLASS,
[GSK_BLUR_NODE] = &GSK_BLUR_NODE_CLASS
[GSK_BLUR_NODE] = &GSK_BLUR_NODE_CLASS,
[GSK_PIXEL_SHADER_NODE] = &GSK_PIXEL_SHADER_NODE_CLASS,
};
GskRenderNode *

View File

@@ -110,6 +110,9 @@ double gsk_cross_fade_node_get_progress (GskRenderNode *node);
GskRenderNode * gsk_blur_node_get_child (GskRenderNode *node);
double gsk_blur_node_get_radius (GskRenderNode *node);
GBytes * gsk_pixel_shader_node_get_fragment_bytes (GskRenderNode *node);
float gsk_pixel_shader_node_get_time (GskRenderNode *node);
G_END_DECLS
#endif /* __GSK_RENDER_NODE_PRIVATE_H__ */

View File

@@ -0,0 +1,171 @@
#include "config.h"
#include "gskvulkancustompipelineprivate.h"
struct _GskVulkanCustomPipeline
{
GObject parent_instance;
};
typedef struct _GskVulkanCustomInstance GskVulkanCustomInstance;
struct _GskVulkanCustomInstance
{
float rect[4];
float tex_rect1[4];
float tex_rect2[4];
float time;
};
G_DEFINE_TYPE (GskVulkanCustomPipeline, gsk_vulkan_custom_pipeline, GSK_TYPE_VULKAN_PIPELINE)
static const VkPipelineVertexInputStateCreateInfo *
gsk_vulkan_custom_pipeline_get_input_state_create_info (GskVulkanPipeline *self)
{
static const VkVertexInputBindingDescription vertexBindingDescriptions[] = {
{
.binding = 0,
.stride = sizeof (GskVulkanCustomInstance),
.inputRate = VK_VERTEX_INPUT_RATE_INSTANCE
}
};
static const VkVertexInputAttributeDescription vertexInputAttributeDescription[] = {
{
.location = 0,
.binding = 0,
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
.offset = 0,
},
{
.location = 1,
.binding = 0,
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
.offset = G_STRUCT_OFFSET (GskVulkanCustomInstance, tex_rect1),
},
{
.location = 2,
.binding = 0,
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
.offset = G_STRUCT_OFFSET (GskVulkanCustomInstance, tex_rect2),
},
{
.location = 3,
.binding = 0,
.format = VK_FORMAT_R32_SFLOAT,
.offset = G_STRUCT_OFFSET (GskVulkanCustomInstance, time),
},
};
static const VkPipelineVertexInputStateCreateInfo info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.vertexBindingDescriptionCount = G_N_ELEMENTS (vertexBindingDescriptions),
.pVertexBindingDescriptions = vertexBindingDescriptions,
.vertexAttributeDescriptionCount = G_N_ELEMENTS (vertexInputAttributeDescription),
.pVertexAttributeDescriptions = vertexInputAttributeDescription
};
return &info;
}
static void
gsk_vulkan_custom_pipeline_finalize (GObject *gobject)
{
//GskVulkanCustomPipeline *self = GSK_VULKAN_BLUR_PIPELINE (gobject);
G_OBJECT_CLASS (gsk_vulkan_custom_pipeline_parent_class)->finalize (gobject);
}
static void
gsk_vulkan_custom_pipeline_class_init (GskVulkanCustomPipelineClass *klass)
{
GskVulkanPipelineClass *pipeline_class = GSK_VULKAN_PIPELINE_CLASS (klass);
G_OBJECT_CLASS (klass)->finalize = gsk_vulkan_custom_pipeline_finalize;
pipeline_class->get_input_state_create_info = gsk_vulkan_custom_pipeline_get_input_state_create_info;
}
static void
gsk_vulkan_custom_pipeline_init (GskVulkanCustomPipeline *self)
{
}
GskVulkanPipeline *
gsk_vulkan_custom_pipeline_new (GdkVulkanContext *context,
VkPipelineLayout layout,
GBytes *fragment_bytes,
VkRenderPass render_pass)
{
GskVulkanShader *vertex_shader;
GskVulkanShader *fragment_shader;
GError *error = NULL;
vertex_shader = gsk_vulkan_shader_new_from_resource (context,
GSK_VULKAN_SHADER_VERTEX,
"custom",
&error);
fragment_shader = gsk_vulkan_shader_new_from_bytes (context,
GSK_VULKAN_SHADER_FRAGMENT,
fragment_bytes,
&error);
if (fragment_shader == NULL)
{
g_error ("%s", error->message);
g_error_free (error);
return NULL;
}
return gsk_vulkan_pipeline_new_with_shaders (GSK_TYPE_VULKAN_CUSTOM_PIPELINE,
context, layout,
vertex_shader,
fragment_shader,
render_pass,
VK_BLEND_FACTOR_ONE,
VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA);
}
gsize
gsk_vulkan_custom_pipeline_count_vertex_data (GskVulkanCustomPipeline *pipeline)
{
return sizeof (GskVulkanCustomInstance);
}
void
gsk_vulkan_custom_pipeline_collect_vertex_data (GskVulkanCustomPipeline *pipeline,
guchar *data,
const graphene_rect_t *bounds,
const graphene_rect_t *tex_rect1,
const graphene_rect_t *tex_rect2,
float time)
{
GskVulkanCustomInstance *instance = (GskVulkanCustomInstance *) data;
instance->rect[0] = bounds->origin.x;
instance->rect[1] = bounds->origin.y;
instance->rect[2] = bounds->size.width;
instance->rect[3] = bounds->size.height;
instance->tex_rect1[0] = tex_rect1->origin.x;
instance->tex_rect1[1] = tex_rect1->origin.y;
instance->tex_rect1[2] = tex_rect1->size.width;
instance->tex_rect1[3] = tex_rect1->size.height;
instance->tex_rect2[0] = tex_rect2->origin.x;
instance->tex_rect2[1] = tex_rect2->origin.y;
instance->tex_rect2[2] = tex_rect2->size.width;
instance->tex_rect2[3] = tex_rect2->size.height;
instance->time = time;
}
gsize
gsk_vulkan_custom_pipeline_draw (GskVulkanCustomPipeline *pipeline,
VkCommandBuffer command_buffer,
gsize offset,
gsize n_commands)
{
vkCmdDraw (command_buffer,
6, n_commands,
0, offset);
return n_commands;
}

View File

@@ -0,0 +1,35 @@
#ifndef __GSK_VULKAN_CUSTOM_PIPELINE_PRIVATE_H__
#define __GSK_VULKAN_CUSTOM_PIPELINE_PRIVATE_H__
#include <graphene.h>
#include "gskvulkanpipelineprivate.h"
G_BEGIN_DECLS
typedef struct _GskVulkanCustomPipelineLayout GskVulkanCustomPipelineLayout;
#define GSK_TYPE_VULKAN_CUSTOM_PIPELINE (gsk_vulkan_custom_pipeline_get_type ())
G_DECLARE_FINAL_TYPE (GskVulkanCustomPipeline, gsk_vulkan_custom_pipeline, GSK, VULKAN_CUSTOM_PIPELINE, GskVulkanPipeline)
GskVulkanPipeline * gsk_vulkan_custom_pipeline_new (GdkVulkanContext *context,
VkPipelineLayout layout,
GBytes *fragment_shader,
VkRenderPass render_pass);
gsize gsk_vulkan_custom_pipeline_count_vertex_data (GskVulkanCustomPipeline *pipeline);
void gsk_vulkan_custom_pipeline_collect_vertex_data (GskVulkanCustomPipeline *pipeline,
guchar *data,
const graphene_rect_t *rect,
const graphene_rect_t *child1_bounds,
const graphene_rect_t *child2_bounds,
float time);
gsize gsk_vulkan_custom_pipeline_draw (GskVulkanCustomPipeline *pipeline,
VkCommandBuffer command_buffer,
gsize offset,
gsize n_commands);
G_END_DECLS
#endif /* __GSK_VULKAN_CUSTOM_PIPELINE_PRIVATE_H__ */

View File

@@ -60,7 +60,9 @@ gsk_vulkan_pipeline_new (GType pipeline_type,
const char *shader_name,
VkRenderPass render_pass)
{
return gsk_vulkan_pipeline_new_full (pipeline_type, context, layout, shader_name, render_pass,
return gsk_vulkan_pipeline_new_full (pipeline_type, context, layout,
shader_name,
render_pass,
VK_BLEND_FACTOR_ONE,
VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA);
}
@@ -73,6 +75,34 @@ gsk_vulkan_pipeline_new_full (GType pipeline_type,
VkRenderPass render_pass,
VkBlendFactor srcBlendFactor,
VkBlendFactor dstBlendFactor)
{
GskVulkanShader *vertex_shader;
GskVulkanShader *fragment_shader;
vertex_shader = gsk_vulkan_shader_new_from_resource (context,
GSK_VULKAN_SHADER_VERTEX,
shader_name,
NULL);
fragment_shader = gsk_vulkan_shader_new_from_resource (context,
GSK_VULKAN_SHADER_FRAGMENT,
shader_name,
NULL);
return gsk_vulkan_pipeline_new_with_shaders (pipeline_type, context, layout,
vertex_shader, fragment_shader,
render_pass,
srcBlendFactor, dstBlendFactor);
}
GskVulkanPipeline *
gsk_vulkan_pipeline_new_with_shaders (GType pipeline_type,
GdkVulkanContext *context,
VkPipelineLayout layout,
GskVulkanShader *vertex_shader,
GskVulkanShader *fragment_shader,
VkRenderPass render_pass,
VkBlendFactor srcBlendFactor,
VkBlendFactor dstBlendFactor)
{
GskVulkanPipelinePrivate *priv;
GskVulkanPipeline *self;
@@ -80,7 +110,8 @@ gsk_vulkan_pipeline_new_full (GType pipeline_type,
g_return_val_if_fail (g_type_is_a (pipeline_type, GSK_TYPE_VULKAN_PIPELINE), NULL);
g_return_val_if_fail (layout != VK_NULL_HANDLE, NULL);
g_return_val_if_fail (shader_name != NULL, NULL);
g_return_val_if_fail (vertex_shader != NULL, NULL);
g_return_val_if_fail (fragment_shader != NULL, NULL);
g_return_val_if_fail (render_pass != VK_NULL_HANDLE, NULL);
self = g_object_new (pipeline_type, NULL);
@@ -92,8 +123,8 @@ gsk_vulkan_pipeline_new_full (GType pipeline_type,
priv->context = context;
priv->layout = layout;
priv->vertex_shader = gsk_vulkan_shader_new_from_resource (context, GSK_VULKAN_SHADER_VERTEX, shader_name, NULL);
priv->fragment_shader = gsk_vulkan_shader_new_from_resource (context, GSK_VULKAN_SHADER_FRAGMENT, shader_name, NULL);
priv->vertex_shader = vertex_shader;
priv->fragment_shader = fragment_shader;
GSK_VK_CHECK (vkCreateGraphicsPipelines, device,
VK_NULL_HANDLE,

View File

@@ -4,6 +4,7 @@
#include <gdk/gdk.h>
#include "gskdebugprivate.h"
#include "gskvulkanshaderprivate.h"
G_BEGIN_DECLS
@@ -44,6 +45,14 @@ GskVulkanPipeline * gsk_vulkan_pipeline_new_full (GType
VkRenderPass render_pass,
VkBlendFactor srcBlendFactor,
VkBlendFactor dstBlendFactor);
GskVulkanPipeline * gsk_vulkan_pipeline_new_with_shaders (GType pipeline_type,
GdkVulkanContext *context,
VkPipelineLayout layout,
GskVulkanShader *vertex_shader,
GskVulkanShader *fragment_shader,
VkRenderPass render_pass,
VkBlendFactor srcBlendFactor,
VkBlendFactor dstBlendFactor);
VkPipeline gsk_vulkan_pipeline_get_pipeline (GskVulkanPipeline *self);
VkPipelineLayout gsk_vulkan_pipeline_get_pipeline_layout (GskVulkanPipeline *self);

View File

@@ -17,6 +17,7 @@
#include "gskvulkancolorpipelineprivate.h"
#include "gskvulkancolortextpipelineprivate.h"
#include "gskvulkancrossfadepipelineprivate.h"
#include "gskvulkancustompipelineprivate.h"
#include "gskvulkaneffectpipelineprivate.h"
#include "gskvulkanlineargradientpipelineprivate.h"
#include "gskvulkantextpipelineprivate.h"
@@ -57,6 +58,7 @@ struct _GskVulkanRender
GList *render_passes;
GSList *cleanup_images;
GSList *cleanup_pipelines;
GQuark render_pass_counter;
GQuark gpu_time_timer;
@@ -419,6 +421,22 @@ gsk_vulkan_render_get_pipeline (GskVulkanRender *self,
return self->pipelines[type];
}
GskVulkanPipeline *
gsk_vulkan_render_get_custom_pipeline (GskVulkanRender *self,
GBytes *fragment_bytes)
{
GskVulkanPipeline *pipeline;
pipeline = gsk_vulkan_custom_pipeline_new (self->vulkan,
self->pipeline_layout[2],
fragment_bytes,
self->render_pass);
self->cleanup_pipelines = g_slist_prepend (self->cleanup_pipelines, pipeline);
return pipeline;
}
VkDescriptorSet
gsk_vulkan_render_get_descriptor_set (GskVulkanRender *self,
gsize id)
@@ -666,6 +684,8 @@ gsk_vulkan_render_cleanup (GskVulkanRender *self)
self->render_passes = NULL;
g_slist_free_full (self->cleanup_images, g_object_unref);
self->cleanup_images = NULL;
g_slist_free_full (self->cleanup_pipelines, g_object_unref);
self->cleanup_pipelines = NULL;
g_clear_pointer (&self->clip, cairo_region_destroy);
g_clear_object (&self->target);

View File

@@ -16,6 +16,7 @@
#include "gskvulkancolorpipelineprivate.h"
#include "gskvulkancolortextpipelineprivate.h"
#include "gskvulkancrossfadepipelineprivate.h"
#include "gskvulkancustompipelineprivate.h"
#include "gskvulkaneffectpipelineprivate.h"
#include "gskvulkanlineargradientpipelineprivate.h"
#include "gskvulkantextpipelineprivate.h"
@@ -53,6 +54,7 @@ typedef enum {
GSK_VULKAN_OP_REPEAT,
GSK_VULKAN_OP_CROSS_FADE,
GSK_VULKAN_OP_BLEND_MODE,
GSK_VULKAN_OP_PIXEL_SHADER,
/* GskVulkanOpText */
GSK_VULKAN_OP_TEXT,
GSK_VULKAN_OP_COLOR_TEXT,
@@ -278,6 +280,13 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self,
default:
FALLBACK ("Unsupported node '%s'\n", node->node_class->type_name);
case GSK_PIXEL_SHADER_NODE:
op.type = GSK_VULKAN_OP_PIXEL_SHADER;
op.render.pipeline = gsk_vulkan_render_get_custom_pipeline (render,
gsk_pixel_shader_node_get_fragment_bytes (node));
g_array_append_val (self->render_ops, op);
return;
case GSK_REPEAT_NODE:
if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds))
pipeline_type = GSK_VULKAN_PIPELINE_TEXTURE;
@@ -304,7 +313,7 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self,
op.type = GSK_VULKAN_OP_BLEND_MODE;
op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
g_array_append_val (self->render_ops, op);
return;
return;
case GSK_CROSS_FADE_NODE:
if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds))
@@ -1040,6 +1049,34 @@ gsk_vulkan_render_pass_upload (GskVulkanRenderPass *self,
}
break;
case GSK_VULKAN_OP_PIXEL_SHADER:
{
GskRenderNode *child1 = gsk_pixel_shader_node_get_child1 (op->render.node);
GskRenderNode *child2 = gsk_pixel_shader_node_get_child2 (op->render.node);
if (child1)
op->render.source = gsk_vulkan_render_pass_get_node_as_texture (self,
render,
uploader,
child1,
&child1->bounds,
clip,
&op->render.source_rect);
else
op->render.source = NULL;
if (child2)
op->render.source2 = gsk_vulkan_render_pass_get_node_as_texture (self,
render,
uploader,
child2,
&child2->bounds,
clip,
&op->render.source_rect);
else
op->render.source2 = NULL;
}
break;
case GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS:
clip = &op->constants.constants.clip;
break;
@@ -1134,6 +1171,11 @@ gsk_vulkan_render_pass_count_vertex_data (GskVulkanRenderPass *self)
n_bytes += op->render.vertex_count;
break;
case GSK_VULKAN_OP_PIXEL_SHADER:
op->render.vertex_count = gsk_vulkan_custom_pipeline_count_vertex_data (GSK_VULKAN_CUSTOM_PIPELINE (op->render.pipeline));
n_bytes += op->render.vertex_count;
break;
default:
g_assert_not_reached ();
@@ -1368,6 +1410,19 @@ gsk_vulkan_render_pass_collect_vertex_data (GskVulkanRenderPass *self,
}
break;
case GSK_VULKAN_OP_PIXEL_SHADER:
{
op->render.vertex_offset = offset + n_bytes;
gsk_vulkan_custom_pipeline_collect_vertex_data (GSK_VULKAN_CUSTOM_PIPELINE (op->render.pipeline),
data + n_bytes + offset,
&op->render.node->bounds,
&op->render.source ? &op->render.source_rect : &GRAPHENE_RECT_INIT(0,0,1,1),
&op->render.source2 ? &op->render.source2_rect : &GRAPHENE_RECT_INIT(0,0,1,1),
gsk_pixel_shader_node_get_time (op->render.node));
n_bytes += op->render.vertex_count;
}
break;
default:
g_assert_not_reached ();
case GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS:
@@ -1445,6 +1500,13 @@ gsk_vulkan_render_pass_reserve_descriptor_sets (GskVulkanRenderPass *self,
op->render.descriptor_set_index = gsk_vulkan_render_reserve_descriptor_set (render, op->render.source, TRUE);
break;
case GSK_VULKAN_OP_PIXEL_SHADER:
if (op->render.source)
op->render.descriptor_set_index = gsk_vulkan_render_reserve_descriptor_set (render, op->render.source, FALSE);
if (op->render.source2)
op->render.descriptor_set_index2 = gsk_vulkan_render_reserve_descriptor_set (render, op->render.source2, FALSE);
break;
case GSK_VULKAN_OP_TEXT:
case GSK_VULKAN_OP_COLOR_TEXT:
op->text.descriptor_set_index = gsk_vulkan_render_reserve_descriptor_set (render, op->text.source, FALSE);
@@ -1844,6 +1906,45 @@ gsk_vulkan_render_pass_draw_rect (GskVulkanRenderPass *self,
current_draw_index, 1);
break;
case GSK_VULKAN_OP_PIXEL_SHADER:
if (current_pipeline != op->render.pipeline)
{
current_pipeline = op->render.pipeline;
vkCmdBindPipeline (command_buffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
gsk_vulkan_pipeline_get_pipeline (current_pipeline));
vkCmdBindVertexBuffers (command_buffer,
0,
1,
(VkBuffer[1]) {
gsk_vulkan_buffer_get_buffer (vertex_buffer)
},
(VkDeviceSize[1]) { op->render.vertex_offset });
current_draw_index = 0;
}
{
VkDescriptorSet ds[2];
gsize size = 0;
if (op->render.source != NULL)
ds[size++] = gsk_vulkan_render_get_descriptor_set (render, op->render.descriptor_set_index);
if (op->render.source2 != NULL)
ds[size++] = gsk_vulkan_render_get_descriptor_set (render, op->render.descriptor_set_index2);
vkCmdBindDescriptorSets (command_buffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
gsk_vulkan_pipeline_get_pipeline_layout (current_pipeline),
0,
size,
ds,
0,
NULL);
}
current_draw_index += gsk_vulkan_custom_pipeline_draw (GSK_VULKAN_CUSTOM_PIPELINE (current_pipeline),
command_buffer,
current_draw_index, 1);
break;
default:
g_assert_not_reached ();
break;

View File

@@ -76,6 +76,8 @@ void gsk_vulkan_render_upload (GskVulk
GskVulkanPipeline * gsk_vulkan_render_get_pipeline (GskVulkanRender *self,
GskVulkanPipelineType pipeline_type);
GskVulkanPipeline * gsk_vulkan_render_get_custom_pipeline (GskVulkanRender *self,
GBytes *fragment_bytes);
VkDescriptorSet gsk_vulkan_render_get_descriptor_set (GskVulkanRender *self,
gsize id);
gsize gsk_vulkan_render_reserve_descriptor_set (GskVulkanRender *self,

View File

@@ -11,7 +11,7 @@ struct _GskVulkanShader
VkShaderModule vk_shader;
};
static GskVulkanShader *
GskVulkanShader *
gsk_vulkan_shader_new_from_bytes (GdkVulkanContext *context,
GskVulkanShaderType type,
GBytes *bytes,

View File

@@ -20,6 +20,10 @@ typedef struct _GskVulkanShader GskVulkanShader;
.pName = "main", \
}
GskVulkanShader * gsk_vulkan_shader_new_from_bytes (GdkVulkanContext *context,
GskVulkanShaderType type,
GBytes *bytes,
GError **error);
GskVulkanShader * gsk_vulkan_shader_new_from_resource (GdkVulkanContext *context,
GskVulkanShaderType type,
const char *resource_name,

View File

@@ -63,6 +63,7 @@ if have_vulkan
'gskvulkancolorpipeline.c',
'gskvulkancolortextpipeline.c',
'gskvulkancrossfadepipeline.c',
'gskvulkancustompipeline.c',
'gskvulkancommandpool.c',
'gskvulkaneffectpipeline.c',
'gskvulkanglyphcache.c',

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,53 @@
#version 420 core
layout(push_constant) uniform PushConstants {
mat4 mvp;
vec4 clip_bounds;
vec4 clip_widths;
vec4 clip_heights;
} push;
layout(location = 0) in vec4 inRect;
layout(location = 1) in vec4 inTexRect1;
layout(location = 2) in vec4 inTexRect2;
layout(location = 3) in float inTime;
layout(location = 0) out vec2 outPos;
layout(location = 1) out vec2 outTexCoord1;
layout(location = 2) out vec2 outTexCoord2;
layout(location = 3) out float outTime;
layout(location = 4) out vec2 outResolution;
out gl_PerVertex {
vec4 gl_Position;
};
vec4 clip(vec4 rect) { return rect; }
vec2 offsets[6] = { vec2(0.0, 0.0),
vec2(1.0, 0.0),
vec2(0.0, 1.0),
vec2(0.0, 1.0),
vec2(1.0, 0.0),
vec2(1.0, 1.0) };
void main() {
vec4 rect = clip (inRect);
vec2 pos = rect.xy + rect.zw * offsets[gl_VertexIndex];
gl_Position = push.mvp * vec4 (pos, 0.0, 1.0);
outPos = pos;
vec4 texrect = vec4((rect.xy - inRect.xy) / inRect.zw,
rect.zw / inRect.zw);
vec4 texrect1 = vec4(inTexRect1.xy + inTexRect1.zw * texrect.xy,
inTexRect1.zw * texrect.zw);
outTexCoord1 = texrect1.xy + texrect1.zw * offsets[gl_VertexIndex];
vec4 texrect2 = vec4(inTexRect2.xy + inTexRect2.zw * texrect.xy,
inTexRect2.zw * texrect.zw);
outTexCoord2 = texrect2.xy + texrect2.zw * offsets[gl_VertexIndex];
outTime = inTime;
outResolution = inRect.zw;
}

Binary file not shown.

View File

@@ -32,6 +32,7 @@ gsk_private_vulkan_vertex_shaders = [
'mask.vert',
'outset-shadow.vert',
'texture.vert',
'custom.vert'
]
gsk_private_vulkan_shaders += gsk_private_vulkan_fragment_shaders

View File

@@ -125,13 +125,13 @@ gtk_snapshot_state_clear (GtkSnapshotState *state)
g_clear_pointer (&state->name, g_free);
}
void
gtk_snapshot_init (GtkSnapshot *snapshot,
GskRenderer *renderer,
gboolean record_names,
const cairo_region_t *clip,
const char *name,
...)
static void
gtk_snapshot_init_va (GtkSnapshot *snapshot,
GskRenderer *renderer,
gboolean record_names,
const cairo_region_t *clip,
const char *name,
va_list args)
{
char *str;
@@ -142,13 +142,7 @@ gtk_snapshot_init (GtkSnapshot *snapshot,
snapshot->nodes = g_ptr_array_new_with_free_func ((GDestroyNotify)gsk_render_node_unref);
if (name && record_names)
{
va_list args;
va_start (args, name);
str = g_strdup_vprintf (name, args);
va_end (args);
}
str = g_strdup_vprintf (name, args);
else
str = NULL;
@@ -159,6 +153,38 @@ gtk_snapshot_init (GtkSnapshot *snapshot,
gtk_snapshot_collect_default);
}
void
gtk_snapshot_init (GtkSnapshot *snapshot,
GskRenderer *renderer,
gboolean record_names,
const cairo_region_t *clip,
const char *name,
...)
{
va_list args;
va_start (args, name);
gtk_snapshot_init_va (snapshot, renderer, record_names, clip, name, args);
va_end (args);
}
GtkSnapshot *
gtk_snapshot_new (GtkSnapshot *parent,
const char *name,
...)
{
GtkSnapshot *snapshot;
va_list args;
snapshot = g_new (GtkSnapshot, 1);
va_start (args, name);
gtk_snapshot_init_va (snapshot, parent->renderer, parent->record_names, NULL, name, args);
va_end (args);
return snapshot;
}
/**
* gtk_snapshot_push:
* @snapshot: a #GtkSnapshot
@@ -1021,6 +1047,17 @@ gtk_snapshot_finish (GtkSnapshot *snapshot)
return result;
}
GskRenderNode *
gtk_snapshot_free (GtkSnapshot *snapshot)
{
GskRenderNode *node;
node = gtk_snapshot_finish (snapshot);
g_free (snapshot);
return node;
}
/**
* gtk_snapshot_pop:
* @snapshot: a #GtkSnapshot

View File

@@ -36,6 +36,13 @@
G_BEGIN_DECLS
GDK_AVAILABLE_IN_3_92
GtkSnapshot * gtk_snapshot_new (GtkSnapshot *parent,
const char *name,
...) G_GNUC_PRINTF (2, 3);
GDK_AVAILABLE_IN_3_92
GskRenderNode * gtk_snapshot_free (GtkSnapshot *snapshot);
GDK_AVAILABLE_IN_3_90
void gtk_snapshot_push (GtkSnapshot *snapshot,
gboolean keep_coordinates,

View File

@@ -87,14 +87,17 @@ struct _GtkSnapshot {
GPtrArray *nodes;
};
GDK_AVAILABLE_IN_ALL
void gtk_snapshot_init (GtkSnapshot *state,
GskRenderer *renderer,
gboolean record_names,
const cairo_region_t *clip,
const char *name,
...) G_GNUC_PRINTF (5, 6);
GDK_AVAILABLE_IN_ALL
GskRenderNode * gtk_snapshot_finish (GtkSnapshot *state);
GDK_AVAILABLE_IN_ALL
GskRenderer * gtk_snapshot_get_renderer (const GtkSnapshot *snapshot);
G_END_DECLS

View File

@@ -518,6 +518,10 @@ void gtk_widget_unrealize (GtkWidget *widget);
GDK_AVAILABLE_IN_ALL
void gtk_widget_draw (GtkWidget *widget,
cairo_t *cr);
GDK_AVAILABLE_IN_3_92
void gtk_widget_snapshot (GtkWidget *widget,
GtkSnapshot *snapshot);
/* Queuing draws */
GDK_AVAILABLE_IN_ALL
void gtk_widget_queue_draw (GtkWidget *widget);

View File

@@ -282,9 +282,6 @@ void gtk_widget_render (GtkWidget
GdkWindow *window,
const cairo_region_t *region);
void gtk_widget_snapshot (GtkWidget *widget,
GtkSnapshot *snapshot);
void gtk_widget_adjust_size_request (GtkWidget *widget,
GtkOrientation orientation,
gint *minimum_size,

View File

@@ -534,6 +534,13 @@ append_node (GtkTreeModelRenderNode *nodemodel,
/* no children */
break;
case GSK_PIXEL_SHADER_NODE:
if (gsk_pixel_shader_node_get_child1 (node))
append_node (nodemodel, gsk_pixel_shader_node_get_child1 (node), priv->nodes->len - 1);
if (gsk_pixel_shader_node_get_child2 (node))
append_node (nodemodel, gsk_pixel_shader_node_get_child2 (node), priv->nodes->len - 1);
break;
case GSK_TRANSFORM_NODE:
append_node (nodemodel, gsk_transform_node_get_child (node), priv->nodes->len - 1);
break;

View File

@@ -189,6 +189,8 @@ node_type_name (GskRenderNodeType type)
return "Text";
case GSK_BLUR_NODE:
return "Blur";
case GSK_PIXEL_SHADER_NODE:
return "Pixel Shader";
}
}