diff options
| author | Alan Third | 2021-05-21 13:33:56 +0100 |
|---|---|---|
| committer | Alan Third | 2021-06-01 19:51:21 +0100 |
| commit | 246e107d73e633c06478eaf021776acedef9dafc (patch) | |
| tree | cb4ac63a1b3f6168fa7d013a792c427ccb2ec4ae /src | |
| parent | a32e65b357ff634a976a24ee5d5c340addc025cd (diff) | |
| download | emacs-246e107d73e633c06478eaf021776acedef9dafc.tar.gz emacs-246e107d73e633c06478eaf021776acedef9dafc.zip | |
Improve performance of NS port's display on macOS
* src/nsterm.h: Update EmacsSurface definition.
* src/nsterm.m ([EmacsView focusOnDrawingBuffer]): Don't change the
CGContext's settings directly.
([EmacsView unfocusDrawingBuffer]): Don't release the context here.
(CACHE_MAX_SIZE): Add maximum cache size.
([EmacsView updateLayer]): Send a request for getContext, which will
copy the buffer and create the context if it doesn't already exist, to
the NS run loop.
([EmacsSurface initWithSize:ColorSpace:Scale:]): Add the scale factor
and if there's already a CGContext available, reuse it.
([EmacsSurface dealloc]): No longer need to release lastSurface
separately.
([EmacsSurface getContext]): Don't create more surfaces than we have
spaces for in the cache.
([EmacsSurface releaseContext]): If there's no context don't try to
release it and put currentSurface back on the cache instead of
lastSurface.
([EmacsSurface copyContentsTo:]): Don't try to copy if the source and
destination are actually the same surface.
Diffstat (limited to 'src')
| -rw-r--r-- | src/nsterm.h | 3 | ||||
| -rw-r--r-- | src/nsterm.m | 121 |
2 files changed, 72 insertions, 52 deletions
diff --git a/src/nsterm.h b/src/nsterm.h index 017c2394ef1..0596f3f3c10 100644 --- a/src/nsterm.h +++ b/src/nsterm.h | |||
| @@ -724,8 +724,9 @@ typedef id instancetype; | |||
| 724 | IOSurfaceRef currentSurface; | 724 | IOSurfaceRef currentSurface; |
| 725 | IOSurfaceRef lastSurface; | 725 | IOSurfaceRef lastSurface; |
| 726 | CGContextRef context; | 726 | CGContextRef context; |
| 727 | CGFloat scale; | ||
| 727 | } | 728 | } |
| 728 | - (id) initWithSize: (NSSize)s ColorSpace: (CGColorSpaceRef)cs; | 729 | - (id) initWithSize: (NSSize)s ColorSpace: (CGColorSpaceRef)cs Scale: (CGFloat)scale; |
| 729 | - (void) dealloc; | 730 | - (void) dealloc; |
| 730 | - (NSSize) getSize; | 731 | - (NSSize) getSize; |
| 731 | - (CGContextRef) getContext; | 732 | - (CGContextRef) getContext; |
diff --git a/src/nsterm.m b/src/nsterm.m index bb20886ab1d..f6168243a49 100644 --- a/src/nsterm.m +++ b/src/nsterm.m | |||
| @@ -8353,19 +8353,17 @@ not_in_argv (NSString *arg) | |||
| 8353 | 8353 | ||
| 8354 | surface = [[EmacsSurface alloc] initWithSize:s | 8354 | surface = [[EmacsSurface alloc] initWithSize:s |
| 8355 | ColorSpace:[[[self window] colorSpace] | 8355 | ColorSpace:[[[self window] colorSpace] |
| 8356 | CGColorSpace]]; | 8356 | CGColorSpace] |
| 8357 | Scale:scale]; | ||
| 8357 | 8358 | ||
| 8358 | /* Since we're using NSViewLayerContentsRedrawOnSetNeedsDisplay | 8359 | /* Since we're using NSViewLayerContentsRedrawOnSetNeedsDisplay |
| 8359 | the layer's scale factor is not set automatically, so do it | 8360 | the layer's scale factor is not set automatically, so do it |
| 8360 | now. */ | 8361 | now. */ |
| 8361 | [[self layer] setContentsScale:[[self window] backingScaleFactor]]; | 8362 | [[self layer] setContentsScale:scale]; |
| 8362 | } | 8363 | } |
| 8363 | 8364 | ||
| 8364 | CGContextRef context = [surface getContext]; | 8365 | CGContextRef context = [surface getContext]; |
| 8365 | 8366 | ||
| 8366 | CGContextTranslateCTM(context, 0, [surface getSize].height); | ||
| 8367 | CGContextScaleCTM(context, scale, -scale); | ||
| 8368 | |||
| 8369 | [NSGraphicsContext | 8367 | [NSGraphicsContext |
| 8370 | setCurrentContext:[NSGraphicsContext | 8368 | setCurrentContext:[NSGraphicsContext |
| 8371 | graphicsContextWithCGContext:context | 8369 | graphicsContextWithCGContext:context |
| @@ -8378,7 +8376,6 @@ not_in_argv (NSString *arg) | |||
| 8378 | NSTRACE ("[EmacsView unfocusDrawingBuffer]"); | 8376 | NSTRACE ("[EmacsView unfocusDrawingBuffer]"); |
| 8379 | 8377 | ||
| 8380 | [NSGraphicsContext setCurrentContext:nil]; | 8378 | [NSGraphicsContext setCurrentContext:nil]; |
| 8381 | [surface releaseContext]; | ||
| 8382 | [self setNeedsDisplay:YES]; | 8379 | [self setNeedsDisplay:YES]; |
| 8383 | } | 8380 | } |
| 8384 | 8381 | ||
| @@ -8516,7 +8513,11 @@ not_in_argv (NSString *arg) | |||
| 8516 | There's a private method, -[CALayer setContentsChanged], that we | 8513 | There's a private method, -[CALayer setContentsChanged], that we |
| 8517 | could use to force it, but we shouldn't often get the same | 8514 | could use to force it, but we shouldn't often get the same |
| 8518 | surface twice in a row. */ | 8515 | surface twice in a row. */ |
| 8516 | [surface releaseContext]; | ||
| 8519 | [[self layer] setContents:(id)[surface getSurface]]; | 8517 | [[self layer] setContents:(id)[surface getSurface]]; |
| 8518 | [surface performSelectorOnMainThread:@selector (getContext) | ||
| 8519 | withObject:nil | ||
| 8520 | waitUntilDone:NO]; | ||
| 8520 | } | 8521 | } |
| 8521 | #endif | 8522 | #endif |
| 8522 | 8523 | ||
| @@ -9717,17 +9718,20 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c) | |||
| 9717 | probably be some sort of pruning job that removes excess | 9718 | probably be some sort of pruning job that removes excess |
| 9718 | surfaces. */ | 9719 | surfaces. */ |
| 9719 | 9720 | ||
| 9721 | #define CACHE_MAX_SIZE 2 | ||
| 9720 | 9722 | ||
| 9721 | - (id) initWithSize: (NSSize)s | 9723 | - (id) initWithSize: (NSSize)s |
| 9722 | ColorSpace: (CGColorSpaceRef)cs | 9724 | ColorSpace: (CGColorSpaceRef)cs |
| 9725 | Scale: (CGFloat)scl | ||
| 9723 | { | 9726 | { |
| 9724 | NSTRACE ("[EmacsSurface initWithSize:ColorSpace:]"); | 9727 | NSTRACE ("[EmacsSurface initWithSize:ColorSpace:]"); |
| 9725 | 9728 | ||
| 9726 | [super init]; | 9729 | [super init]; |
| 9727 | 9730 | ||
| 9728 | cache = [[NSMutableArray arrayWithCapacity:3] retain]; | 9731 | cache = [[NSMutableArray arrayWithCapacity:CACHE_MAX_SIZE] retain]; |
| 9729 | size = s; | 9732 | size = s; |
| 9730 | colorSpace = cs; | 9733 | colorSpace = cs; |
| 9734 | scale = scl; | ||
| 9731 | 9735 | ||
| 9732 | return self; | 9736 | return self; |
| 9733 | } | 9737 | } |
| @@ -9740,8 +9744,6 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c) | |||
| 9740 | 9744 | ||
| 9741 | if (currentSurface) | 9745 | if (currentSurface) |
| 9742 | CFRelease (currentSurface); | 9746 | CFRelease (currentSurface); |
| 9743 | if (lastSurface) | ||
| 9744 | CFRelease (lastSurface); | ||
| 9745 | 9747 | ||
| 9746 | for (id object in cache) | 9748 | for (id object in cache) |
| 9747 | CFRelease ((IOSurfaceRef)object); | 9749 | CFRelease ((IOSurfaceRef)object); |
| @@ -9764,50 +9766,66 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c) | |||
| 9764 | calls cannot be nested. */ | 9766 | calls cannot be nested. */ |
| 9765 | - (CGContextRef) getContext | 9767 | - (CGContextRef) getContext |
| 9766 | { | 9768 | { |
| 9767 | IOSurfaceRef surface = NULL; | 9769 | NSTRACE ("[EmacsSurface getContext]"); |
| 9768 | |||
| 9769 | NSTRACE ("[EmacsSurface getContextWithSize:]"); | ||
| 9770 | NSTRACE_MSG ("IOSurface count: %lu", [cache count] + (lastSurface ? 1 : 0)); | ||
| 9771 | 9770 | ||
| 9772 | for (id object in cache) | 9771 | if (!context) |
| 9773 | { | 9772 | { |
| 9774 | if (!IOSurfaceIsInUse ((IOSurfaceRef)object)) | 9773 | IOSurfaceRef surface = NULL; |
| 9775 | { | ||
| 9776 | surface = (IOSurfaceRef)object; | ||
| 9777 | [cache removeObject:object]; | ||
| 9778 | break; | ||
| 9779 | } | ||
| 9780 | } | ||
| 9781 | 9774 | ||
| 9782 | if (!surface) | 9775 | NSTRACE_MSG ("IOSurface count: %lu", [cache count] + (lastSurface ? 1 : 0)); |
| 9783 | { | ||
| 9784 | int bytesPerRow = IOSurfaceAlignProperty (kIOSurfaceBytesPerRow, | ||
| 9785 | size.width * 4); | ||
| 9786 | 9776 | ||
| 9787 | surface = IOSurfaceCreate | 9777 | for (id object in cache) |
| 9788 | ((CFDictionaryRef)@{(id)kIOSurfaceWidth:[NSNumber numberWithInt:size.width], | 9778 | { |
| 9789 | (id)kIOSurfaceHeight:[NSNumber numberWithInt:size.height], | 9779 | if (!IOSurfaceIsInUse ((IOSurfaceRef)object)) |
| 9790 | (id)kIOSurfaceBytesPerRow:[NSNumber numberWithInt:bytesPerRow], | 9780 | { |
| 9791 | (id)kIOSurfaceBytesPerElement:[NSNumber numberWithInt:4], | 9781 | surface = (IOSurfaceRef)object; |
| 9792 | (id)kIOSurfacePixelFormat:[NSNumber numberWithUnsignedInt:'BGRA']}); | 9782 | [cache removeObject:object]; |
| 9793 | } | 9783 | break; |
| 9784 | } | ||
| 9785 | } | ||
| 9794 | 9786 | ||
| 9795 | IOReturn lockStatus = IOSurfaceLock (surface, 0, nil); | 9787 | if (!surface && [cache count] >= CACHE_MAX_SIZE) |
| 9796 | if (lockStatus != kIOReturnSuccess) | 9788 | { |
| 9797 | NSLog (@"Failed to lock surface: %x", lockStatus); | 9789 | /* Just grab the first one off the cache. This may result |
| 9790 | in tearing effects. The alternative is to wait for one | ||
| 9791 | of the surfaces to become free. */ | ||
| 9792 | surface = (IOSurfaceRef)[cache firstObject]; | ||
| 9793 | [cache removeObject:(id)surface]; | ||
| 9794 | } | ||
| 9795 | else if (!surface) | ||
| 9796 | { | ||
| 9797 | int bytesPerRow = IOSurfaceAlignProperty (kIOSurfaceBytesPerRow, | ||
| 9798 | size.width * 4); | ||
| 9799 | |||
| 9800 | surface = IOSurfaceCreate | ||
| 9801 | ((CFDictionaryRef)@{(id)kIOSurfaceWidth:[NSNumber numberWithInt:size.width], | ||
| 9802 | (id)kIOSurfaceHeight:[NSNumber numberWithInt:size.height], | ||
| 9803 | (id)kIOSurfaceBytesPerRow:[NSNumber numberWithInt:bytesPerRow], | ||
| 9804 | (id)kIOSurfaceBytesPerElement:[NSNumber numberWithInt:4], | ||
| 9805 | (id)kIOSurfacePixelFormat:[NSNumber numberWithUnsignedInt:'BGRA']}); | ||
| 9806 | } | ||
| 9807 | |||
| 9808 | IOReturn lockStatus = IOSurfaceLock (surface, 0, nil); | ||
| 9809 | if (lockStatus != kIOReturnSuccess) | ||
| 9810 | NSLog (@"Failed to lock surface: %x", lockStatus); | ||
| 9798 | 9811 | ||
| 9799 | [self copyContentsTo:surface]; | 9812 | [self copyContentsTo:surface]; |
| 9800 | 9813 | ||
| 9801 | currentSurface = surface; | 9814 | currentSurface = surface; |
| 9815 | |||
| 9816 | context = CGBitmapContextCreate (IOSurfaceGetBaseAddress (currentSurface), | ||
| 9817 | IOSurfaceGetWidth (currentSurface), | ||
| 9818 | IOSurfaceGetHeight (currentSurface), | ||
| 9819 | 8, | ||
| 9820 | IOSurfaceGetBytesPerRow (currentSurface), | ||
| 9821 | colorSpace, | ||
| 9822 | (kCGImageAlphaPremultipliedFirst | ||
| 9823 | | kCGBitmapByteOrder32Host)); | ||
| 9824 | |||
| 9825 | CGContextTranslateCTM(context, 0, size.height); | ||
| 9826 | CGContextScaleCTM(context, scale, -scale); | ||
| 9827 | } | ||
| 9802 | 9828 | ||
| 9803 | context = CGBitmapContextCreate (IOSurfaceGetBaseAddress (currentSurface), | ||
| 9804 | IOSurfaceGetWidth (currentSurface), | ||
| 9805 | IOSurfaceGetHeight (currentSurface), | ||
| 9806 | 8, | ||
| 9807 | IOSurfaceGetBytesPerRow (currentSurface), | ||
| 9808 | colorSpace, | ||
| 9809 | (kCGImageAlphaPremultipliedFirst | ||
| 9810 | | kCGBitmapByteOrder32Host)); | ||
| 9811 | return context; | 9829 | return context; |
| 9812 | } | 9830 | } |
| 9813 | 9831 | ||
| @@ -9818,6 +9836,9 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c) | |||
| 9818 | { | 9836 | { |
| 9819 | NSTRACE ("[EmacsSurface releaseContextAndGetSurface]"); | 9837 | NSTRACE ("[EmacsSurface releaseContextAndGetSurface]"); |
| 9820 | 9838 | ||
| 9839 | if (!context) | ||
| 9840 | return; | ||
| 9841 | |||
| 9821 | CGContextRelease (context); | 9842 | CGContextRelease (context); |
| 9822 | context = NULL; | 9843 | context = NULL; |
| 9823 | 9844 | ||
| @@ -9825,11 +9846,8 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c) | |||
| 9825 | if (lockStatus != kIOReturnSuccess) | 9846 | if (lockStatus != kIOReturnSuccess) |
| 9826 | NSLog (@"Failed to unlock surface: %x", lockStatus); | 9847 | NSLog (@"Failed to unlock surface: %x", lockStatus); |
| 9827 | 9848 | ||
| 9828 | /* Put lastSurface back on the end of the cache. It may not have | 9849 | /* Put currentSurface back on the end of the cache. */ |
| 9829 | been displayed on the screen yet, but we probably want the new | 9850 | [cache addObject:(id)currentSurface]; |
| 9830 | data and not some stale data anyway. */ | ||
| 9831 | if (lastSurface) | ||
| 9832 | [cache addObject:(id)lastSurface]; | ||
| 9833 | lastSurface = currentSurface; | 9851 | lastSurface = currentSurface; |
| 9834 | currentSurface = NULL; | 9852 | currentSurface = NULL; |
| 9835 | } | 9853 | } |
| @@ -9854,7 +9872,7 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c) | |||
| 9854 | 9872 | ||
| 9855 | NSTRACE ("[EmacsSurface copyContentsTo:]"); | 9873 | NSTRACE ("[EmacsSurface copyContentsTo:]"); |
| 9856 | 9874 | ||
| 9857 | if (! lastSurface) | 9875 | if (!lastSurface || lastSurface == destination) |
| 9858 | return; | 9876 | return; |
| 9859 | 9877 | ||
| 9860 | lockStatus = IOSurfaceLock (lastSurface, kIOSurfaceLockReadOnly, nil); | 9878 | lockStatus = IOSurfaceLock (lastSurface, kIOSurfaceLockReadOnly, nil); |
| @@ -9874,6 +9892,7 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c) | |||
| 9874 | NSLog (@"Failed to unlock source surface: %x", lockStatus); | 9892 | NSLog (@"Failed to unlock source surface: %x", lockStatus); |
| 9875 | } | 9893 | } |
| 9876 | 9894 | ||
| 9895 | #undef CACHE_MAX_SIZE | ||
| 9877 | 9896 | ||
| 9878 | @end /* EmacsSurface */ | 9897 | @end /* EmacsSurface */ |
| 9879 | 9898 | ||