Package: emacs;
Reported by: JD Smith <jdtsmith <at> gmail.com>
Date: Wed, 11 Jun 2025 23:09:02 UTC
Severity: normal
To reply to this bug, email your comments to 78766 AT debbugs.gnu.org.
Toggle the display of automated, internal messages from the tracker.
View this report as an mbox folder, status mbox, maintainer mbox
bug-gnu-emacs <at> gnu.org
:bug#78766
; Package emacs
.
(Wed, 11 Jun 2025 23:09:02 GMT) Full text and rfc822 format available.JD Smith <jdtsmith <at> gmail.com>
:bug-gnu-emacs <at> gnu.org
.
(Wed, 11 Jun 2025 23:09:02 GMT) Full text and rfc822 format available.Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):
From: JD Smith <jdtsmith <at> gmail.com> To: bug-gnu-emacs <at> gnu.org Subject: 100-4000x redisplay slowdown with vscroll>0 and make-cursor-line-fully-visible=t Date: Wed, 11 Jun 2025 19:08:01 -0400
[Message part 1 (text/plain, inline)]
Users of ultra-scroll noticed significant slowdowns in some situations. We traced it back to the combination of: - vscroll > 0 (ultra-scroll, like pixel-scroll-precision, uses vscroll for its scrolling implementation) - make-cursor-line-fully-visible=t Note that pixel-scroll-precision disables make-cursor-line-fully-visible, but this leads to partially visible lines causing problems in various other situations (e.g. comint-scroll-show-maximum-output). So disabling isn't ideal. A simple test (validated in Emacs 30 with NS and mac builds) is attached. Evaluate the buffer and it will enable make-cursor-line-fully-visible, visit simple.el, then time moving forward to the end of a line with and without non-zero vscroll. This is painfully slow with make-cursor-line-fully-visible=t. The reported slowdown for simple motion commands like forward-char is 100-4000x. I've profiled the slow case, see attached for the important parts. As is clear, of the ~8s it took to move to the end of the line (twice), get_next_display_element and set_iterator_to_next are the main culprits (arrived at separately via try_window and partial_line_height) with gui_produce_glyphs contributing. Notably, this slowdown attends all frames and windows showing the buffer, and can leak into some other windows like the minibuffer, when a buffer in some window is in this state.
[test_vscroll_induced_lag.el (application/octet-stream, attachment)]
[vscroll_lag_profile.txt (text/plain, attachment)]
[Message part 4 (text/plain, inline)]
[1] In this instance. This can vary with window size, sometimes 2s or more per forward-char is possible.
bug-gnu-emacs <at> gnu.org
:bug#78766
; Package emacs
.
(Thu, 12 Jun 2025 06:02:02 GMT) Full text and rfc822 format available.Message #8 received at 78766 <at> debbugs.gnu.org (full text, mbox):
From: Eli Zaretskii <eliz <at> gnu.org> To: JD Smith <jdtsmith <at> gmail.com> Cc: 78766 <at> debbugs.gnu.org Subject: Re: bug#78766: 100-4000x redisplay slowdown with vscroll>0 and make-cursor-line-fully-visible=t Date: Thu, 12 Jun 2025 09:01:26 +0300
> From: JD Smith <jdtsmith <at> gmail.com> > Date: Wed, 11 Jun 2025 19:08:01 -0400 > > Users of ultra-scroll noticed significant slowdowns in some situations. We traced it back to the combination of: > > - vscroll > 0 (ultra-scroll, like pixel-scroll-precision, uses vscroll for its scrolling implementation) > - make-cursor-line-fully-visible=t > > Note that pixel-scroll-precision disables make-cursor-line-fully-visible, but this leads to partially visible lines causing problems in various other situations (e.g. comint-scroll-show-maximum-output). So disabling isn't ideal. > > A simple test (validated in Emacs 30 with NS and mac builds) is attached. Evaluate the buffer and it will enable make-cursor-line-fully-visible, visit simple.el, then time moving forward to the end of a line with and without non-zero vscroll. > > This is painfully slow with make-cursor-line-fully-visible=t. The reported slowdown for simple motion commands like forward-char is 100-4000x. AFAIU, you are asking Emacs to do the impossible: make the cursor line fully-visible when the cursor line is at the top of the window, and therefore _cannot_ be fully visible due to non-zero vscroll. Am I right, or did I miss something? If I'm right, then could you please explain what do you expect in this situation, and why do you expect this to do anything useful? I haven't yet step through the code in this case, but my guess is that the display engine attempts all kinds of measures to make the cursor line visible, eventually failing, and those attempts are expensive because the display code was never designed to cope with such a strange set of conflicting requirements. In particular, it expects the partially-visible line situation to happen at the bottom of the window, not at its top. > I've profiled the slow case, see attached for the important parts. As is clear, of the ~8s it took to move to the end of the line (twice), get_next_display_element and set_iterator_to_next are the main culprits (arrived at separately via try_window and partial_line_height) with gui_produce_glyphs contributing. These are all normally called functions, so the profile doesn't help much. My guess is that some of the functions are called an abnormally large number of times, but to see that, I need (a) how many times was each function called, and (b) across how many characters did Emacs move during the time the profile was collected. This is so I could calculate how many times each of the functions was called _per_character_move_. Also note that normally, moving the cursor is very fast because it employs significant redisplay optimizations, which usually cause only a single screen line to be updated. The situation you create with this recipe most probably disables all of those optimizations, and thus makes the response to C-f much slower. We could perhaps improve the situation in some way, but I need to understand why such a strange combination of display-related knobs is used in the first place, and what do you expect it to produce and why. Thanks.
bug-gnu-emacs <at> gnu.org
:bug#78766
; Package emacs
.
(Thu, 12 Jun 2025 18:01:01 GMT) Full text and rfc822 format available.Message #11 received at 78766 <at> debbugs.gnu.org (full text, mbox):
From: JD Smith <jdtsmith <at> gmail.com> To: Eli Zaretskii <eliz <at> gnu.org>, 78766 <at> debbugs.gnu.org Subject: Re: bug#78766: 100-4000x redisplay slowdown with vscroll>0 and make-cursor-line-fully-visible=t Date: Thu, 12 Jun 2025 14:00:17 -0400
[Message part 1 (text/plain, inline)]
> On Jun 12, 2025, at 2:01 AM, Eli Zaretskii <eliz <at> gnu.org> wrote: Thanks for taking a look. > AFAIU, you are asking Emacs to do the impossible: make the cursor line > fully-visible when the cursor line is at the top of the window, and > therefore _cannot_ be fully visible due to non-zero vscroll. Am I > right, or did I miss something? > If I'm right, then could you please explain what do you expect in this > situation, and why do you expect this to do anything useful? Other than the extreme slowness, the current behavior with make-cursor-line-fully-visible=t is fine. Commands which move window-start check for partially visible cursor lines (at the bottom of the window) and make them fully visible, presumably by zeroing vscroll and changing window-start. Other point movement commands do nothing special. I do note that line-move as a special case usually resets vscroll to zero, which quickly relieves the issue. > I haven't yet step through the code in this case, but my guess is that > the display engine attempts all kinds of measures to make the cursor > line visible, eventually failing, and those attempts are expensive > because the display code was never designed to cope with such a > strange set of conflicting requirements. In particular, it expects > the partially-visible line situation to happen at the bottom of the > window, not at its top. Interesting. If there's not a straightforward way to avoid these expensive (and doomed to fail) attempts, perhaps a means of preventing the cursor from landing on a partially visible top line would be a better approach. Or even forcibly disabling the checks when point is on the first line of the window, and updating the docs to mention `make-cursor-line-fully-visible=t' works only on the final line. > We could perhaps improve > the situation in some way, but I need to understand why such a strange > combination of display-related knobs is used in the first place, and > what do you expect it to produce and why. This reproduction is not of course meant to be a realistic code path, it's merely a straightforward way to reliably demonstrate the issue. The situation of a cursor landing on a partially visible top line can happen naturally whenever vscroll>0 in a window. Non-zero vscroll is used by pixel scrolling, but also by line-move with tall lines (e.g. images). Point ends up on a partial first line for a variety of reasons. For example, it happens reliably when folding (hiding) an org-mode block which occupies the full window height in a separate window on the frame. Check the ultra-scroll issue[1] for a video of this. I suspect more people haven't noticed this because the #1 way to get non-zero vscroll is pixel-scrolling, and pixel-scroll-precision-mode disables the fully-visible cursor line checks which are apparently misbehaving. >> I've profiled the slow case, see attached for the important parts. As is clear, of the ~8s it took to move to the end of the line (twice), get_next_display_element and set_iterator_to_next are the main culprits (arrived at separately via try_window and partial_line_height) with gui_produce_glyphs contributing. > > These are all normally called functions, so the profile doesn't help > much. My guess is that some of the functions are called an abnormally > large number of times, but to see that, I need (a) how many times was > each function called, and (b) across how many characters did Emacs > move during the time the profile was collected. This is so I could > calculate how many times each of the functions was called > _per_character_move_. I have simplified the test to enable the problematic settings and move just once across the line of 56 chars. This time was very slow; it reports: Commencing move to end of line with 56 chars [20625]! Done in (62.586448000000004 18 0.8153190000000023)! A total of 3383 characters in simple.el were visible during this movement, and one other frame (monitoring *Messages*) was as well. I have expanded all of the relevant profile data, so the sample data are larger; see attached. The columns are: # Samples, Self # Samples, Weight (total time), Self Weight (total time in function body) This (1ms) sampling profile unfortunately doesn't contain call count. I could try to take a look, but you probably have better ideas than me about which functions may be called overly often. [1] https://github.com/jdtsmith/ultra-scroll/issues/32
[vscroll_lag_profile_simple.txt (text/plain, attachment)]
[test_vscroll_induced_lag_simple.el (application/octet-stream, attachment)]
[Message part 4 (text/plain, inline)]
bug-gnu-emacs <at> gnu.org
:bug#78766
; Package emacs
.
(Fri, 13 Jun 2025 14:08:01 GMT) Full text and rfc822 format available.Message #14 received at 78766 <at> debbugs.gnu.org (full text, mbox):
From: JD Smith <jdtsmith <at> gmail.com> To: Eli Zaretskii <eliz <at> gnu.org>, 78766 <at> debbugs.gnu.org Subject: Re: bug#78766: 100-4000x redisplay slowdown with vscroll>0 and make-cursor-line-fully-visible=t Date: Fri, 13 Jun 2025 10:07:05 -0400
[Message part 1 (text/plain, inline)]
Quick followup. I was able to instrument the single function `set_iterator_to_next' to track total call count and distribution of call times of this core function. Since the instrumentation slowed the test down so much, I profiled moving (forward-char) by just 3 chars with redisplay in a partially visible top line, using the slow setting of make-cursor-line-fully-visible=t. The total call count per character moved in the slow case is enormous: >800K.
[PastedGraphic-5.png (image/png, inline)]
[Message part 3 (text/plain, inline)]
I compared this against the fast case in which make-cursor-line-fully-visible=nil. Here I moved 56 * 20 iterations = 1,120 chars. You can see the call count is much, much smaller (although with similar run times per function call):
[PastedGraphic-6.png (image/png, inline)]
[Message part 5 (text/plain, inline)]
I.e. about 280 calls per char moved. In both cases the time series of calls looks fairly normal:
[PastedGraphic-8.png (image/png, inline)]
[Message part 7 (text/plain, inline)]
bug-gnu-emacs <at> gnu.org
:bug#78766
; Package emacs
.
(Fri, 13 Jun 2025 15:17:01 GMT) Full text and rfc822 format available.Message #17 received at 78766 <at> debbugs.gnu.org (full text, mbox):
From: Eli Zaretskii <eliz <at> gnu.org> To: JD Smith <jdtsmith <at> gmail.com> Cc: 78766 <at> debbugs.gnu.org Subject: Re: bug#78766: 100-4000x redisplay slowdown with vscroll>0 and make-cursor-line-fully-visible=t Date: Fri, 13 Jun 2025 18:16:22 +0300
> From: JD Smith <jdtsmith <at> gmail.com> > Date: Fri, 13 Jun 2025 10:07:05 -0400 > > Quick followup. I was able to instrument the single function `set_iterator_to_next' to track total call count and distribution of call times of this core function. Since the instrumentation slowed the test down so much, I profiled moving (forward-char) by just 3 chars with redisplay in a partially visible top line, using the slow setting of make-cursor-line-fully-visible=t. > > The total call count per character moved in the slow case is enormous: >800K. Thanks, but this doesn't really add any useful info. set_iterator_to_next is too low-level to explain what's going on. It is expected that it will be called many times, but the question is why? And the answer to that is at higher levels, at the level of the functions called by redisplay_window. IOW, if we call set_iterator_to_next so many times, we either (a) redraw the entire window many times, or (b) redraw some small subset of the window's lines even more times. Which one(s) of these actually happen and why is the interesting question. And you still haven't explained to me what you want Emacs to do when you set vscroll > 0 (which necessarily makes the top-most screen line partially-visible) and also set make-cursor-line-fully-visible = t. These two contradict one another, so you basically ask Emacs to square the circle. You also haven't explained why using the solution of pixel-scroll-precision-mode is not good for your mode.
bug-gnu-emacs <at> gnu.org
:bug#78766
; Package emacs
.
(Fri, 13 Jun 2025 18:05:04 GMT) Full text and rfc822 format available.Message #20 received at 78766 <at> debbugs.gnu.org (full text, mbox):
From: JD Smith <jdtsmith <at> gmail.com> To: Eli Zaretskii <eliz <at> gnu.org> Cc: 78766 <at> debbugs.gnu.org Subject: Re: bug#78766: 100-4000x redisplay slowdown with vscroll>0 and make-cursor-line-fully-visible=t Date: Fri, 13 Jun 2025 14:04:03 -0400
> On Jun 13, 2025, at 11:16 AM, Eli Zaretskii <eliz <at> gnu.org> wrote: > >> From: JD Smith <jdtsmith <at> gmail.com> >> Date: Fri, 13 Jun 2025 10:07:05 -0400 >> >> Quick followup. I was able to instrument the single function `set_iterator_to_next' to track total call count and distribution of call times of this core function. Since the instrumentation slowed the test down so much, I profiled moving (forward-char) by just 3 chars with redisplay in a partially visible top line, using the slow setting of make-cursor-line-fully-visible=t. >> >> The total call count per character moved in the slow case is enormous: >800K. > > Thanks, but this doesn't really add any useful info. It shows that the iterator movement is not in and of itself slow, but that it is called many times per character movement, which was your request before. But I agree, it's certainly not the full story. > set_iterator_to_next is too low-level to explain what's going on. It > is expected that it will be called many times, but the question is > why? I can see that, but on the other hand, it would be strange to expect it to be called 2850x as many times per character movement when a setting is toggled from nil to t. That seems to me quite excessive. Your intuitions here may be better. > And the answer to that is at higher levels, at the level of the > functions called by redisplay_window. > IOW, if we call set_iterator_to_next so many times, we either (a) > redraw the entire window many times, or (b) redraw some small subset > of the window's lines even more times. Which one(s) of these actually > happen and why is the interesting question. I'm happy to perform call count and duration stats for other functions if you want to suggest some, but it sounds like you may be better positioned to quickly drill down on this. > And you still haven't explained to me what you want Emacs to do when > you set vscroll > 0 (which necessarily makes the top-most screen line > partially-visible) and ... make-cursor-line-fully-visible = t I'm agnostic. Expanding on what I said before, Emacs could either: 1. Prevent the cursor from landing on a partially visible top line. If it tries to go there, adjust window-start. 2. Keep the current behavior, allowing the cursor to fall on such a line, but eliminate the ~3000x slower path that must be lurking there. I suspect #2 means effectively ignoring make-cursor-line-fully-visible = t when the cursor is on the top line. If I got to pick, I'd have a small preference for #1, but I don't have an appreciation for what either would involve in terms of a fix. Hence the agnosticism. > These two contradict one another, so you basically ask Emacs to square the circle. I don't see the inconsistency. These two settings together do not imply a desire for the cursor to end up on the partially-visible line. The setting is not called "make-top-line-fully-visible". > You also haven't explained why using the solution of > pixel-scroll-precision-mode is not good for your mode. As I mentioned earlier: > this leads to partially visible lines causing problems in various other situations (e.g. comint-scroll-show-maximum-output). So disabling isn't ideal. Disabling make-cursor-line-fully-visible leads to... unwanted partially visible cursor lines at the bottom of the window (presumably the reason it exists to begin with). It doesn't affect scrolling, so this slowness bug is AFAIU the only reason to disable it. My mode isn't central to this story, other than the fact that it revealed the problem. Note that make-cursor-line-fully-visible = t is the default, and vscroll>0 can readily occur via line-move across tall images, no pixel-scrolling needed. From simple.el: ;; If we moved into a tall line, set vscroll to make ;; scrolling through tall images more smooth.
bug-gnu-emacs <at> gnu.org
:bug#78766
; Package emacs
.
(Sat, 14 Jun 2025 07:00:01 GMT) Full text and rfc822 format available.Message #23 received at 78766 <at> debbugs.gnu.org (full text, mbox):
From: Eli Zaretskii <eliz <at> gnu.org> To: JD Smith <jdtsmith <at> gmail.com> Cc: 78766 <at> debbugs.gnu.org Subject: Re: bug#78766: 100-4000x redisplay slowdown with vscroll>0 and make-cursor-line-fully-visible=t Date: Sat, 14 Jun 2025 09:59:09 +0300
> From: JD Smith <jdtsmith <at> gmail.com> > Date: Fri, 13 Jun 2025 14:04:03 -0400 > Cc: 78766 <at> debbugs.gnu.org > > > > > On Jun 13, 2025, at 11:16 AM, Eli Zaretskii <eliz <at> gnu.org> wrote: > > > >> From: JD Smith <jdtsmith <at> gmail.com> > >> Date: Fri, 13 Jun 2025 10:07:05 -0400 > >> > >> Quick followup. I was able to instrument the single function `set_iterator_to_next' to track total call count and distribution of call times of this core function. Since the instrumentation slowed the test down so much, I profiled moving (forward-char) by just 3 chars with redisplay in a partially visible top line, using the slow setting of make-cursor-line-fully-visible=t. > >> > >> The total call count per character moved in the slow case is enormous: >800K. > > > > Thanks, but this doesn't really add any useful info. > > It shows that the iterator movement is not in and of itself slow, but that it is called many times per character movement, which was your request before. True, but it's hardly new information. Problems with slow redisplay happen not because set_iterator_to_next is slow, but because it is called too many times, for whatever reasons. It is those reasons that are interesting, because the way to make redisplay fast enough is to eliminate the reasons for those many calls. > > set_iterator_to_next is too low-level to explain what's going on. It > > is expected that it will be called many times, but the question is > > why? > > I can see that, but on the other hand, it would be strange to expect it to be called 2850x as many times per character movement when a setting is toggled from nil to t. That seems to me quite excessive. Your intuitions here may be better. Which is why I'm asking why these many calls happen. What my intuition tells was described up-thread, but what we need here is facts: why does Emacs in fact try to redraw this window's characters so many times? > > And the answer to that is at higher levels, at the level of the > > functions called by redisplay_window. > > IOW, if we call set_iterator_to_next so many times, we either (a) > > redraw the entire window many times, or (b) redraw some small subset > > of the window's lines even more times. Which one(s) of these actually > > happen and why is the interesting question. > > I'm happy to perform call count and duration stats for other functions if you want to suggest some, but it sounds like you may be better positioned to quickly drill down on this. The way to answer these questions is to step through the code in redisplay_window and see what it does and why in that case. If there's no answer to this question by the time I get enough free time to do it myself, I will. > > And you still haven't explained to me what you want Emacs to do when > > you set vscroll > 0 (which necessarily makes the top-most screen line > > partially-visible) and ... make-cursor-line-fully-visible = t > > I'm agnostic. Expanding on what I said before, Emacs could either: > > 1. Prevent the cursor from landing on a partially visible top line. If it tries to go there, adjust window-start. This is AFAIU a new display feature that currently doesn't exist. If I'm right, it will need addition of new code. > 2. Keep the current behavior, allowing the cursor to fall on such a line, but eliminate the ~3000x slower path that must be lurking there. That's impossible, because make-cursor-line-fully-visible cannot be ignored under the current code. It's a hard requirement, and is very central to one of the most important goals of the design of the Emacs display engine: ensure that point is always (fully) visible in the window. > > These two contradict one another, so you basically ask Emacs to square the circle. > > I don't see the inconsistency. These two settings together do not imply a desire for the cursor to end up on the partially-visible line. The setting is not called "make-top-line-fully-visible". The way the display engine is designed, it first find a promising starting-point for displaying the buffer in its window, then tries to display the window with that start point, then check whether such a display is successful (and here it checks whether point is in a fully-visible screen line, among others). I hope you understand the conundrum now? > > You also haven't explained why using the solution of > > pixel-scroll-precision-mode is not good for your mode. > > As I mentioned earlier: > > > this leads to partially visible lines causing problems in various other situations (e.g. comint-scroll-show-maximum-output). So disabling isn't ideal. > > Disabling make-cursor-line-fully-visible leads to... unwanted partially visible cursor lines at the bottom of the window (presumably the reason it exists to begin with). > > It doesn't affect scrolling, so this slowness bug is AFAIU the only reason to disable it. My mode isn't central to this story, other than the fact that it revealed the problem. FWIW, I consider all these weak justifications when you are literally fighting against the design of the display engine. > Note that make-cursor-line-fully-visible = t is the default, and vscroll>0 can readily occur via line-move across tall images, no pixel-scrolling needed. From simple.el: > > ;; If we moved into a tall line, set vscroll to make > ;; scrolling through tall images more smooth. Yes, "for scrolling through tall images". That's what vscroll was designed for, but you are using it for completely different purposes. Why is it a surprise that it doesn't work well? More generally, when will authors of Lisp packages understand a simple truth that the Emacs design principles basically place hard limitations on what Lisp programs can usefully and efficiently do, and stop attempts to use the Emacs features way out of their design space? (No, don't answer that.)
bug-gnu-emacs <at> gnu.org
:bug#78766
; Package emacs
.
(Sat, 14 Jun 2025 13:15:05 GMT) Full text and rfc822 format available.Message #26 received at 78766 <at> debbugs.gnu.org (full text, mbox):
From: Eli Zaretskii <eliz <at> gnu.org> To: jdtsmith <at> gmail.com Cc: 78766 <at> debbugs.gnu.org Subject: Re: bug#78766: 100-4000x redisplay slowdown with vscroll>0 and make-cursor-line-fully-visible=t Date: Sat, 14 Jun 2025 16:14:20 +0300
> Cc: 78766 <at> debbugs.gnu.org > Date: Sat, 14 Jun 2025 09:59:09 +0300 > From: Eli Zaretskii <eliz <at> gnu.org> > > > It shows that the iterator movement is not in and of itself slow, but that it is called many times per character movement, which was your request before. > > True, but it's hardly new information. Problems with slow redisplay > happen not because set_iterator_to_next is slow, but because it is > called too many times, for whatever reasons. It is those reasons that > are interesting, because the way to make redisplay fast enough is to > eliminate the reasons for those many calls. > > > > set_iterator_to_next is too low-level to explain what's going on. It > > > is expected that it will be called many times, but the question is > > > why? > > > > I can see that, but on the other hand, it would be strange to expect it to be called 2850x as many times per character movement when a setting is toggled from nil to t. That seems to me quite excessive. Your intuitions here may be better. > > Which is why I'm asking why these many calls happen. What my > intuition tells was described up-thread, but what we need here is > facts: why does Emacs in fact try to redraw this window's characters > so many times? > > > > And the answer to that is at higher levels, at the level of the > > > functions called by redisplay_window. > > > IOW, if we call set_iterator_to_next so many times, we either (a) > > > redraw the entire window many times, or (b) redraw some small subset > > > of the window's lines even more times. Which one(s) of these actually > > > happen and why is the interesting question. > > > > I'm happy to perform call count and duration stats for other functions if you want to suggest some, but it sounds like you may be better positioned to quickly drill down on this. > > The way to answer these questions is to step through the code in > redisplay_window and see what it does and why in that case. If > there's no answer to this question by the time I get enough free time > to do it myself, I will. I've now looked at this. It's like I guessed: this case of having the cursor in the first visible line that is only partially visible is not supported, and the code just loops until we forcibly stop that: /* If cursor ends up on a partially visible line, treat that as being off the bottom of the screen. */ if (! cursor_row_fully_visible_p (w, extra_scroll_margin_lines <= 1, false, false) /* It's possible that the cursor is on the first line of the buffer, which is partially obscured due to a vscroll (Bug#7537). In that case, avoid looping forever. */ && extra_scroll_margin_lines < w->desired_matrix->nrows - 1) { clear_glyph_matrix (w->desired_matrix); ++extra_scroll_margin_lines; goto too_near_end; } So what happens here is that code loops as many times as there are screen lines in the window, and then bails out. A simple solution I can offer is in the patch below. Please give it enough testing to see that it doesn't cause regressions elsewhere in redisplay. If it doesn't, I will install it. diff --git a/src/xdisp.c b/src/xdisp.c index 27094f8..c3c4315 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -19447,15 +19447,19 @@ try_scrolling (Lisp_Object window, bool just_this_one_p, /* If cursor ends up on a partially visible line, treat that as being off the bottom of the screen. */ if (! cursor_row_fully_visible_p (w, extra_scroll_margin_lines <= 1, - false, false) - /* It's possible that the cursor is on the first line of the - buffer, which is partially obscured due to a vscroll - (Bug#7537). In that case, avoid looping forever. */ - && extra_scroll_margin_lines < w->desired_matrix->nrows - 1) + false, false)) { clear_glyph_matrix (w->desired_matrix); ++extra_scroll_margin_lines; - goto too_near_end; + /* It's possible that the cursor is on the first line of the + buffer, which is partially obscured due to a vscroll + (Bug#7537). In that case, just fail, since the code above + is not prepared to deal with that case. */ + if (MATRIX_ROW_PARTIALLY_VISIBLE_AT_TOP_P + (w, MATRIX_ROW (w->desired_matrix, w->cursor.vpos))) + rc = SCROLLING_FAILED; + else + goto too_near_end; } rc = SCROLLING_SUCCESS; }
bug-gnu-emacs <at> gnu.org
:bug#78766
; Package emacs
.
(Sun, 15 Jun 2025 16:38:02 GMT) Full text and rfc822 format available.Message #29 received at 78766 <at> debbugs.gnu.org (full text, mbox):
From: JD Smith <jdtsmith <at> gmail.com> To: Eli Zaretskii <eliz <at> gnu.org> Cc: 78766 <at> debbugs.gnu.org Subject: Re: bug#78766: 100-4000x redisplay slowdown with vscroll>0 and make-cursor-line-fully-visible=t Date: Sun, 15 Jun 2025 12:37:03 -0400
> On Jun 14, 2025, at 9:14 AM, Eli Zaretskii <eliz <at> gnu.org> wrote: > > I've now looked at this. It's like I guessed: this case of having the > cursor in the first visible line that is only partially visible is not > supported, and the code just loops until we forcibly stop that: Good find, thanks for looking at this. I tested the patch. While it eliminated the slowdown, it led to other redisplay artifacts when point is on a partially visible top line, such as duplicated half overlapping mode-lines when invoking M-x. But perhaps you did not intend to always return SCROLLING_SUCCESS here? + if (MATRIX_ROW_PARTIALLY_VISIBLE_AT_TOP_P + (w, MATRIX_ROW (w->desired_matrix, w->cursor.vpos))) + rc = SCROLLING_FAILED; + else + goto too_near_end; } rc = SCROLLING_SUCCESS; If I modify that to return FAILED immediately, ala: if (MATRIX_ROW_PARTIALLY_VISIBLE_AT_TOP_P (w, MATRIX_ROW (w->desired_matrix, w->cursor.vpos))) return SCROLLING_FAILED; else goto too_near_end; } rc = SCROLLING_SUCCESS; I get no artifacts, and it seems to do what I'd expect — if you try to move point onto a partially visible top row, the window is recentered according to scroll-conservatively. Seems to work normally with line-move and tall images. I will run with it for a while to see if anything else appears. Thanks again.
bug-gnu-emacs <at> gnu.org
:bug#78766
; Package emacs
.
(Sun, 15 Jun 2025 17:01:01 GMT) Full text and rfc822 format available.Message #32 received at 78766 <at> debbugs.gnu.org (full text, mbox):
From: Eli Zaretskii <eliz <at> gnu.org> To: JD Smith <jdtsmith <at> gmail.com> Cc: 78766 <at> debbugs.gnu.org Subject: Re: bug#78766: 100-4000x redisplay slowdown with vscroll>0 and make-cursor-line-fully-visible=t Date: Sun, 15 Jun 2025 20:00:23 +0300
> From: JD Smith <jdtsmith <at> gmail.com> > Date: Sun, 15 Jun 2025 12:37:03 -0400 > Cc: 78766 <at> debbugs.gnu.org > > > > > On Jun 14, 2025, at 9:14 AM, Eli Zaretskii <eliz <at> gnu.org> wrote: > > > > I've now looked at this. It's like I guessed: this case of having the > > cursor in the first visible line that is only partially visible is not > > supported, and the code just loops until we forcibly stop that: > > Good find, thanks for looking at this. I tested the patch. While it eliminated the slowdown, it led to other redisplay artifacts when point is on a partially visible top line, such as duplicated half overlapping mode-lines when invoking M-x. > > But perhaps you did not intend to always return SCROLLING_SUCCESS here? > > + if (MATRIX_ROW_PARTIALLY_VISIBLE_AT_TOP_P > + (w, MATRIX_ROW (w->desired_matrix, w->cursor.vpos))) > + rc = SCROLLING_FAILED; > + else > + goto too_near_end; > } > rc = SCROLLING_SUCCESS; > > If I modify that to return FAILED immediately, ala: > > if (MATRIX_ROW_PARTIALLY_VISIBLE_AT_TOP_P > (w, MATRIX_ROW (w->desired_matrix, w->cursor.vpos))) > return SCROLLING_FAILED; > else > goto too_near_end; > } > rc = SCROLLING_SUCCESS; > > I get no artifacts, and it seems to do what I'd expect — if you try to move point onto a partially visible top row, the window is recentered according to scroll-conservatively. > > Seems to work normally with line-move and tall images. I will run with it for a while to see if anything else appears. If that works for you, it's fine by me. I thought that recentering will effectively disable the effect of vscroll, and so assumed it will be unacceptable for your package. Will wait for your further feedback.
bug-gnu-emacs <at> gnu.org
:bug#78766
; Package emacs
.
(Mon, 16 Jun 2025 21:42:05 GMT) Full text and rfc822 format available.Message #35 received at 78766 <at> debbugs.gnu.org (full text, mbox):
From: JD Smith <jdtsmith <at> gmail.com> To: Eli Zaretskii <eliz <at> gnu.org> Cc: 78766 <at> debbugs.gnu.org Subject: Re: bug#78766: 100-4000x redisplay slowdown with vscroll>0 and make-cursor-line-fully-visible=t Date: Mon, 16 Jun 2025 17:40:46 -0400
[Message part 1 (text/plain, inline)]
> On Jun 15, 2025, at 1:00 PM, Eli Zaretskii <eliz <at> gnu.org> wrote: >> if (MATRIX_ROW_PARTIALLY_VISIBLE_AT_TOP_P >> (w, MATRIX_ROW (w->desired_matrix, w->cursor.vpos))) >> return SCROLLING_FAILED; >> else >> goto too_near_end; >> } >> rc = SCROLLING_SUCCESS; >> >> I get no artifacts, and it seems to do what I'd expect — if you try to move point onto a partially visible top row, the window is recentered according to scroll-conservatively. >> >> Seems to work normally with line-move and tall images. I will run with it for a while to see if anything else appears. > > If that works for you, it's fine by me. I thought that recentering > will effectively disable the effect of vscroll, and so assumed it will > be unacceptable for your package. Your instincts were good. The patch was fine in almost all scenarios, but had problems for images taller than the window. In this situation you have no choice but to keep point on a partially visible top line. The inherent recentering negatively affects line-move across tall images, especially noticeable for multiple tall lines in a row. If we use your top row test as enclosed, this seems to do what we want: avoid the long loop and resulting big slowdown, while permitting the cursor to fall on a partially visible top line. Thanks for your efforts. 
[Message part 2 (text/html, inline)]
[slow_vscroll_make_cursor_fully_visible.patch (application/octet-stream, attachment)]
[Message part 4 (text/html, inline)]
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.