git: 162a2b858854 - main - vt(4): New bitblt_text variant making a copy before unlocking vt_buf
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 24 Nov 2023 17:31:57 UTC
The branch main has been updated by dumbbell: URL: https://cgit.FreeBSD.org/src/commit/?id=162a2b858854656ddd6b75d290be8a640ba8f324 commit 162a2b858854656ddd6b75d290be8a640ba8f324 Author: Jean-Sébastien Pédron <dumbbell@FreeBSD.org> AuthorDate: 2023-11-24 17:30:34 +0000 Commit: Jean-Sébastien Pédron <dumbbell@FreeBSD.org> CommitDate: 2023-11-24 17:31:33 +0000 vt(4): New bitblt_text variant making a copy before unlocking vt_buf [Why] In the DRM drivers and the integration with vt(4), we need to execute DRM code outside of the vtbuf_lock. The reason is that this DRM code acquires locks which can't be acquired when vtbuf_lock, an MTX_SPIN mutex, is already held. [How] A vt(4) backend can now set the `vd_bitblt_after_vtbuf_unlock` flag to true if it wants to be called outside of vt_buf_lock. In this case, vt(4) uses an internal version of bitblt_text that uses the `vd_drawn` arrays, plus a new `vd_pos_to_flush` array, to track characters to draw/refresh. This internal version then uses the backend's bitblt_bmp callback to draw the characters after vt_buf has been unlocked. Drawing borders and CPU logos is also deferred after the vt_buf lock is released for the same reason. We introduce another lock (a default mutex), only used when the `vd_bitblt_after_vtbuf_unlock` flag is set, to replace part the role of the vt_buf lock and manage concurrent calls to vt_flush(). The `SC_NO_CONSDRAWN` define is dropped because we now always need the `vd_drawn` arrays. Reviewed by: manu Approved by: manu Differential Revision: https://reviews.freebsd.org/D42057 --- sys/dev/vt/vt.h | 19 ++++++ sys/dev/vt/vt_core.c | 180 +++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 185 insertions(+), 14 deletions(-) diff --git a/sys/dev/vt/vt.h b/sys/dev/vt/vt.h index a21ecea999ec..56a28c0420c7 100644 --- a/sys/dev/vt/vt.h +++ b/sys/dev/vt/vt.h @@ -165,6 +165,9 @@ struct vt_device { term_char_t *vd_drawn; /* (?) Most recent char drawn. */ term_color_t *vd_drawnfg; /* (?) Most recent fg color drawn. */ term_color_t *vd_drawnbg; /* (?) Most recent bg color drawn. */ + + struct mtx vd_flush_lock; /* (?) vt_flush() lock. */ + bool *vd_pos_to_flush;/* (?) Positions to flush. */ }; #define VD_PASTEBUF(vd) ((vd)->vd_pastebuf.vpb_buf) @@ -175,6 +178,14 @@ struct vt_device { #define VT_UNLOCK(vd) mtx_unlock(&(vd)->vd_lock) #define VT_LOCK_ASSERT(vd, what) mtx_assert(&(vd)->vd_lock, what) +#define VT_FLUSH_LOCK(vd) \ + if ((vd)->vd_driver->vd_bitblt_after_vtbuf_unlock) \ + mtx_lock(&(vd)->vd_flush_lock) + +#define VT_FLUSH_UNLOCK(vd) \ + if ((vd)->vd_driver->vd_bitblt_after_vtbuf_unlock) \ + mtx_unlock(&(vd)->vd_flush_lock) + void vt_resume(struct vt_device *vd); void vt_resume_flush_timer(struct vt_window *vw, int ms); void vt_suspend(struct vt_device *vd); @@ -376,6 +387,14 @@ struct vt_driver { #define VD_PRIORITY_DUMB 10 #define VD_PRIORITY_GENERIC 100 #define VD_PRIORITY_SPECIFIC 1000 + + /* + * Should vd_bitblt_text() be called after unlocking vtbuf? If true, + * characters are copied before vd_bitblt_bmp() is called. + * + * This is only valid when the default bitblt_text callback is used. + */ + bool vd_bitblt_after_vtbuf_unlock; }; /* diff --git a/sys/dev/vt/vt_core.c b/sys/dev/vt/vt_core.c index 6d44c81181a3..3e4db9a1ba30 100644 --- a/sys/dev/vt/vt_core.c +++ b/sys/dev/vt/vt_core.c @@ -202,11 +202,10 @@ SET_DECLARE(vt_drv_set, struct vt_driver); static struct terminal vt_consterm; static struct vt_window vt_conswindow; -#ifndef SC_NO_CONSDRAWN static term_char_t vt_consdrawn[PIXEL_HEIGHT(VT_FB_MAX_HEIGHT) * PIXEL_WIDTH(VT_FB_MAX_WIDTH)]; static term_color_t vt_consdrawnfg[PIXEL_HEIGHT(VT_FB_MAX_HEIGHT) * PIXEL_WIDTH(VT_FB_MAX_WIDTH)]; static term_color_t vt_consdrawnbg[PIXEL_HEIGHT(VT_FB_MAX_HEIGHT) * PIXEL_WIDTH(VT_FB_MAX_WIDTH)]; -#endif +static bool vt_cons_pos_to_flush[PIXEL_HEIGHT(VT_FB_MAX_HEIGHT) * PIXEL_WIDTH(VT_FB_MAX_WIDTH)]; struct vt_device vt_consdev = { .vd_driver = NULL, .vd_softc = NULL, @@ -228,11 +227,11 @@ struct vt_device vt_consdev = { .vd_mcursor_bg = TC_BLACK, #endif -#ifndef SC_NO_CONSDRAWN .vd_drawn = vt_consdrawn, .vd_drawnfg = vt_consdrawnfg, .vd_drawnbg = vt_consdrawnbg, -#endif + + .vd_pos_to_flush = vt_cons_pos_to_flush, }; static term_char_t vt_constextbuf[(_VTDEFW) * (VBF_DEFAULT_HISTORY_SIZE)]; static term_char_t *vt_constextbufrows[VBF_DEFAULT_HISTORY_SIZE]; @@ -292,6 +291,7 @@ vt_update_static(void *dummy) printf("VT: init without driver.\n"); mtx_init(&main_vd->vd_lock, "vtdev", NULL, MTX_DEF); + mtx_init(&main_vd->vd_flush_lock, "vtdev flush", NULL, MTX_DEF); cv_init(&main_vd->vd_winswitch, "vtwswt"); } @@ -1348,6 +1348,122 @@ vt_set_border(struct vt_device *vd, const term_rect_t *area, vd->vd_height - 1, 1, c); } +static void +vt_flush_to_buffer(struct vt_device *vd, + const struct vt_window *vw, const term_rect_t *area) +{ + unsigned int col, row; + term_char_t c; + term_color_t fg, bg; + size_t z; + + for (row = area->tr_begin.tp_row; row < area->tr_end.tp_row; ++row) { + for (col = area->tr_begin.tp_col; col < area->tr_end.tp_col; + ++col) { + z = row * PIXEL_WIDTH(VT_FB_MAX_WIDTH) + col; + if (z >= PIXEL_HEIGHT(VT_FB_MAX_HEIGHT) * + PIXEL_WIDTH(VT_FB_MAX_WIDTH)) + continue; + + c = VTBUF_GET_FIELD(&vw->vw_buf, row, col); + vt_determine_colors(c, + VTBUF_ISCURSOR(&vw->vw_buf, row, col), &fg, &bg); + + if (vd->vd_drawn && (vd->vd_drawn[z] == c) && + vd->vd_drawnfg && (vd->vd_drawnfg[z] == fg) && + vd->vd_drawnbg && (vd->vd_drawnbg[z] == bg)) { + vd->vd_pos_to_flush[z] = false; + continue; + } + + vd->vd_pos_to_flush[z] = true; + + if (vd->vd_drawn) + vd->vd_drawn[z] = c; + if (vd->vd_drawnfg) + vd->vd_drawnfg[z] = fg; + if (vd->vd_drawnbg) + vd->vd_drawnbg[z] = bg; + } + } +} + +static void +vt_bitblt_buffer(struct vt_device *vd, const struct vt_window *vw, + const term_rect_t *area) +{ + unsigned int col, row, x, y; + struct vt_font *vf; + term_char_t c; + term_color_t fg, bg; + const uint8_t *pattern; + size_t z; + + vf = vw->vw_font; + + for (row = area->tr_begin.tp_row; row < area->tr_end.tp_row; ++row) { + for (col = area->tr_begin.tp_col; col < area->tr_end.tp_col; + ++col) { + x = col * vf->vf_width + + vw->vw_draw_area.tr_begin.tp_col; + y = row * vf->vf_height + + vw->vw_draw_area.tr_begin.tp_row; + + z = row * PIXEL_WIDTH(VT_FB_MAX_WIDTH) + col; + if (z >= PIXEL_HEIGHT(VT_FB_MAX_HEIGHT) * + PIXEL_WIDTH(VT_FB_MAX_WIDTH)) + continue; + if (!vd->vd_pos_to_flush[z]) + continue; + + c = vd->vd_drawn[z]; + fg = vd->vd_drawnfg[z]; + bg = vd->vd_drawnbg[z]; + + pattern = vtfont_lookup(vf, c); + vd->vd_driver->vd_bitblt_bmp(vd, vw, + pattern, NULL, vf->vf_width, vf->vf_height, + x, y, fg, bg); + } + } + +#ifndef SC_NO_CUTPASTE + if (!vd->vd_mshown) + return; + + term_rect_t drawn_area; + + drawn_area.tr_begin.tp_col = area->tr_begin.tp_col * vf->vf_width; + drawn_area.tr_begin.tp_row = area->tr_begin.tp_row * vf->vf_height; + drawn_area.tr_end.tp_col = area->tr_end.tp_col * vf->vf_width; + drawn_area.tr_end.tp_row = area->tr_end.tp_row * vf->vf_height; + + if (vt_is_cursor_in_area(vd, &drawn_area)) { + vd->vd_driver->vd_bitblt_bmp(vd, vw, + vd->vd_mcursor->map, vd->vd_mcursor->mask, + vd->vd_mcursor->width, vd->vd_mcursor->height, + vd->vd_mx_drawn + vw->vw_draw_area.tr_begin.tp_col, + vd->vd_my_drawn + vw->vw_draw_area.tr_begin.tp_row, + vd->vd_mcursor_fg, vd->vd_mcursor_bg); + } +#endif +} + +static void +vt_draw_decorations(struct vt_device *vd) +{ + struct vt_window *vw; + const teken_attr_t *a; + + vw = vd->vd_curwindow; + + a = teken_get_curattr(&vw->vw_terminal->tm_emulator); + vt_set_border(vd, &vw->vw_draw_area, a->ta_bgcolor); + + if (vt_draw_logo_cpus) + vtterm_draw_cpu_logos(vd); +} + static int vt_flush(struct vt_device *vd) { @@ -1357,6 +1473,7 @@ vt_flush(struct vt_device *vd) #ifndef SC_NO_CUTPASTE int cursor_was_shown, cursor_moved; #endif + bool needs_refresh; if (inside_vt_flush && KERNEL_PANICKED()) return (0); @@ -1372,8 +1489,9 @@ vt_flush(struct vt_device *vd) if (((vd->vd_flags & VDF_TEXTMODE) == 0) && (vf == NULL)) return (0); - vtbuf_lock(&vw->vw_buf); + VT_FLUSH_LOCK(vd); + vtbuf_lock(&vw->vw_buf); inside_vt_flush = true; #ifndef SC_NO_CUTPASTE @@ -1417,29 +1535,63 @@ vt_flush(struct vt_device *vd) vtbuf_undirty(&vw->vw_buf, &tarea); /* Force a full redraw when the screen contents might be invalid. */ + needs_refresh = false; if (vd->vd_flags & (VDF_INVALID | VDF_SUSPENDED)) { - const teken_attr_t *a; - + needs_refresh = true; vd->vd_flags &= ~VDF_INVALID; - a = teken_get_curattr(&vw->vw_terminal->tm_emulator); - vt_set_border(vd, &vw->vw_draw_area, a->ta_bgcolor); vt_termrect(vd, vf, &tarea); if (vd->vd_driver->vd_invalidate_text) vd->vd_driver->vd_invalidate_text(vd, &tarea); - if (vt_draw_logo_cpus) - vtterm_draw_cpu_logos(vd); } if (tarea.tr_begin.tp_col < tarea.tr_end.tp_col) { - vd->vd_driver->vd_bitblt_text(vd, vw, &tarea); - inside_vt_flush = false; - vtbuf_unlock(&vw->vw_buf); + if (vd->vd_driver->vd_bitblt_after_vtbuf_unlock) { + /* + * When `vd_bitblt_after_vtbuf_unlock` is set to true, + * we first remember the characters to redraw. They are + * already copied to the `vd_drawn` arrays. + * + * We then unlock vt_buf and proceed with the actual + * drawing using the backend driver. + */ + vt_flush_to_buffer(vd, vw, &tarea); + vtbuf_unlock(&vw->vw_buf); + vt_bitblt_buffer(vd, vw, &tarea); + + if (needs_refresh) + vt_draw_decorations(vd); + + /* + * We can reset `inside_vt_flush` after unlocking vtbuf + * here because we also hold vt_flush_lock in this code + * path. + */ + inside_vt_flush = false; + } else { + /* + * When `vd_bitblt_after_vtbuf_unlock` is false, we use + * the backend's `vd_bitblt_text` callback directly. + */ + vd->vd_driver->vd_bitblt_text(vd, vw, &tarea); + + if (needs_refresh) + vt_draw_decorations(vd); + + inside_vt_flush = false; + vtbuf_unlock(&vw->vw_buf); + } + + VT_FLUSH_UNLOCK(vd); + return (1); } inside_vt_flush = false; vtbuf_unlock(&vw->vw_buf); + + VT_FLUSH_UNLOCK(vd); + return (0); }