mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-07-13 11:14:11 +00:00
Switch over to the slang based shaders
This commit is contained in:
parent
425c733c00
commit
61e24215e8
35 changed files with 167 additions and 1529 deletions
|
|
@ -1,22 +0,0 @@
|
|||
vec4 alpha_blend(vec4 over, vec4 under) {
|
||||
// Alpha blend two colors returning the resulting color pre-multiplied by its alpha
|
||||
// and its alpha.
|
||||
// See https://en.wikipedia.org/wiki/Alpha_compositing
|
||||
float alpha = mix(under.a, 1.0f, over.a);
|
||||
vec3 combined_color = mix(under.rgb * under.a, over.rgb, over.a);
|
||||
return vec4(combined_color, alpha);
|
||||
}
|
||||
|
||||
vec4 alpha_blend_premul(vec4 over, vec4 under) {
|
||||
// Same as alpha_blend() except that it assumes over and under are both premultiplied.
|
||||
float inv_over_alpha = 1.0f - over.a;
|
||||
float alpha = over.a + under.a * inv_over_alpha;
|
||||
return vec4(over.rgb + under.rgb * inv_over_alpha, alpha);
|
||||
}
|
||||
|
||||
vec4 alpha_blend_premul(vec4 over, vec3 under) {
|
||||
// same as alpha_blend_premul with under_alpha = 1 outputs a blended color
|
||||
// with alpha 1 which is effectively pre-multiplied since alpha is 1
|
||||
float inv_over_alpha = 1.0f - over.a;
|
||||
return vec4(over.rgb + under.rgb * inv_over_alpha, 1.0);
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
#pragma kitty_include_shader <alpha_blend.glsl>
|
||||
|
||||
uniform sampler2D image;
|
||||
uniform vec4 background;
|
||||
in vec2 texcoord;
|
||||
out vec4 premult_color;
|
||||
|
||||
void main() {
|
||||
vec4 color = texture(image, texcoord);
|
||||
premult_color = alpha_blend(color, background);
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
#define left 0
|
||||
#define top 1
|
||||
#define right 2
|
||||
#define bottom 3
|
||||
#define tex_left 0
|
||||
#define tex_top 0
|
||||
#define tex_right 1
|
||||
#define tex_bottom 1
|
||||
|
||||
uniform float tiled;
|
||||
uniform vec4 sizes; // [ window_width, window_height, image_width, image_height ]
|
||||
uniform vec4 positions; // [ left, top, right, bottom ]
|
||||
|
||||
out vec2 texcoord;
|
||||
|
||||
const vec2 tex_map[] = vec2[4](
|
||||
vec2(tex_left, tex_top),
|
||||
vec2(tex_left, tex_bottom),
|
||||
vec2(tex_right, tex_bottom),
|
||||
vec2(tex_right, tex_top)
|
||||
);
|
||||
|
||||
float scale_factor(float window_size, float image_size) {
|
||||
return window_size / image_size;
|
||||
}
|
||||
|
||||
float tiling_factor(int i) {
|
||||
#define window i
|
||||
#define image i + 2
|
||||
return tiled * scale_factor(sizes[window], sizes[image]) + (1 - tiled);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 pos_map[] = vec2[4](
|
||||
vec2(positions[left], positions[top]),
|
||||
vec2(positions[left], positions[bottom]),
|
||||
vec2(positions[right], positions[bottom]),
|
||||
vec2(positions[right], positions[top])
|
||||
);
|
||||
vec2 tex_coords = tex_map[gl_VertexID];
|
||||
#define x_axis 0
|
||||
#define y_axis 1
|
||||
texcoord = vec2(
|
||||
tex_coords[x_axis] * tiling_factor(x_axis),
|
||||
tex_coords[y_axis] * tiling_factor(y_axis)
|
||||
);
|
||||
gl_Position = vec4(pos_map[gl_VertexID], 0, 1);
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
out vec2 texcoord;
|
||||
|
||||
#define left 0
|
||||
#define top 1
|
||||
#define right 2
|
||||
#define bottom 3
|
||||
|
||||
const ivec2 vertex_pos_map[4] = ivec2[4](
|
||||
ivec2(right, top),
|
||||
ivec2(right, bottom),
|
||||
ivec2(left, bottom),
|
||||
ivec2(left, top)
|
||||
);
|
||||
|
||||
void main() {
|
||||
ivec2 pos = vertex_pos_map[gl_VertexID];
|
||||
texcoord = vec2(src_rect[pos.x], src_rect[pos.y]);
|
||||
gl_Position = vec4(dest_rect[pos.x], dest_rect[pos.y], 0, 1);
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
#pragma kitty_include_shader <alpha_blend.glsl>
|
||||
#pragma kitty_include_shader <utils.glsl>
|
||||
#pragma kitty_include_shader <linear2srgb.glsl>
|
||||
|
||||
uniform sampler2D image;
|
||||
|
||||
in vec2 texcoord;
|
||||
out vec4 output_color;
|
||||
|
||||
void main() {
|
||||
vec4 color_premul = texture(image, texcoord);
|
||||
output_color = vec4_premul(linear2srgb(color_premul.rgb / color_premul.a), color_premul.a);
|
||||
}
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
uniform vec4 src_rect, dest_rect;
|
||||
#pragma kitty_include_shader <blit_common.glsl>
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
in vec4 color_premul;
|
||||
out vec4 output_premul;
|
||||
|
||||
void main() {
|
||||
output_premul = color_premul;
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
#pragma kitty_include_shader <utils.glsl>
|
||||
|
||||
#define DEFAULT_BG 0
|
||||
#define ACTIVE_BORDER_COLOR 1
|
||||
#define INACTIVE_BORDER_COLOR 2
|
||||
#define WINDOW_BACKGROUND_PLACEHOLDER 3
|
||||
#define BELL_BORDER_COLOR 4
|
||||
#define TAB_BAR_BG_COLOR 5
|
||||
#define TAB_BAR_MARGIN_COLOR 6
|
||||
#define TAB_BAR_EDGE_LEFT_COLOR 7
|
||||
#define TAB_BAR_EDGE_RIGHT_COLOR 8
|
||||
uniform uint colors[9];
|
||||
uniform float background_opacity;
|
||||
uniform float gamma_lut[256];
|
||||
|
||||
in vec4 rect; // left, top, right, bottom
|
||||
in uint rect_color;
|
||||
out vec4 color_premul;
|
||||
|
||||
// indices into the rect vector
|
||||
const int LEFT = 0;
|
||||
const int TOP = 1;
|
||||
const int RIGHT = 2;
|
||||
const int BOTTOM = 3;
|
||||
const uint FF = uint(0xff);
|
||||
|
||||
const uvec2 pos_map[] = uvec2[4](
|
||||
uvec2(RIGHT, TOP),
|
||||
uvec2(RIGHT, BOTTOM),
|
||||
uvec2(LEFT, BOTTOM),
|
||||
uvec2(LEFT, TOP)
|
||||
);
|
||||
|
||||
float to_color(uint c) {
|
||||
return gamma_lut[c & FF];
|
||||
}
|
||||
|
||||
float is_integer_value(uint c, int x) {
|
||||
return 1. - step(0.5, abs(float(c) - float(x)));
|
||||
}
|
||||
|
||||
vec3 as_color_vector(uint c, int shift) {
|
||||
return vec3(to_color(c >> shift), to_color(c >> (shift - 8)), to_color(c >> (shift - 16)));
|
||||
}
|
||||
|
||||
void main() {
|
||||
uvec2 pos = pos_map[gl_VertexID];
|
||||
gl_Position = vec4(rect[pos.x], rect[pos.y], 0, 1);
|
||||
vec3 window_bg = as_color_vector(rect_color, 24);
|
||||
uint rc = rect_color & FF;
|
||||
vec3 color3 = as_color_vector(colors[rc], 16);
|
||||
float is_window_bg = is_integer_value(rc, WINDOW_BACKGROUND_PLACEHOLDER); // used by window padding areas
|
||||
float is_default_bg = is_integer_value(rc, DEFAULT_BG);
|
||||
color3 = if_one_then(is_window_bg, window_bg, color3);
|
||||
// Actual border quads and tab bar edge strips must be always drawn opaque
|
||||
float is_not_a_border = zero_or_one(abs(
|
||||
(float(rc) - ACTIVE_BORDER_COLOR) * (float(rc) - INACTIVE_BORDER_COLOR) * (float(rc) - BELL_BORDER_COLOR) *
|
||||
(float(rc) - TAB_BAR_EDGE_LEFT_COLOR) * (float(rc) - TAB_BAR_EDGE_RIGHT_COLOR)
|
||||
));
|
||||
float final_opacity = if_one_then(is_not_a_border, background_opacity, 1.);
|
||||
color_premul = vec4_premul(color3, final_opacity);
|
||||
}
|
||||
|
|
@ -149,7 +149,7 @@ from .session import (
|
|||
most_recent_session,
|
||||
save_as_session,
|
||||
)
|
||||
from .shaders.legacy import load_shader_programs
|
||||
from .shaders.slang import load_shader_programs
|
||||
from .simple_cli_definitions import grab_keyboard_docs
|
||||
from .tabs import SpecialWindow, SpecialWindowInstance, Tab, TabDict, TabManager
|
||||
from .types import AsyncResponse, LayerShellConfig, SingleInstanceData, WindowSystemMouseEvent, ac
|
||||
|
|
|
|||
|
|
@ -1,33 +0,0 @@
|
|||
#define FG_OVERRIDE_ALGO {FG_OVERRIDE_ALGO}
|
||||
#define TEXT_NEW_GAMMA {TEXT_NEW_GAMMA}
|
||||
|
||||
#define DECORATION_SHIFT {DECORATION_SHIFT}
|
||||
#define REVERSE_SHIFT {REVERSE_SHIFT}
|
||||
#define STRIKE_SHIFT {STRIKE_SHIFT}
|
||||
#define DIM_SHIFT {DIM_SHIFT}
|
||||
#define BLINK_SHIFT {BLINK_SHIFT}
|
||||
#define MARK_SHIFT {MARK_SHIFT}
|
||||
#define MARK_MASK {MARK_MASK}
|
||||
#define USE_SELECTION_FG
|
||||
#define NUM_COLORS 256
|
||||
#define COLOR_NOT_SET {COLOR_NOT_SET}
|
||||
#define COLOR_IS_SPECIAL {COLOR_IS_SPECIAL}
|
||||
#define COLOR_IS_RGB {COLOR_IS_RGB}
|
||||
#define COLOR_IS_INDEX {COLOR_IS_INDEX}
|
||||
|
||||
#if {ONLY_BACKGROUND} == 1
|
||||
#define ONLY_BACKGROUND
|
||||
#endif
|
||||
|
||||
#if {ONLY_FOREGROUND} == 1
|
||||
#define ONLY_FOREGROUND
|
||||
#endif
|
||||
|
||||
#if FG_OVERRIDE_ALGO == 0
|
||||
#define DO_FG_OVERRIDE 0
|
||||
#else
|
||||
#define DO_FG_OVERRIDE 1
|
||||
#endif
|
||||
|
||||
// Linear space luminance values
|
||||
const vec3 Y = vec3(0.2126, 0.7152, 0.0722);
|
||||
|
|
@ -1,111 +0,0 @@
|
|||
#pragma kitty_include_shader <alpha_blend.glsl>
|
||||
#pragma kitty_include_shader <linear2srgb.glsl>
|
||||
#pragma kitty_include_shader <cell_defines.glsl>
|
||||
#pragma kitty_include_shader <utils.glsl>
|
||||
|
||||
uniform float text_contrast;
|
||||
uniform float text_gamma_adjustment;
|
||||
uniform sampler2DArray sprites;
|
||||
|
||||
in vec3 background;
|
||||
in vec4 effective_background_premul;
|
||||
#ifndef ONLY_BACKGROUND
|
||||
in float effective_text_alpha;
|
||||
in vec3 sprite_pos;
|
||||
in vec3 underline_pos;
|
||||
in vec3 cursor_pos;
|
||||
in vec3 strike_pos;
|
||||
flat in uint underline_exclusion_pos;
|
||||
in vec3 cell_foreground;
|
||||
in vec4 cursor_color_premult;
|
||||
in vec3 decoration_fg;
|
||||
in float colored_sprite;
|
||||
#endif
|
||||
|
||||
out vec4 output_color;
|
||||
|
||||
// Scaling factor for the extra text-alpha adjustment for luminance-difference.
|
||||
const float text_gamma_scaling = 0.5;
|
||||
|
||||
float clamp_to_unit_float(float x) {
|
||||
// Clamp value to suitable output range
|
||||
return clamp(x, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
#ifndef ONLY_BACKGROUND
|
||||
#if TEXT_NEW_GAMMA == 1
|
||||
vec4 foreground_contrast(vec4 over, vec3 under) {
|
||||
float under_luminance = dot(under, Y);
|
||||
float over_lumininace = dot(over.rgb, Y);
|
||||
// Apply additional gamma-adjustment scaled by the luminance difference, the darker the foreground the more adjustment we apply.
|
||||
// A multiplicative contrast is also available to increase saturation.
|
||||
over.a = clamp_to_unit_float(mix(over.a, pow(over.a, text_gamma_adjustment), (1 - over_lumininace + under_luminance) * text_gamma_scaling) * text_contrast);
|
||||
return over;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
vec4 foreground_contrast(vec4 over, vec3 under) {
|
||||
// Simulation of gamma-incorrect blending
|
||||
float under_luminance = dot(under, Y);
|
||||
float over_lumininace = dot(over.rgb, Y);
|
||||
// This is the original gamma-incorrect rendering, it is the solution of the following equation:
|
||||
//
|
||||
// linear2srgb(over * overA2 + under * (1 - overA2)) = linear2srgb(over) * over.a + linear2srgb(under) * (1 - over.a)
|
||||
// ^ gamma correct blending with new alpha ^ gamma incorrect blending with old alpha
|
||||
over.a = clamp_to_unit_float((srgb2linear(linear2srgb(over_lumininace) * over.a + linear2srgb(under_luminance) * (1.0f - over.a)) - under_luminance) / (over_lumininace - under_luminance));
|
||||
return over;
|
||||
}
|
||||
#endif
|
||||
|
||||
vec4 load_text_foreground_color() {
|
||||
// For colored sprites use the color from the sprite rather than the text foreground
|
||||
// Return non-premultiplied foreground color
|
||||
vec4 text_fg = texture(sprites, sprite_pos);
|
||||
return vec4(mix(cell_foreground, text_fg.rgb, colored_sprite), text_fg.a);
|
||||
}
|
||||
|
||||
vec4 calculate_premul_foreground_from_sprites(vec4 text_fg) {
|
||||
// Return premul foreground color from decorations (cursor, underline, strikethrough)
|
||||
ivec3 sz = textureSize(sprites, 0);
|
||||
float underline_alpha = texture(sprites, underline_pos).a;
|
||||
float underline_exclusion = texelFetch(sprites, ivec3(int(
|
||||
sprite_pos.x * float(sz.x)), int(underline_exclusion_pos), int(sprite_pos.z)), 0).a;
|
||||
underline_alpha *= 1.0f - underline_exclusion;
|
||||
float strike_alpha = texture(sprites, strike_pos).a;
|
||||
float cursor_alpha = texture(sprites, cursor_pos).a;
|
||||
// Since strike and text are the same color, we simply add the alpha values
|
||||
float combined_alpha = min(text_fg.a + strike_alpha, 1.0f);
|
||||
// Underline color might be different, so alpha blend
|
||||
vec4 ans = alpha_blend(vec4(text_fg.rgb, combined_alpha * effective_text_alpha), vec4(decoration_fg, underline_alpha * effective_text_alpha));
|
||||
return mix(ans, cursor_color_premult, cursor_alpha * cursor_color_premult.a);
|
||||
}
|
||||
|
||||
vec4 adjust_foreground_contrast_with_background(vec4 text_fg, vec3 bg) {
|
||||
// When rendering on a background we can adjust the alpha channel contrast
|
||||
// to improve legibility based on the source and destination colors
|
||||
return foreground_contrast(text_fg, bg);
|
||||
}
|
||||
#endif // ifndef ONLY_BACKGROUND
|
||||
|
||||
|
||||
void main() {
|
||||
#ifdef ONLY_FOREGROUND
|
||||
vec4 ans_premul;
|
||||
#else
|
||||
vec4 ans_premul = effective_background_premul;
|
||||
#endif
|
||||
|
||||
#ifndef ONLY_BACKGROUND
|
||||
// blend in the foreground color
|
||||
vec4 text_fg = load_text_foreground_color();
|
||||
text_fg = adjust_foreground_contrast_with_background(text_fg, background);
|
||||
vec4 text_fg_premul = calculate_premul_foreground_from_sprites(text_fg);
|
||||
#ifdef ONLY_FOREGROUND
|
||||
ans_premul = text_fg_premul;
|
||||
#else
|
||||
ans_premul = alpha_blend_premul(text_fg_premul, ans_premul);
|
||||
#endif
|
||||
#endif // ifndef ONLY_BACKGROUND
|
||||
output_color = ans_premul;
|
||||
}
|
||||
|
|
@ -1,387 +0,0 @@
|
|||
#extension GL_ARB_explicit_attrib_location : require
|
||||
#pragma kitty_include_shader <cell_defines.glsl>
|
||||
#pragma kitty_include_shader <utils.glsl>
|
||||
|
||||
|
||||
// Inputs {{{
|
||||
layout(std140) uniform CellRenderData {
|
||||
float use_cell_bg_for_selection_fg, use_cell_fg_for_selection_fg, use_cell_for_selection_bg;
|
||||
|
||||
uint default_fg, highlight_fg, highlight_bg, main_cursor_fg, main_cursor_bg, url_color, url_style, inverted, extra_cursor_fg, extra_cursor_bg;
|
||||
|
||||
uint columns, lines, sprites_xnum, sprites_ynum, cursor_shape, cell_width, cell_height;
|
||||
uint cursor_x1, cursor_x2, cursor_y1, cursor_y2;
|
||||
float cursor_opacity, inactive_text_alpha, fg_override_threshold, row_offset, dim_opacity, blink_opacity;
|
||||
|
||||
// must have unique entries with 0 being default_bg and unset being UINT32_MAX
|
||||
uint bg_colors0, bg_colors1, bg_colors2, bg_colors3, bg_colors4, bg_colors5, bg_colors6, bg_colors7;
|
||||
float bg_opacities0, bg_opacities1, bg_opacities2, bg_opacities3, bg_opacities4, bg_opacities5, bg_opacities6, bg_opacities7;
|
||||
};
|
||||
|
||||
layout(std140) uniform ColorTable {
|
||||
uint color_table[NUM_COLORS + MARK_MASK + MARK_MASK + 2];
|
||||
};
|
||||
uniform float gamma_lut[256];
|
||||
uniform uint draw_bg_bitfield;
|
||||
uniform usampler2D sprite_decorations_map;
|
||||
|
||||
// Have to use fixed locations here as all variants of the cell program share the same VAOs
|
||||
layout(location=0) in uvec3 colors;
|
||||
layout(location=1) in uvec2 sprite_idx;
|
||||
layout(location=2) in uint is_selected;
|
||||
// }}}
|
||||
|
||||
const int fg_index_map[] = int[3](0, 1, 0);
|
||||
const uvec2 cell_pos_map[] = uvec2[4](
|
||||
uvec2(1u, 0u), // right, top
|
||||
uvec2(1u, 1u), // right, bottom
|
||||
uvec2(0u, 1u), // left, bottom
|
||||
uvec2(0u, 0u) // left, top
|
||||
);
|
||||
const uint cursor_shape_map[] = uint[5]( // maps cursor shape to foreground sprite index
|
||||
0u, // NO_CURSOR
|
||||
0u, // BLOCK (this is rendered as background)
|
||||
2u, // BEAM
|
||||
3u, // UNDERLINE
|
||||
4u // UNFOCUSED
|
||||
);
|
||||
|
||||
|
||||
out vec3 background;
|
||||
out vec4 effective_background_premul;
|
||||
#ifndef ONLY_BACKGROUND
|
||||
out float effective_text_alpha;
|
||||
out vec3 sprite_pos;
|
||||
out vec3 underline_pos;
|
||||
out vec3 cursor_pos;
|
||||
out vec3 strike_pos;
|
||||
flat out uint underline_exclusion_pos;
|
||||
out vec3 cell_foreground;
|
||||
out vec4 cursor_color_premult;
|
||||
out vec3 decoration_fg;
|
||||
out float colored_sprite;
|
||||
#endif
|
||||
|
||||
|
||||
// Utility functions {{{
|
||||
const uint BYTE_MASK = uint(0xFF);
|
||||
const uint SPRITE_INDEX_MASK = uint(0x7fffffff);
|
||||
const uint SPRITE_COLORED_MASK = uint(0x80000000);
|
||||
const uint SPRITE_COLORED_SHIFT = 31u;
|
||||
const uint BIT_MASK = 1u;
|
||||
const uint DECORATION_MASK = uint({DECORATION_MASK});
|
||||
|
||||
vec3 color_to_vec(uint c) {
|
||||
uint r, g, b;
|
||||
r = (c >> 16) & BYTE_MASK;
|
||||
g = (c >> 8) & BYTE_MASK;
|
||||
b = c & BYTE_MASK;
|
||||
return vec3(gamma_lut[r], gamma_lut[g], gamma_lut[b]);
|
||||
}
|
||||
|
||||
float one_if_equal_zero_otherwise(float a, float b) { return (1.0f - zero_or_one(abs(float(a) - float(b)))); }
|
||||
// Wee need an integer variant to accommodate GPU driver bugs, see
|
||||
// https://github.com/kovidgoyal/kitty/issues/9072
|
||||
uint one_if_equal_zero_otherwise(int a, int b) { return (1u - uint(zero_or_one(abs(float(a) - float(b))))); }
|
||||
uint one_if_equal_zero_otherwise(uint a, uint b) { return (1u - uint(zero_or_one(abs(float(a) - float(b))))); }
|
||||
|
||||
|
||||
uint resolve_color(uint c, uint defval) {
|
||||
// Convert a cell color to an actual color based on the color table
|
||||
int t = int(c & BYTE_MASK);
|
||||
uint is_one = one_if_equal_zero_otherwise(t, 1);
|
||||
uint is_two = one_if_equal_zero_otherwise(t, 2);
|
||||
uint is_neither_one_nor_two = 1u - is_one - is_two;
|
||||
return is_one * color_table[(c >> 8) & BYTE_MASK] + is_two * (c >> 8) + is_neither_one_nor_two * defval;
|
||||
}
|
||||
|
||||
vec3 to_color(uint c, uint defval) {
|
||||
return color_to_vec(resolve_color(c, defval));
|
||||
}
|
||||
|
||||
vec3 resolve_dynamic_color(uint c, vec3 special_val, vec3 defval) {
|
||||
float type = float((c >> 24) & BYTE_MASK);
|
||||
#define q(which, val) one_if_equal_zero_otherwise(type, float(which)) * val
|
||||
return (
|
||||
q(COLOR_IS_RGB, color_to_vec(c)) + q(COLOR_IS_INDEX, color_to_vec(color_table[c & BYTE_MASK])) +
|
||||
q(COLOR_IS_SPECIAL, special_val) + q(COLOR_NOT_SET, defval)
|
||||
);
|
||||
#undef q
|
||||
}
|
||||
|
||||
float contrast_ratio(float under_luminance, float over_luminance) {
|
||||
return clamp((max(under_luminance, over_luminance) + 0.05f) / (min(under_luminance, over_luminance) + 0.05f), 1.f, 21.f);
|
||||
}
|
||||
|
||||
float contrast_ratio(vec3 a, vec3 b) {
|
||||
return contrast_ratio(dot(a, Y), dot(b, Y));
|
||||
}
|
||||
|
||||
struct ColorPair {
|
||||
vec3 bg, fg;
|
||||
};
|
||||
|
||||
float contrast_ratio(ColorPair a) { return contrast_ratio(a.bg, a.fg); }
|
||||
|
||||
ColorPair if_less_than_pair(float a, float b, ColorPair thenval, ColorPair elseval) {
|
||||
return ColorPair(if_less_than(a, b, thenval.bg, elseval.bg),
|
||||
if_less_than(a, b, thenval.fg, elseval.fg));
|
||||
}
|
||||
|
||||
ColorPair if_one_then_pair(float condition, ColorPair thenval, ColorPair elseval) {
|
||||
return ColorPair(if_one_then(condition, thenval.bg, elseval.bg),
|
||||
if_one_then(condition, thenval.fg, elseval.fg));
|
||||
}
|
||||
|
||||
ColorPair resolve_extra_cursor_colors_for_special_cursor(vec3 cell_bg, vec3 cell_fg) {
|
||||
ColorPair cell = ColorPair(cell_fg, cell_bg), base = ColorPair(color_to_vec(default_fg), color_to_vec(bg_colors0));
|
||||
float cr = contrast_ratio(cell), br = contrast_ratio(base);
|
||||
ColorPair higher_contrast_pair = if_less_than_pair(cr, br, base, cell);
|
||||
return if_less_than_pair(cr, 2.5, higher_contrast_pair, cell);
|
||||
}
|
||||
|
||||
ColorPair resolve_extra_cursor_colors(vec3 cell_bg, vec3 cell_fg, ColorPair main_cursor) {
|
||||
ColorPair ans = ColorPair(
|
||||
resolve_dynamic_color(extra_cursor_bg, main_cursor.bg, main_cursor.bg),
|
||||
resolve_dynamic_color(extra_cursor_fg, cell_bg, main_cursor.fg)
|
||||
);
|
||||
ColorPair special = resolve_extra_cursor_colors_for_special_cursor(cell_bg, cell_fg);
|
||||
return if_one_then_pair(zero_or_one(abs(float(extra_cursor_bg & BYTE_MASK) - COLOR_IS_SPECIAL)), ans, special);
|
||||
}
|
||||
|
||||
uvec3 to_sprite_coords(uint idx) {
|
||||
uint sprites_per_page = sprites_xnum * sprites_ynum;
|
||||
uint z = idx / sprites_per_page;
|
||||
uint num_on_last_page = idx - sprites_per_page * z;
|
||||
uint y = num_on_last_page / sprites_xnum;
|
||||
uint x = num_on_last_page - sprites_xnum * y;
|
||||
return uvec3(x, y, z);
|
||||
}
|
||||
|
||||
vec3 to_sprite_pos(uvec2 pos, uint idx) {
|
||||
uvec3 c = to_sprite_coords(idx);
|
||||
vec2 s_xpos = vec2(c.x, float(c.x) + 1.0f) * (1.0f / float(sprites_xnum));
|
||||
vec2 s_ypos = vec2(c.y, float(c.y) + 1.0f) * (1.0f / float(sprites_ynum));
|
||||
uint texture_height_px = (cell_height + 1u) * sprites_ynum;
|
||||
float row_height = 1.0f / float(texture_height_px);
|
||||
s_ypos[1] -= row_height; // skip the decorations_exclude row
|
||||
return vec3(s_xpos[pos.x], s_ypos[pos.y], c.z);
|
||||
}
|
||||
|
||||
uint to_underline_exclusion_pos() {
|
||||
uvec3 c = to_sprite_coords(sprite_idx[0]);
|
||||
uint cell_top_px = c.y * (cell_height + 1u);
|
||||
return cell_top_px + cell_height;
|
||||
}
|
||||
|
||||
uint read_sprite_decorations_idx() {
|
||||
int idx = int(sprite_idx[0] & SPRITE_INDEX_MASK);
|
||||
ivec2 sz = textureSize(sprite_decorations_map, 0);
|
||||
int y = idx / sz[0];
|
||||
int x = idx - y * sz[0];
|
||||
return texelFetch(sprite_decorations_map, ivec2(x, y), 0).r;
|
||||
}
|
||||
|
||||
uvec2 get_decorations_indices(uint in_url /* [0, 1] */, uint text_attrs) {
|
||||
uint decorations_idx = read_sprite_decorations_idx();
|
||||
// decorations_idx == 0 means no decorations, for example, for a blank line
|
||||
// when drawing fractionally scaled text
|
||||
uint has_decorations = uint(zero_or_one(float(decorations_idx)));
|
||||
uint strike_style = ((text_attrs >> STRIKE_SHIFT) & BIT_MASK); // 0 or 1
|
||||
uint strike_idx = decorations_idx * strike_style;
|
||||
uint underline_style = ((text_attrs >> DECORATION_SHIFT) & DECORATION_MASK);
|
||||
underline_style = in_url * url_style + (1u - in_url) * underline_style; // [0, 5]
|
||||
uint has_underline = uint(step(0.5f, float(underline_style))); // [0, 1]
|
||||
return has_decorations * uvec2(strike_idx, has_underline * (decorations_idx + underline_style));
|
||||
}
|
||||
|
||||
uint is_cursor(uint x, uint y) {
|
||||
uint clamped_x = clamp(x, cursor_x1, cursor_x2);
|
||||
uint clamped_y = clamp(y, cursor_y1, cursor_y2);
|
||||
return one_if_equal_zero_otherwise(x, clamped_x) * one_if_equal_zero_otherwise(y, clamped_y);
|
||||
}
|
||||
// }}}
|
||||
|
||||
struct CellData {
|
||||
float has_cursor, has_block_cursor;
|
||||
uvec2 pos;
|
||||
uint cursor_fg_sprite_idx;
|
||||
ColorPair cursor;
|
||||
} cell_data;
|
||||
|
||||
CellData set_vertex_position(vec3 cell_fg, vec3 cell_bg) {
|
||||
uint instance_id = uint(gl_InstanceID);
|
||||
float dx = 2.0 / float(columns);
|
||||
float dy = 2.0 / float(lines);
|
||||
/* The current cell being rendered */
|
||||
uint row = instance_id / columns;
|
||||
uint column = instance_id - row * columns;
|
||||
/* The position of this vertex, at a corner of the cell */
|
||||
float left = -1.0 + column * dx;
|
||||
float top = 1.0 - (float(row) + row_offset) * dy;
|
||||
uvec2 pos = cell_pos_map[gl_VertexID];
|
||||
gl_Position = vec4(vec2(left, left + dx)[pos.x], vec2(top, top - dy)[pos.y], 0, 1);
|
||||
// The character sprite being rendered
|
||||
#ifndef ONLY_BACKGROUND
|
||||
sprite_pos = to_sprite_pos(pos, sprite_idx[0] & SPRITE_INDEX_MASK);
|
||||
colored_sprite = float((sprite_idx[0] & SPRITE_COLORED_MASK) >> SPRITE_COLORED_SHIFT);
|
||||
#endif
|
||||
// Cursor shape and colors
|
||||
float has_main_cursor = float(is_cursor(column, row));
|
||||
float multicursor_shape = float((is_selected >> 2) & 3u);
|
||||
float multicursor_uses_main_cursor_shape = float((is_selected >> 4) & BIT_MASK);
|
||||
multicursor_shape = if_one_then(multicursor_uses_main_cursor_shape, cursor_shape, multicursor_shape);
|
||||
float final_cursor_shape = if_one_then(has_main_cursor, cursor_shape, multicursor_shape);
|
||||
float has_cursor = zero_or_one(final_cursor_shape);
|
||||
float is_block_cursor = has_cursor * one_if_equal_zero_otherwise(final_cursor_shape, 1.0);
|
||||
ColorPair main_cursor = ColorPair(color_to_vec(main_cursor_bg), color_to_vec(main_cursor_fg));
|
||||
ColorPair extra_cursor = resolve_extra_cursor_colors(cell_bg, cell_fg, main_cursor);
|
||||
ColorPair cursor = if_one_then_pair(has_main_cursor, main_cursor, extra_cursor);
|
||||
return CellData(has_cursor, is_block_cursor, pos, cursor_shape_map[int(final_cursor_shape)], cursor);
|
||||
}
|
||||
|
||||
float background_opacity_for(uint bg, uint colorval, float opacity_if_matched) { // opacity_if_matched if bg == colorval else 1
|
||||
float not_matched = step(1.f, abs(float(colorval - bg))); // not_matched = 0 if bg == colorval else 1
|
||||
return not_matched + opacity_if_matched * (1.f - not_matched);
|
||||
}
|
||||
|
||||
float calc_background_opacity(uint bg) {
|
||||
return (
|
||||
background_opacity_for(bg, bg_colors0, bg_opacities0) *
|
||||
background_opacity_for(bg, bg_colors1, bg_opacities1) *
|
||||
background_opacity_for(bg, bg_colors2, bg_opacities2) *
|
||||
background_opacity_for(bg, bg_colors3, bg_opacities3) *
|
||||
background_opacity_for(bg, bg_colors4, bg_opacities4) *
|
||||
background_opacity_for(bg, bg_colors5, bg_opacities5) *
|
||||
background_opacity_for(bg, bg_colors6, bg_opacities6) *
|
||||
background_opacity_for(bg, bg_colors7, bg_opacities7)
|
||||
);
|
||||
}
|
||||
|
||||
// Overriding of foreground colors for contrast requirements {{{
|
||||
#if DO_FG_OVERRIDE == 1 && !defined(ONLY_BACKGROUND)
|
||||
#define OVERRIDE_FG_COLORS
|
||||
#pragma kitty_include_shader <hsluv.glsl>
|
||||
#if (FG_OVERRIDE_ALGO == 1)
|
||||
vec3 fg_override(float under_luminance, float over_lumininace, vec3 under, vec3 over) {
|
||||
// If the difference in luminance is too small,
|
||||
// force the foreground color to be black or white.
|
||||
float diff_luminance = abs(under_luminance - over_lumininace);
|
||||
float override_level = (1.f - colored_sprite) * step(diff_luminance, fg_override_threshold);
|
||||
float original_level = 1.f - override_level;
|
||||
return original_level * over + override_level * vec3(step(under_luminance, 0.5f));
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
vec3 fg_override(float under_luminance, float over_luminance, vec3 under, vec3 over) {
|
||||
float ratio = contrast_ratio(under_luminance, over_luminance);
|
||||
vec3 diff = abs(under - over);
|
||||
vec3 over_hsluv = rgbToHsluv(over);
|
||||
const float min_contrast_ratio = fg_override_threshold;
|
||||
float target_lum_a = clamp((under_luminance + 0.05f) * min_contrast_ratio - 0.05f, 0.f, 1.f);
|
||||
float target_lum_b = clamp((under_luminance + 0.05f) / min_contrast_ratio - 0.05f, 0.f, 1.f);
|
||||
vec3 result_a = clamp(hsluvToRgb(vec3(over_hsluv.x, over_hsluv.y, target_lum_a * 100.f)), 0.f, 1.f);
|
||||
vec3 result_b = clamp(hsluvToRgb(vec3(over_hsluv.x, over_hsluv.y, target_lum_b * 100.f)), 0.f, 1.f);
|
||||
float result_a_ratio = contrast_ratio(under_luminance, dot(result_a, Y));
|
||||
float result_b_ratio = contrast_ratio(under_luminance, dot(result_b, Y));
|
||||
vec3 result = mix(result_a, result_b, step(result_a_ratio, result_b_ratio));
|
||||
return mix(result, over, max(step(diff.r + diff.g + diff.b, 0.001f), step(min_contrast_ratio, ratio)));
|
||||
}
|
||||
#endif
|
||||
|
||||
vec3 override_foreground_color(vec3 over, vec3 under) {
|
||||
float under_luminance = dot(under, Y);
|
||||
float over_lumininace = dot(over.rgb, Y);
|
||||
return fg_override(under_luminance, over_lumininace, under, over);
|
||||
}
|
||||
#endif
|
||||
// }}}
|
||||
|
||||
void main() {
|
||||
|
||||
|
||||
// set cell color indices {{{
|
||||
uvec2 default_colors = uvec2(default_fg, bg_colors0);
|
||||
uint text_attrs = sprite_idx[1];
|
||||
uint is_reversed = ((text_attrs >> REVERSE_SHIFT) & BIT_MASK);
|
||||
uint is_inverted = is_reversed + inverted;
|
||||
int fg_index = fg_index_map[is_inverted];
|
||||
int bg_index = 1 - fg_index;
|
||||
int mark = int(text_attrs >> MARK_SHIFT) & MARK_MASK;
|
||||
uint has_mark = uint(step(1, float(mark)));
|
||||
uint bg_as_uint = resolve_color(colors[bg_index], default_colors[bg_index]);
|
||||
bg_as_uint = has_mark * color_table[NUM_COLORS + mark - 1] + (BIT_MASK - has_mark) * bg_as_uint;
|
||||
float cell_has_default_bg = 1.f - step(1.f, abs(float(bg_as_uint - bg_colors0))); // 1 if has default bg else 0
|
||||
vec3 bg = color_to_vec(bg_as_uint);
|
||||
uint fg_as_uint = resolve_color(colors[fg_index], default_colors[fg_index]);
|
||||
fg_as_uint = has_mark * color_table[NUM_COLORS + MARK_MASK + mark] + (1u - has_mark) * fg_as_uint;
|
||||
vec3 foreground = color_to_vec(fg_as_uint);
|
||||
CellData cell_data = set_vertex_position(foreground, bg);
|
||||
// }}}
|
||||
|
||||
// Foreground {{{
|
||||
#ifndef ONLY_BACKGROUND // background does not depend on foreground
|
||||
float has_dim = float((text_attrs >> DIM_SHIFT) & BIT_MASK), has_blink = float((text_attrs >> BLINK_SHIFT) & BIT_MASK);
|
||||
effective_text_alpha = inactive_text_alpha * if_one_then(has_dim, dim_opacity, 1.0) * if_one_then(
|
||||
has_blink, blink_opacity, 1.0);
|
||||
float in_url = float((is_selected >> 1) & BIT_MASK);
|
||||
decoration_fg = if_one_then(in_url, color_to_vec(url_color), to_color(colors[2], fg_as_uint));
|
||||
// Selection
|
||||
vec3 selection_color = if_one_then(use_cell_bg_for_selection_fg, bg, color_to_vec(highlight_fg));
|
||||
selection_color = if_one_then(use_cell_fg_for_selection_fg, foreground, selection_color);
|
||||
foreground = if_one_then(float(is_selected & BIT_MASK), selection_color, foreground);
|
||||
decoration_fg = if_one_then(float(is_selected & BIT_MASK), selection_color, decoration_fg);
|
||||
// Underline and strike through (rendered via sprites)
|
||||
uvec2 decs = get_decorations_indices(uint(in_url), text_attrs);
|
||||
strike_pos = to_sprite_pos(cell_data.pos, decs[0]);
|
||||
underline_pos = to_sprite_pos(cell_data.pos, decs[1]);
|
||||
underline_exclusion_pos = to_underline_exclusion_pos();
|
||||
|
||||
// Cursor
|
||||
cursor_color_premult = vec4(cell_data.cursor.bg * cursor_opacity, cursor_opacity);
|
||||
vec3 final_cursor_text_color = mix(foreground, cell_data.cursor.fg, cursor_opacity);
|
||||
foreground = if_one_then(cell_data.has_block_cursor, final_cursor_text_color, foreground);
|
||||
decoration_fg = if_one_then(cell_data.has_block_cursor, final_cursor_text_color, decoration_fg);
|
||||
cursor_pos = to_sprite_pos(cell_data.pos, cell_data.cursor_fg_sprite_idx * uint(cell_data.has_cursor));
|
||||
#endif
|
||||
// }}}
|
||||
|
||||
// Background {{{
|
||||
float bg_alpha = calc_background_opacity(bg_as_uint);
|
||||
// we use max so that opacity of the block cursor cell background goes from bg_alpha to 1
|
||||
float effective_cursor_opacity = max(cursor_opacity, bg_alpha);
|
||||
// is_special_cell is either 0 or 1
|
||||
float is_special_cell = cell_data.has_block_cursor + float(is_selected & BIT_MASK);
|
||||
is_special_cell += float(is_reversed); // reverse video cells should be opaque as well
|
||||
is_special_cell = zero_or_one(is_special_cell);
|
||||
cell_has_default_bg = if_one_then(is_special_cell, 0., cell_has_default_bg);
|
||||
|
||||
// special cells must always be fully opaque, otherwise leave bg_alpha untouched
|
||||
bg_alpha = if_one_then(is_special_cell, 1.f, bg_alpha);
|
||||
// Selection and cursor
|
||||
bg_alpha = if_one_then(cell_data.has_block_cursor, effective_cursor_opacity, bg_alpha);
|
||||
bg = if_one_then(float(is_selected & BIT_MASK), if_one_then(use_cell_for_selection_bg, color_to_vec(fg_as_uint), color_to_vec(highlight_bg)), bg);
|
||||
vec3 background_rgb = if_one_then(cell_data.has_block_cursor, mix(bg, cell_data.cursor.bg, cursor_opacity), bg);
|
||||
background = background_rgb;
|
||||
// }}}
|
||||
|
||||
#if !defined(ONLY_BACKGROUND) && defined(OVERRIDE_FG_COLORS)
|
||||
decoration_fg = override_foreground_color(decoration_fg, background_rgb);
|
||||
foreground = override_foreground_color(foreground, background_rgb);
|
||||
#endif
|
||||
|
||||
#if !defined(ONLY_FOREGROUND)
|
||||
vec4 bgpremul = vec4_premul(background_rgb, bg_alpha);
|
||||
// draw_bg_bitfield has bit 0 set to draw default bg cells and bit 1 set to draw non-default bg cells
|
||||
float cell_has_non_default_bg = 1.f - cell_has_default_bg;
|
||||
uint draw_bg_mask = uint(2.f * cell_has_non_default_bg + cell_has_default_bg); // 1 if has default bg else 2
|
||||
float draw_bg = step(0.5, float(draw_bg_bitfield & draw_bg_mask));
|
||||
bgpremul *= draw_bg;
|
||||
effective_background_premul = bgpremul;
|
||||
#endif
|
||||
|
||||
#ifndef ONLY_BACKGROUND
|
||||
cell_foreground = foreground;
|
||||
#endif
|
||||
}
|
||||
|
|
@ -307,6 +307,9 @@ get_uniform_array_information(int program, const char *name) {
|
|||
GLuint pid = program_id(program);
|
||||
GLuint t;
|
||||
glGetUniformIndices(pid, 1, (void*)names, &t);
|
||||
if (t == GL_INVALID_INDEX) {
|
||||
fatal("Could not find the index for uniform array: %s", name);
|
||||
}
|
||||
glGetActiveUniformsiv(pid, 1, &t, GL_UNIFORM_ARRAY_STRIDE, &ans.stride);
|
||||
glGetActiveUniformsiv(pid, 1, &t, GL_UNIFORM_OFFSET, &ans.offset);
|
||||
glGetActiveUniformsiv(pid, 1, &t, GL_UNIFORM_SIZE, &ans.size);
|
||||
|
|
@ -484,10 +487,8 @@ add_located_attribute_to_vao(ssize_t vao_idx, GLint aloc, GLint size, GLenum dat
|
|||
|
||||
|
||||
void
|
||||
add_attribute_to_vao(int p, ssize_t vao_idx, const char *name, GLint size, GLenum data_type, GLsizei stride, void *offset, GLuint divisor) {
|
||||
GLint aloc = attrib_location(p, name);
|
||||
if (aloc == -1) fatal("No attribute named: %s found in this program", name);
|
||||
add_located_attribute_to_vao(vao_idx, aloc, size, data_type, stride, offset, divisor);
|
||||
add_attribute_to_vao(ssize_t vao_idx, int location, GLint size, GLenum data_type, GLsizei stride, void *offset, GLuint divisor) {
|
||||
add_located_attribute_to_vao(vao_idx, location, size, data_type, stride, offset, divisor);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ ArrayInformation get_uniform_array_information(int program, const char *name);
|
|||
GLint attrib_location(int program, const char *name);
|
||||
ssize_t create_vao(void);
|
||||
size_t add_buffer_to_vao(ssize_t vao_idx, GLenum usage);
|
||||
void add_attribute_to_vao(int p, ssize_t vao_idx, const char *name, GLint size, GLenum data_type, GLsizei stride, void *offset, GLuint divisor);
|
||||
void add_attribute_to_vao(ssize_t vao_idx, int location, GLint size, GLenum data_type, GLsizei stride, void *offset, GLuint divisor);
|
||||
ssize_t alloc_vao_buffer(ssize_t vao_idx, GLsizeiptr size, size_t bufnum, GLenum usage);
|
||||
void* alloc_and_map_vao_buffer(ssize_t vao_idx, GLsizeiptr size, size_t bufnum, bool frequently_updated);
|
||||
void unmap_vao_buffer(ssize_t vao_idx, size_t bufnum);
|
||||
|
|
|
|||
10
kitty/glsl-uniforms.h
generated
10
kitty/glsl-uniforms.h
generated
|
|
@ -48,14 +48,15 @@ static inline void get_uniform_locations_border(int program, BorderUniforms *ans
|
|||
ans->rect_color = 1;
|
||||
ans->Colors.index = block_index(program, "block_Colors_0");
|
||||
ans->Colors.size = block_size(program, ans->Colors.index);
|
||||
ans->colors = get_uniform_array_information(program, "block_Colors_0.colors_0[0]");
|
||||
ans->colors = get_uniform_array_information(program, "colors_0");
|
||||
ans->GammaLUT.index = block_index(program, "block_GammaLUT_0");
|
||||
ans->GammaLUT.size = block_size(program, ans->GammaLUT.index);
|
||||
ans->gamma_lut = get_uniform_array_information(program, "block_GammaLUT_0.gamma_lut_0[0]");
|
||||
ans->gamma_lut = get_uniform_array_information(program, "gamma_lut_0");
|
||||
}
|
||||
|
||||
typedef struct CellUniforms {
|
||||
int draw_bg_bitfield;
|
||||
int sprite_decorations_map;
|
||||
int sprites;
|
||||
int text_contrast;
|
||||
int text_gamma_adjustment;
|
||||
|
|
@ -73,6 +74,7 @@ typedef struct CellUniforms {
|
|||
|
||||
static inline void get_uniform_locations_cell(int program, CellUniforms *ans) {
|
||||
ans->draw_bg_bitfield = get_uniform_location(program, "draw_bg_bitfield_0");
|
||||
ans->sprite_decorations_map = get_uniform_location(program, "sprite_decorations_map_0");
|
||||
ans->sprites = get_uniform_location(program, "sprites_0");
|
||||
ans->text_contrast = get_uniform_location(program, "text_contrast_0");
|
||||
ans->text_gamma_adjustment = get_uniform_location(program, "text_gamma_adjustment_0");
|
||||
|
|
@ -83,10 +85,10 @@ static inline void get_uniform_locations_cell(int program, CellUniforms *ans) {
|
|||
ans->CellRenderData.size = block_size(program, ans->CellRenderData.index);
|
||||
ans->ColorTable.index = block_index(program, "block_ColorTable_0");
|
||||
ans->ColorTable.size = block_size(program, ans->ColorTable.index);
|
||||
ans->color_table = get_uniform_array_information(program, "block_ColorTable_0.color_table_0[0]");
|
||||
ans->color_table = get_uniform_array_information(program, "color_table_0");
|
||||
ans->GammaLUT.index = block_index(program, "block_GammaLUT_0");
|
||||
ans->GammaLUT.size = block_size(program, ans->GammaLUT.index);
|
||||
ans->gamma_lut = get_uniform_array_information(program, "block_GammaLUT_0.gamma_lut_0[0]");
|
||||
ans->gamma_lut = get_uniform_array_information(program, "gamma_lut_0");
|
||||
}
|
||||
|
||||
typedef struct GraphicsUniforms {
|
||||
|
|
|
|||
|
|
@ -1,29 +0,0 @@
|
|||
#pragma kitty_include_shader <alpha_blend.glsl>
|
||||
#pragma kitty_include_shader <utils.glsl>
|
||||
#define ALPHA_TYPE
|
||||
|
||||
uniform sampler2D image;
|
||||
#ifdef ALPHA_MASK
|
||||
uniform vec3 amask_fg;
|
||||
uniform vec4 amask_bg_premult;
|
||||
#else
|
||||
uniform float extra_alpha;
|
||||
#endif
|
||||
|
||||
in vec2 texcoord;
|
||||
out vec4 output_color;
|
||||
|
||||
void main() {
|
||||
vec4 color = texture(image, texcoord);
|
||||
#ifdef ALPHA_MASK
|
||||
color = vec4(amask_fg, color.r);
|
||||
color = vec4_premul(color);
|
||||
color = alpha_blend_premul(color, amask_bg_premult);
|
||||
#else
|
||||
color.a *= extra_alpha;
|
||||
#if TEXTURE_IS_NOT_PREMULTIPLIED
|
||||
color = vec4_premul(color);
|
||||
#endif
|
||||
#endif
|
||||
output_color = color;
|
||||
}
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
uniform vec4 src_rect, dest_rect;
|
||||
#pragma kitty_include_shader <blit_common.glsl>
|
||||
210
kitty/hsluv.glsl
210
kitty/hsluv.glsl
|
|
@ -1,210 +0,0 @@
|
|||
/*
|
||||
HSLUV-GLSL v4.2
|
||||
HSLUV is a human-friendly alternative to HSL. ( http://www.hsluv.org )
|
||||
GLSL port by William Malo ( https://github.com/williammalo )
|
||||
Put this code in your fragment shader.
|
||||
*/
|
||||
|
||||
// stripped down and optimized (branchless) version
|
||||
|
||||
float divide(float num, float denom) {
|
||||
return num / (abs(denom) + 1e-15) * sign(denom);
|
||||
}
|
||||
|
||||
vec3 divide(vec3 num, vec3 denom) {
|
||||
return num / (abs(denom) + 1e-15) * sign(denom);
|
||||
}
|
||||
|
||||
vec3 hsluv_intersectLineLine(vec3 line1x, vec3 line1y, vec3 line2x, vec3 line2y) {
|
||||
return (line1y - line2y) / (line2x - line1x);
|
||||
}
|
||||
|
||||
vec3 hsluv_distanceFromPole(vec3 pointx,vec3 pointy) {
|
||||
return sqrt(pointx*pointx + pointy*pointy);
|
||||
}
|
||||
|
||||
vec3 hsluv_lengthOfRayUntilIntersect(float theta, vec3 x, vec3 y) {
|
||||
vec3 len = divide(y, sin(theta) - x * cos(theta));
|
||||
len = mix(len, vec3(1000.0), step(len, vec3(0.0)));
|
||||
return len;
|
||||
}
|
||||
|
||||
float hsluv_maxSafeChromaForL(float L){
|
||||
mat3 m2 = mat3(
|
||||
3.2409699419045214 ,-0.96924363628087983 , 0.055630079696993609,
|
||||
-1.5373831775700935 , 1.8759675015077207 ,-0.20397695888897657 ,
|
||||
-0.49861076029300328 , 0.041555057407175613, 1.0569715142428786
|
||||
);
|
||||
float sub0 = L + 16.0;
|
||||
float sub1 = sub0 * sub0 * sub0 * .000000641;
|
||||
float sub2 = mix(L / 903.2962962962963, sub1, step(0.0088564516790356308, sub1));
|
||||
|
||||
vec3 top1 = (284517.0 * m2[0] - 94839.0 * m2[2]) * sub2;
|
||||
vec3 bottom = (632260.0 * m2[2] - 126452.0 * m2[1]) * sub2;
|
||||
vec3 top2 = (838422.0 * m2[2] + 769860.0 * m2[1] + 731718.0 * m2[0]) * L * sub2;
|
||||
|
||||
vec3 bounds0x = top1 / bottom;
|
||||
vec3 bounds0y = top2 / bottom;
|
||||
|
||||
vec3 bounds1x = top1 / (bottom+126452.0);
|
||||
vec3 bounds1y = (top2-769860.0*L) / (bottom+126452.0);
|
||||
|
||||
vec3 xs0 = hsluv_intersectLineLine(bounds0x, bounds0y, -1.0/bounds0x, vec3(0.0) );
|
||||
vec3 xs1 = hsluv_intersectLineLine(bounds1x, bounds1y, -1.0/bounds1x, vec3(0.0) );
|
||||
|
||||
vec3 lengths0 = hsluv_distanceFromPole( xs0, bounds0y + xs0 * bounds0x );
|
||||
vec3 lengths1 = hsluv_distanceFromPole( xs1, bounds1y + xs1 * bounds1x );
|
||||
|
||||
return min(lengths0.r,
|
||||
min(lengths1.r,
|
||||
min(lengths0.g,
|
||||
min(lengths1.g,
|
||||
min(lengths0.b,
|
||||
lengths1.b)))));
|
||||
}
|
||||
|
||||
float hsluv_maxChromaForLH(float L, float H) {
|
||||
|
||||
float hrad = radians(H);
|
||||
|
||||
mat3 m2 = mat3(
|
||||
3.2409699419045214 ,-0.96924363628087983 , 0.055630079696993609,
|
||||
-1.5373831775700935 , 1.8759675015077207 ,-0.20397695888897657 ,
|
||||
-0.49861076029300328 , 0.041555057407175613, 1.0569715142428786
|
||||
);
|
||||
float sub1 = pow(L + 16.0, 3.0) / 1560896.0;
|
||||
float sub2 = mix(L / 903.2962962962963, sub1, step(0.0088564516790356308, sub1));
|
||||
|
||||
vec3 top1 = (284517.0 * m2[0] - 94839.0 * m2[2]) * sub2;
|
||||
vec3 bottom = (632260.0 * m2[2] - 126452.0 * m2[1]) * sub2;
|
||||
vec3 top2 = (838422.0 * m2[2] + 769860.0 * m2[1] + 731718.0 * m2[0]) * L * sub2;
|
||||
|
||||
vec3 bound0x = top1 / bottom;
|
||||
vec3 bound0y = top2 / bottom;
|
||||
|
||||
vec3 bound1x = top1 / (bottom+126452.0);
|
||||
vec3 bound1y = (top2-769860.0*L) / (bottom+126452.0);
|
||||
|
||||
vec3 lengths0 = hsluv_lengthOfRayUntilIntersect(hrad, bound0x, bound0y );
|
||||
vec3 lengths1 = hsluv_lengthOfRayUntilIntersect(hrad, bound1x, bound1y );
|
||||
|
||||
return min(lengths0.r,
|
||||
min(lengths1.r,
|
||||
min(lengths0.g,
|
||||
min(lengths1.g,
|
||||
min(lengths0.b,
|
||||
lengths1.b)))));
|
||||
}
|
||||
|
||||
vec3 hsluv_fromLinear(vec3 c) {
|
||||
return mix(c * 12.92, 1.055 * pow(max(c, vec3(0)), vec3(1.0 / 2.4)) - 0.055, step(0.0031308, c));
|
||||
}
|
||||
|
||||
vec3 hsluv_toLinear(vec3 c) {
|
||||
return mix(c / 12.92, pow(max((c + 0.055) / (1.0 + 0.055), vec3(0)), vec3(2.4)), step(0.04045, c));
|
||||
}
|
||||
|
||||
float hsluv_yToL(float Y){
|
||||
return mix(Y * 903.2962962962963, 116.0 * pow(max(Y, 0), 1.0 / 3.0) - 16.0, step(0.0088564516790356308, Y));
|
||||
}
|
||||
|
||||
float hsluv_lToY(float L) {
|
||||
return mix(L / 903.2962962962963, pow((max(L, 0) + 16.0) / 116.0, 3.0), step(8.0, L));
|
||||
}
|
||||
|
||||
vec3 xyzToRgb(vec3 tuple) {
|
||||
const mat3 m = mat3(
|
||||
3.2409699419045214 ,-1.5373831775700935 ,-0.49861076029300328 ,
|
||||
-0.96924363628087983 , 1.8759675015077207 , 0.041555057407175613,
|
||||
0.055630079696993609,-0.20397695888897657, 1.0569715142428786 );
|
||||
return hsluv_fromLinear(tuple*m);
|
||||
}
|
||||
|
||||
vec3 rgbToXyz(vec3 tuple) {
|
||||
const mat3 m = mat3(
|
||||
0.41239079926595948 , 0.35758433938387796, 0.18048078840183429 ,
|
||||
0.21263900587151036 , 0.71516867876775593, 0.072192315360733715,
|
||||
0.019330818715591851, 0.11919477979462599, 0.95053215224966058
|
||||
);
|
||||
return hsluv_toLinear(tuple) * m;
|
||||
}
|
||||
|
||||
vec3 xyzToLuv(vec3 tuple){
|
||||
float X = tuple.x;
|
||||
float Y = tuple.y;
|
||||
float Z = tuple.z;
|
||||
|
||||
float L = hsluv_yToL(Y);
|
||||
float div = 1. / max(dot(tuple, vec3(1, 15, 3)), 1e-15);
|
||||
|
||||
return vec3(
|
||||
1.,
|
||||
(52. * (X*div) - 2.57179),
|
||||
(117.* (Y*div) - 6.08816)
|
||||
) * L;
|
||||
}
|
||||
|
||||
|
||||
vec3 luvToXyz(vec3 tuple) {
|
||||
float L = tuple.x;
|
||||
|
||||
float U = divide(tuple.y, 13.0 * L) + 0.19783000664283681;
|
||||
float V = divide(tuple.z, 13.0 * L) + 0.468319994938791;
|
||||
|
||||
float Y = hsluv_lToY(L);
|
||||
float X = 2.25 * U * Y / V;
|
||||
float Z = (3./V - 5.)*Y - (X/3.);
|
||||
|
||||
return vec3(X, Y, Z);
|
||||
}
|
||||
|
||||
vec3 luvToLch(vec3 tuple) {
|
||||
float L = tuple.x;
|
||||
float U = tuple.y;
|
||||
float V = tuple.z;
|
||||
|
||||
float C = length(tuple.yz);
|
||||
float H = degrees(atan(V,U));
|
||||
H += 360.0 * step(H, 0.0);
|
||||
|
||||
return vec3(L, C, H);
|
||||
}
|
||||
|
||||
vec3 lchToLuv(vec3 tuple) {
|
||||
float hrad = radians(tuple.b);
|
||||
return vec3(
|
||||
tuple.r,
|
||||
cos(hrad) * tuple.g,
|
||||
sin(hrad) * tuple.g
|
||||
);
|
||||
}
|
||||
|
||||
vec3 hsluvToLch(vec3 tuple) {
|
||||
tuple.g *= hsluv_maxChromaForLH(tuple.b, tuple.r) * .01;
|
||||
return tuple.bgr;
|
||||
}
|
||||
|
||||
vec3 lchToHsluv(vec3 tuple) {
|
||||
tuple.g = divide(tuple.g, hsluv_maxChromaForLH(tuple.r, tuple.b) * .01);
|
||||
return tuple.bgr;
|
||||
}
|
||||
|
||||
vec3 lchToRgb(vec3 tuple) {
|
||||
return xyzToRgb(luvToXyz(lchToLuv(tuple)));
|
||||
}
|
||||
|
||||
vec3 rgbToLch(vec3 tuple) {
|
||||
return luvToLch(xyzToLuv(rgbToXyz(tuple)));
|
||||
}
|
||||
|
||||
vec3 hsluvToRgb(vec3 tuple) {
|
||||
return lchToRgb(hsluvToLch(tuple));
|
||||
}
|
||||
|
||||
vec3 rgbToHsluv(vec3 tuple) {
|
||||
return lchToHsluv(rgbToLch(tuple));
|
||||
}
|
||||
|
||||
vec3 luvToRgb(vec3 tuple){
|
||||
return xyzToRgb(luvToXyz(tuple));
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
float srgb2linear(float x) {
|
||||
// sRGB to linear conversion
|
||||
float lower = x / 12.92;
|
||||
float upper = pow((x + 0.055f) / 1.055f, 2.4f);
|
||||
|
||||
return mix(lower, upper, step(0.04045f, x));
|
||||
}
|
||||
|
||||
float linear2srgb(float x) {
|
||||
// Linear to sRGB conversion.
|
||||
float lower = 12.92 * x;
|
||||
float upper = 1.055 * pow(x, 1.0f / 2.4f) - 0.055f;
|
||||
|
||||
return mix(lower, upper, step(0.0031308f, x));
|
||||
}
|
||||
|
||||
vec3 linear2srgb(vec3 x) {
|
||||
vec3 lower = 12.92 * x;
|
||||
vec3 upper = 1.055 * pow(x, vec3(1.0f / 2.4f)) - 0.055f;
|
||||
return mix(lower, upper, step(0.0031308f, x));
|
||||
}
|
||||
|
||||
vec3 srgb2linear(vec3 c) {
|
||||
return vec3(srgb2linear(c.r), srgb2linear(c.g), srgb2linear(c.b));
|
||||
}
|
||||
|
|
@ -58,7 +58,7 @@ from .options.types import Options
|
|||
from .options.utils import DELETE_ENV_VAR
|
||||
from .os_window_size import edge_spacing, initial_window_size_func
|
||||
from .session import create_sessions, get_os_window_sizing_data
|
||||
from .shaders.legacy import CompileError, load_shader_programs
|
||||
from .shaders.slang import load_shader_programs
|
||||
from .types import LayerShellConfig
|
||||
from .utils import (
|
||||
cleanup_ssh_control_masters,
|
||||
|
|
@ -90,7 +90,7 @@ def set_custom_ibeam_cursor() -> None:
|
|||
def load_all_shaders() -> None:
|
||||
try:
|
||||
load_shader_programs()
|
||||
except CompileError as err:
|
||||
except ValueError as err:
|
||||
raise SystemExit(err)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
#pragma kitty_include_shader <alpha_blend.glsl>
|
||||
#pragma kitty_include_shader <utils.glsl>
|
||||
|
||||
in vec2 dimensions;
|
||||
out vec4 output_color;
|
||||
|
||||
uniform vec4 rect;
|
||||
uniform vec2 params;
|
||||
uniform vec4 color;
|
||||
uniform vec4 background_color;
|
||||
|
||||
// Signed distance function for a rounded rectangle
|
||||
float rounded_rectangle_sdf(vec2 p, vec2 b, float r) {
|
||||
// signed distance field
|
||||
// first term is used for points outside the rectangle
|
||||
vec2 q = abs(p) - b;
|
||||
return length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - r;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 size = rect.ba, origin = rect.xy;
|
||||
float thickness = params[0], corner_radius = params[1];
|
||||
// Position must be relative to the center of the rectangle of (size) located at (origin)
|
||||
vec2 position = gl_FragCoord.xy - size / 2.0 - origin;
|
||||
// Calculate distance to rounded rectangle
|
||||
float dist = rounded_rectangle_sdf(position, size*0.5 - corner_radius, corner_radius);
|
||||
|
||||
// The below is for a filled rounded rect
|
||||
// float alpha = 1.0 - smoothstep(0.0, 1.0, dist);
|
||||
// vec4 ans = color; ans.a *= alpha;
|
||||
// output_color = alpha_blend(ans, background_color);
|
||||
|
||||
// The border is outer - inner rects
|
||||
float outer_edge = -dist, inner_edge = outer_edge - thickness;
|
||||
// Smooth borders (anti-alias)
|
||||
const float step_size = 1.0; // controls how blurred the aliasing causes the rect to be
|
||||
float alpha = smoothstep(-step_size, step_size, outer_edge) - smoothstep(-step_size, step_size, inner_edge);
|
||||
vec4 ans = color; ans.a *= alpha;
|
||||
// pre-multiplied output
|
||||
output_color = alpha_blend(ans, background_color);
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
#define left 0
|
||||
#define top 1
|
||||
#define right 2
|
||||
#define bottom 3
|
||||
|
||||
const ivec2 vertex_pos_map[4] = ivec2[4](
|
||||
ivec2(right, top),
|
||||
ivec2(right, bottom),
|
||||
ivec2(left, bottom),
|
||||
ivec2(left, top)
|
||||
);
|
||||
const vec4 dest_rect = vec4(-1, 1, 1, -1);
|
||||
|
||||
void main() {
|
||||
ivec2 pos = vertex_pos_map[gl_VertexID];
|
||||
gl_Position = vec4(dest_rect[pos.x], dest_rect[pos.y], 0, 1);
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
#pragma kitty_include_shader <linear2srgb.glsl>
|
||||
|
||||
uniform sampler2D image;
|
||||
uniform vec2 src_size; // Source texture size in pixels
|
||||
|
||||
in vec2 texcoord;
|
||||
out vec4 output_color;
|
||||
|
||||
void main() {
|
||||
// The input texture contains sRGB colors with premultiplied alpha.
|
||||
// We need to output unpremultiplied sRGB colors with proper downscaling.
|
||||
|
||||
// For proper downscaling, we need to:
|
||||
// 1. Sample neighboring pixels
|
||||
// 2. Convert from sRGB to linear (unpremultiplying first)
|
||||
// 3. Average in linear space
|
||||
// 4. Convert back to sRGB
|
||||
// 5. Output unpremultiplied
|
||||
|
||||
// Calculate the texel size
|
||||
vec2 texel_size = 1.0 / src_size;
|
||||
|
||||
// Sample a 2x2 grid for better quality downscaling
|
||||
// This provides basic bilinear-like filtering in linear space
|
||||
vec2 tc = texcoord;
|
||||
|
||||
vec4 s00 = texture(image, tc + vec2(-0.25, -0.25) * texel_size);
|
||||
vec4 s10 = texture(image, tc + vec2( 0.25, -0.25) * texel_size);
|
||||
vec4 s01 = texture(image, tc + vec2(-0.25, 0.25) * texel_size);
|
||||
vec4 s11 = texture(image, tc + vec2( 0.25, 0.25) * texel_size);
|
||||
|
||||
// Unpremultiply and convert to linear for each sample
|
||||
vec3 linear00 = s00.a > 0.0 ? srgb2linear(s00.rgb / s00.a) : vec3(0.0);
|
||||
vec3 linear10 = s10.a > 0.0 ? srgb2linear(s10.rgb / s10.a) : vec3(0.0);
|
||||
vec3 linear01 = s01.a > 0.0 ? srgb2linear(s01.rgb / s01.a) : vec3(0.0);
|
||||
vec3 linear11 = s11.a > 0.0 ? srgb2linear(s11.rgb / s11.a) : vec3(0.0);
|
||||
|
||||
// Average the alpha values
|
||||
float avg_alpha = (s00.a + s10.a + s01.a + s11.a) * 0.25;
|
||||
|
||||
// For proper downsampling with transparency, weight colors by their alpha
|
||||
// This ensures partially transparent pixels contribute proportionally
|
||||
vec3 weighted_sum = linear00 * s00.a + linear10 * s10.a + linear01 * s01.a + linear11 * s11.a;
|
||||
float total_weight = s00.a + s10.a + s01.a + s11.a;
|
||||
|
||||
// Calculate the weighted average color in linear space
|
||||
vec3 avg_linear = total_weight > 0.0 ? weighted_sum / total_weight : vec3(0.0);
|
||||
|
||||
// Convert back to sRGB
|
||||
vec3 srgb_color = linear2srgb(avg_linear);
|
||||
|
||||
// Output unpremultiplied sRGB color
|
||||
output_color = vec4(srgb_color, avg_alpha);
|
||||
}
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
uniform vec4 src_rect, dest_rect;
|
||||
#pragma kitty_include_shader <blit_common.glsl>
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
#include "text-cache.h"
|
||||
#include "window_logo.h"
|
||||
#include "srgb_gamma.h"
|
||||
#include "uniforms_generated.h"
|
||||
#include "glsl-uniforms.h"
|
||||
#include "state.h"
|
||||
|
||||
enum {
|
||||
|
|
@ -332,12 +332,13 @@ draw_rounded_rect(
|
|||
|
||||
// Cell {{{
|
||||
|
||||
enum { CELL_RENDER_DATA_BINDING_POINT = 0, COLOR_TABLE_BINDING_POINT = 1 };
|
||||
enum { CELL_RENDER_DATA_BINDING_POINT = 0, COLOR_TABLE_BINDING_POINT = 1, GAMMA_LUT_BINDING_POINT = 2, BORDER_COLORS_BINDING_POINT = 3 };
|
||||
enum { GAMMA_LUT_GLOBAL_BUFFER, BORDER_COLORS_GLOBAL_BUFFER };
|
||||
// A VAO used only to hold buffers for UBOs that are shared amongst programs/windows,
|
||||
// its vertex attribute/array facilities are unused.
|
||||
static ssize_t shader_globals_vao_idx = -1;
|
||||
|
||||
typedef struct {
|
||||
UniformBlock render_data;
|
||||
UniformBlock color_table;
|
||||
GLint color_table_stride;
|
||||
CellUniforms uniforms;
|
||||
} CellProgramLayout;
|
||||
static CellProgramLayout cell_program_layouts[NUM_PROGRAMS];
|
||||
|
|
@ -377,19 +378,29 @@ typedef struct BorderProgramLayout {
|
|||
} BorderProgramLayout;
|
||||
static BorderProgramLayout border_program_layout;
|
||||
|
||||
static void
|
||||
write_float_array_to_ubo(void *dest, const GLfloat *src, size_t count, const ArrayInformation *ai) {
|
||||
GLint stride = MAX((GLint)sizeof(GLfloat), ai->stride);
|
||||
uint8_t *buf = (uint8_t*)dest + ai->offset;
|
||||
for (size_t i = 0; i < count; i++, buf += stride) memcpy(buf, src + i, sizeof(GLfloat));
|
||||
}
|
||||
|
||||
static void
|
||||
write_uint_array_to_ubo(void *dest, const GLuint *src, size_t count, const ArrayInformation *ai) {
|
||||
GLint stride = MAX((GLint)sizeof(GLuint), ai->stride);
|
||||
uint8_t *buf = (uint8_t*)dest + ai->offset;
|
||||
for (size_t i = 0; i < count; i++, buf += stride) memcpy(buf, src + i, sizeof(GLuint));
|
||||
}
|
||||
|
||||
static void
|
||||
init_cell_program(void) {
|
||||
GLint gamma_lut_buf_size = 0;
|
||||
for (int i = CELL_PROGRAM; i < CELL_PROGRAM_SENTINEL; i++) {
|
||||
cell_program_layouts[i].render_data.index = block_index(i, "CellRenderData");
|
||||
cell_program_layouts[i].render_data.size = block_size(i, cell_program_layouts[i].render_data.index);
|
||||
cell_program_layouts[i].color_table.index = block_index(i, "ColorTable");
|
||||
cell_program_layouts[i].color_table.size = block_size(i, cell_program_layouts[i].color_table.index);
|
||||
cell_program_layouts[i].color_table_stride = get_uniform_information(i, "color_table[0]", GL_UNIFORM_ARRAY_STRIDE);
|
||||
glUniformBlockBinding(program_id(i), cell_program_layouts[i].render_data.index, CELL_RENDER_DATA_BINDING_POINT);
|
||||
glUniformBlockBinding(program_id(i), cell_program_layouts[i].color_table.index, COLOR_TABLE_BINDING_POINT);
|
||||
get_uniform_locations_cell(i, &cell_program_layouts[i].uniforms);
|
||||
bind_program(i);
|
||||
glUniform1fv(cell_program_layouts[i].uniforms.gamma_lut, arraysz(srgb_lut), srgb_lut);
|
||||
glUniformBlockBinding(program_id(i), cell_program_layouts[i].uniforms.CellRenderData.index, CELL_RENDER_DATA_BINDING_POINT);
|
||||
glUniformBlockBinding(program_id(i), cell_program_layouts[i].uniforms.ColorTable.index, COLOR_TABLE_BINDING_POINT);
|
||||
glUniformBlockBinding(program_id(i), cell_program_layouts[i].uniforms.GammaLUT.index, GAMMA_LUT_BINDING_POINT);
|
||||
gamma_lut_buf_size = MAX(gamma_lut_buf_size, cell_program_layouts[i].uniforms.GammaLUT.size);
|
||||
}
|
||||
|
||||
// Sanity check to ensure the attribute location binding worked
|
||||
|
|
@ -407,9 +418,25 @@ init_cell_program(void) {
|
|||
get_uniform_locations_blit(BLIT_PROGRAM, &blit_program_layout.uniforms);
|
||||
get_uniform_locations_screenshot(SCREENSHOT_PROGRAM, &screenshot_program_layout.uniforms);
|
||||
get_uniform_locations_rounded_rect(ROUNDED_RECT_PROGRAM, &rounded_rect_program_layout.uniforms);
|
||||
bind_program(BORDERS_PROGRAM);
|
||||
get_uniform_locations_border(BORDERS_PROGRAM, &border_program_layout.uniforms);
|
||||
glUniform1fv(border_program_layout.uniforms.gamma_lut, arraysz(srgb_lut), srgb_lut);
|
||||
glUniformBlockBinding(program_id(BORDERS_PROGRAM), border_program_layout.uniforms.Colors.index, BORDER_COLORS_BINDING_POINT);
|
||||
glUniformBlockBinding(program_id(BORDERS_PROGRAM), border_program_layout.uniforms.GammaLUT.index, GAMMA_LUT_BINDING_POINT);
|
||||
|
||||
// The gamma LUT is a constant, shared amongst all the programs that use it via a single UBO.
|
||||
if (shader_globals_vao_idx == -1) {
|
||||
shader_globals_vao_idx = create_vao();
|
||||
add_buffer_to_vao(shader_globals_vao_idx, GL_UNIFORM_BUFFER);
|
||||
add_buffer_to_vao(shader_globals_vao_idx, GL_UNIFORM_BUFFER);
|
||||
gamma_lut_buf_size = MAX(gamma_lut_buf_size, border_program_layout.uniforms.GammaLUT.size);
|
||||
void *gamma_lut_buf = alloc_and_map_vao_buffer(shader_globals_vao_idx, gamma_lut_buf_size, GAMMA_LUT_GLOBAL_BUFFER, false);
|
||||
const ArrayInformation *a = &cell_program_layouts[CELL_PROGRAM].uniforms.gamma_lut;
|
||||
write_float_array_to_ubo(gamma_lut_buf, srgb_lut, arraysz(srgb_lut), a);
|
||||
unmap_vao_buffer(shader_globals_vao_idx, GAMMA_LUT_GLOBAL_BUFFER);
|
||||
// The border colors change on every draw, but are shared amongst all border VAOs (only one is ever drawn at a time)
|
||||
alloc_vao_buffer(shader_globals_vao_idx, border_program_layout.uniforms.Colors.size, BORDER_COLORS_GLOBAL_BUFFER, GL_STREAM_DRAW);
|
||||
bind_vao_uniform_buffer(shader_globals_vao_idx, GAMMA_LUT_GLOBAL_BUFFER, GAMMA_LUT_BINDING_POINT);
|
||||
bind_vao_uniform_buffer(shader_globals_vao_idx, BORDER_COLORS_GLOBAL_BUFFER, BORDER_COLORS_BINDING_POINT);
|
||||
}
|
||||
}
|
||||
|
||||
#define CELL_BUFFERS enum { cell_data_buffer, selection_buffer, uniform_buffer, color_table_buffer };
|
||||
|
|
@ -417,8 +444,9 @@ init_cell_program(void) {
|
|||
ssize_t
|
||||
create_cell_vao(void) {
|
||||
ssize_t vao_idx = create_vao();
|
||||
const struct CellUniforms *u = &cell_program_layouts[CELL_PROGRAM].uniforms;
|
||||
#define A(name, size, dtype, offset, stride) \
|
||||
add_attribute_to_vao(CELL_PROGRAM, vao_idx, #name, \
|
||||
add_attribute_to_vao(vao_idx, u->name, \
|
||||
/*size=*/size, /*dtype=*/dtype, /*stride=*/stride, /*offset=*/offset, /*divisor=*/1);
|
||||
#define A1(name, size, dtype, offset) A(name, size, dtype, (void*)(offsetof(GPUCell, offset)), sizeof(GPUCell))
|
||||
|
||||
|
|
@ -430,24 +458,16 @@ create_cell_vao(void) {
|
|||
A(is_selected, 1, GL_UNSIGNED_BYTE, NULL, 0);
|
||||
|
||||
size_t bufnum = add_buffer_to_vao(vao_idx, GL_UNIFORM_BUFFER);
|
||||
alloc_vao_buffer(vao_idx, cell_program_layouts[CELL_PROGRAM].render_data.size, bufnum, GL_STREAM_DRAW);
|
||||
alloc_vao_buffer(vao_idx, cell_program_layouts[CELL_PROGRAM].uniforms.CellRenderData.size, bufnum, GL_STREAM_DRAW);
|
||||
|
||||
size_t ctbufnum = add_buffer_to_vao(vao_idx, GL_UNIFORM_BUFFER);
|
||||
alloc_vao_buffer(vao_idx, cell_program_layouts[CELL_PROGRAM].color_table.size, ctbufnum, GL_STATIC_DRAW);
|
||||
alloc_vao_buffer(vao_idx, cell_program_layouts[CELL_PROGRAM].uniforms.ColorTable.size, ctbufnum, GL_STATIC_DRAW);
|
||||
|
||||
return vao_idx;
|
||||
#undef A
|
||||
#undef A1
|
||||
}
|
||||
|
||||
ssize_t
|
||||
create_graphics_vao(void) {
|
||||
ssize_t vao_idx = create_vao();
|
||||
add_buffer_to_vao(vao_idx, GL_ARRAY_BUFFER);
|
||||
add_attribute_to_vao(GRAPHICS_PROGRAM, vao_idx, "src", 4, GL_FLOAT, 0, NULL, 0);
|
||||
return vao_idx;
|
||||
}
|
||||
|
||||
#define IS_SPECIAL_COLOR(name) (screen->color_profile->overridden.name.type == COLOR_IS_SPECIAL || (screen->color_profile->overridden.name.type == COLOR_NOT_SET && screen->color_profile->configured.name.type == COLOR_IS_SPECIAL))
|
||||
|
||||
static void
|
||||
|
|
@ -486,11 +506,13 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, i
|
|||
// Send the uniform data
|
||||
ColorProfile *cp = screen->paused_rendering.expires_at ? &screen->paused_rendering.color_profile : screen->color_profile;
|
||||
if (cp->dirty || screen->reload_all_gpu_data) {
|
||||
GLuint *ct_buf = (GLuint*)map_vao_buffer_for_write_only(vao_idx, color_table_buf, 0, cell_program_layouts[CELL_PROGRAM].color_table.size);
|
||||
copy_color_table_to_buffer(cp, ct_buf, 0, cell_program_layouts[CELL_PROGRAM].color_table_stride / sizeof(GLuint));
|
||||
const ArrayInformation *ai = &cell_program_layouts[CELL_PROGRAM].uniforms.color_table;
|
||||
uint8_t *base = (uint8_t*)map_vao_buffer_for_write_only(vao_idx, color_table_buf, 0, cell_program_layouts[CELL_PROGRAM].uniforms.ColorTable.size);
|
||||
GLuint *ct_buf = (GLuint*)(base + ai->offset);
|
||||
copy_color_table_to_buffer(cp, ct_buf, 0, ai->stride / sizeof(GLuint));
|
||||
unmap_vao_buffer(vao_idx, color_table_buf);
|
||||
}
|
||||
struct GPUCellRenderData *rd = (struct GPUCellRenderData*)map_vao_buffer_for_write_only(vao_idx, uniform_buffer, 0, cell_program_layouts[CELL_PROGRAM].render_data.size);
|
||||
struct GPUCellRenderData *rd = (struct GPUCellRenderData*)map_vao_buffer_for_write_only(vao_idx, uniform_buffer, 0, cell_program_layouts[CELL_PROGRAM].uniforms.CellRenderData.size);
|
||||
#define COLOR(name) colorprofile_to_color(cp, cp->overridden.name, cp->configured.name).rgb
|
||||
rd->default_fg = COLOR(default_fg);
|
||||
rd->highlight_fg = COLOR(highlight_fg); rd->highlight_bg = COLOR(highlight_bg);
|
||||
|
|
@ -1467,9 +1489,10 @@ create_border_vao(void) {
|
|||
ssize_t vao_idx = create_vao();
|
||||
|
||||
add_buffer_to_vao(vao_idx, GL_ARRAY_BUFFER);
|
||||
add_attribute_to_vao(BORDERS_PROGRAM, vao_idx, "rect",
|
||||
const BorderUniforms *u = &border_program_layout.uniforms;
|
||||
add_attribute_to_vao(vao_idx, u->rect,
|
||||
/*size=*/4, /*dtype=*/GL_FLOAT, /*stride=*/sizeof(BorderRect), /*offset=*/(void*)offsetof(BorderRect, left), /*divisor=*/1);
|
||||
add_attribute_to_vao(BORDERS_PROGRAM, vao_idx, "rect_color",
|
||||
add_attribute_to_vao(vao_idx, u->rect_color,
|
||||
/*size=*/1, /*dtype=*/GL_UNSIGNED_INT, /*stride=*/sizeof(BorderRect), /*offset=*/(void*)(offsetof(BorderRect, color)), /*divisor=*/1);
|
||||
|
||||
return vao_idx;
|
||||
|
|
@ -1494,7 +1517,9 @@ draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_bu
|
|||
OPT(bell_border_color), OPT(tab_bar_background), OPT(tab_bar_margin_color),
|
||||
w->tab_bar_edge_color.left, w->tab_bar_edge_color.right
|
||||
};
|
||||
glUniform1uiv(border_program_layout.uniforms.colors, arraysz(colors), colors);
|
||||
void *colors_buf = map_vao_buffer_for_write_only(shader_globals_vao_idx, BORDER_COLORS_GLOBAL_BUFFER, 0, border_program_layout.uniforms.Colors.size);
|
||||
write_uint_array_to_ubo(colors_buf, colors, arraysz(colors), &border_program_layout.uniforms.colors);
|
||||
unmap_vao_buffer(shader_globals_vao_idx, BORDER_COLORS_GLOBAL_BUFFER);
|
||||
glUniform1f(border_program_layout.uniforms.background_opacity, background_opacity);
|
||||
if (!w->needs_layers) glEnable(GL_FRAMEBUFFER_SRGB);
|
||||
draw_quad(has_background_image, num_border_rects);
|
||||
|
|
|
|||
|
|
@ -1,242 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# License: GPLv3 Copyright: 2023, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import re
|
||||
from collections.abc import Callable, Iterator
|
||||
from functools import lru_cache, partial
|
||||
from itertools import count
|
||||
from typing import Any, Literal, Optional
|
||||
|
||||
from kitty.constants import read_kitty_resource
|
||||
from kitty.fast_data_types import (
|
||||
BGIMAGE_PROGRAM,
|
||||
BLINK,
|
||||
BLIT_PROGRAM,
|
||||
BORDERS_PROGRAM,
|
||||
CELL_BG_PROGRAM,
|
||||
CELL_FG_PROGRAM,
|
||||
CELL_PROGRAM,
|
||||
COLOR_IS_INDEX,
|
||||
COLOR_IS_RGB,
|
||||
COLOR_IS_SPECIAL,
|
||||
COLOR_NOT_SET,
|
||||
DECORATION,
|
||||
DECORATION_MASK,
|
||||
DIM,
|
||||
GLSL_VERSION,
|
||||
GRAPHICS_ALPHA_MASK_PROGRAM,
|
||||
GRAPHICS_PREMULT_PROGRAM,
|
||||
GRAPHICS_PROGRAM,
|
||||
MARK,
|
||||
MARK_MASK,
|
||||
REVERSE,
|
||||
ROUNDED_RECT_PROGRAM,
|
||||
SCREENSHOT_PROGRAM,
|
||||
STRIKETHROUGH,
|
||||
TINT_PROGRAM,
|
||||
TRAIL_PROGRAM,
|
||||
compile_program,
|
||||
get_options,
|
||||
init_cell_program,
|
||||
)
|
||||
from kitty.options.types import Options, defaults
|
||||
|
||||
|
||||
def identity(x: str) -> str:
|
||||
return x
|
||||
|
||||
|
||||
class CompileError(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
class Program:
|
||||
|
||||
include_pat: Optional['re.Pattern[str]'] = None
|
||||
filename_number_base: int = 7893000
|
||||
|
||||
def __init__(self, name: str, vertex_name: str = '', fragment_name: str = '') -> None:
|
||||
self.name = name
|
||||
self.filename_number_counter = count(self.filename_number_base + 1)
|
||||
self.filename_map: dict[str, int] = {}
|
||||
if Program.include_pat is None:
|
||||
Program.include_pat = re.compile(r'^#pragma\s+kitty_include_shader\s+<(.+?)>', re.MULTILINE)
|
||||
self.vertex_name = vertex_name or f'{name}_vertex.glsl'
|
||||
self.fragment_name = fragment_name or f'{name}_fragment.glsl'
|
||||
self.original_vertex_sources = tuple(self._load_sources(self.vertex_name, set()))
|
||||
self.original_fragment_sources = tuple(self._load_sources(self.fragment_name, set()))
|
||||
self.vertex_sources = self.original_vertex_sources
|
||||
self.fragment_sources = self.original_fragment_sources
|
||||
|
||||
def _load_sources(self, name: str, seen: set[str], level: int = 0) -> Iterator[str]:
|
||||
if level == 0:
|
||||
yield f'#version {GLSL_VERSION}\n'
|
||||
if name in seen:
|
||||
return
|
||||
seen.add(name)
|
||||
self.filename_map[name] = fnum = next(self.filename_number_counter)
|
||||
src = read_kitty_resource(name).decode('utf-8')
|
||||
pos = 0
|
||||
lnum = 0
|
||||
assert Program.include_pat is not None
|
||||
for m in Program.include_pat.finditer(src):
|
||||
prefix = src[pos:m.start()]
|
||||
if prefix:
|
||||
yield f'\n#line {lnum} {fnum}\n{prefix}'
|
||||
lnum += prefix.count('\n')
|
||||
iname = m.group(1)
|
||||
yield from self._load_sources(iname, seen, level+1)
|
||||
pos = m.end()
|
||||
if pos < len(src):
|
||||
yield f'\n#line {lnum} {fnum}\n{src[pos:]}'
|
||||
|
||||
def apply_to_sources(self, vertex: Callable[[str], str] = identity, frag: Callable[[str], str] = identity) -> None:
|
||||
self.vertex_sources = self.original_vertex_sources if vertex is identity else tuple(map(vertex, self.original_vertex_sources))
|
||||
self.fragment_sources = self.original_fragment_sources if frag is identity else tuple(map(frag, self.original_fragment_sources))
|
||||
|
||||
def compile(self, program_id: int, allow_recompile: bool = False) -> None:
|
||||
try:
|
||||
compile_program(program_id, self.vertex_sources, self.fragment_sources, allow_recompile)
|
||||
return
|
||||
except ValueError as err:
|
||||
lines = str(err).splitlines()
|
||||
msg = []
|
||||
pat = re.compile(r'\b(' + str(self.filename_number_base).replace('0', r'\d') + r')\b')
|
||||
rmap = {str(v): k for k, v in self.filename_map.items()}
|
||||
|
||||
def sub(m: 're.Match[str]') -> str:
|
||||
return rmap.get(m.group(1), m.group(1))
|
||||
|
||||
for line in lines:
|
||||
msg.append(pat.sub(sub, line))
|
||||
raise CompileError('\n'.join(msg))
|
||||
|
||||
|
||||
@lru_cache(maxsize=64)
|
||||
def program_for(name: str) -> Program:
|
||||
return Program(name)
|
||||
|
||||
|
||||
class MultiReplacer:
|
||||
|
||||
pat: Optional['re.Pattern[str]'] = None
|
||||
|
||||
def __init__(self, **replacements: Any):
|
||||
self.replacements = {k: str(v) for k, v in replacements.items()}
|
||||
if MultiReplacer.pat is None:
|
||||
MultiReplacer.pat = re.compile(r'\{([A-Z_]+)\}')
|
||||
|
||||
def _sub(self, m: 're.Match[str]') -> str:
|
||||
return self.replacements.get(m.group(1), m.group(1))
|
||||
|
||||
def __call__(self, src: str) -> str:
|
||||
assert self.pat is not None
|
||||
return self.pat.sub(self._sub, src)
|
||||
|
||||
null_replacer = MultiReplacer()
|
||||
|
||||
|
||||
class LoadShaderPrograms:
|
||||
text_fg_override_threshold: tuple[float, Literal['%', 'ratio']] = 0, '%'
|
||||
text_old_gamma: bool = False
|
||||
cell_program_replacer: MultiReplacer = null_replacer
|
||||
opts: Options | None = None
|
||||
compile_programs: bool = True
|
||||
|
||||
@property
|
||||
def needs_recompile(self) -> bool:
|
||||
opts = self.opts or get_options()
|
||||
return (
|
||||
bool(opts.text_fg_override_threshold[0]) != bool(self.text_fg_override_threshold[0]) or
|
||||
opts.text_fg_override_threshold[1] != self.text_fg_override_threshold[1] or
|
||||
(opts.text_composition_strategy == 'legacy') != self.text_old_gamma
|
||||
)
|
||||
|
||||
def recompile_if_needed(self) -> None:
|
||||
if self.needs_recompile:
|
||||
self(allow_recompile=True)
|
||||
|
||||
def __call__(self, allow_recompile: bool = False) -> None:
|
||||
opts = self.opts or get_options()
|
||||
self.text_old_gamma = opts.text_composition_strategy == 'legacy'
|
||||
self.text_fg_override_threshold = opts.text_fg_override_threshold
|
||||
|
||||
cell = program_for('cell')
|
||||
if self.cell_program_replacer is null_replacer:
|
||||
self.cell_program_replacer = MultiReplacer(
|
||||
REVERSE_SHIFT=REVERSE,
|
||||
STRIKE_SHIFT=STRIKETHROUGH,
|
||||
DIM_SHIFT=DIM,
|
||||
BLINK_SHIFT=BLINK,
|
||||
DECORATION_SHIFT=DECORATION,
|
||||
MARK_SHIFT=MARK,
|
||||
MARK_MASK=MARK_MASK,
|
||||
DECORATION_MASK=DECORATION_MASK,
|
||||
COLOR_NOT_SET=COLOR_NOT_SET,
|
||||
COLOR_IS_SPECIAL=COLOR_IS_SPECIAL,
|
||||
COLOR_IS_INDEX=COLOR_IS_INDEX,
|
||||
COLOR_IS_RGB=COLOR_IS_RGB,
|
||||
)
|
||||
|
||||
def resolve_cell_defines(only_fg: int, only_bg: int, src: str) -> str:
|
||||
algo = '1' if self.text_fg_override_threshold[1] == '%' else '2'
|
||||
if not self.text_fg_override_threshold[0]:
|
||||
algo = '0'
|
||||
r = self.cell_program_replacer.replacements
|
||||
r['ONLY_FOREGROUND'] = str(only_fg)
|
||||
r['ONLY_BACKGROUND'] = str(only_bg)
|
||||
r['FG_OVERRIDE_ALGO'] = algo
|
||||
r['TEXT_NEW_GAMMA'] = '0' if self.text_old_gamma else '1'
|
||||
return self.cell_program_replacer(src)
|
||||
for prog, (only_fg, only_bg) in {
|
||||
CELL_PROGRAM: (0, 0), CELL_FG_PROGRAM: (1, 0), CELL_BG_PROGRAM: (0, 1),
|
||||
}.items():
|
||||
fn = partial(resolve_cell_defines, only_fg, only_bg)
|
||||
cell.apply_to_sources(vertex=fn, frag=fn)
|
||||
if self.compile_programs:
|
||||
cell.compile(prog, allow_recompile)
|
||||
graphics = program_for('graphics')
|
||||
|
||||
def resolve_graphics_fragment_defines(which: str, is_premult: bool, f: str) -> str:
|
||||
ans = f.replace('#define ALPHA_TYPE', f'#define {which}', 1)
|
||||
return ans.replace('TEXTURE_IS_NOT_PREMULTIPLIED', '0' if is_premult else '1')
|
||||
|
||||
for p, (which, is_premult) in {
|
||||
GRAPHICS_PROGRAM: ('IMAGE', False),
|
||||
GRAPHICS_ALPHA_MASK_PROGRAM: ('ALPHA_MASK', False),
|
||||
GRAPHICS_PREMULT_PROGRAM: ('IMAGE', True),
|
||||
}.items():
|
||||
graphics.apply_to_sources(frag=partial(resolve_graphics_fragment_defines, which, is_premult))
|
||||
if self.compile_programs:
|
||||
graphics.compile(p, allow_recompile)
|
||||
|
||||
if self.compile_programs:
|
||||
program_for('bgimage').compile(BGIMAGE_PROGRAM, allow_recompile)
|
||||
program_for('tint').compile(TINT_PROGRAM, allow_recompile)
|
||||
program_for('trail').compile(TRAIL_PROGRAM, allow_recompile)
|
||||
program_for('blit').compile(BLIT_PROGRAM, allow_recompile)
|
||||
program_for('screenshot').compile(SCREENSHOT_PROGRAM, allow_recompile)
|
||||
program_for('rounded_rect').compile(ROUNDED_RECT_PROGRAM, allow_recompile)
|
||||
program_for('border').compile(BORDERS_PROGRAM, allow_recompile)
|
||||
init_cell_program()
|
||||
|
||||
|
||||
load_shader_programs = LoadShaderPrograms()
|
||||
|
||||
|
||||
def dump_programs(dest_dir: str) -> None:
|
||||
import os
|
||||
load_shader_programs.compile_programs = False
|
||||
load_shader_programs.opts = defaults
|
||||
try:
|
||||
load_shader_programs()
|
||||
os.makedirs(dest_dir, exist_ok=True)
|
||||
for name in 'border bgimage tint trail blit screenshot rounded_rect cell graphics'.split():
|
||||
p = program_for(name)
|
||||
with open(os.path.join(dest_dir, f'{name}.vert.glsl'), 'w') as f:
|
||||
f.write('\n\n'.join(p.vertex_sources))
|
||||
with open(os.path.join(dest_dir, f'{name}.frag.glsl'), 'w') as f:
|
||||
f.write('\n\n'.join(p.fragment_sources))
|
||||
finally:
|
||||
load_shader_programs.compile_programs = True
|
||||
load_shader_programs.opts = None
|
||||
|
|
@ -510,12 +510,14 @@ class GLSLMetadata:
|
|||
uniform_structs: dict[str, dict[str, str]]
|
||||
input_locations: dict[str, int]
|
||||
uniform_struct_names: dict[str, str]
|
||||
fragment_inputs: dict[int, str]
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.loose_uniforms = {}
|
||||
self.uniform_structs = {}
|
||||
self.input_locations = {}
|
||||
self.uniform_struct_names = {}
|
||||
self.fragment_inputs = {}
|
||||
|
||||
def merge(self, other: 'GLSLMetadata') -> None:
|
||||
self.loose_uniforms.update(other.loose_uniforms)
|
||||
|
|
@ -528,11 +530,24 @@ class GLSLMetadata:
|
|||
'loose_uniforms': self.loose_uniforms,
|
||||
'uniform_structs': self.uniform_structs,
|
||||
'input_locations': self.input_locations,
|
||||
'uniform_struct_names': self.uniform_struct_names}
|
||||
'uniform_struct_names': self.uniform_struct_names,
|
||||
'fragment_inputs': self.fragment_inputs,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def fromdict(cls, d: dict[str, Any]) -> 'GLSLMetadata':
|
||||
ans = GLSLMetadata()
|
||||
ans.loose_uniforms = d['loose_uniforms']
|
||||
ans.uniform_structs = d['uniform_structs']
|
||||
ans.input_locations = d['input_locations']
|
||||
ans.uniform_struct_names = d['uniform_struct_names']
|
||||
ans.fragment_inputs = d['fragment_inputs']
|
||||
return ans
|
||||
|
||||
|
||||
def fixup_opengl_code(glsl_code: str, path: str) -> tuple[str, GLSLMetadata]:
|
||||
is_fragment_shader = 'frag' in os.path.basename(path).split('.')
|
||||
def fixup_opengl_code(glsl_code: str, shader_name: str, existing_metadata: GLSLMetadata | None) -> tuple[str, GLSLMetadata]:
|
||||
is_fragment_shader = existing_metadata is None
|
||||
shader_name += '.frag.glsl' if is_fragment_shader else '.vert.glsl'
|
||||
lines: list[str] = []
|
||||
in_uniform_block = False
|
||||
in_uniform_block_contents = False
|
||||
|
|
@ -545,12 +560,33 @@ def fixup_opengl_code(glsl_code: str, path: str) -> tuple[str, GLSLMetadata]:
|
|||
uniform_structs = {}
|
||||
uniform_struct_names = {}
|
||||
input_locations = {}
|
||||
named_interface_blocks = set()
|
||||
pipeline_io_vars: dict[int, str] = {}
|
||||
replacements = {
|
||||
'gl_VertexIndex': 'gl_VertexID',
|
||||
'gl_BaseVertex': '0',
|
||||
'gl_InstanceIndex': 'gl_InstanceID',
|
||||
'gl_BaseInstance': '0',
|
||||
}
|
||||
fragment_inputs = {} if existing_metadata is None else existing_metadata.fragment_inputs.copy()
|
||||
|
||||
def register_pipeline_boundary_io(line: str, next_line: str) -> None:
|
||||
m = re.search(r'location = (\d+)', line)
|
||||
assert m is not None
|
||||
name = next_line.split()[-1].rstrip(';')
|
||||
location = int(m.group(1))
|
||||
if existing_metadata is None:
|
||||
if not next_line.startswith('out '):
|
||||
pipeline_io_vars[location] = name
|
||||
else:
|
||||
with suppress(KeyError):
|
||||
replacements[name] = fragment_inputs.pop(location)
|
||||
|
||||
def add_uniform_name(name: str, uniform_names: dict[str, str] = uniform_names) -> str:
|
||||
name = name.rstrip(';')
|
||||
uniform_name = name.rpartition('_')[0]
|
||||
if uniform_name in uniform_names:
|
||||
raise KeyError(f'The uniform name {uniform_name} is used with multiple suffixes in {path}')
|
||||
raise KeyError(f'The uniform name {uniform_name} is used with multiple suffixes in {shader_name}')
|
||||
if '[' in name:
|
||||
name = name.partition('[')[0] + '[0]'
|
||||
uniform_names[uniform_name] = name
|
||||
|
|
@ -566,6 +602,8 @@ def fixup_opengl_code(glsl_code: str, path: str) -> tuple[str, GLSLMetadata]:
|
|||
block_name = line.lstrip('}').rstrip(';').strip()
|
||||
if uniform_block_is_struct:
|
||||
uniform_structs[current_uniform_struct_name] = current_uniform_struct_members
|
||||
named_interface_blocks.add(block_name)
|
||||
line = '};'
|
||||
else:
|
||||
uniform_blocks[block_name] = current_uniform_names
|
||||
line = '// ' + line
|
||||
|
|
@ -591,9 +629,11 @@ def fixup_opengl_code(glsl_code: str, path: str) -> tuple[str, GLSLMetadata]:
|
|||
line = '// ' + line
|
||||
elif line.startswith('layout(binding ='):
|
||||
line = '// ' + line
|
||||
elif line.startswith('layout(location =') and (is_fragment_shader or next_line.startswith('out ')):
|
||||
elif line.startswith('layout(location =') and (is_fragment_shader or next_line.startswith('out ')): # ))
|
||||
register_pipeline_boundary_io(line, next_line)
|
||||
line = '// ' + line
|
||||
elif line.startswith('flat layout(location ='):
|
||||
register_pipeline_boundary_io(line[len('flat '):], next_line)
|
||||
line = 'flat'
|
||||
elif line: # ))))
|
||||
words = line.split()
|
||||
|
|
@ -609,47 +649,66 @@ def fixup_opengl_code(glsl_code: str, path: str) -> tuple[str, GLSLMetadata]:
|
|||
uniform_struct_names[current_uniform_struct_name] = words[-1]
|
||||
else:
|
||||
line = '// ' + line
|
||||
elif words[0] == 'uniform' and len(words) > 2 and words[1].startswith('sampler'):
|
||||
elif words[0] == 'uniform' and len(words) > 2 and words[1].removeprefix('u').removeprefix('i').startswith('sampler'):
|
||||
add_uniform_name(words[2])
|
||||
elif not is_fragment_shader and words[0] == 'in':
|
||||
name = words[-1].rstrip(';')
|
||||
input_locations[name.rpartition('_')[0]] = int(lines[-1].split()[-1].rstrip(')'))
|
||||
lines.append(line)
|
||||
if fragment_inputs:
|
||||
raise ValueError(
|
||||
f'Could not match vertex outputs to fragment inputs for shader: {shader_name}. Leftover fragment inputs: {", ".join(fragment_inputs.values())}')
|
||||
ans = '\n'.join(lines)
|
||||
for block_name, names in uniform_blocks.items():
|
||||
for u in names:
|
||||
u = u.partition('[')[0] # ]
|
||||
ans = ans.replace(f'{block_name}.{u}', u)
|
||||
ans = ans.replace('gl_VertexIndex', 'gl_VertexID')
|
||||
ans = ans.replace('gl_BaseVertex', '0')
|
||||
ans = ans.replace('gl_InstanceIndex', 'gl_InstanceID')
|
||||
ans = ans.replace('gl_BaseInstance', '0')
|
||||
replacements[f'{block_name}.{u}'] = u
|
||||
|
||||
for x in named_interface_blocks:
|
||||
replacements[f'{x}.'] = ''
|
||||
|
||||
def sub(m: re.Match[str]) -> str:
|
||||
return replacements[m.group(1)]
|
||||
ans = re.sub(r'\b(' + '|'.join(re.escape(word) for word in replacements) + r')\b', sub, ans)
|
||||
m = GLSLMetadata()
|
||||
m.loose_uniforms = uniform_names
|
||||
m.uniform_structs = uniform_structs
|
||||
m.input_locations = input_locations
|
||||
m.uniform_struct_names = uniform_struct_names
|
||||
if is_fragment_shader:
|
||||
m.fragment_inputs = pipeline_io_vars
|
||||
return ans, m
|
||||
|
||||
|
||||
def fixup_opengl_files(paths: Iterable[str], metadata_map: dict[str, GLSLMetadata]) -> None:
|
||||
def shader_name_from_path(path: str) -> str:
|
||||
parts = os.path.basename(path).split('.')
|
||||
if parts[1] in ('vert', 'frag', 'glsl'):
|
||||
return parts[0]
|
||||
return '.'.join(parts[:2])
|
||||
|
||||
|
||||
def fixup_opengl_files(paths: Iterable[str]) -> None:
|
||||
' Convert the GLSL output of slangc to something that will work with OpenGL 3.1 '
|
||||
for path in paths:
|
||||
metadata_map: dict[str, GLSLMetadata] = {}
|
||||
dest_dir = ''
|
||||
for path in sorted(paths):
|
||||
dest_dir = os.path.dirname(path)
|
||||
with open(path, 'r') as f:
|
||||
glsl_code = f.read()
|
||||
shader_name = shader_name_from_path(path)
|
||||
try:
|
||||
fixed, metadata = fixup_opengl_code(glsl_code, path)
|
||||
fixed, metadata = fixup_opengl_code(glsl_code, shader_name, metadata_map.get(shader_name))
|
||||
except Exception:
|
||||
os.unlink(path)
|
||||
raise
|
||||
parts = os.path.basename(path).split('.')
|
||||
write_if_changed(path, fixed)
|
||||
if len(parts) == 3:
|
||||
name = parts[0]
|
||||
if name in metadata_map:
|
||||
metadata_map[name].merge(metadata)
|
||||
else:
|
||||
metadata_map[name] = metadata
|
||||
if shader_name in metadata_map:
|
||||
metadata_map[shader_name].merge(metadata)
|
||||
else:
|
||||
metadata_map[shader_name] = metadata
|
||||
for name, gm in metadata_map.items():
|
||||
with open(os.path.join(dest_dir, f'{name}.glsl.json'), 'w') as f:
|
||||
f.write(json.dumps(gm.asdict()))
|
||||
|
||||
|
||||
def write_if_changed(dest: str, text: str) -> None:
|
||||
|
|
@ -661,10 +720,19 @@ def write_if_changed(dest: str, text: str) -> None:
|
|||
f.write(text)
|
||||
|
||||
|
||||
def write_glsl_header(metadata_map: dict[str, GLSLMetadata], dest: str = 'kitty/glsl-uniforms.h') -> None:
|
||||
def write_glsl_header(dest_dir: str, dest: str = 'kitty/glsl-uniforms.h') -> None:
|
||||
metadata_map = {}
|
||||
for x in glob.glob(os.path.join(dest_dir, '*.glsl.json')):
|
||||
shader_name = shader_name_from_path(x)
|
||||
with open(x) as f:
|
||||
d = json.load(f)
|
||||
metadata_map[shader_name] = GLSLMetadata.fromdict(d)
|
||||
|
||||
lines = ['// generated by slang.py DO NOT EDIT', '#include "gl.h"', '']
|
||||
a = lines.append
|
||||
for name in sorted(metadata_map):
|
||||
if '.' in name:
|
||||
continue
|
||||
m = metadata_map[name]
|
||||
struct_name = name.capitalize() + 'Uniforms'
|
||||
a('')
|
||||
|
|
@ -694,11 +762,11 @@ def write_glsl_header(metadata_map: dict[str, GLSLMetadata], dest: str = 'kitty/
|
|||
for u in sorted(m.uniform_structs):
|
||||
a(f' ans->{u}.index = block_index(program, "{m.uniform_struct_names[u]}");')
|
||||
a(f' ans->{u}.size = block_size(program, ans->{u}.index);')
|
||||
block_name = m.uniform_struct_names[u]
|
||||
for v in sorted(m.uniform_structs[u]):
|
||||
vv = m.uniform_structs[u][v]
|
||||
if ']' in vv:
|
||||
a(f' ans->{v} = get_uniform_array_information(program, "{block_name}.{vv}");')
|
||||
vv = vv.partition('[')[0] # ]
|
||||
a(f' ans->{v} = get_uniform_array_information(program, "{vv}");')
|
||||
a('}')
|
||||
write_if_changed(dest, '\n'.join(lines))
|
||||
# }}}
|
||||
|
|
@ -773,12 +841,11 @@ def compile_builtin_shaders(build_dir: str, dest_dir: str, parallel_run: Paralle
|
|||
glsl_commands = commands_to_compile_to_glsl(source_tree, build_dir, dest_dir, built_glsl_files)
|
||||
# Now run all commands
|
||||
parallel_run(chain(spirv_commands, glsl_commands))
|
||||
metadata_map = {}
|
||||
fixup_opengl_files(glob.glob(os.path.join(dest_dir, '*.glsl')), metadata_map=metadata_map)
|
||||
fixup_opengl_files(built_glsl_files)
|
||||
if shutil.which('glslangValidator'):
|
||||
from kitty.shaders.validate_shaders import validation_command_for_file
|
||||
parallel_run((True, f'Validating |{os.path.basename(x)}| ...', validation_command_for_file(x)) for x in built_glsl_files)
|
||||
write_glsl_header(metadata_map)
|
||||
write_glsl_header(dest_dir)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
|
|
|
|||
|
|
@ -582,7 +582,6 @@ void os_window_regions(const OSWindow*, Region *main, Region *tab_bar);
|
|||
bool drag_scroll(Window *, OSWindow*);
|
||||
void draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_buf, bool rect_data_is_dirty, color_type, unsigned int, bool, OSWindow *w);
|
||||
ssize_t create_cell_vao(void);
|
||||
ssize_t create_graphics_vao(void);
|
||||
ssize_t create_border_vao(void);
|
||||
bool send_cell_data_to_gpu(ssize_t, Screen *, OSWindow *);
|
||||
void draw_cells(const WindowRenderData*, OSWindow *, bool, bool, bool, Window*);
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
uniform vec4 tint_color;
|
||||
out vec4 color; // must be in linear space and pre-multiplied
|
||||
|
||||
void main() {
|
||||
color = tint_color;
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
|
||||
uniform vec4 edges;
|
||||
|
||||
void main() {
|
||||
float left = edges[0];
|
||||
float top = edges[1];
|
||||
float right = edges[2];
|
||||
float bottom = edges[3];
|
||||
vec2 pos_map[] = vec2[4](
|
||||
vec2(left, top),
|
||||
vec2(left, bottom),
|
||||
vec2(right, bottom),
|
||||
vec2(right, top)
|
||||
);
|
||||
|
||||
|
||||
gl_Position = vec4(pos_map[gl_VertexID], 0, 1);
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
uniform vec2 cursor_edge_x;
|
||||
uniform vec2 cursor_edge_y;
|
||||
uniform vec3 trail_color;
|
||||
uniform float trail_opacity;
|
||||
|
||||
in vec2 frag_pos;
|
||||
out vec4 final_color;
|
||||
|
||||
void main() {
|
||||
float opacity = trail_opacity;
|
||||
// Dont render if fragment is within cursor area
|
||||
float in_x = step(cursor_edge_x[0], frag_pos.x) * step(frag_pos.x, cursor_edge_x[1]);
|
||||
float in_y = step(cursor_edge_y[1], frag_pos.y) * step(frag_pos.y, cursor_edge_y[0]);
|
||||
opacity *= 1.0f - in_x * in_y;
|
||||
final_color = vec4(trail_color * opacity, opacity);
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
uniform vec4 x_coords;
|
||||
uniform vec4 y_coords;
|
||||
|
||||
out vec2 frag_pos;
|
||||
|
||||
void main() {
|
||||
vec2 pos = vec2(x_coords[gl_VertexID], y_coords[gl_VertexID]);
|
||||
gl_Position = vec4(pos, 1.0, 1.0);
|
||||
frag_pos = pos;
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
// Return 0 if x < 1 otherwise 1
|
||||
#define zero_or_one(x) step(1.f, x)
|
||||
// condition must be zero or one. When 1 thenval is returned otherwise elseval
|
||||
#define if_one_then(condition, thenval, elseval) mix(elseval, thenval, condition)
|
||||
// a < b ? thenval : elseval
|
||||
#define if_less_than(a, b, thenval, elseval) mix(thenval, elseval, step(b, a))
|
||||
|
||||
vec4 vec4_premul(vec3 rgb, float a) {
|
||||
return vec4(rgb * a, a);
|
||||
}
|
||||
|
||||
vec4 vec4_premul(vec4 rgba) {
|
||||
return vec4(rgba.rgb * rgba.a, rgba.a);
|
||||
}
|
||||
|
|
@ -39,11 +39,6 @@ class TestBuild(BaseTest):
|
|||
from kitty.shaders.slang import test_slang_build
|
||||
test_slang_build()
|
||||
|
||||
def test_loading_shaders(self) -> None:
|
||||
from kitty.shaders.legacy import Program
|
||||
for name in 'cell border bgimage tint graphics'.split():
|
||||
Program(name)
|
||||
|
||||
def test_macos_dictation_forwarding(self) -> None:
|
||||
from kitty.constants import glfw_path, is_macos
|
||||
if not is_macos or not shutil.which('clang'):
|
||||
|
|
|
|||
51
setup.py
51
setup.py
|
|
@ -1361,56 +1361,6 @@ def build_cli_parser_specs(skip_generation: bool = False) -> str:
|
|||
return dest
|
||||
|
||||
|
||||
def build_uniforms_header(skip_generation: bool = False) -> str:
|
||||
dest = 'kitty/uniforms_generated.h'
|
||||
if skip_generation:
|
||||
return dest
|
||||
lines: list[str] = []
|
||||
a = lines.append
|
||||
uniform_names: Dict[str, Tuple[str, ...]] = {}
|
||||
class_names = {}
|
||||
function_names = {}
|
||||
|
||||
def find_uniform_names(raw: str) -> Iterator[str]:
|
||||
for m in re.finditer(r'^uniform\s+\S+\s+(.+?);', raw, flags=re.MULTILINE):
|
||||
for x in m.group(1).split(','):
|
||||
yield x.strip().partition('[')[0]
|
||||
|
||||
for x in sorted(glob.glob('kitty/*.glsl')):
|
||||
name = os.path.basename(x).partition('.')[0]
|
||||
name, sep, shader_type = name.rpartition('_')
|
||||
if not sep or shader_type not in ('fragment', 'vertex'):
|
||||
continue
|
||||
class_names[name] = f'{name.capitalize()}Uniforms'
|
||||
function_names[name] = f'get_uniform_locations_{name}'
|
||||
with open(x) as f:
|
||||
raw = f.read()
|
||||
uniform_names[name] = uniform_names.setdefault(name, ()) + tuple(find_uniform_names(raw))
|
||||
for name in sorted(class_names):
|
||||
class_name, function_name, uniforms = class_names[name], function_names[name], uniform_names[name]
|
||||
a(f'typedef struct {class_name} ''{')
|
||||
for n in uniforms:
|
||||
a(f' int {n};')
|
||||
a('}'f' {class_name};')
|
||||
a('')
|
||||
a(f'static inline void\n{function_name}(int program, {class_name} *ans) ''{')
|
||||
for n in uniforms:
|
||||
a(f' ans->{n} = get_uniform_location(program, "{n}");')
|
||||
a('}')
|
||||
a('')
|
||||
# }]]]))
|
||||
src = '\n'.join(lines)
|
||||
try:
|
||||
with open(dest) as f:
|
||||
current = f.read()
|
||||
except FileNotFoundError:
|
||||
current = ''
|
||||
if src != current:
|
||||
with open(dest, 'w') as f:
|
||||
f.write(src)
|
||||
return dest
|
||||
|
||||
|
||||
@lru_cache
|
||||
def wrapped_kittens() -> str:
|
||||
with open('shell-integration/ssh/kitty') as f:
|
||||
|
|
@ -1446,7 +1396,6 @@ def build(args: Options, native_optimizations: bool = True, call_init: bool = Tr
|
|||
sources, headers = find_c_files()
|
||||
headers.append(build_ref_map(args.skip_code_generation))
|
||||
headers.append(build_cli_parser_specs(args.skip_code_generation))
|
||||
headers.append(build_uniforms_header(args.skip_code_generation))
|
||||
compile_c_extension(
|
||||
kitty_env(args), 'kitty/fast_data_types', args.compilation_database, sources, headers,
|
||||
build_dsym=args.build_dsym,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue