mirror of
https://github.com/freebsd/freebsd-src
synced 2024-10-15 04:43:53 +00:00
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
This commit is contained in:
parent
24d6f256f8
commit
162a2b8588
|
@ -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;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -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) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue