From 0fb49f413950ae5cc26ddad21f88e68fdb00b05d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 11 Dec 2024 08:36:03 +0530 Subject: [PATCH] Switch to using an index for sprite tracking Frees up two bytes in GPUCell. Doesn't require a minimum texture row size. Makes a bunch of code faster. Index uses 31 bits which gives us 2,147,483,647 aka ~ 2 billion sprites. --- kitty/cell_vertex.glsl | 26 ++++++++------ kitty/data-types.c | 5 --- kitty/data-types.h | 4 +-- kitty/fast_data_types.pyi | 4 +-- kitty/fonts.c | 74 ++++++++++++++++++++++----------------- kitty/fonts/render.py | 15 ++++---- kitty/glyph-cache.h | 2 +- kitty/line.c | 4 +-- kitty/line.h | 8 ++--- kitty/shaders.c | 14 +++++--- kitty/state.h | 9 ++++- kitty_tests/fonts.py | 3 +- 12 files changed, 97 insertions(+), 71 deletions(-) diff --git a/kitty/cell_vertex.glsl b/kitty/cell_vertex.glsl index 4dd0f8502..2822d211c 100644 --- a/kitty/cell_vertex.glsl +++ b/kitty/cell_vertex.glsl @@ -22,7 +22,7 @@ uniform uint draw_bg_bitfield; // Have to use fixed locations here as all variants of the cell program share the same VAO layout(location=0) in uvec3 colors; -layout(location=1) in uvec4 sprite_coords; +layout(location=1) in uvec2 sprite_idx; layout(location=2) in uint is_selected; uniform float gamma_lut[256]; @@ -58,8 +58,9 @@ out float effective_text_alpha; // Utility functions {{{ const uint BYTE_MASK = uint(0xFF); -const uint Z_MASK = uint(0xFFF); -const uint COLOR_MASK = uint(0x4000); +const uint Z_MASK = uint(0x7fffffff); +const uint COLOR_MASK = uint(0x80000000); +const uint COLOR_SHIFT = uint(31); const uint ZERO = uint(0); const uint ONE = uint(1); const uint TWO = uint(2); @@ -95,7 +96,12 @@ vec3 to_color(uint c, uint defval) { return color_to_vec(resolve_color(c, defval)); } -vec3 to_sprite_pos(uvec2 pos, uint x, uint y, uint z) { +vec3 to_sprite_pos(uvec2 pos, 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; + uint y = num_on_last_page / sprites_xnum; + uint x = num_on_last_page % sprites_xnum; vec2 s_xpos = vec2(x, float(x) + 1.0f) * (1.0f / float(sprites_xnum)); vec2 s_ypos = vec2(y, float(y) + 1.0f) * (1.0f / float(sprites_ynum)); return vec3(s_xpos[pos.x], s_ypos[pos.y], z); @@ -140,8 +146,8 @@ CellData set_vertex_position() { gl_Position = vec4(xpos[pos.x], ypos[pos.y], 0, 1); #ifdef NEEDS_FOREGROUND // The character sprite being rendered - sprite_pos = to_sprite_pos(pos, sprite_coords.x, sprite_coords.y, sprite_coords.z & Z_MASK); - colored_sprite = float((sprite_coords.z & COLOR_MASK) >> 14); + sprite_pos = to_sprite_pos(pos, sprite_idx[0] & Z_MASK); + colored_sprite = float((sprite_idx[0] & COLOR_MASK) >> COLOR_SHIFT); #endif float is_block_cursor = step(float(cursor_fg_sprite_idx), 0.5); float has_cursor = is_cursor(c, r); @@ -172,7 +178,7 @@ void main() { // set cell color indices {{{ uvec2 default_colors = uvec2(default_fg, bg_colors0); - uint text_attrs = sprite_coords[3]; + uint text_attrs = sprite_idx[1]; uint is_reversed = ((text_attrs >> REVERSE_SHIFT) & ONE); uint is_inverted = is_reversed + inverted; int fg_index = fg_index_map[is_inverted]; @@ -202,15 +208,15 @@ void main() { foreground = choose_color(float(is_selected & ONE), selection_color, foreground); decoration_fg = choose_color(float(is_selected & ONE), selection_color, decoration_fg); // Underline and strike through (rendered via sprites) - underline_pos = choose_color(in_url, to_sprite_pos(cell_data.pos, url_style, ZERO, ZERO), to_sprite_pos(cell_data.pos, (text_attrs >> DECORATION_SHIFT) & DECORATION_MASK, ZERO, ZERO)); - strike_pos = to_sprite_pos(cell_data.pos, ((text_attrs >> STRIKE_SHIFT) & ONE) * STRIKE_SPRITE_INDEX, ZERO, ZERO); + underline_pos = choose_color(in_url, to_sprite_pos(cell_data.pos, url_style), to_sprite_pos(cell_data.pos, (text_attrs >> DECORATION_SHIFT) & DECORATION_MASK)); + strike_pos = to_sprite_pos(cell_data.pos, ((text_attrs >> STRIKE_SHIFT) & ONE) * STRIKE_SPRITE_INDEX); // Cursor cursor_color_premult = vec4(color_to_vec(cursor_bg) * cursor_opacity, cursor_opacity); vec3 final_cursor_text_color = mix(foreground, color_to_vec(cursor_fg), cursor_opacity); foreground = choose_color(cell_data.has_block_cursor, final_cursor_text_color, foreground); decoration_fg = choose_color(cell_data.has_block_cursor, final_cursor_text_color, decoration_fg); - cursor_pos = to_sprite_pos(cell_data.pos, cursor_fg_sprite_idx * uint(cell_data.has_cursor), ZERO, ZERO); + cursor_pos = to_sprite_pos(cell_data.pos, cursor_fg_sprite_idx * uint(cell_data.has_cursor)); #endif // }}} diff --git a/kitty/data-types.c b/kitty/data-types.c index c452ecb0c..d4feca297 100644 --- a/kitty/data-types.c +++ b/kitty/data-types.c @@ -710,11 +710,6 @@ shift_to_first_set_bit(CellAttrs x) { EXPORTED PyMODINIT_FUNC PyInit_fast_data_types(void) { PyObject *m; - if (sizeof(CellAttrs) != 2u) { - PyErr_SetString(PyExc_RuntimeError, "Size of CellAttrs is not 2 on this platform"); - return NULL; - } - m = PyModule_Create(&module); if (m == NULL) return NULL; init_monotonic(); diff --git a/kitty/data-types.h b/kitty/data-types.h index 1cedcf799..526c47736 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -73,7 +73,7 @@ typedef uint16_t combining_type; typedef uint16_t glyph_index; typedef uint32_t pixel; typedef unsigned int index_type; -typedef uint16_t sprite_index; +typedef uint32_t sprite_index; typedef enum CursorShapes { NO_CURSOR_SHAPE, CURSOR_BLOCK, CURSOR_BEAM, CURSOR_UNDERLINE, CURSOR_HOLLOW, NUM_OF_CURSOR_SHAPES } CursorShape; typedef enum { DISABLE_LIGATURES_NEVER, DISABLE_LIGATURES_CURSOR, DISABLE_LIGATURES_ALWAYS } DisableLigature; @@ -276,7 +276,7 @@ typedef struct FontCellMetrics { #define FONTS_DATA_HEAD SPRITE_MAP_HANDLE sprite_map; double logical_dpi_x, logical_dpi_y, font_sz_in_pts; FontCellMetrics fcm; typedef struct {FONTS_DATA_HEAD} *FONTS_DATA_HANDLE; -#define clear_sprite_position(cell) (cell).sprite_x = 0; (cell).sprite_y = 0; (cell).sprite_z = 0; +#define clear_sprite_position(cell) (cell).sprite_idx = 0; #define ensure_space_for(base, array, type, num, capacity, initial_cap, zero_mem) \ if ((base)->capacity < num) { \ diff --git a/kitty/fast_data_types.pyi b/kitty/fast_data_types.pyi index fe122e9be..ddd59d1bd 100644 --- a/kitty/fast_data_types.pyi +++ b/kitty/fast_data_types.pyi @@ -1108,8 +1108,7 @@ def parse_input_from_terminal( class Line: - def sprite_at(self, cell: int) -> Tuple[int, int, int]: - pass + def sprite_at(self, cell: int) -> int: ... def test_shape(line: Line, @@ -1719,6 +1718,7 @@ def cocoa_play_system_sound_by_id_async(sound_id: int) -> None: ... def glfw_get_system_color_theme(query_if_unintialized: bool = True) -> Literal['light', 'dark', 'no_preference']: ... def set_redirect_keys_to_overlay(os_window_id: int, tab_id: int, window_id: int, overlay_window_id: int) -> None: ... def buffer_keys_in_window(os_window_id: int, tab_id: int, window_id: int, enabled: bool = True) -> bool: ... +def sprite_idx_to_pos(idx: int, xnum: int, ynum: int) -> tuple[int, int, int]: ... class MousePosition(TypedDict): cell_x: int diff --git a/kitty/fonts.c b/kitty/fonts.c index 2e86c1727..ff90a4620 100644 --- a/kitty/fonts.c +++ b/kitty/fonts.c @@ -30,8 +30,7 @@ typedef enum { typedef struct { - size_t max_y; - unsigned int x, y, z, xnum, ynum; + unsigned x, y, z, xnum, ynum, max_y; } GPUSpriteTracker; typedef struct RunFont { @@ -263,6 +262,10 @@ do_increment(FontGroup *fg, int *error) { } } +static uint32_t +current_sprite_index(GPUSpriteTracker *sprite_tracker) { + return sprite_tracker->z * (sprite_tracker->xnum * sprite_tracker->ynum) + sprite_tracker->y * sprite_tracker->xnum + sprite_tracker->x; +} static SpritePosition* sprite_position_for(FontGroup *fg, RunFont rf, glyph_index *glyphs, unsigned glyph_count, uint8_t ligature_index, unsigned cell_count, int *error) { @@ -274,7 +277,7 @@ sprite_position_for(FontGroup *fg, RunFont rf, glyph_index *glyphs, unsigned gly rf.scale, subscale, rf.multicell_y, rf.vertical_align, &created); if (!s) { *error = 1; return NULL; } if (created) { - s->x = fg->sprite_tracker.x; s->y = fg->sprite_tracker.y; s->z = fg->sprite_tracker.z; + s->idx = current_sprite_index(&fg->sprite_tracker); do_increment(fg, error); } return s; @@ -405,9 +408,12 @@ free_font_groups(void) { } static void -python_send_to_gpu(FONTS_DATA_HANDLE fg, unsigned int x, unsigned int y, unsigned int z, pixel* buf) { +python_send_to_gpu(FONTS_DATA_HANDLE fg_, unsigned int idx, pixel *buf) { + FontGroup *fg = (FontGroup*)fg_; if (python_send_to_gpu_impl) { if (!num_font_groups) fatal("Cannot call send to gpu with no font groups"); + unsigned int x, y, z; + sprite_index_to_pos(idx, fg->sprite_tracker.xnum, fg->sprite_tracker.ynum, &x, &y, &z); PyObject *ret = PyObject_CallFunction(python_send_to_gpu_impl, "IIIN", x, y, z, PyBytes_FromStringAndSize((const char*)buf, sizeof(pixel) * fg->fcm.cell_width * fg->fcm.cell_height)); if (ret == NULL) PyErr_Print(); else Py_DECREF(ret); @@ -670,11 +676,6 @@ START_ALLOW_CASE_RANGE END_ALLOW_CASE_RANGE } -static void -set_sprite(GPUCell *cell, sprite_index x, sprite_index y, sprite_index z) { - cell->sprite_x = x; cell->sprite_y = y; cell->sprite_z = z; -} - // Gives a unique (arbitrary) id to a box glyph static glyph_index box_glyph_id(char_type ch) { @@ -855,6 +856,12 @@ dump_sprite(pixel *b, unsigned width, unsigned height, bool actual_pixel_values) } } +static void +set_cell_sprite(GPUCell *cell, const SpritePosition *sp) { + cell->sprite_idx = sp->idx & 0x7fffffff; + if (sp->colored) cell->sprite_idx |= 0x80000000; +} + static void render_box_cell(FontGroup *fg, RunFont rf, CPUCell *cpu_cell, GPUCell *gpu_cell, const TextCache *tc) { int error = 0; @@ -869,7 +876,7 @@ render_box_cell(FontGroup *fg, RunFont rf, CPUCell *cpu_cell, GPUCell *gpu_cell, else global_glyph_render_scratch.lc->chars[i] = 0; } if (!num_glyphs) { - for (unsigned i = 0; i < rf.scale; i++) set_sprite(gpu_cell + i, 0, 0, 0); + for (unsigned i = 0; i < rf.scale; i++) gpu_cell[i].sprite_idx = 0; return; } bool all_rendered = true; @@ -878,11 +885,11 @@ render_box_cell(FontGroup *fg, RunFont rf, CPUCell *cpu_cell, GPUCell *gpu_cell, sp[ligature_index] = sprite_position_for(fg, rf, global_glyph_render_scratch.glyphs, num_glyphs, ligature_index, rf.scale, &error); if (sp[ligature_index] == NULL) { sprite_map_set_error(error); PyErr_Print(); - for (unsigned i = 0; i < rf.scale; i++) set_sprite(gpu_cell + i, 0, 0, 0); + for (unsigned i = 0; i < rf.scale; i++) gpu_cell[i].sprite_idx = 0; return; } - set_sprite(gpu_cell + ligature_index, sp[ligature_index]->x, sp[ligature_index]->y, sp[ligature_index]->z); sp[ligature_index]->colored = false; + set_cell_sprite(gpu_cell + ligature_index, sp[ligature_index]); if (!sp[ligature_index]->rendered) all_rendered = false; } if (all_rendered) return; @@ -915,7 +922,7 @@ render_box_cell(FontGroup *fg, RunFont rf, CPUCell *cpu_cell, GPUCell *gpu_cell, /*printf("Rendered char sz: (%u, %u)\n", width, height); dump_sprite(fg->canvas.buf, width, height, false);*/ if (scale == 1.f && rf.scale == 1 && !rf.subscale_n) { sp[0]->rendered = true; - current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, sp[0]->x, sp[0]->y, sp[0]->z, fg->canvas.buf); + current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, sp[0]->idx, fg->canvas.buf); } else { calculate_regions_for_line(rf, fg->fcm.cell_height, &src, &dest); /*printf("width: %u height: %u unscaled_cell_width: %u unscaled_cell_height: %u src.top: %u src.bottom: %u rf.scale: %u\n", width, height, fg->fcm.cell_width, fg->fcm.cell_height, src.top, src.bottom, rf.scale);*/ @@ -923,7 +930,7 @@ render_box_cell(FontGroup *fg, RunFont rf, CPUCell *cpu_cell, GPUCell *gpu_cell, pixel *b = extract_cell_region(&fg->canvas, i, &src, &dest, width, fg->fcm); /*printf("Sprite %u: pos: (%u, %u, %u) sz: (%u, %u)\n", i, sp[i]->x, sp[i]->y, sp[i]->z, fg->fcm.cell_width, fg->fcm.cell_height); dump_sprite(b, fg->fcm.cell_width, fg->fcm.cell_height, false);*/ sp[i]->rendered = true; - current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, sp[i]->x, sp[i]->y, sp[i]->z, b); + current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, sp[i]->idx, b); } } #undef sp @@ -948,12 +955,6 @@ load_hb_buffer(CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_ } -static void -set_cell_sprite(GPUCell *cell, const SpritePosition *sp) { - cell->sprite_x = sp->x; cell->sprite_y = sp->y; cell->sprite_z = sp->z; - if (sp->colored) cell->sprite_z |= 0x4000; -} - static void render_filled_sprite(pixel *buf, unsigned num_glyphs, FontCellMetrics scaled_metrics, unsigned num_scaled_cells) { if (num_scaled_cells > num_glyphs) { @@ -1014,10 +1015,10 @@ render_group( if (num_cells == num_scaled_cells && rf.scale == 1.f) { for (unsigned i = 0; i < num_cells; i++) { if (!sp[i]->rendered) { - bool is_repeat_sprite = is_infinite_ligature && i > 0 && sp[i]->x == sp[i-1]->x && sp[i]->y == sp[i-1]->y && sp[i]->z == sp[i-1]->z; + bool is_repeat_sprite = is_infinite_ligature && i > 0 && sp[i]->idx == sp[i-1]->idx; if (!is_repeat_sprite) { pixel *b = num_cells == 1 ? fg->canvas.buf : extract_cell_from_canvas(fg, i, num_cells); - current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, sp[i]->x, sp[i]->y, sp[i]->z, b); + current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, sp[i]->idx, b); } sp[i]->rendered = true; sp[i]->colored = was_colored; } @@ -1031,7 +1032,7 @@ render_group( if (!sp[i]->rendered) { pixel *b = extract_cell_region(&fg->canvas, i, &src, &dest, scaled_metrics.cell_width * num_scaled_cells, unscaled_metrics); /*printf("cell %u src -> dest: (%u %u) -> (%u %u)\n", i, src.left, src.right, dest.left, dest.right);*/ - current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, sp[i]->x, sp[i]->y, sp[i]->z, b); + current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, sp[i]->idx, b); /*dump_sprite(b, unscaled_metrics.cell_width, unscaled_metrics.cell_height, false);*/ sp[i]->rendered = true; sp[i]->colored = was_colored; } @@ -1583,7 +1584,7 @@ render_run(FontGroup *fg, CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, inde render_groups(fg, rf, center_glyph, tc); break; case BLANK_FONT: - while(num_cells--) { set_sprite(first_gpu_cell, 0, 0, 0); first_cpu_cell++; first_gpu_cell++; } + while(num_cells--) { first_gpu_cell->sprite_idx = 0; first_cpu_cell++; first_gpu_cell++; } break; case BOX_FONT: while(num_cells) { @@ -1594,7 +1595,7 @@ render_run(FontGroup *fg, CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, inde } break; case MISSING_FONT: - while(num_cells--) { set_sprite(first_gpu_cell, MISSING_GLYPH, 0, 0); first_cpu_cell++; first_gpu_cell++; } + while(num_cells--) { first_gpu_cell->sprite_idx = MISSING_GLYPH; first_cpu_cell++; first_gpu_cell++; } break; } } @@ -1759,26 +1760,24 @@ set_font_data(PyObject UNUSED *m, PyObject *args) { static void send_prerendered_sprites(FontGroup *fg) { int error = 0; - sprite_index x = 0, y = 0, z = 0; // blank cell ensure_canvas_can_fit(fg, 1, 1); - current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, x, y, z, fg->canvas.buf); - do_increment(fg, &error); + current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, 0, fg->canvas.buf); if (error != 0) { sprite_map_set_error(error); PyErr_Print(); fatal("Failed"); } PyObject *args = PyObject_CallFunction(prerender_function, "IIIIIIIffdd", fg->fcm.cell_width, fg->fcm.cell_height, fg->fcm.baseline, fg->fcm.underline_position, fg->fcm.underline_thickness, fg->fcm.strikethrough_position, fg->fcm.strikethrough_thickness, OPT(cursor_beam_thickness), OPT(cursor_underline_thickness), fg->logical_dpi_x, fg->logical_dpi_y); if (args == NULL) { PyErr_Print(); fatal("Failed to pre-render cells"); } PyObject *cell_addresses = PyTuple_GET_ITEM(args, 0); for (ssize_t i = 0; i < PyTuple_GET_SIZE(cell_addresses); i++) { - x = fg->sprite_tracker.x; y = fg->sprite_tracker.y; z = fg->sprite_tracker.z; - if (y > 0) { fatal("Too many pre-rendered sprites for your GPU or the font size is too large"); } do_increment(fg, &error); if (error != 0) { sprite_map_set_error(error); PyErr_Print(); fatal("Failed"); } uint8_t *alpha_mask = PyLong_AsVoidPtr(PyTuple_GET_ITEM(cell_addresses, i)); ensure_canvas_can_fit(fg, 1, 1); // clear canvas Region r = { .right = fg->fcm.cell_width, .bottom = fg->fcm.cell_height }; render_alpha_mask(alpha_mask, fg->canvas.buf, &r, &r, fg->fcm.cell_width, fg->fcm.cell_width, 0xffffff); - current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, x, y, z, fg->canvas.buf); + current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, current_sprite_index(&fg->sprite_tracker), fg->canvas.buf); } + do_increment(fg, &error); + if (error != 0) { sprite_map_set_error(error); PyErr_Print(); fatal("Failed"); } Py_CLEAR(args); } @@ -1893,7 +1892,9 @@ test_sprite_position_for(PyObject UNUSED *self, PyObject *args) { RunFont rf = {.scale = 1, .font_idx=fg->medium_font_idx}; SpritePosition *pos = sprite_position_for(fg, rf, glyphs, PyTuple_GET_SIZE(args), 0, 1, &error); if (pos == NULL) { sprite_map_set_error(error); return NULL; } - return Py_BuildValue("HHH", pos->x, pos->y, pos->z); + unsigned int x, y, z; + sprite_index_to_pos(pos->idx, fg->sprite_tracker.xnum, fg->sprite_tracker.ynum, &x, &y, &z); + return Py_BuildValue("III", x, y, z); } static PyObject* @@ -2125,8 +2126,17 @@ set_allow_use_of_box_fonts(PyObject *self UNUSED, PyObject *val) { Py_RETURN_NONE; } +static PyObject* +sprite_idx_to_pos(PyObject *self UNUSED, PyObject *args) { + unsigned x, y, z, idx, xnum, ynum; + if (!PyArg_ParseTuple(args, "III", &idx, &xnum, &ynum)) return NULL; + sprite_index_to_pos(idx, xnum, ynum, &x, &y, &z); + return Py_BuildValue("III", x, y, z); +} + static PyMethodDef module_methods[] = { METHODB(set_font_data, METH_VARARGS), + METHODB(sprite_idx_to_pos, METH_VARARGS), METHODB(free_font_data, METH_NOARGS), METHODB(create_test_font_group, METH_VARARGS), METHODB(sprite_map_set_layout, METH_VARARGS), diff --git a/kitty/fonts/render.py b/kitty/fonts/render.py index a3c282083..a53ed7dbf 100644 --- a/kitty/fonts/render.py +++ b/kitty/fonts/render.py @@ -21,6 +21,7 @@ from kitty.fast_data_types import ( set_font_data, set_options, set_send_sprite_to_gpu, + sprite_idx_to_pos, sprite_map_set_limits, test_render_line, test_shape, @@ -437,6 +438,9 @@ def render_box_drawing(codepoint: int, cell_width: int, cell_height: int, dpi: f class setup_for_testing: + xnum = 100000 + ynum = 100 + def __init__(self, family: str = 'monospace', size: float = 11.0, dpi: float = 96.0, main_face_path: str = ''): self.family, self.size, self.dpi = family, size, dpi self.main_face_path = main_face_path @@ -450,7 +454,7 @@ class setup_for_testing: def send_to_gpu(x: int, y: int, z: int, data: bytes) -> None: sprites[(x, y, z)] = data - sprite_map_set_limits(100000, 100) + sprite_map_set_limits(self.xnum, self.ynum) set_send_sprite_to_gpu(send_to_gpu) self.orig_desc_overrides = descriptor_overrides descriptor_overrides = {} @@ -479,13 +483,12 @@ def render_string(text: str, family: str = 'monospace', size: float = 11.0, dpi: cells = [] found_content = False for i in reversed(range(s.columns)): - sp = list(line.sprite_at(i)) - sp[2] &= 0xfff - tsp = sp[0], sp[1], sp[2] - if tsp == (0, 0, 0) and not found_content: + sp = line.sprite_at(i) + sp &= 0x7fffffff + if not sp and not found_content: continue found_content = True - cells.append(sprites[tsp]) + cells.append(sprites[sprite_idx_to_pos(sp, setup_for_testing.xnum, setup_for_testing.ynum)]) return cell_width, cell_height, list(reversed(cells)) diff --git a/kitty/glyph-cache.h b/kitty/glyph-cache.h index a8c4e461b..4a54dc559 100644 --- a/kitty/glyph-cache.h +++ b/kitty/glyph-cache.h @@ -12,7 +12,7 @@ void free_glyph_cache_global_resources(void); typedef struct SpritePosition { bool rendered, colored; - sprite_index x, y, z; + sprite_index idx; } SpritePosition; typedef struct {int x;} *SPRITE_POSITION_MAP_HANDLE; diff --git a/kitty/line.c b/kitty/line.c index e5be99128..a6916b5a6 100644 --- a/kitty/line.c +++ b/kitty/line.c @@ -408,7 +408,7 @@ sprite_at(Line* self, PyObject *x) { unsigned long xval = PyLong_AsUnsignedLong(x); if (xval >= self->xnum) { PyErr_SetString(PyExc_IndexError, "Column number out of bounds"); return NULL; } GPUCell *c = self->gpu_cells + xval; - return Py_BuildValue("HHH", c->sprite_x, c->sprite_y, c->sprite_z); + return Py_BuildValue("I", (unsigned int)c->sprite_idx); } static void @@ -682,7 +682,7 @@ line_apply_cursor(Line *self, const Cursor *cursor, unsigned int at, unsigned in } else { for (index_type i = at; i < self->xnum && i < at + num; i++) { gc.attrs.mark = self->gpu_cells[i].attrs.mark; - gc.sprite_x = self->gpu_cells[i].sprite_x; gc.sprite_y = self->gpu_cells[i].sprite_y; gc.sprite_z = self->gpu_cells[i].sprite_z; + gc.sprite_idx = self->gpu_cells[i].sprite_idx; memcpy(self->gpu_cells + i, &gc, sizeof(gc)); } } diff --git a/kitty/line.h b/kitty/line.h index 60c135e56..da5e3f211 100644 --- a/kitty/line.h +++ b/kitty/line.h @@ -27,11 +27,11 @@ typedef union CellAttrs { uint16_t strike : 1; uint16_t dim : 1; uint16_t mark : 2; - uint16_t : 6; + uint32_t : 22; }; - uint16_t val; + uint32_t val; } CellAttrs; -static_assert(sizeof(CellAttrs) == sizeof(uint16_t), "Fix the ordering of CellAttrs"); +static_assert(sizeof(CellAttrs) == sizeof(uint32_t), "Fix the ordering of CellAttrs"); #define WIDTH_MASK (3u) #define DECORATION_MASK (7u) @@ -44,7 +44,7 @@ static_assert(sizeof(CellAttrs) == sizeof(uint16_t), "Fix the ordering of CellAt typedef struct { color_type fg, bg, decoration_fg; - sprite_index sprite_x, sprite_y, sprite_z; + sprite_index sprite_idx; CellAttrs attrs; } GPUCell; static_assert(sizeof(GPUCell) == 20, "Fix the ordering of GPUCell"); diff --git a/kitty/shaders.c b/kitty/shaders.c index 8c3616166..bb4da3a40 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -143,13 +143,17 @@ ensure_sprite_map(FONTS_DATA_HANDLE fg) { } void -send_sprite_to_gpu(FONTS_DATA_HANDLE fg, unsigned int x, unsigned int y, unsigned int z, pixel *buf) { +send_sprite_to_gpu(FONTS_DATA_HANDLE fg, unsigned int idx, pixel *buf) { SpriteMap *sprite_map = (SpriteMap*)fg->sprite_map; - unsigned int xnum, ynum, znum; + unsigned int xnum, ynum, znum, x, y, z; sprite_tracker_current_layout(fg, &xnum, &ynum, &znum); - if ((int)znum >= sprite_map->last_num_of_layers || (znum == 0 && (int)ynum > sprite_map->last_ynum)) realloc_sprite_texture(fg); + if ((int)znum >= sprite_map->last_num_of_layers || (znum == 0 && (int)ynum > sprite_map->last_ynum)) { + realloc_sprite_texture(fg); + sprite_tracker_current_layout(fg, &xnum, &ynum, &znum); + } glBindTexture(GL_TEXTURE_2D_ARRAY, sprite_map->texture_id); glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + sprite_index_to_pos(idx, xnum, ynum, &x, &y, &z); x *= fg->fcm.cell_width; y *= fg->fcm.cell_height; glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, x, y, z, fg->fcm.cell_width, fg->fcm.cell_height, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, buf); } @@ -228,7 +232,7 @@ init_cell_program(void) { // Sanity check to ensure the attribute location binding worked #define C(p, name, expected) { int aloc = attrib_location(p, #name); if (aloc != expected && aloc != -1) fatal("The attribute location for %s is %d != %d in program: %d", #name, aloc, expected, p); } for (int p = CELL_PROGRAM; p < BORDERS_PROGRAM; p++) { - C(p, colors, 0); C(p, sprite_coords, 1); C(p, is_selected, 2); + C(p, colors, 0); C(p, sprite_idx, 1); C(p, is_selected, 2); } #undef C for (int i = GRAPHICS_PROGRAM; i <= GRAPHICS_ALPHA_MASK_PROGRAM; i++) { @@ -249,7 +253,7 @@ create_cell_vao(void) { #define A1(name, size, dtype, offset) A(name, size, dtype, (void*)(offsetof(GPUCell, offset)), sizeof(GPUCell)) add_buffer_to_vao(vao_idx, GL_ARRAY_BUFFER); - A1(sprite_coords, 4, GL_UNSIGNED_SHORT, sprite_x); + A1(sprite_idx, 2, GL_UNSIGNED_INT, sprite_idx); A1(colors, 3, GL_UNSIGNED_INT, fg); add_buffer_to_vao(vao_idx, GL_ARRAY_BUFFER); diff --git a/kitty/state.h b/kitty/state.h index 1ea6311e4..19696c774 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -338,6 +338,13 @@ extern GlobalState global_state; else Py_DECREF(cret_); \ } +static inline void +sprite_index_to_pos(unsigned idx, unsigned xnum, unsigned ynum, unsigned *x, unsigned *y, unsigned *z) { + div_t r = div(idx & 0x7fffffff, ynum * xnum), r2 = div(r.rem, xnum); + *z = r.quot; *y = r2.quot; *x = r2.rem; +} + + void gl_init(void); void remove_vao(ssize_t vao_idx); bool remove_os_window(id_type os_window_id); @@ -379,7 +386,7 @@ void update_surface_size(int, int, uint32_t); void free_texture(uint32_t*); void free_framebuffer(uint32_t*); void send_image_to_gpu(uint32_t*, const void*, int32_t, int32_t, bool, bool, bool, RepeatStrategy); -void send_sprite_to_gpu(FONTS_DATA_HANDLE fg, unsigned int, unsigned int, unsigned int, pixel*); +void send_sprite_to_gpu(FONTS_DATA_HANDLE fg, unsigned int, pixel*); void blank_canvas(float, color_type); void blank_os_window(OSWindow *); void set_os_window_chrome(OSWindow *w); diff --git a/kitty_tests/fonts.py b/kitty_tests/fonts.py index 602c30758..933f55c08 100644 --- a/kitty_tests/fonts.py +++ b/kitty_tests/fonts.py @@ -14,6 +14,7 @@ from kitty.fast_data_types import ( ParsedFontFeature, get_fallback_font, set_allow_use_of_box_fonts, + sprite_idx_to_pos, sprite_map_set_layout, sprite_map_set_limits, test_render_line, @@ -231,7 +232,7 @@ def block_helpers(s, sprites, cell_width, cell_height): line = s.line(y) test_render_line(line) for x in range(width * scale): - ans.append(sprites[line.sprite_at(x)]) + ans.append(sprites[sprite_idx_to_pos(line.sprite_at(x), setup_for_testing.xnum, setup_for_testing.ynum)]) return ans def block_test(*expected, **kw):