From 97746a26f5fa2f0cf39dc471c975aa13b5845aba Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 13 Jun 2023 18:08:39 +0530 Subject: [PATCH] Move loading of shader programs into shaders module --- kitty/boss.py | 3 +- kitty/main.py | 2 +- kitty/shaders.py | 121 +++++++++++++++++++++++++++++++++++++++++++++-- kitty/window.py | 111 ------------------------------------------- 4 files changed, 121 insertions(+), 116 deletions(-) diff --git a/kitty/boss.py b/kitty/boss.py index 790482785..29fb24be8 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -123,6 +123,7 @@ from .options.utils import MINIMUM_FONT_SIZE, KeyMap, SubSequenceMap from .os_window_size import initial_window_size_func from .rgb import color_from_int from .session import Session, create_sessions, get_os_window_sizing_data +from .shaders import load_shader_programs from .tabs import SpecialWindow, SpecialWindowInstance, Tab, TabDict, TabManager from .types import _T, AsyncResponse, SingleInstanceData, WindowSystemMouseEvent, ac from .typing import PopenType, TypedDict @@ -148,7 +149,7 @@ from .utils import ( startup_notification_handler, which, ) -from .window import CommandOutput, CwdRequest, Window, load_shader_programs +from .window import CommandOutput, CwdRequest, Window if TYPE_CHECKING: from .rc.base import ResponseType diff --git a/kitty/main.py b/kitty/main.py index 8c27c0fff..49c50848c 100644 --- a/kitty/main.py +++ b/kitty/main.py @@ -50,6 +50,7 @@ from .options.types import Options from .options.utils import DELETE_ENV_VAR from .os_window_size import initial_window_size_func from .session import create_sessions, get_os_window_sizing_data +from .shaders import load_shader_programs from .types import SingleInstanceData from .utils import ( cleanup_ssh_control_masters, @@ -61,7 +62,6 @@ from .utils import ( startup_notification_handler, unix_socket_paths, ) -from .window import load_shader_programs def set_custom_ibeam_cursor() -> None: diff --git a/kitty/shaders.py b/kitty/shaders.py index f5eab8df5..5c78f5307 100644 --- a/kitty/shaders.py +++ b/kitty/shaders.py @@ -2,11 +2,34 @@ # License: GPLv3 Copyright: 2023, Kovid Goyal import re -from functools import lru_cache -from typing import Callable, Iterator, Optional +from functools import lru_cache, partial +from typing import Any, Callable, Iterator, Optional from .constants import read_kitty_resource -from .fast_data_types import GLSL_VERSION, compile_program +from .fast_data_types import ( + BGIMAGE_PROGRAM, + BLIT_PROGRAM, + CELL_BG_PROGRAM, + CELL_FG_PROGRAM, + CELL_PROGRAM, + CELL_SPECIAL_PROGRAM, + DECORATION, + DECORATION_MASK, + DIM, + GLSL_VERSION, + GRAPHICS_ALPHA_MASK_PROGRAM, + GRAPHICS_PREMULT_PROGRAM, + GRAPHICS_PROGRAM, + MARK, + MARK_MASK, + NUM_UNDERLINE_STYLES, + REVERSE, + STRIKETHROUGH, + TINT_PROGRAM, + compile_program, + get_options, + init_cell_program, +) def identity(x: str) -> str: @@ -55,3 +78,95 @@ class Program: @lru_cache(maxsize=64) def program_for(name: str) -> Program: return Program(name) + + +def multi_replace(src: str, **replacements: Any) -> str: + r = {k: str(v) for k, v in replacements.items()} + + def sub(m: 're.Match[str]') -> str: + return r.get(m.group(1), m.group(1)) + + return re.sub(r'\{([A-Z_]+)\}', sub, src) + + +class LoadShaderPrograms: + + text_fg_override_threshold: float = 0 + text_old_gamma: bool = False + semi_transparent: bool = False + + @property + def needs_recompile(self) -> bool: + opts = get_options() + return opts.text_fg_override_threshold != self.text_fg_override_threshold or (opts.text_composition_strategy == 'legacy') != self.text_old_gamma + + def recompile_if_needed(self) -> None: + if self.needs_recompile: + self(self.semi_transparent, allow_recompile=True) + + def __call__(self, semi_transparent: bool = False, allow_recompile: bool = False) -> None: + self.semi_transparent = semi_transparent + opts = get_options() + self.text_old_gamma = opts.text_composition_strategy == 'legacy' + self.text_fg_override_threshold = max(0, min(opts.text_fg_override_threshold, 100)) * 0.01 + program_for('blit').compile(BLIT_PROGRAM, allow_recompile) + cell = program_for('cell') + + def resolve_cell_vertex_defines(which: str, v: str) -> str: + v = multi_replace( + v, + WHICH_PROGRAM=which, + REVERSE_SHIFT=REVERSE, + STRIKE_SHIFT=STRIKETHROUGH, + DIM_SHIFT=DIM, + DECORATION_SHIFT=DECORATION, + MARK_SHIFT=MARK, + MARK_MASK=MARK_MASK, + DECORATION_MASK=DECORATION_MASK, + STRIKE_SPRITE_INDEX=NUM_UNDERLINE_STYLES + 1, + ) + if semi_transparent: + v = v.replace('#define NOT_TRANSPARENT', '#define TRANSPARENT') + return v + + def resolve_cell_fragment_defines(which: str, f: str) -> str: + f = f.replace('{WHICH_PROGRAM}', which) + if self.text_fg_override_threshold != 0.: + f = f.replace('#define NO_FG_OVERRIDE', f'#define FG_OVERRIDE {self.text_fg_override_threshold}') + if self.text_old_gamma: + f = f.replace('#define TEXT_NEW_GAMMA', '#define TEXT_OLD_GAMMA') + if semi_transparent: + f = f.replace('#define NOT_TRANSPARENT', '#define TRANSPARENT') + return f + + for which, p in { + 'SIMPLE': CELL_PROGRAM, + 'BACKGROUND': CELL_BG_PROGRAM, + 'SPECIAL': CELL_SPECIAL_PROGRAM, + 'FOREGROUND': CELL_FG_PROGRAM, + }.items(): + cell.apply_to_sources( + vertex=partial(resolve_cell_vertex_defines, which), + frag=partial(resolve_cell_fragment_defines, which), + ) + cell.compile(p, allow_recompile) + + graphics = program_for('graphics') + + def resolve_graphics_fragment_defines(which: str, f: str) -> str: + return f.replace('ALPHA_TYPE', which) + + for which, p in { + 'SIMPLE': GRAPHICS_PROGRAM, + 'PREMULT': GRAPHICS_PREMULT_PROGRAM, + 'ALPHA_MASK': GRAPHICS_ALPHA_MASK_PROGRAM, + }.items(): + graphics.apply_to_sources(frag=partial(resolve_cell_fragment_defines, which)) + graphics.compile(p, allow_recompile) + + program_for('bgimage').compile(BGIMAGE_PROGRAM, allow_recompile) + program_for('tint').compile(TINT_PROGRAM) + init_cell_program() + + +load_shader_programs = LoadShaderPrograms() diff --git a/kitty/window.py b/kitty/window.py index 6601fd48a..985c59429 100644 --- a/kitty/window.py +++ b/kitty/window.py @@ -42,34 +42,16 @@ from .constants import ( wakeup_io_loop, ) from .fast_data_types import ( - BGIMAGE_PROGRAM, - BLIT_PROGRAM, - CELL_BG_PROGRAM, - CELL_FG_PROGRAM, - CELL_PROGRAM, - CELL_SPECIAL_PROGRAM, CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE, DCS, - DECORATION, - DECORATION_MASK, - DIM, GLFW_MOD_CONTROL, - GRAPHICS_ALPHA_MASK_PROGRAM, - GRAPHICS_PREMULT_PROGRAM, - GRAPHICS_PROGRAM, - MARK, - MARK_MASK, NO_CURSOR_SHAPE, - NUM_UNDERLINE_STYLES, OSC, - REVERSE, SCROLL_FULL, SCROLL_LINE, SCROLL_PAGE, - STRIKETHROUGH, - TINT_PROGRAM, Color, KeyEvent, Screen, @@ -83,7 +65,6 @@ from .fast_data_types import ( get_boss, get_click_interval, get_options, - init_cell_program, last_focused_os_window_id, mark_os_window_dirty, mouse_selection, @@ -106,7 +87,6 @@ from .notify import ( ) from .options.types import Options from .rgb import to_color -from .shaders import program_for from .terminfo import get_capabilities from .types import MouseEvent, OverlayType, WindowGeometry, ac, run_once from .typing import BossType, ChildType, EdgeLiteral, TabType, TypedDict @@ -362,97 +342,6 @@ def as_text( return ans -def multi_replace(src: str, **replacements: Any) -> str: - r = {k: str(v) for k, v in replacements.items()} - - def sub(m: 're.Match[str]') -> str: - return r.get(m.group(1), m.group(1)) - - return re.sub(r'\{([A-Z_]+)\}', sub, src) - - -class LoadShaderPrograms: - - text_fg_override_threshold: float = 0 - text_old_gamma: bool = False - semi_transparent: bool = False - - @property - def needs_recompile(self) -> bool: - opts = get_options() - return opts.text_fg_override_threshold != self.text_fg_override_threshold or (opts.text_composition_strategy == 'legacy') != self.text_old_gamma - - def recompile_if_needed(self) -> None: - if self.needs_recompile: - self(self.semi_transparent, allow_recompile=True) - - def __call__(self, semi_transparent: bool = False, allow_recompile: bool = False) -> None: - self.semi_transparent = semi_transparent - opts = get_options() - self.text_old_gamma = opts.text_composition_strategy == 'legacy' - self.text_fg_override_threshold = max(0, min(opts.text_fg_override_threshold, 100)) * 0.01 - program_for('blit').compile(BLIT_PROGRAM, allow_recompile) - cell = program_for('cell') - - def resolve_cell_vertex_defines(which: str, v: str) -> str: - v = multi_replace( - v, - WHICH_PROGRAM=which, - REVERSE_SHIFT=REVERSE, - STRIKE_SHIFT=STRIKETHROUGH, - DIM_SHIFT=DIM, - DECORATION_SHIFT=DECORATION, - MARK_SHIFT=MARK, - MARK_MASK=MARK_MASK, - DECORATION_MASK=DECORATION_MASK, - STRIKE_SPRITE_INDEX=NUM_UNDERLINE_STYLES + 1, - ) - if semi_transparent: - v = v.replace('#define NOT_TRANSPARENT', '#define TRANSPARENT') - return v - - def resolve_cell_fragment_defines(which: str, f: str) -> str: - f = f.replace('{WHICH_PROGRAM}', which) - if self.text_fg_override_threshold != 0.: - f = f.replace('#define NO_FG_OVERRIDE', f'#define FG_OVERRIDE {self.text_fg_override_threshold}') - if self.text_old_gamma: - f = f.replace('#define TEXT_NEW_GAMMA', '#define TEXT_OLD_GAMMA') - if semi_transparent: - f = f.replace('#define NOT_TRANSPARENT', '#define TRANSPARENT') - return f - - for which, p in { - 'SIMPLE': CELL_PROGRAM, - 'BACKGROUND': CELL_BG_PROGRAM, - 'SPECIAL': CELL_SPECIAL_PROGRAM, - 'FOREGROUND': CELL_FG_PROGRAM, - }.items(): - cell.apply_to_sources( - vertex=partial(resolve_cell_vertex_defines, which), - frag=partial(resolve_cell_fragment_defines, which), - ) - cell.compile(p, allow_recompile) - - graphics = program_for('graphics') - - def resolve_graphics_fragment_defines(which: str, f: str) -> str: - return f.replace('ALPHA_TYPE', which) - - for which, p in { - 'SIMPLE': GRAPHICS_PROGRAM, - 'PREMULT': GRAPHICS_PREMULT_PROGRAM, - 'ALPHA_MASK': GRAPHICS_ALPHA_MASK_PROGRAM, - }.items(): - graphics.apply_to_sources(frag=partial(resolve_cell_fragment_defines, which)) - graphics.compile(p, allow_recompile) - - program_for('bgimage').compile(BGIMAGE_PROGRAM, allow_recompile) - program_for('tint').compile(TINT_PROGRAM) - init_cell_program() - - -load_shader_programs = LoadShaderPrograms() - def setup_colors(screen: Screen, opts: Options) -> None: screen.color_profile.update_ansi_color_table(build_ansi_color_table(opts))