Make the Core Text renderer faster

This change reworks `MMCoreTextView` to keep track of the state of the
screen instead of drawing to the screen or to an image. This lets
`drawRect:` draw any part of the view at any time, as needed.

This change came about when the old strategy stopped working: The old
strategy calls `drawRect:` for the entire view to handle any draw
command from the backend, but drew only the changes on top of the old
content of the view. This did not work in new versions of macOS that use
layers, because `drawRect:` is now expected to fill the entire rect with
new content. If it doesn't, the rest of the view will just contain
garbage. bbad3edf5a worked around this
issue by adding an intermediate CGImage which was preserved between
draws. This fixed the problem but made rendering slower.

With the change, the intermediate image is no longer needed and
rendering is much faster overall, which resolves #796.

As part of this change, font substitution is now handled by Core Text,
which changes which fallback fonts are used in some cases but matches
other macOS apps.
This commit is contained in:
Sidney San Martín
2020-12-03 12:06:02 -05:00
parent 96a68b5874
commit dba6293677
14 changed files with 429 additions and 1217 deletions
-2
View File
@@ -248,8 +248,6 @@ fsEventCallback(ConstFSEventStreamRef streamRef,
[NSNumber numberWithBool:NO], MMSuppressTerminationAlertKey,
[NSNumber numberWithBool:YES], MMNativeFullScreenKey,
[NSNumber numberWithDouble:0.25], MMFullScreenFadeTimeKey,
[NSNumber numberWithBool:NO], MMUseCGLayerAlwaysKey,
@(shouldUseBufferedDrawing()), MMBufferedDrawingKey,
[NSNumber numberWithBool:YES], MMShareFindPboardKey,
nil];
+3 -35
View File
@@ -31,42 +31,13 @@
BOOL antialias;
BOOL ligatures;
BOOL thinStrokes;
BOOL drawPending;
NSMutableArray *drawData;
MMTextViewHelper *helper;
unsigned maxlen;
CGGlyph *glyphs;
CGPoint *positions;
NSMutableArray *fontCache;
// Issue draws onto an CGImage that caches the drawn results instead of
// directly in drawRect:. This is the default behavior in cases where simply
// drawing incrementally in drawRect: doesn't work. Those cases are:
// 1. Non-native fullscreen
// 2. 10.14+ (views are always layer-backed which means the view buffer will
// be cleared and we can't incrementally draw in drawRect:)
//
// This can be configured by setting MMBufferedDrawingKey in user defaults.
BOOL cgBufferDrawEnabled;
BOOL cgBufferDrawNeedsUpdateContext;
CGContextRef cgContext;
NSMutableDictionary<NSNumber *, NSFont *> *fontVariants;
NSMutableSet<NSString *> *characterStrings;
NSMutableDictionary<NSNumber *,NSCache<NSString *,id> *> *characterLines;
// *Deprecated*
// Draw onto a CGLayer instead of lazily updating the view's buffer in
// drawRect: which is error-prone and relying on undocumented behaviors
// (that the OS will preserve the old buffer).
//
// This is deprecated. Use cgBufferDrawEnabled instead which is more
// efficient.
//
// This can be configured by setting MMUseCGLayerAlwaysKey in user defaults.
BOOL cgLayerEnabled;
CGLayerRef cgLayer;
CGContextRef cgLayerContext;
NSLock *cgLayerLock;
// These are used in MMCoreTextView+ToolTip.m
id trackingRectOwner_; // (not retained)
void *trackingRectUserData_;
@@ -113,13 +84,10 @@
- (BOOL)convertPoint:(NSPoint)point toRow:(int *)row column:(int *)column;
- (NSRect)rectForRow:(int)row column:(int)column numRows:(int)nr
numColumns:(int)nc;
- (void)setCGLayerEnabled:(BOOL)enabled;
- (BOOL)getCGLayerEnabled;
//
// NSTextView methods
//
- (void)setFrameSize:(NSSize)newSize;
- (void)keyDown:(NSEvent *)event;
- (void)insertText:(id)string;
- (void)doCommandBySelector:(SEL)selector;
+399 -1099
View File
File diff suppressed because it is too large Load Diff
-3
View File
@@ -34,9 +34,6 @@
// Controls the speed of the fade in and out.
double fadeTime;
double fadeReservationTime;
// For pre-10.14 we manually sets CGLayer mode, so need to remember the original state
BOOL origCGLayerEnabled;
}
- (MMFullScreenWindow *)initWithWindow:(NSWindow *)t view:(MMVimView *)v
-10
View File
@@ -113,8 +113,6 @@ enum {
fadeTime = MIN(fadeTime, 0.5 * (kCGMaxDisplayReservationInterval - 1));
fadeReservationTime = 2.0 * fadeTime + 1;
origCGLayerEnabled = NO;
return self;
}
@@ -174,11 +172,6 @@ enum {
oldPosition = [view frame].origin;
[view removeFromSuperviewWithoutNeedingDisplay];
if (floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_12) {
// This shouldn't do much in 10.14+.
origCGLayerEnabled = [[view textView] getCGLayerEnabled];
[[view textView] setCGLayerEnabled:YES];
}
[[self contentView] addSubview:view];
[self setInitialFirstResponder:[view textView]];
@@ -295,9 +288,6 @@ enum {
[view setFrameOrigin:oldPosition];
[self close];
if (floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_12)
[[view textView] setCGLayerEnabled:origCGLayerEnabled];
// Set the text view to initial first responder, otherwise the 'plus'
// button on the tabline steals the first responder status.
[target setInitialFirstResponder:[view textView]];
-2
View File
@@ -73,6 +73,4 @@
// NOT IMPLEMENTED (only in Core Text renderer)
- (void)deleteSign:(NSString *)signName;
- (void)setToolTipAtMousePoint:(NSString *)string;
- (void)setCGLayerEnabled:(BOOL)enabled;
- (BOOL)getCGLayerEnabled;
@end
-10
View File
@@ -522,16 +522,6 @@
// ONLY in Core Text!
}
- (void)setCGLayerEnabled:(BOOL)enabled
{
// ONLY in Core Text!
}
- (BOOL)getCGLayerEnabled
{
return NO;
}
- (BOOL)isOpaque
{
return NO;
+1
View File
@@ -18,6 +18,7 @@
#define GREEN(argb) (((argb>>8) & 0xff)/255.0f)
#define RED(argb) (((argb>>16) & 0xff)/255.0f)
#define ALPHA(argb) (((argb>>24) & 0xff)/255.0f)
#define COMPONENTS(argb) ((CGFloat[]){RED(argb), GREEN(argb), BLUE(argb), ALPHA(argb)})
@interface MMTextViewHelper : NSObject {
+16 -10
View File
@@ -170,6 +170,7 @@
// on whether the tabline separator is visible or not.
NSView *contentView = [win contentView];
[contentView setAutoresizesSubviews:YES];
contentView.wantsLayer = YES;
vimView = [[MMVimView alloc] initWithFrame:[contentView frame]
vimController:vimController];
@@ -205,15 +206,6 @@
if ([win respondsToSelector:@selector(_setContentHasShadow:)])
[win _setContentHasShadow:NO];
if (!(styleMask & NSWindowStyleMaskTitled)) {
// In the no titlebar mode (aka borderless), we need to set CGLayer
// mode since otherwise the legacy renderer would not render properly.
// For more reference see MMFullscreenWindow's enterFullscreen:
// This shouldn't do much in 10.14+.
if (floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_12)
[[vimView textView] setCGLayerEnabled:YES];
}
#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7)
// Building on Mac OS X 10.7 or greater.
@@ -652,7 +644,7 @@
if (fullScreenWindow)
[fullScreenWindow setOpaque:isOpaque];
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400
if (@available(macos 10.14, *)) {
// We usually don't really need to change the background color of the
// window, but in 10.14+ we switched to using layer-backed drawing.
@@ -792,6 +784,20 @@
// Do it last so whatever resizing we have done above will take effect
// immediate too instead of waiting till next frame.
[vimView finishPlaceScrollbars];
// Work around a bug which affects macOS 10.14 and older.
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101500
if (@available(macos 10.15, *)) {
} else
#endif
{
// Ensure that the app waits until the next frame to commit the current
// CATransaction. Without this, layer-backed views display as soon as
// the thread returns to the event loop, potentially drawing *many*
// times for a single screen update. The app correctly waits to draw
// when a window needs display, so mark the window as needing display.
self.window.viewsNeedDisplay = YES;
}
}
- (void)showTabBar:(BOOL)on
+2
View File
@@ -58,6 +58,7 @@
# define NSAlertStyleInformational NSInformationalAlertStyle
# define NSAlertStyleWarning NSWarningAlertStyle
# define NSCompositingOperationSourceOver NSCompositeSourceOver
# define NSCompositingOperationDifference NSCompositeDifference
# define NSControlSizeRegular NSRegularControlSize
# define NSEventModifierFlagCapsLock NSAlphaShiftKeyMask
# define NSEventModifierFlagCommand NSCommandKeyMask
@@ -367,6 +368,7 @@ extern NSString *VimFindPboardType;
@interface NSColor (MMExtras)
@property(readonly) unsigned argbInt;
+ (NSColor *)colorWithRgbInt:(unsigned)rgb;
+ (NSColor *)colorWithArgbInt:(unsigned)argb;
@end
+8
View File
@@ -157,6 +157,14 @@ debugStringForMessageQueue(NSArray *queue)
@implementation NSColor (MMExtras)
- (unsigned)argbInt {
CGFloat rf, gf, bf, af;
[[self colorUsingColorSpace:NSColorSpace.deviceRGBColorSpace]
getRed:&rf green:&gf blue:&bf alpha:&af];
unsigned r = rf * 255, g = gf * 255, b = bf * 255, a = af*255;
return a<<24 | r<<16 | g<<8 | b;
}
+ (NSColor *)colorWithRgbInt:(unsigned)rgb
{
float r = ((rgb>>16) & 0xff)/255.0f;
-3
View File
@@ -55,8 +55,6 @@ extern NSString *MMSuppressTerminationAlertKey;
extern NSString *MMNativeFullScreenKey;
extern NSString *MMUseMouseTimeKey;
extern NSString *MMFullScreenFadeTimeKey;
extern NSString *MMUseCGLayerAlwaysKey;
extern NSString *MMBufferedDrawingKey;
// Enum for MMUntitledWindowKey
@@ -168,6 +166,5 @@ NSArray *normalizeFilenames(NSArray *filenames);
BOOL shouldUseYosemiteTabBarStyle();
BOOL shouldUseMojaveTabBarStyle();
BOOL shouldUseBufferedDrawing();
int getCurrentAppearance(NSAppearance *appearance);
-14
View File
@@ -51,8 +51,6 @@ NSString *MMSuppressTerminationAlertKey = @"MMSuppressTerminationAlert";
NSString *MMNativeFullScreenKey = @"MMNativeFullScreen";
NSString *MMUseMouseTimeKey = @"MMUseMouseTime";
NSString *MMFullScreenFadeTimeKey = @"MMFullScreenFadeTime";
NSString *MMUseCGLayerAlwaysKey = @"MMUseCGLayerAlways";
NSString *MMBufferedDrawingKey = @"MMBufferedDrawing";
@@ -326,18 +324,6 @@ shouldUseMojaveTabBarStyle()
return false;
}
BOOL
shouldUseBufferedDrawing()
{
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14
if (@available(macos 10.14, *)) {
return YES;
}
#endif
return NO;
}
int
getCurrentAppearance(NSAppearance *appearance){
int flag = 0; // for macOS 10.13 or eariler always return 0;
-29
View File
@@ -19,9 +19,6 @@
#include "vim.h"
#ifdef FEAT_GUI_MACVIM
static void redraw_for_ligatures(win_T *wp);
#endif
static int scrolljump_value(void);
static int check_top_offset(void);
static void curs_rows(win_T *wp);
@@ -162,29 +159,6 @@ redraw_for_cursorline(win_T *wp)
}
}
#ifdef FEAT_GUI_MACVIM
/*
* Redraw when 'macligatures' is set.
* This is basically the same as when 'cursorline'
* or 'relativenumber' is set but unconditional.
*/
static void
redraw_for_ligatures(wp)
win_T *wp;
{
/* Only if ligatures are on but neither
* 'cursorline' nor 'relativenumber'.
*/
if (p_macligatures
&& (wp->w_p_rnu == 0
#ifdef FEAT_SYN_HL
&& wp->w_p_cul == 0
#endif
))
redraw_win_later(wp, CLEAR);
}
#endif
/*
* Update curwin->w_topline and redraw if necessary.
* Used to update the screen before printing a message.
@@ -809,9 +783,6 @@ curs_rows(win_T *wp)
}
redraw_for_cursorline(curwin);
#ifdef FEAT_GUI_MACVIM
redraw_for_ligatures(curwin);
#endif
wp->w_valid |= VALID_CROW|VALID_CHEIGHT;
}