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.
This commit is contained in:
Kovid Goyal
2018-05-27 21:25:09 +05:30
parent c8756612f7
commit 8dea5b3e3e
13 changed files with 256 additions and 212 deletions

View File

@@ -178,7 +178,7 @@ find_substitute_face(CFStringRef str, CTFontRef old_font) {
} }
PyObject* 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; CTFace *self = (CTFace*)base_face;
CTFontRef new_font; CTFontRef new_font;
if (emoji_presentation) new_font = CTFontCreateWithName((CFStringRef)@"AppleColorEmoji", self->scaled_point_sz, NULL); if (emoji_presentation) new_font = CTFontCreateWithName((CFStringRef)@"AppleColorEmoji", self->scaled_point_sz, NULL);

View File

@@ -136,7 +136,7 @@ END_ALLOW_CASE_RANGE
} }
void 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 RANGE for(unsigned c = 0; c < cell_count; c++, cell++)
#define SET(shift) RANGE { cell->attrs |= (1 << shift); } break; #define SET(shift) RANGE { cell->attrs |= (1 << shift); } break;
#define RESET(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; unsigned int i = 0, attr;
if (!count) { params[0] = 0; count = 1; } if (!count) { params[0] = 0; count = 1; }
while (i < count) { while (i < count) {
Cell *cell = first_cell; GPUCell *cell = first_cell;
attr = params[i++]; attr = params[i++];
switch(attr) { switch(attr) {
case 0: case 0:

View File

@@ -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; (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) \ #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) #define COPY_SELF_CELL(s, d) COPY_CELL(self, s, self, d)
@@ -140,15 +140,19 @@ typedef struct {
color_type fg, bg, decoration_fg; color_type fg, bg, decoration_fg;
sprite_index sprite_x, sprite_y, sprite_z; sprite_index sprite_x, sprite_y, sprite_z;
attrs_type attrs; attrs_type attrs;
// The following are only needed on the CPU, not the GPU } GPUCell;
typedef struct {
char_type ch; char_type ch;
combining_type cc_idx[2]; combining_type cc_idx[2];
} Cell; } CPUCell;
typedef struct { typedef struct {
PyObject_HEAD PyObject_HEAD
Cell *cells; GPUCell *gpu_cells;
CPUCell *cpu_cells;
index_type xnum, ynum; index_type xnum, ynum;
bool continued, needs_free, has_dirty_text; bool continued, needs_free, has_dirty_text;
} Line; } Line;
@@ -157,14 +161,16 @@ typedef struct {
typedef struct { typedef struct {
PyObject_HEAD PyObject_HEAD
Cell *buf; GPUCell *gpu_cell_buf;
CPUCell *cpu_cell_buf;
index_type xnum, ynum, *line_map, *scratch; index_type xnum, ynum, *line_map, *scratch;
line_attrs_type *line_attrs; line_attrs_type *line_attrs;
Line *line; Line *line;
} LineBuf; } LineBuf;
typedef struct { typedef struct {
Cell *cells; GPUCell *gpu_cells;
CPUCell *cpu_cells;
line_attrs_type *line_attrs; line_attrs_type *line_attrs;
} HistoryBufSegment; } HistoryBufSegment;
@@ -226,10 +232,10 @@ typedef struct {FONTS_DATA_HEAD} *FONTS_DATA_HANDLE;
for(index_type __i__ = (at); __i__ < (line)->xnum - (num); __i__++) { \ for(index_type __i__ = (at); __i__ < (line)->xnum - (num); __i__++) { \
COPY_CELL(line, __i__ + (num), line, __i__) \ COPY_CELL(line, __i__ + (num), line, __i__) \
} \ } \
if ((((line)->cells[(at)].attrs) & WIDTH_MASK) != 1) { \ if ((((line)->gpu_cells[(at)].attrs) & WIDTH_MASK) != 1) { \
(line)->cells[(at)].ch = BLANK_CHAR; \ (line)->cpu_cells[(at)].ch = BLANK_CHAR; \
(line)->cells[(at)].attrs = BLANK_CHAR ? 1 : 0; \ (line)->gpu_cells[(at)].attrs = BLANK_CHAR ? 1 : 0; \
clear_sprite_position((line)->cells[(at)]); \ 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_copy_to(Cursor *src, Cursor *dest);
void cursor_reset_display_attrs(Cursor*); void cursor_reset_display_attrs(Cursor*);
void cursor_from_sgr(Cursor *self, unsigned int *params, unsigned int count); 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*); const char* cursor_as_sgr(Cursor*, Cursor*);
double monotonic(); double monotonic();

View File

@@ -194,7 +194,7 @@ end:
} }
PyObject* 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; PyObject *ans = NULL;
FcPattern *pat = FcPatternCreate(); FcPattern *pat = FcPatternCreate();
if (pat == NULL) return PyErr_NoMemory(); if (pat == NULL) return PyErr_NoMemory();

View File

@@ -420,12 +420,12 @@ face_has_codepoint(PyObject* face, char_type cp) {
} }
static inline bool static inline bool
has_emoji_presentation(Cell *cell) { has_emoji_presentation(CPUCell *cpu_cell, GPUCell *gpu_cell) {
return (cell->attrs & WIDTH_MASK) == 2 && is_emoji(cell->ch) && cell->cc_idx[0] != VS15; return (gpu_cell->attrs & WIDTH_MASK) == 2 && is_emoji(cpu_cell->ch) && cpu_cell->cc_idx[0] != VS15;
} }
static inline bool 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; if (!face_has_codepoint(self->face, cell->ch)) return false;
for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) { for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) {
combining_type cc_idx = cell->cc_idx[i]; combining_type cc_idx = cell->cc_idx[i];
@@ -436,7 +436,7 @@ has_cell_text(Font *self, Cell *cell) {
} }
static inline void 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); printf("U+%x ", cell->ch);
for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) { for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) {
printf("U+%x ", codepoint_for_mark(cell->cc_idx[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 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; } if (fg->fallback_fonts_count > 100) { log_error("Too many fallback fonts"); return MISSING_FONT; }
ssize_t f; ssize_t f;
@@ -475,21 +475,21 @@ load_fallback_font(FontGroup *fg, Cell *cell, bool bold, bool italic, bool emoji
} }
static inline ssize_t static inline ssize_t
fallback_font(FontGroup *fg, Cell *cell) { fallback_font(FontGroup *fg, CPUCell *cpu_cell, GPUCell *gpu_cell) {
bool bold = (cell->attrs >> BOLD_SHIFT) & 1; bool bold = (gpu_cell->attrs >> BOLD_SHIFT) & 1;
bool italic = (cell->attrs >> ITALIC_SHIFT) & 1; bool italic = (gpu_cell->attrs >> ITALIC_SHIFT) & 1;
bool emoji_presentation = has_emoji_presentation(cell); bool emoji_presentation = has_emoji_presentation(cpu_cell, gpu_cell);
// Check if one of the existing fallback fonts has this text // 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++) { for (size_t i = 0, j = fg->first_fallback_font_idx; i < fg->fallback_fonts_count; i++, j++) {
Font *ff = fg->fonts +j; Font *ff = fg->fonts +j;
if (ff->bold == bold && ff->italic == italic && ff->emoji_presentation == emoji_presentation && has_cell_text(ff, cell)) { 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(cell, bold, italic, emoji_presentation, ff->face, false); if (global_state.debug_font_fallback) output_cell_fallback_data(cpu_cell, bold, italic, emoji_presentation, ff->face, false);
return j; 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 static inline ssize_t
@@ -502,10 +502,10 @@ in_symbol_maps(FontGroup *fg, char_type ch) {
static ssize_t 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 START_ALLOW_CASE_RANGE
ssize_t ans; ssize_t ans;
switch(cell->ch) { switch(cpu_cell->ch) {
case 0: case 0:
case ' ': case ' ':
return BLANK_FONT; return BLANK_FONT;
@@ -517,9 +517,9 @@ START_ALLOW_CASE_RANGE
case 0xe0b6: case 0xe0b6:
return BOX_FONT; return BOX_FONT;
default: default:
ans = in_symbol_maps(fg, cell->ch); ans = in_symbol_maps(fg, cpu_cell->ch);
if (ans > -1) return ans; if (ans > -1) return ans;
switch(BI_VAL(cell->attrs)) { switch(BI_VAL(gpu_cell->attrs)) {
case 0: case 0:
ans = fg->medium_font_idx; break; ans = fg->medium_font_idx; break;
case 1: case 1:
@@ -530,14 +530,14 @@ START_ALLOW_CASE_RANGE
ans = fg->bi_font_idx; break; ans = fg->bi_font_idx; break;
} }
if (ans < 0) ans = fg->medium_font_idx; if (ans < 0) ans = fg->medium_font_idx;
if (!has_emoji_presentation(cell) && has_cell_text(fg->fonts + ans, cell)) return ans; if (!has_emoji_presentation(cpu_cell, gpu_cell) && has_cell_text(fg->fonts + ans, cpu_cell)) return ans;
return fallback_font(fg, cell); return fallback_font(fg, cpu_cell, gpu_cell);
} }
END_ALLOW_CASE_RANGE END_ALLOW_CASE_RANGE
} }
static inline void 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; 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 static void
render_box_cell(FontGroup *fg, Cell *cell) { render_box_cell(FontGroup *fg, CPUCell *cpu_cell, GPUCell *gpu_cell) {
int error = 0; 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}}; static ExtraGlyphs extra_glyphs = {{0}};
SpritePosition *sp = sprite_position_for(fg, &fg->fonts[BOX_FONT], glyph, &extra_glyphs, false, &error); SpritePosition *sp = sprite_position_for(fg, &fg->fonts[BOX_FONT], glyph, &extra_glyphs, false, &error);
if (sp == NULL) { if (sp == NULL) {
sprite_map_set_error(error); PyErr_Print(); sprite_map_set_error(error); PyErr_Print();
set_sprite(cell, 0, 0, 0); set_sprite(gpu_cell, 0, 0, 0);
return; return;
} }
set_sprite(cell, sp->x, sp->y, sp->z); set_sprite(gpu_cell, sp->x, sp->y, sp->z);
if (sp->rendered) return; if (sp->rendered) return;
sp->rendered = true; sp->rendered = true;
sp->colored = false; 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; } if (ret == NULL) { PyErr_Print(); return; }
uint8_t *alpha_mask = PyLong_AsVoidPtr(PyTuple_GET_ITEM(ret, 0)); uint8_t *alpha_mask = PyLong_AsVoidPtr(PyTuple_GET_ITEM(ret, 0));
clear_canvas(fg); clear_canvas(fg);
@@ -602,17 +602,17 @@ render_box_cell(FontGroup *fg, Cell *cell) {
} }
static inline void 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; index_type num;
hb_buffer_clear_contents(harfbuzz_buffer); hb_buffer_clear_contents(harfbuzz_buffer);
while (num_cells) { while (num_cells) {
attrs_type prev_width = 0; 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; } if (prev_width == 2) { prev_width = 0; continue; }
shape_buffer[num++] = first_cell->ch; shape_buffer[num++] = first_cpu_cell->ch;
prev_width = first_cell->attrs & WIDTH_MASK; prev_width = first_gpu_cell->attrs & WIDTH_MASK;
for (unsigned i = 0; i < arraysz(first_cell->cc_idx) && first_cell->cc_idx[i]; i++) { 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_cell->cc_idx[i]); shape_buffer[num++] = codepoint_for_mark(first_cpu_cell->cc_idx[i]);
} }
} }
hb_buffer_add_utf32(harfbuzz_buffer, shape_buffer, num, 0, num); 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 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; cell->sprite_x = sp->x; cell->sprite_y = sp->y; cell->sprite_z = sp->z;
if (sp->colored) cell->sprite_z |= 0x4000; 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 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]; static SpritePosition* sprite_position[16];
int error = 0; int error = 0;
num_cells = MIN(sizeof(sprite_position)/sizeof(sprite_position[0]), num_cells); 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 (error != 0) { sprite_map_set_error(error); PyErr_Print(); return; }
} }
if (sprite_position[0]->rendered) { 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; return;
} }
clear_canvas(fg); 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); 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(); if (PyErr_Occurred()) PyErr_Print();
for (unsigned int i = 0; i < num_cells; i++) { for (unsigned int i = 0; i < num_cells; i++) {
sprite_position[i]->rendered = true; sprite_position[i]->rendered = true;
sprite_position[i]->colored = was_colored; 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); 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); 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 { typedef struct {
Cell *cell; CPUCell *cpu_cell;
GPUCell *gpu_cell;
unsigned int num_codepoints; unsigned int num_codepoints;
unsigned int codepoints_consumed; unsigned int codepoints_consumed;
char_type current_codepoint; char_type current_codepoint;
@@ -681,7 +682,8 @@ typedef struct {
CellData current_cell_data; CellData current_cell_data;
Group *groups; Group *groups;
size_t groups_capacity, group_idx, glyph_idx, cell_idx, num_cells, num_glyphs; 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_info_t *info;
hb_glyph_position_t *positions; hb_glyph_position_t *positions;
} GroupState; } GroupState;
@@ -689,14 +691,14 @@ typedef struct {
static GroupState group_state = {0}; static GroupState group_state = {0};
static inline unsigned int static inline unsigned int
num_codepoints_in_cell(Cell *cell) { num_codepoints_in_cell(CPUCell *cell) {
unsigned int ans = 1; unsigned int ans = 1;
for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) ans++; for (unsigned i = 0; i < arraysz(cell->cc_idx) && cell->cc_idx[i]; i++) ans++;
return ans; return ans;
} }
static inline void 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) { if (group_state.groups_capacity <= 2 * num_cells) {
group_state.groups_capacity = MAX(128, 2 * num_cells); // avoid unnecessary reallocs 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); 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.previous_cluster = UINT32_MAX;
group_state.prev_was_special = false; group_state.prev_was_special = false;
group_state.prev_was_empty = 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); memset(group_state.groups, 0, sizeof(Group) * group_state.groups_capacity);
group_state.group_idx = 0; group_state.group_idx = 0;
group_state.glyph_idx = 0; group_state.glyph_idx = 0;
group_state.cell_idx = 0; group_state.cell_idx = 0;
group_state.num_cells = num_cells; group_state.num_cells = num_cells;
group_state.first_cell = first_cell; group_state.first_cpu_cell = first_cpu_cell;
group_state.last_cell = first_cell + (num_cells ? num_cells - 1 : 0); group_state.first_gpu_cell = first_gpu_cell;
load_hb_buffer(first_cell, num_cells); 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); hb_shape(font, harfbuzz_buffer, NULL, 0);
unsigned int info_length, positions_length; unsigned int info_length, positions_length;
group_state.info = hb_buffer_get_glyph_infos(harfbuzz_buffer, &info_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 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++; cell_data->codepoints_consumed++;
if (cell_data->codepoints_consumed >= cell_data->num_codepoints) { if (cell_data->codepoints_consumed >= cell_data->num_codepoints) {
attrs_type width = cell_data->cell->attrs & WIDTH_MASK; attrs_type width = cell_data->gpu_cell->attrs & WIDTH_MASK;
cell_data->cell += MAX(1, width); cell_data->cpu_cell += MAX(1, width);
cell_data->gpu_cell += MAX(1, width);
cell_data->codepoints_consumed = 0; cell_data->codepoints_consumed = 0;
if (cell_data->cell <= last_cell) { if (cell_data->cpu_cell <= last_cpu_cell) {
cell_data->num_codepoints = num_codepoints_in_cell(cell_data->cell); cell_data->num_codepoints = num_codepoints_in_cell(cell_data->cpu_cell);
cell_data->current_codepoint = cell_data->cell->ch; cell_data->current_codepoint = cell_data->cpu_cell->ch;
} else cell_data->current_codepoint = 0; } else cell_data->current_codepoint = 0;
return width; return width;
} else { } else {
switch(cell_data->codepoints_consumed) { switch(cell_data->codepoints_consumed) {
case 0: case 0:
cell_data->current_codepoint = cell_data->cell->ch; cell_data->current_codepoint = cell_data->cpu_cell->ch;
break; break;
default: 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; break;
} }
} }
@@ -778,8 +787,8 @@ check_cell_consumed(CellData *cell_data, Cell *last_cell) {
static inline void static inline void
shape_run(Cell *first_cell, index_type num_cells, Font *font) { shape_run(CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells, Font *font) {
shape(first_cell, num_cells, harfbuzz_font_for_face(font->face)); shape(first_cpu_cell, first_gpu_cell, num_cells, harfbuzz_font_for_face(font->face));
#if 0 #if 0
// You can also generate this easily using hb-shape --show-extents --cluster-level=1 --shapers=ot /path/to/font/file text // 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); 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 { } else {
unsigned int num_cells_consumed = 0, start_cell_idx = G(cell_idx); unsigned int num_cells_consumed = 0, start_cell_idx = G(cell_idx);
while (num_codepoints_used_by_glyph && G(cell_idx) < G(num_cells)) { 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; G(cell_idx) += w;
num_cells_consumed += w; num_cells_consumed += w;
num_codepoints_used_by_glyph--; num_codepoints_used_by_glyph--;
@@ -915,7 +924,7 @@ render_groups(FontGroup *fg, Font *font) {
int last = -1; 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; } 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; 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++; idx++;
} }
} }
@@ -927,7 +936,7 @@ test_shape(PyObject UNUSED *self, PyObject *args) {
int index = 0; int index = 0;
if(!PyArg_ParseTuple(args, "O!|zi", &Line_Type, &line, &path, &index)) return NULL; if(!PyArg_ParseTuple(args, "O!|zi", &Line_Type, &line, &path, &index)) return NULL;
index_type num = 0; 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; PyObject *face = NULL;
Font *font; Font *font;
if (!num_font_groups) { PyErr_SetString(PyExc_RuntimeError, "must create at least one font group first"); return NULL; } 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; FontGroup *fg = font_groups;
font = fg->fonts + fg->medium_font_idx; 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); PyObject *ans = PyList_New(0);
unsigned int idx = 0; unsigned int idx = 0;
@@ -961,21 +970,21 @@ test_shape(PyObject UNUSED *self, PyObject *args) {
#undef G #undef G
static inline void 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) { switch(font_idx) {
default: 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(); if (pua_space_ligature) merge_groups_for_pua_space_ligature();
render_groups(fg, &fg->fonts[font_idx]); render_groups(fg, &fg->fonts[font_idx]);
break; break;
case BLANK_FONT: 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; break;
case BOX_FONT: 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; break;
case MISSING_FONT: 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; break;
} }
} }
@@ -987,32 +996,33 @@ is_private_use(char_type ch) {
void void
render_line(FONTS_DATA_HANDLE fg_, Line *line) { 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_; FontGroup *fg = (FontGroup*)fg_;
ssize_t run_font_idx = NO_FONT; ssize_t run_font_idx = NO_FONT;
index_type first_cell_in_run, i; index_type first_cell_in_run, i;
attrs_type prev_width = 0; attrs_type prev_width = 0;
for (i=0, first_cell_in_run=0; i < line->xnum; i++) { for (i=0, first_cell_in_run=0; i < line->xnum; i++) {
if (prev_width == 2) { prev_width = 0; continue; } if (prev_width == 2) { prev_width = 0; continue; }
Cell *cell = line->cells + i; CPUCell *cpu_cell = line->cpu_cells + i;
ssize_t cell_font_idx = font_for_cell(fg, cell); GPUCell *gpu_cell = line->gpu_cells + i;
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) { 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. // 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 // Ensure the space cell uses the foreground colors from the PUA cell
// This is needed because there are stupid applications like // This is needed because there are stupid applications like
// powerline that use PUA+space with different foreground colors // 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 // 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;
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; run_font_idx = NO_FONT;
first_cell_in_run = i + 2; 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++; i++;
continue; 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 == NO_FONT) run_font_idx = cell_font_idx;
if (run_font_idx == cell_font_idx) continue; if (run_font_idx == cell_font_idx) continue;
RENDER; RENDER;
@@ -1259,15 +1269,16 @@ get_fallback_font(PyObject UNUSED *self, PyObject *args) {
PyObject *text; PyObject *text;
int bold, italic; int bold, italic;
if (!PyArg_ParseTuple(args, "Upp", &text, &bold, &italic)) return NULL; if (!PyArg_ParseTuple(args, "Upp", &text, &bold, &italic)) return NULL;
Cell cell = {0}; CPUCell cpu_cell = {0};
static Py_UCS4 char_buf[2 + arraysz(cell.cc_idx)]; 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; if (!PyUnicode_AsUCS4(text, char_buf, arraysz(char_buf), 1)) return NULL;
cell.ch = char_buf[0]; cpu_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]); 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) cell.attrs |= 1 << BOLD_SHIFT; if (bold) gpu_cell.attrs |= 1 << BOLD_SHIFT;
if (italic) cell.attrs |= 1 << ITALIC_SHIFT; if (italic) gpu_cell.attrs |= 1 << ITALIC_SHIFT;
FontGroup *fg = font_groups; 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; } if (ans < 0) { PyErr_SetString(PyExc_ValueError, "Too many fallback fonts"); return NULL; }
return fg->fonts[ans].face; return fg->fonts[ans].face;
} }

