From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.ciao.gmane.io!not-for-mail From: Alan Third Newsgroups: gmane.emacs.devel Subject: Re: Emacs NS 'scratch/ns/draw-to-bitmap' branch performance Date: Wed, 5 Feb 2020 23:10:06 +0000 Message-ID: <20200205231006.GA2736@breton.holly.idiocy.org> References: Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="KsGdsel6WgEHnImy" Content-Transfer-Encoding: 8bit Injection-Info: ciao.gmane.io; posting-host="ciao.gmane.io:159.69.161.202"; logging-data="68830"; mail-complaints-to="usenet@ciao.gmane.io" Cc: emacs-devel To: Daniel Pittman Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Thu Feb 06 00:10:58 2020 Return-path: Envelope-to: ged-emacs-devel@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1izTov-000Hkq-Iy for ged-emacs-devel@m.gmane-mx.org; Thu, 06 Feb 2020 00:10:57 +0100 Original-Received: from localhost ([::1]:58266 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1izTou-0005KV-Kc for ged-emacs-devel@m.gmane-mx.org; Wed, 05 Feb 2020 18:10:56 -0500 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:42182) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1izToE-0004lC-Bk for emacs-devel@gnu.org; Wed, 05 Feb 2020 18:10:17 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1izToC-0003P1-3y for emacs-devel@gnu.org; Wed, 05 Feb 2020 18:10:14 -0500 Original-Received: from mail-wm1-x32c.google.com ([2a00:1450:4864:20::32c]:52128) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1izToB-0003EJ-NU for emacs-devel@gnu.org; Wed, 05 Feb 2020 18:10:12 -0500 Original-Received: by mail-wm1-x32c.google.com with SMTP id t23so4293742wmi.1 for ; Wed, 05 Feb 2020 15:10:11 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlemail.com; s=20161025; h=sender:date:from:to:cc:subject:message-id:references:mime-version :content-disposition:content-transfer-encoding:in-reply-to; bh=vERX/0jX/dMxnO5jCfgcRCcYyi3MiuB5mrpl0GYwGBQ=; b=cH3DPjWRzFmom0IBMjHXvmbVt+tG5cf6rkUD49RFzE+A7FOVHpLzpF++pFFnk1l5+j Eq2B4j3jVhnjA1ugM+uWBJUMJ7RrtbEw5xMKWjcYI0tBEcfFrWV+XOey+1j8dfkaILz+ L0cYWa7JDJ4NczgxpX6r/ql80e4ddNxCoh/D5DIegZ5j1h+Y3UjDSEENlnw4wsrP84VE /mKEOGdnw71WOdbX0nUVwHxacxAXYhjQNc0Ji7moMWMI8oy9wfLdXY5RWbta/0LRhy60 PiAB7mzUgf5oGkYsIf6t2Os6oG5G/3/KQeMMekxD68tYiksMVDmRuvvw39CAGIWIGDRE p7/w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:from:to:cc:subject:message-id :references:mime-version:content-disposition :content-transfer-encoding:in-reply-to; bh=vERX/0jX/dMxnO5jCfgcRCcYyi3MiuB5mrpl0GYwGBQ=; b=i06pFUnjJkyJmb5GZ7ablbY0bya6iDj8qfWhyyRSu9U+IfGqq+74cpJAWxMZvEGLbH m6F5ekjYXp7g51CMAVmFycOdnvyap0UJqkkvejaQFH3sySu3rdExuKk34yqDwbHbSrC3 MmFTqb/kWfQaX2RKB7cSbRtsnvGRFW3O9msxcIaikBaPy2KCUyYeEGYt4B6ymdM72hOj g0f/uPvqI1W8+A1W6pwm2BizWzqkJGgFJMHMM+5j19l+Y8TUUhpicMHtaw82IlbaX82h ukHqOqpe3nkj3KTos1eljPUnFy3dmPojV2rKj+xvnBn4gcosnfb7KpSIncFfXcSGARdQ 1tCw== X-Gm-Message-State: APjAAAWvIm8oHKWYMVWEAk0t5+RTU8dQieoXLgUF0XYu+0YczaU4qUMw aKLgpQCAfG5Zz/j0udcz/f0= X-Google-Smtp-Source: APXvYqxZIxP9Fx+Opj3YMJADoKQEwCwEgISasN1zJs303ORpTVE8RX2j2U3fOkrzvf3xzJXdZYwxoA== X-Received: by 2002:a7b:c190:: with SMTP id y16mr8371998wmi.107.1580944209783; Wed, 05 Feb 2020 15:10:09 -0800 (PST) Original-Received: from breton.holly.idiocy.org (ip6-2001-08b0-03f8-8129-5826-eb34-fa9b-b20b.holly.idiocy.org. [2001:8b0:3f8:8129:5826:eb34:fa9b:b20b]) by smtp.gmail.com with ESMTPSA id l8sm1319408wmj.2.2020.02.05.15.10.08 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 05 Feb 2020 15:10:08 -0800 (PST) Content-Disposition: inline In-Reply-To: X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::32c X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Original-Sender: "Emacs-devel" Xref: news.gmane.io gmane.emacs.devel:244873 Archived-At: --KsGdsel6WgEHnImy Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: 8bit On Wed, Feb 05, 2020 at 11:00:05AM -0500, Daniel Pittman wrote: > G'day Alan, et al. > > After building Emacs with the ns/draw-to-bitmap branch merged, on macOS > 10.15.2, I'm seeing some performance issues. I'd like, Alan, to know how > you would prefer me to proceed in a way that best helps you (and anyone > else here) resolve them. > > This is on a MacBook Pro (15-inch, 2019), Intel UHD Graphics 630 1536 MB + > Radeon Pro 555X machine, driving the 2880 x 1800 internal LCD at the > default 1680 x 1050 apparent resolution. `frame-edges` are at 78, 45 and > 1677, 1049, as far as Emacs is concerned. > > The trivial reproduction, for me, is to hold any key (eg: "n") to get > auto-repeat inserting it. On this machine I see approximately 50 > characters every 10 seconds, or ~ 5 "frames" per second. This is, of > course, substantially lower than anywhere else in the OS, and also far > below what I'd experience prior to that merge. > > This is a pretty clear "graphics update" cost: > (list (progn (goto-char 0) (benchmark-run 40 (next-line))) (progn > (goto-char 0) (benchmark-run 40 (next-line) (redisplay)))) > => ((0.006762000000000001 0 0.0) (9.116147 0 0.0)) > > I can dig into this myself, and see if I can't figure out what is slowing > everything down, but I figured I'd ask first. To start with you should probably try the attached patch. I find it much faster, but others still report it as unacceptably slow. > PS: I didn't check yet, but one of my first points of call is going to be > the "canDrawConcurrently" flag on the NSView, since the bitmap rep means it > doesn't need to block anything while redrawing, yes? > > https://developer.apple.com/documentation/appkit/nsview/1483425-candrawconcurrently?language=objc That makes sense, but I’ve added that to the patch, but it doesn’t seem to make much of a difference here. You can try commenting it out at nsterm.m:7431, see if you can see any difference. Perhaps we need to do something different to really let it do its thing? Honestly, though, I’d be happy for you to have a look as we’re rather struggling. Using the offscreen bitmap means Emacs doesn’t have any odd graphical glitches. The other method, described here https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32932#412 does have serious glitches but has *much* better performance. -- Alan Third --KsGdsel6WgEHnImy Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v3-0001-Use-CGLayer-instead-of-NSBitmapImageRep-bug-32932.patch" >From 86017c66a8fef5abf091c957a6b9a40dfd276413 Mon Sep 17 00:00:00 2001 From: Alan Third Date: Sat, 1 Feb 2020 21:17:29 +0000 Subject: [PATCH v3] Use CGLayer instead of NSBitmapImageRep (bug#32932) --- src/nsterm.h | 4 +-- src/nsterm.m | 95 +++++++++++++++++++++++++--------------------------- 2 files changed, 48 insertions(+), 51 deletions(-) diff --git a/src/nsterm.h b/src/nsterm.h index 980ca534cf..eefa4d706d 100644 --- a/src/nsterm.h +++ b/src/nsterm.h @@ -418,7 +418,7 @@ #define NSTRACE_UNSILENCE() NSWindow *nonfs_window; BOOL fs_is_native; #ifdef NS_IMPL_COCOA - NSBitmapImageRep *drawingBuffer; + CGLayerRef drawingBuffer; #endif @public struct frame *emacsframe; @@ -464,7 +464,7 @@ #define NSTRACE_UNSILENCE() - (void)focusOnDrawingBuffer; #endif - (void)copyRect:(NSRect)srcRect to:(NSRect)dstRect; -- (void)createDrawingBufferWithRect:(NSRect)rect; +- (void)createDrawingBuffer; /* Non-notification versions of NSView methods. Used for direct calls. */ - (void)windowWillEnterFullScreen; diff --git a/src/nsterm.m b/src/nsterm.m index 9d427b9b38..0f26db4d8a 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -1141,7 +1141,6 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen) #ifdef NS_IMPL_COCOA [NSGraphicsContext setCurrentContext:nil]; - [view display]; #else block_input (); @@ -2853,7 +2852,9 @@ so some key presses (TAB) are swallowed by the system. */ ns_unfocus (f); /* as of 2006/11 or so this is now needed */ - ns_redraw_scroll_bars (f); + /* FIXME: I don't see any reason for this and removing it makes no + difference here. Do we need it for GNUstep? */ + //ns_redraw_scroll_bars (f); unblock_input (); } @@ -3169,18 +3170,6 @@ so some key presses (TAB) are swallowed by the system. */ NSTRACE_RECT ("fromRect", fromRect); - /* Because we're drawing into an offscreen buffer which isn't - flipped, the images come out upside down. To work around it - we need to do some fancy transforms. */ - { - NSAffineTransform *transform = [NSAffineTransform transform]; - [transform translateXBy:0 yBy:NSMaxY(imageRect)]; - [transform scaleXBy:1 yBy:-1]; - [transform concat]; - - imageRect.origin.y = 0; - } - [img drawInRect: imageRect fromRect: fromRect operation: NSCompositingOperationSourceOver @@ -3938,11 +3927,6 @@ Function modeled after x_draw_glyph_string_box (). NSAffineTransform *doTransform = [NSAffineTransform transform]; - /* We have to flip the image around the X axis as the offscreen - bitmap we're drawing to is flipped. */ - [doTransform scaleXBy:1 yBy:-1]; - [doTransform translateXBy:0 yBy:-[img size].height]; - /* ImageMagick images don't have transforms. */ if (img->transform) [doTransform appendTransform:img->transform]; @@ -4838,7 +4822,7 @@ in certain situations (rapid incoming events). if (NILP (window->vertical_scroll_bar)) { if (width > 0 && height > 0) - ns_clear_frame_area (f, left, top, width, height); + ns_clear_frame_area (f, left, top, width, height); bar = [[EmacsScroller alloc] initFrame: r window: win]; wset_vertical_scroll_bar (window, make_mint_ptr (bar)); @@ -7104,7 +7088,7 @@ - (void) updateFrameSize: (BOOL) delay from non-native fullscreen, in other circumstances it appears to be a noop. (bug#28872) */ wr = NSMakeRect (0, 0, neww, newh); - [self createDrawingBufferWithRect:wr]; + [self createDrawingBuffer]; [view setFrame: wr]; // To do: consider using [NSNotificationCenter postNotificationName:]. @@ -7444,7 +7428,8 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f maximizing_resize = NO; #endif - [self createDrawingBufferWithRect:r]; + [self setCanDrawConcurrently:YES]; + [self createDrawingBuffer]; win = [[EmacsWindow alloc] initWithContentRect: r @@ -8229,20 +8214,30 @@ - (instancetype)toggleToolbar: (id)sender } -- (void)createDrawingBufferWithRect:(NSRect)rect - /* Create and store a new NSBitmapImageRep for Emacs to draw - into. +- (void)createDrawingBuffer + /* Create and store a new CGLayer for Emacs to draw into. - Drawing to an offscreen bitmap doesn't work in GNUstep as there's - a bug in graphicsContextWithBitmapImageRep - (https://savannah.gnu.org/bugs/?38405). So under GNUstep we - retain the old method of drawing direct to the EmacsView. */ + We can't do this in GNUstep as there's no CGLayer or equivalent. + So under GNUstep we retain the old method of drawing direct to + the EmacsView. */ { #ifdef NS_IMPL_COCOA + NSGraphicsContext *screen; + CGFloat scale = [[self window] backingScaleFactor]; + NSRect frame = [self frame]; + if (drawingBuffer != nil) - [drawingBuffer release]; + CGLayerRelease (drawingBuffer); - drawingBuffer = [[self bitmapImageRepForCachingDisplayInRect:rect] retain]; + screen = [NSGraphicsContext graphicsContextWithBitmapImageRep: + [self bitmapImageRepForCachingDisplayInRect:frame]]; + + drawingBuffer = CGLayerCreateWithContext ([screen CGContext], + CGSizeMake (NSWidth (frame) * scale, + NSHeight (frame) * scale), + nil); + + CGContextScaleCTM(CGLayerGetContext (drawingBuffer), scale, scale); #endif } @@ -8250,11 +8245,12 @@ - (void)createDrawingBufferWithRect:(NSRect)rect #ifdef NS_IMPL_COCOA - (void)focusOnDrawingBuffer { - /* Creating the graphics context each time is very slow, but it - doesn't seem possible to cache and reuse it. */ - [NSGraphicsContext - setCurrentContext: - [NSGraphicsContext graphicsContextWithBitmapImageRep:drawingBuffer]]; + CGContextRef cgctx = CGLayerGetContext (drawingBuffer); + NSGraphicsContext *buf = + [NSGraphicsContext + graphicsContextWithCGContext:cgctx flipped:YES]; + + [NSGraphicsContext setCurrentContext:buf]; } @@ -8269,7 +8265,7 @@ - (void)windowDidChangeBackingProperties:(NSNotification *)notification if (old != new) { NSRect frame = [self frame]; - [self createDrawingBufferWithRect:frame]; + [self createDrawingBuffer]; ns_clear_frame (emacsframe); expose_frame (emacsframe, 0, 0, NSWidth (frame), NSHeight (frame)); } @@ -8284,13 +8280,18 @@ - (void)copyRect:(NSRect)srcRect to:(NSRect)dstRect NSTRACE_RECT ("Destination", dstRect); #ifdef NS_IMPL_COCOA - [drawingBuffer drawInRect:dstRect - fromRect:srcRect - operation:NSCompositingOperationCopy - fraction:1.0 - respectFlipped:NO - hints:nil]; + NSRect frame = [self frame]; + CGRect offsetRect = CGRectMake (NSMinX (dstRect) - NSMinX (srcRect), + NSMinY (dstRect) - NSMinY (srcRect), + NSWidth (frame), NSHeight (frame)); + [[NSGraphicsContext currentContext] saveGraphicsState]; + NSRectClip (dstRect); + + CGContextDrawLayerInRect ([[NSGraphicsContext currentContext] CGContext], + offsetRect, drawingBuffer); + + [[NSGraphicsContext currentContext] restoreGraphicsState]; [self setNeedsDisplayInRect:dstRect]; #else hide_bell(); // Ensure the bell image isn't scrolled. @@ -8313,12 +8314,8 @@ - (void)drawRect: (NSRect)rect return; #ifdef NS_IMPL_COCOA - [drawingBuffer drawInRect:rect - fromRect:rect - operation:NSCompositingOperationSourceOver - fraction:1 - respectFlipped:NO - hints:nil]; + CGContextRef ctx = [[NSGraphicsContext currentContext] CGContext]; + CGContextDrawLayerInRect (ctx, NSRectToCGRect ([self frame]), drawingBuffer); #else int x = NSMinX (rect), y = NSMinY (rect); int width = NSWidth (rect), height = NSHeight (rect); -- 2.24.0 --KsGdsel6WgEHnImy--