Centralize the cell shader defines

This commit is contained in:
Kovid Goyal
2023-06-23 16:18:17 +05:30
parent ed08cbac05
commit 1798c52f77
4 changed files with 80 additions and 79 deletions

35
kitty/cell_defines.glsl Normal file
View File

@@ -0,0 +1,35 @@
#define PHASE_BOTH 1
#define PHASE_BACKGROUND 2
#define PHASE_SPECIAL 3
#define PHASE_FOREGROUND 4
#define PHASE {WHICH_PHASE}
#define HAS_TRANSPARENCY {TRANSPARENT}
#define FG_OVERRIDE {FG_OVERRIDE}
#define FG_OVERRIDE_THRESHOLD {FG_OVERRIDE_THRESHOLD}
#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 MARK_SHIFT {MARK_SHIFT}
#define MARK_MASK {MARK_MASK}
#define USE_SELECTION_FG
#define NUM_COLORS 256
#if (PHASE == PHASE_BOTH) || (PHASE == PHASE_BACKGROUND) || (PHASE == PHASE_SPECIAL)
#define NEEDS_BACKROUND
#endif
#if (PHASE == PHASE_BOTH) || (PHASE == PHASE_FOREGROUND)
#define NEEDS_FOREGROUND
#endif
#if (HAS_TRANSPARENCY == 1)
#define TRANSPARENT
#endif
#if defined(TRANSPARENT) || (PHASE == PHASE_SPECIAL)
#define NEEDS_BG_ALPHA
#endif

View File

