mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-21 16:05:02 +02:00
Move the update_cell_data() inner loop to native code
This commit is contained in:
@@ -235,8 +235,7 @@ class Boss(Thread):
|
|||||||
|
|
||||||
def apply_update_screen(self):
|
def apply_update_screen(self):
|
||||||
self.pending_update_screen = None
|
self.pending_update_screen = None
|
||||||
changes = self.screen.consolidate_changes()
|
self.char_grid.update_cell_data()
|
||||||
self.char_grid.update_cell_data(changes)
|
|
||||||
glfw.glfwPostEmptyEvent()
|
glfw.glfwPostEmptyEvent()
|
||||||
|
|
||||||
def title_changed(self, new_title):
|
def title_changed(self, new_title):
|
||||||
|
|||||||
@@ -141,6 +141,10 @@ class RenderData:
|
|||||||
empty_cell = (' ', 0)
|
empty_cell = (' ', 0)
|
||||||
|
|
||||||
|
|
||||||
|
def color_as_int(val):
|
||||||
|
return val[0] << 16 | val[1] << 8 | val[2]
|
||||||
|
|
||||||
|
|
||||||
class CharGrid:
|
class CharGrid:
|
||||||
|
|
||||||
def __init__(self, screen, opts, window_width, window_height):
|
def __init__(self, screen, opts, window_width, window_height):
|
||||||
@@ -161,7 +165,7 @@ class CharGrid:
|
|||||||
self.last_render_data = RenderData()
|
self.last_render_data = RenderData()
|
||||||
self.default_cursor = Cursor(0, 0, False, opts.cursor_shape, opts.cursor, opts.cursor_blink)
|
self.default_cursor = Cursor(0, 0, False, opts.cursor_shape, opts.cursor, opts.cursor_blink)
|
||||||
self.render_queue.put(RenderData(
|
self.render_queue.put(RenderData(
|
||||||
viewport=Size(self.width, self.height), clear_color=self.original_bg,
|
viewport=Size(self.width, self.height), clear_color=color_as_int(self.original_bg),
|
||||||
cursor=self.default_cursor))
|
cursor=self.default_cursor))
|
||||||
self.sprites.ensure_state()
|
self.sprites.ensure_state()
|
||||||
self.clear_count = 4
|
self.clear_count = 4
|
||||||
@@ -170,7 +174,8 @@ class CharGrid:
|
|||||||
self.sprites.destroy()
|
self.sprites.destroy()
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
self.default_bg, self.default_fg = self.original_bg, self.original_fg
|
self.default_bg = color_as_int(self.original_bg)
|
||||||
|
self.default_fg = color_as_int(self.original_fg)
|
||||||
self.apply_opts(self.opts)
|
self.apply_opts(self.opts)
|
||||||
|
|
||||||
def apply_opts(self, opts):
|
def apply_opts(self, opts):
|
||||||
@@ -202,51 +207,30 @@ class CharGrid:
|
|||||||
for which, val in changes.items():
|
for which, val in changes.items():
|
||||||
if which in ('fg', 'bg'):
|
if which in ('fg', 'bg'):
|
||||||
if not val:
|
if not val:
|
||||||
setattr(self, 'default_' + which, getattr(self, 'original_' + which))
|
setattr(self, 'default_' + which, color_as_int(getattr(self, 'original_' + which)))
|
||||||
dirtied = True
|
dirtied = True
|
||||||
else:
|
else:
|
||||||
val = to_color(val)
|
val = to_color(val)
|
||||||
if val is not None:
|
if val is not None:
|
||||||
setattr(self, 'default_' + which, val)
|
setattr(self, 'default_' + which, color_as_int(val))
|
||||||
dirtied = True
|
dirtied = True
|
||||||
if dirtied:
|
if dirtied:
|
||||||
self.render_queue.put(RenderData(clear_color=self.default_bg))
|
self.render_queue.put(RenderData(clear_color=self.default_bg))
|
||||||
self.clear_count = 4
|
self.clear_count = 4
|
||||||
|
|
||||||
def update_cell_data(self, changes=None, add_viewport_data=False):
|
def update_cell_data(self, add_viewport_data=False):
|
||||||
rd = RenderData(sprite_layout=self.sprites.layout)
|
rd = RenderData(sprite_layout=self.sprites.layout)
|
||||||
if add_viewport_data:
|
if add_viewport_data:
|
||||||
rd.viewport = Size(self.width, self.height)
|
rd.viewport = Size(self.width, self.height)
|
||||||
rd.screen_geometry = self.screen_geometry
|
rd.screen_geometry = self.screen_geometry
|
||||||
if changes is None:
|
ptr = addressof(self.sprite_map)
|
||||||
changes = {'screen': True}
|
with self.lock:
|
||||||
sg = self.screen_geometry
|
cursor_changed = self.screen.update_cell_data(
|
||||||
cell_data_changed = changes['screen'] or changes['cells'] or changes['lines']
|
self.sprites.backend, self.color_profile, ptr, self.default_fg, self.default_bg, add_viewport_data)
|
||||||
if cell_data_changed:
|
|
||||||
if changes['screen']:
|
|
||||||
lines = range(sg.ynum)
|
|
||||||
cell_ranges = {}
|
|
||||||
else:
|
|
||||||
lines = changes['lines']
|
|
||||||
cell_ranges = changes['cells']
|
|
||||||
|
|
||||||
dfbg = self.default_bg
|
rd.cell_data = copy(self.sprite_map)
|
||||||
dffg = self.default_fg
|
rd.sprite_layout = self.sprites.layout
|
||||||
dfbg = dfbg[0] << 16 | dfbg[1] << 8 | dfbg[2]
|
if cursor_changed:
|
||||||
dffg = dffg[0] << 16 | dffg[1] << 8 | dffg[2]
|
|
||||||
ptr = addressof(self.sprite_map)
|
|
||||||
|
|
||||||
with self.lock:
|
|
||||||
for y in lines:
|
|
||||||
self.update_line(y, [(0, sg.xnum - 1)], dffg, dfbg, ptr)
|
|
||||||
|
|
||||||
for y, ranges in cell_ranges.items():
|
|
||||||
self.update_line(y, ranges, dffg, dfbg, ptr)
|
|
||||||
|
|
||||||
rd.cell_data = copy(self.sprite_map)
|
|
||||||
rd.sprite_layout = self.sprites.layout
|
|
||||||
c = changes.get('cursor')
|
|
||||||
if c:
|
|
||||||
c = self.screen.cursor
|
c = self.screen.cursor
|
||||||
rd.cursor = Cursor(c.x, c.y, c.hidden, c.shape, c.color, c.blink)
|
rd.cursor = Cursor(c.x, c.y, c.hidden, c.shape, c.color, c.blink)
|
||||||
self.render_queue.put(rd)
|
self.render_queue.put(rd)
|
||||||
@@ -287,7 +271,7 @@ class CharGrid:
|
|||||||
cell_data_changed |= rd.cell_data is not None
|
cell_data_changed |= rd.cell_data is not None
|
||||||
if rd.clear_color is not None:
|
if rd.clear_color is not None:
|
||||||
bg = rd.clear_color
|
bg = rd.clear_color
|
||||||
glClearColor(bg[0] / 255, bg[1] / 255, bg[2] / 255, 1)
|
glClearColor((bg >> 16) / 255, ((bg >> 8) & 0xff) / 255, (bg & 0xff) / 255, 1)
|
||||||
if rd.viewport is not None:
|
if rd.viewport is not None:
|
||||||
glViewport(0, 0, self.width, self.height)
|
glViewport(0, 0, self.width, self.height)
|
||||||
data.update(rd)
|
data.update(rd)
|
||||||
|
|||||||
@@ -90,6 +90,13 @@ dealloc(LineBuf* self) {
|
|||||||
(l)->decoration_fg = (lb)->decoration_fg + (ynum) * (lb)->xnum; \
|
(l)->decoration_fg = (lb)->decoration_fg + (ynum) * (lb)->xnum; \
|
||||||
(l)->combining_chars = (lb)->combining_chars + (ynum) * (lb)->xnum;
|
(l)->combining_chars = (lb)->combining_chars + (ynum) * (lb)->xnum;
|
||||||
|
|
||||||
|
void linebuf_init_line(LineBuf *self, index_type idx) {
|
||||||
|
self->line->ynum = idx;
|
||||||
|
self->line->xnum = self->xnum;
|
||||||
|
self->line->continued = self->continued_map[idx];
|
||||||
|
INIT_LINE(self, self->line, self->line_map[idx]);
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
line(LineBuf *self, PyObject *y) {
|
line(LineBuf *self, PyObject *y) {
|
||||||
#define line_doc "Return the specified line as a Line object. Note the Line Object is a live view into the underlying buffer. And only a single line object can be used at a time."
|
#define line_doc "Return the specified line as a Line object. Note the Line Object is a live view into the underlying buffer. And only a single line object can be used at a time."
|
||||||
@@ -98,10 +105,7 @@ line(LineBuf *self, PyObject *y) {
|
|||||||
PyErr_SetString(PyExc_IndexError, "Line number too large");
|
PyErr_SetString(PyExc_IndexError, "Line number too large");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
self->line->ynum = idx;
|
linebuf_init_line(self, idx);
|
||||||
self->line->xnum = self->xnum;
|
|
||||||
self->line->continued = self->continued_map[idx];
|
|
||||||
INIT_LINE(self, self->line, self->line_map[idx]);
|
|
||||||
Py_INCREF(self->line);
|
Py_INCREF(self->line);
|
||||||
return (PyObject*)self->line;
|
return (PyObject*)self->line;
|
||||||
}
|
}
|
||||||
@@ -351,7 +355,7 @@ static PyMemberDef members[] = {
|
|||||||
{NULL} /* Sentinel */
|
{NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
static PyTypeObject LineBuf_Type = {
|
PyTypeObject LineBuf_Type = {
|
||||||
PyVarObject_HEAD_INIT(NULL, 0)
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
.tp_name = "fast_data_types.LineBuf",
|
.tp_name = "fast_data_types.LineBuf",
|
||||||
.tp_basicsize = sizeof(LineBuf),
|
.tp_basicsize = sizeof(LineBuf),
|
||||||
|
|||||||
@@ -87,6 +87,9 @@ class Screen:
|
|||||||
def line(self, i):
|
def line(self, i):
|
||||||
return self.linebuf.line(i)
|
return self.linebuf.line(i)
|
||||||
|
|
||||||
|
def update_cell_data(self, *a):
|
||||||
|
return self.tracker.update_cell_data(self.linebuf, *a)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return ("{0}({1}, {2})".format(self.__class__.__name__,
|
return ("{0}({1}, {2})".format(self.__class__.__name__,
|
||||||
self.columns, self.lines))
|
self.columns, self.lines))
|
||||||
|
|||||||
@@ -46,12 +46,10 @@ class Sprites:
|
|||||||
self.texture_id = self.buffer_id = self.buffer_texture_id = None
|
self.texture_id = self.buffer_id = self.buffer_texture_id = None
|
||||||
self.last_num_of_layers = 1
|
self.last_num_of_layers = 1
|
||||||
self.last_ynum = -1
|
self.last_ynum = -1
|
||||||
self.update_cell_data = lambda *a: None
|
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
self.texture_unit = GL_TEXTURE0
|
self.texture_unit = GL_TEXTURE0
|
||||||
self.backend = SpriteMap(glGetIntegerv(GL_MAX_TEXTURE_SIZE), glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS))
|
self.backend = SpriteMap(glGetIntegerv(GL_MAX_TEXTURE_SIZE), glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS))
|
||||||
self.update_cell_data = self.backend.update_cell_data
|
|
||||||
self.do_layout(getattr(self, 'cell_width', 1), getattr(self, 'cell_height', 1))
|
self.do_layout(getattr(self, 'cell_width', 1), getattr(self, 'cell_height', 1))
|
||||||
|
|
||||||
def do_layout(self, cell_width=1, cell_height=1):
|
def do_layout(self, cell_width=1, cell_height=1):
|
||||||
|
|||||||
@@ -133,32 +133,22 @@ position_for(SpriteMap *self, PyObject *args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyObject*
|
bool
|
||||||
update_cell_data(SpriteMap *self, PyObject *args) {
|
update_cell_range_data(SpriteMap *self, Line *line, unsigned int xstart, unsigned int xmax, ColorProfile *color_profile, const uint32_t default_bg, const uint32_t default_fg, unsigned int *data) {
|
||||||
#define update_cell_data_doc "update_cell_data(line, xstart, xmax, color_profile, default_bg, default_fg, data_pointer) -> Update the range [xstart, xmax] in data_pointer with the data from line"
|
#define update_cell_data_doc "update_cell_data(line, xstart, xmax, color_profile, default_bg, default_fg, data_pointer) -> Update the range [xstart, xmax] in data_pointer with the data from line"
|
||||||
Line *line;
|
|
||||||
unsigned int xstart, xlimit;
|
|
||||||
SpritePosition *sp;
|
SpritePosition *sp;
|
||||||
PyObject *dp;
|
|
||||||
char_type previous_ch=0, ch;
|
char_type previous_ch=0, ch;
|
||||||
color_type color;
|
color_type color;
|
||||||
uint32_t bg, fg;
|
uint32_t bg, fg;
|
||||||
uint8_t previous_width = 0;
|
uint8_t previous_width = 0;
|
||||||
ColorProfile *color_profile;
|
|
||||||
unsigned long default_bg, default_fg;
|
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "O!IIO!kkO!", &Line_Type, &line, &xstart, &xlimit, &ColorProfile_Type, &color_profile, &default_bg, &default_fg, &PyLong_Type, &dp)) return NULL;
|
|
||||||
|
|
||||||
unsigned int *data = PyLong_AsVoidPtr(dp);
|
|
||||||
size_t base = line->ynum * line->xnum * 9;
|
size_t base = line->ynum * line->xnum * 9;
|
||||||
default_fg &= COL_MASK;
|
for (size_t i = xstart, offset = base + xstart * 9; i <= xmax; i++, offset += 9) {
|
||||||
default_bg &= COL_MASK;
|
|
||||||
for (size_t i = xstart, offset = base + xstart * 9; i <= xlimit; i++, offset += 9) {
|
|
||||||
ch = line->chars[i];
|
ch = line->chars[i];
|
||||||
if (previous_width == 2) sp = sprite_position_for(self, previous_ch, 0, true, &err);
|
if (previous_width == 2) sp = sprite_position_for(self, previous_ch, 0, true, &err);
|
||||||
else sp = sprite_position_for(self, ch, line->combining_chars[i], false, &err);
|
else sp = sprite_position_for(self, ch, line->combining_chars[i], false, &err);
|
||||||
if (sp == NULL) { set_sprite_error(err); return NULL; }
|
if (sp == NULL) { set_sprite_error(err); return false; }
|
||||||
data[offset] = sp->x;
|
data[offset] = sp->x;
|
||||||
data[offset+1] = sp->y;
|
data[offset+1] = sp->y;
|
||||||
data[offset+2] = sp->z;
|
data[offset+2] = sp->z;
|
||||||
@@ -170,8 +160,7 @@ update_cell_data(SpriteMap *self, PyObject *args) {
|
|||||||
PACK_COL(offset + 3, fg);
|
PACK_COL(offset + 3, fg);
|
||||||
PACK_COL(offset + 6, bg);
|
PACK_COL(offset + 6, bg);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
@@ -222,13 +211,12 @@ static PyMemberDef members[] = {
|
|||||||
static PyMethodDef methods[] = {
|
static PyMethodDef methods[] = {
|
||||||
METHOD(layout, METH_VARARGS)
|
METHOD(layout, METH_VARARGS)
|
||||||
METHOD(position_for, METH_VARARGS)
|
METHOD(position_for, METH_VARARGS)
|
||||||
METHOD(update_cell_data, METH_VARARGS)
|
|
||||||
METHOD(render_dirty_cells, METH_VARARGS)
|
METHOD(render_dirty_cells, METH_VARARGS)
|
||||||
{NULL} /* Sentinel */
|
{NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static PyTypeObject SpriteMap_Type = {
|
PyTypeObject SpriteMap_Type = {
|
||||||
PyVarObject_HEAD_INIT(NULL, 0)
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
.tp_name = "fast_data_types.SpriteMap",
|
.tp_name = "fast_data_types.SpriteMap",
|
||||||
.tp_basicsize = sizeof(SpriteMap),
|
.tp_basicsize = sizeof(SpriteMap),
|
||||||
|
|||||||
@@ -7,7 +7,11 @@
|
|||||||
|
|
||||||
#include "data-types.h"
|
#include "data-types.h"
|
||||||
#include "tracker.h"
|
#include "tracker.h"
|
||||||
#include <structmember.h>
|
extern PyTypeObject SpriteMap_Type;
|
||||||
|
extern PyTypeObject ColorProfile_Type;
|
||||||
|
extern PyTypeObject LineBuf_Type;
|
||||||
|
extern bool update_cell_range_data(SpriteMap *, Line *, unsigned int, unsigned int, ColorProfile *, const uint32_t, const uint32_t, unsigned int *);
|
||||||
|
extern void linebuf_init_line(LineBuf *, index_type);
|
||||||
|
|
||||||
#define RESET_STATE_VARS(self) \
|
#define RESET_STATE_VARS(self) \
|
||||||
self->screen_changed = false; self->cursor_changed = false; self->dirty = false; self->history_line_added_count = 0;
|
self->screen_changed = false; self->cursor_changed = false; self->dirty = false; self->history_line_added_count = 0;
|
||||||
@@ -104,6 +108,62 @@ update_cell_range(ChangeTracker *self, PyObject *args) {
|
|||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
update_cell_data(ChangeTracker *self, PyObject *args) {
|
||||||
|
#define update_cell_data_doc "update_cell_data(line_buf, sprite_map, color_profile, data_ptr, default_fg, default_bg, force_screen_refresh)"
|
||||||
|
SpriteMap *spm;
|
||||||
|
LineBuf *lb;
|
||||||
|
ColorProfile *color_profile;
|
||||||
|
PyObject *dp;
|
||||||
|
unsigned int *data, y;
|
||||||
|
unsigned long default_bg, default_fg;
|
||||||
|
Py_ssize_t start;
|
||||||
|
int force_screen_refresh;
|
||||||
|
if (!PyArg_ParseTuple(args, "O!O!O!O!kkp", &LineBuf_Type, &lb, &SpriteMap_Type, &spm, &ColorProfile_Type, &color_profile, &PyLong_Type, &dp, &default_fg, &default_bg, &force_screen_refresh)) return NULL;
|
||||||
|
data = PyLong_AsVoidPtr(dp);
|
||||||
|
default_fg &= COL_MASK;
|
||||||
|
default_bg &= COL_MASK;
|
||||||
|
|
||||||
|
#define UPDATE_RANGE(xstart, xmax) \
|
||||||
|
linebuf_init_line(lb, y); \
|
||||||
|
if (!update_cell_range_data(spm, lb->line, (xstart), (xmax), color_profile, default_bg, default_fg, data)) return NULL;
|
||||||
|
|
||||||
|
if (self->screen_changed || force_screen_refresh) {
|
||||||
|
for (y = 0; y < self->ynum; y++) {
|
||||||
|
UPDATE_RANGE(0, self->xnum - 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (y = 0; y < self->ynum; y++) {
|
||||||
|
if (self->changed_lines[y]) {
|
||||||
|
UPDATE_RANGE(0, self->xnum - 1);
|
||||||
|
} else if (self->lines_with_changed_cells[y]) {
|
||||||
|
start = -1;
|
||||||
|
bool *line = self->changed_cells + y * self->xnum;
|
||||||
|
for (unsigned int i = 0; i < self->xnum; i++) {
|
||||||
|
if (line[i]) {
|
||||||
|
if (start == -1) {
|
||||||
|
start = i;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (start != -1) {
|
||||||
|
UPDATE_RANGE(start, i - 1);
|
||||||
|
start = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (start != -1) {
|
||||||
|
UPDATE_RANGE(start, self->xnum - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *cursor_changed = self->cursor_changed ? Py_True : Py_False;
|
||||||
|
reset_inner(self);
|
||||||
|
Py_INCREF(cursor_changed);
|
||||||
|
return cursor_changed;
|
||||||
|
}
|
||||||
|
|
||||||
static inline PyObject*
|
static inline PyObject*
|
||||||
get_ranges(bool *line, unsigned int xnum) {
|
get_ranges(bool *line, unsigned int xnum) {
|
||||||
PyObject *ans = PyList_New(0), *t;
|
PyObject *ans = PyList_New(0), *t;
|
||||||
@@ -199,6 +259,7 @@ static PyGetSetDef getseters[] = {
|
|||||||
|
|
||||||
static PyMethodDef methods[] = {
|
static PyMethodDef methods[] = {
|
||||||
METHOD(resize, METH_VARARGS)
|
METHOD(resize, METH_VARARGS)
|
||||||
|
METHOD(update_cell_data, METH_VARARGS)
|
||||||
METHOD(reset, METH_NOARGS)
|
METHOD(reset, METH_NOARGS)
|
||||||
METHOD(cursor_changed, METH_NOARGS)
|
METHOD(cursor_changed, METH_NOARGS)
|
||||||
METHOD(consolidate_changes, METH_NOARGS)
|
METHOD(consolidate_changes, METH_NOARGS)
|
||||||
|
|||||||
Reference in New Issue
Block a user