Module Name:    src
Committed By:   macallan
Date:           Wed May 25 06:01:39 UTC 2011

Modified Files:
        src/sys/dev/wscons: wsdisplay_vcons.c wsdisplay_vcons_util.c
            wsdisplay_vconsvar.h

Log Message:
add a cache so when we update the screen we only redraw the character cells
that actually changed since last time. This gives a noticeable speedup on
slower hardware with dumb framebuffers.
For now this works with VCONS_DRAW_INTR and VCONS_DONT_READ only.


To generate a diff of this commit:
cvs rdiff -u -r1.24 -r1.25 src/sys/dev/wscons/wsdisplay_vcons.c
cvs rdiff -u -r1.1 -r1.2 src/sys/dev/wscons/wsdisplay_vcons_util.c
cvs rdiff -u -r1.18 -r1.19 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/wsdisplay_vcons.c
diff -u src/sys/dev/wscons/wsdisplay_vcons.c:1.24 src/sys/dev/wscons/wsdisplay_vcons.c:1.25
--- src/sys/dev/wscons/wsdisplay_vcons.c:1.24	Fri Feb 18 13:56:11 2011
+++ src/sys/dev/wscons/wsdisplay_vcons.c	Wed May 25 06:01:38 2011
@@ -1,4 +1,4 @@
-/*	$NetBSD: wsdisplay_vcons.c,v 1.24 2011/02/18 13:56:11 jmcneill Exp $ */
+/*	$NetBSD: wsdisplay_vcons.c,v 1.25 2011/05/25 06:01:38 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.24 2011/02/18 13:56:11 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: wsdisplay_vcons.c,v 1.25 2011/05/25 06:01:38 macallan Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -102,6 +102,9 @@
 static void vcons_copyrows(void *, int, int, int);
 static void vcons_eraserows(void *, int, int, long);
 static void vcons_putchar(void *, int, int, u_int, long);
+#ifdef VCONS_DRAW_INTR
+static void vcons_putchar_cached(void *, int, int, u_int, long);
+#endif
 static void vcons_cursor(void *, int, int, int);
 
 /*
@@ -159,6 +162,12 @@
 	vd->currenttype = def;
 	callout_init(&vd->switch_callout, 0);
 	callout_setfunc(&vd->switch_callout, vcons_do_switch, vd);
+#ifdef VCONS_DRAW_INTR
+	vd->cells = 0;
+	vd->attrs = NULL;
+	vd->chars = NULL;
+	vd->cursor_offset = -1;
+#endif
 
 	/*
 	 * a lock to serialize access to the framebuffer.
@@ -234,6 +243,9 @@
 {
 	struct rasops_info *ri = &scr->scr_ri;
 	int cnt, i;
+#ifdef VCONS_DRAW_INTR
+	int size;
+#endif
 
 	scr->scr_cookie = vd->cookie;
 	scr->scr_vd = scr->scr_origvd = vd;
@@ -313,6 +325,18 @@
 		scr->scr_chars[i] = 0x20;
 	}
 
+#ifdef VCONS_DRAW_INTR
+	size = ri->ri_cols * ri->ri_rows;
+	if (size > vd->cells) {
+		if (vd->chars != NULL) free(vd->chars, M_DEVBUF);
+		if (vd->attrs != NULL) free(vd->attrs, M_DEVBUF);
+		vd->cells = size;
+		vd->chars = malloc(size * sizeof(uint16_t), M_DEVBUF, M_WAITOK);
+		vd->attrs = malloc(size * sizeof(long), M_DEVBUF, M_WAITOK);
+		vcons_invalidate_cache(vd);
+	}
+#endif
+
 	if(vd->active == NULL) {
 		vd->active = scr;
 		SCREEN_VISIBLE(scr);
@@ -402,7 +426,8 @@
 	uint16_t *charptr = scr->scr_chars;
 	long *attrptr = scr->scr_attrs;
 	struct rasops_info *ri = &scr->scr_ri;
-	int i, j, offset;
+	struct vcons_data *vd = scr->scr_vd;
+	int i, j, offset, boffset = 0;
 
 	vcons_lock(scr);
 	if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
@@ -412,7 +437,7 @@
 		 * going to overwrite every single character cell anyway
 		 */
 		if (ri->ri_flg & RI_FULLCLEAR) {
-			scr->scr_vd->eraserows(ri, 0, ri->ri_rows,
+			vd->eraserows(ri, 0, ri->ri_rows,
 			    scr->scr_defattr);
 		}
 
@@ -430,16 +455,79 @@
 				 * and we already made sure the screen we're
 				 * working on is visible
 				 */
-				scr->scr_vd->putchar(ri, i, j, 
+				vd->putchar(ri, i, j, 
 				    charptr[offset], attrptr[offset]);
+#ifdef VCONS_DRAW_INTR
+				vd->chars[boffset] = charptr[offset];
+				vd->attrs[boffset] = attrptr[offset];
+#endif
+				offset++;
+				boffset++;
+			}
+		}
+		ri->ri_flg &= ~RI_CURSOR;
+		scr->scr_vd->cursor(ri, 1, ri->ri_crow, ri->ri_ccol);
+#ifdef VCONS_DRAW_INTR
+		vd->cursor_offset = ri->ri_crow * ri->ri_cols + ri->ri_ccol;
+#endif
+	}
+	vcons_unlock(scr);
+}
+
+#ifdef VCONS_DRAW_INTR
+void
+vcons_update_screen(struct vcons_screen *scr)
+{
+	uint16_t *charptr = scr->scr_chars;
+	long *attrptr = scr->scr_attrs;
+	struct rasops_info *ri = &scr->scr_ri;
+	struct vcons_data *vd = scr->scr_vd;
+	int i, j, offset, boffset = 0;
+
+	vcons_lock(scr);
+	if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
+
+		/* redraw the screen */
+#ifdef WSDISPLAY_SCROLLSUPPORT
+		offset = scr->scr_current_offset;
+#else
+		offset = 0;
+#endif
+		/*
+		 * we mark the character cell occupied by the cursor as dirty
+		 * so we don't have to deal with it
+		 * notice that this isn't necessarily the position where rasops
+		 * thinks it is, just where we drew it the last time
+		 */
+		if (vd->cursor_offset >= 0)
+			vd->attrs[vd->cursor_offset] = 0xffffffff;
+
+		for (i = 0; i < ri->ri_rows; i++) {
+			for (j = 0; j < ri->ri_cols; j++) {
+				/*
+				 * no need to use the wrapper function - we 
+				 * don't change any characters or attributes
+				 * and we already made sure the screen we're
+				 * working on is visible
+				 */
+				if ((vd->chars[boffset] != charptr[offset]) ||
+				    (vd->attrs[boffset] != attrptr[offset])) {
+					vd->putchar(ri, i, j, 
+				 	   charptr[offset], attrptr[offset]);
+					vd->chars[boffset] = charptr[offset];
+					vd->attrs[boffset] = attrptr[offset];
+				}
 				offset++;
+				boffset++;
 			}
 		}
 		ri->ri_flg &= ~RI_CURSOR;
 		scr->scr_vd->cursor(ri, 1, ri->ri_crow, ri->ri_ccol);
+		vd->cursor_offset = ri->ri_crow * ri->ri_cols + ri->ri_ccol;
 	}
 	vcons_unlock(scr);
 }
