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.
This commit is contained in:
parent
a32e65b357
commit
246e107d73
2 changed files with 76 additions and 56 deletions
|
@ -724,8 +724,9 @@ typedef id instancetype;
|
|||
IOSurfaceRef currentSurface;
|
||||
IOSurfaceRef lastSurface;
|
||||
CGContextRef context;
|
||||
CGFloat scale;
|
||||
}
|
||||
- (id) initWithSize: (NSSize)s ColorSpace: (CGColorSpaceRef)cs;
|
||||
- (id) initWithSize: (NSSize)s ColorSpace: (CGColorSpaceRef)cs Scale: (CGFloat)scale;
|
||||
- (void) dealloc;
|
||||
- (NSSize) getSize;
|
||||
- (CGContextRef) getContext;
|
||||
|
|
129
src/nsterm.m
129
src/nsterm.m
|
@ -8353,19 +8353,17 @@ - (void)focusOnDrawingBuffer
|
|||
|
||||
surface = [[EmacsSurface alloc] initWithSize:s
|
||||
ColorSpace:[[[self window] colorSpace]
|
||||
CGColorSpace]];
|
||||
CGColorSpace]
|
||||
Scale:scale];
|
||||
|
||||
/* Since we're using NSViewLayerContentsRedrawOnSetNeedsDisplay
|
||||
the layer's scale factor is not set automatically, so do it
|
||||
now. */
|
||||
[[self layer] setContentsScale:[[self window] backingScaleFactor]];
|
||||
[[self layer] setContentsScale:scale];
|
||||
}
|
||||
|
||||
CGContextRef context = [surface getContext];
|
||||
|
||||
CGContextTranslateCTM(context, 0, [surface getSize].height);
|
||||
CGContextScaleCTM(context, scale, -scale);
|
||||
|
||||
[NSGraphicsContext
|
||||
setCurrentContext:[NSGraphicsContext
|
||||
graphicsContextWithCGContext:context
|
||||
|
@ -8378,7 +8376,6 @@ - (void)unfocusDrawingBuffer
|
|||
NSTRACE ("[EmacsView unfocusDrawingBuffer]");
|
||||
|
||||
[NSGraphicsContext setCurrentContext:nil];
|
||||
[surface releaseContext];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
|
@ -8516,7 +8513,11 @@ - (void)updateLayer
|
|||
There's a private method, -[CALayer setContentsChanged], that we
|
||||
could use to force it, but we shouldn't often get the same
|
||||
surface twice in a row. */
|
||||
[surface releaseContext];
|
||||
[[self layer] setContents:(id)[surface getSurface]];
|
||||
[surface performSelectorOnMainThread:@selector (getContext)
|
||||
withObject:nil
|
||||
waitUntilDone:NO];
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -9717,17 +9718,20 @@ @implementation EmacsSurface
|
|||
probably be some sort of pruning job that removes excess
|
||||
surfaces. */
|
||||
|
||||
#define CACHE_MAX_SIZE 2
|
||||
|
||||
- (id) initWithSize: (NSSize)s
|
||||
ColorSpace: (CGColorSpaceRef)cs
|
||||
Scale: (CGFloat)scl
|
||||
{
|
||||
NSTRACE ("[EmacsSurface initWithSize:ColorSpace:]");
|
||||
|
||||
[super init];
|
||||
|
||||
cache = [[NSMutableArray arrayWithCapacity:3] retain];
|
||||
cache = [[NSMutableArray arrayWithCapacity:CACHE_MAX_SIZE] retain];
|
||||
size = s;
|
||||
colorSpace = cs;
|
||||
scale = scl;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
@ -9740,8 +9744,6 @@ - (void) dealloc
|
|||
|
||||
if (currentSurface)
|
||||
CFRelease (currentSurface);
|
||||
if (lastSurface)
|
||||
CFRelease (lastSurface);
|
||||
|
||||
for (id object in cache)
|
||||
CFRelease ((IOSurfaceRef)object);
|
||||
|
@ -9764,50 +9766,66 @@ - (NSSize) getSize
|
|||
calls cannot be nested. */
|
||||
- (CGContextRef) getContext
|
||||
{
|
||||
IOSurfaceRef surface = NULL;
|
||||
NSTRACE ("[EmacsSurface getContext]");
|
||||
|
||||
NSTRACE ("[EmacsSurface getContextWithSize:]");
|
||||
NSTRACE_MSG ("IOSurface count: %lu", [cache count] + (lastSurface ? 1 : 0));
|
||||
|
||||
for (id object in cache)
|
||||
if (!context)
|
||||
{
|
||||
if (!IOSurfaceIsInUse ((IOSurfaceRef)object))
|
||||
{
|
||||
surface = (IOSurfaceRef)object;
|
||||
[cache removeObject:object];
|
||||
break;
|
||||
}
|
||||
IOSurfaceRef surface = NULL;
|
||||
|
||||
NSTRACE_MSG ("IOSurface count: %lu", [cache count] + (lastSurface ? 1 : 0));
|
||||
|
||||
for (id object in cache)
|
||||
{
|
||||
if (!IOSurfaceIsInUse ((IOSurfaceRef)object))
|
||||
{
|
||||
surface = (IOSurfaceRef)object;
|
||||
[cache removeObject:object];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!surface && [cache count] >= CACHE_MAX_SIZE)
|
||||
{
|
||||
/* Just grab the first one off the cache. This may result
|
||||
in tearing effects. The alternative is to wait for one
|
||||
of the surfaces to become free. */
|
||||
surface = (IOSurfaceRef)[cache firstObject];
|
||||
[cache removeObject:(id)surface];
|
||||
}
|
||||
else if (!surface)
|
||||
{
|
||||
int bytesPerRow = IOSurfaceAlignProperty (kIOSurfaceBytesPerRow,
|
||||
size.width * 4);
|
||||
|
||||
surface = IOSurfaceCreate
|
||||
((CFDictionaryRef)@{(id)kIOSurfaceWidth:[NSNumber numberWithInt:size.width],
|
||||
(id)kIOSurfaceHeight:[NSNumber numberWithInt:size.height],
|
||||
(id)kIOSurfaceBytesPerRow:[NSNumber numberWithInt:bytesPerRow],
|
||||
(id)kIOSurfaceBytesPerElement:[NSNumber numberWithInt:4],
|
||||
(id)kIOSurfacePixelFormat:[NSNumber numberWithUnsignedInt:'BGRA']});
|
||||
}
|
||||
|
||||
IOReturn lockStatus = IOSurfaceLock (surface, 0, nil);
|
||||
if (lockStatus != kIOReturnSuccess)
|
||||
NSLog (@"Failed to lock surface: %x", lockStatus);
|
||||
|
||||
[self copyContentsTo:surface];
|
||||
|
||||
currentSurface = surface;
|
||||
|
||||
context = CGBitmapContextCreate (IOSurfaceGetBaseAddress (currentSurface),
|
||||
IOSurfaceGetWidth (currentSurface),
|
||||
IOSurfaceGetHeight (currentSurface),
|
||||
8,
|
||||
IOSurfaceGetBytesPerRow (currentSurface),
|
||||
colorSpace,
|
||||
(kCGImageAlphaPremultipliedFirst
|
||||
| kCGBitmapByteOrder32Host));
|
||||
|
||||
CGContextTranslateCTM(context, 0, size.height);
|
||||
CGContextScaleCTM(context, scale, -scale);
|
||||
}
|
||||
|
||||
if (!surface)
|
||||
{
|
||||
int bytesPerRow = IOSurfaceAlignProperty (kIOSurfaceBytesPerRow,
|
||||
size.width * 4);
|
||||
|
||||
surface = IOSurfaceCreate
|
||||
((CFDictionaryRef)@{(id)kIOSurfaceWidth:[NSNumber numberWithInt:size.width],
|
||||
(id)kIOSurfaceHeight:[NSNumber numberWithInt:size.height],
|
||||
(id)kIOSurfaceBytesPerRow:[NSNumber numberWithInt:bytesPerRow],
|
||||
(id)kIOSurfaceBytesPerElement:[NSNumber numberWithInt:4],
|
||||
(id)kIOSurfacePixelFormat:[NSNumber numberWithUnsignedInt:'BGRA']});
|
||||
}
|
||||
|
||||
IOReturn lockStatus = IOSurfaceLock (surface, 0, nil);
|
||||
if (lockStatus != kIOReturnSuccess)
|
||||
NSLog (@"Failed to lock surface: %x", lockStatus);
|
||||
|
||||
[self copyContentsTo:surface];
|
||||
|
||||
currentSurface = surface;
|
||||
|
||||
context = CGBitmapContextCreate (IOSurfaceGetBaseAddress (currentSurface),
|
||||
IOSurfaceGetWidth (currentSurface),
|
||||
IOSurfaceGetHeight (currentSurface),
|
||||
8,
|
||||
IOSurfaceGetBytesPerRow (currentSurface),
|
||||
colorSpace,
|
||||
(kCGImageAlphaPremultipliedFirst
|
||||
| kCGBitmapByteOrder32Host));
|
||||
return context;
|
||||
}
|
||||
|
||||
|
@ -9818,6 +9836,9 @@ - (void) releaseContext
|
|||
{
|
||||
NSTRACE ("[EmacsSurface releaseContextAndGetSurface]");
|
||||
|
||||
if (!context)
|
||||
return;
|
||||
|
||||
CGContextRelease (context);
|
||||
context = NULL;
|
||||
|
||||
|
@ -9825,11 +9846,8 @@ - (void) releaseContext
|
|||
if (lockStatus != kIOReturnSuccess)
|
||||
NSLog (@"Failed to unlock surface: %x", lockStatus);
|
||||
|
||||
/* Put lastSurface back on the end of the cache. It may not have
|
||||
been displayed on the screen yet, but we probably want the new
|
||||
data and not some stale data anyway. */
|
||||
if (lastSurface)
|
||||
[cache addObject:(id)lastSurface];
|
||||
/* Put currentSurface back on the end of the cache. */
|
||||
[cache addObject:(id)currentSurface];
|
||||
lastSurface = currentSurface;
|
||||
currentSurface = NULL;
|
||||
}
|
||||
|
@ -9854,7 +9872,7 @@ - (void) copyContentsTo: (IOSurfaceRef) destination
|
|||
|
||||
NSTRACE ("[EmacsSurface copyContentsTo:]");
|
||||
|
||||
if (! lastSurface)
|
||||
if (!lastSurface || lastSurface == destination)
|
||||
return;
|
||||
|
||||
lockStatus = IOSurfaceLock (lastSurface, kIOSurfaceLockReadOnly, nil);
|
||||
|
@ -9874,6 +9892,7 @@ - (void) copyContentsTo: (IOSurfaceRef) destination
|
|||
NSLog (@"Failed to unlock source surface: %x", lockStatus);
|
||||
}
|
||||
|
||||
#undef CACHE_MAX_SIZE
|
||||
|
||||
@end /* EmacsSurface */
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue