From 91a18a568c951bae8180ef8a4628a9d1cae8ffbd Mon Sep 17 00:00:00 2001
From: Bach Le <bach@bullno1.com>
Date: Sat, 23 Nov 2024 10:19:25 +0800
Subject: [PATCH] Add preprocessed shader source as comment in generated header

---
 CMakeLists.txt                        |   1 +
 samples/spaceshooter_data/flash_shd.h | 360 +++++++++++++++++
 samples/waves_data/waves_shd.h        | 392 +++++++++++++++++++
 src/cute_graphics.cpp                 |  24 +-
 src/cute_shader/cute_shader.cpp       |  31 +-
 src/cute_shader/cute_shader.h         |  11 +-
 src/cute_shader/cute_shaderc.cpp      |  10 +
 src/data/builtin_shaders_bytecode.h   | 544 ++++++++++++++++++++++++++
 8 files changed, 1349 insertions(+), 24 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index ebec3650..a5179c95 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -432,6 +432,7 @@ add_custom_command(
 	COMMAND cute-shaderc
 		-type=builtin
 		-oheader=${CMAKE_CURRENT_SOURCE_DIR}/src/data/builtin_shaders_bytecode.h
+	DEPENDS src/cute_shader/builtin_shaders.h
 )
 
 # Link up all dependencies to Cute.
diff --git a/samples/spaceshooter_data/flash_shd.h b/samples/spaceshooter_data/flash_shd.h
index b8054127..826962a6 100644
--- a/samples/spaceshooter_data/flash_shd.h
+++ b/samples/spaceshooter_data/flash_shd.h
@@ -1,5 +1,320 @@
 #pragma once
 