+#endif
 
 static int
 vcons_ioctl(void *v, void *vs, u_long cmd, void *data, int flag,
@@ -618,20 +706,33 @@
 {
 	struct rasops_info *ri = cookie;
 	struct vcons_screen *scr = ri->ri_hw;
+	struct vcons_data *vd = scr->scr_vd;
 
 	if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
-		int pos, c, offset;
+		int pos, c, offset, ppos;
 
 #ifdef WSDISPLAY_SCROLLSUPPORT
 		offset = scr->scr_current_offset;
 #else
 		offset = 0;
 #endif
-		pos = ri->ri_cols * row + dstcol + offset;
+		ppos = ri->ri_cols * row + dstcol;
+		pos = ppos + offset;
 		for (c = dstcol; c < (dstcol + ncols); c++) {
-			scr->scr_vd->putchar(cookie, row, c, 
-			   scr->scr_chars[pos], scr->scr_attrs[pos]);
+#ifdef VCONS_DRAW_INTR
+			if ((scr->scr_chars[pos] != vd->chars[ppos]) ||
+			    (scr->scr_attrs[pos] != vd->attrs[ppos])) {
+				vd->putchar(cookie, row, c, 
+				   scr->scr_chars[pos], scr->scr_attrs[pos]);
+				vd->chars[ppos] = scr->scr_chars[pos];
+				vd->attrs[ppos] = scr->scr_attrs[pos];
+			}
+#else
+			vd->putchar(cookie, row, c, scr->scr_chars[pos],
+			    scr->scr_attrs[pos]);
+#endif
 			pos++;
+			ppos++;
 		}
 	}
 }
@@ -664,6 +765,23 @@
 #endif
 }
 
+#ifdef VCONS_DRAW_INTR
+static void
+vcons_erasecols_cached(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 i, pos = row * ri->ri_cols + startcol;
+
+	for (i = pos; i < ncols; i++) {
+		vd->chars[i] = 0x20;
+		vd->attrs[i] = fillattr;
+	}
+	vd->erasecols(cookie, row, startcol, ncols, fillattr);
+}
+#endif
+
 static void
 vcons_erasecols(void *cookie, int row, int startcol, int ncols, long fillattr)
 {
@@ -686,8 +804,12 @@
 			    fillattr);
 		} else
 #endif
-			scr->scr_vd->erasecols(cookie, row, startcol, ncols, 
+#ifdef VCONS_DRAW_INTR
+			vcons_erasecols_cached(cookie, row, startcol, ncols, 
 			    fillattr);
+#else
+		scr->scr_vd->erasecols(cookie, row, startcol, ncols, fillattr);
+#endif	
 	}
 	vcons_unlock(scr);
 }
@@ -763,21 +885,36 @@
 {
 	struct rasops_info *ri = cookie;
 	struct vcons_screen *scr = ri->ri_hw;
+	struct vcons_data *vd = scr->scr_vd;
+	int dist;
 
 	if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
-		int pos, l, c, offset;
+		int pos, l, c, offset, ppos;
 
 #ifdef WSDISPLAY_SCROLLSUPPORT
 		offset = scr->scr_current_offset;
 #else
 		offset = 0;
 #endif
-		pos = ri->ri_cols * dstrow + offset;
+		dist = (dstrow - srcrow) * ri->ri_cols;
+		ppos = ri->ri_cols * dstrow;
+		pos = ppos + offset;
 		for (l = dstrow; l < (dstrow + nrows); l++) {
 			for (c = 0; c < ri->ri_cols; c++) {
-				scr->scr_vd->putchar(cookie, l, c, 
-				   scr->scr_chars[pos], scr->scr_attrs[pos]);
+#ifdef VCONS_DRAW_INTR
+				if ((scr->scr_chars[pos] != vd->chars[ppos]) ||
+				    (scr->scr_attrs[pos] != vd->attrs[ppos])) {
+					vd->putchar(cookie, l, c, 
+					   scr->scr_chars[pos], scr->scr_attrs[pos]);
+					vd->chars[ppos] = scr->scr_chars[pos];
+					vd->attrs[ppos] = scr->scr_attrs[pos];
+				}
+#else
+				vd->putchar(cookie, l, c, scr->scr_chars[pos],
+				    scr->scr_attrs[pos]);
+#endif
 				pos++;
+				ppos++;
 			}
 		}
 	}
@@ -832,7 +969,7 @@
 			vcons_eraserows_async(cookie, row, nrows, fillattr);
 		} else
 #endif
-			scr->scr_vd->eraserows(cookie, row, nrows, fillattr);
+		scr->scr_vd->eraserows(cookie, row, nrows, fillattr);
 	}
 	vcons_unlock(scr);
 }
@@ -868,6 +1005,27 @@
 #endif
 }
 
+#ifdef VCONS_DRAW_INTR
+static void
+vcons_putchar_cached(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 pos = row * ri->ri_cols + col;
+
+	if ((vd->chars == NULL) || (vd->attrs == NULL)) {
+		vd->putchar(cookie, row, col, c, attr);
+		return;
+	}
+	if ((vd->chars[pos] != c) || (vd->attrs[pos] != attr)) {
+		vd->attrs[pos] = attr;
+		vd->chars[pos] = c;
+		vd->putchar(cookie, row, col, c, attr);
+	}
+}
+#endif
+
 static void
 vcons_putchar(void *cookie, int row, int col, u_int c, long attr)
 {
@@ -889,7 +1047,11 @@
 			vcons_putchar_async(cookie, row, col, c, attr);
 		} else
 #endif
-			scr->scr_vd->putchar(cookie, row, col, c, attr);
+#ifdef VCONS_DRAW_INTR
+		vcons_putchar_cached(cookie, row, col, c, attr);
+#else
+		scr->scr_vd->putchar(cookie, row, col, c, attr);
+#endif
 	}
 	vcons_unlock(scr);
 }
@@ -1060,9 +1222,15 @@
 	scr->scr_vd->copyrows(scr, from, to, num);
 	for (i = 0; i < abs(dist); i++) {
 		for (j = 0; j < scr->scr_ri.ri_cols; j++) {
+#ifdef VCONS_DRAW_INTR
+			vcons_putchar_cached(scr, i + r_start, j,
+			    scr->scr_chars[r_offset],
+			    scr->scr_attrs[r_offset]);
+#else
 			scr->scr_vd->putchar(scr, i + r_start, j,
 			    scr->scr_chars[r_offset],
 			    scr->scr_attrs[r_offset]);
+#endif
 			r_offset++;
 		}
 	}
@@ -1332,7 +1500,7 @@
 			idx = vcons_copy_params(vd, 5, params);
 			vd->rb_read = idx;
 			membar_producer();
