From 8b984eb45ff363bb9082bd81ebcc38e16b24707a Mon Sep 17 00:00:00 2001 From: Antonino Daplas Date: Tue, 6 Jul 2004 09:19:02 -0700 Subject: [PATCH] [PATCH] fbcon: new scrolling mode: YPAN + REDRAW Added a new scrolling mode (SCROLL_PAN_REDRAW) to fbcon that should greatly benefit unaccelerated drivers such as VESA fbdev. An increase of 3-10 times in scrolling speed can be expected. Currently, fbcon has 4 different scrolling methods (1-4). Potentially, we can have 6. This patch implements SCROLL_PAN_REDRAW (5). SCROLL_WRAP_REDRAW (6) is still unimplemented. Scroll Mode Operation YPan YWrap +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1. SCROLL_ACCEL copyarea No No 2. SCROLL_REDRAW imageblit No No 3. SCROLL_PAN copyarea Yes No 4. SCROLL_WRAP copyarea No Yes 5. SCROLL_PAN_REDRAW imageblit Yes No 6. SCROLL_WRAP_REDRAW imageblit No Yes +++++++++++++++++++++++++++++++++++++++++++++++++++++ Note 1: I've changed the nomenclature to increase clarity: SCROLL_ACCEL = SCROLL_MOVE SCROLL_REDRAW = SCROLL_REDRAW SCROLL_PAN = SCROLL_PAN_MOVE SCROLL_WRAP = SCROLL_WRAP_MOVE To demonstrate the effect of each of the scrolling methods on an unaccelerated PCI/AGP-based driver (vesafb), I used a simple benchmark (time cat linux/MAINTAINERS - a 50K text file). The framebuffer is set at: 1024x768-8bpp, 8x16 font, yres_virtual = 2*yres 1. SCROLL_MOVE: real 5m50.277s user 0m0.001s sys 5m50.227s Almost 6 minutes for a 50K text file. Using soft copyarea on a PCI-based card is just too slow (because copyarea has to read from the framebuffer memory). 2. SCROLL_PAN_MOVE scrollmode: SCROLL_PAN real 0m8.592s user 0m0.000s sys 0m8.586s Using ypan with copyarea dramatically improves the scrolling. However, the scrolling action is jerky (fast during the panning stages, slows down during the copyarea stages). 3. SCROLL_REDRAW (this is the default scrolling mode) real 0m3.189s user 0m0.000s sys 0m3.170s Simply eliminating the copyarea, even without ypanning, makes it faster than SCROLL_PAN_MOvE. Plus, the scrolling action is smoother. So, if we combine YPanning with imageblit (PAN_REDRAW), we get this: 4. SCROLL_PAN_REDRAW real 0m0.520s user 0m0.000s sys 0m0.518s That's almost 6x faster than SCROLL_REDRAW. Increasing the amount of video RAM still increases the speed, but not very dramatically. Higher than 16 MB, the increase is negligible. Using an accelerated driver, we see almost the same effect but not as dramatically: 1. SCROLL_MOVE - accel real 0m3.112s user 0m0.000s sys 0m3.112s 2. SCROLL_REDRAW - accel real 0m2.604s user 0m0.000s sys 0m2.603s Redraw is still faster than move, but not much. 3. SCROLL_PAN_MOVE - accel real 0m0.203s user 0m0.000s sys 0m0.202s 4. SCROLL_PAN_REDRAW - accel real 0m0.326s user 0m0.002s sys 0m0.323s This is one exception. If panning is enabled, move is actually faster than redraw. As to why, I don't know. So based on the above, fbcon will choose the scrolling method based on the following preference: Ypan/Ywrap > accel imageblit > accel copyarea > soft imageblit > (soft copyarea) Note 2: Exception: accel copyarea > accel imageblit if Pan/Wrap is enabled. Note 3: soft copyarea will be avoided by fbcon as much as possible unless there is a specific override, ie., FBINFO_READS_FAST flag is set. If this flag is set, fbcon will prefer copyarea over imageblit, accel or soft. As a final note, in order for fbcon to use the best scrolling method, the low-level drivers must provide the correct hinting by setting the FBINFO_HWACCEL_* flags. To vesafb users: boot vesafb like this for fastest scrolling action: video=vesafb:ypan,vram:16 Signed-off-by: Antonino Daplas Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/console/fbcon.c | 158 ++++++++++++++++++++++++++++++---- drivers/video/console/fbcon.h | 38 +++++++- 2 files changed, 176 insertions(+), 20 deletions(-) diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 17b1a1ad2735c..691ae83e087da 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -183,6 +183,8 @@ static __inline__ void ypan_down(struct vc_data *vc, int count); static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int sx, int dy, int dx, int height, int width, u_int y_break); static void fbcon_set_disp(struct fb_info *info, struct vc_data *vc); +static void fbcon_redraw_move(struct vc_data *vc, struct display *p, + int line, int count, int dy); #ifdef CONFIG_MAC /* @@ -763,7 +765,7 @@ static void fbcon_init(struct vc_data *vc, int init) if ((cap & FBINFO_HWACCEL_COPYAREA) && !(cap & FBINFO_HWACCEL_DISABLED)) - p->scrollmode = SCROLL_ACCEL; + p->scrollmode = SCROLL_MOVE; else /* default to something safe */ p->scrollmode = SCROLL_REDRAW; @@ -1226,6 +1228,31 @@ static __inline__ void ypan_up(struct vc_data *vc, int count) scrollback_current = 0; } +static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count) +{ + struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; + struct display *p = &fb_display[vc->vc_num]; + int redraw = 0; + + p->yscroll += count; + if (p->yscroll > p->vrows - vc->vc_rows) { + p->yscroll -= p->vrows - vc->vc_rows; + redraw = 1; + } + + info->var.xoffset = 0; + info->var.yoffset = p->yscroll * vc->vc_font.height; + info->var.vmode &= ~FB_VMODE_YWRAP; + if (redraw) + fbcon_redraw_move(vc, p, t + count, vc->vc_rows - count, t); + update_var(vc->vc_num, info); + accel_clear_margins(vc, info, 1); + scrollback_max += count; + if (scrollback_max > scrollback_phys_max) + scrollback_max = scrollback_phys_max; + scrollback_current = 0; +} + static __inline__ void ypan_down(struct vc_data *vc, int count) { struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; @@ -1248,6 +1275,30 @@ static __inline__ void ypan_down(struct vc_data *vc, int count) scrollback_current = 0; } +static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count) +{ + struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; + struct display *p = &fb_display[vc->vc_num]; + int redraw = 0; + + p->yscroll -= count; + if (p->yscroll < 0) { + p->yscroll += p->vrows - vc->vc_rows; + redraw = 1; + } + info->var.xoffset = 0; + info->var.yoffset = p->yscroll * vc->vc_font.height; + info->var.vmode &= ~FB_VMODE_YWRAP; + if (redraw) + fbcon_redraw_move(vc, p, t, vc->vc_rows - count, t + count); + update_var(vc->vc_num, info); + accel_clear_margins(vc, info, 1); + scrollback_max -= count; + if (scrollback_max < 0) + scrollback_max = 0; + scrollback_current = 0; +} + static void fbcon_redraw_softback(struct vc_data *vc, struct display *p, long delta) { @@ -1343,6 +1394,42 @@ static void fbcon_redraw_softback(struct vc_data *vc, struct display *p, } } +static void fbcon_redraw_move(struct vc_data *vc, struct display *p, + int line, int count, int dy) +{ + struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; + unsigned short *s = (unsigned short *) + (vc->vc_origin + vc->vc_size_row * line); + + while (count--) { + unsigned short *start = s; + unsigned short *le = advance_row(s, 1); + unsigned short c; + int x = 0; + unsigned short attr = 1; + + do { + c = scr_readw(s); + if (attr != (c & 0xff00)) { + attr = c & 0xff00; + if (s > start) { + accel_putcs(vc, info, start, s - start, + real_y(p, dy), x); + x += s - start; + start = s; + } + } + console_conditional_schedule(); + s++; + } while (s < le); + if (s > start) + accel_putcs(vc, info, start, s - start, + real_y(p, dy), x); + console_conditional_schedule(); + dy++; + } +} + static void fbcon_redraw(struct vc_data *vc, struct display *p, int line, int count, int offset) { @@ -1455,14 +1542,14 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, if (logo_shown >= 0) goto redraw_up; switch (p->scrollmode) { - case SCROLL_ACCEL: + case SCROLL_MOVE: accel_bmove(vc, info, t + count, 0, t, 0, b - t - count, vc->vc_cols); accel_clear(vc, info, b - count, 0, count, vc->vc_cols); break; - case SCROLL_WRAP: + case SCROLL_WRAP_MOVE: if (b - t - count > 3 * vc->vc_rows >> 2) { if (t > 0) fbcon_bmove(vc, 0, 0, count, 0, t, @@ -1480,7 +1567,25 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, fbcon_clear(vc, b - count, 0, count, vc->vc_cols); break; - case SCROLL_PAN: + case SCROLL_PAN_REDRAW: + if ((p->yscroll + count <= + 2 * (p->vrows - vc->vc_rows)) + && ((!scroll_partial && (b - t == vc->vc_rows)) + || (scroll_partial + && (b - t - count > + 3 * vc->vc_rows >> 2)))) { + if (t > 0) + fbcon_redraw_move(vc, p, 0, t, count); + ypan_up_redraw(vc, t, count); + if (vc->vc_rows - b > 0) + fbcon_redraw_move(vc, p, b - count, + vc->vc_rows - b, b); + } else + fbcon_redraw_move(vc, p, t + count, b - t - count, t); + fbcon_clear(vc, b - count, 0, count, vc->vc_cols); + break; + + case SCROLL_PAN_MOVE: if ((p->yscroll + count <= 2 * (p->vrows - vc->vc_rows)) && ((!scroll_partial && (b - t == vc->vc_rows)) @@ -1522,13 +1627,13 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, if (count > vc->vc_rows) /* Maximum realistic size */ count = vc->vc_rows; switch (p->scrollmode) { - case SCROLL_ACCEL: + case SCROLL_MOVE: accel_bmove(vc, info, t, 0, t + count, 0, b - t - count, vc->vc_cols); accel_clear(vc, info, t, 0, count, vc->vc_cols); break; - case SCROLL_WRAP: + case SCROLL_WRAP_MOVE: if (b - t - count > 3 * vc->vc_rows >> 2) { if (vc->vc_rows - b > 0) fbcon_bmove(vc, b, 0, b - count, 0, @@ -1546,7 +1651,7 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, fbcon_clear(vc, t, 0, count, vc->vc_cols); break; - case SCROLL_PAN: + case SCROLL_PAN_MOVE: if ((count - p->yscroll <= p->vrows - vc->vc_rows) && ((!scroll_partial && (b - t == vc->vc_rows)) || (scroll_partial @@ -1568,6 +1673,23 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, fbcon_clear(vc, t, 0, count, vc->vc_cols); break; + case SCROLL_PAN_REDRAW: + if ((count - p->yscroll <= p->vrows - vc->vc_rows) + && ((!scroll_partial && (b - t == vc->vc_rows)) + || (scroll_partial + && (b - t - count > + 3 * vc->vc_rows >> 2)))) { + if (vc->vc_rows - b > 0) + fbcon_redraw_move(vc, p, b, vc->vc_rows - b, + b - count); + ypan_down_redraw(vc, t, count); + if (t > 0) + fbcon_redraw_move(vc, p, count, t, 0); + } else + fbcon_redraw_move(vc, p, t, b - t - count, t + count); + fbcon_clear(vc, t, 0, count, vc->vc_cols); + break; + case SCROLL_REDRAW: redraw_down: fbcon_redraw(vc, p, b - 1, b - t - count, @@ -1657,12 +1779,13 @@ static __inline__ void updatescrollmode(struct display *p, struct fb_info *info, int cap = info->flags; int good_pan = (cap & FBINFO_HWACCEL_YPAN) && divides(info->fix.ypanstep, vc->vc_font.height) - && info->var.yres_virtual >= 2*info->var.yres; + && info->var.yres_virtual > info->var.yres; int good_wrap = (cap & FBINFO_HWACCEL_YWRAP) && divides(info->fix.ywrapstep, vc->vc_font.height) && divides(vc->vc_font.height, info->var.yres_virtual); int reading_fast = cap & FBINFO_READS_FAST; int fast_copyarea = (cap & FBINFO_HWACCEL_COPYAREA) && !(cap & FBINFO_HWACCEL_DISABLED); + int fast_imageblit = (cap & FBINFO_HWACCEL_IMAGEBLIT) && !(cap & FBINFO_HWACCEL_DISABLED); p->vrows = info->var.yres_virtual/fh; if (info->var.yres > (fh * (vc->vc_rows + 1))) @@ -1673,12 +1796,13 @@ static __inline__ void updatescrollmode(struct display *p, struct fb_info *info, if (good_wrap || good_pan) { if (reading_fast || fast_copyarea) - p->scrollmode = good_wrap ? SCROLL_WRAP : SCROLL_PAN; + p->scrollmode = good_wrap ? SCROLL_WRAP_MOVE : SCROLL_PAN_MOVE; else - p->scrollmode = SCROLL_REDRAW; + p->scrollmode = good_wrap ? SCROLL_REDRAW : + SCROLL_PAN_REDRAW; } else { - if (reading_fast || fast_copyarea) - p->scrollmode = SCROLL_ACCEL; + if (reading_fast || (fast_copyarea && !fast_imageblit)) + p->scrollmode = SCROLL_MOVE; else p->scrollmode = SCROLL_REDRAW; } @@ -1774,10 +1898,11 @@ static int fbcon_switch(struct vc_data *vc) } switch (p->scrollmode) { - case SCROLL_WRAP: + case SCROLL_WRAP_MOVE: scrollback_phys_max = p->vrows - vc->vc_rows; break; - case SCROLL_PAN: + case SCROLL_PAN_MOVE: + case SCROLL_PAN_REDRAW: scrollback_phys_max = p->vrows - 2 * vc->vc_rows; if (scrollback_phys_max < 0) scrollback_phys_max = 0; @@ -2363,10 +2488,11 @@ static int fbcon_scrolldelta(struct vc_data *vc, int lines) offset = p->yscroll - scrollback_current; limit = p->vrows; switch (p->scrollmode) { - case SCROLL_WRAP: + case SCROLL_WRAP_MOVE: info->var.vmode |= FB_VMODE_YWRAP; break; - case SCROLL_PAN: + case SCROLL_PAN_MOVE: + case SCROLL_PAN_REDRAW: limit -= vc->vc_rows; info->var.vmode &= ~FB_VMODE_YWRAP; break; diff --git a/drivers/video/console/fbcon.h b/drivers/video/console/fbcon.h index 539373d32a502..71643f5ad94f4 100644 --- a/drivers/video/console/fbcon.h +++ b/drivers/video/console/fbcon.h @@ -69,6 +69,35 @@ extern int set_con2fb_map(int unit, int newidx); /* There are several methods fbcon can use to move text around the screen: * + * Operation Pan Wrap + *--------------------------------------------- + * SCROLL_MOVE copyarea No No + * SCROLL_PAN_MOVE copyarea Yes No + * SCROLL_WRAP_MOVE copyarea No Yes + * SCROLL_REDRAW imageblit No No + * SCROLL_PAN_REDRAW imageblit Yes No + * SCROLL_WRAP_REDRAW imageblit No Yes + * + * (SCROLL_WRAP_REDRAW is not implemented yet) + * + * In general, fbcon will choose the best scrolling + * method based on the rule below: + * + * Pan/Wrap > accel imageblit > accel copyarea > + * soft imageblit > (soft copyarea) + * + * Exception to the rule: Pan + accel copyarea is + * preferred over Pan + accel imageblit. + * + * The above is typical for PCI/AGP cards. Unless + * overridden, fbcon will never use soft copyarea. + * + * If you need to override the above rule, set the + * appropriate flags in fb_info->flags. For example, + * to prefer copyarea over imageblit, set + * FBINFO_READS_FAST. + * + * Other notes: * + use the hardware engine to move the text * (hw-accelerated copyarea() and fillrect()) * + use hardware-supported panning on a large virtual screen @@ -84,10 +113,11 @@ extern int set_con2fb_map(int unit, int newidx); * */ -#define SCROLL_ACCEL 0x001 -#define SCROLL_PAN 0x002 -#define SCROLL_WRAP 0x003 -#define SCROLL_REDRAW 0x004 +#define SCROLL_MOVE 0x001 +#define SCROLL_PAN_MOVE 0x002 +#define SCROLL_WRAP_MOVE 0x003 +#define SCROLL_REDRAW 0x004 +#define SCROLL_PAN_REDRAW 0x005 extern int fb_console_init(void); -- 2.53.0