@@ -1,23 +1,11 @@
#pragma kitty_include_shader <alpha_blend.glsl>
#pragma kitty_include_shader <linear2srgb.glsl>
#define {WHICH_PROGRAM}
#define NOT_TRANSPARENT
#define NO_FG_OVERRIDE
#define TEXT_NEW_GAMMA
#if defined(SIMPLE) || defined(BACKGROUND) || defined(SPECIAL)
#define NEEDS_BACKROUND
#endif
#if defined(SIMPLE) || defined(FOREGROUND)
#define NEEDS_FOREGROUND
#endif
#pragma kitty_include_shader <cell_defines.glsl>
#ifdef NEEDS_BACKROUND
in vec3 background;
in float draw_bg;
#if defined(TRANSPARENT) || defined(SPECIAL)
#ifdef NEEDS_BG_ALPHA
in float bg_alpha;
#endif
#endif
@@ -53,8 +41,10 @@ vec4 vec4_premul(vec4 rgba) {
/*
* Explanation of rendering:
* There are a couple of cases, in order of increasing complexity:
* 1) Simple -- this path is used when there are either no images, or all images are
* There are two types of rendering, single pass and multi-pass. Multi-pass rendering is used when there
* are images that are below the foreground. Single pass rendering has PHASE=PHASE_BOTH. Otherwise, there
* are three passes, PHASE=PHASE_BACKGROUND, PHASE=PHASE_SPECIAL, PHASE=PHASE_FOREGROUND.
* 1) Single pass -- this path is used when there are either no images, or all images are
* drawn on top of text and the background is opaque. In this case, there is a single pass,
* of this shader with cell foreground and background colors blended directly.
* Expected output is a color premultiplied by alpha, with an alpha specified as well.
@@ -77,10 +67,9 @@ vec4 vec4_premul(vec4 rgba) {
* 2b) Transparent bg with images
* Same as (2a) except blending is done with PREMULT_BLEND and TRANSPARENT is defined in the shaders. background_opacity
* is applied to default colored background cells in step (1).
*
* In this shader exactly *one* of SIMPLE, SPECIAL, FOREGROUND or BACKGROUND will be defined, corresponding
* to the appropriate rendering pass from above.
*/
// foreground functions {{{
#ifdef NEEDS_FOREGROUND
// sRGB luminance values
const vec3 Y = vec3(0.2126, 0.7152, 0.0722);
@@ -92,12 +81,12 @@ float clamp_to_unit_float(float x) {
return clamp(x, 0.0f, 1.0f);
}
#ifdef FG_OVERRIDE
#if (FG_OVERRIDE == 1)
vec3 fg_override(float under_luminance, float over_lumininace, 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);
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));
}
@@ -107,7 +96,7 @@ vec4 foreground_contrast(vec4 over, vec3 under) {
float under_luminance = dot(under, Y);
float over_lumininace = dot(over.rgb, Y);
#ifdef FG_OVERRIDE
#if (FG_OVERRIDE == 1)
over.rgb = fg_override(under_luminance, over_lumininace, over.rgb);
#endif
@@ -117,12 +106,12 @@ vec4 foreground_contrast(vec4 over, vec3 under) {
return over;
}
#ifdef TEXT_OLD_GAMMA
#if (TEXT_NEW_GAMMA == 0)
vec4 foreground_contrast_incorrect(vec4 over, vec3 under) {
// Simulation of gamma-incorrect blending
float under_luminance = dot(under, Y);
float over_lumininace = dot(over.rgb, Y);
#ifdef FG_OVERRIDE
#if (FG_OVERRIDE == 1)
over.rgb = fg_override(under_luminance, over_lumininace, over.rgb);
#endif
// This is the original gamma-incorrect rendering, it is the solution of the following equation:
@@ -156,7 +145,7 @@ vec4 calculate_premul_foreground_from_sprites(vec4 text_fg) {
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
#ifdef TEXT_OLD_GAMMA
#if (TEXT_NEW_GAMMA == 0)
return foreground_contrast_incorrect(text_fg, bg);
#else
return foreground_contrast(text_fg, bg);
@@ -164,6 +153,7 @@ vec4 adjust_foreground_contrast_with_background(vec4 text_fg, vec3 bg) {
}
#endif
// end foreground functions }}}
float adjust_alpha_for_incorrect_blending_by_compositor(float text_fg_alpha, float final_alpha) {
// Adjust the transparent alpha-channel to account for incorrect
@@ -179,7 +169,7 @@ float adjust_alpha_for_incorrect_blending_by_compositor(float text_fg_alpha, flo
}
void main() {
#ifdef SIMPLE
#if (PHASE == PHASE_BOTH)
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);
@@ -191,7 +181,7 @@ void main() {
#endif
#endif
#ifdef SPECIAL
#if (PHASE == PHASE_SPECIAL)
#ifdef TRANSPARENT
final_color = vec4_premul(background, bg_alpha);
#else
@@ -199,7 +189,7 @@ void main() {
#endif
#endif
#ifdef BACKGROUND
#if (PHASE == PHASE_BACKGROUND)
#ifdef TRANSPARENT
final_color = vec4_premul(background, bg_alpha);
#else
@@ -207,7 +197,7 @@ void main() {
#endif
#endif
#ifdef FOREGROUND
#if (PHASE == PHASE_FOREGROUND)
vec4 text_fg = load_text_foreground_color();
vec4 text_fg_premul = calculate_premul_foreground_from_sprites(text_fg);
final_color = text_fg_premul;

View File

@@ -1,16 +1,7 @@
#extension GL_ARB_explicit_attrib_location : require
#pragma kitty_include_shader <srgb_gamma.glsl>
#pragma kitty_include_shader <cell_defines.glsl>
#define {WHICH_PROGRAM}
#define NOT_TRANSPARENT
#define DECORATION_SHIFT {DECORATION_SHIFT}
#define REVERSE_SHIFT {REVERSE_SHIFT}
#define STRIKE_SHIFT {STRIKE_SHIFT}
#define DIM_SHIFT {DIM_SHIFT}
#define MARK_SHIFT {MARK_SHIFT}
#define MARK_MASK {MARK_MASK}
#define USE_SELECTION_FG
#define NUM_COLORS 256
// Inputs {{{
layout(std140) uniform CellRenderData {
@@ -23,7 +14,7 @@ layout(std140) uniform CellRenderData {
uint color_table[NUM_COLORS + MARK_MASK + MARK_MASK + 2];
};
#ifdef BACKGROUND
#if (PHASE == PHASE_BACKGROUND)
uniform uint draw_bg_bitfield;
#endif
@@ -43,18 +34,10 @@ const uvec2 cell_pos_map[] = uvec2[4](
// }}}
#if defined(SIMPLE) || defined(BACKGROUND) || defined(SPECIAL)
#define NEEDS_BACKROUND
#endif
#if defined(SIMPLE) || defined(FOREGROUND)
#define NEEDS_FOREGROUND
#endif
#ifdef NEEDS_BACKROUND
out vec3 background;
out float draw_bg;
#if defined(TRANSPARENT) || defined(SPECIAL)
#ifdef NEEDS_BG_ALPHA
out float bg_alpha;
#endif
#endif
@@ -217,7 +200,7 @@ void main() {
float cell_has_non_default_bg = step(1, float(abs(bg_as_uint - default_colors[1])));
draw_bg = 1;
#if defined(BACKGROUND)
#if (PHASE == PHASE_BACKGROUND)
background = bg;
// draw_bg_bitfield has bit 0 set to draw default bg cells and bit 1 set to draw non-default bg cells
uint draw_bg_mask = uint(2 * cell_has_non_default_bg + (1 - cell_has_non_default_bg));
@@ -230,21 +213,21 @@ void main() {
// On other cells it should be 1. For the SPECIAL program it should be 1 on cells with
// selections/block cursor and 0 everywhere else.
float is_special_cell = cell_has_block_cursor + float(is_selected & ONE);
#ifndef SPECIAL
#if (PHASE != PHASE_SPECIAL)
is_special_cell += cell_has_non_default_bg + float(is_reversed);
#endif
bg_alpha = step(0.5, is_special_cell);
#ifndef SPECIAL
#if (PHASE != PHASE_SPECIAL)
bg_alpha = bg_alpha + (1.0f - bg_alpha) * background_opacity;
bg_alpha *= draw_bg;
#endif
#endif
#if defined(SPECIAL) || defined(SIMPLE)
#if (PHASE == PHASE_SPECIAL) || (PHASE == PHASE_BOTH)
// Selection and cursor
bg = choose_color(float(is_selected & ONE), choose_color(use_cell_for_selection_bg, color_to_vec(fg_as_uint), color_to_vec(highlight_bg)), bg);
background = choose_color(cell_has_block_cursor, color_to_vec(cursor_bg), bg);
#if !defined(TRANSPARENT) && defined(SPECIAL)
#if !defined(TRANSPARENT) && (PHASE == PHASE_SPECIAL)
float is_special_cell = cell_has_block_cursor + float(is_selected & ONE);
bg_alpha = step(0.5, is_special_cell);
#endif

View File

@@ -4,7 +4,7 @@
import re
from functools import lru_cache, partial
from itertools import count
from typing import Any, Callable, Dict, Iterator, Optional
from typing import Any, Callable, Dict, Iterator, Optional, Set
from .constants import read_kitty_resource
from .fast_data_types import (
@@ -53,16 +53,17 @@ class Program:
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))
self.original_fragment_sources = tuple(self._load_sources(self.fragment_name))
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, level: int = 0) -> Iterator[str]:
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 self.filename_map:
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
@@ -74,7 +75,7 @@ class Program:
yield f'\n#line {lnum} {fnum}\n{prefix}'
lnum += prefix.count('\n')
iname = m.group(1)
yield from self._load_sources(iname, level+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:]}'
@@ -161,32 +162,24 @@ class LoadShaderPrograms:
STRIKE_SPRITE_INDEX=NUM_UNDERLINE_STYLES + 1,
)
def resolve_cell_vertex_defines(which: str, v: str) -> str:
self.cell_program_replacer.replacements['WHICH_PROGRAM'] = which
v = self.cell_program_replacer(v)
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
def resolve_cell_defines(which: str, src: str) -> str:
r = self.cell_program_replacer.replacements
r['WHICH_PHASE'] = f'PHASE_{which}'
r['TRANSPARENT'] = '1' if semi_transparent else '0'
r['FG_OVERRIDE_THRESHOLD'] = str(self.text_fg_override_threshold)
r['FG_OVERRIDE'] = '1' if self.text_fg_override_threshold != 0. else '0'
r['TEXT_NEW_GAMMA'] = '0' if self.text_old_gamma else '1'
return self.cell_program_replacer(src)
for which, p in {
'SIMPLE': CELL_PROGRAM,
'BOTH': 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),
vertex=partial(resolve_cell_defines, which),
frag=partial(resolve_cell_defines, which),
)
cell.compile(p, allow_recompile)