Move the update_cell_data() inner loop to native code

This commit is contained in:
Kovid Goyal 2016-11-12 10:57:06 +05:30
parent dbd4bab212
commit c8a71ef5f8
7 changed files with 99 additions and 62 deletions

View File

@ -235,8 +235,7 @@ class Boss(Thread):
def apply_update_screen(self):
self.pending_update_screen = None
changes = self.screen.consolidate_changes()
self.char_grid.update_cell_data(changes)
self.char_grid.update_cell_data()
glfw.glfwPostEmptyEvent()
def title_changed(self, new_title):

View File

@ -141,6 +141,10 @@ class RenderData:
empty_cell = (' ', 0)
def color_as_int(val):
return val[0] << 16 | val[1] << 8 | val[2]
class CharGrid:
def __init__(self, screen, opts, window_width, window_height):
@ -161,7 +165,7 @@ class CharGrid:
self.last_render_data = RenderData()
self.default_cursor = Cursor(0, 0, False, opts.cursor_shape, opts.cursor, opts.cursor_blink)
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))
self.sprites.ensure_state()
self.clear_count = 4
@ -170,7 +174,8 @@ class CharGrid:
self.sprites.destroy()
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)
def apply_opts(self, opts):
@ -202,51 +207,30 @@ class CharGrid:
for which, val in changes.items():
if which in ('fg', 'bg'):
if not val:
setattr(self, 'default_' + which, getattr(self, 'original_' + which))
setattr(self, 'default_' + which, color_as_int(getattr(self, 'original_' + which)))
dirtied = True
else:
val = to_color(val)
if val is not None:
setattr(self, 'default_' + which, val)
setattr(self, 'default_' + which, color_as_int(val))
dirtied = True
if dirtied:
self.render_queue.put(RenderData(clear_color=self.default_bg))
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)
if add_viewport_data:
rd.viewport = Size(self.width, self.height)
rd.screen_geometry = self.screen_geometry
if changes is None:
changes = {'screen': True}
sg = self.screen_geometry
cell_data_changed = changes['screen'] or changes['cells'] or changes['lines']
if cell_data_changed:
if changes['screen']:
lines = range(sg.ynum)
cell_ranges = {}
else:
lines = changes['lines']
cell_ranges = changes['cells']
ptr = addressof(self.sprite_map)
with self.lock:
cursor_changed = self.screen.update_cell_data(
self.sprites.backend, self.color_profile, ptr, self.default_fg, self.default_bg, add_viewport_data)
dfbg = self.default_bg
dffg = self.default_fg
dfbg = dfbg[0] << 16 | dfbg[1] << 8 | dfbg[2]
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:
rd.cell_data = copy(self.sprite_map)
rd.sprite_layout = self.sprites.layout
if cursor_changed:
c = self.screen.cursor
rd.cursor = Cursor(c.x, c.y, c.hidden, c.shape, c.color, c.blink)
self.render_queue.put(rd)
@ -287,7 +271,7 @@ class CharGrid:
cell_data_changed |= rd.cell_data is not None
if rd.clear_color is not None:
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:
glViewport(0, 0, self.width, self.height)
data.update(rd)

View File

@ -90,6 +90,13 @@ dealloc(LineBuf* self) {
(l)->decoration_fg = (lb)->decoration_fg + (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*
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."
@ -98,10 +105,7 @@ line(LineBuf *self, PyObject *y) {
PyErr_SetString(PyExc_IndexError, "Line number too large");
return NULL;
}
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]);
linebuf_init_line(self, idx);
Py_INCREF(self->line);
return (PyObject*)self->line;
}
@ -351,7 +355,7 @@ static PyMemberDef members[] = {
{NULL} /* Sentinel */
};
static PyTypeObject LineBuf_Type = {
PyTypeObject LineBuf_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "fast_data_types.LineBuf",
.tp_basicsize = sizeof(LineBuf),

View File

@ -87,6 +87,9 @@ class Screen:
def line(self, i):
return self.linebuf.line(i)
def update_cell_data(self, *a):
return self.tracker.update_cell_data(self.linebuf, *a)
def __repr__(self):
return ("{0}({1}, {2})".format(self.__class__.__name__,
self.columns, self.lines))

View File

@ -46,12 +46,10 @@ class Sprites:
self.texture_id = self.buffer_id = self.buffer_texture_id = None
self.last_num_of_layers = 1
self.last_ynum = -1
self.update_cell_data = lambda *a: None
def initialize(self):
self.texture_unit = GL_TEXTURE0
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))
def do_layout(self, cell_width=1, cell_height=1):

View File

@ -133,32 +133,22 @@ position_for(SpriteMap *self, PyObject *args) {
}
static PyObject*
update_cell_data(SpriteMap *self, PyObject *args) {
bool
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"
Line *line;
unsigned int xstart, xlimit;
SpritePosition *sp;
PyObject *dp;
char_type previous_ch=0, ch;
color_type color;
uint32_t bg, fg;
uint8_t previous_width = 0;
ColorProfile *color_profile;
unsigned long default_bg, default_fg;
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;
default_fg &= COL_MASK;
default_bg &= COL_MASK;
for (size_t i = xstart, offset = base + xstart * 9; i <= xlimit; i++, offset += 9) {
for (size_t i = xstart, offset = base + xstart * 9; i <= xmax; i++, offset += 9) {
ch = line->chars[i];
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);
if (sp == NULL) { set_sprite_error(err); return NULL; }
if (sp == NULL) { set_sprite_error(err); return false; }
data[offset] = sp->x;
data[offset+1] = sp->y;
data[offset+2] = sp->z;
@ -170,8 +160,7 @@ update_cell_data(SpriteMap *self, PyObject *args) {
PACK_COL(offset + 3, fg);
PACK_COL(offset + 6, bg);
}
Py_RETURN_NONE;
return true;
}
static PyObject*
@ -222,13 +211,12 @@ static PyMemberDef members[] = {
static PyMethodDef methods[] = {
METHOD(layout, METH_VARARGS)
METHOD(position_for, METH_VARARGS)
METHOD(update_cell_data, METH_VARARGS)
METHOD(render_dirty_cells, METH_VARARGS)
{NULL} /* Sentinel */
};
static PyTypeObject SpriteMap_Type = {
PyTypeObject SpriteMap_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "fast_data_types.SpriteMap",
.tp_basicsize = sizeof(SpriteMap),

View File

@ -7,7 +7,11 @@
#include "data-types.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) \
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;
}
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*
get_ranges(bool *line, unsigned int xnum) {
PyObject *ans = PyList_New(0), *t;
@ -199,6 +259,7 @@ static PyGetSetDef getseters[] = {
static PyMethodDef methods[] = {
METHOD(resize, METH_VARARGS)
METHOD(update_cell_data, METH_VARARGS)
METHOD(reset, METH_NOARGS)
METHOD(cursor_changed, METH_NOARGS)
METHOD(consolidate_changes, METH_NOARGS)