Skip to content

Commit bbad3ed

Browse files
authored
Merge pull request #757 from s4y/fix-the-flicker
Fix rendering issues when built against the 10.14 SDK.
2 parents 63dbd58 + ab71bd8 commit bbad3ed

File tree

9 files changed

+200
-10
lines changed

9 files changed

+200
-10
lines changed

runtime/doc/gui_mac.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,9 @@ to use in normal mode and type ":set imd" followed by ":set noimd".
715715
This list is by no means exhaustive, it only enumerates some of the more
716716
prominent bugs/missing features.
717717

718+
- Under macOS Mojave (10.14), the default renderer (Core Text renderer) has
719+
some performance issues and scrolling is not as smooth as previous macOS
720+
versions (10.13 or below).
718721
- Localized menus are not supported. Choosing anything but "English" in the
719722
"International" pane of "System Prefences" may break the menus (and
720723
toolbar).

src/MacVim/MMAppController.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ + (void)initialize
248248
[NSNumber numberWithBool:YES], MMNativeFullScreenKey,
249249
[NSNumber numberWithDouble:0.25], MMFullScreenFadeTimeKey,
250250
[NSNumber numberWithBool:NO], MMUseCGLayerAlwaysKey,
251+
@(shouldUseBufferedDrawing()), MMBufferedDrawingKey,
251252
[NSNumber numberWithBool:YES], MMShareFindPboardKey,
252253
nil];
253254

src/MacVim/MMCoreTextView.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,27 @@
4141
CGPoint *positions;
4242
NSMutableArray *fontCache;
4343

44+
// Issue draws onto an CGImage that caches the drawn results instead of
45+
// directly in drawRect:. This is the default behavior in cases where simply
46+
// drawing incrementally in drawRect: doesn't work. Those cases are:
47+
// 1. Non-native fullscreen
48+
// 2. 10.14+ (views are always layer-backed which means the view buffer will
49+
// be cleared and we can't incrementally draw in drawRect:)
50+
//
51+
// This can be configured by setting MMBufferedDrawingKey in user defaults.
52+
BOOL cgBufferDrawEnabled;
53+
BOOL cgBufferDrawNeedsUpdateContext;
54+
CGContextRef cgContext;
55+
56+
// *Deprecated*
57+
// Draw onto a CGLayer instead of lazily updating the view's buffer in
58+
// drawRect: which is error-prone and relying on undocumented behaviors
59+
// (that the OS will preserve the old buffer).
60+
//
61+
// This is deprecated. Use cgBufferDrawEnabled instead which is more
62+
// efficient.
63+
//
64+
// This can be configured by setting MMUseCGLayerAlwaysKey in user defaults.
4465
BOOL cgLayerEnabled;
4566
CGLayerRef cgLayer;
4667
CGContextRef cgLayerContext;

src/MacVim/MMCoreTextView.m

Lines changed: 145 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,10 @@ - (id)initWithFrame:(NSRect)frame
132132
{
133133
if (!(self = [super initWithFrame:frame]))
134134
return nil;
135+
136+
cgBufferDrawEnabled = [[NSUserDefaults standardUserDefaults]
137+
boolForKey:MMBufferedDrawingKey];
138+
cgBufferDrawNeedsUpdateContext = NO;
135139

136140
cgLayerEnabled = [[NSUserDefaults standardUserDefaults]
137141
boolForKey:MMUseCGLayerAlwaysKey];
@@ -168,6 +172,8 @@ - (void)dealloc
168172
[drawData release]; drawData = nil;
169173
[fontCache release]; fontCache = nil;
170174

175+
CGContextRelease(cgContext); cgContext = nil;
176+
171177
[helper setTextView:nil];
172178
[helper release]; helper = nil;
173179

@@ -215,6 +221,7 @@ - (void)setDefaultColorsBackground:(NSColor *)bgColor
215221
[defaultForegroundColor release];
216222
defaultForegroundColor = fgColor ? [fgColor retain] : nil;
217223
}
224+
[self setNeedsDisplay:YES];
218225
}
219226

220227
- (NSColor *)defaultBackgroundColor
@@ -444,13 +451,33 @@ - (BOOL)_wantsKeyDownForEvent:(id)event
444451
}
445452

