diff --git a/kitty/alpha_blend.glsl b/kitty/alpha_blend.glsl deleted file mode 100644 index d611c546f..000000000 --- a/kitty/alpha_blend.glsl +++ /dev/null @@ -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); -} diff --git a/kitty/bgimage_fragment.glsl b/kitty/bgimage_fragment.glsl deleted file mode 100644 index 06fefdfd1..000000000 --- a/kitty/bgimage_fragment.glsl +++ /dev/null @@ -1,11 +0,0 @@ -#pragma kitty_include_shader - -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); -} diff --git a/kitty/bgimage_vertex.glsl b/kitty/bgimage_vertex.glsl deleted file mode 100644 index 2100e86e0..000000000 --- a/kitty/bgimage_vertex.glsl +++ /dev/null @@ -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); -} diff --git a/kitty/blit_common.glsl b/kitty/blit_common.glsl deleted file mode 100644 index 06ab1ffd2..000000000 --- a/kitty/blit_common.glsl +++ /dev/null @@ -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); -} diff --git a/kitty/blit_fragment.glsl b/kitty/blit_fragment.glsl deleted file mode 100644 index 207bdd1d3..000000000 --- a/kitty/blit_fragment.glsl +++ /dev/null @@ -1,13 +0,0 @@ -#pragma kitty_include_shader -#pragma kitty_include_shader -#pragma kitty_include_shader - -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); -} diff --git a/kitty/blit_vertex.glsl b/kitty/blit_vertex.glsl deleted file mode 100644 index 41ec78ff0..000000000 --- a/kitty/blit_vertex.glsl +++ /dev/null @@ -1,2 +0,0 @@ -uniform vec4 src_rect, dest_rect; -#pragma kitty_include_shader diff --git a/kitty/border_fragment.glsl b/kitty/border_fragment.glsl deleted file mode 100644 index 8327584de..000000000 --- a/kitty/border_fragment.glsl +++ /dev/null @@ -1,6 +0,0 @@ -in vec4 color_premul; -out vec4 output_premul; - -void main() { - output_premul = color_premul; -} diff --git a/kitty/border_vertex.glsl b/kitty/border_vertex.glsl deleted file mode 100644 index 6e08f942e..000000000 --- a/kitty/border_vertex.glsl +++ /dev/null @@ -1,62 +0,0 @@ -#pragma kitty_include_shader - -#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); -} diff --git a/kitty/boss.py b/kitty/boss.py index 7cb255a0b..2fb41e9c0 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -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 diff --git a/kitty/cell_defines.glsl b/kitty/cell_defines.glsl deleted file mode 100644 index 2df43379d..000000000 --- a/kitty/cell_defines.glsl +++ /dev/null @@ -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); diff --git a/kitty/cell_fragment.glsl b/kitty/cell_fragment.glsl deleted file mode 100644 index 877da11d0..000000000 --- a/kitty/cell_fragment.glsl +++ /dev/null @@ -1,111 +0,0 @@ -#pragma kitty_include_shader -#pragma kitty_include_shader -#pragma kitty_include_shader -#pragma kitty_include_shader - -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; -} diff --git a/kitty/cell_vertex.glsl b/kitty/cell_vertex.glsl deleted file mode 100644 index cb42d3720..000000000 --- a/kitty/cell_vertex.glsl +++ /dev/null @@ -1,387 +0,0 @@ -#extension GL_ARB_explicit_attrib_location : require -#pragma kitty_include_shader -#pragma kitty_include_shader - - -// 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 -#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 -} diff --git a/kitty/gl.c b/kitty/gl.c index 905813db4..7b04f4c33 100644 --- a/kitty/gl.c +++ b/kitty/gl.c @@ -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 diff --git a/kitty/gl.h b/kitty/gl.h index f643e8092..1b8b08747 100644 --- a/kitty/gl.h +++ b/kitty/gl.h @@ -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); diff --git a/kitty/glsl-uniforms.h b/kitty/glsl-uniforms.h index e34f6e15b..87fd07a50 100644 --- a/kitty/glsl-uniforms.h +++ b/kitty/glsl-uniforms.h @@ -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 { diff --git a/kitty/graphics_fragment.glsl b/kitty/graphics_fragment.glsl deleted file mode 100644 index 1afbd9778..000000000 --- a/kitty/graphics_fragment.glsl +++ /dev/null @@ -1,29 +0,0 @@ -#pragma kitty_include_shader -#pragma kitty_include_shader -#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; -} diff --git a/kitty/graphics_vertex.glsl b/kitty/graphics_vertex.glsl deleted file mode 100644 index 41ec78ff0..000000000 --- a/kitty/graphics_vertex.glsl +++ /dev/null @@ -1,2 +0,0 @@ -uniform vec4 src_rect, dest_rect; -#pragma kitty_include_shader diff --git a/kitty/hsluv.glsl b/kitty/hsluv.glsl deleted file mode 100644 index 14c13e935..000000000 --- a/kitty/hsluv.glsl +++ /dev/null @@ -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)); -} diff --git a/kitty/linear2srgb.glsl b/kitty/linear2srgb.glsl deleted file mode 100644 index 63e2fe5c0..000000000 --- a/kitty/linear2srgb.glsl +++ /dev/null @@ -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)); -} diff --git a/kitty/main.py b/kitty/main.py index 3f038385b..f27cfe632 100644 --- a/kitty/main.py +++ b/kitty/main.py @@ -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) diff --git a/kitty/rounded_rect_fragment.glsl b/kitty/rounded_rect_fragment.glsl deleted file mode 100644 index 7b638d726..000000000 --- a/kitty/rounded_rect_fragment.glsl +++ /dev/null @@ -1,41 +0,0 @@ -#pragma kitty_include_shader -#pragma kitty_include_shader - -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); -} diff --git a/kitty/rounded_rect_vertex.glsl b/kitty/rounded_rect_vertex.glsl deleted file mode 100644 index 990d738c4..000000000 --- a/kitty/rounded_rect_vertex.glsl +++ /dev/null @@ -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); -} diff --git a/kitty/screenshot_fragment.glsl b/kitty/screenshot_fragment.glsl deleted file mode 100644 index 3fad35dc5..000000000 --- a/kitty/screenshot_fragment.glsl +++ /dev/null @@ -1,54 +0,0 @@ -#pragma kitty_include_shader - -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); -} diff --git a/kitty/screenshot_vertex.glsl b/kitty/screenshot_vertex.glsl deleted file mode 100644 index 41ec78ff0..000000000 --- a/kitty/screenshot_vertex.glsl +++ /dev/null @@ -1,2 +0,0 @@ -uniform vec4 src_rect, dest_rect; -#pragma kitty_include_shader diff --git a/kitty/shaders.c b/kitty/shaders.c index 37abf831b..c40eef68c 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -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); diff --git a/kitty/shaders/legacy.py b/kitty/shaders/legacy.py deleted file mode 100644 index cacf204a1..000000000 --- a/kitty/shaders/legacy.py +++ /dev/null @@ -1,242 +0,0 @@ -#!/usr/bin/env python -# License: GPLv3 Copyright: 2023, Kovid Goyal - -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 diff --git a/kitty/shaders/slang.py b/kitty/shaders/slang.py index 4a990c817..415b689da 100644 --- a/kitty/shaders/slang.py +++ b/kitty/shaders/slang.py @@ -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: diff --git a/kitty/state.h b/kitty/state.h index 243bfd67f..c8222b11b 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -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*); diff --git a/kitty/tint_fragment.glsl b/kitty/tint_fragment.glsl deleted file mode 100644 index 209c1e76b..000000000 --- a/kitty/tint_fragment.glsl +++ /dev/null @@ -1,6 +0,0 @@ -uniform vec4 tint_color; -out vec4 color; // must be in linear space and pre-multiplied - -void main() { - color = tint_color; -} diff --git a/kitty/tint_vertex.glsl b/kitty/tint_vertex.glsl deleted file mode 100644 index b8ad88a1d..000000000 --- a/kitty/tint_vertex.glsl +++ /dev/null @@ -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); -} diff --git a/kitty/trail_fragment.glsl b/kitty/trail_fragment.glsl deleted file mode 100644 index f1bb3c3cd..000000000 --- a/kitty/trail_fragment.glsl +++ /dev/null @@ -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); -} diff --git a/kitty/trail_vertex.glsl b/kitty/trail_vertex.glsl deleted file mode 100644 index 035aaecb8..000000000 --- a/kitty/trail_vertex.glsl +++ /dev/null @@ -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; -} diff --git a/kitty/utils.glsl b/kitty/utils.glsl deleted file mode 100644 index e758ffb5c..000000000 --- a/kitty/utils.glsl +++ /dev/null @@ -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); -} diff --git a/kitty_tests/check_build.py b/kitty_tests/check_build.py index 639671f0e..4ebc4be58 100644 --- a/kitty_tests/check_build.py +++ b/kitty_tests/check_build.py @@ -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'): diff --git a/setup.py b/setup.py index da23e1026..386e69973 100755 --- a/setup.py +++ b/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,