]> granicus.if.org Git - vim/commitdiff
patch 8.1.1517: when a popup changes all windows are redrawn v8.1.1517
authorBram Moolenaar <Bram@vim.org>
Mon, 10 Jun 2019 19:24:12 +0000 (21:24 +0200)
committerBram Moolenaar <Bram@vim.org>
Mon, 10 Jun 2019 19:24:12 +0000 (21:24 +0200)
Problem:    When a popup changes all windows are redrawn.
Solution:   Only update the lines that were affected.  Add a file for
            profiling popup windows efficiency.

Filelist
src/globals.h
src/popupwin.c
src/proto/screen.pro
src/screen.c
src/testdir/popupbounce.vim [new file with mode: 0644]
src/ui.c
src/version.c

index 7bf4fd6759a4626b026b7380552f9225c24f96af..99e10048ff72178782c5ed8766e313cefd6aa549 100644 (file)
--- a/Filelist
+++ b/Filelist
@@ -152,6 +152,7 @@ SRC_ALL =   \
                src/testdir/if_ver*.vim \
                src/testdir/color_ramp.vim \
                src/testdir/silent.wav \
+               src/testdir/popupbounce.vim \
                src/proto.h \
                src/protodef.h \
                src/proto/arabic.pro \
index 06091f97b4db6c8f5ffffe67a92aa6f0cd4075ed..3e615457348b20ac36ea4dff1413b47aea7db5ee 100644 (file)
@@ -73,6 +73,7 @@ EXTERN short  *TabPageIdxs INIT(= NULL);
 #ifdef FEAT_TEXT_PROP
 // Array with size Rows x Columns containing zindex of popups.
 EXTERN short   *popup_mask INIT(= NULL);
+EXTERN short   *popup_mask_next INIT(= NULL);
 
 // Flag set to TRUE when popup_mask needs to be updated.
 EXTERN int     popup_mask_refresh INIT(= TRUE);
index c5123697d1912d2f3a9a7df7481ac0914f004b43..3781c6fe2d58e1219f3678315c397b10ba0d7a05 100644 (file)
@@ -571,8 +571,7 @@ popup_adjust_position(win_T *wp)
            || org_width != wp->w_width
            || org_height != wp->w_height)
     {
-       // TODO: redraw only windows that were below the popup.
-       redraw_all_later(NOT_VALID);
+       redraw_all_later(VALID);
        popup_mask_refresh = TRUE;
     }
 }
index 8c2e0083b787c84d7ee3d083229d1c780b5832a2..ab73e0ba9b88c244e7c7daa21a22dbbed9514dec 100644 (file)
@@ -16,7 +16,6 @@ int update_screen(int type_arg);
 int conceal_cursor_line(win_T *wp);
 void conceal_check_cursor_line(void);
 void update_debug_sign(buf_T *buf, linenr_T lnum);
-int may_update_popup_mask(int type_arg);
 void updateWindow(win_T *wp);
 int screen_get_current_line_off(void);
 void screen_line(int row, int coloff, int endcol, int clear_width, int flags);
index fae9fe7eb835f540f7ed1030d51d1b492b39e52f..1ffc729df4f3d8f699ff5edd34ed87dd8fe88ecb 100644 (file)
@@ -122,6 +122,7 @@ static int redrawing_for_callback = 0;
 static schar_T *current_ScreenLine;
 
 #ifdef FEAT_TEXT_PROP
+static void may_update_popup_mask(int type);
 static void update_popups(void);
 #endif
 static void win_update(win_T *wp);
@@ -612,8 +613,9 @@ update_screen(int type_arg)
     }
 
 #ifdef FEAT_TEXT_PROP
-    // Update popup_mask if needed.
-    type = may_update_popup_mask(type);
+    // Update popup_mask if needed.  This may set w_redraw_top and w_redraw_bot
+    // in some windows.
+    may_update_popup_mask(type);
 #endif
 
     updating_screen = TRUE;