446453
- (void)setFrameSize:(NSSize)newSize {
447-
if (!drawPending && !NSEqualSizes(newSize, self.frame.size) && drawData.count == 0) {
448-
[NSAnimationContext beginGrouping];
449-
drawPending = YES;
454+
if (!NSEqualSizes(newSize, self.bounds.size)) {
455+
if (!drawPending && !cgBufferDrawEnabled) {
456+
// When resizing a window, it will invalidate the buffer and cause
457+
// MacVim to draw black until we get the draw commands from Vim and
458+
// we draw them out in drawRect. Use beginGrouping to stop the
459+
// window resize from happening until we get the draw calls.
460+
//
461+
// The updateLayer/cgBufferDrawEnabled path handles this differently
462+
// and don't need this.
463+
[NSAnimationContext beginGrouping];
464+
drawPending = YES;
465+
}
466+
if (cgBufferDrawEnabled) {
467+
cgBufferDrawNeedsUpdateContext = YES;
468+
}
450469
}
470+
451471
[super setFrameSize:newSize];
452472
}
453473

474+
- (void)viewDidChangeBackingProperties {
475+
if (cgBufferDrawEnabled) {
476+
cgBufferDrawNeedsUpdateContext = YES;
477+
}
478+
[super viewDidChangeBackingProperties];
479+
}
480+
454481
- (void)keyDown:(NSEvent *)event
455482
{
456483
[helper keyDown:event];
@@ -606,6 +633,87 @@ - (BOOL)isFlipped
606633
return NO;
607634
}
608635

636+
- (void)updateCGContext {
637+
if (cgContext) {
638+
CGContextRelease(cgContext);
639+
cgContext = nil;
640+
}
641+
642+
NSRect backingRect = [self convertRectToBacking:self.bounds];
643+
cgContext = CGBitmapContextCreate(NULL, NSWidth(backingRect), NSHeight(backingRect), 8, 0, self.window.colorSpace.CGColorSpace, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host);
644+
CGContextScaleCTM(cgContext, self.window.backingScaleFactor, self.window.backingScaleFactor);
645+
646+
cgBufferDrawNeedsUpdateContext = NO;
647+
}
648+
649+
- (BOOL)wantsUpdateLayer {
650+
return cgBufferDrawEnabled;
651+
}
652+
653+
- (void)updateLayer {
654+
if (!cgContext) {
655+
[self updateCGContext];
656+
} else if (cgBufferDrawNeedsUpdateContext) {
657+
if ([drawData count] != 0) {
658+
[self updateCGContext];
659+
} else {
660+
// In this case, we don't have a single draw command, meaning that
661+
// Vim hasn't caught up yet and hasn't issued draw commands. We
662+
// don't want to use [NSAnimationContext beginGrouping] as it's
663+
// fragile (we may miss the endGrouping call due to order of
664+
// operation), and also it makes the animation jerky.
665+
// Instead, copy the image to the new context and align it to the
666+
// top left and make sure it doesn't stretch. This makes the
667+
// resizing smooth while Vim tries to catch up in issuing draws.
668+
CGImageRef oldImage = CGBitmapContextCreateImage(cgContext);
669+
670+
[self updateCGContext]; // This will make a new cgContext
671+
672+
CGContextSaveGState(cgContext);
673+
CGContextSetBlendMode(cgContext, kCGBlendModeCopy);
674+
675+
// Filling the background so the edge won't be black.
676+
NSRect newRect = [self bounds];
677+
float r = [defaultBackgroundColor redComponent];
678+
float g = [defaultBackgroundColor greenComponent];
679+
float b = [defaultBackgroundColor blueComponent];
680+
float a = [defaultBackgroundColor alphaComponent];
681+
CGContextSetRGBFillColor(cgContext, r, g, b, a);
682+
CGContextFillRect(cgContext, *(CGRect*)&newRect);
683+
CGContextSetBlendMode(cgContext, kCGBlendModeNormal);
684+
685+
// Copy the old image over to the new image, and make sure to
686+
// respect scaling and remember that CGImage's Y origin is
687+
// bottom-left.
688+
CGFloat scale = self.window.backingScaleFactor;
689+
size_t oldWidth = CGImageGetWidth(oldImage) / scale;
690+
size_t oldHeight = CGImageGetHeight(oldImage) / scale;
691+
CGFloat newHeight = newRect.size.height;
692+
NSRect imageRect = NSMakeRect(0, newHeight - oldHeight, (CGFloat)oldWidth, (CGFloat)oldHeight);
693+
694+
CGContextDrawImage(cgContext, imageRect, oldImage);
695+
CGImageRelease(oldImage);
696+
CGContextRestoreGState(cgContext);
697+
}
698+
}
699+
700+
// Now issue the batched draw commands
701+
if ([drawData count] != 0) {
702+
[NSGraphicsContext saveGraphicsState];
703+
NSGraphicsContext.currentContext = [NSGraphicsContext graphicsContextWithCGContext:cgContext flipped:self.flipped];
704+
id data;
705+
NSEnumerator *e = [drawData objectEnumerator];
706+
while ((data = [e nextObject]))
707+
[self batchDrawData:data];
708+
[drawData removeAllObjects];
709+
[NSGraphicsContext restoreGraphicsState];
710+
}
711+
712+
CGImageRef contentsImage = CGBitmapContextCreateImage(cgContext);
713+
self.layer.contents = (id)contentsImage;
714+
CGImageRelease(contentsImage);
715+
}
716+
609717
- (void)drawRect:(NSRect)rect
610718
{
611719
NSGraphicsContext *context = [NSGraphicsContext currentContext];
@@ -653,7 +761,25 @@ - (void)drawRect:(NSRect)rect
653761

654762
- (void)performBatchDrawWithData:(NSData *)data
655763
{
656-
if (cgLayerEnabled && drawData.count == 0 && [self getCGContext]) {
764+
if (cgBufferDrawEnabled) {
765+
// We batch up all the commands and actually perform the draw at
766+
// updateLayer. The reason is that right now MacVim has a lot of
767+
// different paths that could change the view size (zoom, user resizing
768+
// from either dragging border or another program, Cmd-+/- to change
769+
// font size, fullscreen, etc). Those different paths don't currently
770+
// have a consistent order of operation of (Vim or MacVim go first), so
771+
// sometimes Vim gets updated and issue a batch draw first, but
772+
// sometimes MacVim gets notified first (e.g. when window is resized).
773+
// If frame size has changed we need to call updateCGContext but we
774+
// can't do it here because of the order of operation issue. That's why
775+
// we wait till updateLayer to do it where everything has already been
776+
// done and settled.
777+
//
778+
// Note: Should probably refactor the different ways window size could
779+
// be changed and unify them instead of the status quo of spaghetti.
780+
[drawData addObject:data];
781+
[self setNeedsDisplay:YES];
782+
} else if (cgLayerEnabled && drawData.count == 0 && [self getCGContext]) {
657783
[cgLayerLock lock];
658784
[self batchDrawData:data];
659785
[cgLayerLock unlock];
@@ -669,6 +795,9 @@ - (void)performBatchDrawWithData:(NSData *)data
669795

670796
- (void)setCGLayerEnabled:(BOOL)enabled
671797
{
798+
if (cgContext)
799+
return;
800+
672801
cgLayerEnabled = enabled;
673802

674803
if (!cgLayerEnabled)
@@ -1491,7 +1620,18 @@ - (void)drawString:(const UniChar *)chars length:(UniCharCount)length
14911620

14921621
- (void)scrollRect:(NSRect)rect lineCount:(int)count
14931622
{
1494-
if (cgLayerEnabled) {
1623+
if (cgContext) {
1624+
NSRect fromRect = NSOffsetRect(self.bounds, 0, -count * cellSize.height);
1625+
NSRect toRect = NSOffsetRect(rect, 0, -count * cellSize.height);
1626+
CGContextSaveGState(cgContext);
1627+
CGContextClipToRect(cgContext, toRect);
1628+
CGContextSetBlendMode(cgContext, kCGBlendModeCopy);
1629+
CGImageRef contentsImage = CGBitmapContextCreateImage(cgContext);
1630+
CGContextDrawImage(cgContext, fromRect, contentsImage);
1631+
CGImageRelease(contentsImage);
1632+
CGContextRestoreGState(cgContext);
1633+
[self setNeedsDisplayCGLayerInRect:toRect];
1634+
} else if (cgLayerEnabled) {
14951635
CGContextRef context = [self getCGContext];
14961636
int yOffset = count * cellSize.height;
14971637
NSRect clipRect = rect;

src/MacVim/MMVimView.m

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ - (void)dealloc
189189

190190
- (BOOL)isOpaque
191191
{
192-
return YES;
192+
return textView.defaultBackgroundColor.alphaComponent == 1;
193193
}
194194

195195
- (void)drawRect:(NSRect)rect
@@ -511,6 +511,7 @@ - (void)setDefaultColorsBackground:(NSColor *)back foreground:(NSColor *)fore
511511
MMScroller *sb = [scrollbars objectAtIndex:i];
512512
[sb setNeedsDisplay:YES];
513513
}
514+
[self setNeedsDisplay:YES];
514515
}
515516

516517

src/MacVim/MMWindowController.m

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,7 @@ - (void)setDefaultColorsBackground:(NSColor *)back foreground:(NSColor *)fore
543543
[decoratedWindow setOpaque:isOpaque];
544544
if (fullScreenWindow)
545545
[fullScreenWindow setOpaque:isOpaque];
546+
[decoratedWindow setBackgroundColor:back];
546547

547548
[vimView setDefaultColorsBackground:back foreground:fore];
548549
}
@@ -1543,11 +1544,17 @@ - (void)updateTablineSeparator
15431544
BOOL windowTextured = ([decoratedWindow styleMask] &
15441545
NSWindowStyleMaskTexturedBackground) != 0;
15451546
BOOL hideSeparator = NO;
1546-
1547-
if (fullScreenEnabled || tabBarVisible)
1547+
1548+
if (floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_10) {
1549+
// The tabline separator is mostly an old feature and not necessary
1550+
// modern macOS versions.
15481551
hideSeparator = YES;
1549-
else
1550-
hideSeparator = toolbarHidden && !windowTextured;
1552+
} else {
1553+
if (fullScreenEnabled || tabBarVisible)
1554+
hideSeparator = YES;
1555+
else
1556+
hideSeparator = toolbarHidden && !windowTextured;
1557+
}
15511558

15521559
[self hideTablineSeparator:hideSeparator];
15531560
}

src/MacVim/MacVim.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@
4545
#ifndef NSAppKitVersionNumber10_12
4646
# define NSAppKitVersionNumber10_12 1504
4747
#endif
48+
#ifndef NSAppKitVersionNumber10_13
49+
# define NSAppKitVersionNumber10_13 1561
50+
#endif
4851

4952
#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12
5053
// Deprecated constants in 10.12 SDK

src/MacVim/Miscellaneous.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ extern NSString *MMNativeFullScreenKey;
5353
extern NSString *MMUseMouseTimeKey;
5454
extern NSString *MMFullScreenFadeTimeKey;
5555
extern NSString *MMUseCGLayerAlwaysKey;
56+
extern NSString *MMBufferedDrawingKey;
5657

5758

5859
// Enum for MMUntitledWindowKey
@@ -156,3 +157,4 @@ NSArray *normalizeFilenames(NSArray *filenames);
156157

157158
BOOL shouldUseYosemiteTabBarStyle();
158159
BOOL shouldUseMojaveTabBarStyle();
160+
BOOL shouldUseBufferedDrawing();

src/MacVim/Miscellaneous.m

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
NSString *MMUseMouseTimeKey = @"MMUseMouseTime";
5050
NSString *MMFullScreenFadeTimeKey = @"MMFullScreenFadeTime";
5151
NSString *MMUseCGLayerAlwaysKey = @"MMUseCGLayerAlways";
52+
NSString *MMBufferedDrawingKey = @"MMBufferedDrawing";
5253

5354

5455

@@ -316,3 +317,14 @@ - (NSInteger)tag
316317
#endif
317318
return false;
318319
}
320+
321+
BOOL
322+
shouldUseBufferedDrawing()
323+
{
324+
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14
325+
if (@available(macos 10.14, *)) {
326+
return YES;
327+
}
328+
#endif
329+
return NO;
330+
}

0 commit comments

Comments
 (0)