From 8dea5b3e3ec1db8597f3a3649b5cefd52f41e409 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 27 May 2018 21:25:09 +0530 Subject: [PATCH] Reduce data sent to GPU per draw by 30% Split up the Cell structure into a CPUCell and a GPUCell. Only the GPUCell part needs to be sent to the GPU. Should make kitty use even less system resources and make a performance difference on systems where the GPU bandwidth is constrained. Also allows adding more CPU only data in the future without affecting GPU bandwidth. For example, hyperlinks or more combining characters. --- kitty/core_text.m | 2 +- kitty/cursor.c | 4 +- kitty/data-types.h | 28 +++++--- kitty/fontconfig.c | 2 +- kitty/fonts.c | 167 ++++++++++++++++++++++++--------------------- kitty/fonts.h | 2 +- kitty/history.c | 29 +++++--- kitty/line-buf.c | 55 +++++++++------ kitty/line.c | 114 ++++++++++++++++--------------- kitty/lineops.h | 19 +++--- kitty/rewrap.h | 5 +- kitty/screen.c | 37 +++++----- kitty/shaders.c | 4 +- 13 files changed, 256 insertions(+), 212 deletions(-) diff --git a/kitty/core_text.m b/kitty/core_text.m index 2412d539a..9f931eb85 100644 --- a/kitty/core_text.m +++ b/kitty/core_text.m @@ -178,7 +178,7 @@ find_substitute_face(CFStringRef str, CTFontRef old_font) { } PyObject* -create_fallback_face(PyObject *base_face, Cell* cell, bool UNUSED bold, bool UNUSED italic, bool emoji_presentation, FONTS_DATA_HANDLE fg UNUSED) { +create_fallback_face(PyObject *base_face, CPUCell* cell, bool UNUSED bold, bool UNUSED italic, bool emoji_presentation, FONTS_DATA_HANDLE fg UNUSED) { CTFace *self = (CTFace*)base_face; CTFontRef new_font; if (emoji_presentation) new_font = CTFontCreateWithName((CFStringRef)@"AppleColorEmoji", self->scaled_point_sz, NULL); diff --git a/kitty/cursor.c b/kitty/cursor.c index 15848a5b6..759ffd473 100644 --- a/kitty/cursor.c +++ b/kitty/cursor.c @@ -136,7 +136,7 @@ END_ALLOW_CASE_RANGE } void -apply_sgr_to_cells(Cell *first_cell, unsigned int cell_count, unsigned int *params, unsigned int count) { +apply_sgr_to_cells(GPUCell *first_cell, unsigned int cell_count, unsigned int *params, unsigned int count) { #define RANGE for(unsigned c = 0; c < cell_count; c++, cell++) #define SET(shift) RANGE { cell->attrs |= (1 << shift); } break; #define RESET(shift) RANGE { cell->attrs &= ~(1 << shift); } break; @@ -148,7 +148,7 @@ apply_sgr_to_cells(Cell *first_cell, unsigned int cell_count, unsigned int *para unsigned int i = 0, attr; if (!count) { params[0] = 0; count = 1; } while (i < count) { - Cell *cell = first_cell; + GPUCell *cell = first_cell; attr = params[i++]; switch(attr) { case 0: diff --git a/kitty/data-types.h b/kitty/data-types.h index 07bfda4d8..236bcef24 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -80,7 +80,7 @@ typedef enum MouseShapes { BEAM, HAND, ARROW } MouseShape; (c)->reverse = (a >> REVERSE_SHIFT) & 1; (c)->strikethrough = (a >> STRIKE_SHIFT) & 1; (c)->dim = (a >> DIM_SHIFT) & 1; #define COPY_CELL(src, s, dest, d) \ - (dest)->cells[d] = (src)->cells[s]; + (dest)->cpu_cells[d] = (src)->cpu_cells[s]; (dest)->gpu_cells[d] = (src)->gpu_cells[s]; #define COPY_SELF_CELL(s, d) COPY_CELL(self, s, self, d) @@ -140,15 +140,19 @@ typedef struct { color_type fg, bg, decoration_fg; sprite_index sprite_x, sprite_y, sprite_z; attrs_type attrs; - // The following are only needed on the CPU, not the GPU +} GPUCell; + +typedef struct { char_type ch; combining_type cc_idx[2]; -} Cell; +} CPUCell; + typedef struct { PyObject_HEAD - Cell *cells; + GPUCell *gpu_cells; + CPUCell *cpu_cells; index_type xnum, ynum; bool continued, needs_free, has_dirty_text; } Line; @@ -157,14 +161,16 @@ typedef struct { typedef struct { PyObject_HEAD - Cell *buf; + GPUCell *gpu_cell_buf; + CPUCell *cpu_cell_buf; index_type xnum, ynum, *line_map, *scratch; line_attrs_type *line_attrs; Line *line; } LineBuf; typedef struct { - Cell *cells; + GPUCell *gpu_cells; + CPUCell *cpu_cells; line_attrs_type *line_attrs; } HistoryBufSegment; @@ -226,10 +232,10 @@ typedef struct {FONTS_DATA_HEAD} *FONTS_DATA_HANDLE; for(index_type __i__ = (at); __i__ < (line)->xnum - (num); __i__++) { \ COPY_CELL(line, __i__ + (num), line, __i__) \ } \ - if ((((line)->cells[(at)].attrs) & WIDTH_MASK) != 1) { \ - (line)->cells[(at)].ch = BLANK_CHAR; \ - (line)->cells[(at)].attrs = BLANK_CHAR ? 1 : 0; \ - clear_sprite_position((line)->cells[(at)]); \ + if ((((line)->gpu_cells[(at)].attrs) & WIDTH_MASK) != 1) { \ + (line)->cpu_cells[(at)].ch = BLANK_CHAR; \ + (line)->gpu_cells[(at)].attrs = BLANK_CHAR ? 1 : 0; \ + clear_sprite_position((line)->gpu_cells[(at)]); \ }\ } @@ -260,7 +266,7 @@ Cursor* cursor_copy(Cursor*); void cursor_copy_to(Cursor *src, Cursor *dest); void cursor_reset_display_attrs(Cursor*); void cursor_from_sgr(Cursor *self, unsigned int *params, unsigned int count); -void apply_sgr_to_cells(Cell *first_cell, unsigned int cell_count, unsigned int *params, unsigned int count); +void apply_sgr_to_cells(GPUCell *first_cell, unsigned int cell_count, unsigned int *params, unsigned int count); const char* cursor_as_sgr(Cursor*, Cursor*); double monotonic(); diff --git a/kitty/fontconfig.c b/kitty/fontconfig.c index d45bf4ae9..9123de462 100644 --- a/kitty/fontconfig.c +++ b/kitty/fontconfig.c @@ -194,7 +194,7 @@ end: } PyObject* -create_fallback_face(PyObject UNUSED *base_face, Cell* cell, bool bold, bool italic, bool emoji_presentation, FONTS_DATA_HANDLE fg) { +create_fallback_face(PyObject UNUSED *base_face, CPUCell* cell, bool bold, bool italic, bool emoji_presentation, FONTS_DATA_HANDLE fg) { PyObject *ans = NULL; FcPattern *pat = FcPatternCreate(); if (pat == NULL) return PyErr_NoMemory(); diff --git a/kitty/fonts.c b/kitty/fonts.c index 0338fbba7..e3fcd1eb9 100644 --- a/kitty/fonts.c +++ b/kitty/fonts.c @@ -420,12 +420,12 @@ face_has_codepoint(PyObject* face, char_type cp) { } static inline bool -has_emoji_presentation(Cell *cell) { - return (cell->attrs & WIDTH_MASK) == 2 && is_emoji(cell->ch) && cell->cc_idx[0] != VS15; +has_emoji_presentation(CPUCell *cpu_cell, GPUCell *gpu_cell) { + return (gpu_cell->attrs & WIDTH_MASK) == 2 && is_emoji(cpu_cell->ch) && cpu_cell->cc_idx[0] != VS15; } static inline bool -has_cell_text(Font *self, Cell *cell) { +has_cell_text(Font *self, CPUCell *cell) { if (!face_has_codepoint(self->face, cell->ch)) return false; for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) { combining_type cc_idx = cell->cc_idx[i]; @@ -436,7 +436,7 @@ has_cell_text(Font *self, Cell *cell) { } static inline void -output_cell_fallback_data(Cell *cell, bool bold, bool italic, bool emoji_presentation, PyObject *face, bool new_face) { +output_cell_fallback_data(CPUCell *cell, bool bold, bool italic, bool emoji_presentation, PyObject *face, bool new_face) { printf("U+%x ", cell->ch); for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) { printf("U+%x ", codepoint_for_mark(cell->cc_idx[i])); @@ -450,7 +450,7 @@ output_cell_fallback_data(Cell *cell, bool bold, bool italic, bool emoji_present } static inline ssize_t -load_fallback_font(FontGroup *fg, Cell *cell, bool bold, bool italic, bool emoji_presentation) { +load_fallback_font(FontGroup *fg, CPUCell *cell, bool bold, bool italic, bool emoji_presentation) { if (fg->fallback_fonts_count > 100) { log_error("Too many fallback fonts"); return MISSING_FONT; } ssize_t f; @@ -475,21 +475,21 @@ load_fallback_font(FontGroup *fg, Cell *cell, bool bold, bool italic, bool emoji } static inline ssize_t -fallback_font(FontGroup *fg, Cell *cell) { - bool bold = (cell->attrs >> BOLD_SHIFT) & 1; - bool italic = (cell->attrs >> ITALIC_SHIFT) & 1; - bool emoji_presentation = has_emoji_presentation(cell); +fallback_font(FontGroup *fg, CPUCell *cpu_cell, GPUCell *gpu_cell) { + bool bold = (gpu_cell->attrs >> BOLD_SHIFT) & 1; + bool italic = (gpu_cell->attrs >> ITALIC_SHIFT) & 1; + bool emoji_presentation = has_emoji_presentation(cpu_cell, gpu_cell); // Check if one of the existing fallback fonts has this text for (size_t i = 0, j = fg->first_fallback_font_idx; i < fg->fallback_fonts_count; i++, j++) { Font *ff = fg->fonts +j; - if (ff->bold == bold && ff->italic == italic && ff->emoji_presentation == emoji_presentation && has_cell_text(ff, cell)) { - if (global_state.debug_font_fallback) output_cell_fallback_data(cell, bold, italic, emoji_presentation, ff->face, false); + if (ff->bold == bold && ff->italic == italic && ff->emoji_presentation == emoji_presentation && has_cell_text(ff, cpu_cell)) { + if (global_state.debug_font_fallback) output_cell_fallback_data(cpu_cell, bold, italic, emoji_presentation, ff->face, false); return j; } } - return load_fallback_font(fg, cell, bold, italic, emoji_presentation); + return load_fallback_font(fg, cpu_cell, bold, italic, emoji_presentation); } static inline ssize_t @@ -502,10 +502,10 @@ in_symbol_maps(FontGroup *fg, char_type ch) { static ssize_t -font_for_cell(FontGroup *fg, Cell *cell) { +font_for_cell(FontGroup *fg, CPUCell *cpu_cell, GPUCell *gpu_cell) { START_ALLOW_CASE_RANGE ssize_t ans; - switch(cell->ch) { + switch(cpu_cell->ch) { case 0: case ' ': return BLANK_FONT; @@ -517,9 +517,9 @@ START_ALLOW_CASE_RANGE case 0xe0b6: return BOX_FONT; default: - ans = in_symbol_maps(fg, cell->ch); + ans = in_symbol_maps(fg, cpu_cell->ch); if (ans > -1) return ans; - switch(BI_VAL(cell->attrs)) { + switch(BI_VAL(gpu_cell->attrs)) { case 0: ans = fg->medium_font_idx; break; case 1: @@ -530,14 +530,14 @@ START_ALLOW_CASE_RANGE ans = fg->bi_font_idx; break; } if (ans < 0) ans = fg->medium_font_idx; - if (!has_emoji_presentation(cell) && has_cell_text(fg->fonts + ans, cell)) return ans; - return fallback_font(fg, cell); + if (!has_emoji_presentation(cpu_cell, gpu_cell) && has_cell_text(fg->fonts + ans, cpu_cell)) return ans; + return fallback_font(fg, cpu_cell, gpu_cell); } END_ALLOW_CASE_RANGE } static inline void -set_sprite(Cell *cell, sprite_index x, sprite_index y, sprite_index z) { +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; } @@ -577,21 +577,21 @@ render_alpha_mask(uint8_t *alpha_mask, pixel* dest, Region *src_rect, Region *de } static void -render_box_cell(FontGroup *fg, Cell *cell) { +render_box_cell(FontGroup *fg, CPUCell *cpu_cell, GPUCell *gpu_cell) { int error = 0; - glyph_index glyph = box_glyph_id(cell->ch); + glyph_index glyph = box_glyph_id(cpu_cell->ch); static ExtraGlyphs extra_glyphs = {{0}}; SpritePosition *sp = sprite_position_for(fg, &fg->fonts[BOX_FONT], glyph, &extra_glyphs, false, &error); if (sp == NULL) { sprite_map_set_error(error); PyErr_Print(); - set_sprite(cell, 0, 0, 0); + set_sprite(gpu_cell, 0, 0, 0); return; } - set_sprite(cell, sp->x, sp->y, sp->z); + set_sprite(gpu_cell, sp->x, sp->y, sp->z); if (sp->rendered) return; sp->rendered = true; sp->colored = false; - PyObject *ret = PyObject_CallFunction(box_drawing_function, "IIId", cell->ch, fg->cell_width, fg->cell_height, (fg->logical_dpi_x + fg->logical_dpi_y) / 2.0); + PyObject *ret = PyObject_CallFunction(box_drawing_function, "IIId", cpu_cell->ch, fg->cell_width, fg->cell_height, (fg->logical_dpi_x + fg->logical_dpi_y) / 2.0); if (ret == NULL) { PyErr_Print(); return; } uint8_t *alpha_mask = PyLong_AsVoidPtr(PyTuple_GET_ITEM(ret, 0)); clear_canvas(fg); @@ -602,17 +602,17 @@ render_box_cell(FontGroup *fg, Cell *cell) { } static inline void -load_hb_buffer(Cell *first_cell, index_type num_cells) { +load_hb_buffer(CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells) { index_type num; hb_buffer_clear_contents(harfbuzz_buffer); while (num_cells) { attrs_type prev_width = 0; - for (num = 0; num_cells && num < arraysz(shape_buffer) - 20 - arraysz(first_cell->cc_idx); first_cell++, num_cells--) { + for (num = 0; num_cells && num < arraysz(shape_buffer) - 20 - arraysz(first_cpu_cell->cc_idx); first_cpu_cell++, first_gpu_cell++, num_cells--) { if (prev_width == 2) { prev_width = 0; continue; } - shape_buffer[num++] = first_cell->ch; - prev_width = first_cell->attrs & WIDTH_MASK; - for (unsigned i = 0; i < arraysz(first_cell->cc_idx) && first_cell->cc_idx[i]; i++) { - shape_buffer[num++] = codepoint_for_mark(first_cell->cc_idx[i]); + shape_buffer[num++] = first_cpu_cell->ch; + prev_width = first_gpu_cell->attrs & WIDTH_MASK; + for (unsigned i = 0; i < arraysz(first_cpu_cell->cc_idx) && first_cpu_cell->cc_idx[i]; i++) { + shape_buffer[num++] = codepoint_for_mark(first_cpu_cell->cc_idx[i]); } } hb_buffer_add_utf32(harfbuzz_buffer, shape_buffer, num, 0, num); @@ -622,7 +622,7 @@ load_hb_buffer(Cell *first_cell, index_type num_cells) { static inline void -set_cell_sprite(Cell *cell, SpritePosition *sp) { +set_cell_sprite(GPUCell *cell, SpritePosition *sp) { cell->sprite_x = sp->x; cell->sprite_y = sp->y; cell->sprite_z = sp->z; if (sp->colored) cell->sprite_z |= 0x4000; } @@ -636,7 +636,7 @@ extract_cell_from_canvas(FontGroup *fg, unsigned int i, unsigned int num_cells) } static inline void -render_group(FontGroup *fg, unsigned int num_cells, unsigned int num_glyphs, Cell *cells, hb_glyph_info_t *info, hb_glyph_position_t *positions, Font *font, glyph_index glyph, ExtraGlyphs *extra_glyphs) { +render_group(FontGroup *fg, unsigned int num_cells, unsigned int num_glyphs, CPUCell *cpu_cells, GPUCell *gpu_cells, hb_glyph_info_t *info, hb_glyph_position_t *positions, Font *font, glyph_index glyph, ExtraGlyphs *extra_glyphs) { static SpritePosition* sprite_position[16]; int error = 0; num_cells = MIN(sizeof(sprite_position)/sizeof(sprite_position[0]), num_cells); @@ -645,19 +645,19 @@ render_group(FontGroup *fg, unsigned int num_cells, unsigned int num_glyphs, Cel if (error != 0) { sprite_map_set_error(error); PyErr_Print(); return; } } if (sprite_position[0]->rendered) { - for (unsigned int i = 0; i < num_cells; i++) { set_cell_sprite(cells + i, sprite_position[i]); } + for (unsigned int i = 0; i < num_cells; i++) { set_cell_sprite(gpu_cells + i, sprite_position[i]); } return; } clear_canvas(fg); - bool was_colored = (cells->attrs & WIDTH_MASK) == 2 && is_emoji(cells->ch); + bool was_colored = (gpu_cells->attrs & WIDTH_MASK) == 2 && is_emoji(cpu_cells->ch); render_glyphs_in_cells(font->face, font->bold, font->italic, info, positions, num_glyphs, fg->canvas, fg->cell_width, fg->cell_height, num_cells, fg->baseline, &was_colored, (FONTS_DATA_HANDLE)fg); if (PyErr_Occurred()) PyErr_Print(); for (unsigned int i = 0; i < num_cells; i++) { sprite_position[i]->rendered = true; sprite_position[i]->colored = was_colored; - set_cell_sprite(cells + i, sprite_position[i]); + set_cell_sprite(gpu_cells + i, sprite_position[i]); pixel *buf = num_cells == 1 ? fg->canvas : extract_cell_from_canvas(fg, i, num_cells); current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, sprite_position[i]->x, sprite_position[i]->y, sprite_position[i]->z, buf); } @@ -665,7 +665,8 @@ render_group(FontGroup *fg, unsigned int num_cells, unsigned int num_glyphs, Cel } typedef struct { - Cell *cell; + CPUCell *cpu_cell; + GPUCell *gpu_cell; unsigned int num_codepoints; unsigned int codepoints_consumed; char_type current_codepoint; @@ -681,7 +682,8 @@ typedef struct { CellData current_cell_data; Group *groups; size_t groups_capacity, group_idx, glyph_idx, cell_idx, num_cells, num_glyphs; - Cell *first_cell, *last_cell; + CPUCell *first_cpu_cell, *last_cpu_cell; + GPUCell *first_gpu_cell, *last_gpu_cell; hb_glyph_info_t *info; hb_glyph_position_t *positions; } GroupState; @@ -689,14 +691,14 @@ typedef struct { static GroupState group_state = {0}; static inline unsigned int -num_codepoints_in_cell(Cell *cell) { +num_codepoints_in_cell(CPUCell *cell) { unsigned int ans = 1; for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) ans++; return ans; } static inline void -shape(Cell *first_cell, index_type num_cells, hb_font_t *font) { +shape(CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells, hb_font_t *font) { if (group_state.groups_capacity <= 2 * num_cells) { group_state.groups_capacity = MAX(128, 2 * num_cells); // avoid unnecessary reallocs group_state.groups = realloc(group_state.groups, sizeof(Group) * group_state.groups_capacity); @@ -705,15 +707,21 @@ shape(Cell *first_cell, index_type num_cells, hb_font_t *font) { group_state.previous_cluster = UINT32_MAX; group_state.prev_was_special = false; group_state.prev_was_empty = false; - group_state.current_cell_data.cell = first_cell; group_state.current_cell_data.num_codepoints = num_codepoints_in_cell(first_cell); group_state.current_cell_data.codepoints_consumed = 0; group_state.current_cell_data.current_codepoint = first_cell->ch; + group_state.current_cell_data.cpu_cell = first_cpu_cell; + group_state.current_cell_data.gpu_cell = first_gpu_cell; + group_state.current_cell_data.num_codepoints = num_codepoints_in_cell(first_cpu_cell); + group_state.current_cell_data.codepoints_consumed = 0; + group_state.current_cell_data.current_codepoint = first_cpu_cell->ch; memset(group_state.groups, 0, sizeof(Group) * group_state.groups_capacity); group_state.group_idx = 0; group_state.glyph_idx = 0; group_state.cell_idx = 0; group_state.num_cells = num_cells; - group_state.first_cell = first_cell; - group_state.last_cell = first_cell + (num_cells ? num_cells - 1 : 0); - load_hb_buffer(first_cell, num_cells); + group_state.first_cpu_cell = first_cpu_cell; + group_state.first_gpu_cell = first_gpu_cell; + group_state.last_cpu_cell = first_cpu_cell + (num_cells ? num_cells - 1 : 0); + group_state.last_gpu_cell = first_gpu_cell + (num_cells ? num_cells - 1 : 0); + load_hb_buffer(first_cpu_cell, first_gpu_cell, num_cells); hb_shape(font, harfbuzz_buffer, NULL, 0); unsigned int info_length, positions_length; group_state.info = hb_buffer_get_glyph_infos(harfbuzz_buffer, &info_length); @@ -752,24 +760,25 @@ is_empty_glyph(glyph_index glyph_id, Font *font) { } static inline unsigned int -check_cell_consumed(CellData *cell_data, Cell *last_cell) { +check_cell_consumed(CellData *cell_data, CPUCell *last_cpu_cell) { cell_data->codepoints_consumed++; if (cell_data->codepoints_consumed >= cell_data->num_codepoints) { - attrs_type width = cell_data->cell->attrs & WIDTH_MASK; - cell_data->cell += MAX(1, width); + attrs_type width = cell_data->gpu_cell->attrs & WIDTH_MASK; + cell_data->cpu_cell += MAX(1, width); + cell_data->gpu_cell += MAX(1, width); cell_data->codepoints_consumed = 0; - if (cell_data->cell <= last_cell) { - cell_data->num_codepoints = num_codepoints_in_cell(cell_data->cell); - cell_data->current_codepoint = cell_data->cell->ch; + if (cell_data->cpu_cell <= last_cpu_cell) { + cell_data->num_codepoints = num_codepoints_in_cell(cell_data->cpu_cell); + cell_data->current_codepoint = cell_data->cpu_cell->ch; } else cell_data->current_codepoint = 0; return width; } else { switch(cell_data->codepoints_consumed) { case 0: - cell_data->current_codepoint = cell_data->cell->ch; + cell_data->current_codepoint = cell_data->cpu_cell->ch; break; default: - cell_data->current_codepoint = codepoint_for_mark(cell_data->cell->cc_idx[cell_data->codepoints_consumed - 1]); + cell_data->current_codepoint = codepoint_for_mark(cell_data->cpu_cell->cc_idx[cell_data->codepoints_consumed - 1]); break; } } @@ -778,8 +787,8 @@ check_cell_consumed(CellData *cell_data, Cell *last_cell) { static inline void -shape_run(Cell *first_cell, index_type num_cells, Font *font) { - shape(first_cell, num_cells, harfbuzz_font_for_face(font->face)); +shape_run(CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells, Font *font) { + shape(first_cpu_cell, first_gpu_cell, num_cells, harfbuzz_font_for_face(font->face)); #if 0 // You can also generate this easily using hb-shape --show-extents --cluster-level=1 --shapers=ot /path/to/font/file text hb_buffer_serialize_glyphs(harfbuzz_buffer, 0, group_state.num_glyphs, (char*)canvas, sizeof(pixel) * CELLS_IN_CANVAS * cell_width * cell_height, NULL, harfbuzz_font_for_face(font->face), HB_BUFFER_SERIALIZE_FORMAT_TEXT, HB_BUFFER_SERIALIZE_FLAG_DEFAULT | HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS); @@ -853,7 +862,7 @@ shape_run(Cell *first_cell, index_type num_cells, Font *font) { } else { unsigned int num_cells_consumed = 0, start_cell_idx = G(cell_idx); while (num_codepoints_used_by_glyph && G(cell_idx) < G(num_cells)) { - unsigned int w = check_cell_consumed(&G(current_cell_data), G(last_cell)); + unsigned int w = check_cell_consumed(&G(current_cell_data), G(last_cpu_cell)); G(cell_idx) += w; num_cells_consumed += w; num_codepoints_used_by_glyph--; @@ -915,7 +924,7 @@ render_groups(FontGroup *fg, Font *font) { int last = -1; for (i = 1; i < MIN(arraysz(ed.data) + 1, group->num_glyphs); i++) { last = i - 1; ed.data[last] = G(info)[group->first_glyph_idx + i].codepoint; } if ((size_t)(last + 1) < arraysz(ed.data)) ed.data[last + 1] = 0; - render_group(fg, group->num_cells, group->num_glyphs, G(first_cell) + group->first_cell_idx, G(info) + group->first_glyph_idx, G(positions) + group->first_glyph_idx, font, primary, &ed); + render_group(fg, group->num_cells, group->num_glyphs, G(first_cpu_cell) + group->first_cell_idx, G(first_gpu_cell) + group->first_cell_idx, G(info) + group->first_glyph_idx, G(positions) + group->first_glyph_idx, font, primary, &ed); idx++; } } @@ -927,7 +936,7 @@ test_shape(PyObject UNUSED *self, PyObject *args) { int index = 0; if(!PyArg_ParseTuple(args, "O!|zi", &Line_Type, &line, &path, &index)) return NULL; index_type num = 0; - while(num < line->xnum && line->cells[num].ch) num += line->cells[num].attrs & WIDTH_MASK; + while(num < line->xnum && line->cpu_cells[num].ch) num += line->gpu_cells[num].attrs & WIDTH_MASK; PyObject *face = NULL; Font *font; if (!num_font_groups) { PyErr_SetString(PyExc_RuntimeError, "must create at least one font group first"); return NULL; } @@ -940,7 +949,7 @@ test_shape(PyObject UNUSED *self, PyObject *args) { FontGroup *fg = font_groups; font = fg->fonts + fg->medium_font_idx; } - shape_run(line->cells, num, font); + shape_run(line->cpu_cells, line->gpu_cells, num, font); PyObject *ans = PyList_New(0); unsigned int idx = 0; @@ -961,21 +970,21 @@ test_shape(PyObject UNUSED *self, PyObject *args) { #undef G static inline void -render_run(FontGroup *fg, Cell *first_cell, index_type num_cells, ssize_t font_idx, bool pua_space_ligature) { +render_run(FontGroup *fg, CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells, ssize_t font_idx, bool pua_space_ligature) { switch(font_idx) { default: - shape_run(first_cell, num_cells, &fg->fonts[font_idx]); + shape_run(first_cpu_cell, first_gpu_cell, num_cells, &fg->fonts[font_idx]); if (pua_space_ligature) merge_groups_for_pua_space_ligature(); render_groups(fg, &fg->fonts[font_idx]); break; case BLANK_FONT: - while(num_cells--) set_sprite(first_cell++, 0, 0, 0); + while(num_cells--) { set_sprite(first_gpu_cell, 0, 0, 0); first_cpu_cell++; first_gpu_cell++; } break; case BOX_FONT: - while(num_cells--) render_box_cell(fg, first_cell++); + while(num_cells--) { render_box_cell(fg, first_cpu_cell, first_gpu_cell); first_cpu_cell++; first_gpu_cell++; } break; case MISSING_FONT: - while(num_cells--) set_sprite(first_cell++, MISSING_GLYPH, 0, 0); + while(num_cells--) { set_sprite(first_gpu_cell, MISSING_GLYPH, 0, 0); first_cpu_cell++; first_gpu_cell++; } break; } } @@ -987,32 +996,33 @@ is_private_use(char_type ch) { void render_line(FONTS_DATA_HANDLE fg_, Line *line) { -#define RENDER if (run_font_idx != NO_FONT && i > first_cell_in_run) render_run(fg, line->cells + first_cell_in_run, i - first_cell_in_run, run_font_idx, false); +#define RENDER if (run_font_idx != NO_FONT && i > first_cell_in_run) render_run(fg, line->cpu_cells + first_cell_in_run, line->gpu_cells + first_cell_in_run, i - first_cell_in_run, run_font_idx, false); FontGroup *fg = (FontGroup*)fg_; ssize_t run_font_idx = NO_FONT; index_type first_cell_in_run, i; attrs_type prev_width = 0; for (i=0, first_cell_in_run=0; i < line->xnum; i++) { if (prev_width == 2) { prev_width = 0; continue; } - Cell *cell = line->cells + i; - ssize_t cell_font_idx = font_for_cell(fg, cell); - if (is_private_use(cell->ch) && i + 1 < line->xnum && (line->cells[i+1].ch == ' ' || line->cells[i+1].ch == 0) && cell_font_idx != BOX_FONT && cell_font_idx != MISSING_FONT) { + CPUCell *cpu_cell = line->cpu_cells + i; + GPUCell *gpu_cell = line->gpu_cells + i; + ssize_t cell_font_idx = font_for_cell(fg, cpu_cell, gpu_cell); + if (is_private_use(cpu_cell->ch) && i + 1 < line->xnum && (line->cpu_cells[i+1].ch == ' ' || line->cpu_cells[i+1].ch == 0) && cell_font_idx != BOX_FONT && cell_font_idx != MISSING_FONT) { // We have a private use char followed by a space char, render it as a two cell ligature. - Cell *space_cell = line->cells + i+1; + GPUCell *space_cell = line->gpu_cells + i+1; // Ensure the space cell uses the foreground colors from the PUA cell // This is needed because there are stupid applications like // powerline that use PUA+space with different foreground colors // for the space and the PUA. See for example: https://github.com/kovidgoyal/kitty/issues/467 - space_cell->fg = cell->fg; space_cell->decoration_fg = cell->decoration_fg; + space_cell->fg = gpu_cell->fg; space_cell->decoration_fg = gpu_cell->decoration_fg; RENDER; - render_run(fg, line->cells + i, 2, cell_font_idx, true); + render_run(fg, line->cpu_cells + i, line->gpu_cells + i, 2, cell_font_idx, true); run_font_idx = NO_FONT; first_cell_in_run = i + 2; - prev_width = line->cells[i+1].attrs & WIDTH_MASK; + prev_width = line->gpu_cells[i+1].attrs & WIDTH_MASK; i++; continue; } - prev_width = cell->attrs & WIDTH_MASK; + prev_width = gpu_cell->attrs & WIDTH_MASK; if (run_font_idx == NO_FONT) run_font_idx = cell_font_idx; if (run_font_idx == cell_font_idx) continue; RENDER; @@ -1259,15 +1269,16 @@ get_fallback_font(PyObject UNUSED *self, PyObject *args) { PyObject *text; int bold, italic; if (!PyArg_ParseTuple(args, "Upp", &text, &bold, &italic)) return NULL; - Cell cell = {0}; - static Py_UCS4 char_buf[2 + arraysz(cell.cc_idx)]; + CPUCell cpu_cell = {0}; + GPUCell gpu_cell = {0}; + static Py_UCS4 char_buf[2 + arraysz(cpu_cell.cc_idx)]; if (!PyUnicode_AsUCS4(text, char_buf, arraysz(char_buf), 1)) return NULL; - cell.ch = char_buf[0]; - for (unsigned i = 0; i + 1 < (unsigned) PyUnicode_GetLength(text) && i < arraysz(cell.cc_idx); i++) cell.cc_idx[i] = mark_for_codepoint(char_buf[i + 1]); - if (bold) cell.attrs |= 1 << BOLD_SHIFT; - if (italic) cell.attrs |= 1 << ITALIC_SHIFT; + cpu_cell.ch = char_buf[0]; + for (unsigned i = 0; i + 1 < (unsigned) PyUnicode_GetLength(text) && i < arraysz(cpu_cell.cc_idx); i++) cpu_cell.cc_idx[i] = mark_for_codepoint(char_buf[i + 1]); + if (bold) gpu_cell.attrs |= 1 << BOLD_SHIFT; + if (italic) gpu_cell.attrs |= 1 << ITALIC_SHIFT; FontGroup *fg = font_groups; - ssize_t ans = fallback_font(fg, &cell); + ssize_t ans = fallback_font(fg, &cpu_cell, &gpu_cell); if (ans < 0) { PyErr_SetString(PyExc_ValueError, "Too many fallback fonts"); return NULL; } return fg->fonts[ans].face; } diff --git a/kitty/fonts.h b/kitty/fonts.h index 380de0957..0e5a7aacb 100644 --- a/kitty/fonts.h +++ b/kitty/fonts.h @@ -22,7 +22,7 @@ hb_font_t* harfbuzz_font_for_face(PyObject*); bool set_size_for_face(PyObject*, unsigned int, bool, FONTS_DATA_HANDLE); void cell_metrics(PyObject*, unsigned int*, unsigned int*, unsigned int*, unsigned int*, unsigned int*); bool render_glyphs_in_cells(PyObject *f, bool bold, bool italic, hb_glyph_info_t *info, hb_glyph_position_t *positions, unsigned int num_glyphs, pixel *canvas, unsigned int cell_width, unsigned int cell_height, unsigned int num_cells, unsigned int baseline, bool *was_colored, FONTS_DATA_HANDLE); -PyObject* create_fallback_face(PyObject *base_face, Cell* cell, bool bold, bool italic, bool emoji_presentation, FONTS_DATA_HANDLE fg); +PyObject* create_fallback_face(PyObject *base_face, CPUCell* cell, bool bold, bool italic, bool emoji_presentation, FONTS_DATA_HANDLE fg); PyObject* specialize_font_descriptor(PyObject *base_descriptor, FONTS_DATA_HANDLE); PyObject* face_from_path(const char *path, int index, FONTS_DATA_HANDLE); PyObject* face_from_descriptor(PyObject*, FONTS_DATA_HANDLE); diff --git a/kitty/history.c b/kitty/history.c index 7a43eda57..9a77da866 100644 --- a/kitty/history.c +++ b/kitty/history.c @@ -18,9 +18,10 @@ add_segment(HistoryBuf *self) { self->segments = PyMem_Realloc(self->segments, sizeof(HistoryBufSegment) * self->num_segments); if (self->segments == NULL) fatal("Out of memory allocating new history buffer segment"); HistoryBufSegment *s = self->segments + self->num_segments - 1; - s->cells = PyMem_Calloc(self->xnum * SEGMENT_SIZE, sizeof(Cell)); + s->cpu_cells = PyMem_Calloc(self->xnum * SEGMENT_SIZE, sizeof(CPUCell)); + s->gpu_cells = PyMem_Calloc(self->xnum * SEGMENT_SIZE, sizeof(GPUCell)); s->line_attrs = PyMem_Calloc(SEGMENT_SIZE, sizeof(line_attrs_type)); - if (s->cells == NULL || s->line_attrs == NULL) fatal("Out of memory allocating new history buffer segment"); + if (s->cpu_cells == NULL || s->gpu_cells == NULL || s->line_attrs == NULL) fatal("Out of memory allocating new history buffer segment"); } static inline index_type @@ -37,9 +38,14 @@ segment_for(HistoryBuf *self, index_type y) { return self->segments[seg_num].which + y * stride; \ } -static inline Cell* -lineptr(HistoryBuf *self, index_type y) { - seg_ptr(cells, self->xnum); +static inline CPUCell* +cpu_lineptr(HistoryBuf *self, index_type y) { + seg_ptr(cpu_cells, self->xnum); +} + +static inline GPUCell* +gpu_lineptr(HistoryBuf *self, index_type y) { + seg_ptr(gpu_cells, self->xnum); } @@ -64,12 +70,12 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) { if (self != NULL) { self->xnum = xnum; self->ynum = ynum; - self->line = alloc_line(); self->num_segments = 0; add_segment(self); + self->line = alloc_line(); self->line->xnum = xnum; for(index_type y = 0; y < self->ynum; y++) { - clear_chars_in_line(lineptr(self, y), self->xnum, BLANK_CHAR); + clear_chars_in_line(cpu_lineptr(self, y), gpu_lineptr(self, y), self->xnum, BLANK_CHAR); } } @@ -80,7 +86,8 @@ static void dealloc(HistoryBuf* self) { Py_CLEAR(self->line); for (size_t i = 0; i < self->num_segments; i++) { - PyMem_Free(self->segments[i].cells); + PyMem_Free(self->segments[i].cpu_cells); + PyMem_Free(self->segments[i].gpu_cells); PyMem_Free(self->segments[i].line_attrs); } PyMem_Free(self->segments); @@ -99,7 +106,8 @@ index_of(HistoryBuf *self, index_type lnum) { static inline void init_line(HistoryBuf *self, index_type num, Line *l) { // Initialize the line l, setting its pointer to the offsets for the line at index (buffer position) num - l->cells = lineptr(self, num); + l->cpu_cells = cpu_lineptr(self, num); + l->gpu_cells = gpu_lineptr(self, num); l->continued = *attrptr(self, num) & CONTINUED_MASK; l->has_dirty_text = *attrptr(self, num) & TEXT_DIRTY_MASK ? true : false; } @@ -286,7 +294,8 @@ void historybuf_rewrap(HistoryBuf *self, HistoryBuf *other) { if (other->xnum == self->xnum && other->ynum == self->ynum) { // Fast path for (index_type i = 0; i < self->num_segments; i++) { - memcpy(other->segments[i].cells, self->segments[i].cells, SEGMENT_SIZE * self->xnum * sizeof(Cell)); + memcpy(other->segments[i].cpu_cells, self->segments[i].cpu_cells, SEGMENT_SIZE * self->xnum * sizeof(CPUCell)); + memcpy(other->segments[i].gpu_cells, self->segments[i].gpu_cells, SEGMENT_SIZE * self->xnum * sizeof(GPUCell)); memcpy(other->segments[i].line_attrs, self->segments[i].line_attrs, SEGMENT_SIZE * sizeof(line_attrs_type)); } other->count = self->count; other->start_of_data = self->start_of_data; diff --git a/kitty/line-buf.c b/kitty/line-buf.c index 2b298faa2..0e8179639 100644 --- a/kitty/line-buf.c +++ b/kitty/line-buf.c @@ -12,19 +12,25 @@ extern PyTypeObject Line_Type; extern PyTypeObject HistoryBuf_Type; -static inline Cell* -lineptr(LineBuf *linebuf, index_type y) { - return linebuf->buf + y * linebuf->xnum; +static inline CPUCell* +cpu_lineptr(LineBuf *linebuf, index_type y) { + return linebuf->cpu_cell_buf + y * linebuf->xnum; +} + +static inline GPUCell* +gpu_lineptr(LineBuf *linebuf, index_type y) { + return linebuf->gpu_cell_buf + y * linebuf->xnum; } static inline void clear_chars_to(LineBuf* linebuf, index_type y, char_type ch) { - clear_chars_in_line(lineptr(linebuf, y), linebuf->xnum, ch); + clear_chars_in_line(cpu_lineptr(linebuf, y), gpu_lineptr(linebuf, y), linebuf->xnum, ch); } void linebuf_clear(LineBuf *self, char_type ch) { - memset(self->buf, 0, self->xnum * self->ynum * sizeof(Cell)); + memset(self->cpu_cell_buf, 0, self->xnum * self->ynum * sizeof(CPUCell)); + memset(self->gpu_cell_buf, 0, self->xnum * self->ynum * sizeof(GPUCell)); memset(self->line_attrs, 0, self->ynum * sizeof(line_attrs_type)); for (index_type i = 0; i < self->ynum; i++) self->line_map[i] = i; if (ch != 0) { @@ -73,14 +79,15 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) { if (self != NULL) { self->xnum = xnum; self->ynum = ynum; - self->buf = PyMem_Calloc(xnum * ynum, sizeof(Cell)); + self->cpu_cell_buf = PyMem_Calloc(xnum * ynum, sizeof(CPUCell)); + self->gpu_cell_buf = PyMem_Calloc(xnum * ynum, sizeof(GPUCell)); self->line_map = PyMem_Calloc(ynum, sizeof(index_type)); self->scratch = PyMem_Calloc(ynum, sizeof(index_type)); self->line_attrs = PyMem_Calloc(ynum, sizeof(line_attrs_type)); self->line = alloc_line(); - if (self->buf == NULL || self->line_map == NULL || self->scratch == NULL || self->line_attrs == NULL || self->line == NULL) { + if (self->cpu_cell_buf == NULL || self->gpu_cell_buf == NULL || self->line_map == NULL || self->scratch == NULL || self->line_attrs == NULL || self->line == NULL) { PyErr_NoMemory(); - PyMem_Free(self->buf); PyMem_Free(self->line_map); PyMem_Free(self->line_attrs); Py_CLEAR(self->line); + PyMem_Free(self->cpu_cell_buf); PyMem_Free(self->gpu_cell_buf); PyMem_Free(self->line_map); PyMem_Free(self->line_attrs); Py_CLEAR(self->line); Py_CLEAR(self); } else { self->line->xnum = xnum; @@ -96,7 +103,8 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) { static void dealloc(LineBuf* self) { - PyMem_Free(self->buf); + PyMem_Free(self->cpu_cell_buf); + PyMem_Free(self->gpu_cell_buf); PyMem_Free(self->line_map); PyMem_Free(self->line_attrs); PyMem_Free(self->scratch); @@ -106,7 +114,8 @@ dealloc(LineBuf* self) { static inline void init_line(LineBuf *lb, Line *l, index_type ynum) { - l->cells = lineptr(lb, ynum); + l->cpu_cells = cpu_lineptr(lb, ynum); + l->gpu_cells = gpu_lineptr(lb, ynum); } void @@ -133,13 +142,13 @@ line(LineBuf *self, PyObject *y) { unsigned int linebuf_char_width_at(LineBuf *self, index_type x, index_type y) { - return (lineptr(self, self->line_map[y])[x].attrs) & WIDTH_MASK; + return (gpu_lineptr(self, self->line_map[y])[x].attrs) & WIDTH_MASK; } void linebuf_set_attribute(LineBuf *self, unsigned int shift, unsigned int val) { for (index_type y = 0; y < self->ynum; y++) { - set_attribute_on_line(lineptr(self, y), shift, val, self->xnum); + set_attribute_on_line(gpu_lineptr(self, y), shift, val, self->xnum); self->line_attrs[y] |= TEXT_DIRTY_MASK; } } @@ -181,12 +190,14 @@ dirty_lines(LineBuf *self, PyObject *a UNUSED) { static inline bool allocate_line_storage(Line *line, bool initialize) { if (initialize) { - line->cells = PyMem_Calloc(line->xnum, sizeof(Cell)); - if (line->cells == NULL) { PyErr_NoMemory(); return false; } - if (BLANK_CHAR != 0) clear_chars_in_line(line->cells, line->xnum, BLANK_CHAR); + line->cpu_cells = PyMem_Calloc(line->xnum, sizeof(CPUCell)); + line->gpu_cells = PyMem_Calloc(line->xnum, sizeof(GPUCell)); + if (line->cpu_cells == NULL || line->gpu_cells) { PyErr_NoMemory(); return false; } + if (BLANK_CHAR != 0) clear_chars_in_line(line->cpu_cells, line->gpu_cells, line->xnum, BLANK_CHAR); } else { - line->cells = PyMem_Malloc(line->xnum * sizeof(Cell)); - if (line->cells == NULL) { PyErr_NoMemory(); return false; } + line->cpu_cells = PyMem_Malloc(line->xnum * sizeof(CPUCell)); + line->gpu_cells = PyMem_Malloc(line->xnum * sizeof(GPUCell)); + if (line->cpu_cells == NULL || line->gpu_cells == NULL) { PyErr_NoMemory(); return false; } } line->needs_free = 1; return true; @@ -232,8 +243,9 @@ copy_line_to(LineBuf *self, PyObject *args) { static inline void clear_line_(Line *l, index_type xnum) { - memset(l->cells, 0, xnum * sizeof(Cell)); - if (BLANK_CHAR != 0) clear_chars_in_line(l->cells, xnum, BLANK_CHAR); + memset(l->cpu_cells, 0, xnum * sizeof(CPUCell)); + memset(l->gpu_cells, 0, xnum * sizeof(GPUCell)); + if (BLANK_CHAR != 0) clear_chars_in_line(l->cpu_cells, l->gpu_cells, xnum, BLANK_CHAR); l->has_dirty_text = false; } @@ -515,7 +527,8 @@ linebuf_rewrap(LineBuf *self, LineBuf *other, index_type *num_content_lines_befo if (other->xnum == self->xnum && other->ynum == self->ynum) { memcpy(other->line_map, self->line_map, sizeof(index_type) * self->ynum); memcpy(other->line_attrs, self->line_attrs, sizeof(bool) * self->ynum); - memcpy(other->buf, self->buf, self->xnum * self->ynum * sizeof(Cell)); + memcpy(other->cpu_cell_buf, self->cpu_cell_buf, self->xnum * self->ynum * sizeof(CPUCell)); + memcpy(other->gpu_cell_buf, self->gpu_cell_buf, self->xnum * self->ynum * sizeof(GPUCell)); *num_content_lines_before = self->ynum; *num_content_lines_after = self->ynum; return; } @@ -524,7 +537,7 @@ linebuf_rewrap(LineBuf *self, LineBuf *other, index_type *num_content_lines_befo first = self->ynum; do { first--; - Cell *cells = lineptr(self, self->line_map[first]); + CPUCell *cells = cpu_lineptr(self, self->line_map[first]); for(i = 0; i < self->xnum; i++) { if ((cells[i].ch) != BLANK_CHAR) { is_empty = false; break; } } diff --git a/kitty/line.c b/kitty/line.c index 0af7ac390..b545be181 100644 --- a/kitty/line.c +++ b/kitty/line.c @@ -20,7 +20,8 @@ new(PyTypeObject UNUSED *type, PyObject UNUSED *args, PyObject UNUSED *kwds) { static void dealloc(Line* self) { if (self->needs_free) { - PyMem_Free(self->cells); + PyMem_Free(self->cpu_cells); + PyMem_Free(self->gpu_cells); } Py_TYPE(self)->tp_free((PyObject*)self); } @@ -29,13 +30,13 @@ unsigned int line_length(Line *self) { index_type last = self->xnum - 1; for (index_type i = 0; i < self->xnum; i++) { - if ((self->cells[last - i].ch) != BLANK_CHAR) return self->xnum - i; + if ((self->cpu_cells[last - i].ch) != BLANK_CHAR) return self->xnum - i; } return 0; } PyObject* -cell_text(Cell *cell) { +cell_text(CPUCell *cell) { PyObject *ans; unsigned num = 1; static Py_UCS4 buf[arraysz(cell->cc_idx) + 1]; @@ -59,7 +60,7 @@ find_colon_slash(Line *self, index_type x, index_type limit) { limit = MAX(2, limit); if (pos < limit) return 0; do { - char_type ch = self->cells[pos].ch; + char_type ch = self->cpu_cells[pos].ch; if (!is_url_char(ch)) return false; switch(state) { case ANY: @@ -83,7 +84,7 @@ prefix_matches(Line *self, index_type at, const char* prefix, index_type prefix_ if (prefix_len > at) return false; index_type p, i; for (p = at - prefix_len, i = 0; i < prefix_len && p < self->xnum; i++, p++) { - if ((self->cells[p].ch) != (unsigned char)prefix[i]) return false; + if ((self->cpu_cells[p].ch) != (unsigned char)prefix[i]) return false; } return i == prefix_len; } @@ -108,7 +109,7 @@ static inline bool has_url_beyond(Line *self, index_type x) { if (self->xnum <= x + MIN_URL_LEN + 3) return false; for (index_type i = x; i < MIN(x + MIN_URL_LEN + 3, self->xnum); i++) { - if (!is_url_char(self->cells[i].ch)) return false; + if (!is_url_char(self->cpu_cells[i].ch)) return false; } return true; } @@ -134,9 +135,9 @@ index_type line_url_end_at(Line *self, index_type x, bool check_short) { index_type ans = x; if (x >= self->xnum || (check_short && self->xnum <= MIN_URL_LEN + 3)) return 0; - while (ans < self->xnum && is_url_char(self->cells[ans].ch)) ans++; + while (ans < self->xnum && is_url_char(self->cpu_cells[ans].ch)) ans++; if (ans) ans--; - while (ans > x && can_strip_from_end_of_url(self->cells[ans].ch)) ans--; + while (ans > x && can_strip_from_end_of_url(self->cpu_cells[ans].ch)) ans--; return ans; } @@ -158,11 +159,11 @@ static PyObject* text_at(Line* self, Py_ssize_t xval) { #define text_at_doc "[x] -> Return the text in the specified cell" if ((unsigned)xval >= self->xnum) { PyErr_SetString(PyExc_IndexError, "Column number out of bounds"); return NULL; } - return cell_text(self->cells + xval); + return cell_text(self->cpu_cells + xval); } size_t -cell_as_unicode(Cell *cell, bool include_cc, Py_UCS4 *buf, char_type zero_char) { +cell_as_unicode(CPUCell *cell, bool include_cc, Py_UCS4 *buf, char_type zero_char) { size_t n = 1; buf[0] = cell->ch ? cell->ch : zero_char; if (include_cc) { @@ -172,7 +173,7 @@ cell_as_unicode(Cell *cell, bool include_cc, Py_UCS4 *buf, char_type zero_char) } size_t -cell_as_utf8(Cell *cell, bool include_cc, char *buf, char_type zero_char) { +cell_as_utf8(CPUCell *cell, bool include_cc, char *buf, char_type zero_char) { size_t n = encode_utf8(cell->ch ? cell->ch : zero_char, buf); if (include_cc) { for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) n += encode_utf8(codepoint_for_mark(cell->cc_idx[i]), buf + n); @@ -188,13 +189,13 @@ unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc static Py_UCS4 buf[4096]; if (leading_char) buf[n++] = leading_char; char_type previous_width = 0; - for(index_type i = start; i < limit && n < arraysz(buf) - 2 - arraysz(self->cells->cc_idx); i++) { - char_type ch = self->cells[i].ch; + for(index_type i = start; i < limit && n < arraysz(buf) - 2 - arraysz(self->cpu_cells->cc_idx); i++) { + char_type ch = self->cpu_cells[i].ch; if (ch == 0) { if (previous_width == 2) { previous_width = 0; continue; }; } - n += cell_as_unicode(self->cells + i, include_cc, buf + n, ' '); - previous_width = self->cells[i].attrs & WIDTH_MASK; + n += cell_as_unicode(self->cpu_cells + i, include_cc, buf + n, ' '); + previous_width = self->gpu_cells[i].attrs & WIDTH_MASK; } return PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, buf, n); } @@ -209,7 +210,7 @@ sprite_at(Line* self, PyObject *x) { #define sprite_at_doc "[x] -> Return the sprite in the specified cell" unsigned long xval = PyLong_AsUnsignedLong(x); if (xval >= self->xnum) { PyErr_SetString(PyExc_IndexError, "Column number out of bounds"); return NULL; } - Cell *c = self->cells + xval; + GPUCell *c = self->gpu_cells + xval; return Py_BuildValue("HHH", c->sprite_x, c->sprite_y, c->sprite_z); } @@ -236,21 +237,21 @@ line_as_ansi(Line *self, Py_UCS4 *buf, index_type buflen) { Cursor *cursor = &c1, *prev_cursor = &c2, *t; for (index_type pos=0; pos < limit; pos++) { - char_type attrs = self->cells[pos].attrs, ch = self->cells[pos].ch; + char_type attrs = self->gpu_cells[pos].attrs, ch = self->cpu_cells[pos].ch; if (ch == 0) { if (previous_width == 2) { previous_width = 0; continue; } ch = ' '; } ATTRS_TO_CURSOR(attrs, cursor); - cursor->fg = self->cells[pos].fg; cursor->bg = self->cells[pos].bg; - cursor->decoration_fg = self->cells[pos].decoration_fg & COL_MASK; + cursor->fg = self->gpu_cells[pos].fg; cursor->bg = self->gpu_cells[pos].bg; + cursor->decoration_fg = self->gpu_cells[pos].decoration_fg & COL_MASK; const char *sgr = cursor_as_sgr(cursor, prev_cursor); t = prev_cursor; prev_cursor = cursor; cursor = t; if (*sgr) WRITE_SGR(sgr); WRITE_CH(ch); - for(unsigned c = 0; c < arraysz(self->cells[pos].cc_idx) && self->cells[pos].cc_idx[c]; c++) { - WRITE_CH(codepoint_for_mark(self->cells[pos].cc_idx[c])); + for(unsigned c = 0; c < arraysz(self->cpu_cells[pos].cc_idx) && self->cpu_cells[pos].cc_idx[c]; c++) { + WRITE_CH(codepoint_for_mark(self->cpu_cells[pos].cc_idx[c])); } previous_width = attrs & WIDTH_MASK; } @@ -293,13 +294,13 @@ width(Line *self, PyObject *val) { #define width_doc "width(x) -> the width of the character at x" unsigned long x = PyLong_AsUnsignedLong(val); if (x >= self->xnum) { PyErr_SetString(PyExc_ValueError, "Out of bounds"); return NULL; } - char_type attrs = self->cells[x].attrs; + char_type attrs = self->gpu_cells[x].attrs; return PyLong_FromUnsignedLong((unsigned long) (attrs & WIDTH_MASK)); } void line_add_combining_char(Line *self, uint32_t ch, unsigned int x) { - Cell *cell = self->cells + x; + CPUCell *cell = self->cpu_cells + x; if (!cell->ch) return; // dont allow adding combining chars to a null cell for (unsigned i = 0; i < arraysz(cell->cc_idx); i++) { if (!cell->cc_idx[i]) { cell->cc_idx[i] = mark_for_codepoint(ch); return; } @@ -349,12 +350,12 @@ set_text(Line* self, PyObject *args) { color_type dfg = cursor->decoration_fg & COL_MASK; for (index_type i = cursor->x; offset < limit && i < self->xnum; i++, offset++) { - self->cells[i].ch = (PyUnicode_READ(kind, buf, offset)); - self->cells[i].attrs = attrs; - self->cells[i].fg = fg; - self->cells[i].bg = bg; - self->cells[i].decoration_fg = dfg; - memset(self->cells[i].cc_idx, 0, sizeof(self->cells[i].cc_idx)); + self->cpu_cells[i].ch = (PyUnicode_READ(kind, buf, offset)); + self->gpu_cells[i].attrs = attrs; + self->gpu_cells[i].fg = fg; + self->gpu_cells[i].bg = bg; + self->gpu_cells[i].decoration_fg = dfg; + memset(self->cpu_cells[i].cc_idx, 0, sizeof(self->cpu_cells[i].cc_idx)); } Py_RETURN_NONE; @@ -373,10 +374,10 @@ cursor_from(Line* self, PyObject *args) { ans = alloc_cursor(); if (ans == NULL) { PyErr_NoMemory(); return NULL; } ans->x = x; ans->y = y; - char_type attrs = self->cells[x].attrs; + char_type attrs = self->gpu_cells[x].attrs; ATTRS_TO_CURSOR(attrs, ans); - ans->fg = self->cells[x].fg; ans->bg = self->cells[x].bg; - ans->decoration_fg = self->cells[x].decoration_fg & COL_MASK; + ans->fg = self->gpu_cells[x].fg; ans->bg = self->gpu_cells[x].bg; + ans->decoration_fg = self->gpu_cells[x].decoration_fg & COL_MASK; return (PyObject*)ans; } @@ -386,8 +387,8 @@ line_clear_text(Line *self, unsigned int at, unsigned int num, char_type ch) { attrs_type width = ch ? 1 : 0; #define PREFIX \ for (index_type i = at; i < MIN(self->xnum, at + num); i++) { \ - self->cells[i].ch = ch; memset(self->cells[i].cc_idx, 0, sizeof(self->cells[i].cc_idx)); \ - self->cells[i].attrs = (self->cells[i].attrs & ATTRS_MASK_WITHOUT_WIDTH) | width; \ + self->cpu_cells[i].ch = ch; memset(self->cpu_cells[i].cc_idx, 0, sizeof(self->cpu_cells[i].cc_idx)); \ + self->gpu_cells[i].attrs = (self->gpu_cells[i].attrs & ATTRS_MASK_WITHOUT_WIDTH) | width; \ } if (CHAR_IS_BLANK(ch)) { PREFIX @@ -415,16 +416,16 @@ line_apply_cursor(Line *self, Cursor *cursor, unsigned int at, unsigned int num, for (index_type i = at; i < self->xnum && i < at + num; i++) { if (clear_char) { - self->cells[i].ch = BLANK_CHAR; - memset(self->cells[i].cc_idx, 0, sizeof(self->cells[i].cc_idx)); - self->cells[i].attrs = attrs; - clear_sprite_position(self->cells[i]); + self->cpu_cells[i].ch = BLANK_CHAR; + memset(self->cpu_cells[i].cc_idx, 0, sizeof(self->cpu_cells[i].cc_idx)); + self->gpu_cells[i].attrs = attrs; + clear_sprite_position(self->gpu_cells[i]); } else { - attrs_type w = self->cells[i].attrs & WIDTH_MASK; - self->cells[i].attrs = attrs | w; + attrs_type w = self->gpu_cells[i].attrs & WIDTH_MASK; + self->gpu_cells[i].attrs = attrs | w; } - self->cells[i].fg = fg; self->cells[i].bg = bg; - self->cells[i].decoration_fg = dfg; + self->gpu_cells[i].fg = fg; self->gpu_cells[i].bg = bg; + self->gpu_cells[i].decoration_fg = dfg; } } @@ -444,11 +445,11 @@ void line_right_shift(Line *self, unsigned int at, unsigned int num) { COPY_SELF_CELL(i - num, i) } // Check if a wide character was split at the right edge - char_type w = (self->cells[self->xnum - 1].attrs) & WIDTH_MASK; + char_type w = (self->gpu_cells[self->xnum - 1].attrs) & WIDTH_MASK; if (w != 1) { - self->cells[self->xnum - 1].ch = BLANK_CHAR; - self->cells[self->xnum - 1].attrs = BLANK_CHAR ? 1 : 0; - clear_sprite_position(self->cells[self->xnum - 1]); + self->cpu_cells[self->xnum - 1].ch = BLANK_CHAR; + self->gpu_cells[self->xnum - 1].attrs = BLANK_CHAR ? 1 : 0; + clear_sprite_position(self->gpu_cells[self->xnum - 1]); } } @@ -483,15 +484,15 @@ left_shift(Line *self, PyObject *args) { void line_set_char(Line *self, unsigned int at, uint32_t ch, unsigned int width, Cursor *cursor, bool UNUSED is_second) { if (cursor == NULL) { - self->cells[at].attrs = (self->cells[at].attrs & ATTRS_MASK_WITHOUT_WIDTH) | width; + self->gpu_cells[at].attrs = (self->gpu_cells[at].attrs & ATTRS_MASK_WITHOUT_WIDTH) | width; } else { - self->cells[at].attrs = CURSOR_TO_ATTRS(cursor, width & WIDTH_MASK); - self->cells[at].fg = (cursor->fg & COL_MASK); - self->cells[at].bg = (cursor->bg & COL_MASK); - self->cells[at].decoration_fg = cursor->decoration_fg & COL_MASK; + self->gpu_cells[at].attrs = CURSOR_TO_ATTRS(cursor, width & WIDTH_MASK); + self->gpu_cells[at].fg = (cursor->fg & COL_MASK); + self->gpu_cells[at].bg = (cursor->bg & COL_MASK); + self->gpu_cells[at].decoration_fg = cursor->decoration_fg & COL_MASK; } - self->cells[at].ch = ch; - memset(self->cells[at].cc_idx, 0, sizeof(self->cells[at].cc_idx)); + self->cpu_cells[at].ch = ch; + memset(self->cpu_cells[at].cc_idx, 0, sizeof(self->cpu_cells[at].cc_idx)); } static PyObject* @@ -516,7 +517,7 @@ set_attribute(Line *self, PyObject *args) { unsigned int shift, val; if (!PyArg_ParseTuple(args, "II", &shift, &val)) return NULL; if (shift < DECORATION_SHIFT || shift > DIM_SHIFT) { PyErr_SetString(PyExc_ValueError, "Unknown attribute"); return NULL; } - set_attribute_on_line(self->cells, shift, val, self->xnum); + set_attribute_on_line(self->gpu_cells, shift, val, self->xnum); Py_RETURN_NONE; } @@ -525,8 +526,9 @@ __len__(PyObject *self) { return (Py_ssize_t)(((Line*)self)->xnum); } -static int __eq__(Line *a, Line *b) { - return a->xnum == b->xnum && memcmp(a->cells, b->cells, sizeof(Cell) * a->xnum) == 0; +static int +__eq__(Line *a, Line *b) { + return a->xnum == b->xnum && memcmp(a->cpu_cells, b->cpu_cells, sizeof(CPUCell) * a->xnum) == 0 && memcmp(a->gpu_cells, b->gpu_cells, sizeof(GPUCell) * a->xnum) == 0; } // Boilerplate {{{ diff --git a/kitty/lineops.h b/kitty/lineops.h index b2a238a28..6592f81c7 100644 --- a/kitty/lineops.h +++ b/kitty/lineops.h @@ -9,7 +9,7 @@ #include "data-types.h" static inline void -set_attribute_on_line(Cell *cells, uint32_t shift, uint32_t val, index_type xnum) { +set_attribute_on_line(GPUCell *cells, uint32_t shift, uint32_t val, index_type xnum) { // Set a single attribute on all cells in the line attrs_type mask = shift == DECORATION_SHIFT ? 3 : 1; attrs_type aval = (val & mask) << shift; @@ -17,21 +17,18 @@ set_attribute_on_line(Cell *cells, uint32_t shift, uint32_t val, index_type xnum for (index_type i = 0; i < xnum; i++) cells[i].attrs = (cells[i].attrs & mask) | aval; } -static inline void -copy_cells(const Cell *src, Cell *dest, index_type xnum) { - memcpy(dest, src, sizeof(Cell) * xnum); -} static inline void copy_line(const Line *src, Line *dest) { - copy_cells(src->cells, dest->cells, MIN(src->xnum, dest->xnum)); + memcpy(dest->cpu_cells, src->cpu_cells, sizeof(CPUCell) * MIN(src->xnum, dest->xnum)); + memcpy(dest->gpu_cells, src->gpu_cells, sizeof(GPUCell) * MIN(src->xnum, dest->xnum)); } static inline void -clear_chars_in_line(Cell *cells, index_type xnum, char_type ch) { +clear_chars_in_line(CPUCell *cpu_cells, GPUCell *gpu_cells, index_type xnum, char_type ch) { // Clear only the char part of each cell, the rest must have been cleared by a memset or similar if (ch) { - for (index_type i = 0; i < xnum; i++) { cells[i].ch = ch; cells[i].attrs = 1; } + for (index_type i = 0; i < xnum; i++) { cpu_cells[i].ch = ch; gpu_cells[i].attrs = 1; } } } @@ -39,7 +36,7 @@ static inline index_type xlimit_for_line(Line *line) { index_type xlimit = line->xnum; if (BLANK_CHAR == 0) { - while (xlimit > 0 && (line->cells[xlimit - 1].ch) == BLANK_CHAR) xlimit--; + while (xlimit > 0 && (line->cpu_cells[xlimit - 1].ch) == BLANK_CHAR) xlimit--; } return xlimit; } @@ -54,8 +51,8 @@ index_type line_url_start_at(Line *self, index_type x); index_type line_url_end_at(Line *self, index_type x, bool); index_type line_as_ansi(Line *self, Py_UCS4 *buf, index_type buflen); unsigned int line_length(Line *self); -size_t cell_as_unicode(Cell *cell, bool include_cc, Py_UCS4 *buf, char_type); -size_t cell_as_utf8(Cell *cell, bool include_cc, char *buf, char_type); +size_t cell_as_unicode(CPUCell *cell, bool include_cc, Py_UCS4 *buf, char_type); +size_t cell_as_utf8(CPUCell *cell, bool include_cc, char *buf, char_type); PyObject* unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc, char leading_char); PyObject* line_as_unicode(Line *); diff --git a/kitty/rewrap.h b/kitty/rewrap.h index ce82543ea..e58c93d08 100644 --- a/kitty/rewrap.h +++ b/kitty/rewrap.h @@ -43,7 +43,8 @@ static inline void copy_range(Line *src, index_type src_at, Line* dest, index_type dest_at, index_type num) { - memcpy(dest->cells + dest_at, src->cells + src_at, num * sizeof(Cell)); + memcpy(dest->cpu_cells + dest_at, src->cpu_cells + src_at, num * sizeof(CPUCell)); + memcpy(dest->gpu_cells + dest_at, src->gpu_cells + src_at, num * sizeof(GPUCell)); } @@ -60,7 +61,7 @@ rewrap_inner(BufType *src, BufType *dest, const index_type src_limit, HistoryBuf src_x_limit = src->xnum; if (!src_line_is_continued) { // Trim trailing blanks since there is a hard line break at the end of this line - while(src_x_limit && (src->line->cells[src_x_limit - 1].ch) == BLANK_CHAR) src_x_limit--; + while(src_x_limit && (src->line->cpu_cells[src_x_limit - 1].ch) == BLANK_CHAR) src_x_limit--; } if (is_tracked_line && *track_x >= src_x_limit) *track_x = MAX(1, src_x_limit) - 1; diff --git a/kitty/screen.c b/kitty/screen.c index 917290641..3aa2b747e 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -280,9 +280,10 @@ screen_designate_charset(Screen *self, uint32_t which, uint32_t as) { } static inline void -move_widened_char(Screen *self, Cell* cell, index_type xpos, index_type ypos) { +move_widened_char(Screen *self, CPUCell* cpu_cell, GPUCell *gpu_cell, index_type xpos, index_type ypos) { self->cursor->x = xpos; self->cursor->y = ypos; - Cell src = *cell, *dest; + CPUCell src_cpu = *cpu_cell, *dest_cpu; + GPUCell src_gpu = *gpu_cell, *dest_gpu; line_clear_text(self->linebuf->line, xpos, 1, BLANK_CHAR); if (self->modes.mDECAWM) { // overflow goes onto next line @@ -290,14 +291,17 @@ move_widened_char(Screen *self, Cell* cell, index_type xpos, index_type ypos) { screen_linefeed(self); self->linebuf->line_attrs[self->cursor->y] |= CONTINUED_MASK; linebuf_init_line(self->linebuf, self->cursor->y); - dest = self->linebuf->line->cells; + dest_cpu = self->linebuf->line->cpu_cells; + dest_gpu = self->linebuf->line->gpu_cells; self->cursor->x = MIN(2, self->columns); linebuf_mark_line_dirty(self->linebuf, self->cursor->y); } else { - dest = cell - 1; + dest_cpu = cpu_cell - 1; + dest_gpu = gpu_cell - 1; self->cursor->x = self->columns; } - *dest = src; + *dest_cpu = src_cpu; + *dest_gpu = src_gpu; } static inline void @@ -320,10 +324,11 @@ draw_combining_char(Screen *self, char_type ch) { self->is_dirty = true; linebuf_mark_line_dirty(self->linebuf, ypos); if (ch == 0xfe0f) { // emoji presentation variation marker makes default text presentation emoji (narrow emoji) into wide emoji - Cell *cell = self->linebuf->line->cells + xpos; - if ((cell->attrs & WIDTH_MASK) != 2 && cell->cc_idx[0] == VS16 && is_emoji_presentation_base(cell->ch)) { - cell->attrs = (cell->attrs & !WIDTH_MASK) | 2; - if (xpos == self->columns - 1) move_widened_char(self, cell, xpos, ypos); + CPUCell *cpu_cell = self->linebuf->line->cpu_cells + xpos; + GPUCell *gpu_cell = self->linebuf->line->gpu_cells + xpos; + if ((gpu_cell->attrs & WIDTH_MASK) != 2 && cpu_cell->cc_idx[0] == VS16 && is_emoji_presentation_base(cpu_cell->ch)) { + gpu_cell->attrs = (gpu_cell->attrs & !WIDTH_MASK) | 2; + if (xpos == self->columns - 1) move_widened_char(self, cpu_cell, gpu_cell, xpos, ypos); else self->cursor->x++; } } @@ -409,7 +414,7 @@ select_graphic_rendition(Screen *self, unsigned int *params, unsigned int count, num = MIN(num, self->columns - x); for (index_type y = region.top; y < MIN(region.bottom + 1, self->lines); y++) { linebuf_init_line(self->linebuf, y); - apply_sgr_to_cells(self->linebuf->line->cells + x, num, params, count); + apply_sgr_to_cells(self->linebuf->line->gpu_cells + x, num, params, count); } } else { index_type x, num; @@ -418,7 +423,7 @@ select_graphic_rendition(Screen *self, unsigned int *params, unsigned int count, else if (y == region.bottom) { x = 0; num = MIN(region.right + 1, self->columns); } else { x = 0; num = self->columns; } linebuf_init_line(self->linebuf, y); - apply_sgr_to_cells(self->linebuf->line->cells + x, num, params, count); + apply_sgr_to_cells(self->linebuf->line->gpu_cells + x, num, params, count); } } } else cursor_from_sgr(self->cursor, params, count); @@ -1280,8 +1285,8 @@ screen_request_capabilities(Screen *self, char c, PyObject *q) { // Rendering {{{ static inline void update_line_data(Line *line, unsigned int dest_y, uint8_t *data) { - size_t base = dest_y * line->xnum * sizeof(Cell); - memcpy(data + base, line->cells, line->xnum * sizeof(Cell)); + size_t base = dest_y * line->xnum * sizeof(GPUCell); + memcpy(data + base, line->gpu_cells, line->xnum * sizeof(GPUCell)); } @@ -1718,8 +1723,8 @@ screen_selection_range_for_line(Screen *self, index_type y, index_type *start, i if (y >= self->lines) { return false; } Line *line = visual_line_(self, y); index_type xlimit = line->xnum, xstart = 0; - while (xlimit > 0 && CHAR_IS_BLANK(line->cells[xlimit - 1].ch)) xlimit--; - while (xstart < xlimit && CHAR_IS_BLANK(line->cells[xstart].ch)) xstart++; + while (xlimit > 0 && CHAR_IS_BLANK(line->cpu_cells[xlimit - 1].ch)) xlimit--; + while (xstart < xlimit && CHAR_IS_BLANK(line->cpu_cells[xstart].ch)) xstart++; *start = xstart; *end = xlimit; return true; } @@ -1738,7 +1743,7 @@ screen_selection_range_for_word(Screen *self, index_type x, index_type *y1, inde index_type start, end; Line *line = visual_line_(self, *y1); *y2 = *y1; -#define is_ok(x) (is_word_char((line->cells[x].ch)) || is_opt_word_char(line->cells[x].ch)) +#define is_ok(x) (is_word_char((line->cpu_cells[x].ch)) || is_opt_word_char(line->cpu_cells[x].ch)) if (!is_ok(x)) { start = x; end = x + 1; } else { diff --git a/kitty/shaders.c b/kitty/shaders.c index d15d4c1c3..4c3844c91 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -183,7 +183,7 @@ create_cell_vao() { #define A(name, size, dtype, offset, stride) \ add_attribute_to_vao(CELL_PROGRAM, vao_idx, #name, \ /*size=*/size, /*dtype=*/dtype, /*stride=*/stride, /*offset=*/offset, /*divisor=*/1); -#define A1(name, size, dtype, offset) A(name, size, dtype, (void*)(offsetof(Cell, offset)), sizeof(Cell)) +#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); @@ -267,7 +267,7 @@ cell_prepare_to_render(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen, GLfloa ensure_sprite_map(fonts_data); if (screen->scroll_changed || screen->is_dirty) { - sz = sizeof(Cell) * screen->lines * screen->columns; + sz = sizeof(GPUCell) * screen->lines * screen->columns; address = alloc_and_map_vao_buffer(vao_idx, sz, cell_data_buffer, GL_STREAM_DRAW, GL_WRITE_ONLY); screen_update_cell_data(screen, address, sz, fonts_data); unmap_vao_buffer(vao_idx, cell_data_buffer); address = NULL;