Pass cell data to GPU directly

Converts update_cell_range() to a simple memcpy(). The GPU has to do
roughly the same amount of work (there is one extra attribute, and one
extra calculation for reverse video).
This commit is contained in:
Kovid Goyal 2017-09-08 20:40:34 +05:30
parent 51231b7eb2
commit b8acb9b133
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
8 changed files with 42 additions and 57 deletions

View File

@ -7,6 +7,7 @@ uniform uvec4 default_colors; // The default colors
uniform ColorTable {
uint color_table[256]; // The color table
};
in uint text_attrs;
in uvec3 sprite_coords;
in uvec3 colors;
in float is_selected;
@ -24,11 +25,13 @@ const uvec2 pos_map[] = uvec2[4](
uvec2(0, 0) // left, top
);
const uint strike_map[] = uint[2](uint(0), uint(3));
const uint BYTE_MASK = uint(0xFF);
const uint SHORT_MASK = uint(0xFFFF);
const uint ZERO = uint(0);
const uint SMASK = uint(3);
const uint ONE = uint(1);
const uint THREE = uint(3);
const uint DECORATION_MASK = uint(3);
const uint STRIKE_MASK = uint(1);
vec3 color_to_vec(uint c) {
uint r, g, b;
@ -76,12 +79,13 @@ void main() {
gl_Position = vec4(xpos[pos.x], ypos[pos.y], 0, 1);
sprite_pos = to_sprite_pos(pos, sprite_coords.x, sprite_coords.y, sprite_coords.z & SHORT_MASK);
uint fg = colors[color_indices[0]];
uint bg = colors[color_indices[1]];
uint reverse = (text_attrs >> 30) & STRIKE_MASK;
uint fg = colors[color_indices[reverse]];
uint bg = colors[color_indices[ONE - reverse]];
uint decoration = colors[2];
foreground = apply_selection(to_color(fg, default_colors[color_indices[0]]), default_colors[2]);
background = apply_selection(to_color(bg, default_colors[color_indices[1]]), default_colors[3]);
decoration_fg = to_color(decoration, default_colors[color_indices[0]]);
underline_pos = to_sprite_pos(pos, (sprite_coords.z >> 24) & SMASK, ZERO, ZERO);
strike_pos = to_sprite_pos(pos, strike_map[(sprite_coords.z >> 26)] & SMASK, ZERO, ZERO);
underline_pos = to_sprite_pos(pos, (text_attrs >> 26) & DECORATION_MASK, ZERO, ZERO);
strike_pos = to_sprite_pos(pos, ((text_attrs >> 31) & STRIKE_MASK) * THREE, ZERO, ZERO);
}

View File

@ -13,10 +13,10 @@ from .constants import (
GLfloat, GLuint, ScreenGeometry, cell_size, viewport_size
)
from .fast_data_types import (
CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE, DATA_CELL_SIZE, GL_BLEND,
GL_FLOAT, GL_LINE_LOOP, GL_TRIANGLE_FAN, GL_UNSIGNED_INT, glDisable,
glDrawArrays, glDrawArraysInstanced, glEnable, glUniform1i, glUniform2f,
glUniform2i, glUniform2ui, glUniform4f, glUniform4ui
CELL, CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE, GL_BLEND, GL_FLOAT,
GL_LINE_LOOP, GL_TRIANGLE_FAN, GL_UNSIGNED_INT, GL_UNSIGNED_SHORT,
glDisable, glDrawArrays, glDrawArraysInstanced, glEnable, glUniform1i,
glUniform2f, glUniform2i, glUniform2ui, glUniform4f, glUniform4ui
)
from .rgb import to_color
from .shaders import ShaderProgram, load_shaders
@ -51,10 +51,10 @@ class CellProgram(ShaderProgram):
def create_sprite_map(self):
with self.array_object_creator() as add_attribute:
stride = DATA_CELL_SIZE * sizeof(GLuint)
size = DATA_CELL_SIZE // 2
add_attribute('sprite_coords', size=size, dtype=GL_UNSIGNED_INT, stride=stride, divisor=1)
add_attribute('colors', size=size, dtype=GL_UNSIGNED_INT, stride=stride, offset=stride // 2, divisor=1)
stride = CELL['size']
add_attribute('text_attrs', size=1, dtype=GL_UNSIGNED_INT, offset=CELL['ch'], stride=stride, divisor=1)
add_attribute('sprite_coords', size=3, dtype=GL_UNSIGNED_SHORT, offset=CELL['sprite_x'], stride=stride, divisor=1)
add_attribute('colors', size=3, dtype=GL_UNSIGNED_INT, stride=stride, offset=CELL['fg'], divisor=1)
add_attribute.newbuf()
add_attribute('is_selected', size=1, dtype=GL_FLOAT, stride=sizeof(GLfloat), divisor=1)
return add_attribute.vao_id
@ -137,16 +137,18 @@ def calculate_gl_geometry(window_geometry, viewport_width, viewport_height, cell
return ScreenGeometry(xstart, ystart, window_geometry.xnum, window_geometry.ynum, dx, dy)
def render_cells(vao_id, sg, cell_program, sprites, color_profile, invert_colors=False):
def render_cells(vao_id, sg, cell_program, sprites, color_profile, invert_colors=False, screen_reversed=False):
if color_profile.dirty:
cell_program.send_color_table(color_profile)
color_profile.dirty = False
ul = cell_program.uniform_location
glUniform2ui(ul('dimensions'), sg.xnum, sg.ynum)
glUniform4ui(ul('default_colors'), color_profile.default_fg, color_profile.default_bg, color_profile.highlight_fg, color_profile.highlight_bg)
glUniform2i(ul('color_indices'), 1 if invert_colors else 0, 0 if invert_colors else 1)
inverted = invert_colors or screen_reversed
glUniform2i(ul('color_indices'), 1 if inverted else 0, 0 if inverted else 1)
glUniform4f(ul('steps'), sg.xstart, sg.ystart, sg.dx, sg.dy)
glUniform1i(ul('sprites'), sprites.sampler_num)
glUniform1i(ul('screen_reversed'), 1 if screen_reversed else 0)
glUniform2f(ul('sprite_layout'), *(sprites.layout))
with cell_program.bound_vertex_array(vao_id), cell_program.bound_uniform_buffer(color_profile.ubo):
glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, sg.xnum * sg.ynum)
@ -159,6 +161,7 @@ class CharGrid:
def __init__(self, screen, opts):
self.vao_id = None
self.current_selection = Selection()
self.screen_reversed = False
self.scroll_changed = False
self.last_rendered_selection = None
self.render_data = None
@ -194,7 +197,7 @@ class CharGrid:
def resize(self, window_geometry):
self.update_position(window_geometry)
self.data_buffer_size = sizeof(GLuint) * self.screen_geometry.ynum * self.screen_geometry.xnum * DATA_CELL_SIZE
self.data_buffer_size = self.screen_geometry.ynum * self.screen_geometry.xnum * CELL['size']
self.selection_buf = (GLfloat * (self.screen_geometry.ynum * self.screen_geometry.xnum))()
self.current_selection.clear()
@ -230,7 +233,7 @@ class CharGrid:
if self.data_buffer_size == 0:
return
with cell_program.mapped_vertex_data(self.vao_id, self.data_buffer_size) as address:
cursor_changed, self.scrolled_by = self.screen.update_cell_data(
cursor_changed, self.scrolled_by, self.screen_reversed = self.screen.update_cell_data(
address, self.scrolled_by, force_full_refresh)
self.current_selection.clear()
@ -370,7 +373,7 @@ class CharGrid:
return sg
def render_cells(self, sg, cell_program, sprites, invert_colors=False):
render_cells(self.vao_id, sg, cell_program, sprites, self.screen.color_profile, invert_colors=invert_colors)
render_cells(self.vao_id, sg, cell_program, sprites, self.screen.color_profile, invert_colors=invert_colors, screen_reversed=self.screen_reversed)
def render_cursor(self, sg, cursor_program, is_focused):
cursor = self.current_cursor

View File

@ -145,7 +145,6 @@ PyInit_fast_data_types(void) {
PyModule_AddIntMacro(m, DECCOLM);
PyModule_AddIntMacro(m, DECOM);
PyModule_AddIntMacro(m, IRM);
PyModule_AddIntMacro(m, DATA_CELL_SIZE);
PyModule_AddIntMacro(m, ANY_MODE);
PyModule_AddIntMacro(m, MOTION_MODE);
PyModule_AddIntMacro(m, BUTTON_MODE);

View File

@ -38,8 +38,6 @@ typedef uint16_t sprite_index;
#define SGR_PROTOCOL 2
#define URXVT_PROTOCOL 3
#define DATA_CELL_SIZE 6
#define BLANK_CHAR 32
#define CHAR_MASK 0xFFFFFF
#define ATTRS_SHIFT 24
@ -313,7 +311,6 @@ void cursor_reset(Cursor*);
Cursor* cursor_copy(Cursor*);
void cursor_copy_to(Cursor *src, Cursor *dest);
void cursor_reset_display_attrs(Cursor*);
bool update_cell_range_data(ScreenModes *modes, Line *, unsigned int, unsigned int, unsigned int *);
void set_sprite_position(Cell *cell, Cell *previous_cell);
PyObject* line_text_at(char_type, combining_type);

View File

@ -826,7 +826,7 @@ int add_module_gl_constants(PyObject *module) {
GLC(GL_TEXTURE_MIN_FILTER); GLC(GL_TEXTURE_MAG_FILTER);
GLC(GL_TEXTURE_WRAP_S); GLC(GL_TEXTURE_WRAP_T);
GLC(GL_UNPACK_ALIGNMENT);
GLC(GL_R8); GLC(GL_RED); GLC(GL_UNSIGNED_BYTE); GLC(GL_R32UI); GLC(GL_RGB32UI); GLC(GL_RGBA);
GLC(GL_R8); GLC(GL_RED); GLC(GL_UNSIGNED_BYTE); GLC(GL_UNSIGNED_SHORT); GLC(GL_R32UI); GLC(GL_RGB32UI); GLC(GL_RGBA);
GLC(GL_TEXTURE_BUFFER); GLC(GL_STATIC_DRAW); GLC(GL_STREAM_DRAW); GLC(GL_DYNAMIC_DRAW);
GLC(GL_SRC_ALPHA); GLC(GL_ONE_MINUS_SRC_ALPHA);
GLC(GL_WRITE_ONLY); GLC(GL_READ_ONLY); GLC(GL_READ_WRITE);

View File

@ -1178,10 +1178,17 @@ change_scrollback_size(Screen *self, PyObject *args) {
Py_RETURN_NONE;
}
static inline void
update_line_data(Line *line, unsigned int dest_y, uint8_t *data) {
size_t base = dest_y * line->xnum * sizeof(Cell);
memcpy(data + base, line->cells, line->xnum * sizeof(Cell));
}
static PyObject*
screen_update_cell_data(Screen *self, PyObject *args) {
update_cell_data(Screen *self, PyObject *args) {
PyObject *dp;
unsigned int *data;
uint8_t *data;
int force_screen_refresh;
unsigned int scrolled_by;
unsigned int history_line_added_count = self->history_line_added_count;
@ -1192,15 +1199,13 @@ screen_update_cell_data(Screen *self, PyObject *args) {
reset_dirty(self);
for (index_type y = 0; y < MIN(self->lines, scrolled_by); y++) {
historybuf_init_line(self->historybuf, scrolled_by - 1 - y, self->historybuf->line);
self->historybuf->line->ynum = y;
if (!update_cell_range_data(&(self->modes), self->historybuf->line, 0, self->columns - 1, data)) return NULL;
update_line_data(self->historybuf->line, y, data);
}
for (index_type y = scrolled_by; y < self->lines; y++) {
linebuf_init_line(self->linebuf, y - scrolled_by);
self->linebuf->line->ynum = y;
if (!update_cell_range_data(&(self->modes), self->linebuf->line, 0, self->columns - 1, data)) return NULL;
update_line_data(self->linebuf->line, y, data);
}
return Py_BuildValue("OI", cursor_changed, scrolled_by);
return Py_BuildValue("OIO", cursor_changed, scrolled_by, self->modes.mDECSCNM ? Py_True : Py_False);
}
static PyObject*
@ -1303,7 +1308,7 @@ static PyMethodDef methods[] = {
MND(apply_selection, METH_VARARGS)
MND(toggle_alt_screen, METH_NOARGS)
MND(reset_callbacks, METH_NOARGS)
{"update_cell_data", (PyCFunction)screen_update_cell_data, METH_VARARGS, ""},
{"update_cell_data", (PyCFunction)update_cell_data, METH_VARARGS, ""},
{"select_graphic_rendition", (PyCFunction)_select_graphic_rendition, METH_VARARGS, ""},
{NULL} /* Sentinel */

View File

@ -167,28 +167,6 @@ sprite_position_for(PyObject UNUSED *self, PyObject *args) {
return Py_BuildValue("III", pos->x, pos->y, pos->z);
}
bool
update_cell_range_data(ScreenModes *modes, Line *line, unsigned int xstart, unsigned int xmax, unsigned int *data) {
char_type ch;
const bool screen_reversed = modes->mDECSCNM;
size_t base = line->ynum * line->xnum * DATA_CELL_SIZE;
for (size_t i = xstart, offset = base + xstart * DATA_CELL_SIZE; i <= xmax; i++, offset += DATA_CELL_SIZE) {
ch = line->cells[i].ch;
char_type attrs = ch >> ATTRS_SHIFT;
unsigned int decoration = (attrs >> DECORATION_SHIFT) & DECORATION_MASK;
unsigned int strikethrough = ((attrs >> STRIKE_SHIFT) & 1) ? 3 : 0;
bool reverse = ((attrs >> REVERSE_SHIFT) & 1) ^ screen_reversed;
data[offset] = line->cells[i].sprite_x;
data[offset+1] = line->cells[i].sprite_y;
data[offset+2] = line->cells[i].sprite_z | (decoration << 24) | (strikethrough << 26);
data[offset+(reverse ? 4 : 3)] = line->cells[i].fg;
data[offset+(reverse ? 3 : 4)] = line->cells[i].bg;
data[offset+5] = line->cells[i].fg;
}
return true;
}
PyObject*
render_dirty_sprites(PyObject UNUSED *s_) {
#define render_dirty_cells_doc "Render all cells that are marked as dirty"

View File

@ -3,7 +3,6 @@
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
from collections import deque, namedtuple
from ctypes import sizeof
from functools import partial
from .borders import Borders
@ -15,7 +14,7 @@ from .constants import (
shell_path, viewport_size
)
from .fast_data_types import (
DATA_CELL_SIZE, DECAWM, Screen, glfw_post_empty_event
CELL, DECAWM, Screen, glfw_post_empty_event
)
from .layout import Rect, all_layouts
from .utils import color_as_int
@ -234,7 +233,7 @@ class TabBar:
s.resize(1, ncells)
s.reset_mode(DECAWM)
self.selection_buf = (GLuint * (s.lines * s.columns))()
self.data_buffer_size = sizeof(GLuint) * s.lines * s.columns * DATA_CELL_SIZE
self.data_buffer_size = s.lines * s.columns * CELL['size']
self.selection_buf_changed = True
margin = (viewport_width - ncells * cell_width) // 2
self.window_geometry = g = WindowGeometry(