-			vd->erasecols(cookie, params[1], params[2], params[3], params[4]);
+			vcons_erasecols_cached(cookie, params[1], params[2], params[3], params[4]);
 			break;
 		case VCMD_COPYROWS:
 			idx = vcons_copy_params(vd, 4, params);
@@ -1344,13 +1512,13 @@
 			idx = vcons_copy_params(vd, 4, params);
 			vd->rb_read = idx;
 			membar_producer();
-			vd->eraserows(cookie, params[1], params[2], params[3]);
+			vcons_eraserows_cached(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]);
+			vcons_putchar_cached(cookie, params[1], params[2], params[3], params[4]);
 			break;
 		case VCMD_CURSOR:
 			idx = vcons_copy_params(vd, 4, params);
@@ -1432,7 +1600,7 @@
 			dirty = atomic_swap_uint(&scr->scr_dirty, 0);
 			if (dirty > 0) {
 				if ((scr->scr_flags & VCONS_NO_REDRAW) == 0)
-					vcons_redraw_screen(scr);
+					vcons_update_screen(scr);
 			}
 		}
 	}
@@ -1497,3 +1665,19 @@
 	if (vd->show_screen_cb != NULL)
 		vd->show_screen_cb(scr);
 }
+
+#ifdef VCONS_DRAW_INTR
+void
+vcons_invalidate_cache(struct vcons_data *vd)
+{
+	int i;
+
+	if (vd->cells == 0)
+		return;
+
+	for (i = 0; i > vd->cells; i++) {
+		vd->chars[i] = -1;
+		vd->attrs[i] = -1;
+	}
+}
+#endif

Index: src/sys/dev/wscons/wsdisplay_vcons_util.c
diff -u src/sys/dev/wscons/wsdisplay_vcons_util.c:1.1 src/sys/dev/wscons/wsdisplay_vcons_util.c:1.2
--- src/sys/dev/wscons/wsdisplay_vcons_util.c:1.1	Thu Aug 20 02:01:08 2009
+++ src/sys/dev/wscons/wsdisplay_vcons_util.c	Wed May 25 06:01:39 2011
@@ -1,7 +1,7 @@
-/*	$NetBSD: wsdisplay_vcons_util.c,v 1.1 2009/08/20 02:01:08 macallan Exp $ */
+/*	$NetBSD: wsdisplay_vcons_util.c,v 1.2 2011/05/25 06:01:39 macallan Exp $ */
 
 /*-
- * Copyright (c) 2005, 2006 Michael Lorenz
+ * Copyright (c) 2009 Michael Lorenz
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -56,5 +56,5 @@
 			rptr = 0;
 	}
 	scr->scr_status = status;
-	vcons_redraw_screen(scr);
+	vcons_update_screen(scr);
 }

Index: src/sys/dev/wscons/wsdisplay_vconsvar.h
diff -u src/sys/dev/wscons/wsdisplay_vconsvar.h:1.18 src/sys/dev/wscons/wsdisplay_vconsvar.h:1.19
--- src/sys/dev/wscons/wsdisplay_vconsvar.h:1.18	Fri Feb 18 13:56:11 2011
+++ src/sys/dev/wscons/wsdisplay_vconsvar.h	Wed May 25 06:01:38 2011
@@ -1,4 +1,4 @@
-/*	$NetBSD: wsdisplay_vconsvar.h,v 1.18 2011/02/18 13:56:11 jmcneill Exp $ */
+/*	$NetBSD: wsdisplay_vconsvar.h,v 1.19 2011/05/25 06:01:38 macallan Exp $ */
 
 /*-
  * Copyright (c) 2005, 2006 Michael Lorenz
@@ -136,6 +136,10 @@
 	uint32_t rb_buffer[VCONS_RING_BUFFER_LENGTH];
 #endif
 #ifdef VCONS_DRAW_INTR
+	int cells;
+	long *attrs;
+	uint16_t *chars;
+	int cursor_offset;
 	callout_t intr;
 	int intr_valid;
 	void *intr_softint;
@@ -171,8 +175,17 @@
 int	vcons_init_screen(struct vcons_data *, struct vcons_screen *, int,
     long *);
 
+/* completely redraw the screen, clear it if RI_FULLCLEAR is set */
 void	vcons_redraw_screen(struct vcons_screen *);
 
+#ifdef VCONS_DRAW_INTR
+/* redraw all dirty character cells */
+void	vcons_update_screen(struct vcons_screen *);
+void	vcons_invalidate_cache(struct vcons_data *);
+#else
+#define vcons_update_screen vcons_redraw_screen
+#endif
+
 void	vcons_replay_msgbuf(struct vcons_screen *);
 
 void	vcons_enable_polling(struct vcons_data *);

Reply via email to