View File

@@ -22,7 +22,7 @@ hb_font_t* harfbuzz_font_for_face(PyObject*);
bool set_size_for_face(PyObject*, unsigned int, bool, FONTS_DATA_HANDLE); 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*); 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); 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* specialize_font_descriptor(PyObject *base_descriptor, FONTS_DATA_HANDLE);
PyObject* face_from_path(const char *path, int index, FONTS_DATA_HANDLE); PyObject* face_from_path(const char *path, int index, FONTS_DATA_HANDLE);
PyObject* face_from_descriptor(PyObject*, FONTS_DATA_HANDLE); PyObject* face_from_descriptor(PyObject*, FONTS_DATA_HANDLE);

View File

@@ -18,9 +18,10 @@ add_segment(HistoryBuf *self) {
self->segments = PyMem_Realloc(self->segments, sizeof(HistoryBufSegment) * self->num_segments); self->segments = PyMem_Realloc(self->segments, sizeof(HistoryBufSegment) * self->num_segments);
if (self->segments == NULL) fatal("Out of memory allocating new history buffer segment"); if (self->segments == NULL) fatal("Out of memory allocating new history buffer segment");
HistoryBufSegment *s = self->segments + self->num_segments - 1; 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)); 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 static inline index_type
@@ -37,9 +38,14 @@ segment_for(HistoryBuf *self, index_type y) {
return self->segments[seg_num].which + y * stride; \ return self->segments[seg_num].which + y * stride; \
} }
static inline Cell* static inline CPUCell*
lineptr(HistoryBuf *self, index_type y) { cpu_lineptr(HistoryBuf *self, index_type y) {
seg_ptr(cells, self->xnum); 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) { if (self != NULL) {
self->xnum = xnum; self->xnum = xnum;
self->ynum = ynum; self->ynum = ynum;
self->line = alloc_line();
self->num_segments = 0; self->num_segments = 0;
add_segment(self); add_segment(self);
self->line = alloc_line();
self->line->xnum = xnum; self->line->xnum = xnum;
for(index_type y = 0; y < self->ynum; y++) { 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) { dealloc(HistoryBuf* self) {
Py_CLEAR(self->line); Py_CLEAR(self->line);
for (size_t i = 0; i < self->num_segments; i++) { 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[i].line_attrs);
} }
PyMem_Free(self->segments); PyMem_Free(self->segments);
@@ -99,7 +106,8 @@ index_of(HistoryBuf *self, index_type lnum) {
static inline void static inline void
init_line(HistoryBuf *self, index_type num, Line *l) { 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 // 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->continued = *attrptr(self, num) & CONTINUED_MASK;
l->has_dirty_text = *attrptr(self, num) & TEXT_DIRTY_MASK ? true : false; 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) { if (other->xnum == self->xnum && other->ynum == self->ynum) {
// Fast path // Fast path
for (index_type i = 0; i < self->num_segments; i++) { 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)); 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; other->count = self->count; other->start_of_data = self->start_of_data;

View File

@@ -12,19 +12,25 @@
extern PyTypeObject Line_Type; extern PyTypeObject Line_Type;
extern PyTypeObject HistoryBuf_Type; extern PyTypeObject HistoryBuf_Type;
static inline Cell* static inline CPUCell*
lineptr(LineBuf *linebuf, index_type y) { cpu_lineptr(LineBuf *linebuf, index_type y) {
return linebuf->buf + y * linebuf->xnum; 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 static inline void
clear_chars_to(LineBuf* linebuf, index_type y, char_type ch) { 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 void
linebuf_clear(LineBuf *self, char_type ch) { 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)); 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; for (index_type i = 0; i < self->ynum; i++) self->line_map[i] = i;
if (ch != 0) { if (ch != 0) {
@@ -73,14 +79,15 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
if (self != NULL) { if (self != NULL) {
self->xnum = xnum; self->xnum = xnum;
self->ynum = ynum; 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->line_map = PyMem_Calloc(ynum, sizeof(index_type));
self->scratch = 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_attrs = PyMem_Calloc(ynum, sizeof(line_attrs_type));
self->line = alloc_line(); 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(); 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); Py_CLEAR(self);
} else { } else {
self->line->xnum = xnum; self->line->xnum = xnum;
@@ -96,7 +103,8 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
static void static void
dealloc(LineBuf* self) { 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_map);
PyMem_Free(self->line_attrs); PyMem_Free(self->line_attrs);
PyMem_Free(self->scratch); PyMem_Free(self->scratch);
@@ -106,7 +114,8 @@ dealloc(LineBuf* self) {
static inline void static inline void
init_line(LineBuf *lb, Line *l, index_type ynum) { 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 void
@@ -133,13 +142,13 @@ line(LineBuf *self, PyObject *y) {
unsigned int unsigned int
linebuf_char_width_at(LineBuf *self, index_type x, index_type y) { 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 void
linebuf_set_attribute(LineBuf *self, unsigned int shift, unsigned int val) { linebuf_set_attribute(LineBuf *self, unsigned int shift, unsigned int val) {
for (index_type y = 0; y < self->ynum; y++) { 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; self->line_attrs[y] |= TEXT_DIRTY_MASK;
} }
} }
@@ -181,12 +190,14 @@ dirty_lines(LineBuf *self, PyObject *a UNUSED) {
static inline bool static inline bool
allocate_line_storage(Line *line, bool initialize) { allocate_line_storage(Line *line, bool initialize) {
if (initialize) { if (initialize) {
line->cells = PyMem_Calloc(line->xnum, sizeof(Cell)); line->cpu_cells = PyMem_Calloc(line->xnum, sizeof(CPUCell));
if (line->cells == NULL) { PyErr_NoMemory(); return false; } line->gpu_cells = PyMem_Calloc(line->xnum, sizeof(GPUCell));
if (BLANK_CHAR != 0) clear_chars_in_line(line->cells, line->xnum, BLANK_CHAR); 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 { } else {
line->cells = PyMem_Malloc(line->xnum * sizeof(Cell)); line->cpu_cells = PyMem_Malloc(line->xnum * sizeof(CPUCell));
if (line->cells == NULL) { PyErr_NoMemory(); return false; } 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; line->needs_free = 1;
return true; return true;
@@ -232,8 +243,9 @@ copy_line_to(LineBuf *self, PyObject *args) {
static inline void static inline void
clear_line_(Line *l, index_type xnum) { clear_line_(Line *l, index_type xnum) {
memset(l->cells, 0, xnum * sizeof(Cell)); memset(l->cpu_cells, 0, xnum * sizeof(CPUCell));
if (BLANK_CHAR != 0) clear_chars_in_line(l->cells, xnum, BLANK_CHAR); 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; 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) { if (other->xnum == self->xnum && other->ynum == self->ynum) {
memcpy(other->line_map, self->line_map, sizeof(index_type) * 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->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; *num_content_lines_before = self->ynum; *num_content_lines_after = self->ynum;
return; return;
} }
@@ -524,7 +537,7 @@ linebuf_rewrap(LineBuf *self, LineBuf *other, index_type *num_content_lines_befo
first = self->ynum; first = self->ynum;
do { do {
first--; 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++) { for(i = 0; i < self->xnum; i++) {
if ((cells[i].ch) != BLANK_CHAR) { is_empty = false; break; } if ((cells[i].ch) != BLANK_CHAR) { is_empty = false; break; }
} }

View File

@@ -20,7 +20,8 @@ new(PyTypeObject UNUSED *type, PyObject UNUSED *args, PyObject UNUSED *kwds) {
static void static void
dealloc(Line* self) { dealloc(Line* self) {
if (self->needs_free) { 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); Py_TYPE(self)->tp_free((PyObject*)self);
} }
@@ -29,13 +30,13 @@ unsigned int
line_length(Line *self) { line_length(Line *self) {
index_type last = self->xnum - 1; index_type last = self->xnum - 1;
for (index_type i = 0; i < self->xnum; i++) { 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; return 0;
} }
PyObject* PyObject*
cell_text(Cell *cell) { cell_text(CPUCell *cell) {
PyObject *ans; PyObject *ans;
unsigned num = 1; unsigned num = 1;
static Py_UCS4 buf[arraysz(cell->cc_idx) + 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); limit = MAX(2, limit);
if (pos < limit) return 0; if (pos < limit) return 0;
do { do {
char_type ch = self->cells[pos].ch; char_type ch = self->cpu_cells[pos].ch;
if (!is_url_char(ch)) return false; if (!is_url_char(ch)) return false;
switch(state) { switch(state) {
case ANY: 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; if (prefix_len > at) return false;
index_type p, i; index_type p, i;
for (p = at - prefix_len, i = 0; i < prefix_len && p < self->xnum; i++, p++) { 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; return i == prefix_len;
} }
@@ -108,7 +109,7 @@ static inline bool
has_url_beyond(Line *self, index_type x) { has_url_beyond(Line *self, index_type x) {
if (self->xnum <= x + MIN_URL_LEN + 3) return false; 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++) { 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; return true;
} }
@@ -134,9 +135,9 @@ index_type
line_url_end_at(Line *self, index_type x, bool check_short) { line_url_end_at(Line *self, index_type x, bool check_short) {
index_type ans = x; index_type ans = x;
if (x >= self->xnum || (check_short && self->xnum <= MIN_URL_LEN + 3)) return 0; 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--; 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; return ans;
} }
@@ -158,11 +159,11 @@ static PyObject*
text_at(Line* self, Py_ssize_t xval) { text_at(Line* self, Py_ssize_t xval) {
#define text_at_doc "[x] -> Return the text in the specified cell" #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; } 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 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; size_t n = 1;
buf[0] = cell->ch ? cell->ch : zero_char; buf[0] = cell->ch ? cell->ch : zero_char;
if (include_cc) { if (include_cc) {
@@ -172,7 +173,7 @@ cell_as_unicode(Cell *cell, bool include_cc, Py_UCS4 *buf, char_type zero_char)
} }
size_t 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); size_t n = encode_utf8(cell->ch ? cell->ch : zero_char, buf);
if (include_cc) { 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); 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]; static Py_UCS4 buf[4096];
if (leading_char) buf[n++] = leading_char; if (leading_char) buf[n++] = leading_char;
char_type previous_width = 0; char_type previous_width = 0;
for(index_type i = start; i < limit && n < arraysz(buf) - 2 - arraysz(self->cells->cc_idx); i++) { for(index_type i = start; i < limit && n < arraysz(buf) - 2 - arraysz(self->cpu_cells->cc_idx); i++) {
char_type ch = self->cells[i].ch; char_type ch = self->cpu_cells[i].ch;
if (ch == 0) { if (ch == 0) {
if (previous_width == 2) { previous_width = 0; continue; }; if (previous_width == 2) { previous_width = 0; continue; };
} }
n += cell_as_unicode(self->cells + i, include_cc, buf + n, ' '); n += cell_as_unicode(self->cpu_cells + i, include_cc, buf + n, ' ');
previous_width = self->cells[i].attrs & WIDTH_MASK; previous_width = self->gpu_cells[i].attrs & WIDTH_MASK;
} }
return PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, buf, n); 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" #define sprite_at_doc "[x] -> Return the sprite in the specified cell"
unsigned long xval = PyLong_AsUnsignedLong(x); unsigned long xval = PyLong_AsUnsignedLong(x);
if (xval >= self->xnum) { PyErr_SetString(PyExc_IndexError, "Column number out of bounds"); return NULL; } 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); 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; Cursor *cursor = &c1, *prev_cursor = &c2, *t;
for (index_type pos=0; pos < limit; pos++) { 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 (ch == 0) {
if (previous_width == 2) { previous_width = 0; continue; } if (previous_width == 2) { previous_width = 0; continue; }
ch = ' '; ch = ' ';
} }
ATTRS_TO_CURSOR(attrs, cursor); ATTRS_TO_CURSOR(attrs, cursor);
cursor->fg = self->cells[pos].fg; cursor->bg = self->cells[pos].bg; cursor->fg = self->gpu_cells[pos].fg; cursor->bg = self->gpu_cells[pos].bg;
cursor->decoration_fg = self->cells[pos].decoration_fg & COL_MASK; cursor->decoration_fg = self->gpu_cells[pos].decoration_fg & COL_MASK;
const char *sgr = cursor_as_sgr(cursor, prev_cursor); const char *sgr = cursor_as_sgr(cursor, prev_cursor);
t = prev_cursor; prev_cursor = cursor; cursor = t; t = prev_cursor; prev_cursor = cursor; cursor = t;
if (*sgr) WRITE_SGR(sgr); if (*sgr) WRITE_SGR(sgr);
WRITE_CH(ch); WRITE_CH(ch);
for(unsigned c = 0; c < arraysz(self->cells[pos].cc_idx) && self->cells[pos].cc_idx[c]; 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->cells[pos].cc_idx[c])); WRITE_CH(codepoint_for_mark(self->cpu_cells[pos].cc_idx[c]));
} }
previous_width = attrs & WIDTH_MASK; 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" #define width_doc "width(x) -> the width of the character at x"
unsigned long x = PyLong_AsUnsignedLong(val); unsigned long x = PyLong_AsUnsignedLong(val);
if (x >= self->xnum) { PyErr_SetString(PyExc_ValueError, "Out of bounds"); return NULL; } 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)); return PyLong_FromUnsignedLong((unsigned long) (attrs & WIDTH_MASK));
} }
void void
line_add_combining_char(Line *self, uint32_t ch, unsigned int x) { 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 if (!cell->ch) return; // dont allow adding combining chars to a null cell
for (unsigned i = 0; i < arraysz(cell->cc_idx); i++) { for (unsigned i = 0; i < arraysz(cell->cc_idx); i++) {
if (!cell->cc_idx[i]) { cell->cc_idx[i] = mark_for_codepoint(ch); return; } 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; color_type dfg = cursor->decoration_fg & COL_MASK;
for (index_type i = cursor->x; offset < limit && i < self->xnum; i++, offset++) { for (index_type i = cursor->x; offset < limit && i < self->xnum; i++, offset++) {
self->cells[i].ch = (PyUnicode_READ(kind, buf, offset)); self->cpu_cells[i].ch = (PyUnicode_READ(kind, buf, offset));
self->cells[i].attrs = attrs; self->gpu_cells[i].attrs = attrs;
self->cells[i].fg = fg; self->gpu_cells[i].fg = fg;
self->cells[i].bg = bg; self->gpu_cells[i].bg = bg;
self->cells[i].decoration_fg = dfg; self->gpu_cells[i].decoration_fg = dfg;
memset(self->cells[i].cc_idx, 0, sizeof(self->cells[i].cc_idx)); memset(self->cpu_cells[i].cc_idx, 0, sizeof(self->cpu_cells[i].cc_idx));
} }
Py_RETURN_NONE; Py_RETURN_NONE;
@@ -373,10 +374,10 @@ cursor_from(Line* self, PyObject *args) {
ans = alloc_cursor(); ans = alloc_cursor();
if (ans == NULL) { PyErr_NoMemory(); return NULL; } if (ans == NULL) { PyErr_NoMemory(); return NULL; }
ans->x = x; ans->y = y; 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); ATTRS_TO_CURSOR(attrs, ans);
ans->fg = self->cells[x].fg; ans->bg = self->cells[x].bg; ans->fg = self->gpu_cells[x].fg; ans->bg = self->gpu_cells[x].bg;
ans->decoration_fg = self->cells[x].decoration_fg & COL_MASK; ans->decoration_fg = self->gpu_cells[x].decoration_fg & COL_MASK;
return (PyObject*)ans; 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; attrs_type width = ch ? 1 : 0;
#define PREFIX \ #define PREFIX \
for (index_type i = at; i < MIN(self->xnum, at + num); i++) { \ 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->cpu_cells[i].ch = ch; memset(self->cpu_cells[i].cc_idx, 0, sizeof(self->cpu_cells[i].cc_idx)); \
self->cells[i].attrs = (self->cells[i].attrs & ATTRS_MASK_WITHOUT_WIDTH) | width; \ self->gpu_cells[i].attrs = (self->gpu_cells[i].attrs & ATTRS_MASK_WITHOUT_WIDTH) | width; \
} }
if (CHAR_IS_BLANK(ch)) { if (CHAR_IS_BLANK(ch)) {
PREFIX 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++) { for (index_type i = at; i < self->xnum && i < at + num; i++) {
if (clear_char) { if (clear_char) {
self->cells[i].ch = BLANK_CHAR; self->cpu_cells[i].ch = BLANK_CHAR;
memset(self->cells[i].cc_idx, 0, sizeof(self->cells[i].cc_idx)); memset(self->cpu_cells[i].cc_idx, 0, sizeof(self->cpu_cells[i].cc_idx));
self->cells[i].attrs = attrs; self->gpu_cells[i].attrs = attrs;
clear_sprite_position(self->cells[i]); clear_sprite_position(self->gpu_cells[i]);
} else { } else {
attrs_type w = self->cells[i].attrs & WIDTH_MASK; attrs_type w = self->gpu_cells[i].attrs & WIDTH_MASK;
self->cells[i].attrs = attrs | w; self->gpu_cells[i].attrs = attrs | w;
} }
self->cells[i].fg = fg; self->cells[i].bg = bg; self->gpu_cells[i].fg = fg; self->gpu_cells[i].bg = bg;
self->cells[i].decoration_fg = dfg; 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) COPY_SELF_CELL(i - num, i)
} }
// Check if a wide character was split at the right edge // 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) { if (w != 1) {
self->cells[self->xnum - 1].ch = BLANK_CHAR; self->cpu_cells[self->xnum - 1].ch = BLANK_CHAR;
self->cells[self->xnum - 1].attrs = BLANK_CHAR ? 1 : 0; self->gpu_cells[self->xnum - 1].attrs = BLANK_CHAR ? 1 : 0;
clear_sprite_position(self->cells[self->xnum - 1]); clear_sprite_position(self->gpu_cells[self->xnum - 1]);
} }
} }
@@ -483,15 +484,15 @@ left_shift(Line *self, PyObject *args) {
void void
line_set_char(Line *self, unsigned int at, uint32_t ch, unsigned int width, Cursor *cursor, bool UNUSED is_second) { line_set_char(Line *self, unsigned int at, uint32_t ch, unsigned int width, Cursor *cursor, bool UNUSED is_second) {
if (cursor == NULL) { 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 { } else {
self->cells[at].attrs = CURSOR_TO_ATTRS(cursor, width & WIDTH_MASK); self->gpu_cells[at].attrs = CURSOR_TO_ATTRS(cursor, width & WIDTH_MASK);
self->cells[at].fg = (cursor->fg & COL_MASK); self->gpu_cells[at].fg = (cursor->fg & COL_MASK);
self->cells[at].bg = (cursor->bg & COL_MASK); self->gpu_cells[at].bg = (cursor->bg & COL_MASK);
self->cells[at].decoration_fg = cursor->decoration_fg & COL_MASK; self->gpu_cells[at].decoration_fg = cursor->decoration_fg & COL_MASK;
} }
self->cells[at].ch = ch; self->cpu_cells[at].ch = ch;
memset(self->cells[at].cc_idx, 0, sizeof(self->cells[at].cc_idx)); memset(self->cpu_cells[at].cc_idx, 0, sizeof(self->cpu_cells[at].cc_idx));
} }
static PyObject* static PyObject*
@@ -516,7 +517,7 @@ set_attribute(Line *self, PyObject *args) {
unsigned int shift, val; unsigned int shift, val;
if (!PyArg_ParseTuple(args, "II", &shift, &val)) return NULL; if (!PyArg_ParseTuple(args, "II", &shift, &val)) return NULL;
if (shift < DECORATION_SHIFT || shift > DIM_SHIFT) { PyErr_SetString(PyExc_ValueError, "Unknown attribute"); 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; Py_RETURN_NONE;
} }
@@ -525,8 +526,9 @@ __len__(PyObject *self) {
return (Py_ssize_t)(((Line*)self)->xnum); return (Py_ssize_t)(((Line*)self)->xnum);
} }
static int __eq__(Line *a, Line *b) { static int
return a->xnum == b->xnum && memcmp(a->cells, b->cells, sizeof(Cell) * a->xnum) == 0; __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 {{{ // Boilerplate {{{

View File

@@ -9,7 +9,7 @@
#include "data-types.h" #include "data-types.h"
static inline void 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 // Set a single attribute on all cells in the line
attrs_type mask = shift == DECORATION_SHIFT ? 3 : 1; attrs_type mask = shift == DECORATION_SHIFT ? 3 : 1;
attrs_type aval = (val & mask) << shift; 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; 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 static inline void
copy_line(const Line *src, Line *dest) { 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 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 // Clear only the char part of each cell, the rest must have been cleared by a memset or similar
if (ch) { 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) { xlimit_for_line(Line *line) {
index_type xlimit = line->xnum; index_type xlimit = line->xnum;
if (BLANK_CHAR == 0) { 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; 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_url_end_at(Line *self, index_type x, bool);
index_type line_as_ansi(Line *self, Py_UCS4 *buf, index_type buflen); index_type line_as_ansi(Line *self, Py_UCS4 *buf, index_type buflen);
unsigned int line_length(Line *self); 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_unicode(CPUCell *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_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* unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc, char leading_char);
PyObject* line_as_unicode(Line *); PyObject* line_as_unicode(Line *);

View File

@@ -43,7 +43,8 @@
static inline void static inline void
copy_range(Line *src, index_type src_at, Line* dest, index_type dest_at, index_type num) { 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; src_x_limit = src->xnum;
if (!src_line_is_continued) { if (!src_line_is_continued) {
// Trim trailing blanks since there is a hard line break at the end of this line // 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; if (is_tracked_line && *track_x >= src_x_limit) *track_x = MAX(1, src_x_limit) - 1;

View File

@@ -280,9 +280,10 @@ screen_designate_charset(Screen *self, uint32_t which, uint32_t as) {
} }
static inline void 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; 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); line_clear_text(self->linebuf->line, xpos, 1, BLANK_CHAR);
if (self->modes.mDECAWM) { // overflow goes onto next line 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); screen_linefeed(self);
self->linebuf->line_attrs[self->cursor->y] |= CONTINUED_MASK; self->linebuf->line_attrs[self->cursor->y] |= CONTINUED_MASK;
linebuf_init_line(self->linebuf, self->cursor->y); 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); self->cursor->x = MIN(2, self->columns);
linebuf_mark_line_dirty(self->linebuf, self->cursor->y); linebuf_mark_line_dirty(self->linebuf, self->cursor->y);
} else { } else {
dest = cell - 1; dest_cpu = cpu_cell - 1;
dest_gpu = gpu_cell - 1;
self->cursor->x = self->columns; self->cursor->x = self->columns;
} }
*dest = src; *dest_cpu = src_cpu;
*dest_gpu = src_gpu;
} }
static inline void static inline void
@@ -320,10 +324,11 @@ draw_combining_char(Screen *self, char_type ch) {
self->is_dirty = true; self->is_dirty = true;
linebuf_mark_line_dirty(self->linebuf, ypos); linebuf_mark_line_dirty(self->linebuf, ypos);
if (ch == 0xfe0f) { // emoji presentation variation marker makes default text presentation emoji (narrow emoji) into wide emoji if (ch == 0xfe0f) { // emoji presentation variation marker makes default text presentation emoji (narrow emoji) into wide emoji
Cell *cell = self->linebuf->line->cells + xpos; CPUCell *cpu_cell = self->linebuf->line->cpu_cells + xpos;
if ((cell->attrs & WIDTH_MASK) != 2 && cell->cc_idx[0] == VS16 && is_emoji_presentation_base(cell->ch)) { GPUCell *gpu_cell = self->linebuf->line->gpu_cells + xpos;
cell->attrs = (cell->attrs & !WIDTH_MASK) | 2; if ((gpu_cell->attrs & WIDTH_MASK) != 2 && cpu_cell->cc_idx[0] == VS16 && is_emoji_presentation_base(cpu_cell->ch)) {
if (xpos == self->columns - 1) move_widened_char(self, cell, xpos, ypos); 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++; 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); num = MIN(num, self->columns - x);
for (index_type y = region.top; y < MIN(region.bottom + 1, self->lines); y++) { for (index_type y = region.top; y < MIN(region.bottom + 1, self->lines); y++) {
linebuf_init_line(self->linebuf, 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 { } else {
index_type x, num; 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 if (y == region.bottom) { x = 0; num = MIN(region.right + 1, self->columns); }
else { x = 0; num = self->columns; } else { x = 0; num = self->columns; }
linebuf_init_line(self->linebuf, 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 cursor_from_sgr(self->cursor, params, count); } else cursor_from_sgr(self->cursor, params, count);
@@ -1280,8 +1285,8 @@ screen_request_capabilities(Screen *self, char c, PyObject *q) {
// Rendering {{{ // Rendering {{{
static inline void static inline void
update_line_data(Line *line, unsigned int dest_y, uint8_t *data) { update_line_data(Line *line, unsigned int dest_y, uint8_t *data) {
size_t base = dest_y * line->xnum * sizeof(Cell); size_t base = dest_y * line->xnum * sizeof(GPUCell);
memcpy(data + base, line->cells, line->xnum * sizeof(Cell)); 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; } if (y >= self->lines) { return false; }
Line *line = visual_line_(self, y); Line *line = visual_line_(self, y);
index_type xlimit = line->xnum, xstart = 0; index_type xlimit = line->xnum, xstart = 0;
while (xlimit > 0 && CHAR_IS_BLANK(line->cells[xlimit - 1].ch)) xlimit--; while (xlimit > 0 && CHAR_IS_BLANK(line->cpu_cells[xlimit - 1].ch)) xlimit--;
while (xstart < xlimit && CHAR_IS_BLANK(line->cells[xstart].ch)) xstart++; while (xstart < xlimit && CHAR_IS_BLANK(line->cpu_cells[xstart].ch)) xstart++;
*start = xstart; *end = xlimit; *start = xstart; *end = xlimit;
return true; return true;
} }
@@ -1738,7 +1743,7 @@ screen_selection_range_for_word(Screen *self, index_type x, index_type *y1, inde
index_type start, end; index_type start, end;
Line *line = visual_line_(self, *y1); Line *line = visual_line_(self, *y1);
*y2 = *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)) { if (!is_ok(x)) {
start = x; end = x + 1; start = x; end = x + 1;
} else { } else {

View File

@@ -183,7 +183,7 @@ create_cell_vao() {
#define A(name, size, dtype, offset, stride) \ #define A(name, size, dtype, offset, stride) \
add_attribute_to_vao(CELL_PROGRAM, vao_idx, #name, \ add_attribute_to_vao(CELL_PROGRAM, vao_idx, #name, \
/*size=*/size, /*dtype=*/dtype, /*stride=*/stride, /*offset=*/offset, /*divisor=*/1); /*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); add_buffer_to_vao(vao_idx, GL_ARRAY_BUFFER);
A1(sprite_coords, 4, GL_UNSIGNED_SHORT, sprite_x); 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); ensure_sprite_map(fonts_data);
if (screen->scroll_changed || screen->is_dirty) { 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); 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); screen_update_cell_data(screen, address, sz, fonts_data);
unmap_vao_buffer(vao_idx, cell_data_buffer); address = NULL; unmap_vao_buffer(vao_idx, cell_data_buffer); address = NULL;