1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
|
#version 450
// pardes's Shadertoy prefix (docs/render-pipeline.md §6), put before every
// post pass: a bundled one in shaders/post/ at build time, a user's file at
// run time. The uniform names, types and std140 order are ghostty 1.3.2's
// (src/renderer/shaders/shadertoy_prefix.glsl), so a shader written for
// ghostty runs here; only the bindings are SDL GPU's (a fragment shader's
// textures in set 2, its uniform blocks in set 3). Y is down everywhere, as
// on ghostty's Metal renderer: fragCoord, iChannel0's uv and the cursor
// rectangles share one top-left space, and iCurrentCursor is (left x,
// BOTTOM-edge y, width, height) in framebuffer pixels.
layout(set = 3, binding = 0, std140) uniform Globals {
uniform vec3 iResolution;
uniform float iTime;
uniform float iTimeDelta;
uniform float iFrameRate;
uniform int iFrame;
uniform float iChannelTime[4];
uniform vec3 iChannelResolution[4];
uniform vec4 iMouse;
uniform vec4 iDate;
uniform float iSampleRate;
uniform vec4 iCurrentCursor;
uniform vec4 iPreviousCursor;
uniform vec4 iCurrentCursorColor;
uniform vec4 iPreviousCursorColor;
uniform int iCurrentCursorStyle;
uniform int iPreviousCursorStyle;
uniform int iCursorVisible;
uniform float iTimeCursorChange;
uniform float iTimeFocus;
uniform int iFocus;
};
// ghostty's one block, cut in three: SDL GPU binds at most 4 KiB of a
// uniform block (Vulkan), and the palette alone is 4 KiB. The names and
// their order are ghostty's; each block is a slice of the same memory.
layout(set = 3, binding = 1, std140) uniform Palette {
uniform vec3 iPalette[256];
};
layout(set = 3, binding = 2, std140) uniform Colors {
uniform vec3 iBackgroundColor;
uniform vec3 iForegroundColor;
uniform vec3 iCursorColor;
uniform vec3 iCursorText;
uniform vec3 iSelectionForegroundColor;
uniform vec3 iSelectionBackgroundColor;
};
// pardes's own: pardesOpaque is 1 when the window is opaque, so a pass
// cannot punch holes in it through its alpha; pardesLevel is a bundled
// pass's strength, 1 to 3 (`Crt 3`), and 0 for a user's file; pardesScale
// is the display's density (2 on a 2x screen), for a pass whose pattern is
// in logical pixels. iResolution, fragCoord and iMouse stay in the display's
// pixels, as ghostty's do. pardesCap is the most a bundled pass may do on
// this theme and still leave its text, and its selection's, at their own
// contrast or 4.5 (docs/effects.md); pardesSpare holds pardesSpareCount
// rectangles (x0, y0, x1, y1 in fragCoord's pixels) a bundled pass leaves
// alone: every tag, grip and notice, and the cursor, so no pass ever touches
// a focus indicator.
layout(set = 3, binding = 3, std140) uniform PardesPost {
float pardesOpaque;
float pardesLevel;
float pardesScale;
float pardesCap;
float pardesSpareCount;
vec4 pardesSpare[32];
};
bool pardesSpared(vec2 p) {
for (int i = 0; i < int(pardesSpareCount); i++) {
vec4 r = pardesSpare[i];
if (p.x >= r.x && p.y >= r.y && p.x < r.z && p.y < r.w) return true;
}
return false;
}
// A bundled pass's constant at levels 1, 2 and 3.
float pardesByLevel(float one, float two, float three) {
return pardesLevel < 1.5 ? one : (pardesLevel < 2.5 ? two : three);
}
#define CURSORSTYLE_BLOCK 0
#define CURSORSTYLE_BLOCK_HOLLOW 1
#define CURSORSTYLE_BAR 2
#define CURSORSTYLE_UNDERLINE 3
#define CURSORSTYLE_LOCK 4
layout(set = 2, binding = 0) uniform sampler2D iChannel0;
layout(location = 0) out vec4 _fragColor;
#define texture2D texture
void mainImage(out vec4 fragColor, in vec2 fragCoord);
void main() {
mainImage(_fragColor, gl_FragCoord.xy);
if (pardesOpaque > 0.5) _fragColor.a = 1.0;
}
// The shader's own lines count from 1, so glslc's errors name them.
#line 1
// Vignette: the window's corners fall a little into shade, in linear light,
// so the eye settles on the middle. Round, whatever the window's shape;
// nothing within half the way to a corner is touched. At most 28% at the
// far corners (level 3), and never more than pardesCap, so text and its
// selection keep their own contrast or 4.5. Tags, grips, notices and the
// cursor are left alone (pardesSpare): the focus indicators never darken.
// It is a Shadertoy file: pardes puts its prefix before it.
float strength() { return min(pardesByLevel(0.10, 0.18, 0.28), pardesCap); }
vec3 toLinear(vec3 c) {
return mix(pow((c + 0.055) / 1.055, vec3(2.4)), c / 12.92, lessThanEqual(c, vec3(0.04045)));
}
vec3 toSrgb(vec3 c) {
c = max(c, vec3(0.0));
return mix(1.055 * pow(c, vec3(1.0 / 2.4)) - 0.055, c * 12.92, lessThanEqual(c, vec3(0.0031308)));
}
float ign(vec2 p) {
return fract(52.9829189 * fract(dot(p, vec2(0.06711056, 0.00583715))));
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
vec4 frame = texture(iChannel0, uv);
if (pardesSpared(fragCoord)) {
fragColor = frame;
return;
}
// 0 at the middle, 1 at a corner, on a circle's measure.
vec2 half_size = 0.5 * iResolution.xy;
float r = length(fragCoord - half_size) / length(half_size);
float shade = strength() * pow(smoothstep(0.5, 1.0, r), 1.5);
vec3 srgb = toSrgb(toLinear(frame.rgb) * (1.0 - shade));
srgb += (ign(fragCoord) - 0.5) / 255.0 * step(1e-4, shade);
fragColor = vec4(clamp(srgb, 0.0, 1.0), frame.a);
}
|