]> git.hungrycats.org Git - linux/commitdiff
[PATCH] fbcon: new scrolling mode: YPAN + REDRAW
authorAntonino Daplas <adaplas@hotpop.com>
Tue, 6 Jul 2004 16:19:02 +0000 (09:19 -0700)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Tue, 6 Jul 2004 16:19:02 +0000 (09:19 -0700)
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 <adaplas@pol.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
drivers/video/console/fbcon.c
drivers/video/console/fbcon.h

index 17b1a1ad2735cfcca614ec5e9398b3351920c234..691ae83e087da0fd279acfbad3678b0d0c4e304f 100644 (file)
@@ -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;
index 539373d32a502619822eaa5ebcc99c13c0e0d35c..71643f5ad94f487769d8615f9c265d5ebfa978d6 100644 (file)
@@ -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);