v0.3.0 cf370a0

compose.frag RawHistoryFolder

1#version 450
2// Draws a solid fill, a client dmabuf (sampled image) or a client wl_shm
3// buffer (B8G8R8A8 words in a storage buffer). Client pixels are
4// premultiplied, as Wayland buffers are: the pipeline blends ONE,
5// ONE_MINUS_SRC_ALPHA. Scaled draws filter linearly; 1:1 draws read the
6// texel under the fragment, pixel exact.
7
8layout(push_constant) uniform Draw {
9    ivec4 rect;
10    vec4 map;
11    vec4 mapy;
12    vec4 color;
13    vec4 crop;
14    uvec4 buf;
15    uvec4 misc;
16} d;
17
18layout(set = 0, binding = 0) uniform sampler2D tex;
19layout(std430, set = 0, binding = 1) readonly buffer Pixels { uint pixels[]; };
20layout(set = 0, binding = 2) uniform sampler2D chromaTex;
21
22layout(location = 0) out vec4 color;
23
24const uint modeSolid = 0u;
25const uint modeImage = 1u;
26const uint flagOpaque = 1u;
27const uint flagExact = 2u;
28const uint flagPQ = 4u;
29const uint flagExtendedLinear = 8u;
30const uint flagYUV = 16u;
31const uint flagP010 = 32u;
32// flagSmooth averages four samples a quarter of a target pixel around the
33// point: a thumbnail shrunk under half size keeps thin lines and text.
34const uint flagSmooth = 64u;
35
36// PQ values are absolute nits. BT.2020 is converted in linear light to
37// BT.709, then clipped to the 8-bit sRGB intermediate. Highlights above
38// SDR reference white are lost (no tone mapping).
39vec3 pqToSRGB(vec3 v, float whiteNits) {
40    vec3 p = pow(clamp(v, 0.0, 1.0), vec3(32.0 / 2523.0));
41    vec3 nits = pow(max(p - vec3(3424.0 / 4096.0), vec3(0.0)) /
42        max(vec3(2413.0 / 128.0) - p * (2392.0 / 128.0), vec3(0.00001)), vec3(16384.0 / 2610.0)) * 10000.0;
43    vec3 rgb = vec3(
44        dot(nits, vec3(1.660491, -0.587641, -0.072850)),
45        dot(nits, vec3(-0.124550, 1.132900, -0.008349)),
46        dot(nits, vec3(-0.018151, -0.100579, 1.118730))
47    );
48    rgb = clamp(rgb / whiteNits, 0.0, 1.0);
49    return mix(rgb * 12.92, 1.055 * pow(rgb, vec3(1.0 / 2.4)) - 0.055, greaterThan(rgb, vec3(0.0031308)));
50}
51
52// H.273 Y'CbCr 4:2:0 reconstruction in electrical (non-linear) light.
53// Type 0 places chroma at x=even luma and halfway between luma rows.
54// vec2 coordinates are normalized to the chroma image for linear filtering.
55vec4 sampleYUV(vec2 src) {
56    ivec2 size = textureSize(tex, 0);
57    vec2 luma = clamp(src, d.crop.xy + 0.5, d.crop.zw - 0.5);
58    float y = texture(tex, luma / vec2(size)).r;
59    // Type 0 chroma is shifted half a luma pixel in x. Clamp in
60    // half-resolution texel space before filtering the chroma plane.
61    vec2 chroma = clamp((src + vec2(0.5, 0.0)) * 0.5,
62        d.crop.xy * 0.5 + 0.5, d.crop.zw * 0.5 - 0.5);
63    vec2 chromaUV = chroma * 2.0 / vec2(size);
64    vec2 cbcr = texture(chromaTex, chromaUV).rg;
65    bool tenBit = (d.misc.y & flagP010) != 0u;
66    bool limited = ((d.buf.w >> 8) & 255u) == 2u;
67    float maxCode = tenBit ? 1023.0 : 255.0;
68    // Packed 10-bit plane views normalize directly to the 0..1023 code range.
69    float yOff = limited ? (tenBit ? 64.0 : 16.0) / maxCode : 0.0;
70    float yScale = limited ? maxCode / (tenBit ? 876.0 : 219.0) : 1.0;
71    float cScale = limited ? maxCode / (tenBit ? 896.0 : 224.0) : 1.0;
72    float yy = (y - yOff) * yScale;
73    vec2 cc = (cbcr - vec2(tenBit ? 512.0/1023.0 : 128.0/255.0)) * cScale;
74    uint coeff = d.buf.w & 255u;
75    float kr = coeff == 4u ? 0.299 : coeff == 6u ? 0.2627 : 0.2126;
76    float kb = coeff == 4u ? 0.114 : coeff == 6u ? 0.0593 : 0.0722;
77    vec3 rgb = vec3(yy + 2.0*(1.0-kr)*cc.y,
78        yy - 2.0*kb*(1.0-kb)/(1.0-kr-kb)*cc.x - 2.0*kr*(1.0-kr)/(1.0-kr-kb)*cc.y,
79        yy + 2.0*(1.0-kb)*cc.x);
80    return vec4(rgb, 1.0);
81}
82
83// texel reads buffer pixel p, clamped to the buffer (clamp to edge).
84vec4 texel(ivec2 p) {
85    p = clamp(p, ivec2(floor(d.crop.xy)), min(ivec2(d.buf.yz) - 1, ivec2(ceil(d.crop.zw)) - 1));
86    // Little-endian B,G,R,A bytes: unpack yields (B, G, R, A).
87    return unpackUnorm4x8(pixels[d.buf.x + uint(p.y) * d.buf.y + uint(p.x)]).zyxw;
88}
89
90// sampleBuffer reads the client buffer at src (buffer pixels).
91vec4 sampleBuffer(vec2 src, bool exact) {
92    if (d.misc.x == modeImage && (d.misc.y & flagYUV) != 0u) {
93        return sampleYUV(src);
94    }
95    if (d.misc.x == modeImage) {
96        ivec2 size = textureSize(tex, 0);
97        vec2 p = clamp(src, d.crop.xy + 0.5, d.crop.zw - 0.5);
98        return exact ? texelFetch(tex, clamp(ivec2(floor(p)), ivec2(0), size - 1), 0) : texture(tex, p / vec2(size));
99    }
100    if (exact) {
101        return texel(ivec2(floor(src)));
102    }
103    // Bilinear: four texel reads around the sample point.
104    vec2 p = src - 0.5;
105    ivec2 i = ivec2(floor(p));
106    vec2 f = p - vec2(i);
107    return mix(mix(texel(i), texel(i + ivec2(1, 0)), f.x), mix(texel(i + ivec2(0, 1)), texel(i + ivec2(1, 1)), f.x), f.y);
108}
109
110void main() {
111    vec2 src = d.map.xy + gl_FragCoord.x * d.map.zw + gl_FragCoord.y * d.mapy.xy;
112    bool exact = (d.misc.y & flagExact) != 0u;
113    vec4 c;
114    if (d.misc.x == modeSolid) {
115        c = d.color;
116    } else if ((d.misc.y & flagSmooth) != 0u) {
117        // Premultiplied samples average without fringes.
118        vec2 dx = d.map.zw * 0.25, dy = d.mapy.xy * 0.25;
119        c = (sampleBuffer(src - dx - dy, false) + sampleBuffer(src + dx - dy, false) +
120            sampleBuffer(src - dx + dy, false) + sampleBuffer(src + dx + dy, false)) * 0.25;
121    } else {
122        c = sampleBuffer(src, exact);
123    }
124    if ((d.misc.y & flagOpaque) != 0u) {
125        c.a = 1.0;
126    }
127    if (c.a > 0.0) {
128        if ((d.misc.y & flagPQ) != 0u) {
129            c.rgb = pqToSRGB(c.rgb / c.a, d.color.x) * c.a;
130        } else if ((d.misc.y & flagExtendedLinear) != 0u) {
131            vec3 v = clamp(c.rgb / c.a * (80.0 / 203.0), 0.0, 1.0);
132            c.rgb = mix(v * 12.92, 1.055 * pow(v, vec3(1.0 / 2.4)) - 0.055, greaterThan(v, vec3(0.0031308))) * c.a;
133        }
134    }
135    if (d.mapy.z > 0.0) {
136        // Focus pulse: a screen-like lift toward white, on premultiplied
137        // values (rgb/a + k * (1 - rgb/a)) * a. Black shows it most; white
138        // never saturates.
139        c.rgb += d.mapy.z * (c.a - c.rgb);
140    }
141    if (d.misc.x != modeSolid) {
142        // The surface's alpha modifier; premultiplied, it scales all four.
143        c *= d.color.a;
144    }
145    color = c;
146}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 23

Tags 10

↑↓ moveenter openesc close