Switch to using an index for sprite tracking

Frees up two bytes in GPUCell. Doesn't require a minimum texture row
size. Makes a bunch of code faster. Index uses 31 bits which gives us
2,147,483,647 aka ~ 2 billion sprites.
This commit is contained in:
Kovid Goyal 2024-12-11 08:36:03 +05:30
parent 203a5f4e00
commit 0fb49f4139
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
12 changed files with 97 additions and 71 deletions

View file

@ -22,7 +22,7 @@ uniform uint draw_bg_bitfield;
// Have to use fixed locations here as all variants of the cell program share the same VAO
layout(location=0) in uvec3 colors;
layout(location=1) in uvec4 sprite_coords;
layout(location=1) in uvec2 sprite_idx;
layout(location=2) in uint is_selected;
uniform float gamma_lut[256];
@ -58,8 +58,9 @@ out float effective_text_alpha;
// Utility functions {{{
const uint BYTE_MASK = uint(0xFF);
const uint Z_MASK = uint(0xFFF);
const uint COLOR_MASK = uint(0x4000);
const uint Z_MASK = uint(0x7fffffff);
const uint COLOR_MASK = uint(0x80000000);
const uint COLOR_SHIFT = uint(31);
const uint ZERO = uint(0);
const uint ONE = uint(1);
const uint TWO = uint(2);
@ -95,7 +96,12 @@ vec3 to_color(uint c, uint defval) {
return color_to_vec(resolve_color(c, defval));
}
vec3 to_sprite_pos(uvec2 pos, uint x, uint y, uint z) {
vec3 to_sprite_pos(uvec2 pos, uint idx) {
uint sprites_per_page = sprites_xnum * sprites_ynum;
uint z = idx / sprites_per_page;
uint num_on_last_page = idx % sprites_per_page;
uint y = num_on_last_page / sprites_xnum;
uint x = num_on_last_page % sprites_xnum;
vec2 s_xpos = vec2(x, float(x) + 1.0f) * (1.0f / float(sprites_xnum));
vec2 s_ypos = vec2(y, float(y) + 1.0f) * (1.0f / float(sprites_ynum));
return vec3(s_xpos[pos.x], s_ypos[pos.y], z);
@ -140,8 +146,8 @@ CellData set_vertex_position() {
gl_Position = vec4(xpos[pos.x], ypos[pos.y], 0, 1);
#ifdef NEEDS_FOREGROUND
// The character sprite being rendered
sprite_pos = to_sprite_pos(pos, sprite_coords.x, sprite_coords.y, sprite_coords.z & Z_MASK);
colored_sprite = float((sprite_coords.z & COLOR_MASK) >> 14);
sprite_pos = to_sprite_pos(pos, sprite_idx[0] & Z_MASK);
colored_sprite = float((sprite_idx[0] & COLOR_MASK) >> COLOR_SHIFT);
#endif
float is_block_cursor = step(float(cursor_fg_sprite_idx), 0.5);
float has_cursor = is_cursor(c, r);
@ -172,7 +178,7 @@ void main() {
// set cell color indices {{{
uvec2 default_colors = uvec2(default_fg, bg_colors0);
uint text_attrs = sprite_coords[3];
uint text_attrs = sprite_idx[1];
uint is_reversed = ((text_attrs >> REVERSE_SHIFT) & ONE);
uint is_inverted = is_reversed + inverted;
int fg_index = fg_index_map[is_inverted];
@ -202,15 +208,15 @@ void main() {
foreground = choose_color(float(is_selected & ONE), selection_color, foreground);
decoration_fg = choose_color(float(is_selected & ONE), selection_color, decoration_fg);
// Underline and strike through (rendered via sprites)
underline_pos = choose_color(in_url, to_sprite_pos(cell_data.pos, url_style, ZERO, ZERO), to_sprite_pos(cell_data.pos, (text_attrs >> DECORATION_SHIFT) & DECORATION_MASK, ZERO, ZERO));
strike_pos = to_sprite_pos(cell_data.pos, ((text_attrs >> STRIKE_SHIFT) & ONE) * STRIKE_SPRITE_INDEX, ZERO, ZERO);
underline_pos = choose_color(in_url, to_sprite_pos(cell_data.pos, url_style), to_sprite_pos(cell_data.pos, (text_attrs >> DECORATION_SHIFT) & DECORATION_MASK));
strike_pos = to_sprite_pos(cell_data.pos, ((text_attrs >> STRIKE_SHIFT) & ONE) * STRIKE_SPRITE_INDEX);
// Cursor
cursor_color_premult = vec4(color_to_vec(cursor_bg) * cursor_opacity, cursor_opacity);
vec3 final_cursor_text_color = mix(foreground, color_to_vec(cursor_fg), cursor_opacity);
foreground = choose_color(cell_data.has_block_cursor, final_cursor_text_color, foreground);
decoration_fg = choose_color(cell_data.has_block_cursor, final_cursor_text_color, decoration_fg);
cursor_pos = to_sprite_pos(cell_data.pos, cursor_fg_sprite_idx * uint(cell_data.has_cursor), ZERO, ZERO);
cursor_pos = to_sprite_pos(cell_data.pos, cursor_fg_sprite_idx * uint(cell_data.has_cursor));
#endif
// }}}

View file

@ -710,11 +710,6 @@ shift_to_first_set_bit(CellAttrs x) {
EXPORTED PyMODINIT_FUNC
PyInit_fast_data_types(void) {
PyObject *m;
if (sizeof(CellAttrs) != 2u) {
PyErr_SetString(PyExc_RuntimeError, "Size of CellAttrs is not 2 on this platform");
return NULL;
}
m = PyModule_Create(&module);
if (m == NULL) return NULL;
init_monotonic();

View file

@ -73,7 +73,7 @@ typedef uint16_t combining_type;
typedef uint16_t glyph_index;
typedef uint32_t pixel;
typedef unsigned int index_type;
typedef uint16_t sprite_index;
typedef uint32_t sprite_index;
typedef enum CursorShapes { NO_CURSOR_SHAPE, CURSOR_BLOCK, CURSOR_BEAM, CURSOR_UNDERLINE, CURSOR_HOLLOW, NUM_OF_CURSOR_SHAPES } CursorShape;
typedef enum { DISABLE_LIGATURES_NEVER, DISABLE_LIGATURES_CURSOR, DISABLE_LIGATURES_ALWAYS } DisableLigature;
@ -276,7 +276,7 @@ typedef struct FontCellMetrics {
#define FONTS_DATA_HEAD SPRITE_MAP_HANDLE sprite_map; double logical_dpi_x, logical_dpi_y, font_sz_in_pts; FontCellMetrics fcm;
typedef struct {FONTS_DATA_HEAD} *FONTS_DATA_HANDLE;
#define clear_sprite_position(cell) (cell).sprite_x = 0; (cell).sprite_y = 0; (cell).sprite_z = 0;
#define clear_sprite_position(cell) (cell).sprite_idx = 0;
#define ensure_space_for(base, array, type, num, capacity, initial_cap, zero_mem) \
if ((base)->capacity < num) { \

View file

@ -1108,8 +1108,7 @@ def parse_input_from_terminal(
class Line:
def sprite_at(self, cell: int) -> Tuple[int, int, int]:
pass
def sprite_at(self, cell: int) -> int: ...
def test_shape(line: Line,
@ -1719,6 +1718,7 @@ def cocoa_play_system_sound_by_id_async(sound_id: int) -> None: ...
def glfw_get_system_color_theme(query_if_unintialized: bool = True) -> Literal['light', 'dark', 'no_preference']: ...
def set_redirect_keys_to_overlay(os_window_id: int, tab_id: int, window_id: int, overlay_window_id: int) -> None: ...
def buffer_keys_in_window(os_window_id: int, tab_id: int, window_id: int, enabled: bool = True) -> bool: ...
def sprite_idx_to_pos(idx: int, xnum: int, ynum: int) -> tuple[int, int, int]: ...
class MousePosition(TypedDict):
cell_x: int

View file

@ -30,8 +30,7 @@ typedef enum {
typedef struct {
size_t max_y;
unsigned int x, y, z, xnum, ynum;
unsigned x, y, z, xnum, ynum, max_y;
} GPUSpriteTracker;
typedef struct RunFont {
@ -263,6 +262,10 @@ do_increment(FontGroup *fg, int *error) {
}
}
static uint32_t
current_sprite_index(GPUSpriteTracker *sprite_tracker) {
return sprite_tracker->z * (sprite_tracker->xnum * sprite_tracker->ynum) + sprite_tracker->y * sprite_tracker->xnum + sprite_tracker->x;
}
static SpritePosition*
sprite_position_for(FontGroup *fg, RunFont rf, glyph_index *glyphs, unsigned glyph_count, uint8_t ligature_index, unsigned cell_count, int *error) {
@ -274,7 +277,7 @@ sprite_position_for(FontGroup *fg, RunFont rf, glyph_index *glyphs, unsigned gly
rf.scale, subscale, rf.multicell_y, rf.vertical_align, &created);
if (!s) { *error = 1; return NULL; }
if (created) {
s->x = fg->sprite_tracker.x; s->y = fg->sprite_tracker.y; s->z = fg->sprite_tracker.z;
s->idx = current_sprite_index(&fg->sprite_tracker);
do_increment(fg, error);
}
return s;
@ -405,9 +408,12 @@ free_font_groups(void) {
}
static void
python_send_to_gpu(FONTS_DATA_HANDLE fg, unsigned int x, unsigned int y, unsigned int z, pixel* buf) {
python_send_to_gpu(FONTS_DATA_HANDLE fg_, unsigned int idx, pixel *buf) {
FontGroup *fg = (FontGroup*)fg_;
if (python_send_to_gpu_impl) {
if (!num_font_groups) fatal("Cannot call send to gpu with no font groups");
unsigned int x, y, z;
sprite_index_to_pos(idx, fg->sprite_tracker.xnum, fg->sprite_tracker.ynum, &x, &y, &z);
PyObject *ret = PyObject_CallFunction(python_send_to_gpu_impl, "IIIN", x, y, z, PyBytes_FromStringAndSize((const char*)buf, sizeof(pixel) * fg->fcm.cell_width * fg->fcm.cell_height));
if (ret == NULL) PyErr_Print();
else Py_DECREF(ret);
@ -670,11 +676,6 @@ START_ALLOW_CASE_RANGE
END_ALLOW_CASE_RANGE
}
static void
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;
}
// Gives a unique (arbitrary) id to a box glyph
static glyph_index
box_glyph_id(char_type ch) {
@ -855,6 +856,12 @@ dump_sprite(pixel *b, unsigned width, unsigned height, bool actual_pixel_values)
}
}
static void
set_cell_sprite(GPUCell *cell, const SpritePosition *sp) {
cell->sprite_idx = sp->idx & 0x7fffffff;
if (sp->colored) cell->sprite_idx |= 0x80000000;
}
static void
render_box_cell(FontGroup *fg, RunFont rf, CPUCell *cpu_cell, GPUCell *gpu_cell, const TextCache *tc) {
int error = 0;
@ -869,7 +876,7 @@ render_box_cell(FontGroup *fg, RunFont rf, CPUCell *cpu_cell, GPUCell *gpu_cell,
else global_glyph_render_scratch.lc->chars[i] = 0;
}
if (!num_glyphs) {
for (unsigned i = 0; i < rf.scale; i++) set_sprite(gpu_cell + i, 0, 0, 0);
for (unsigned i = 0; i < rf.scale; i++) gpu_cell[i].sprite_idx = 0;
return;
}
bool all_rendered = true;
@ -878,11 +885,11 @@ render_box_cell(FontGroup *fg, RunFont rf, CPUCell *cpu_cell, GPUCell *gpu_cell,
sp[ligature_index] = sprite_position_for(fg, rf, global_glyph_render_scratch.glyphs, num_glyphs, ligature_index, rf.scale, &error);
if (sp[ligature_index] == NULL) {
sprite_map_set_error(error); PyErr_Print();
for (unsigned i = 0; i < rf.scale; i++) set_sprite(gpu_cell + i, 0, 0, 0);
for (unsigned i = 0; i < rf.scale; i++) gpu_cell[i].sprite_idx = 0;
return;
}
set_sprite(gpu_cell + ligature_index, sp[ligature_index]->x, sp[ligature_index]->y, sp[ligature_index]->z);
sp[ligature_index]->colored = false;
set_cell_sprite(gpu_cell + ligature_index, sp[ligature_index]);
if (!sp[ligature_index]->rendered) all_rendered = false;
}
if (all_rendered) return;
@ -915,7 +922,7 @@ render_box_cell(FontGroup *fg, RunFont rf, CPUCell *cpu_cell, GPUCell *gpu_cell,
/*printf("Rendered char sz: (%u, %u)\n", width, height); dump_sprite(fg->canvas.buf, width, height, false);*/
if (scale == 1.f && rf.scale == 1 && !rf.subscale_n) {
sp[0]->rendered = true;
current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, sp[0]->x, sp[0]->y, sp[0]->z, fg->canvas.buf);
current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, sp[0]->idx, fg->canvas.buf);
} else {
calculate_regions_for_line(rf, fg->fcm.cell_height, &src, &dest);
/*printf("width: %u height: %u unscaled_cell_width: %u unscaled_cell_height: %u src.top: %u src.bottom: %u rf.scale: %u\n", width, height, fg->fcm.cell_width, fg->fcm.cell_height, src.top, src.bottom, rf.scale);*/
@ -923,7 +930,7 @@ render_box_cell(FontGroup *fg, RunFont rf, CPUCell *cpu_cell, GPUCell *gpu_cell,
pixel *b = extract_cell_region(&fg->canvas, i, &src, &dest, width, fg->fcm);
/*printf("Sprite %u: pos: (%u, %u, %u) sz: (%u, %u)\n", i, sp[i]->x, sp[i]->y, sp[i]->z, fg->fcm.cell_width, fg->fcm.cell_height); dump_sprite(b, fg->fcm.cell_width, fg->fcm.cell_height, false);*/
sp[i]->rendered = true;
current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, sp[i]->x, sp[i]->y, sp[i]->z, b);
current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, sp[i]->idx, b);
}
}
#undef sp
@ -948,12 +955,6 @@ load_hb_buffer(CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_
}
static void
set_cell_sprite(GPUCell *cell, const SpritePosition *sp) {
cell->sprite_x = sp->x; cell->sprite_y = sp->y; cell->sprite_z = sp->z;
if (sp->colored) cell->sprite_z |= 0x4000;
}
static void
render_filled_sprite(pixel *buf, unsigned num_glyphs, FontCellMetrics scaled_metrics, unsigned num_scaled_cells) {
if (num_scaled_cells > num_glyphs) {
@ -1014,10 +1015,10 @@ render_group(
if (num_cells == num_scaled_cells && rf.scale == 1.f) {
for (unsigned i = 0; i < num_cells; i++) {
if (!sp[i]->rendered) {
bool is_repeat_sprite = is_infinite_ligature && i > 0 && sp[i]->x == sp[i-1]->x && sp[i]->y == sp[i-1]->y && sp[i]->z == sp[i-1]->z;
bool is_repeat_sprite = is_infinite_ligature && i > 0 && sp[i]->idx == sp[i-1]->idx;
if (!is_repeat_sprite) {
pixel *b = num_cells == 1 ? fg->canvas.buf : extract_cell_from_canvas(fg, i, num_cells);
current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, sp[i]->x, sp[i]->y, sp[i]->z, b);
current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, sp[i]->idx, b);
}
sp[i]->rendered = true; sp[i]->colored = was_colored;
}
@ -1031,7 +1032,7 @@ render_group(
if (!sp[i]->rendered) {
pixel *b = extract_cell_region(&fg->canvas, i, &src, &dest, scaled_metrics.cell_width * num_scaled_cells, unscaled_metrics);
/*printf("cell %u src -> dest: (%u %u) -> (%u %u)\n", i, src.left, src.right, dest.left, dest.right);*/
current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, sp[i]->x, sp[i]->y, sp[i]->z, b);
current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, sp[i]->idx, b);
/*dump_sprite(b, unscaled_metrics.cell_width, unscaled_metrics.cell_height, false);*/
sp[i]->rendered = true; sp[i]->colored = was_colored;
}
@ -1583,7 +1584,7 @@ render_run(FontGroup *fg, CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, inde
render_groups(fg, rf, center_glyph, tc);
break;
case BLANK_FONT:
while(num_cells--) { set_sprite(first_gpu_cell, 0, 0, 0); first_cpu_cell++; first_gpu_cell++; }
while(num_cells--) { first_gpu_cell->sprite_idx = 0; first_cpu_cell++; first_gpu_cell++; }
break;
case BOX_FONT:
while(num_cells) {
@ -1594,7 +1595,7 @@ render_run(FontGroup *fg, CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, inde
}
break;
case MISSING_FONT:
while(num_cells--) { set_sprite(first_gpu_cell, MISSING_GLYPH, 0, 0); first_cpu_cell++; first_gpu_cell++; }
while(num_cells--) { first_gpu_cell->sprite_idx = MISSING_GLYPH; first_cpu_cell++; first_gpu_cell++; }
break;
}
}
@ -1759,26 +1760,24 @@ set_font_data(PyObject UNUSED *m, PyObject *args) {
static void
send_prerendered_sprites(FontGroup *fg) {
int error = 0;
sprite_index x = 0, y = 0, z = 0;
// blank cell
ensure_canvas_can_fit(fg, 1, 1);
current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, x, y, z, fg->canvas.buf);
do_increment(fg, &error);
current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, 0, fg->canvas.buf);
if (error != 0) { sprite_map_set_error(error); PyErr_Print(); fatal("Failed"); }
PyObject *args = PyObject_CallFunction(prerender_function, "IIIIIIIffdd", fg->fcm.cell_width, fg->fcm.cell_height, fg->fcm.baseline, fg->fcm.underline_position, fg->fcm.underline_thickness, fg->fcm.strikethrough_position, fg->fcm.strikethrough_thickness, OPT(cursor_beam_thickness), OPT(cursor_underline_thickness), fg->logical_dpi_x, fg->logical_dpi_y);
if (args == NULL) { PyErr_Print(); fatal("Failed to pre-render cells"); }
PyObject *cell_addresses = PyTuple_GET_ITEM(args, 0);
for (ssize_t i = 0; i < PyTuple_GET_SIZE(cell_addresses); i++) {
x = fg->sprite_tracker.x; y = fg->sprite_tracker.y; z = fg->sprite_tracker.z;
if (y > 0) { fatal("Too many pre-rendered sprites for your GPU or the font size is too large"); }
do_increment(fg, &error);
if (error != 0) { sprite_map_set_error(error); PyErr_Print(); fatal("Failed"); }
uint8_t *alpha_mask = PyLong_AsVoidPtr(PyTuple_GET_ITEM(cell_addresses, i));
ensure_canvas_can_fit(fg, 1, 1); // clear canvas
Region r = { .right = fg->fcm.cell_width, .bottom = fg->fcm.cell_height };
render_alpha_mask(alpha_mask, fg->canvas.buf, &r, &r, fg->fcm.cell_width, fg->fcm.cell_width, 0xffffff);
current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, x, y, z, fg->canvas.buf);
current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, current_sprite_index(&fg->sprite_tracker), fg->canvas.buf);
}
do_increment(fg, &error);
if (error != 0) { sprite_map_set_error(error); PyErr_Print(); fatal("Failed"); }
Py_CLEAR(args);
}
@ -1893,7 +1892,9 @@ test_sprite_position_for(PyObject UNUSED *self, PyObject *args) {
RunFont rf = {.scale = 1, .font_idx=fg->medium_font_idx};
SpritePosition *pos = sprite_position_for(fg, rf, glyphs, PyTuple_GET_SIZE(args), 0, 1, &error);
if (pos == NULL) { sprite_map_set_error(error); return NULL; }
return Py_BuildValue("HHH", pos->x, pos->y, pos->z);
unsigned int x, y, z;
sprite_index_to_pos(pos->idx, fg->sprite_tracker.xnum, fg->sprite_tracker.ynum, &x, &y, &z);
return Py_BuildValue("III", x, y, z);
}
static PyObject*
@ -2125,8 +2126,17 @@ set_allow_use_of_box_fonts(PyObject *self UNUSED, PyObject *val) {
Py_RETURN_NONE;
}
static PyObject*
sprite_idx_to_pos(PyObject *self UNUSED, PyObject *args) {
unsigned x, y, z, idx, xnum, ynum;
if (!PyArg_ParseTuple(args, "III", &idx, &xnum, &ynum)) return NULL;
sprite_index_to_pos(idx, xnum, ynum, &x, &y, &z);
return Py_BuildValue("III", x, y, z);
}
static PyMethodDef module_methods[] = {
METHODB(set_font_data, METH_VARARGS),
METHODB(sprite_idx_to_pos, METH_VARARGS),
METHODB(free_font_data, METH_NOARGS),
METHODB(create_test_font_group, METH_VARARGS),
METHODB(sprite_map_set_layout, METH_VARARGS),

View file

@ -21,6 +21,7 @@
set_font_data,
set_options,
set_send_sprite_to_gpu,
sprite_idx_to_pos,
sprite_map_set_limits,
test_render_line,
test_shape,
@ -437,6 +438,9 @@ def render_box_drawing(codepoint: int, cell_width: int, cell_height: int, dpi: f
class setup_for_testing:
xnum = 100000
ynum = 100
def __init__(self, family: str = 'monospace', size: float = 11.0, dpi: float = 96.0, main_face_path: str = ''):
self.family, self.size, self.dpi = family, size, dpi
self.main_face_path = main_face_path
@ -450,7 +454,7 @@ def __enter__(self) -> tuple[dict[tuple[int, int, int], bytes], int, int]:
def send_to_gpu(x: int, y: int, z: int, data: bytes) -> None:
sprites[(x, y, z)] = data
sprite_map_set_limits(100000, 100)
sprite_map_set_limits(self.xnum, self.ynum)
set_send_sprite_to_gpu(send_to_gpu)
self.orig_desc_overrides = descriptor_overrides
descriptor_overrides = {}
@ -479,13 +483,12 @@ def render_string(text: str, family: str = 'monospace', size: float = 11.0, dpi:
cells = []
found_content = False
for i in reversed(range(s.columns)):
sp = list(line.sprite_at(i))
sp[2] &= 0xfff
tsp = sp[0], sp[1], sp[2]
if tsp == (0, 0, 0) and not found_content:
sp = line.sprite_at(i)
sp &= 0x7fffffff
if not sp and not found_content:
continue
found_content = True
cells.append(sprites[tsp])
cells.append(sprites[sprite_idx_to_pos(sp, setup_for_testing.xnum, setup_for_testing.ynum)])
return cell_width, cell_height, list(reversed(cells))

View file

@ -12,7 +12,7 @@ void free_glyph_cache_global_resources(void);
typedef struct SpritePosition {
bool rendered, colored;
sprite_index x, y, z;
sprite_index idx;
} SpritePosition;
typedef struct {int x;} *SPRITE_POSITION_MAP_HANDLE;

View file

@ -408,7 +408,7 @@ sprite_at(Line* self, PyObject *x) {
unsigned long xval = PyLong_AsUnsignedLong(x);
if (xval >= self->xnum) { PyErr_SetString(PyExc_IndexError, "Column number out of bounds"); return NULL; }
GPUCell *c = self->gpu_cells + xval;
return Py_BuildValue("HHH", c->sprite_x, c->sprite_y, c->sprite_z);
return Py_BuildValue("I", (unsigned int)c->sprite_idx);
}
static void
@ -682,7 +682,7 @@ line_apply_cursor(Line *self, const Cursor *cursor, unsigned int at, unsigned in
} else {
for (index_type i = at; i < self->xnum && i < at + num; i++) {
gc.attrs.mark = self->gpu_cells[i].attrs.mark;
gc.sprite_x = self->gpu_cells[i].sprite_x; gc.sprite_y = self->gpu_cells[i].sprite_y; gc.sprite_z = self->gpu_cells[i].sprite_z;
gc.sprite_idx = self->gpu_cells[i].sprite_idx;
memcpy(self->gpu_cells + i, &gc, sizeof(gc));
}
}

View file

@ -27,11 +27,11 @@ typedef union CellAttrs {
uint16_t strike : 1;
uint16_t dim : 1;
uint16_t mark : 2;
uint16_t : 6;
uint32_t : 22;
};
uint16_t val;
uint32_t val;
} CellAttrs;
static_assert(sizeof(CellAttrs) == sizeof(uint16_t), "Fix the ordering of CellAttrs");
static_assert(sizeof(CellAttrs) == sizeof(uint32_t), "Fix the ordering of CellAttrs");
#define WIDTH_MASK (3u)
#define DECORATION_MASK (7u)
@ -44,7 +44,7 @@ static_assert(sizeof(CellAttrs) == sizeof(uint16_t), "Fix the ordering of CellAt
typedef struct {
color_type fg, bg, decoration_fg;
sprite_index sprite_x, sprite_y, sprite_z;
sprite_index sprite_idx;
CellAttrs attrs;
} GPUCell;
static_assert(sizeof(GPUCell) == 20, "Fix the ordering of GPUCell");

View file

@ -143,13 +143,17 @@ ensure_sprite_map(FONTS_DATA_HANDLE fg) {
}
void
send_sprite_to_gpu(FONTS_DATA_HANDLE fg, unsigned int x, unsigned int y, unsigned int z, pixel *buf) {
send_sprite_to_gpu(FONTS_DATA_HANDLE fg, unsigned int idx, pixel *buf) {
SpriteMap *sprite_map = (SpriteMap*)fg->sprite_map;
unsigned int xnum, ynum, znum;
unsigned int xnum, ynum, znum, x, y, z;
sprite_tracker_current_layout(fg, &xnum, &ynum, &znum);
if ((int)znum >= sprite_map->last_num_of_layers || (znum == 0 && (int)ynum > sprite_map->last_ynum)) realloc_sprite_texture(fg);
if ((int)znum >= sprite_map->last_num_of_layers || (znum == 0 && (int)ynum > sprite_map->last_ynum)) {
realloc_sprite_texture(fg);
sprite_tracker_current_layout(fg, &xnum, &ynum, &znum);
}
glBindTexture(GL_TEXTURE_2D_ARRAY, sprite_map->texture_id);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
sprite_index_to_pos(idx, xnum, ynum, &x, &y, &z);
x *= fg->fcm.cell_width; y *= fg->fcm.cell_height;
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, x, y, z, fg->fcm.cell_width, fg->fcm.cell_height, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, buf);
}
@ -228,7 +232,7 @@ init_cell_program(void) {
// Sanity check to ensure the attribute location binding worked
#define C(p, name, expected) { int aloc = attrib_location(p, #name); if (aloc != expected && aloc != -1) fatal("The attribute location for %s is %d != %d in program: %d", #name, aloc, expected, p); }
for (int p = CELL_PROGRAM; p < BORDERS_PROGRAM; p++) {
C(p, colors, 0); C(p, sprite_coords, 1); C(p, is_selected, 2);
C(p, colors, 0); C(p, sprite_idx, 1); C(p, is_selected, 2);
}
#undef C
for (int i = GRAPHICS_PROGRAM; i <= GRAPHICS_ALPHA_MASK_PROGRAM; i++) {
@ -249,7 +253,7 @@ create_cell_vao(void) {
#define A1(name, size, dtype, offset) A(name, size, dtype, (void*)(offsetof(GPUCell, offset)), sizeof(GPUCell))
add_buffer_to_vao(vao_idx, GL_ARRAY_BUFFER);
A1(sprite_coords, 4, GL_UNSIGNED_SHORT, sprite_x);
A1(sprite_idx, 2, GL_UNSIGNED_INT, sprite_idx);
A1(colors, 3, GL_UNSIGNED_INT, fg);
add_buffer_to_vao(vao_idx, GL_ARRAY_BUFFER);

View file

@ -338,6 +338,13 @@ extern GlobalState global_state;
else Py_DECREF(cret_); \
}
static inline void
sprite_index_to_pos(unsigned idx, unsigned xnum, unsigned ynum, unsigned *x, unsigned *y, unsigned *z) {
div_t r = div(idx & 0x7fffffff, ynum * xnum), r2 = div(r.rem, xnum);
*z = r.quot; *y = r2.quot; *x = r2.rem;
}
void gl_init(void);
void remove_vao(ssize_t vao_idx);
bool remove_os_window(id_type os_window_id);
@ -379,7 +386,7 @@ void update_surface_size(int, int, uint32_t);
void free_texture(uint32_t*);
void free_framebuffer(uint32_t*);
void send_image_to_gpu(uint32_t*, const void*, int32_t, int32_t, bool, bool, bool, RepeatStrategy);
void send_sprite_to_gpu(FONTS_DATA_HANDLE fg, unsigned int, unsigned int, unsigned int, pixel*);
void send_sprite_to_gpu(FONTS_DATA_HANDLE fg, unsigned int, pixel*);
void blank_canvas(float, color_type);
void blank_os_window(OSWindow *);
void set_os_window_chrome(OSWindow *w);

View file

@ -14,6 +14,7 @@
ParsedFontFeature,
get_fallback_font,
set_allow_use_of_box_fonts,
sprite_idx_to_pos,
sprite_map_set_layout,
sprite_map_set_limits,
test_render_line,
@ -231,7 +232,7 @@ def multiline_render(text, scale=1, width=1, **kw):
line = s.line(y)
test_render_line(line)
for x in range(width * scale):
ans.append(sprites[line.sprite_at(x)])
ans.append(sprites[sprite_idx_to_pos(line.sprite_at(x), setup_for_testing.xnum, setup_for_testing.ynum)])
return ans
def block_test(*expected, **kw):