Commit 8b984eb4 authored by Antonino Daplas's avatar Antonino Daplas Committed by Linus Torvalds

[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: default avatarAntonino Daplas <adaplas@pol.net>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent f032365d
...@@ -183,6 +183,8 @@ static __inline__ void ypan_down(struct vc_data *vc, int count); ...@@ -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, 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); 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_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 #ifdef CONFIG_MAC
/* /*
...@@ -763,7 +765,7 @@ static void fbcon_init(struct vc_data *vc, int init) ...@@ -763,7 +765,7 @@ static void fbcon_init(struct vc_data *vc, int init)
if ((cap & FBINFO_HWACCEL_COPYAREA) && if ((cap & FBINFO_HWACCEL_COPYAREA) &&
!(cap & FBINFO_HWACCEL_DISABLED)) !(cap & FBINFO_HWACCEL_DISABLED))
p->scrollmode = SCROLL_ACCEL; p->scrollmode = SCROLL_MOVE;
else /* default to something safe */ else /* default to something safe */
p->scrollmode = SCROLL_REDRAW; p->scrollmode = SCROLL_REDRAW;
...@@ -1226,6 +1228,31 @@ static __inline__ void ypan_up(struct vc_data *vc, int count) ...@@ -1226,6 +1228,31 @@ static __inline__ void ypan_up(struct vc_data *vc, int count)
scrollback_current = 0; 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) static __inline__ void ypan_down(struct vc_data *vc, int count)
{ {
struct fb_info *info = registered_fb[(int) con2fb_map[vc->vc_num]]; 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) ...@@ -1248,6 +1275,30 @@ static __inline__ void ypan_down(struct vc_data *vc, int count)
scrollback_current = 0; 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, static void fbcon_redraw_softback(struct vc_data *vc, struct display *p,
long delta) long delta)
{ {
...@@ -1343,6 +1394,42 @@ static void fbcon_redraw_softback(struct vc_data *vc, struct display *p, ...@@ -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, static void fbcon_redraw(struct vc_data *vc, struct display *p,
int line, int count, int offset) int line, int count, int offset)
{ {
...@@ -1455,14 +1542,14 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, ...@@ -1455,14 +1542,14 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
if (logo_shown >= 0) if (logo_shown >= 0)
goto redraw_up; goto redraw_up;
switch (p->scrollmode) { switch (p->scrollmode) {
case SCROLL_ACCEL: case SCROLL_MOVE:
accel_bmove(vc, info, t + count, 0, t, 0, accel_bmove(vc, info, t + count, 0, t, 0,
b - t - count, vc->vc_cols); b - t - count, vc->vc_cols);
accel_clear(vc, info, b - count, 0, count, accel_clear(vc, info, b - count, 0, count,
vc->vc_cols); vc->vc_cols);
break; break;
case SCROLL_WRAP: case SCROLL_WRAP_MOVE:
if (b - t - count > 3 * vc->vc_rows >> 2) { if (b - t - count > 3 * vc->vc_rows >> 2) {
if (t > 0) if (t > 0)
fbcon_bmove(vc, 0, 0, count, 0, t, 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, ...@@ -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); fbcon_clear(vc, b - count, 0, count, vc->vc_cols);
break; 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 <= if ((p->yscroll + count <=
2 * (p->vrows - vc->vc_rows)) 2 * (p->vrows - vc->vc_rows))
&& ((!scroll_partial && (b - t == 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, ...@@ -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 */ if (count > vc->vc_rows) /* Maximum realistic size */
count = vc->vc_rows; count = vc->vc_rows;
switch (p->scrollmode) { switch (p->scrollmode) {
case SCROLL_ACCEL: case SCROLL_MOVE:
accel_bmove(vc, info, t, 0, t + count, 0, accel_bmove(vc, info, t, 0, t + count, 0,
b - t - count, vc->vc_cols); b - t - count, vc->vc_cols);
accel_clear(vc, info, t, 0, count, vc->vc_cols); accel_clear(vc, info, t, 0, count, vc->vc_cols);
break; break;
case SCROLL_WRAP: case SCROLL_WRAP_MOVE:
if (b - t - count > 3 * vc->vc_rows >> 2) { if (b - t - count > 3 * vc->vc_rows >> 2) {
if (vc->vc_rows - b > 0) if (vc->vc_rows - b > 0)
fbcon_bmove(vc, b, 0, b - count, 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, ...@@ -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); fbcon_clear(vc, t, 0, count, vc->vc_cols);
break; break;
case SCROLL_PAN: case SCROLL_PAN_MOVE:
if ((count - p->yscroll <= p->vrows - vc->vc_rows) if ((count - p->yscroll <= p->vrows - vc->vc_rows)
&& ((!scroll_partial && (b - t == vc->vc_rows)) && ((!scroll_partial && (b - t == vc->vc_rows))
|| (scroll_partial || (scroll_partial
...@@ -1568,6 +1673,23 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, ...@@ -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); fbcon_clear(vc, t, 0, count, vc->vc_cols);
break; 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: case SCROLL_REDRAW:
redraw_down: redraw_down:
fbcon_redraw(vc, p, b - 1, b - t - count, fbcon_redraw(vc, p, b - 1, b - t - count,
...@@ -1657,12 +1779,13 @@ static __inline__ void updatescrollmode(struct display *p, struct fb_info *info, ...@@ -1657,12 +1779,13 @@ static __inline__ void updatescrollmode(struct display *p, struct fb_info *info,
int cap = info->flags; int cap = info->flags;
int good_pan = (cap & FBINFO_HWACCEL_YPAN) int good_pan = (cap & FBINFO_HWACCEL_YPAN)
&& divides(info->fix.ypanstep, vc->vc_font.height) && 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) int good_wrap = (cap & FBINFO_HWACCEL_YWRAP)
&& divides(info->fix.ywrapstep, vc->vc_font.height) && divides(info->fix.ywrapstep, vc->vc_font.height)
&& divides(vc->vc_font.height, info->var.yres_virtual); && divides(vc->vc_font.height, info->var.yres_virtual);
int reading_fast = cap & FBINFO_READS_FAST; int reading_fast = cap & FBINFO_READS_FAST;
int fast_copyarea = (cap & FBINFO_HWACCEL_COPYAREA) && !(cap & FBINFO_HWACCEL_DISABLED); 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; p->vrows = info->var.yres_virtual/fh;
if (info->var.yres > (fh * (vc->vc_rows + 1))) if (info->var.yres > (fh * (vc->vc_rows + 1)))
...@@ -1673,12 +1796,13 @@ static __inline__ void updatescrollmode(struct display *p, struct fb_info *info, ...@@ -1673,12 +1796,13 @@ static __inline__ void updatescrollmode(struct display *p, struct fb_info *info,
if (good_wrap || good_pan) { if (good_wrap || good_pan) {
if (reading_fast || fast_copyarea) if (reading_fast || fast_copyarea)
p->scrollmode = good_wrap ? SCROLL_WRAP : SCROLL_PAN; p->scrollmode = good_wrap ? SCROLL_WRAP_MOVE : SCROLL_PAN_MOVE;
else else
p->scrollmode = SCROLL_REDRAW; p->scrollmode = good_wrap ? SCROLL_REDRAW :
SCROLL_PAN_REDRAW;
} else { } else {
if (reading_fast || fast_copyarea) if (reading_fast || (fast_copyarea && !fast_imageblit))
p->scrollmode = SCROLL_ACCEL; p->scrollmode = SCROLL_MOVE;
else else
p->scrollmode = SCROLL_REDRAW; p->scrollmode = SCROLL_REDRAW;
} }
...@@ -1774,10 +1898,11 @@ static int fbcon_switch(struct vc_data *vc) ...@@ -1774,10 +1898,11 @@ static int fbcon_switch(struct vc_data *vc)
} }
switch (p->scrollmode) { switch (p->scrollmode) {
case SCROLL_WRAP: case SCROLL_WRAP_MOVE:
scrollback_phys_max = p->vrows - vc->vc_rows; scrollback_phys_max = p->vrows - vc->vc_rows;
break; break;
case SCROLL_PAN: case SCROLL_PAN_MOVE:
case SCROLL_PAN_REDRAW:
scrollback_phys_max = p->vrows - 2 * vc->vc_rows; scrollback_phys_max = p->vrows - 2 * vc->vc_rows;
if (scrollback_phys_max < 0) if (scrollback_phys_max < 0)
scrollback_phys_max = 0; scrollback_phys_max = 0;
...@@ -2363,10 +2488,11 @@ static int fbcon_scrolldelta(struct vc_data *vc, int lines) ...@@ -2363,10 +2488,11 @@ static int fbcon_scrolldelta(struct vc_data *vc, int lines)
offset = p->yscroll - scrollback_current; offset = p->yscroll - scrollback_current;
limit = p->vrows; limit = p->vrows;
switch (p->scrollmode) { switch (p->scrollmode) {
case SCROLL_WRAP: case SCROLL_WRAP_MOVE:
info->var.vmode |= FB_VMODE_YWRAP; info->var.vmode |= FB_VMODE_YWRAP;
break; break;
case SCROLL_PAN: case SCROLL_PAN_MOVE:
case SCROLL_PAN_REDRAW:
limit -= vc->vc_rows; limit -= vc->vc_rows;
info->var.vmode &= ~FB_VMODE_YWRAP; info->var.vmode &= ~FB_VMODE_YWRAP;
break; break;
......
...@@ -69,6 +69,35 @@ extern int set_con2fb_map(int unit, int newidx); ...@@ -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: /* 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 * + use the hardware engine to move the text
* (hw-accelerated copyarea() and fillrect()) * (hw-accelerated copyarea() and fillrect())
* + use hardware-supported panning on a large virtual screen * + use hardware-supported panning on a large virtual screen
...@@ -84,10 +113,11 @@ extern int set_con2fb_map(int unit, int newidx); ...@@ -84,10 +113,11 @@ extern int set_con2fb_map(int unit, int newidx);
* *
*/ */
#define SCROLL_ACCEL 0x001 #define SCROLL_MOVE 0x001
#define SCROLL_PAN 0x002 #define SCROLL_PAN_MOVE 0x002
#define SCROLL_WRAP 0x003 #define SCROLL_WRAP_MOVE 0x003
#define SCROLL_REDRAW 0x004 #define SCROLL_REDRAW 0x004
#define SCROLL_PAN_REDRAW 0x005
extern int fb_console_init(void); extern int fb_console_init(void);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment