Module Name: src Committed By: macallan Date: Tue Jan 25 20:28:21 UTC 2011
Modified Files: src/sys/dev/wscons: files.wscons wsdisplay_vcons.c wsdisplay_vconsvar.h Log Message: Add support for asynchronous drawing in vcons. This is not finished but good enough for others to play with, enable with options VCONS_DRAW_ASYNC With this all drawing operations will be posted to a ring buffer instead of being run directly, and run by a kernel thread. This avoids having to wait for drawing operations to finish with the kernel lock held ( to a degree at least ) and scrolling a (slow) framebuffer console should not disrupt other operations anymore. Problems: - we need to switch back to synchronous operations when panicing or entering ddb, also re-enable async drawing when leaving ddb - there are still occasional glitches tested on an SS20 with cg14 and cg6 for dumb and accelerated cases To generate a diff of this commit: cvs rdiff -u -r1.41 -r1.42 src/sys/dev/wscons/files.wscons cvs rdiff -u -r1.18 -r1.19 src/sys/dev/wscons/wsdisplay_vcons.c cvs rdiff -u -r1.12 -r1.13 src/sys/dev/wscons/wsdisplay_vconsvar.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/wscons/files.wscons diff -u src/sys/dev/wscons/files.wscons:1.41 src/sys/dev/wscons/files.wscons:1.42 --- src/sys/dev/wscons/files.wscons:1.41 Thu Aug 20 02:01:08 2009 +++ src/sys/dev/wscons/files.wscons Tue Jan 25 20:28:21 2011 @@ -1,4 +1,4 @@ -# $NetBSD: files.wscons,v 1.41 2009/08/20 02:01:08 macallan Exp $ +# $NetBSD: files.wscons,v 1.42 2011/01/25 20:28:21 macallan Exp $ # # "Workstation Console" glue; attaches frame buffer to emulator & keyboard, @@ -75,4 +75,4 @@ # generic virtual console support on bitmapped framebuffers file dev/wscons/wsdisplay_vcons.c vcons file dev/wscons/wsdisplay_vcons_util.c vcons -defflag opt_vcons.h VCONS_SWITCH_ASYNC +defflag opt_vcons.h VCONS_DRAW_ASYNC VCONS_ASYNC_DEBUG Index: src/sys/dev/wscons/wsdisplay_vcons.c diff -u src/sys/dev/wscons/wsdisplay_vcons.c:1.18 src/sys/dev/wscons/wsdisplay_vcons.c:1.19 --- src/sys/dev/wscons/wsdisplay_vcons.c:1.18 Tue Sep 21 03:33:14 2010 +++ src/sys/dev/wscons/wsdisplay_vcons.c Tue Jan 25 20:28:21 2011 @@ -1,4 +1,4 @@ -/* $NetBSD: wsdisplay_vcons.c,v 1.18 2010/09/21 03:33:14 macallan Exp $ */ +/* $NetBSD: wsdisplay_vcons.c,v 1.19 2011/01/25 20:28:21 macallan Exp $ */ /*- * Copyright (c) 2005, 2006 Michael Lorenz @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: wsdisplay_vcons.c,v 1.18 2010/09/21 03:33:14 macallan Exp $"); +__KERNEL_RCSID(0, "$NetBSD: wsdisplay_vcons.c,v 1.19 2011/01/25 20:28:21 macallan Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -42,6 +42,7 @@ #include <sys/proc.h> #include <sys/kthread.h> #include <sys/tprintf.h> +#include <sys/atomic.h> #include <dev/wscons/wsdisplayvar.h> #include <dev/wscons/wsconsio.h> @@ -78,6 +79,16 @@ static void vcons_eraserows_buffer(void *, int, int, long); static void vcons_putchar_buffer(void *, int, int, u_int, long); +#ifdef VCONS_DRAW_ASYNC +/* methods that work asynchronously */ +static void vcons_copycols_async(void *, int, int, int, int); +static void vcons_erasecols_async(void *, int, int, int, long); +static void vcons_copyrows_async(void *, int, int, int); +static void vcons_eraserows_async(void *, int, int, long); +static void vcons_putchar_async(void *, int, int, u_int, long); +static void vcons_cursor_async(void *, int, int, int); +#endif + /* * actual wrapper methods which call both the _buffer ones above and the * driver supplied ones to do the drawing @@ -103,7 +114,7 @@ static void vcons_lock(struct vcons_screen *); static void vcons_unlock(struct vcons_screen *); -#ifdef VCONS_SWITCH_ASYNC +#ifdef VCONS_DRAW_ASYNC static void vcons_kthread(void *); #endif @@ -148,9 +159,9 @@ #ifdef DIAGNOSTIC vd->switch_poll_count = 0; #endif -#ifdef VCONS_SWITCH_ASYNC +#ifdef VCONS_DRAW_ASYNC kthread_create(PRI_NONE, 0, NULL, vcons_kthread, vd, - &vd->redraw_thread, "vcons_draw"); + &vd->drawing_thread, "vcons_draw"); #endif return 0; } @@ -181,9 +192,6 @@ #ifdef VCONS_PARANOIA splx(s); #endif -#ifdef VCONS_SWITCH_ASYNC - wakeup(&scr->scr_vd->done_drawing); -#endif } static void @@ -223,29 +231,30 @@ * our wrappers */ vd->eraserows = ri->ri_ops.eraserows; - vd->copyrows = ri->ri_ops.copyrows; vd->erasecols = ri->ri_ops.erasecols; - vd->copycols = ri->ri_ops.copycols; vd->putchar = ri->ri_ops.putchar; vd->cursor = ri->ri_ops.cursor; - ri->ri_ops.eraserows = vcons_eraserows; - ri->ri_ops.erasecols = vcons_erasecols; - ri->ri_ops.putchar = vcons_putchar; - ri->ri_ops.cursor = vcons_cursor; - if (scr->scr_flags & VCONS_NO_COPYCOLS) { - ri->ri_ops.copycols = vcons_copycols_noread; + vd->copycols = vcons_copycols_noread; } else { - ri->ri_ops.copycols = vcons_copycols; + vd->copycols = ri->ri_ops.copycols; } if (scr->scr_flags & VCONS_NO_COPYROWS) { - ri->ri_ops.copyrows = vcons_copyrows_noread; + vd->copyrows = vcons_copyrows_noread; } else { - ri->ri_ops.copyrows = vcons_copyrows; + vd->copyrows = ri->ri_ops.copyrows; } + ri->ri_ops.eraserows = vcons_eraserows; + ri->ri_ops.erasecols = vcons_erasecols; + ri->ri_ops.putchar = vcons_putchar; + ri->ri_ops.cursor = vcons_cursor; + ri->ri_ops.copycols = vcons_copycols; + ri->ri_ops.copyrows = vcons_copyrows; + + ri->ri_hw = scr; /* @@ -520,10 +529,6 @@ vd->wanted = scr; vd->switch_cb = cb; vd->switch_cb_arg = cb_arg; -#ifdef VCONS_SWITCH_ASYNC - wakeup(&vd->start_drawing); - return EAGAIN; -#else if (cb) { callout_schedule(&vd->switch_callout, 0); return EAGAIN; @@ -531,7 +536,6 @@ vcons_do_switch(vd); return 0; -#endif } /* wrappers for rasops_info methods */ @@ -570,7 +574,13 @@ vcons_lock(scr); if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { - scr->scr_vd->copycols(cookie, row, srccol, dstcol, ncols); +#ifdef VCONS_DRAW_ASYNC + struct vcons_data *vd = scr->scr_vd; + if (vd->use_async) { + vcons_copycols_async(cookie, row, srccol, dstcol, ncols); + } else +#endif + scr->scr_vd->copycols(cookie, row, srccol, dstcol, ncols); } vcons_unlock(scr); } @@ -581,9 +591,6 @@ struct rasops_info *ri = cookie; struct vcons_screen *scr = ri->ri_hw; - vcons_copycols_buffer(cookie, row, srccol, dstcol, ncols); - - vcons_lock(scr); if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { int pos, c, offset; @@ -599,7 +606,6 @@ pos++; } } - vcons_unlock(scr); } static void @@ -636,8 +642,15 @@ vcons_lock(scr); if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { - scr->scr_vd->erasecols(cookie, row, startcol, ncols, - fillattr); +#ifdef VCONS_DRAW_ASYNC + struct vcons_data *vd = scr->scr_vd; + if (vd->use_async) { + vcons_erasecols_async(cookie, row, startcol, ncols, + fillattr); + } else +#endif + scr->scr_vd->erasecols(cookie, row, startcol, ncols, + fillattr); } vcons_unlock(scr); } @@ -688,7 +701,13 @@ vcons_lock(scr); if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { - scr->scr_vd->copyrows(cookie, srcrow, dstrow, nrows); +#ifdef VCONS_DRAW_ASYNC + struct vcons_data *vd = scr->scr_vd; + if (vd->use_async) { + vcons_copyrows_async(cookie, srcrow, dstrow, nrows); + } else +#endif + scr->scr_vd->copyrows(cookie, srcrow, dstrow, nrows); } vcons_unlock(scr); } @@ -699,9 +718,6 @@ struct rasops_info *ri = cookie; struct vcons_screen *scr = ri->ri_hw; - vcons_copyrows_buffer(cookie, srcrow, dstrow, nrows); - - vcons_lock(scr); if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { int pos, l, c, offset; @@ -719,7 +735,6 @@ } } } - vcons_unlock(scr); } static void @@ -756,7 +771,13 @@ vcons_lock(scr); if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { - scr->scr_vd->eraserows(cookie, row, nrows, fillattr); +#ifdef VCONS_DRAW_ASYNC + struct vcons_data *vd = scr->scr_vd; + if (vd->use_async) { + vcons_eraserows_async(cookie, row, nrows, fillattr); + } else +#endif + scr->scr_vd->eraserows(cookie, row, nrows, fillattr); } vcons_unlock(scr); } @@ -798,7 +819,13 @@ vcons_lock(scr); if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { - scr->scr_vd->putchar(cookie, row, col, c, attr); +#ifdef VCONS_DRAW_ASYNC + struct vcons_data *vd = scr->scr_vd; + if (vd->use_async) { + vcons_putchar_async(cookie, row, col, c, attr); + } else +#endif + scr->scr_vd->putchar(cookie, row, col, c, attr); } vcons_unlock(scr); } @@ -811,7 +838,13 @@ vcons_lock(scr); if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { - scr->scr_vd->cursor(cookie, on, row, col); +#ifdef VCONS_DRAW_ASYNC + struct vcons_data *vd = scr->scr_vd; + if (vd->use_async) { + vcons_cursor_async(cookie, on, row, col); + } else +#endif + scr->scr_vd->cursor(cookie, on, row, col); } else { scr->scr_ri.ri_crow = row; scr->scr_ri.ri_ccol = col; @@ -967,46 +1000,334 @@ /* async drawing using a kernel thread */ -#ifdef VCONS_SWITCH_ASYNC +#ifdef VCONS_DRAW_ASYNC + +static inline uint32_t +vcons_words_in_buffer(struct vcons_data *vd) +{ + int len = vd->rb_write - vd->rb_read; + + if (len < 0) len += VCONS_RING_BUFFER_LENGTH; + if (len < 0) vd->use_async = 0; + if (len >= VCONS_RING_BUFFER_LENGTH) vd->use_async = 0; + return (uint32_t)len; +} + +static inline int +vcons_wait_buffer(struct vcons_data *vd, uint32_t words) +{ + int bail = 0; + + mutex_enter(&vd->go_buffer_il); + while (((VCONS_RING_BUFFER_LENGTH - vcons_words_in_buffer(vd)) < words) + && (bail < 3)) { + if (cv_timedwait(&vd->go_buffer, &vd->go_buffer_il, hz) + == EWOULDBLOCK) + bail++; + } + if (bail >= 3) { + /* + * waited too long, something is wrong so fall back to sync + * we should probably kill the kthread here and try to empty + * the command buffer as well + */ + vd->use_async = 0; + } + return 0; +} + +#define VRB_NEXT(idx) ((idx + 1) >= VCONS_RING_BUFFER_LENGTH) ? 0 : idx + 1 + static void -vcons_kthread(void *cookie) +vcons_copycols_async(void *cookie, int row, int srccol, int dstcol, int ncols) { - struct vcons_data *vd = cookie; - struct vcons_screen *scr; - int sec = hz; + struct rasops_info *ri = cookie; + struct vcons_screen *scr = ri->ri_hw; + struct vcons_data *vd = scr->scr_vd; + int idx; - while (1) { + vcons_wait_buffer(vd, 5); + mutex_enter(&vd->drawing_mutex); + mutex_exit(&vd->go_buffer_il); + idx = vd->rb_write; + vd->rb_buffer[idx] = VCMD_COPYCOLS; + idx = VRB_NEXT(idx); + vd->rb_buffer[idx] = row; + idx = VRB_NEXT(idx); + vd->rb_buffer[idx] = srccol; + idx = VRB_NEXT(idx); + vd->rb_buffer[idx] = dstcol; + idx = VRB_NEXT(idx); + vd->rb_buffer[idx] = ncols; + idx = VRB_NEXT(idx); + membar_producer(); + vd->rb_write = idx; + membar_enter(); + mutex_exit(&vd->drawing_mutex); + cv_signal(&vd->go_draw); +} + +static void +vcons_erasecols_async(void *cookie, int row, int startcol, int ncols, + long fillattr) +{ + struct rasops_info *ri = cookie; + struct vcons_screen *scr = ri->ri_hw; + struct vcons_data *vd = scr->scr_vd; + int idx; - tsleep(&vd->start_drawing, 0, "vc_idle", sec); - if ((vd->wanted != vd->active) && (vd->wanted != NULL)) { - /* - * we need to switch screens - * so first we mark the active screen as invisible - * and wait until it's idle - */ - scr = vd->wanted; - SCREEN_INVISIBLE(vd->active); - while (SCREEN_IS_BUSY(vd->active)) { + vcons_wait_buffer(vd, 5); + mutex_enter(&vd->drawing_mutex); + mutex_exit(&vd->go_buffer_il); + idx = vd->rb_write; + vd->rb_buffer[idx] = VCMD_ERASECOLS; + idx = VRB_NEXT(idx); + vd->rb_buffer[idx] = row; + idx = VRB_NEXT(idx); + vd->rb_buffer[idx] = startcol; + idx = VRB_NEXT(idx); + vd->rb_buffer[idx] = ncols; + idx = VRB_NEXT(idx); + /* + * XXX all drivers I've seen use 32bit attributes although fillattr is + * a 64bit value on LP64 + */ + vd->rb_buffer[idx] = (uint32_t)fillattr; + idx = VRB_NEXT(idx); + membar_producer(); + vd->rb_write = idx; + membar_enter(); + mutex_exit(&vd->drawing_mutex); + cv_signal(&vd->go_draw); +} - tsleep(&vd->done_drawing, 0, "vc_wait", sec); - } +static void +vcons_copyrows_async(void *cookie, int srcrow, int dstrow, int nrows) +{ + struct rasops_info *ri = cookie; + struct vcons_screen *scr = ri->ri_hw; + struct vcons_data *vd = scr->scr_vd; + int idx; + + vcons_wait_buffer(vd, 4); + mutex_enter(&vd->drawing_mutex); + mutex_exit(&vd->go_buffer_il); + idx = vd->rb_write; + vd->rb_buffer[idx] = VCMD_COPYROWS; + idx = VRB_NEXT(idx); + vd->rb_buffer[idx] = srcrow; + idx = VRB_NEXT(idx); + vd->rb_buffer[idx] = dstrow; + idx = VRB_NEXT(idx); + vd->rb_buffer[idx] = nrows; + idx = VRB_NEXT(idx); + membar_producer(); + vd->rb_write = idx; + membar_enter(); + mutex_exit(&vd->drawing_mutex); + cv_signal(&vd->go_draw); +} + +static void +vcons_eraserows_async(void *cookie, int row, int nrows, long fillattr) +{ + struct rasops_info *ri = cookie; + struct vcons_screen *scr = ri->ri_hw; + struct vcons_data *vd = scr->scr_vd; + int idx; + + vcons_wait_buffer(vd, 4); + mutex_enter(&vd->drawing_mutex); + mutex_exit(&vd->go_buffer_il); + idx = vd->rb_write; + vd->rb_buffer[idx] = VCMD_ERASEROWS; + idx = VRB_NEXT(idx); + vd->rb_buffer[idx] = row; + idx = VRB_NEXT(idx); + vd->rb_buffer[idx] = nrows; + idx = VRB_NEXT(idx); + vd->rb_buffer[idx] = (uint32_t)fillattr; + idx = VRB_NEXT(idx); + membar_producer(); + vd->rb_write = idx; + membar_enter(); + mutex_exit(&vd->drawing_mutex); + cv_signal(&vd->go_draw); +} + +static void +vcons_putchar_async(void *cookie, int row, int col, u_int c, long attr) +{ + struct rasops_info *ri = cookie; + struct vcons_screen *scr = ri->ri_hw; + struct vcons_data *vd = scr->scr_vd; + int idx; + +#ifdef VCONS_ASYNC_DEBUG + /* mess with the background attribute so we can see if we draw async */ + attr &= 0xff00ffff; + attr |= (WSCOL_LIGHT_BROWN << 16); +#endif + + vcons_wait_buffer(vd, 5); + mutex_enter(&vd->drawing_mutex); + mutex_exit(&vd->go_buffer_il); + idx = vd->rb_write; + vd->rb_buffer[idx] = VCMD_PUTCHAR; + idx = VRB_NEXT(idx); + vd->rb_buffer[idx] = row; + idx = VRB_NEXT(idx); + vd->rb_buffer[idx] = col; + idx = VRB_NEXT(idx); + vd->rb_buffer[idx] = c; + idx = VRB_NEXT(idx); + vd->rb_buffer[idx] = (uint32_t)attr; + idx = VRB_NEXT(idx); + membar_producer(); + vd->rb_write = idx; + membar_enter(); + mutex_exit(&vd->drawing_mutex); + cv_signal(&vd->go_draw); +} + +static void +vcons_cursor_async(void *cookie, int on, int row, int col) +{ + struct rasops_info *ri = cookie; + struct vcons_screen *scr = ri->ri_hw; + struct vcons_data *vd = scr->scr_vd; + int idx; + + vcons_wait_buffer(vd, 4); + mutex_enter(&vd->drawing_mutex); + mutex_exit(&vd->go_buffer_il); + idx = vd->rb_write; + vd->rb_buffer[idx] = VCMD_CURSOR; + idx = VRB_NEXT(idx); + vd->rb_buffer[idx] = on; + idx = VRB_NEXT(idx); + vd->rb_buffer[idx] = row; + idx = VRB_NEXT(idx); + vd->rb_buffer[idx] = col; + idx = VRB_NEXT(idx); + membar_producer(); + vd->rb_write = idx; + membar_enter(); + mutex_exit(&vd->drawing_mutex); + cv_signal(&vd->go_draw); +} + +static int +vcons_copy_params(struct vcons_data *vd, int len, uint32_t *buf) +{ + int idx = vd->rb_read, i; + + for (i = 0; i < len; i++) { + buf[i] = vd->rb_buffer[idx]; + idx = VRB_NEXT(idx); + } + return idx; +} + + +static void +vcons_process_command(struct vcons_data *vd) +{ + /* we take a command out of the buffer, run it and return */ + void *cookie; + int idx = vd->rb_read; + uint32_t cmd = vd->rb_buffer[idx]; + uint32_t params[10]; + + KASSERT(vd->active != NULL); + cookie = &vd->active->scr_ri; + + switch (cmd) { + case VCMD_COPYCOLS: + idx = vcons_copy_params(vd, 5, params); + vd->rb_read = idx; + membar_producer(); + vd->copycols(cookie, params[1], params[2], params[3], params[4]); + break; + case VCMD_ERASECOLS: + idx = vcons_copy_params(vd, 5, params); + vd->rb_read = idx; + membar_producer(); + vd->erasecols(cookie, params[1], params[2], params[3], params[4]); + break; + case VCMD_COPYROWS: + idx = vcons_copy_params(vd, 4, params); + vd->rb_read = idx; + membar_producer(); + vd->copyrows(cookie, params[1], params[2], params[3]); + break; + case VCMD_ERASEROWS: + idx = vcons_copy_params(vd, 4, params); + vd->rb_read = idx; + membar_producer(); + vd->eraserows(cookie, params[1], params[2], params[3]); + break; + case VCMD_PUTCHAR: + idx = vcons_copy_params(vd, 5, params); + vd->rb_read = idx; + membar_producer(); + vd->putchar(cookie, params[1], params[2], params[3], params[4]); + break; + case VCMD_CURSOR: + idx = vcons_copy_params(vd, 4, params); + vd->rb_read = idx; + membar_producer(); + vd->cursor(cookie, params[1], params[2], params[3]); + break; + default: /* - * now we mark the wanted screen busy so nobody - * messes around while we redraw it + * invalid command, something is wrong so we fall back + * to synchronous operations */ - vd->active = scr; - vd->wanted = NULL; - SCREEN_VISIBLE(scr); + vd->use_async = 0; + vd->rb_read = 0; + vd->rb_write = 0; + } +} + +static void vcons_cursor(void *, int, int, int); + +static void +vcons_kthread(void *cookie) +{ + struct vcons_data *vd = cookie; - if (vd->show_screen_cb != NULL) - vd->show_screen_cb(scr); + /* initialize the synchronization goo */ + cv_init(&vd->go_draw, "go_draw"); + cv_init(&vd->go_buffer, "go_buffer"); + mutex_init(&vd->drawing_mutex, MUTEX_DEFAULT, IPL_NONE); + mutex_init(&vd->go_draw_il, MUTEX_DEFAULT, IPL_NONE); + mutex_init(&vd->go_buffer_il, MUTEX_DEFAULT, IPL_NONE); + vd->rb_read = 1000; + vd->rb_write = 2; + printf("%d\n", vcons_words_in_buffer(vd)); + vd->rb_read = 0; + vd->rb_write = 0; + printf("%d\n", vcons_words_in_buffer(vd)); + printf("%d %d\n", VRB_NEXT(1), VRB_NEXT(1023)); + /* now we're good to go */ + vd->use_async = 1; - if ((scr->scr_flags & VCONS_NO_REDRAW) == 0) - vcons_redraw_screen(scr); + while (1) { - if (vd->switch_cb) - vd->switch_cb(vd->switch_cb_arg, 0, 0); - } + while (vcons_words_in_buffer(vd) > 0) { + vcons_process_command(vd); + cv_signal(&vd->go_buffer); + } + /* + * We don't really need the interlock here since there is no + * need for serializing access to the buffer - we're the only + * consumer. All we want is to sleep until someone gives us + * something to so. + */ + mutex_enter(&vd->go_draw_il); + cv_timedwait(&vd->go_draw, &vd->go_draw_il, hz); + mutex_exit(&vd->go_draw_il); } } -#endif /* VCONS_SWITCH_ASYNC */ +#endif /* VCONS_DRAW_ASYNC */ Index: src/sys/dev/wscons/wsdisplay_vconsvar.h diff -u src/sys/dev/wscons/wsdisplay_vconsvar.h:1.12 src/sys/dev/wscons/wsdisplay_vconsvar.h:1.13 --- src/sys/dev/wscons/wsdisplay_vconsvar.h:1.12 Tue Sep 21 03:33:14 2010 +++ src/sys/dev/wscons/wsdisplay_vconsvar.h Tue Jan 25 20:28:21 2011 @@ -1,4 +1,4 @@ -/* $NetBSD: wsdisplay_vconsvar.h,v 1.12 2010/09/21 03:33:14 macallan Exp $ */ +/* $NetBSD: wsdisplay_vconsvar.h,v 1.13 2011/01/25 20:28:21 macallan Exp $ */ /*- * Copyright (c) 2005, 2006 Michael Lorenz @@ -112,18 +112,50 @@ /* virtual screen management stuff */ void (*switch_cb)(void *, int, int); void *switch_cb_arg; -#ifdef VCONS_SWITCH_ASYNC - lwp_t *redraw_thread; - int start_drawing, done_drawing; /* for the drawing thread */ -#endif struct callout switch_callout; uint32_t switch_pending; LIST_HEAD(, vcons_screen) screens; struct vcons_screen *active, *wanted; const struct wsscreen_descr *currenttype; int switch_poll_count; +#ifdef VCONS_DRAW_ASYNC + lwp_t *drawing_thread; + kmutex_t drawing_mutex; + kcondvar_t go_draw; /* wakeup the drawing thread */ + kcondvar_t go_buffer; /* wakeup anyone waiting for room in the + * buffer */ + kmutex_t go_draw_il, go_buffer_il; /* interlocks for above */ + int use_async; /* use async drawing when non-zero */ +#define VCONS_RING_BUFFER_LENGTH 2048 + uint32_t rb_read; /* to be written by the drawing thread only */ + uint32_t rb_write; /* written by the async drawing methods with + * drawing_mutex held */ + uint32_t rb_buffer[VCONS_RING_BUFFER_LENGTH]; +#endif }; +/* + * the ring buffer contains commands in the form: + * uint32_t command + * uint32_t parameters[] + * uint32_t command + * ... + * parameters are exactly what the corresponding rasops method would take as a + * series of uint32_ts, except for the cookie which is always assumed to be + * the active screen + * There should probably be a command to do vcons_redraw_screen() as a single + * operation, not a huge series of VCMD_PUTCHAR, Also, when we find a buffer + * with lots of commands in it we should probably ignore all VCMD_CURSOR except + * the first that removes the cursor and the last that draws it again + */ + +#define VCMD_COPYCOLS 0x80000001 +#define VCMD_COPYROWS 0x80000002 +#define VCMD_ERASECOLS 0x80000003 +#define VCMD_ERASEROWS 0x80000004 +#define VCMD_PUTCHAR 0x80000005 +#define VCMD_CURSOR 0x80000006 + int vcons_init(struct vcons_data *, void *cookie, struct wsscreen_descr *, struct wsdisplay_accessops *);