mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-13 04:27:47 -05:00
wip: initial multithreaded renderer
This commit is contained in:
parent
d1b88f67e4
commit
c531795b83
8 changed files with 287 additions and 89 deletions
199
render.c
199
render.c
|
|
@ -76,45 +76,52 @@ gseq_flush(struct terminal *term, struct buffer *buf)
|
|||
}
|
||||
|
||||
static void
|
||||
draw_underline(const struct terminal *term, struct buffer *buf,
|
||||
draw_underline(const struct terminal *term, struct buffer *buf, size_t buf_idx,
|
||||
const struct font *font, struct rgb color, double x, double y)
|
||||
{
|
||||
//const struct font *font = attrs_to_font(term, &cell->attrs);
|
||||
double baseline = y + term->fextents.height - term->fextents.descent;
|
||||
double width = font->underline.thickness;
|
||||
double y_under = baseline - font->underline.position - width / 2.;
|
||||
cairo_t *cr = buf->cairo[buf_idx];
|
||||
|
||||
cairo_set_source_rgb(buf->cairo, color.r, color.g, color.b);
|
||||
cairo_set_line_width(buf->cairo, width);
|
||||
cairo_move_to(buf->cairo, x, round(y_under) + 0.5);
|
||||
cairo_rel_line_to(buf->cairo, term->cell_width, 0);
|
||||
cairo_stroke(buf->cairo);
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_set_source_rgb(cr, color.r, color.g, color.b);
|
||||
cairo_set_line_width(cr, width);
|
||||
cairo_move_to(cr, x, round(y_under) + 0.5);
|
||||
cairo_rel_line_to(cr, term->cell_width, 0);
|
||||
cairo_stroke(cr);
|
||||
}
|
||||
|
||||
static void
|
||||
draw_bar(const struct terminal *term, struct buffer *buf, struct rgb color,
|
||||
double x, double y)
|
||||
draw_bar(const struct terminal *term, struct buffer *buf, size_t buf_idx,
|
||||
struct rgb color, double x, double y)
|
||||
{
|
||||
cairo_set_source_rgb(buf->cairo, color.r, color.g, color.b);
|
||||
cairo_set_line_width(buf->cairo, 1.0);
|
||||
cairo_move_to(buf->cairo, x + 0.5, y);
|
||||
cairo_rel_line_to(buf->cairo, 0, term->cell_height);
|
||||
cairo_stroke(buf->cairo);
|
||||
cairo_t *cr = buf->cairo[buf_idx];
|
||||
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_set_source_rgb(cr, color.r, color.g, color.b);
|
||||
cairo_set_line_width(cr, 1.0);
|
||||
cairo_move_to(cr, x + 0.5, y);
|
||||
cairo_rel_line_to(cr, 0, term->cell_height);
|
||||
cairo_stroke(cr);
|
||||
}
|
||||
|
||||
static void
|
||||
draw_strikeout(const struct terminal *term, struct buffer *buf,
|
||||
draw_strikeout(const struct terminal *term, struct buffer *buf, size_t buf_idx,
|
||||
const struct font *font, struct rgb color, double x, double y)
|
||||
{
|
||||
double baseline = y + term->fextents.height - term->fextents.descent;
|
||||
double width = font->strikeout.thickness;
|
||||
double y_strike = baseline - font->strikeout.position - width / 2.;
|
||||
cairo_t *cr = buf->cairo[buf_idx];
|
||||
|
||||
cairo_set_source_rgb(buf->cairo, color.r, color.g, color.b);
|
||||
cairo_set_line_width(buf->cairo, width);
|
||||
cairo_move_to(buf->cairo, x, round(y_strike) + 0.5);
|
||||
cairo_rel_line_to(buf->cairo, term->cell_width, 0);
|
||||
cairo_stroke(buf->cairo);
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_set_source_rgb(cr, color.r, color.g, color.b);
|
||||
cairo_set_line_width(cr, width);
|
||||
cairo_move_to(cr, x, round(y_strike) + 0.5);
|
||||
cairo_rel_line_to(cr, term->cell_width, 0);
|
||||
cairo_stroke(cr);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
|
@ -166,9 +173,10 @@ arm_blink_timer(struct terminal *term)
|
|||
}
|
||||
|
||||
static void
|
||||
render_cell(struct terminal *term, struct buffer *buf, const struct cell *cell,
|
||||
int col, int row, bool has_cursor)
|
||||
render_cell(struct terminal *term, struct buffer *buf, size_t buf_idx,
|
||||
const struct cell *cell, int col, int row, bool has_cursor)
|
||||
{
|
||||
cairo_t *cr = buf->cairo[buf_idx];
|
||||
double width = term->cell_width;
|
||||
double height = term->cell_height;
|
||||
double x = col * width;
|
||||
|
|
@ -208,18 +216,19 @@ render_cell(struct terminal *term, struct buffer *buf, const struct cell *cell,
|
|||
}
|
||||
|
||||
/* Background */
|
||||
cairo_set_source_rgb(buf->cairo, bg.r, bg.g, bg.b);
|
||||
cairo_rectangle(buf->cairo, x, y, width, height);
|
||||
cairo_fill(buf->cairo);
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_set_source_rgb(cr, bg.r, bg.g, bg.b);
|
||||
cairo_rectangle(cr, x, y, width, height);
|
||||
cairo_fill(cr);
|
||||
|
||||
/* Non-block cursors */
|
||||
if (has_cursor) {
|
||||
struct rgb cursor_color = color_hex_to_rgb(term->cursor_color.cursor);
|
||||
if (term->cursor_style == CURSOR_BAR)
|
||||
draw_bar(term, buf, cursor_color, x, y);
|
||||
draw_bar(term, buf, buf_idx, cursor_color, x, y);
|
||||
else if (term->cursor_style == CURSOR_UNDERLINE)
|
||||
draw_underline(
|
||||
term, buf, attrs_to_font(term, &cell->attrs), cursor_color, x, y);
|
||||
term, buf, buf_idx, attrs_to_font(term, &cell->attrs), cursor_color, x, y);
|
||||
}
|
||||
|
||||
if (cell->attrs.blink && !term->blink.active) {
|
||||
|
|
@ -232,10 +241,10 @@ render_cell(struct terminal *term, struct buffer *buf, const struct cell *cell,
|
|||
|
||||
/* Underline */
|
||||
if (cell->attrs.underline)
|
||||
draw_underline(term, buf, attrs_to_font(term, &cell->attrs), fg, x, y);
|
||||
draw_underline(term, buf, buf_idx, attrs_to_font(term, &cell->attrs), fg, x, y);
|
||||
|
||||
if (cell->attrs.strikethrough)
|
||||
draw_strikeout(term, buf, attrs_to_font(term, &cell->attrs), fg, x, y);
|
||||
draw_strikeout(term, buf, buf_idx, attrs_to_font(term, &cell->attrs), fg, x, y);
|
||||
|
||||
/*
|
||||
* cairo_show_glyphs() apparently works *much* faster when
|
||||
|
|
@ -268,15 +277,25 @@ render_cell(struct terminal *term, struct buffer *buf, const struct cell *cell,
|
|||
|
||||
struct glyph _glyph;
|
||||
if (glyph == NULL) {
|
||||
if (!font_glyph_for_utf8(font, cell->c, &_glyph))
|
||||
if (!font_glyph_for_utf8(font, cell->c, &_glyph)) {
|
||||
LOG_ERR("FAILED: %.4s", cell->c);
|
||||
return;
|
||||
}
|
||||
glyph = &_glyph;
|
||||
}
|
||||
|
||||
assert(glyph != NULL);
|
||||
cairo_set_source_rgb(buf->cairo, fg.r, fg.g, fg.b);
|
||||
cairo_set_operator(buf->cairo, CAIRO_OPERATOR_OVER);
|
||||
cairo_mask_surface(buf->cairo, glyph->surf, x + glyph->left, y + term->fextents.ascent - glyph->top);
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
|
||||
cairo_set_source_rgb(cr, fg.r, fg.g, fg.b);
|
||||
|
||||
#if 0
|
||||
cairo_surface_t *surf = cairo_image_surface_create_for_data(
|
||||
glyph->data, glyph->format, glyph->width, glyph->height, glyph->stride);
|
||||
cairo_mask_surface(cr, surf, x + glyph->left, y + term->fextents.ascent - glyph->top);
|
||||
cairo_surface_destroy(surf);
|
||||
#else
|
||||
cairo_mask_surface(cr, glyph->surf, x + glyph->left, y + term->fextents.ascent - glyph->top);
|
||||
#endif
|
||||
|
||||
if (glyph == &_glyph) {
|
||||
cairo_surface_destroy(_glyph.surf);
|
||||
|
|
@ -303,11 +322,11 @@ grid_render_scroll(struct terminal *term, struct buffer *buf,
|
|||
buf->size);
|
||||
|
||||
if (height > 0) {
|
||||
cairo_surface_flush(buf->cairo_surface);
|
||||
uint8_t *raw = cairo_image_surface_get_data(buf->cairo_surface);
|
||||
cairo_surface_flush(buf->cairo_surface[0]);
|
||||
uint8_t *raw = cairo_image_surface_get_data(buf->cairo_surface[0]);
|
||||
|
||||
memmove(raw + dst_y * stride, raw + src_y * stride, height * stride);
|
||||
cairo_surface_mark_dirty(buf->cairo_surface);
|
||||
cairo_surface_mark_dirty(buf->cairo_surface[0]);
|
||||
|
||||
wl_surface_damage_buffer(term->wl.surface, 0, dst_y, width, height);
|
||||
}
|
||||
|
|
@ -332,26 +351,74 @@ grid_render_scroll_reverse(struct terminal *term, struct buffer *buf,
|
|||
buf->size);
|
||||
|
||||
if (height > 0) {
|
||||
cairo_surface_flush(buf->cairo_surface);
|
||||
uint8_t *raw = cairo_image_surface_get_data(buf->cairo_surface);
|
||||
cairo_surface_flush(buf->cairo_surface[0]);
|
||||
uint8_t *raw = cairo_image_surface_get_data(buf->cairo_surface[0]);
|
||||
|
||||
memmove(raw + dst_y * stride, raw + src_y * stride, height * stride);
|
||||
cairo_surface_mark_dirty(buf->cairo_surface);
|
||||
cairo_surface_mark_dirty(buf->cairo_surface[0]);
|
||||
|
||||
wl_surface_damage_buffer(term->wl.surface, 0, dst_y, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
render_row(struct terminal *term, struct buffer *buf, struct row *row, int row_no)
|
||||
render_row(struct terminal *term, struct buffer *buf, size_t buf_idx, struct row *row, int row_no)
|
||||
{
|
||||
for (int col = 0; col < term->cols; col++)
|
||||
render_cell(term, buf, &row->cells[col], col, row_no, false);
|
||||
render_cell(term, buf, buf_idx, &row->cells[col], col, row_no, false);
|
||||
|
||||
#if 0
|
||||
wl_surface_damage_buffer(
|
||||
term->wl.surface,
|
||||
0, row_no * term->cell_height,
|
||||
term->width, term->cell_height);
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
render_worker_thread(void *_ctx)
|
||||
{
|
||||
struct render_worker_context *ctx = _ctx;
|
||||
struct terminal *term = ctx->term;
|
||||
const int my_id = ctx->my_id;
|
||||
|
||||
sem_t *start = &term->render.workers.start;
|
||||
sem_t *done = &term->render.workers.done;
|
||||
mtx_t *lock = &term->render.workers.lock;
|
||||
cnd_t *cond = &term->render.workers.cond;
|
||||
|
||||
while (true) {
|
||||
sem_wait(start);
|
||||
|
||||
struct buffer *buf = term->render.workers.buf;
|
||||
bool frame_done = false;
|
||||
|
||||
while (!frame_done) {
|
||||
mtx_lock(lock);
|
||||
while (tll_length(term->render.workers.queue) == 0)
|
||||
cnd_wait(cond, lock);
|
||||
|
||||
int row_no = tll_pop_front(term->render.workers.queue);
|
||||
mtx_unlock(lock);
|
||||
|
||||
switch (row_no) {
|
||||
default:
|
||||
assert(buf != NULL);
|
||||
render_row(term, buf, my_id, grid_row_in_view(term->grid, row_no), row_no);
|
||||
break;
|
||||
|
||||
case -1:
|
||||
frame_done = true;
|
||||
sem_post(done);
|
||||
break;
|
||||
|
||||
case -2:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void frame_callback(
|
||||
|
|
@ -374,8 +441,8 @@ grid_render(struct terminal *term)
|
|||
assert(term->width > 0);
|
||||
assert(term->height > 0);
|
||||
|
||||
struct buffer *buf = shm_get_buffer(term->wl.shm, term->width, term->height);
|
||||
cairo_set_operator(buf->cairo, CAIRO_OPERATOR_SOURCE);
|
||||
struct buffer *buf = shm_get_buffer(term->wl.shm, term->width, term->height, 1 + term->render.workers.count);
|
||||
cairo_set_operator(buf->cairo[0], CAIRO_OPERATOR_SOURCE);
|
||||
|
||||
gseq.g = gseq.glyphs;
|
||||
gseq.count = 0;
|
||||
|
|
@ -385,7 +452,7 @@ grid_render(struct terminal *term)
|
|||
/* Erase old cursor (if we rendered a cursor last time) */
|
||||
if (term->render.last_cursor.cell != NULL) {
|
||||
render_cell(
|
||||
term, buf,
|
||||
term, buf, 0,
|
||||
term->render.last_cursor.cell,
|
||||
term->render.last_cursor.in_view.col,
|
||||
term->render.last_cursor.in_view.row, false);
|
||||
|
|
@ -424,11 +491,11 @@ grid_render(struct terminal *term)
|
|||
|
||||
uint32_t _bg = !term->reverse ? term->colors.bg : term->colors.fg;
|
||||
struct rgb bg = color_hex_to_rgb(_bg);
|
||||
cairo_set_source_rgb(buf->cairo, bg.r, bg.g, bg.b);
|
||||
cairo_set_source_rgb(buf->cairo[0], bg.r, bg.g, bg.b);
|
||||
|
||||
cairo_rectangle(buf->cairo, rmargin, 0, rmargin_width, term->height);
|
||||
cairo_rectangle(buf->cairo, 0, bmargin, term->width, bmargin_height);
|
||||
cairo_fill(buf->cairo);
|
||||
cairo_rectangle(buf->cairo[0], rmargin, 0, rmargin_width, term->height);
|
||||
cairo_rectangle(buf->cairo[0], 0, bmargin, term->width, bmargin_height);
|
||||
cairo_fill(buf->cairo[0]);
|
||||
|
||||
wl_surface_damage_buffer(
|
||||
term->wl.surface, rmargin, 0, rmargin_width, term->height);
|
||||
|
|
@ -456,20 +523,38 @@ grid_render(struct terminal *term)
|
|||
tll_remove(term->grid->scroll_damage, it);
|
||||
}
|
||||
|
||||
term->render.workers.buf = buf;
|
||||
for (size_t i = 0; i < term->render.workers.count; i++)
|
||||
sem_post(&term->render.workers.start);
|
||||
|
||||
assert(tll_length(term->render.workers.queue) == 0);
|
||||
|
||||
for (int r = 0; r < term->rows; r++) {
|
||||
struct row *row = grid_row_in_view(term->grid, r);
|
||||
|
||||
if (!row->dirty)
|
||||
continue;
|
||||
|
||||
//LOG_WARN("rendering line: %d", r);
|
||||
mtx_lock(&term->render.workers.lock);
|
||||
tll_push_back(term->render.workers.queue, r);
|
||||
cnd_signal(&term->render.workers.cond);
|
||||
mtx_unlock(&term->render.workers.lock);
|
||||
|
||||
row->dirty = false;
|
||||
all_clean = false;
|
||||
|
||||
render_row(term, buf, row, r);
|
||||
wl_surface_damage_buffer(
|
||||
term->wl.surface,
|
||||
0, r * term->cell_height,
|
||||
term->width, term->cell_height);
|
||||
}
|
||||
|
||||
mtx_lock(&term->render.workers.lock);
|
||||
for (size_t i = 0; i < term->render.workers.count; i++)
|
||||
tll_push_back(term->render.workers.queue, -1);
|
||||
cnd_broadcast(&term->render.workers.cond);
|
||||
mtx_unlock(&term->render.workers.lock);
|
||||
|
||||
if (term->blink.active) {
|
||||
/* Check if there are still any visible blinking cells */
|
||||
bool none_is_blinking = true;
|
||||
|
|
@ -516,6 +601,10 @@ grid_render(struct terminal *term)
|
|||
cursor_is_visible = true;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < term->render.workers.count; i++)
|
||||
sem_wait(&term->render.workers.done);
|
||||
term->render.workers.buf = NULL;
|
||||
|
||||
if (cursor_is_visible && !term->hide_cursor) {
|
||||
/* Remember cursor coordinates so that we can erase it next
|
||||
* time. Note that we need to re-align it against the view. */
|
||||
|
|
@ -531,7 +620,7 @@ grid_render(struct terminal *term)
|
|||
|
||||
term->render.last_cursor.cell = &row->cells[term->cursor.col];
|
||||
render_cell(
|
||||
term, buf, term->render.last_cursor.cell,
|
||||
term, buf, 0, term->render.last_cursor.cell,
|
||||
term->cursor.col, view_aligned_row, true);
|
||||
|
||||
wl_surface_damage_buffer(
|
||||
|
|
@ -549,10 +638,10 @@ grid_render(struct terminal *term)
|
|||
}
|
||||
|
||||
if (term->flash.active) {
|
||||
cairo_set_source_rgba(buf->cairo, 1.0, 1.0, 0.0, 0.5);
|
||||
cairo_set_operator(buf->cairo, CAIRO_OPERATOR_OVER);
|
||||
cairo_rectangle(buf->cairo, 0, 0, term->width, term->height);
|
||||
cairo_fill(buf->cairo);
|
||||
cairo_set_source_rgba(buf->cairo[0], 1.0, 1.0, 0.0, 0.5);
|
||||
cairo_set_operator(buf->cairo[0], CAIRO_OPERATOR_OVER);
|
||||
cairo_rectangle(buf->cairo[0], 0, 0, term->width, term->height);
|
||||
cairo_fill(buf->cairo[0]);
|
||||
|
||||
wl_surface_damage_buffer(
|
||||
term->wl.surface, 0, 0, term->width, term->height);
|
||||
|
|
@ -561,7 +650,7 @@ grid_render(struct terminal *term)
|
|||
assert(term->grid->offset >= 0 && term->grid->offset < term->grid->num_rows);
|
||||
assert(term->grid->view >= 0 && term->grid->view < term->grid->num_rows);
|
||||
|
||||
cairo_surface_flush(buf->cairo_surface);
|
||||
cairo_surface_flush(buf->cairo_surface[0]);
|
||||
wl_surface_attach(term->wl.surface, buf->wl_buf, 0, 0);
|
||||
|
||||
assert(term->render.frame_callback == NULL);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue