Package: emacs;
Reported by: Stefan Kangas <stefankangas <at> gmail.com>
Date: Mon, 30 Sep 2024 08:03:02 UTC
Severity: normal
To reply to this bug, email your comments to 73563 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
ben <at> bensimms.moe, bug-gnu-emacs <at> gnu.org
:bug#73563
; Package emacs
.
(Mon, 30 Sep 2024 08:03:02 GMT) Full text and rfc822 format available.Stefan Kangas <stefankangas <at> gmail.com>
:ben <at> bensimms.moe, bug-gnu-emacs <at> gnu.org
.
(Mon, 30 Sep 2024 08:03:02 GMT) Full text and rfc822 format available.Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):
From: Stefan Kangas <stefankangas <at> gmail.com> To: bug-gnu-emacs <at> gnu.org Subject: [Ben Simms] Performance bottleneck in ns_draw_fringe_bitmap Date: Mon, 30 Sep 2024 07:21:03 +0000
[Message part 1 (text/plain, inline)]
I'm forwarding this to the bug tracker so that we don't lose track of it. Original message: https://lists.gnu.org/r/emacs-devel/2024-06/msg00900.html -------------------- Start of forwarded message -------------------- From: Ben Simms <ben <at> bensimms.moe> Date: Wed, 26 Jun 2024 13:56:43 +0200 Subject: Performance bottleneck in ns_draw_fringe_bitmap To: emacs-devel <at> gnu.org
[Message part 2 (text/plain, attachment)]
[Message part 3 (text/html, attachment)]
bug-gnu-emacs <at> gnu.org
:bug#73563
; Package emacs
.
(Mon, 30 Sep 2024 12:28:02 GMT) Full text and rfc822 format available.Message #8 received at 73563 <at> debbugs.gnu.org (full text, mbox):
From: Eli Zaretskii <eliz <at> gnu.org> To: Stefan Kangas <stefankangas <at> gmail.com> Cc: ben <at> bensimms.moe, 73563 <at> debbugs.gnu.org Subject: Re: bug#73563: [Ben Simms] Performance bottleneck in ns_draw_fringe_bitmap Date: Mon, 30 Sep 2024 15:26:31 +0300
> Cc: Ben Simms <ben <at> bensimms.moe> > From: Stefan Kangas <stefankangas <at> gmail.com> > Date: Mon, 30 Sep 2024 07:21:03 +0000 > > Hi all, I recently started using Emacs (ns) HEAD on an ARM macos sonoma > system. > > I've noticed that ns_draw_fringe_bitmap is a fairly large performance sink > when using pixel scrolling (to the point of 99% of cpu time being inside this > function, with Emacs drawing at approx 5Hz). The slowness here isn't as > obvious when not pixel scrolling, presumably because Emacs never tries to > redraw at 60+Hz otherwise. > > I have performed some profiling and discovered that in my observed worse case > situation, of the 99% of cpu time spent in ns_draw_fringe_bitmap, approx 50% > is spent in [NSBezierPath copy], and approx 30% in [NSBezierPath fill]. According to the posted profile, ns_draw_fringe_bitmap takes much less than 99% of CPU time, somewhere around 10%. What am I missing?
bug-gnu-emacs <at> gnu.org
:bug#73563
; Package emacs
.
(Tue, 13 May 2025 11:40:01 GMT) Full text and rfc822 format available.Message #11 received at submit <at> debbugs.gnu.org (full text, mbox):
From: Jordan Ellis Coppard <jc+o.emacs <at> wz.ht> To: Emacs Bugs <bug-gnu-emacs <at> gnu.org> Cc: Ben Simms <ben <at> bensimms.moe> Subject: bug#73563: [Ben Simms] Performance bottleneck in ns_draw_fringe_bitmap Date: Tue, 13 May 2025 20:37:55 +0900
Howdy, I've been using the following patch (bottom of this email) until my most recent rebuild of Emacs from master. Since the recent commit fixing stipple drawing the patch can be reduced to just: https://github.com/emacs-mirror/emacs/commit/7f2efe6503fdce2a4e552c14802644a05b581bc7 (link to a diff authored by Ben Simms). I've now noticed again extreme slowdown when fringe bitmaps are used. It appears to be proportional to the number of bits set in the bitmap. An 'empty' bitmap (all zeroes) doesn't yield any noticeable slowdown, a bitmap with 1 bit set yields a little, with 2 its noticeable and with all 8 bits set Emacs takes up to 1 second to respond to any input at all. This has been the case since at least August of last year. I can get a minimal reproduction but it has happened on completely different configurations with the only common denominator being the NS build (on ARM-based macOS), and the use of fringe bitmaps. Below is trace with update_frame as the root context. Captured with Instruments.app (which uses dtrace and more under the hood IIRC). ns_draw_fringe_bitmap takes up 94% of the time for the short reproduction this trace records. The deepest the trace goes is to CG::Path::recalculate_bounding_box() which eats 80% of time. So instead of a cached value the bounding box is being recalculated every time which appears very expensive, but beyond that I have close to zero experience with macOS' graphics stack. I've just recompiled Emacs against the same commit of my recent master build (648453c04d9b91d96452b930c0c948b0b39b5dc0) except now with the patch applied (since the stipple changes are merged it's just the smallest subset: https://github.com/emacs-mirror/emacs/commit/7f2efe6503fdce2a4e552c14802644a05b581bc7) and once again fringe performance is back to being buttery smooth. (trace also here in-case formatting gets borked: trace: 3.07 s 100.0% 0 s update_frame 3.06 s 100.0% 0 s update_window_tree 3.06 s 100.0% 1.00 ms update_window 2.89 s 94.2% 0 s gui_update_window_end 2.88 s 94.1% 0 s draw_window_fringes 2.88 s 93.9% 0 s draw_fringe_bitmap 2.88 s 93.9% 0 s draw_fringe_bitmap_1 2.88 s 93.9% 0 s ns_draw_fringe_bitmap 2.72 s 88.7% 0 s -[NSBezierPath copyWithZone:] 2.72 s 88.7% 0 s -[NSBezierPath _appendToPath:] 2.72 s 88.7% 0 s -[NSBezierPath _enumeratePathElementsUsingBlock:] 2.72 s 88.7% 0 s CGPathApplyWithBlock2 2.72 s 88.7% 5.00 ms CG::Path::apply(void (CGPathElementType, CGPoint const*, bool*) block_pointer) const 2.71 s 88.5% 30.00 ms __CGPathApplyWithBlock2_block_invoke 2.68 s 87.4% 11.00 ms __49-[NSBezierPath _enumeratePathElementsUsingBlock:]_block_invoke 2.48 s 80.8% 1.00 ms -[NSBezierPath(NSBezierPathDevicePrimitives) _deviceMoveToPoint:] 2.47 s 80.6% 2.00 ms CGPathMoveToPoint 2.46 s 80.3% 2.46 s CG::Path::recalculate_bounding_box() 5.00 ms 0.2% 4.00 ms CG::Path::move_to_point(CGPoint const&, CGAffineTransform const*) 1.00 ms 0.0% 1.00 ms CG::Path::convert_to_huge() 2.00 ms 0.1% 2.00 ms (anonymous namespace)::transform_is_valid(CGAffineTransform const*) 1.00 ms 0.0% 1.00 ms CG::Path::convert_to_huge() 1.00 ms 0.0% 1.00 ms CGFloatValidateWithLog 1.00 ms 0.0% 1.00 ms DYLD-STUB$$CGPathMoveToPoint 1.00 ms 0.0% 1.00 ms -[NSBezierPath _cgPath] 1.00 ms 0.0% 1.00 ms objc_msgSend 1.00 ms 0.0% 1.00 ms (anonymous namespace)::transform_is_valid(CGAffineTransform const*) 67.00 ms 2.2% 7.00 ms -[NSBezierPath(NSBezierPathDevicePrimitives) _deviceLineToPoint:] 40.00 ms 1.3% 5.00 ms -[NSBezierPath(NSBezierPathDevicePrimitives) _deviceClosePath] 39.00 ms 1.3% 2.00 ms -[NSBezierPath lineToPoint:] 14.00 ms 0.5% 14.00 ms objc_msgSend 6.00 ms 0.2% 6.00 ms -[NSBezierPath _cgPath] 5.00 ms 0.2% 5.00 ms CGPathAddLineToPoint 4.00 ms 0.1% 4.00 ms __30-[NSBezierPath _appendToPath:]_block_invoke 4.00 ms 0.1% 4.00 ms objc_msgSend$lineToPoint: 2.00 ms 0.1% 2.00 ms CGPathIsEmpty 2.00 ms 0.1% 2.00 ms objc_msgSend$moveToPoint: 2.00 ms 0.1% 2.00 ms CG::Path::close_subpath() 1.00 ms 0.0% 1.00 ms objc_msgSend$_deviceLineToPoint: 1.00 ms 0.0% 1.00 ms CGPathGetCurrentPoint 1.00 ms 0.0% 1.00 ms -[NSBezierPath moveToPoint:] 1.00 ms 0.0% 1.00 ms objc_msgSend$_deviceClosePath 1.00 ms 0.0% 1.00 ms objc_msgSend$closePath 1.00 ms 0.0% 1.00 ms objc_msgSend$_deviceMoveToPoint: 3.00 ms 0.1% 3.00 ms -[NSBezierPath lineToPoint:] 1.00 ms 0.0% 1.00 ms -[NSBezierPath(NSBezierPathDevicePrimitives) _deviceLineToPoint:] 2.00 ms 0.1% 2.00 ms __49-[NSBezierPath _enumeratePathElementsUsingBlock:]_block_invoke 1.00 ms 0.0% 1.00 ms objc_msgSend$setFlatness: 104.00 ms 3.4% 0 s -[NSBezierPath fill] 42.00 ms 1.4% 0 s -[NSBezierPath transformUsingAffineTransform:] 7.00 ms 0.2% 0 s NSRectFill 2.00 ms 0.1% 0 s NSColorSetWithFillAndStroke 1.00 ms 0.0% 1.00 ms CGContextClipToRect 1.00 ms 0.0% 0 s -[NSBezierPath dealloc] 1.00 ms 0.0% 1.00 ms CGGStateSetCompositeOperation 1.00 ms 0.0% 1.00 ms lookup_named_face 5.00 ms 0.2% 4.00 ms set_buffer_internal_2 1.00 ms 0.0% 0 s unblock_input 1.00 ms 0.0% 0 s ns_draw_window_cursor 121.00 ms 3.9% 0 s ns_scroll_run 51.00 ms 1.7% 1.00 ms update_window_line 4.00 ms 0.1% 4.00 ms row_equal_p 1.00 ms 0.0% 1.00 ms xwidget_end_redisplay 1.00 ms 0.0% 0 s ns_update_end Patch: --- src/nsimage.m.orig +++ src/nsimage.m @@ -28,6 +28,7 @@ Updated by Christian Limpach (chris <at> nice.ch) /* This should be the first include, as it may set up #defines affecting interpretation of even the system includes. */ #include <config.h> +#include <CoreGraphics/CoreGraphics.h> #include "lisp.h" #include "dispextern.h" @@ -510,10 +511,20 @@ - (void) setAlphaAtX: (int) x Y: (int) y to: (unsigned char) a } /* Returns a pattern color, which is cached here. */ -- (NSColor *)stippleMask +- (CGImageRef)stippleMask { - if (stippleMask == nil) - stippleMask = [[NSColor colorWithPatternImage: self] retain]; + if (stippleMask == nil) { + CGDataProviderRef provider = CGDataProviderCreateWithData (NULL, [bmRep bitmapData], + [self sizeInBytes], NULL); + id mask = (id)CGImageMaskCreate( + [self size].width, + [self size].height, + 8, 8, [self size].width, + provider, NULL, 0); + + CGDataProviderRelease(provider); + stippleMask = (CGImageRef)[mask retain]; + } return stippleMask; } --- src/nsterm.h.orig +++ src/nsterm.h @@ -670,7 +670,7 @@ enum ns_return_frame_mode { NSBitmapImageRep *bmRep; /* used for accessing pixel data */ unsigned char *pixmapData[5]; /* shortcut to access pixel data */ - NSColor *stippleMask; + CGImageRef stippleMask; @public NSAffineTransform *transform; BOOL smoothing; @@ -687,8 +687,8 @@ enum ns_return_frame_mode green: (unsigned char)g blue: (unsigned char)b alpha:(unsigned char)a; - (void)setAlphaAtX: (int)x Y: (int)y to: (unsigned char)a; -- (NSColor *)stippleMask; +- (CGImageRef)stippleMask; - (Lisp_Object)getMetadata; - (BOOL)setFrame: (unsigned int) index; - (void)setTransform: (double[3][3]) m; --- src/nsterm.m.orig +++ src/nsterm.m @@ -2903,22 +2903,24 @@ Hide the window (X11 semantics) static void ns_define_fringe_bitmap (int which, unsigned short *bits, int h, int w) { - NSBezierPath *p = [NSBezierPath bezierPath]; - if (!fringe_bmp) fringe_bmp = [[NSMutableDictionary alloc] initWithCapacity:25]; - [p moveToPoint:NSMakePoint (0, 0)]; - for (int y = 0 ; y < h ; y++) - for (int x = 0 ; x < w ; x++) - { - bool bit = bits[y] & (1 << (w - x - 1)); - if (bit) - [p appendBezierPathWithRect:NSMakeRect (x, y, 1, 1)]; - } + for (int i = 0; i < h; i++) + bits[i] = ~bits[i]; + + CGDataProviderRef provider = CGDataProviderCreateWithData (NULL, bits, + sizeof (unsigned short) * h, NULL); + if (provider) { + id p = (id)CGImageMaskCreate (w, h, 1, 1, + sizeof (unsigned short), + provider, NULL, 0); + CGDataProviderRelease (provider); + + [fringe_bmp setObject:p forKey:[NSNumber numberWithInt:which]]; + } - [fringe_bmp setObject:p forKey:[NSNumber numberWithInt:which]]; } @@ -2981,26 +2983,29 @@ Hide the window (X11 semantics) NSRectFill (clearRect); } - NSBezierPath *bmp = [fringe_bmp objectForKey:[NSNumber numberWithInt:p->which]]; + CGImageRef bmp = (CGImageRef)[fringe_bmp objectForKey:[NSNumber numberWithInt:p->which]]; if (bmp == nil && p->which < max_used_fringe_bitmap) { gui_define_fringe_bitmap (f, p->which); - bmp = [fringe_bmp objectForKey: [NSNumber numberWithInt: p->which]]; + bmp = (CGImageRef)[fringe_bmp objectForKey: [NSNumber numberWithInt: p->which]]; } if (bmp) { - NSAffineTransform *transform = [NSAffineTransform transform]; - NSColor *bm_color; + CGRect bounds = CGRectMake (p->x, p->y - p->dh, + CGImageGetWidth (bmp), CGImageGetHeight (bmp)); + + NSGraphicsContext *ctx = [NSGraphicsContext currentContext]; + [ctx saveGraphicsState]; + CGContextRef context = [ctx CGContext]; - /* Because the image is defined at (0, 0) we need to take a copy - and then transform that copy to the new origin. */ - bmp = [bmp copy]; - [transform translateXBy:p->x yBy:p->y - p->dh]; - [bmp transformUsingAffineTransform:transform]; + CGContextTranslateCTM (context, + CGRectGetMinX (bounds), CGRectGetMaxY (bounds)); + CGContextScaleCTM (context, 1, -1); + NSColor *bm_color; if (!p->cursor_p) bm_color = [NSColor colorWithUnsignedLong:face->foreground]; else if (p->overlay_p) @@ -3009,9 +3014,10 @@ Hide the window (X11 semantics) bm_color = f->output_data.ns->cursor_color; [bm_color set]; - [bmp fill]; + bounds.origin = CGPointZero; + CGContextDrawImage (context, bounds, bmp); - [bmp release]; + [[NSGraphicsContext currentContext] restoreGraphicsState]; } ns_unfocus (f); } @@ -3273,11 +3279,10 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors. ns_draw_underwave (struct glyph_string *s, EmacsCGFloat width, EmacsCGFloat x) { int wave_height = 3, wave_length = 2; - int y, dx, dy, odd, xmax; - NSPoint a, b; + int y, dx, dy, xmax; NSRect waveClip; - dx = wave_length; + dx = wave_length * 2; dy = wave_height - 1; y = s->ybase - wave_height + 3; xmax = x + width; @@ -3287,25 +3292,24 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors. [[NSGraphicsContext currentContext] saveGraphicsState]; NSRectClip (waveClip); - /* Draw the waves */ - a.x = x - ((int)(x) % dx) + (EmacsCGFloat) 0.5; - b.x = a.x + dx; - odd = (int)(a.x/dx) % 2; - a.y = b.y = y + 0.5; + float ax = x - ((int)(x) % dx); + float ay = y + wave_height / 2.0; - if (odd) - a.y += dy; - else - b.y += dy; + NSBezierPath *path = [[NSBezierPath alloc] init]; + [path moveToPoint: (NSPoint){ ax, ay }]; + + NSPoint stepOne = { dx, 0 }; + NSPoint controlOne = { 0.5 * dx, dy }; + NSPoint controlTwo = { 0.5 * dx, -dy }; - while (a.x <= xmax) + while (ax <= xmax) { - [NSBezierPath strokeLineFromPoint:a toPoint:b]; - a.x = b.x, a.y = b.y; - b.x += dx, b.y = y + 0.5 + odd*dy; - odd = !odd; + [path relativeCurveToPoint:stepOne controlPoint1:controlOne controlPoint2:controlTwo]; + ax += dx; } + [path stroke]; + /* Restore previous clipping rectangle(s) */ [[NSGraphicsContext currentContext] restoreGraphicsState]; } @@ -3825,10 +3829,35 @@ Function modeled after x_draw_glyph_string_box (). int box_line_width = max (s->face->box_horizontal_line_width, 0); if (s->stippled_p) - { - struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (s->f); - [[dpyinfo->bitmaps[face->stipple-1].img stippleMask] set]; - goto fill; + { + [[NSColor colorWithUnsignedLong:face->background] set]; + r = NSMakeRect (s->x, s->y + box_line_width, + s->background_width, + s->height - 2 * box_line_width); + NSRectFill (r); + s->background_filled_p = 1; + + struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (s->f); + CGImageRef mask = + [dpyinfo->bitmaps[face->stipple - 1].img stippleMask]; + + CGRect bounds = CGRectMake (s->x, s->y + box_line_width, + s->background_width, + s->height - 2 * box_line_width); + NSGraphicsContext *ctx = [NSGraphicsContext currentContext]; + [ctx saveGraphicsState]; + CGContextRef context = [ctx CGContext]; + + CGContextClipToRect (context, bounds); + + CGContextScaleCTM (context, 1, -1); + [[NSColor colorWithUnsignedLong:face->foreground] set]; + + CGRect imageSize = CGRectMake (0, 0, CGImageGetWidth (mask), + CGImageGetHeight (mask)); + + CGContextDrawTiledImage (context, imageSize, mask); + [[NSGraphicsContext currentContext] restoreGraphicsState]; } else if (FONT_HEIGHT (s->font) < s->height - 2 * box_line_width /* When xdisp.c ignores FONT_HEIGHT, we cannot trust font @@ -3851,7 +3880,6 @@ Function modeled after x_draw_glyph_string_box (). else [FRAME_CURSOR_COLOR (s->f) set]; - fill: r = NSMakeRect (s->x, s->y + box_line_width, s->background_width, s->height - 2 * box_line_width); @@ -4175,12 +4203,42 @@ Function modeled after x_draw_glyph_string_box (). dpyinfo = FRAME_DISPLAY_INFO (s->f); if (s->hl == DRAW_CURSOR) [FRAME_CURSOR_COLOR (s->f) set]; - else if (s->stippled_p) - [[dpyinfo->bitmaps[s->face->stipple - 1].img stippleMask] set]; - else + else if (s->stippled_p) { + [[NSColor colorWithUnsignedLong:s->face->background] + set]; + NSRectFill ( + NSMakeRect (x, s->y, background_width, s->height)); + + CGImageRef mask = + [dpyinfo->bitmaps[s->face->stipple - 1] + .img stippleMask]; + + CGRect bounds + = CGRectMake (s->x, s->y, s->background_width, + s->height); + + NSGraphicsContext *ctx = + [NSGraphicsContext currentContext]; + [ctx saveGraphicsState]; + CGContextRef context = [ctx CGContext]; + CGContextClipToRect(context, bounds); + CGContextScaleCTM (context, 1, -1); + [[NSColor colorWithUnsignedLong:s->face->foreground] + set]; + + CGRect imageSize + = CGRectMake (0, 0, CGImageGetWidth (mask), + CGImageGetHeight (mask)); + + CGContextDrawTiledImage (context, imageSize, mask); + + [[NSGraphicsContext currentContext] + restoreGraphicsState]; + } + else { [[NSColor colorWithUnsignedLong: s->face->background] set]; - - NSRectFill (NSMakeRect (x, s->y, background_width, s->height)); + NSRectFill (NSMakeRect (x, s->y, background_width, s->height)); + } } }
bug-gnu-emacs <at> gnu.org
:bug#73563
; Package emacs
.
(Sat, 17 May 2025 09:27:01 GMT) Full text and rfc822 format available.Message #14 received at 73563 <at> debbugs.gnu.org (full text, mbox):
From: Eli Zaretskii <eliz <at> gnu.org> To: Jordan Ellis Coppard <jc+o.emacs <at> wz.ht>, Alan Third <alan <at> idiocy.org>, Gerd Möllmann <gerd.moellmann <at> gmail.com> Cc: ben <at> bensimms.moe, 73563 <at> debbugs.gnu.org Subject: Re: bug#73563: [Ben Simms] Performance bottleneck in ns_draw_fringe_bitmap Date: Sat, 17 May 2025 12:26:26 +0300
> Cc: Ben Simms <ben <at> bensimms.moe> > Date: Tue, 13 May 2025 20:37:55 +0900 > From: Jordan Ellis Coppard via "Bug reports for GNU Emacs, > the Swiss army knife of text editors" <bug-gnu-emacs <at> gnu.org> > > Howdy, > > > I've been using the following patch (bottom of this email) until my most > recent rebuild of Emacs from master. Since the recent commit fixing > stipple drawing the patch can be reduced to just: > https://github.com/emacs-mirror/emacs/commit/7f2efe6503fdce2a4e552c14802644a05b581bc7 > (link to a diff authored by Ben Simms). > > I've now noticed again extreme slowdown when fringe bitmaps are used. It > appears to be proportional to the number of bits set in the bitmap. An > 'empty' bitmap (all zeroes) doesn't yield any noticeable slowdown, a > bitmap with 1 bit set yields a little, with 2 its noticeable and with > all 8 bits set Emacs takes up to 1 second to respond to any input at all. > > This has been the case since at least August of last year. I can get a > minimal reproduction but it has happened on completely different > configurations with the only common denominator being the NS build (on > ARM-based macOS), and the use of fringe bitmaps. > > Below is trace with update_frame as the root context. Captured with > Instruments.app (which uses dtrace and more under the hood IIRC). > > ns_draw_fringe_bitmap takes up 94% of the time for the short > reproduction this trace records. The deepest the trace goes is to > CG::Path::recalculate_bounding_box() which eats 80% of time. So instead > of a cached value the bounding box is being recalculated every time > which appears very expensive, but beyond that I have close to zero > experience with macOS' graphics stack. > > I've just recompiled Emacs against the same commit of my recent master > build (648453c04d9b91d96452b930c0c948b0b39b5dc0) except now with the > patch applied (since the stipple changes are merged it's just the > smallest subset: > https://github.com/emacs-mirror/emacs/commit/7f2efe6503fdce2a4e552c14802644a05b581bc7) > and once again fringe performance is back to being buttery smooth. Alan and Gerd, any comments or suggestions? > (trace also here in-case formatting gets borked: > trace: > > 3.07 s 100.0% 0 s update_frame > 3.06 s 100.0% 0 s update_window_tree > 3.06 s 100.0% 1.00 ms update_window > 2.89 s 94.2% 0 s gui_update_window_end > 2.88 s 94.1% 0 s draw_window_fringes > 2.88 s 93.9% 0 s draw_fringe_bitmap > 2.88 s 93.9% 0 s draw_fringe_bitmap_1 > 2.88 s 93.9% 0 s ns_draw_fringe_bitmap > 2.72 s 88.7% 0 s -[NSBezierPath copyWithZone:] > 2.72 s 88.7% 0 s -[NSBezierPath _appendToPath:] > 2.72 s 88.7% 0 s -[NSBezierPath > _enumeratePathElementsUsingBlock:] > 2.72 s 88.7% 0 s CGPathApplyWithBlock2 > 2.72 s 88.7% 5.00 ms CG::Path::apply(void > (CGPathElementType, CGPoint const*, bool*) block_pointer) const > 2.71 s 88.5% 30.00 ms > __CGPathApplyWithBlock2_block_invoke > 2.68 s 87.4% 11.00 ms __49-[NSBezierPath > _enumeratePathElementsUsingBlock:]_block_invoke > 2.48 s 80.8% 1.00 ms > -[NSBezierPath(NSBezierPathDevicePrimitives) _deviceMoveToPoint:] > 2.47 s 80.6% 2.00 ms CGPathMoveToPoint > 2.46 s 80.3% 2.46 s > CG::Path::recalculate_bounding_box() > 5.00 ms 0.2% 4.00 ms > CG::Path::move_to_point(CGPoint const&, CGAffineTransform const*) > 1.00 ms 0.0% 1.00 ms > CG::Path::convert_to_huge() > 2.00 ms 0.1% 2.00 ms (anonymous > namespace)::transform_is_valid(CGAffineTransform const*) > 1.00 ms 0.0% 1.00 ms > CG::Path::convert_to_huge() > 1.00 ms 0.0% 1.00 ms > CGFloatValidateWithLog > 1.00 ms 0.0% 1.00 ms > DYLD-STUB$$CGPathMoveToPoint > 1.00 ms 0.0% 1.00 ms -[NSBezierPath > _cgPath] > 1.00 ms 0.0% 1.00 ms objc_msgSend > 1.00 ms 0.0% 1.00 ms (anonymous > namespace)::transform_is_valid(CGAffineTransform const*) > 67.00 ms 2.2% 7.00 ms > -[NSBezierPath(NSBezierPathDevicePrimitives) _deviceLineToPoint:] > 40.00 ms 1.3% 5.00 ms > -[NSBezierPath(NSBezierPathDevicePrimitives) _deviceClosePath] > 39.00 ms 1.3% 2.00 ms -[NSBezierPath > lineToPoint:] > 14.00 ms 0.5% 14.00 ms objc_msgSend > 6.00 ms 0.2% 6.00 ms -[NSBezierPath _cgPath] > 5.00 ms 0.2% 5.00 ms CGPathAddLineToPoint > 4.00 ms 0.1% 4.00 ms __30-[NSBezierPath > _appendToPath:]_block_invoke > 4.00 ms 0.1% 4.00 ms > objc_msgSend$lineToPoint: > 2.00 ms 0.1% 2.00 ms CGPathIsEmpty > 2.00 ms 0.1% 2.00 ms > objc_msgSend$moveToPoint: > 2.00 ms 0.1% 2.00 ms > CG::Path::close_subpath() > 1.00 ms 0.0% 1.00 ms > objc_msgSend$_deviceLineToPoint: > 1.00 ms 0.0% 1.00 ms CGPathGetCurrentPoint > 1.00 ms 0.0% 1.00 ms -[NSBezierPath > moveToPoint:] > 1.00 ms 0.0% 1.00 ms > objc_msgSend$_deviceClosePath > 1.00 ms 0.0% 1.00 ms objc_msgSend$closePath > 1.00 ms 0.0% 1.00 ms > objc_msgSend$_deviceMoveToPoint: > 3.00 ms 0.1% 3.00 ms -[NSBezierPath > lineToPoint:] > 1.00 ms 0.0% 1.00 ms > -[NSBezierPath(NSBezierPathDevicePrimitives) _deviceLineToPoint:] > 2.00 ms 0.1% 2.00 ms __49-[NSBezierPath > _enumeratePathElementsUsingBlock:]_block_invoke > 1.00 ms 0.0% 1.00 ms objc_msgSend$setFlatness: > 104.00 ms 3.4% 0 s -[NSBezierPath fill] > 42.00 ms 1.4% 0 s -[NSBezierPath > transformUsingAffineTransform:] > 7.00 ms 0.2% 0 s NSRectFill > 2.00 ms 0.1% 0 s NSColorSetWithFillAndStroke > 1.00 ms 0.0% 1.00 ms CGContextClipToRect > 1.00 ms 0.0% 0 s -[NSBezierPath dealloc] > 1.00 ms 0.0% 1.00 ms CGGStateSetCompositeOperation > 1.00 ms 0.0% 1.00 ms lookup_named_face > 5.00 ms 0.2% 4.00 ms set_buffer_internal_2 > 1.00 ms 0.0% 0 s unblock_input > 1.00 ms 0.0% 0 s ns_draw_window_cursor > 121.00 ms 3.9% 0 s ns_scroll_run > 51.00 ms 1.7% 1.00 ms update_window_line > 4.00 ms 0.1% 4.00 ms row_equal_p > 1.00 ms 0.0% 1.00 ms xwidget_end_redisplay > 1.00 ms 0.0% 0 s ns_update_end > > > > Patch: > > --- src/nsimage.m.orig > +++ src/nsimage.m > @@ -28,6 +28,7 @@ Updated by Christian Limpach (chris <at> nice.ch) > /* This should be the first include, as it may set up #defines affecting > interpretation of even the system includes. */ > #include <config.h> > +#include <CoreGraphics/CoreGraphics.h> > > #include "lisp.h" > #include "dispextern.h" > @@ -510,10 +511,20 @@ - (void) setAlphaAtX: (int) x Y: (int) y to: > (unsigned char) a > } > > /* Returns a pattern color, which is cached here. */ > -- (NSColor *)stippleMask > +- (CGImageRef)stippleMask > { > - if (stippleMask == nil) > - stippleMask = [[NSColor colorWithPatternImage: self] retain]; > + if (stippleMask == nil) { > + CGDataProviderRef provider = CGDataProviderCreateWithData (NULL, > [bmRep bitmapData], > + [self > sizeInBytes], NULL); > + id mask = (id)CGImageMaskCreate( > + [self size].width, > + [self size].height, > + 8, 8, [self size].width, > + provider, NULL, 0); > + > + CGDataProviderRelease(provider); > + stippleMask = (CGImageRef)[mask retain]; > + } > return stippleMask; > } > > --- src/nsterm.h.orig > +++ src/nsterm.h > @@ -670,7 +670,7 @@ enum ns_return_frame_mode > { > NSBitmapImageRep *bmRep; /* used for accessing pixel data */ > unsigned char *pixmapData[5]; /* shortcut to access pixel data */ > - NSColor *stippleMask; > + CGImageRef stippleMask; > @public > NSAffineTransform *transform; > BOOL smoothing; > @@ -687,8 +687,8 @@ enum ns_return_frame_mode > green: (unsigned char)g blue: (unsigned char)b > alpha:(unsigned char)a; > - (void)setAlphaAtX: (int)x Y: (int)y to: (unsigned char)a; > -- (NSColor *)stippleMask; > +- (CGImageRef)stippleMask; > - (Lisp_Object)getMetadata; > - (BOOL)setFrame: (unsigned int) index; > - (void)setTransform: (double[3][3]) m; > > --- src/nsterm.m.orig > +++ src/nsterm.m > @@ -2903,22 +2903,24 @@ Hide the window (X11 semantics) > static void > ns_define_fringe_bitmap (int which, unsigned short *bits, int h, int w) > { > - NSBezierPath *p = [NSBezierPath bezierPath]; > - > if (!fringe_bmp) > fringe_bmp = [[NSMutableDictionary alloc] initWithCapacity:25]; > > - [p moveToPoint:NSMakePoint (0, 0)]; > > - for (int y = 0 ; y < h ; y++) > - for (int x = 0 ; x < w ; x++) > - { > - bool bit = bits[y] & (1 << (w - x - 1)); > - if (bit) > - [p appendBezierPathWithRect:NSMakeRect (x, y, 1, 1)]; > - } > + for (int i = 0; i < h; i++) > + bits[i] = ~bits[i]; > + > + CGDataProviderRef provider = CGDataProviderCreateWithData (NULL, bits, > + sizeof (unsigned short) * h, NULL); > + if (provider) { > + id p = (id)CGImageMaskCreate (w, h, 1, 1, > + sizeof (unsigned short), > + provider, NULL, 0); > + CGDataProviderRelease (provider); > + > + [fringe_bmp setObject:p forKey:[NSNumber numberWithInt:which]]; > + } > > - [fringe_bmp setObject:p forKey:[NSNumber numberWithInt:which]]; > } > > > @@ -2981,26 +2983,29 @@ Hide the window (X11 semantics) > NSRectFill (clearRect); > } > > - NSBezierPath *bmp = [fringe_bmp objectForKey:[NSNumber > numberWithInt:p->which]]; > + CGImageRef bmp = (CGImageRef)[fringe_bmp objectForKey:[NSNumber > numberWithInt:p->which]]; > > if (bmp == nil > && p->which < max_used_fringe_bitmap) > { > gui_define_fringe_bitmap (f, p->which); > - bmp = [fringe_bmp objectForKey: [NSNumber numberWithInt: p->which]]; > + bmp = (CGImageRef)[fringe_bmp objectForKey: [NSNumber > numberWithInt: p->which]]; > } > > if (bmp) > { > - NSAffineTransform *transform = [NSAffineTransform transform]; > - NSColor *bm_color; > + CGRect bounds = CGRectMake (p->x, p->y - p->dh, > + CGImageGetWidth (bmp), CGImageGetHeight (bmp)); > + > + NSGraphicsContext *ctx = [NSGraphicsContext currentContext]; > + [ctx saveGraphicsState]; > + CGContextRef context = [ctx CGContext]; > > - /* Because the image is defined at (0, 0) we need to take a copy > - and then transform that copy to the new origin. */ > - bmp = [bmp copy]; > - [transform translateXBy:p->x yBy:p->y - p->dh]; > - [bmp transformUsingAffineTransform:transform]; > + CGContextTranslateCTM (context, > + CGRectGetMinX (bounds), CGRectGetMaxY (bounds)); > + CGContextScaleCTM (context, 1, -1); > > + NSColor *bm_color; > if (!p->cursor_p) > bm_color = [NSColor colorWithUnsignedLong:face->foreground]; > else if (p->overlay_p) > @@ -3009,9 +3014,10 @@ Hide the window (X11 semantics) > bm_color = f->output_data.ns->cursor_color; > > [bm_color set]; > - [bmp fill]; > + bounds.origin = CGPointZero; > + CGContextDrawImage (context, bounds, bmp); > > - [bmp release]; > + [[NSGraphicsContext currentContext] restoreGraphicsState]; > } > ns_unfocus (f); > } > @@ -3273,11 +3279,10 @@ Note that CURSOR_WIDTH is meaningful only for > (h)bar cursors. > ns_draw_underwave (struct glyph_string *s, EmacsCGFloat width, > EmacsCGFloat x) > { > int wave_height = 3, wave_length = 2; > - int y, dx, dy, odd, xmax; > - NSPoint a, b; > + int y, dx, dy, xmax; > NSRect waveClip; > > - dx = wave_length; > + dx = wave_length * 2; > dy = wave_height - 1; > y = s->ybase - wave_height + 3; > xmax = x + width; > @@ -3287,25 +3292,24 @@ Note that CURSOR_WIDTH is meaningful only for > (h)bar cursors. > [[NSGraphicsContext currentContext] saveGraphicsState]; > NSRectClip (waveClip); > > - /* Draw the waves */ > - a.x = x - ((int)(x) % dx) + (EmacsCGFloat) 0.5; > - b.x = a.x + dx; > - odd = (int)(a.x/dx) % 2; > - a.y = b.y = y + 0.5; > + float ax = x - ((int)(x) % dx); > + float ay = y + wave_height / 2.0; > > - if (odd) > - a.y += dy; > - else > - b.y += dy; > + NSBezierPath *path = [[NSBezierPath alloc] init]; > + [path moveToPoint: (NSPoint){ ax, ay }]; > + > + NSPoint stepOne = { dx, 0 }; > + NSPoint controlOne = { 0.5 * dx, dy }; > + NSPoint controlTwo = { 0.5 * dx, -dy }; > > - while (a.x <= xmax) > + while (ax <= xmax) > { > - [NSBezierPath strokeLineFromPoint:a toPoint:b]; > - a.x = b.x, a.y = b.y; > - b.x += dx, b.y = y + 0.5 + odd*dy; > - odd = !odd; > + [path relativeCurveToPoint:stepOne controlPoint1:controlOne > controlPoint2:controlTwo]; > + ax += dx; > } > > + [path stroke]; > + > /* Restore previous clipping rectangle(s) */ > [[NSGraphicsContext currentContext] restoreGraphicsState]; > } > @@ -3825,10 +3829,35 @@ Function modeled after x_draw_glyph_string_box (). > int box_line_width = max (s->face->box_horizontal_line_width, 0); > > if (s->stippled_p) > - { > - struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (s->f); > - [[dpyinfo->bitmaps[face->stipple-1].img stippleMask] set]; > - goto fill; > + { > + [[NSColor colorWithUnsignedLong:face->background] set]; > + r = NSMakeRect (s->x, s->y + box_line_width, > + s->background_width, > + s->height - 2 * box_line_width); > + NSRectFill (r); > + s->background_filled_p = 1; > + > + struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (s->f); > + CGImageRef mask = > + [dpyinfo->bitmaps[face->stipple - 1].img stippleMask]; > + > + CGRect bounds = CGRectMake (s->x, s->y + box_line_width, > + s->background_width, > + s->height - 2 * box_line_width); > + NSGraphicsContext *ctx = [NSGraphicsContext currentContext]; > + [ctx saveGraphicsState]; > + CGContextRef context = [ctx CGContext]; > + > + CGContextClipToRect (context, bounds); > + > + CGContextScaleCTM (context, 1, -1); > + [[NSColor colorWithUnsignedLong:face->foreground] set]; > + > + CGRect imageSize = CGRectMake (0, 0, CGImageGetWidth (mask), > + CGImageGetHeight (mask)); > + > + CGContextDrawTiledImage (context, imageSize, mask); > + [[NSGraphicsContext currentContext] restoreGraphicsState]; > } > else if (FONT_HEIGHT (s->font) < s->height - 2 * box_line_width > /* When xdisp.c ignores FONT_HEIGHT, we cannot trust font > @@ -3851,7 +3880,6 @@ Function modeled after x_draw_glyph_string_box (). > else > [FRAME_CURSOR_COLOR (s->f) set]; > > - fill: > r = NSMakeRect (s->x, s->y + box_line_width, > s->background_width, > s->height - 2 * box_line_width); > @@ -4175,12 +4203,42 @@ Function modeled after x_draw_glyph_string_box (). > dpyinfo = FRAME_DISPLAY_INFO (s->f); > if (s->hl == DRAW_CURSOR) > [FRAME_CURSOR_COLOR (s->f) set]; > - else if (s->stippled_p) > - [[dpyinfo->bitmaps[s->face->stipple - 1].img stippleMask] set]; > - else > + else if (s->stippled_p) { > + [[NSColor colorWithUnsignedLong:s->face->background] > + set]; > + NSRectFill ( > + NSMakeRect (x, s->y, background_width, s->height)); > + > + CGImageRef mask = > + [dpyinfo->bitmaps[s->face->stipple - 1] > + .img stippleMask]; > + > + CGRect bounds > + = CGRectMake (s->x, s->y, s->background_width, > + s->height); > + > + NSGraphicsContext *ctx = > + [NSGraphicsContext currentContext]; > + [ctx saveGraphicsState]; > + CGContextRef context = [ctx CGContext]; > + CGContextClipToRect(context, bounds); > + CGContextScaleCTM (context, 1, -1); > + [[NSColor colorWithUnsignedLong:s->face->foreground] > + set]; > + > + CGRect imageSize > + = CGRectMake (0, 0, CGImageGetWidth (mask), > + CGImageGetHeight (mask)); > + > + CGContextDrawTiledImage (context, imageSize, mask); > + > + [[NSGraphicsContext currentContext] > + restoreGraphicsState]; > + } > + else { > [[NSColor colorWithUnsignedLong: s->face->background] set]; > - > - NSRectFill (NSMakeRect (x, s->y, background_width, s->height)); > + NSRectFill (NSMakeRect (x, s->y, background_width, s->height)); > + } > } > } > > > > >
bug-gnu-emacs <at> gnu.org
:bug#73563
; Package emacs
.
(Sat, 17 May 2025 09:46:02 GMT) Full text and rfc822 format available.Message #17 received at 73563 <at> debbugs.gnu.org (full text, mbox):
From: Gerd Möllmann <gerd.moellmann <at> gmail.com> To: Eli Zaretskii <eliz <at> gnu.org> Cc: ben <at> bensimms.moe, Alan Third <alan <at> idiocy.org>, 73563 <at> debbugs.gnu.org, Jordan Ellis Coppard <jc+o.emacs <at> wz.ht> Subject: Re: bug#73563: [Ben Simms] Performance bottleneck in ns_draw_fringe_bitmap Date: Sat, 17 May 2025 11:45:33 +0200
Eli Zaretskii <eliz <at> gnu.org> writes: > Alan and Gerd, any comments or suggestions? That goes beyond my macOS graphics knowledge I'm afraid.
bug-gnu-emacs <at> gnu.org
:bug#73563
; Package emacs
.
(Sat, 17 May 2025 11:25:01 GMT) Full text and rfc822 format available.Message #20 received at 73563 <at> debbugs.gnu.org (full text, mbox):
From: Alan Third <alan <at> idiocy.org> To: Eli Zaretskii <eliz <at> gnu.org> Cc: Gerd Möllmann <gerd.moellmann <at> gmail.com>, ben <at> bensimms.moe, 73563 <at> debbugs.gnu.org, Jordan Ellis Coppard <jc+o.emacs <at> wz.ht> Subject: Re: bug#73563: [Ben Simms] Performance bottleneck in ns_draw_fringe_bitmap Date: Sat, 17 May 2025 12:23:53 +0100
On Sat, May 17, 2025 at 12:26:26PM +0300, Eli Zaretskii wrote: > > Cc: Ben Simms <ben <at> bensimms.moe> > > Date: Tue, 13 May 2025 20:37:55 +0900 > > From: Jordan Ellis Coppard via "Bug reports for GNU Emacs, > > the Swiss army knife of text editors" <bug-gnu-emacs <at> gnu.org> > > > > Howdy, > > > > > > I've been using the following patch (bottom of this email) until my most > > recent rebuild of Emacs from master. Since the recent commit fixing > > stipple drawing the patch can be reduced to just: > > https://github.com/emacs-mirror/emacs/commit/7f2efe6503fdce2a4e552c14802644a05b581bc7 > > (link to a diff authored by Ben Simms). > > > > I've now noticed again extreme slowdown when fringe bitmaps are used. It > > appears to be proportional to the number of bits set in the bitmap. An > > 'empty' bitmap (all zeroes) doesn't yield any noticeable slowdown, a > > bitmap with 1 bit set yields a little, with 2 its noticeable and with > > all 8 bits set Emacs takes up to 1 second to respond to any input at all. > > > > This has been the case since at least August of last year. I can get a > > minimal reproduction but it has happened on completely different > > configurations with the only common denominator being the NS build (on > > ARM-based macOS), and the use of fringe bitmaps. > > > > Below is trace with update_frame as the root context. Captured with > > Instruments.app (which uses dtrace and more under the hood IIRC). > > > > ns_draw_fringe_bitmap takes up 94% of the time for the short > > reproduction this trace records. The deepest the trace goes is to > > CG::Path::recalculate_bounding_box() which eats 80% of time. So instead > > of a cached value the bounding box is being recalculated every time > > which appears very expensive, but beyond that I have close to zero > > experience with macOS' graphics stack. > > > > I've just recompiled Emacs against the same commit of my recent master > > build (648453c04d9b91d96452b930c0c948b0b39b5dc0) except now with the > > patch applied (since the stipple changes are merged it's just the > > smallest subset: > > https://github.com/emacs-mirror/emacs/commit/7f2efe6503fdce2a4e552c14802644a05b581bc7) > > and once again fringe performance is back to being buttery smooth. > > Alan and Gerd, any comments or suggestions? I've not looked at it closely, but I will point out that AFAIK GNUStep does not support Core Graphics, so any function or struct starting with "CG" needs an alternative method in place. -- Alan Third
bug-gnu-emacs <at> gnu.org
:bug#73563
; Package emacs
.
(Sat, 24 May 2025 16:12:02 GMT) Full text and rfc822 format available.Message #23 received at 73563 <at> debbugs.gnu.org (full text, mbox):
From: Alan Third <alan <at> idiocy.org> To: Jordan Ellis Coppard <jc+o.emacs <at> wz.ht> Cc: Ben Simms <ben <at> bensimms.moe>, 73563 <at> debbugs.gnu.org Subject: Re: bug#73563: [Ben Simms] Performance bottleneck in ns_draw_fringe_bitmap Date: Sat, 24 May 2025 17:10:55 +0100
[Message part 1 (text/plain, inline)]
On Tue, May 13, 2025 at 08:37:55PM +0900, Jordan Ellis Coppard wrote: > Howdy, > > > I've been using the following patch (bottom of this email) until my most > recent rebuild of Emacs from master. Since the recent commit fixing stipple > drawing the patch can be reduced to just: https://github.com/emacs-mirror/emacs/commit/7f2efe6503fdce2a4e552c14802644a05b581bc7 > (link to a diff authored by Ben Simms). > > I've now noticed again extreme slowdown when fringe bitmaps are used. It > appears to be proportional to the number of bits set in the bitmap. An > 'empty' bitmap (all zeroes) doesn't yield any noticeable slowdown, a bitmap > with 1 bit set yields a little, with 2 its noticeable and with all 8 bits > set Emacs takes up to 1 second to respond to any input at all. > > This has been the case since at least August of last year. I can get a > minimal reproduction but it has happened on completely different > configurations with the only common denominator being the NS build (on > ARM-based macOS), and the use of fringe bitmaps. I have no idea what's going on, but it looks from the trace like the problem is in the transform to move it to the final position, or the copy... Which are both odd as to my mind they should be fast processes. Since we can't really use Core Graphics code in the NS port, I've tried improving the bitmap tracing a little. I have my doubts it will improve things much, but can you please try it and see how it compares? I tried it here and the difference between all three versions of the code is within 0.2 of a second, and totally inconsistent. I couldn't vouch for any of them being consistently faster. -- Alan Third
[0001-Simplified-NS-fringe-vectors.patch (text/x-diff, attachment)]
bug-gnu-emacs <at> gnu.org
:bug#73563
; Package emacs
.
(Tue, 03 Jun 2025 08:27:03 GMT) Full text and rfc822 format available.Message #26 received at 73563 <at> debbugs.gnu.org (full text, mbox):
From: Jordan Ellis Coppard <jc+o.emacs <at> wz.ht> To: Alan Third <alan <at> idiocy.org>, 73563 <at> debbugs.gnu.org, Ben Simms <ben <at> bensimms.moe> Subject: Re: bug#73563: [Ben Simms] Performance bottleneck in ns_draw_fringe_bitmap Date: Tue, 3 Jun 2025 17:25:40 +0900
On 25/5/2025 1:10 am, Alan Third wrote: > Since we can't really use Core Graphics code in the NS port, I've > tried improving the bitmap tracing a little. I have my doubts it will > improve things much, but can you please try it and see how it > compares? I tried it here and the difference between all three > versions of the code is within 0.2 of a second, and totally > inconsistent. I couldn't vouch for any of them being consistently > faster. Your patch appears to have fixed it. I can now add 8-bit fringe bitmaps and scroll a buffer with no noticeable slowdown. Unpatched Emacs will slow down to a crawl, probably only redisplaying 1-3 times a second, Ben Simms' patch looks equally as performant as this one however Ben's seems to break with dape-mode which arranges the Emacs frame into a bunch of windows etc. Yours, Alan, seems to not bork Emacs display under dape-mode but there are tiny 1-pixel artifacts in the margin (only in dape-mode). Dape-mode seems to be using Emacs' features that break when combined with fringes as if I turn fringes off Emacs displays normally thereafter. I tried writing a benchmark for this (here: https://gist.github.com/tsujp/79d743ca23555c679f5a9cb667ddc3be) but even on the version of Emacs which was incredibly laggy the results all came out within ~0.2 seconds of each other. It appeared to lag the most when the margin was approaching the end of, or start of, the top/bottom of a buffer and while it still lagged a lot otherwise it was a tiny bit less so (it felt like). I can provide screenshots and/or screen recordings of the lag in all 3 variants (vanilla Emacs, Ben's patch, and your patch Alan) since this seems like quite a nasty thing to demonstrate. /Jordan
bug-gnu-emacs <at> gnu.org
:bug#73563
; Package emacs
.
(Tue, 03 Jun 2025 21:08:01 GMT) Full text and rfc822 format available.Message #29 received at 73563 <at> debbugs.gnu.org (full text, mbox):
From: Alan Third <alan <at> idiocy.org> To: Jordan Ellis Coppard <jc+o.emacs <at> wz.ht> Cc: Ben Simms <ben <at> bensimms.moe>, 73563 <at> debbugs.gnu.org Subject: Re: bug#73563: [Ben Simms] Performance bottleneck in ns_draw_fringe_bitmap Date: Tue, 3 Jun 2025 22:07:01 +0100
On Tue, Jun 03, 2025 at 05:25:40PM +0900, Jordan Ellis Coppard wrote: > On 25/5/2025 1:10 am, Alan Third wrote: > > Since we can't really use Core Graphics code in the NS port, I've > > tried improving the bitmap tracing a little. I have my doubts it will > > improve things much, but can you please try it and see how it > > compares? I tried it here and the difference between all three > > versions of the code is within 0.2 of a second, and totally > > inconsistent. I couldn't vouch for any of them being consistently > > faster. > > Your patch appears to have fixed it. I can now add 8-bit fringe bitmaps and > scroll a buffer with no noticeable slowdown. Thanks for testing! > Unpatched Emacs will slow down to a crawl, probably only > redisplaying 1-3 times a second, Ben Simms' patch looks equally as > performant as this one however Ben's seems to break with dape-mode > which arranges the Emacs frame into a bunch of windows etc. Hmm, I'm surprised to hear that as I didn't think Ben's patch would have touched anything that should break that. > Yours, Alan, seems to not bork Emacs display under dape-mode but > there are tiny 1-pixel artifacts in the margin (only in dape-mode). Can you send a screenshot? That sounds odd. -- Alan Third
bug-gnu-emacs <at> gnu.org
:bug#73563
; Package emacs
.
(Fri, 06 Jun 2025 10:03:02 GMT) Full text and rfc822 format available.Message #32 received at 73563 <at> debbugs.gnu.org (full text, mbox):
From: Jordan Ellis Coppard <jc+o.emacs <at> wz.ht> To: Alan Third <alan <at> idiocy.org>, 73563 <at> debbugs.gnu.org, Ben Simms <ben <at> bensimms.moe> Subject: Re: bug#73563: [Ben Simms] Performance bottleneck in ns_draw_fringe_bitmap Date: Fri, 6 Jun 2025 19:01:45 +0900
On 4/6/2025 6:07 am, Alan Third wrote: > Can you send a screenshot? That sounds odd. I forgot to check that on emacs -Q and it appears to not be present there. In my config I change the size of the fringes a bit, however that might mean your patch doesn't account for that somehow. I'm still bisecting. I've decided to record all 3 Emacs variants in-play here: (1) Vanilla, unpatched Emacs. (2) Emacs patched with Ben Simms' fringe changes. (3) Emacs patched with yours (Alan Third's) fringe changes. I've uploaded the three recordings to YouTube. All are emacs -Q. Note in 1's case the extreme lag when scrolling the *test fringe bench* buffer and that in 2 and 3's case there is no such lag. In all cases I start scrolling by holding down the arrow keys up/down (for simplicity, in actuality I am using C-n or a key set to run (next-line) and so forth) and then I scroll a bit with the trackpad. I _think_ Ben's might be a bit more performant but at this point it feels like splitting hairs especially since there doesn't appear (yet) to be a concrete way to benchmark this without human interaction which is concerning (I think you and others would agree a way to get concrete data here would be better for long term regression testing). Videos in respective order: (1) https://www.youtube.com/watch?v=G8T0S1m-mLs (2) https://www.youtube.com/watch?v=-9Ro3myXztM (3) https://www.youtube.com/watch?v=CYwAfPBt8us Here are four screenshots in the same respective order as the videos in terms of Emacs version. Unpatched Emacs with dape-mode shows no corruption, Ben's patch shows the frame and all it's windows borked (when it should look like 1, 3, or 4), your patch shows dape-mode without corruption, and the fourth screenshot which is Emacs (3) but with my dotfiles loaded shows the small 1-pixel-ish corruption in the margin (look at the fringe for lines 1-15). That corruption only appears when I scroll the buffer down and up. Whereas Ben's corruption appears immediately and indefinitely unless fringe-mode is toggled off. Screenshots (direct links): (1) https://i.imgur.com/BpXQxcj.png (2) https://i.imgur.com/Nd3YPda.png (3) https://i.imgur.com/gjPmMc2.png (4) https://i.imgur.com/X2ybi2Q.png Album link: https://imgur.com/a/0oReqr2 The window configurations in the Emacs frame (except for the fourth) are all exactly the same, screenshots taken at the same point in time: M-x dape M-x dape-breakpoint-toggle and then 'r' in the dape console to start debugging. I forgot to disable macOS' shadow-adding behaviour when capturing the screenshot. Also there's a tangential thing I noticed. My benchmark takes ~18 seconds to run under emacs -Q but (for all 3 Emacsen) only ~2.7 seconds when using my config (https://github.com/tsujp/dotfiles/tree/master/.config/emacs). As you can see from the fourth screenshot I disable native scroll bars, toolbar mode and various other things. Such a huge increase in performance there is.. perhaps concerning too. /Jordan
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.