+/*
+#extension GL_ARB_shading_language_include : require
+
+#line 1 0
+
+layout(location = 0) in vec2 v_pos;
+layout(location = 1) in flat int v_n;
+layout(location = 2) in vec4 v_ab;
+layout(location = 3) in vec4 v_cd;
+layout(location = 4) in vec4 v_ef;
+layout(location = 5) in vec4 v_gh;
+layout(location = 6) in vec2 v_uv;
+layout(location = 7) in vec4 v_col;
+layout(location = 8) in float v_radius;
+layout(location = 9) in float v_stroke;
+layout(location = 10) in float v_aa;
+layout(location = 11) in float v_type;
+layout(location = 12) in float v_alpha;
+layout(location = 13) in float v_fill;
+layout(location = 14) in vec2 v_posH;
+layout(location = 15) in vec4 v_user;
+
+layout(location = 0) out vec4 result;
+
+layout(set = 2, binding = 0) uniform sampler2D u_image;
+
+layout(set = 3, binding = 0) uniform uniform_block {
+ vec2 u_texture_size;
+ int u_alpha_discard;
+};
+
+#line 1 "blend.shd"
+
+
+
+
+vec3 rgb_to_hsv(vec3 c)
+{
+ vec4 K = vec4(0.0, - 1.0 / 3.0, 2.0 / 3.0, - 1.0);
+ vec4 p = c.g < c.b ? vec4(c.bg, K.wz) : vec4(c.gb, K.xy);
+ vec4 q = c.r < p.x ? vec4(p.xyw, c.r) : vec4(c.r, p.yzx);
+ float d = q.x - min(q.w, q.y);
+ float e = 1.0e-10;
+ return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
+}
+
+vec3 hsv_to_rgb(vec3 c)
+{
+ vec3 rgb = clamp(abs(mod(c.x * 6.0 + vec3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0);
+ rgb = rgb * rgb * (3.0 - 2.0 * rgb);
+ return c.z * mix(vec3(1.0), rgb, c.y);
+}
+
+vec3 hue(vec3 base, vec3 tint)
+{
+ base = rgb_to_hsv(base);
+ tint = rgb_to_hsv(tint);
+ return hsv_to_rgb(vec3(tint.r, base.gb));
+}
+
+vec4 hue(vec4 base, vec4 tint)
+{
+ return vec4(hue(base.rgb, tint.rgb), base.a);
+}
+
+float overlay(float base, float blend)
+{
+ return(base <= 0.5) ? 2 * base * blend : 1 - 2 * (1 - base) * (1 - blend);
+}
+
+vec3 overlay(vec3 base, vec3 blend)
+{
+ return vec3(overlay(base.r, blend.r), overlay(base.g, blend.g), overlay(base.b, blend.b));
+}
+
+vec4 overlay(vec4 base, vec4 blend)
+{
+ return vec4(overlay(base.rgb, blend.rgb), base.a);
+}
+
+float softlight(float base, float blend)
+{
+ if (blend <= 0.5) return base - (1 - 2 * blend) * base * (1 - base);
+ else return base + (2.0 * blend - 1) * ( ( (base <= 0.25) ? ( (16.0 * base - 12.0) * base + 4.0) * base : sqrt(base)) - base);
+}
+
+vec3 softlight(vec3 base, vec3 blend)
+{
+ return vec3(softlight(base.r, blend.r), softlight(base.g, blend.g), softlight(base.b, blend.b));
+}
+
+vec4 softlight(vec4 base, vec4 blend)
+{
+ return vec4(softlight(base.rgb, blend.rgb), base.a);
+}
+#line 29 0
+#line 1 "gamma.shd"
+
+vec4 gamma(vec4 c)
+{
+ return vec4(pow(abs(c.rgb), vec3(1.0 / 2.2)), c.a);
+}
+
+vec4 de_gamma(vec4 c)
+{
+ return vec4(pow(abs(c.rgb), vec3(2.2)), c.a);
+}
+#line 30 0
+#line 1 "smooth_uv.shd"
+
+vec2 smooth_uv(vec2 uv, vec2 texture_size)
+{
+ vec2 pixel = uv * texture_size;
+ vec2 seam = floor(pixel + 0.5);
+ pixel = seam + clamp( (pixel - seam) / fwidth(pixel), - 0.5, 0.5);
+ return pixel / texture_size;
+}
+#line 31 0
+#line 1 "distance.shd"
+
+float safe_div(float a, float b)
+{
+ return b == 0.0 ? 0.0 : a / b;
+}
+
+float safe_len(vec2 v)
+{
+ float d = dot(v, v);
+ return d == 0.0 ? 0.0 : sqrt(d);
+}
+
+vec2 safe_norm(vec2 v, float l)
+{
+ return mix(vec2(0), v / l, l == 0.0 ? 0.0 : 1.0);
+}
+
+vec2 skew(vec2 v)
+{
+ return vec2(- v.y, v.x);
+}
+
+float det2(vec2 a, vec2 b)
+{
+ return a.x * b.y - a.y * b.x;
+}
+
+float sdf_stroke(float d)
+{
+ return abs(d) - v_stroke;
+}
+
+float sdf_intersect(float a, float b)
+{
+ return max(a, b);
+}
+
+float sdf_union(float a, float b)
+{
+ return min(a, b);
+}
+
+float sdf_subtract(float d0, float d1)
+{
+ return max(d0, - d1);
+}
+
+float dd(float d)
+{
+ return length(vec2(dFdx(d), dFdy(d)));
+}
+
+
+
+vec4 sdf(vec4 a, vec4 b, float d)
+{
+ float wire_d = sdf_stroke(d);
+ vec4 stroke_aa = mix(b, a, smoothstep(0.0, v_aa, wire_d));
+ vec4 stroke_no_aa = wire_d <= 0.0 ? b : a;
+
+ vec4 fill_aa = mix(b, a, smoothstep(0.0, v_aa, d));
+ vec4 fill_no_aa = clamp(d, - 1.0, 1.0) <= 0.0 ? b : a;
+
+ vec4 stroke = mix(stroke_aa, stroke_aa, v_aa > 0.0 ? 1.0 : 0.0);
+ vec4 fill = mix(fill_no_aa, fill_aa, v_aa > 0.0 ? 1.0 : 0.0);
+
+ result = mix(stroke, fill, v_fill);
+ return result;
+}
+
+float distance_aabb(vec2 p, vec2 he)
+{
+ vec2 d = abs(p) - he;
+ return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0);
+}
+
+float distance_box(vec2 p, vec2 c, vec2 he, vec2 u)
+{
+ mat2 m = transpose(mat2(u, skew(u)));
+ p = p - c;
+ p = m * p;
+ return distance_aabb(p, he);
+}
+
+
+float distance_segment(vec2 p, vec2 a, vec2 b)
+{
+ vec2 n = b - a;
+ vec2 pa = p - a;
+ float d = safe_div(dot(pa, n), dot(n, n));
+ float h = clamp(d, 0.0, 1.0);
+ return safe_len(pa - h * n);
+}
+
+
+float distance_triangle(vec2 p, vec2 a, vec2 b, vec2 c)
+{
+ vec2 e0 = b - a;
+ vec2 e1 = c - b;
+ vec2 e2 = a - c;
+
+ vec2 v0 = p - a;
+ vec2 v1 = p - b;
+ vec2 v2 = p - c;
+
+ vec2 pq0 = v0 - e0 * clamp(safe_div(dot(v0, e0), dot(e0, e0)), 0.0, 1.0);
+ vec2 pq1 = v1 - e1 * clamp(safe_div(dot(v1, e1), dot(e1, e1)), 0.0, 1.0);
+ vec2 pq2 = v2 - e2 * clamp(safe_div(dot(v2, e2), dot(e2, e2)), 0.0, 1.0);
+
+ float s = det2(e0, e2);
+ vec2 d = min(min(vec2(dot(pq0, pq0), s * det2(v0, e0)),
+                  vec2(dot(pq1, pq1), s * det2(v1, e1))),
+                  vec2(dot(pq2, pq2), s * det2(v2, e2)));
+
+ return - sqrt(d.x) * sign(d.y);
+}
+
+
+float distance_polygon(vec2 p, vec2[8] v, int N)
+{
+ float d = dot(p - v[0], p - v[0]);
+ float s = 1.0;
+ for (int i = 0, j = N - 1; i < N; j = i, i ++) {
+  vec2 e = v[j] - v[i];
+  vec2 w = p - v[i];
+  vec2 b = w - e * clamp(dot(w, e) / dot(e, e), 0.0, 1.0);
+  d = min(d, dot(b, b));
+
+  bvec3 cond = bvec3(p.y >= v[i].y,
+                     p.y < v[j].y,
+                     e.x * w.y > e.y * w.x);
+  if (all(cond) || all(not(cond))) {
+   s = - s;
+  }
+ }
+
+ return s * sqrt(d);
+}
+#line 32 0
+#line 1 "shader_stub.shd"
+ vec4 shader(vec4 color, vec2 pos, vec2 screen_uv, vec4 params)
+{
+ return vec4(mix(color.rgb, params.rgb, params.a), color.a);
+}
+#line 33 0
+
+
+vec2 pts[8];
+
+void main()
+{
+ bool is_sprite = v_type >= (0.0 / 255.0) && v_type < (0.5 / 255.0);
+ bool is_text = v_type > (0.5 / 255.0) && v_type < (1.5 / 255.0);
+ bool is_box = v_type > (1.5 / 255.0) && v_type < (2.5 / 255.0);
+ bool is_seg = v_type > (2.5 / 255.0) && v_type < (3.5 / 255.0);
+ bool is_tri = v_type > (3.5 / 255.0) && v_type < (4.5 / 255.0);
+ bool is_tri_sdf = v_type > (4.5 / 255.0) && v_type < (5.5 / 255.0);
+ bool is_poly = v_type > (5.5 / 255.0) && v_type < (6.5 / 255.0);
+
+
+ vec4 c = vec4(0);
+ c = ! (is_sprite && is_text) ? de_gamma(texture(u_image, smooth_uv(v_uv, u_texture_size))) : c;
+ c = is_sprite ? gamma(c) : c;
+ c = is_text ? v_col * c.a : c;
+ c = is_tri ? v_col : c;
+
+
+ float d = 0;
+ if (is_box) {
+  d = distance_box(v_pos, v_ab.xy, v_ab.zw, v_cd.xy);
+ } else if (is_seg) {
+  d = distance_segment(v_pos, v_ab.xy, v_ab.zw);
+  d = min(d, distance_segment(v_pos, v_ab.zw, v_cd.xy));
+ } else if (is_tri_sdf) {
+  d = distance_triangle(v_pos, v_ab.xy, v_ab.zw, v_cd.xy);
+ } else if (is_poly) {
+  pts[0] = v_ab.xy;
+  pts[1] = v_ab.zw;
+  pts[2] = v_cd.xy;
+  pts[3] = v_cd.zw;
+  pts[4] = v_ef.xy;
+  pts[5] = v_ef.zw;
+  pts[6] = v_gh.xy;
+  pts[7] = v_gh.zw;
+  d = distance_polygon(v_pos, pts, v_n);
+ }
+ c = (! is_sprite && ! is_text && ! is_tri) ? sdf(c, v_col, d - v_radius) : c;
+
+ c *= v_alpha;
+ vec2 screen_uv = (v_posH + vec2(1, - 1)) * 0.5 * vec2(1, - 1);
+ c = shader(c, v_pos, screen_uv, v_user);
+ if (u_alpha_discard != 0 && c.a == 0) discard;
+ result = c;
+}
+
+*/
 static const uint8_t s_flash_shd_bytecode_draw_content[21016] = {
     0x03, 0x02, 0x23, 0x07, 0x00, 0x03, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x77, 0x03, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00,
@@ -1320,6 +1635,51 @@ static const CF_ShaderBytecode s_flash_shd_bytecode_draw = {
     .content = s_flash_shd_bytecode_draw_content,
     .size = 21016,
 };
+/*
+#extension GL_ARB_shading_language_include : require
+
+#line 1 0
+
+layout(location = 0) in vec2 v_uv;
+layout(location = 1) in vec2 v_pos;
+layout(location = 2) in vec2 v_posH;
+layout(location = 3) in vec4 v_params;
+
+layout(location = 0) out vec4 result;
+
+layout(set = 2, binding = 0) uniform sampler2D u_image;
+
+#line 1 "smooth_uv.shd"
+
+vec2 smooth_uv(vec2 uv, vec2 texture_size)
+{
+ vec2 pixel = uv * texture_size;
+ vec2 seam = floor(pixel + 0.5);
+ pixel = seam + clamp( (pixel - seam) / fwidth(pixel), - 0.5, 0.5);
+ return pixel / texture_size;
+}
+#line 12 0
+#line 1 "shader_stub.shd"
+ vec4 shader(vec4 color, vec2 pos, vec2 screen_uv, vec4 params)
+{
+ return vec4(mix(color.rgb, params.rgb, params.a), color.a);
+}
+#line 13 0
+
+layout(set = 3, binding = 0) uniform uniform_block {
+ vec2 u_texture_size;
+ int u_alpha_discard;
+};
+
+void main() {
+ vec4 color = texture(u_image, smooth_uv(v_uv, u_texture_size));
+ vec2 screen_uv = (v_posH + vec2(1, - 1)) * 0.5 * vec2(1, - 1);
+ vec4 c = shader(color, v_pos, screen_uv, v_params);
+ if (u_alpha_discard != 0 && c.a == 0) discard;
+ result = c;
+}
+
+*/
 static const uint8_t s_flash_shd_bytecode_blit_content[3364] = {
     0x03, 0x02, 0x23, 0x07, 0x00, 0x03, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x84, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00,
diff --git a/samples/waves_data/waves_shd.h b/samples/waves_data/waves_shd.h
index 7e52501f..2ff9e817 100644
--- a/samples/waves_data/waves_shd.h
+++ b/samples/waves_data/waves_shd.h
@@ -1,5 +1,336 @@
 #pragma once
 
+/*
+#extension GL_ARB_shading_language_include : require
+
+#line 1 0
+
+layout(location = 0) in vec2 v_pos;
+layout(location = 1) in flat int v_n;
+layout(location = 2) in vec4 v_ab;
+layout(location = 3) in vec4 v_cd;
+layout(location = 4) in vec4 v_ef;
+layout(location = 5) in vec4 v_gh;
+layout(location = 6) in vec2 v_uv;
+layout(location = 7) in vec4 v_col;
+layout(location = 8) in float v_radius;
+layout(location = 9) in float v_stroke;
+layout(location = 10) in float v_aa;
+layout(location = 11) in float v_type;
+layout(location = 12) in float v_alpha;
+layout(location = 13) in float v_fill;
+layout(location = 14) in vec2 v_posH;
+layout(location = 15) in vec4 v_user;
+
+layout(location = 0) out vec4 result;
+
+layout(set = 2, binding = 0) uniform sampler2D u_image;
+
+layout(set = 3, binding = 0) uniform uniform_block {
+ vec2 u_texture_size;
+ int u_alpha_discard;
+};
+
+#line 1 "blend.shd"
+
+
+
+
+vec3 rgb_to_hsv(vec3 c)
+{
+ vec4 K = vec4(0.0, - 1.0 / 3.0, 2.0 / 3.0, - 1.0);
+ vec4 p = c.g < c.b ? vec4(c.bg, K.wz) : vec4(c.gb, K.xy);
+ vec4 q = c.r < p.x ? vec4(p.xyw, c.r) : vec4(c.r, p.yzx);
+ float d = q.x - min(q.w, q.y);
+ float e = 1.0e-10;
+ return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
+}
+
+vec3 hsv_to_rgb(vec3 c)
+{
+ vec3 rgb = clamp(abs(mod(c.x * 6.0 + vec3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0);
+ rgb = rgb * rgb * (3.0 - 2.0 * rgb);
+ return c.z * mix(vec3(1.0), rgb, c.y);
+}
+
+vec3 hue(vec3 base, vec3 tint)
+{
+ base = rgb_to_hsv(base);
+ tint = rgb_to_hsv(tint);
+ return hsv_to_rgb(vec3(tint.r, base.gb));
+}
+
+vec4 hue(vec4 base, vec4 tint)
+{
+ return vec4(hue(base.rgb, tint.rgb), base.a);
+}
+
+float overlay(float base, float blend)
+{
+ return(base <= 0.5) ? 2 * base * blend : 1 - 2 * (1 - base) * (1 - blend);
+}
+
+vec3 overlay(vec3 base, vec3 blend)
+{
+ return vec3(overlay(base.r, blend.r), overlay(base.g, blend.g), overlay(base.b, blend.b));
+}
+
+vec4 overlay(vec4 base, vec4 blend)
+{
+ return vec4(overlay(base.rgb, blend.rgb), base.a);
+}
+
+float softlight(float base, float blend)
+{
+ if (blend <= 0.5) return base - (1 - 2 * blend) * base * (1 - base);
+ else return base + (2.0 * blend - 1) * ( ( (base <= 0.25) ? ( (16.0 * base - 12.0) * base + 4.0) * base : sqrt(base)) - base);
+}
+
+vec3 softlight(vec3 base, vec3 blend)
+{
+ return vec3(softlight(base.r, blend.r), softlight(base.g, blend.g), softlight(base.b, blend.b));
+}
+
+vec4 softlight(vec4 base, vec4 blend)
+{
+ return vec4(softlight(base.rgb, blend.rgb), base.a);
+}
+#line 29 0
+#line 1 "gamma.shd"
+
+vec4 gamma(vec4 c)
+{
+ return vec4(pow(abs(c.rgb), vec3(1.0 / 2.2)), c.a);
+}
+
+vec4 de_gamma(vec4 c)
+{
+ return vec4(pow(abs(c.rgb), vec3(2.2)), c.a);
+}
+#line 30 0
+#line 1 "smooth_uv.shd"
+
+vec2 smooth_uv(vec2 uv, vec2 texture_size)
+{
+ vec2 pixel = uv * texture_size;
+ vec2 seam = floor(pixel + 0.5);
+ pixel = seam + clamp( (pixel - seam) / fwidth(pixel), - 0.5, 0.5);
+ return pixel / texture_size;
+}
+#line 31 0
+#line 1 "distance.shd"
+
+float safe_div(float a, float b)
+{
+ return b == 0.0 ? 0.0 : a / b;
+}
+
+float safe_len(vec2 v)
+{
+ float d = dot(v, v);
+ return d == 0.0 ? 0.0 : sqrt(d);
+}
+
+vec2 safe_norm(vec2 v, float l)
+{
+ return mix(vec2(0), v / l, l == 0.0 ? 0.0 : 1.0);
+}
+
+vec2 skew(vec2 v)
+{
+ return vec2(- v.y, v.x);
+}
+
+float det2(vec2 a, vec2 b)
+{
+ return a.x * b.y - a.y * b.x;
+}
+
+float sdf_stroke(float d)
+{
+ return abs(d) - v_stroke;
+}
+
+float sdf_intersect(float a, float b)
+{
+ return max(a, b);
+}
+
+float sdf_union(float a, float b)
+{
+ return min(a, b);
+}
+
+float sdf_subtract(float d0, float d1)
+{
+ return max(d0, - d1);
+}
+
+float dd(float d)
+{
+ return length(vec2(dFdx(d), dFdy(d)));
+}
+
+
+
+vec4 sdf(vec4 a, vec4 b, float d)
+{
+ float wire_d = sdf_stroke(d);
+ vec4 stroke_aa = mix(b, a, smoothstep(0.0, v_aa, wire_d));
+ vec4 stroke_no_aa = wire_d <= 0.0 ? b : a;
+
+ vec4 fill_aa = mix(b, a, smoothstep(0.0, v_aa, d));
+ vec4 fill_no_aa = clamp(d, - 1.0, 1.0) <= 0.0 ? b : a;
+
+ vec4 stroke = mix(stroke_aa, stroke_aa, v_aa > 0.0 ? 1.0 : 0.0);
+ vec4 fill = mix(fill_no_aa, fill_aa, v_aa > 0.0 ? 1.0 : 0.0);
+
+ result = mix(stroke, fill, v_fill);
+ return result;
+}
+
+float distance_aabb(vec2 p, vec2 he)
+{
+ vec2 d = abs(p) - he;
+ return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0);
+}
+
+float distance_box(vec2 p, vec2 c, vec2 he, vec2 u)
+{
+ mat2 m = transpose(mat2(u, skew(u)));
+ p = p - c;
+ p = m * p;
+ return distance_aabb(p, he);
+}
+
+
+float distance_segment(vec2 p, vec2 a, vec2 b)
+{
+ vec2 n = b - a;
+ vec2 pa = p - a;
+ float d = safe_div(dot(pa, n), dot(n, n));
+ float h = clamp(d, 0.0, 1.0);
+ return safe_len(pa - h * n);
+}
+
+
+float distance_triangle(vec2 p, vec2 a, vec2 b, vec2 c)
+{
+ vec2 e0 = b - a;
+ vec2 e1 = c - b;
+ vec2 e2 = a - c;
+
+ vec2 v0 = p - a;
+ vec2 v1 = p - b;
+ vec2 v2 = p - c;
+
+ vec2 pq0 = v0 - e0 * clamp(safe_div(dot(v0, e0), dot(e0, e0)), 0.0, 1.0);
+ vec2 pq1 = v1 - e1 * clamp(safe_div(dot(v1, e1), dot(e1, e1)), 0.0, 1.0);
+ vec2 pq2 = v2 - e2 * clamp(safe_div(dot(v2, e2), dot(e2, e2)), 0.0, 1.0);
+
+ float s = det2(e0, e2);
+ vec2 d = min(min(vec2(dot(pq0, pq0), s * det2(v0, e0)),
+                  vec2(dot(pq1, pq1), s * det2(v1, e1))),
+                  vec2(dot(pq2, pq2), s * det2(v2, e2)));
+
+ return - sqrt(d.x) * sign(d.y);
+}
+
+
+float distance_polygon(vec2 p, vec2[8] v, int N)
+{
+ float d = dot(p - v[0], p - v[0]);
+ float s = 1.0;
+ for (int i = 0, j = N - 1; i < N; j = i, i ++) {
+  vec2 e = v[j] - v[i];
+  vec2 w = p - v[i];
+  vec2 b = w - e * clamp(dot(w, e) / dot(e, e), 0.0, 1.0);
+  d = min(d, dot(b, b));
+
+  bvec3 cond = bvec3(p.y >= v[i].y,
+                     p.y < v[j].y,
+                     e.x * w.y > e.y * w.x);
+  if (all(cond) || all(not(cond))) {
+   s = - s;
+  }
+ }
+
+ return s * sqrt(d);
+}
+#line 32 0
+#line 1 "shader_stub.shd"
+ layout(set = 2, binding = 1) uniform sampler2D water_tex;
+layout(set = 2, binding = 2) uniform sampler2D noise_tex;
+
+layout(set = 3, binding = 1) uniform shd_uniforms {
+ float amplitude;
+ float time;
+ float show_noise;
+};
+
+vec4 shader(vec4 color, vec2 pos, vec2 screen_uv, vec4 params)
+{
+ vec2 noise_uv = screen_uv;
+ noise_uv.x *= 640.0 / 128.0;
+ noise_uv.y *= 480.0 / 128.0;
+ vec4 noise_c = vec4(texture(noise_tex, noise_uv * 0.5 + time).r - 0.5);
+ vec2 offset = vec2(1.0 / 640.0, 1.0 / 480.0) * noise_c.r * amplitude;
+ vec4 c = texture(water_tex, screen_uv + offset);
+ c = show_noise > 0 ? noise_c + vec4(0.5) : c;
+ return c;
+}
+#line 33 0
+
+
+vec2 pts[8];
+
+void main()
+{
+ bool is_sprite = v_type >= (0.0 / 255.0) && v_type < (0.5 / 255.0);
+ bool is_text = v_type > (0.5 / 255.0) && v_type < (1.5 / 255.0);
+ bool is_box = v_type > (1.5 / 255.0) && v_type < (2.5 / 255.0);
+ bool is_seg = v_type > (2.5 / 255.0) && v_type < (3.5 / 255.0);
+ bool is_tri = v_type > (3.5 / 255.0) && v_type < (4.5 / 255.0);
+ bool is_tri_sdf = v_type > (4.5 / 255.0) && v_type < (5.5 / 255.0);
+ bool is_poly = v_type > (5.5 / 255.0) && v_type < (6.5 / 255.0);
+
+
+ vec4 c = vec4(0);
+ c = ! (is_sprite && is_text) ? de_gamma(texture(u_image, smooth_uv(v_uv, u_texture_size))) : c;
+ c = is_sprite ? gamma(c) : c;
+ c = is_text ? v_col * c.a : c;
+ c = is_tri ? v_col : c;
+
+
+ float d = 0;
+ if (is_box) {
+  d = distance_box(v_pos, v_ab.xy, v_ab.zw, v_cd.xy);
+ } else if (is_seg) {
+  d = distance_segment(v_pos, v_ab.xy, v_ab.zw);
+  d = min(d, distance_segment(v_pos, v_ab.zw, v_cd.xy));
+ } else if (is_tri_sdf) {
+  d = distance_triangle(v_pos, v_ab.xy, v_ab.zw, v_cd.xy);
+ } else if (is_poly) {
+  pts[0] = v_ab.xy;
+  pts[1] = v_ab.zw;
+  pts[2] = v_cd.xy;
+  pts[3] = v_cd.zw;
+  pts[4] = v_ef.xy;
+  pts[5] = v_ef.zw;
+  pts[6] = v_gh.xy;
+  pts[7] = v_gh.zw;
+  d = distance_polygon(v_pos, pts, v_n);
+ }
+ c = (! is_sprite && ! is_text && ! is_tri) ? sdf(c, v_col, d - v_radius) : c;
+
+ c *= v_alpha;
+ vec2 screen_uv = (v_posH + vec2(1, - 1)) * 0.5 * vec2(1, - 1);
+ c = shader(c, v_pos, screen_uv, v_user);
+ if (u_alpha_discard != 0 && c.a == 0) discard;
+ result = c;
+}
+
+*/
 static const uint8_t s_waves_shd_bytecode_draw_content[22272] = {
     0x03, 0x02, 0x23, 0x07, 0x00, 0x03, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0xA4, 0x03, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00,
@@ -1398,6 +1729,67 @@ static const CF_ShaderBytecode s_waves_shd_bytecode_draw = {
     .content = s_waves_shd_bytecode_draw_content,
     .size = 22272,
 };
+/*
+#extension GL_ARB_shading_language_include : require
+
+#line 1 0
+
+layout(location = 0) in vec2 v_uv;
+layout(location = 1) in vec2 v_pos;
+layout(location = 2) in vec2 v_posH;
+layout(location = 3) in vec4 v_params;
+
+layout(location = 0) out vec4 result;
+
+layout(set = 2, binding = 0) uniform sampler2D u_image;
+
+#line 1 "smooth_uv.shd"
+
+vec2 smooth_uv(vec2 uv, vec2 texture_size)
+{
+ vec2 pixel = uv * texture_size;
+ vec2 seam = floor(pixel + 0.5);
+ pixel = seam + clamp( (pixel - seam) / fwidth(pixel), - 0.5, 0.5);
+ return pixel / texture_size;
+}
+#line 12 0
+#line 1 "shader_stub.shd"
+ layout(set = 2, binding = 1) uniform sampler2D water_tex;
+layout(set = 2, binding = 2) uniform sampler2D noise_tex;
+
+layout(set = 3, binding = 1) uniform shd_uniforms {
+ float amplitude;
+ float time;
+ float show_noise;
+};
+
+vec4 shader(vec4 color, vec2 pos, vec2 screen_uv, vec4 params)
+{
+ vec2 noise_uv = screen_uv;
+ noise_uv.x *= 640.0 / 128.0;
+ noise_uv.y *= 480.0 / 128.0;
+ vec4 noise_c = vec4(texture(noise_tex, noise_uv * 0.5 + time).r - 0.5);
+ vec2 offset = vec2(1.0 / 640.0, 1.0 / 480.0) * noise_c.r * amplitude;
+ vec4 c = texture(water_tex, screen_uv + offset);
+ c = show_noise > 0 ? noise_c + vec4(0.5) : c;
+ return c;
+}
+#line 13 0
+
+layout(set = 3, binding = 0) uniform uniform_block {
+ vec2 u_texture_size;
+ int u_alpha_discard;
+};
+
+void main() {
+ vec4 color = texture(u_image, smooth_uv(v_uv, u_texture_size));
+ vec2 screen_uv = (v_posH + vec2(1, - 1)) * 0.5 * vec2(1, - 1);
+ vec4 c = shader(color, v_pos, screen_uv, v_params);
+ if (u_alpha_discard != 0 && c.a == 0) discard;
+ result = c;
+}
+
+*/
 static const uint8_t s_waves_shd_bytecode_blit_content[4652] = {
     0x03, 0x02, 0x23, 0x07, 0x00, 0x03, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0xB3, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00,
diff --git a/src/cute_graphics.cpp b/src/cute_graphics.cpp
index 2995bf2d..d61be36d 100644
--- a/src/cute_graphics.cpp
+++ b/src/cute_graphics.cpp
@@ -373,15 +373,21 @@ const dyna uint8_t* cf_compile_shader_to_bytecode_internal(const char* shader_sr
 		include_dirs[num_include_dirs++] = app->shader_directory.c_str();
 	}
 
-	cute_shader_config_t config;
-	config.automatic_include_guard = true;
-	config.num_builtin_includes = num_builtin_includes;
-	config.builtin_includes = builtin_includes;
-	config.num_include_dirs = num_include_dirs;
-	config.include_dirs = include_dirs;
-	config.num_builtin_defines = 0;
-	config.builtin_defines = NULL;
-	config.vfs = &s_cute_shader_vfs;
+	cute_shader_config_t config = {
+		.num_builtin_defines = 0,
+		.builtin_defines = NULL,
+
+		.num_builtin_includes = num_builtin_includes,
+		.builtin_includes = builtin_includes,
+
+		.num_include_dirs = num_include_dirs,
+		.include_dirs = include_dirs,
+
+		.automatic_include_guard = true,
+		.return_preprocessed_source = false,
+
+		.vfs = &s_cute_shader_vfs,
+	};
 
 	cute_shader_result_t result = cute_shader_compile(shader_src, stage, config);
 	if (result.success) {
diff --git a/src/cute_shader/cute_shader.cpp b/src/cute_shader/cute_shader.cpp
index 4409b481..54cbdd80 100644
--- a/src/cute_shader/cute_shader.cpp
+++ b/src/cute_shader/cute_shader.cpp
@@ -156,11 +156,10 @@ cute_shader_failure(const char* message, const char* info_log, const char* debug
 	snprintf(full_msg, msg_size, "%s\n%s\n\n%s\n", message, info_log, debug_log);
 	full_msg[msg_size] = '\0';
 
-	cute_shader_result_t result;
-	result.success = false;
-	result.bytecode = NULL;
-	result.bytecode_size = 0;
-	result.error_message = full_msg;
+	cute_shader_result_t result = {
+		.success = false,
+		.error_message = full_msg,
+	};
 	return result;
 }
 
@@ -266,19 +265,31 @@ cute_shader_compile(
 	options.validate = false;
 	glslang::GlslangToSpv(*program.getIntermediate(glslang_stage), spirv, &options);
 
+	char* preprocessed_source_copy = NULL;
+	size_t preprocessed_source_size = 0;
+	if (config.return_preprocessed_source) {
+		preprocessed_source_size = preprocessed_source.size();
+		preprocessed_source_copy = (char*)malloc(preprocessed_source_size + 1);
+		memcpy(preprocessed_source_copy, preprocessed_source.c_str(), preprocessed_source_size);
+		preprocessed_source_copy[preprocessed_source_size] = '\0';
+	}
+
 	size_t bytecode_size = sizeof(uint32_t) * spirv.size();
 	void* bytecode = malloc(bytecode_size);
 	memcpy(bytecode, spirv.data(), bytecode_size);
-	cute_shader_result_t result;
-	result.success = true;
-	result.bytecode = bytecode;
-	result.bytecode_size = bytecode_size;
-	result.error_message = NULL;
+	cute_shader_result_t result = {
+		.success = true,
+		.bytecode = bytecode,
+		.bytecode_size = bytecode_size,
+		.preprocessed_source = preprocessed_source_copy,
+		.preprocessed_source_size = preprocessed_source_size,
+	};
 	return result;
 }
 
 void
 cute_shader_free_result(cute_shader_result_t result) {
 	free(result.bytecode);
+	free((char*)result.preprocessed_source);
 	free((char*)result.error_message);
 }
diff --git a/src/cute_shader/cute_shader.h b/src/cute_shader/cute_shader.h
index ccdac822..9ca2f212 100644
--- a/src/cute_shader/cute_shader.h
+++ b/src/cute_shader/cute_shader.h
@@ -13,11 +13,6 @@ typedef struct cute_shader_file_t {
 	const char* content;
 } cute_shader_file_t;
 
-typedef struct cute_shader_str_t {
-	size_t len;
-	char* chars;
-} cute_shader_str_t;
-
 typedef struct cute_shader_vfs_t {
 	char* (*read_file_content)(const char* path, size_t* len, void* fcontext);
 	void (*free_file_content)(char* content, void* context);
@@ -40,14 +35,20 @@ typedef struct cute_shader_config_t {
 	const char** include_dirs;
 
 	bool automatic_include_guard;
+	bool return_preprocessed_source;
 
 	cute_shader_vfs_t* vfs;
 } cute_shader_config_t;
 
 typedef struct cute_shader_result_t {
 	bool success;
+
 	void* bytecode;
 	size_t bytecode_size;
+
+	const char* preprocessed_source;
+	size_t preprocessed_source_size;
+
 	const char* error_message;
 } cute_shader_result_t;
 
diff --git a/src/cute_shader/cute_shaderc.cpp b/src/cute_shader/cute_shaderc.cpp
index e2c523ec..43de0af5 100644
--- a/src/cute_shader/cute_shaderc.cpp
+++ b/src/cute_shader/cute_shaderc.cpp
@@ -72,6 +72,13 @@ static bool write_bytecode_struct(
 	const char* suffix
 ) {
 	const uint8_t* content = (const uint8_t*)compile_result.bytecode;
+
+	// Write preprocessed shader as comment
+	fprintf(file, "/*\n");
+	fprintf(file, "%.*s\n", (int)compile_result.preprocessed_source_size, compile_result.preprocessed_source);
+	fprintf(file, "*/\n");
+
+	// Write the actual struct
 	fprintf(file, "static const uint8_t %s%s_content[%zu] = {", var_name, suffix, compile_result.bytecode_size);
 	for (size_t i = 0; i < compile_result.bytecode_size; ++i) {
 		if ((i % HEADER_LINE_SIZE) == 0) {
@@ -291,6 +298,7 @@ int main(int argc, const char* argv[]) {
 			.include_dirs = include_dirs,
 
 			.automatic_include_guard = true,
+			.return_preprocessed_source = true,
 		};
 
 		cute_shader_result_t draw_shader_result = cute_shader_compile(
@@ -352,6 +360,7 @@ int main(int argc, const char* argv[]) {
 			.include_dirs = include_dirs,
 
 			.automatic_include_guard = true,
+			.return_preprocessed_source = true,
 		};
 
 		int num_builtin_shaders = sizeof(s_builtin_shader_sources) / sizeof(s_builtin_shader_sources[0]);
@@ -422,6 +431,7 @@ int main(int argc, const char* argv[]) {
 			.include_dirs = include_dirs,
 
 			.automatic_include_guard = true,
+			.return_preprocessed_source = true,
 		};
 
 		cute_shader_result_t result = cute_shader_compile(
diff --git a/src/data/builtin_shaders_bytecode.h b/src/data/builtin_shaders_bytecode.h
index a3a710e4..6398d6dc 100644
--- a/src/data/builtin_shaders_bytecode.h
+++ b/src/data/builtin_shaders_bytecode.h
@@ -1,5 +1,68 @@
 #pragma once
 
+/*
+#extension GL_ARB_shading_language_include : require
+
+#line 1 0
+
+layout(location = 0) in vec2 in_pos;
+layout(location = 1) in vec2 in_posH;
+layout(location = 2) in vec2 in_uv;
+
+layout(location = 3) in int in_n;
+layout(location = 4) in vec4 in_ab;
+layout(location = 5) in vec4 in_cd;
+layout(location = 6) in vec4 in_ef;
+layout(location = 7) in vec4 in_gh;
+layout(location = 8) in vec4 in_col;
+layout(location = 9) in float in_radius;
+layout(location = 10) in float in_stroke;
+layout(location = 11) in float in_aa;
+layout(location = 12) in vec4 in_params;
+layout(location = 13) in vec4 in_user_params;
+
+layout(location = 0) out vec2 v_pos;
+layout(location = 1) out int v_n;
+layout(location = 2) out vec4 v_ab;
+layout(location = 3) out vec4 v_cd;
+layout(location = 4) out vec4 v_ef;
+layout(location = 5) out vec4 v_gh;
+layout(location = 6) out vec2 v_uv;
+layout(location = 7) out vec4 v_col;
+layout(location = 8) out float v_radius;
+layout(location = 9) out float v_stroke;
+layout(location = 10) out float v_aa;
+layout(location = 11) out float v_type;
+layout(location = 12) out float v_alpha;
+layout(location = 13) out float v_fill;
+layout(location = 14) out vec2 v_posH;
+layout(location = 15) out vec4 v_user;
+
+void main()
+{
+ v_pos = in_pos;
+ v_n = in_n;
+ v_ab = in_ab;
+ v_cd = in_cd;
+ v_ef = in_ef;
+ v_gh = in_gh;
+ v_uv = in_uv;
+ v_col = in_col;
+ v_radius = in_radius;
+ v_stroke = in_stroke * 0.5;
+ v_aa = in_aa;
+ v_type = in_params.r;
+ v_alpha = in_params.g;
+ v_fill = in_params.b;
+
+
+ vec4 posH = vec4(in_posH, 0, 1);
+ gl_Position = posH;
+ v_posH = in_posH;
+ v_user = in_user_params;
+}
+
+*/
 static const uint8_t s_draw_vs_bytecode_content[3176] = {
     0x03, 0x02, 0x23, 0x07, 0x00, 0x03, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x58, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00,
@@ -205,6 +268,322 @@ static const CF_ShaderBytecode s_draw_vs_bytecode = {
     .content = s_draw_vs_bytecode_content,
     .size = 3176,
 };
+/*
+#extension GL_ARB_shading_language_include : require
+
+#line 1 0
+
+layout(location = 0) in vec2 v_pos;
+layout(location = 1) in flat int v_n;
+layout(location = 2) in vec4 v_ab;
+layout(location = 3) in vec4 v_cd;
+layout(location = 4) in vec4 v_ef;
+layout(location = 5) in vec4 v_gh;
+layout(location = 6) in vec2 v_uv;
+layout(location = 7) in vec4 v_col;
+layout(location = 8) in float v_radius;
+layout(location = 9) in float v_stroke;
+layout(location = 10) in float v_aa;
+layout(location = 11) in float v_type;
+layout(location = 12) in float v_alpha;
+layout(location = 13) in float v_fill;
+layout(location = 14) in vec2 v_posH;
+layout(location = 15) in vec4 v_user;
+
+layout(location = 0) out vec4 result;
+
+layout(set = 2, binding = 0) uniform sampler2D u_image;
+
+layout(set = 3, binding = 0) uniform uniform_block {
+ vec2 u_texture_size;
+ int u_alpha_discard;
+};
+
+#line 1 "blend.shd"
+
+
+
+
+vec3 rgb_to_hsv(vec3 c)
+{
+ vec4 K = vec4(0.0, - 1.0 / 3.0, 2.0 / 3.0, - 1.0);
+ vec4 p = c.g < c.b ? vec4(c.bg, K.wz) : vec4(c.gb, K.xy);
+ vec4 q = c.r < p.x ? vec4(p.xyw, c.r) : vec4(c.r, p.yzx);
+ float d = q.x - min(q.w, q.y);
+ float e = 1.0e-10;
+ return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
+}
+
+vec3 hsv_to_rgb(vec3 c)
+{
+ vec3 rgb = clamp(abs(mod(c.x * 6.0 + vec3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0);
+ rgb = rgb * rgb * (3.0 - 2.0 * rgb);
+ return c.z * mix(vec3(1.0), rgb, c.y);
+}
+
+vec3 hue(vec3 base, vec3 tint)
+{
+ base = rgb_to_hsv(base);
+ tint = rgb_to_hsv(tint);
+ return hsv_to_rgb(vec3(tint.r, base.gb));
+}
+
+vec4 hue(vec4 base, vec4 tint)
+{
+ return vec4(hue(base.rgb, tint.rgb), base.a);
+}
+
+float overlay(float base, float blend)
+{
+ return(base <= 0.5) ? 2 * base * blend : 1 - 2 * (1 - base) * (1 - blend);
+}
+
+vec3 overlay(vec3 base, vec3 blend)
+{
+ return vec3(overlay(base.r, blend.r), overlay(base.g, blend.g), overlay(base.b, blend.b));
+}
+
+vec4 overlay(vec4 base, vec4 blend)
+{
+ return vec4(overlay(base.rgb, blend.rgb), base.a);
+}
+
+float softlight(float base, float blend)
+{
+ if (blend <= 0.5) return base - (1 - 2 * blend) * base * (1 - base);
+ else return base + (2.0 * blend - 1) * ( ( (base <= 0.25) ? ( (16.0 * base - 12.0) * base + 4.0) * base : sqrt(base)) - base);
+}
+
+vec3 softlight(vec3 base, vec3 blend)
+{
+ return vec3(softlight(base.r, blend.r), softlight(base.g, blend.g), softlight(base.b, blend.b));
+}
+
+vec4 softlight(vec4 base, vec4 blend)
+{
+ return vec4(softlight(base.rgb, blend.rgb), base.a);
+}
+#line 29 0
+#line 1 "gamma.shd"
+
+vec4 gamma(vec4 c)
+{
+ return vec4(pow(abs(c.rgb), vec3(1.0 / 2.2)), c.a);
+}
+
+vec4 de_gamma(vec4 c)
+{
+ return vec4(pow(abs(c.rgb), vec3(2.2)), c.a);
+}
+#line 30 0
+#line 1 "smooth_uv.shd"
+
+vec2 smooth_uv(vec2 uv, vec2 texture_size)
+{
+ vec2 pixel = uv * texture_size;
+ vec2 seam = floor(pixel + 0.5);
+ pixel = seam + clamp( (pixel - seam) / fwidth(pixel), - 0.5, 0.5);
+ return pixel / texture_size;
+}
+#line 31 0
+#line 1 "distance.shd"
+
+float safe_div(float a, float b)
+{
+ return b == 0.0 ? 0.0 : a / b;
+}
+
+float safe_len(vec2 v)
+{
+ float d = dot(v, v);
+ return d == 0.0 ? 0.0 : sqrt(d);
+}
+
+vec2 safe_norm(vec2 v, float l)
+{
+ return mix(vec2(0), v / l, l == 0.0 ? 0.0 : 1.0);
+}
+
+vec2 skew(vec2 v)
+{
+ return vec2(- v.y, v.x);
+}
+
+float det2(vec2 a, vec2 b)
+{
+ return a.x * b.y - a.y * b.x;
+}
+
+float sdf_stroke(float d)
+{
+ return abs(d) - v_stroke;
+}
+
+float sdf_intersect(float a, float b)
+{
+ return max(a, b);
+}
+
+float sdf_union(float a, float b)
+{
+ return min(a, b);
+}
+
+float sdf_subtract(float d0, float d1)
+{
+ return max(d0, - d1);
+}
+
+float dd(float d)
+{
+ return length(vec2(dFdx(d), dFdy(d)));
+}
+
+
+
+vec4 sdf(vec4 a, vec4 b, float d)
+{
+ float wire_d = sdf_stroke(d);
+ vec4 stroke_aa = mix(b, a, smoothstep(0.0, v_aa, wire_d));
+ vec4 stroke_no_aa = wire_d <= 0.0 ? b : a;
+
+ vec4 fill_aa = mix(b, a, smoothstep(0.0, v_aa, d));
+ vec4 fill_no_aa = clamp(d, - 1.0, 1.0) <= 0.0 ? b : a;
+
+ vec4 stroke = mix(stroke_aa, stroke_aa, v_aa > 0.0 ? 1.0 : 0.0);
+ vec4 fill = mix(fill_no_aa, fill_aa, v_aa > 0.0 ? 1.0 : 0.0);
+
+ result = mix(stroke, fill, v_fill);
+ return result;
+}
+
+float distance_aabb(vec2 p, vec2 he)
+{
+ vec2 d = abs(p) - he;
+ return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0);
+}
+
+float distance_box(vec2 p, vec2 c, vec2 he, vec2 u)
+{
+ mat2 m = transpose(mat2(u, skew(u)));
+ p = p - c;
+ p = m * p;
+ return distance_aabb(p, he);
+}
+
+
+float distance_segment(vec2 p, vec2 a, vec2 b)
+{
+ vec2 n = b - a;
+ vec2 pa = p - a;
+ float d = safe_div(dot(pa, n), dot(n, n));
+ float h = clamp(d, 0.0, 1.0);
+ return safe_len(pa - h * n);
+}
+
+
+float distance_triangle(vec2 p, vec2 a, vec2 b, vec2 c)
+{
+ vec2 e0 = b - a;
+ vec2 e1 = c - b;
+ vec2 e2 = a - c;
+
+ vec2 v0 = p - a;
+ vec2 v1 = p - b;
+ vec2 v2 = p - c;
+
+ vec2 pq0 = v0 - e0 * clamp(safe_div(dot(v0, e0), dot(e0, e0)), 0.0, 1.0);
+ vec2 pq1 = v1 - e1 * clamp(safe_div(dot(v1, e1), dot(e1, e1)), 0.0, 1.0);
+ vec2 pq2 = v2 - e2 * clamp(safe_div(dot(v2, e2), dot(e2, e2)), 0.0, 1.0);
+
+ float s = det2(e0, e2);
+ vec2 d = min(min(vec2(dot(pq0, pq0), s * det2(v0, e0)),
+                  vec2(dot(pq1, pq1), s * det2(v1, e1))),
+                  vec2(dot(pq2, pq2), s * det2(v2, e2)));
+
+ return - sqrt(d.x) * sign(d.y);
+}
+
+
+float distance_polygon(vec2 p, vec2[8] v, int N)
+{
+ float d = dot(p - v[0], p - v[0]);
+ float s = 1.0;
+ for (int i = 0, j = N - 1; i < N; j = i, i ++) {
+  vec2 e = v[j] - v[i];
+  vec2 w = p - v[i];
+  vec2 b = w - e * clamp(dot(w, e) / dot(e, e), 0.0, 1.0);
+  d = min(d, dot(b, b));
+
+  bvec3 cond = bvec3(p.y >= v[i].y,
+                     p.y < v[j].y,
+                     e.x * w.y > e.y * w.x);
+  if (all(cond) || all(not(cond))) {
+   s = - s;
+  }
+ }
+
+ return s * sqrt(d);
+}
+#line 32 0
+#line 1 "shader_stub.shd"
+
+vec4 shader(vec4 color, vec2 pos, vec2 screen_uv, vec4 params)
+{
+ return color;
+}
+#line 33 0
+
+
+vec2 pts[8];
+
+void main()
+{
+ bool is_sprite = v_type >= (0.0 / 255.0) && v_type < (0.5 / 255.0);
+ bool is_text = v_type > (0.5 / 255.0) && v_type < (1.5 / 255.0);
+ bool is_box = v_type > (1.5 / 255.0) && v_type < (2.5 / 255.0);
+ bool is_seg = v_type > (2.5 / 255.0) && v_type < (3.5 / 255.0);
+ bool is_tri = v_type > (3.5 / 255.0) && v_type < (4.5 / 255.0);
+ bool is_tri_sdf = v_type > (4.5 / 255.0) && v_type < (5.5 / 255.0);
+ bool is_poly = v_type > (5.5 / 255.0) && v_type < (6.5 / 255.0);
+
+
+ vec4 c = vec4(0);
+ c = ! (is_sprite && is_text) ? de_gamma(texture(u_image, smooth_uv(v_uv, u_texture_size))) : c;
+ c = is_sprite ? gamma(c) : c;
+ c = is_text ? v_col * c.a : c;
+ c = is_tri ? v_col : c;
+
+
+ float d = 0;
+ if (is_box) {
+  d = distance_box(v_pos, v_ab.xy, v_ab.zw, v_cd.xy);
+ } else if (is_seg) {
+  d = distance_segment(v_pos, v_ab.xy, v_ab.zw);
+  d = min(d, distance_segment(v_pos, v_ab.zw, v_cd.xy));
+ } else if (is_tri_sdf) {
+  d = distance_triangle(v_pos, v_ab.xy, v_ab.zw, v_cd.xy);
+ } else if (is_poly) {
+  pts[0] = v_ab.xy;
+  pts[1] = v_ab.zw;
+  pts[2] = v_cd.xy;
+  pts[3] = v_cd.zw;
+  pts[4] = v_ef.xy;
+  pts[5] = v_ef.zw;
+  pts[6] = v_gh.xy;
+  pts[7] = v_gh.zw;
+  d = distance_polygon(v_pos, pts, v_n);
+ }
+ c = (! is_sprite && ! is_text && ! is_tri) ? sdf(c, v_col, d - v_radius) : c;
+
+ c *= v_alpha;
+ vec2 screen_uv = (v_posH + vec2(1, - 1)) * 0.5 * vec2(1, - 1);
+ c = shader(c, v_pos, screen_uv, v_user);
+ if (u_alpha_discard != 0 && c.a == 0) discard;
+ result = c;
+}
+
+*/
 static const uint8_t s_draw_fs_bytecode_content[20720] = {
     0x03, 0x02, 0x23, 0x07, 0x00, 0x03, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x6A, 0x03, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00,
@@ -1506,6 +1885,19 @@ static const CF_ShaderBytecode s_draw_fs_bytecode = {
     .content = s_draw_fs_bytecode_content,
     .size = 20720,
 };
+/*
+#extension GL_ARB_shading_language_include : require
+
+#line 1 0
+
+layout(location = 0) in vec2 in_posH;
+
+void main()
+{
+ gl_Position = vec4(in_posH, 0, 1);
+}
+
+*/
 static const uint8_t s_basic_vs_bytecode_content[896] = {
     0x03, 0x02, 0x23, 0x07, 0x00, 0x03, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x1B, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00,
@@ -1568,6 +1960,19 @@ static const CF_ShaderBytecode s_basic_vs_bytecode = {
     .content = s_basic_vs_bytecode_content,
     .size = 896,
 };
+/*
+#extension GL_ARB_shading_language_include : require
+
+#line 1 0
+
+layout(location = 0) out vec4 result;
+
+void main()
+{
+ result = vec4(1);
+}
+
+*/
 static const uint8_t s_basic_fs_bytecode_content[408] = {
     0x03, 0x02, 0x23, 0x07, 0x00, 0x03, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x0C, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00,
@@ -1600,6 +2005,19 @@ static const CF_ShaderBytecode s_basic_fs_bytecode = {
     .content = s_basic_fs_bytecode_content,
     .size = 408,
 };
+/*
+#extension GL_ARB_shading_language_include : require
+
+#line 1 0
+
+layout(location = 0) in vec2 in_posH;
+
+void main()
+{
+ gl_Position = vec4(in_posH, 0, 1);
+}
+
+*/
 static const uint8_t s_backbuffer_vs_bytecode_content[896] = {
     0x03, 0x02, 0x23, 0x07, 0x00, 0x03, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x1B, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00,
@@ -1662,6 +2080,19 @@ static const CF_ShaderBytecode s_backbuffer_vs_bytecode = {
     .content = s_backbuffer_vs_bytecode_content,
     .size = 896,
 };
+/*
+#extension GL_ARB_shading_language_include : require
+
+#line 1 0
+
+layout(location = 0) out vec4 result;
+
+void main()
+{
+ result = vec4(1);
+}
+
+*/
 static const uint8_t s_backbuffer_fs_bytecode_content[408] = {
     0x03, 0x02, 0x23, 0x07, 0x00, 0x03, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x0C, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00,
@@ -1694,6 +2125,30 @@ static const CF_ShaderBytecode s_backbuffer_fs_bytecode = {
     .content = s_backbuffer_fs_bytecode_content,
     .size = 408,
 };
+/*
+#extension GL_ARB_shading_language_include : require
+
+#line 1 0
+
+layout(location = 0) in vec2 in_pos;
+layout(location = 1) in vec2 in_posH;
+layout(location = 2) in vec2 in_uv;
+layout(location = 3) in vec4 in_params;
+
+layout(location = 0) out vec2 v_uv;
+layout(location = 1) out vec2 v_pos;
+layout(location = 2) out vec2 v_posH;
+layout(location = 3) out vec4 v_params;
+
+void main() {
+ v_uv = in_uv;
+ v_pos = in_pos;
+ v_posH = in_posH;
+ v_params = in_params;
+ gl_Position = vec4(in_posH, 0, 1);
+}
+
+*/
 static const uint8_t s_blit_vs_bytecode_content[1412] = {
     0x03, 0x02, 0x23, 0x07, 0x00, 0x03, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x28, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00,
@@ -1789,6 +2244,52 @@ static const CF_ShaderBytecode s_blit_vs_bytecode = {
     .content = s_blit_vs_bytecode_content,
     .size = 1412,
 };
+/*
+#extension GL_ARB_shading_language_include : require
+
+#line 1 0
+
+layout(location = 0) in vec2 v_uv;
+layout(location = 1) in vec2 v_pos;
+layout(location = 2) in vec2 v_posH;
+layout(location = 3) in vec4 v_params;
+
+layout(location = 0) out vec4 result;
+
+layout(set = 2, binding = 0) uniform sampler2D u_image;
+
+#line 1 "smooth_uv.shd"
+
+vec2 smooth_uv(vec2 uv, vec2 texture_size)
+{
+ vec2 pixel = uv * texture_size;
+ vec2 seam = floor(pixel + 0.5);
+ pixel = seam + clamp( (pixel - seam) / fwidth(pixel), - 0.5, 0.5);
+ return pixel / texture_size;
+}
+#line 12 0
+#line 1 "shader_stub.shd"
+
+vec4 shader(vec4 color, vec2 pos, vec2 screen_uv, vec4 params)
+{
+ return color;
+}
+#line 13 0
+
+layout(set = 3, binding = 0) uniform uniform_block {
+ vec2 u_texture_size;
+ int u_alpha_discard;
+};
+
+void main() {
+ vec4 color = texture(u_image, smooth_uv(v_uv, u_texture_size));
+ vec2 screen_uv = (v_posH + vec2(1, - 1)) * 0.5 * vec2(1, - 1);
+ vec4 c = shader(color, v_pos, screen_uv, v_params);
+ if (u_alpha_discard != 0 && c.a == 0) discard;
+ result = c;
+}
+
+*/
 static const uint8_t s_blit_fs_bytecode_content[3052] = {
     0x03, 0x02, 0x23, 0x07, 0x00, 0x03, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x76, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00,
@@ -1986,6 +2487,30 @@ static const CF_ShaderBytecode s_blit_fs_bytecode = {
     .content = s_blit_fs_bytecode_content,
     .size = 3052,
 };
+/*
+#extension GL_ARB_shading_language_include : require
+
+#line 1 0
+
+layout(location = 0) in vec2 pos;
+layout(location = 1) in vec2 uv;
+layout(location = 2) in vec4 col;
+
+layout(location = 0) out vec4 v_col;
+layout(location = 1) out vec2 v_uv;
+
+layout(set = 1, binding = 0) uniform uniform_block {
+ mat4 ProjectionMatrix;
+};
+
+void main()
+{
+ v_col = col;
+ v_uv = uv;
+ gl_Position = ProjectionMatrix * vec4(pos.xy, 0, 1);
+}
+
+*/
 static const uint8_t s_imgui_vs_bytecode_content[1480] = {
     0x03, 0x02, 0x23, 0x07, 0x00, 0x03, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x2B, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00,
@@ -2085,6 +2610,25 @@ static const CF_ShaderBytecode s_imgui_vs_bytecode = {
     .content = s_imgui_vs_bytecode_content,
     .size = 1480,
 };
+/*
+#extension GL_ARB_shading_language_include : require
+
+#line 1 0
+
+layout(location = 0) in vec4 v_col;
+layout(location = 1) in vec2 v_uv;
+
+layout(location = 0) out vec4 result;
+
+layout(set = 2, binding = 0) uniform sampler2D u_image;
+
+void main()
+{
+ vec4 color = v_col * texture(u_image, v_uv);
+ result = color;
+}
+
+*/
 static const uint8_t s_imgui_fs_bytecode_content[808] = {
     0x03, 0x02, 0x23, 0x07, 0x00, 0x03, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x1B, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00,