@@ -1014,17 +1016,19 @@ get_wcr_attr(win_T *wp)
 }
 
 #ifdef FEAT_TEXT_PROP
+
 /*
  * Update "popup_mask" if needed.
  * Also recomputes the popup size and positions.
  * Also updates "popup_visible".
- * If more redrawing is needed than "type_arg" a higher value is returned.
+ * Also marks window lines for redrawing.
  */
-    int
-may_update_popup_mask(int type_arg)
+    static void
+may_update_popup_mask(int type)
 {
-    int                type = type_arg;
     win_T      *wp;
+    short      *mask;
+    int                line, col;
 
     if (popup_mask_tab != curtab)
        popup_mask_refresh = TRUE;
@@ -1038,14 +1042,22 @@ may_update_popup_mask(int type_arg)
            if (wp->w_popup_last_changedtick != CHANGEDTICK(wp->w_buffer))
                popup_mask_refresh = TRUE;
        if (!popup_mask_refresh)
-           return type;
+           return;
     }
 
+    // Need to update the mask, something has changed.
     popup_mask_refresh = FALSE;
     popup_mask_tab = curtab;
-
     popup_visible = FALSE;
-    vim_memset(popup_mask, 0, screen_Rows * screen_Columns * sizeof(short));
+
+    // If redrawing everything, just update "popup_mask".
+    // If redrawing only what is needed, update "popup_mask_next" and then
+    // compare with "popup_mask" to see what changed.
+    if (type >= SOME_VALID)
+       mask = popup_mask;
+    else
+       mask = popup_mask_next;
+    vim_memset(mask, 0, screen_Rows * screen_Columns * sizeof(short));
 
     // Find the window with the lowest zindex that hasn't been handled yet,
     // so that the window with a higher zindex overwrites the value in
@@ -1053,10 +1065,7 @@ may_update_popup_mask(int type_arg)
     popup_reset_handled();
     while ((wp = find_next_popup(TRUE)) != NULL)
     {
-       int         top_off, bot_off;
-       int         left_off, right_off;
-       short       *p;
-       int         line, col;
+       int         height_extra, width_extra;
 
        popup_visible = TRUE;
 
@@ -1064,30 +1073,71 @@ may_update_popup_mask(int type_arg)
        if (wp->w_popup_last_changedtick != CHANGEDTICK(wp->w_buffer))
            popup_adjust_position(wp);
 
-       // the position and size are for the inside, add the padding and
+       // the width and height are for the inside, add the padding and
        // border
-       top_off = wp->w_popup_padding[0] + wp->w_popup_border[0];
-       bot_off = wp->w_popup_padding[2] + wp->w_popup_border[2];
-       left_off = wp->w_popup_padding[3] + wp->w_popup_border[3];
-       right_off = wp->w_popup_padding[1] + wp->w_popup_border[1];
+       height_extra = wp->w_popup_padding[0] + wp->w_popup_border[0]
+                             + wp->w_popup_padding[2] + wp->w_popup_border[2];
+       width_extra = wp->w_popup_padding[3] + wp->w_popup_border[3]
+                             + wp->w_popup_padding[1] + wp->w_popup_border[1];
 
-       for (line = wp->w_winrow + top_off;
-               line < wp->w_winrow + wp->w_height + bot_off
+       for (line = wp->w_winrow;
+               line < wp->w_winrow + wp->w_height + height_extra
                                                 && line < screen_Rows; ++line)
-           for (col = wp->w_wincol + left_off;
-                col < wp->w_wincol + wp->w_width + right_off
+           for (col = wp->w_wincol;
+                col < wp->w_wincol + wp->w_width + width_extra
                                                && col < screen_Columns; ++col)
+               mask[line * screen_Columns + col] = wp->w_zindex;
+    }
+
+    // Only check which lines are to be updated if not already
+    // updating all lines.
+    if (mask == popup_mask_next)
+       for (line = 0; line < screen_Rows; ++line)
+       {
+           int     col_done = 0;
+
+           for (col = 0; col < screen_Columns; ++col)
            {
-               p = popup_mask + line * screen_Columns + col;
-               if (*p != wp->w_zindex)
+               int off = line * screen_Columns + col;
+
+               if (popup_mask[off] != popup_mask_next[off])
                {
-                   *p = wp->w_zindex;
-                   type = NOT_VALID;
+                   popup_mask[off] = popup_mask_next[off];
+
+                   // The screen position "line" / "col" needs to be redrawn.
+                   // Figure out what window that is and update w_redraw_top
+                   // and w_redr_bot.  Only needs to be done for each window
+                   // line.
+                   if (col >= col_done)
+                   {
+                       linenr_T        lnum;
+                       int             line_cp = line;
+                       int             col_cp = col;
+
+                       // find the window where the row is in
+                       wp = mouse_find_win(&line_cp, &col_cp);
+                       if (wp != NULL)
+                       {
+                           if (line_cp >= wp->w_height)
+                               // In (or below) status line
+                               wp->w_redr_status = TRUE;
+                           // compute the position in the buffer line from the
+                           // position on the screen
+                           else if (mouse_comp_pos(wp, &line_cp, &col_cp,
+                                                                       &lnum))
+                               // past bottom
+                               wp->w_redr_status = TRUE;
+                           else
+                               redrawWinline(wp, lnum);
+
+                           // This line is going to be redrawn, no need to
+                           // check until the right side of the window.
+                           col_done = wp->w_wincol + wp->w_width - 1;
+                       }
+                   }
                }
            }
-    }
-
-    return type;
+       }
 }
 
 /*
@@ -9112,6 +9162,7 @@ screenalloc(int doclear)
     short          *new_TabPageIdxs;
 #ifdef FEAT_TEXT_PROP
     short          *new_popup_mask;
+    short          *new_popup_mask_next;
 #endif
     tabpage_T      *tp;
     static int     entered = FALSE;            /* avoid recursiveness */
@@ -9196,6 +9247,7 @@ retry:
     new_TabPageIdxs = LALLOC_MULT(short, Columns);
 #ifdef FEAT_TEXT_PROP
     new_popup_mask = LALLOC_MULT(short, Rows * Columns);
+    new_popup_mask_next = LALLOC_MULT(short, Rows * Columns);
 #endif
 
     FOR_ALL_TAB_WINDOWS(tp, wp)
@@ -9241,6 +9293,7 @@ give_up:
            || new_TabPageIdxs == NULL
 #ifdef FEAT_TEXT_PROP
            || new_popup_mask == NULL
+           || new_popup_mask_next == NULL
 #endif
            || outofmem)
     {
@@ -9264,6 +9317,7 @@ give_up:
        VIM_CLEAR(new_TabPageIdxs);
 #ifdef FEAT_TEXT_PROP
        VIM_CLEAR(new_popup_mask);
+       VIM_CLEAR(new_popup_mask_next);
 #endif
     }
     else
@@ -9353,6 +9407,7 @@ give_up:
     TabPageIdxs = new_TabPageIdxs;
 #ifdef FEAT_TEXT_PROP
     popup_mask = new_popup_mask;
+    popup_mask_next = new_popup_mask_next;
     vim_memset(popup_mask, 0, Rows * Columns * sizeof(short));
     popup_mask_refresh = TRUE;
 #endif
@@ -9421,6 +9476,7 @@ free_screenlines(void)
     VIM_CLEAR(TabPageIdxs);
 #ifdef FEAT_TEXT_PROP
     VIM_CLEAR(popup_mask);
+    VIM_CLEAR(popup_mask_next);
 #endif
 }
 
@@ -10027,7 +10083,7 @@ win_do_lines(
     }
 
 #ifdef FEAT_TEXT_PROP
-    // this doesn't work when tere are popups visible
+    // this doesn't work when there are popups visible
     if (popup_visible)
        return FAIL;
 #endif
diff --git a/src/testdir/popupbounce.vim b/src/testdir/popupbounce.vim
new file mode 100644 (file)
index 0000000..2d1345b
--- /dev/null
@@ -0,0 +1,80 @@
+" Use this script to measure the redrawing performance when a popup is being
+" displayed.  Usage with gcc:
+"    cd src
+"    # Edit Makefile to uncomment PROFILE_CFLAGS and PROFILE_LIBS
+"    make reconfig
+"    ./vim --clean -S testdir/popupbounce.vim main.c
+"    gprof vim gmon.out | vim -
+
+" using line contination
+set nocp
+
+" don't switch screens when quitting, so we can read the frames/sec
+set t_te=
+
+let winid = popup_create(['line1', 'line2', 'line3', 'line4'], {
+             \   'line' : 1,
+             \   'col' : 1,
+             \   'zindex' : 101,
+             \ })
+redraw
+
+let start = reltime()
+let framecount = 0
+
+let line = 1.0
+let col = 1
+let downwards = 1
+let col_inc = 1
+let initial_speed = 0.2
+let speed = initial_speed
+let accel = 1.1
+let time = 0.1
+
+let countdown = 0
+
+while 1
+  if downwards
+    let speed += time * accel
+    let line += speed
+  else
+    let speed -= time * accel
+    let line -= speed
+  endif
+
+  if line + 3 >= &lines
+    let downwards = 0
+    let speed = speed * 0.8
+    let line = &lines - 3
+  endif
+  if !downwards && speed < 1.0
+    let downwards = 1
+    let speed = initial_speed
+    if line + 4 > &lines && countdown == 0
+      let countdown = 50
+    endif
+  endif
+
+  let col += col_inc
+  if col + 4 >= &columns
+    let col_inc = -1
+  elseif col <= 1
+    let col_inc = 1
+  endif
+
+  call popup_move(winid, {'line': float2nr(line), 'col': col})
+  redraw
+  let framecount += 1
+  if countdown > 0
+    let countdown -= 1
+    if countdown == 0
+      break
+    endif
+  endif
+
+endwhile
+
+let elapsed = reltimefloat(reltime(start))
+echomsg framecount .. ' frames in ' .. string(elapsed) .. ' seconds, ' .. string(framecount / elapsed) .. ' frames/sec'
+
+qa
index ed0e50abe0ba51af30cee343a6d207181f37780d..4a684d2361fb059f7ca00706209504804d156cb7 100644 (file)
--- a/src/ui.c
+++ b/src/ui.c
@@ -3242,15 +3242,19 @@ retnomove:
            || curwin->w_cursor.col != old_cursor.col)
        count |= CURSOR_MOVED;          /* Cursor has moved */
 
-#ifdef FEAT_FOLDING
+# ifdef FEAT_FOLDING
     if (mouse_char == '+')
        count |= MOUSE_FOLD_OPEN;
     else if (mouse_char != ' ')
        count |= MOUSE_FOLD_CLOSE;
-#endif
+# endif
 
     return count;
 }
+#endif
+
+// Functions also used for popup windows.
+#if defined(FEAT_MOUSE) || defined(FEAT_TEXT_PROP) || defined(PROTO)
 
 /*
  * Compute the position in the buffer line from the posn on the screen in
@@ -3347,7 +3351,7 @@ mouse_comp_pos(
  * Returns NULL when something is wrong.
  */
     win_T *
-mouse_find_win(int *rowp, int *colp UNUSED)
+mouse_find_win(int *rowp, int *colp)
 {
     frame_T    *fp;
     win_T      *wp;
index c4c8e3fc1c249028ebcfab57f5f48237a105048e..7a662958800e103d1bd09a7d4b997f8956c7bd99 100644 (file)
@@ -777,6 +777,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1517,
 /**/
     1516,
 /**/