Skip to content
Snippets Groups Projects
Commit 87352a1d authored by George Nachman's avatar George Nachman
Browse files

Implement a really damn elegant solution to subpixel antialiasing in the GPU...

Implement a really damn elegant solution to subpixel antialiasing in the GPU renderer, if I don't say so myself.

- Add a frame cursor renderer. Pass screen scale through to all transient states.
- Use a single value for gridsize between frameData and perFrameState
- Subpixel color channels are completely independent. Change models to be keyed on a pair of colors, not a combo of six.  Get bilinear interpolation working! YAY!
- Refactor here and there
- Move inputs to transient state into a new class so it's easier to pass new data to renderers' transient states
- Fork the text fragment shader into a blending and nonblending version
- Draw background image to a temporary texture so the text renderer can sample its background color to do subpixel aa
- Alpha blend default background color over background image
parent 5f892dc5
No related branches found
No related tags found
No related merge requests found
Showing
with 522 additions and 262 deletions
Loading
Loading
@@ -1250,6 +1250,9 @@
A60BD9191B3F5D76007D7F11 /* OpenDirectory.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A60BD9181B3F5D76007D7F11 /* OpenDirectory.framework */; };
A60D85A81A3A8105003AEE22 /* NSPasteboard+iTerm.h in Headers */ = {isa = PBXBuildFile; fileRef = A60D85A61A3A8105003AEE22 /* NSPasteboard+iTerm.h */; };
A60D85A91A3A8105003AEE22 /* NSPasteboard+iTerm.h in Headers */ = {isa = PBXBuildFile; fileRef = A60D85A61A3A8105003AEE22 /* NSPasteboard+iTerm.h */; };
A60EBD171FAED952008C7F5B /* iTermCopyBackground.metal in Sources */ = {isa = PBXBuildFile; fileRef = A63819D21FAECE6800A9EF9E /* iTermCopyBackground.metal */; };
A60EBD181FAED953008C7F5B /* iTermCopyBackground.metal in Sources */ = {isa = PBXBuildFile; fileRef = A63819D21FAECE6800A9EF9E /* iTermCopyBackground.metal */; };
A60EBD191FAED954008C7F5B /* iTermCopyBackground.metal in Sources */ = {isa = PBXBuildFile; fileRef = A63819D21FAECE6800A9EF9E /* iTermCopyBackground.metal */; };
A6184F891BAB3ED70088EF3C /* ColorPicker.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A6184F881BAB3ED70088EF3C /* ColorPicker.framework */; };
A6184F8A1BAB3ED70088EF3C /* ColorPicker.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A6184F881BAB3ED70088EF3C /* ColorPicker.framework */; };
A6184F8B1BAB3ED70088EF3C /* ColorPicker.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A6184F881BAB3ED70088EF3C /* ColorPicker.framework */; };
Loading
Loading
@@ -1339,6 +1342,8 @@
A6374A251DEA0929000A136F /* PTYTextViewTest-golden-testUnderline.png in Resources */ = {isa = PBXBuildFile; fileRef = A6374A241DEA0929000A136F /* PTYTextViewTest-golden-testUnderline.png */; };
A6374A281DEE941F000A136F /* iTermPromptOnCloseReason.h in Headers */ = {isa = PBXBuildFile; fileRef = A6374A261DEE941F000A136F /* iTermPromptOnCloseReason.h */; };
A6374A291DEE941F000A136F /* iTermPromptOnCloseReason.m in Sources */ = {isa = PBXBuildFile; fileRef = A6374A271DEE941F000A136F /* iTermPromptOnCloseReason.m */; };
A63819D01FAECD3F00A9EF9E /* iTermCopyBackgroundRenderer.h in Headers */ = {isa = PBXBuildFile; fileRef = A63819CE1FAECD3F00A9EF9E /* iTermCopyBackgroundRenderer.h */; };
A63819D11FAECD3F00A9EF9E /* iTermCopyBackgroundRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = A63819CF1FAECD3F00A9EF9E /* iTermCopyBackgroundRenderer.m */; };
A63BA39518A9CB43002BE075 /* iTermSelection.h in Headers */ = {isa = PBXBuildFile; fileRef = A63BA39318A9CB43002BE075 /* iTermSelection.h */; };
A63BA39F18B27B92002BE075 /* iTermTextExtractor.h in Headers */ = {isa = PBXBuildFile; fileRef = A63BA39D18B27B92002BE075 /* iTermTextExtractor.h */; };
A63F4095183B398C003A6A6D /* PTYNoteViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = A63F4093183B398C003A6A6D /* PTYNoteViewController.h */; };
Loading
Loading
@@ -3380,6 +3385,9 @@
A6374A241DEA0929000A136F /* PTYTextViewTest-golden-testUnderline.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "PTYTextViewTest-golden-testUnderline.png"; path = "tests/Goldens/PTYTextViewTest-golden-testUnderline.png"; sourceTree = "<group>"; };
A6374A261DEE941F000A136F /* iTermPromptOnCloseReason.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iTermPromptOnCloseReason.h; sourceTree = "<group>"; };
A6374A271DEE941F000A136F /* iTermPromptOnCloseReason.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = iTermPromptOnCloseReason.m; sourceTree = "<group>"; };
A63819CE1FAECD3F00A9EF9E /* iTermCopyBackgroundRenderer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = iTermCopyBackgroundRenderer.h; path = Metal/Renderers/iTermCopyBackgroundRenderer.h; sourceTree = "<group>"; };
A63819CF1FAECD3F00A9EF9E /* iTermCopyBackgroundRenderer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = iTermCopyBackgroundRenderer.m; path = Metal/Renderers/iTermCopyBackgroundRenderer.m; sourceTree = "<group>"; };
A63819D21FAECE6800A9EF9E /* iTermCopyBackground.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; name = iTermCopyBackground.metal; path = Metal/Shaders/iTermCopyBackground.metal; sourceTree = "<group>"; };
A63BA39318A9CB43002BE075 /* iTermSelection.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = iTermSelection.h; sourceTree = "<group>"; tabWidth = 4; };
A63BA39418A9CB43002BE075 /* iTermSelection.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.objc; path = iTermSelection.m; sourceTree = "<group>"; tabWidth = 4; };
A63BA39D18B27B92002BE075 /* iTermTextExtractor.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = iTermTextExtractor.h; sourceTree = "<group>"; tabWidth = 4; };
Loading
Loading
@@ -5475,6 +5483,7 @@
A67960C11F81FCA5008A42BC /* iTermCursorRenderer.metal */,
A67960BB1F81FCA5008A42BC /* iTermMark.metal */,
A67960BF1F81FCA5008A42BC /* iTermText.metal */,
A63819D21FAECE6800A9EF9E /* iTermCopyBackground.metal */,
);
name = Shaders;
sourceTree = "<group>";
Loading
Loading
@@ -5498,6 +5507,8 @@
A67960A21F81FC9C008A42BC /* iTermCursorRenderer.m */,
A679609E1F81FC9C008A42BC /* iTermMarkRenderer.m */,
A67960A11F81FC9C008A42BC /* iTermTextRenderer.mm */,
A63819CE1FAECD3F00A9EF9E /* iTermCopyBackgroundRenderer.h */,
A63819CF1FAECD3F00A9EF9E /* iTermCopyBackgroundRenderer.m */,
);
name = Renderers;
sourceTree = "<group>";
Loading
Loading
@@ -7118,6 +7129,7 @@
A667193D1DCE36C3000CE608 /* iTermHotkeyPreferencesModel.h in Headers */,
A667193E1DCE36C3000CE608 /* NSLocale+iTerm.h in Headers */,
A667193F1DCE36C3000CE608 /* iTermDirectoryTree.h in Headers */,
A63819D01FAECD3F00A9EF9E /* iTermCopyBackgroundRenderer.h in Headers */,
A623D9471F8972750011F8C3 /* iTermCallWithTimeout.h in Headers */,
A66719401DCE36C3000CE608 /* iTermOpenQuicklyCommands.h in Headers */,
A62A1F761E711BC000363EE9 /* iTermHelpMessageViewController.h in Headers */,
Loading
Loading
@@ -8619,6 +8631,7 @@
A67960FE1F8202C2008A42BC /* iTermText.metal in Sources */,
A67960FB1F8202C2008A42BC /* iTermCursorGuide.metal in Sources */,
A67960F91F8202C2008A42BC /* iTermBadge.metal in Sources */,
A60EBD191FAED954008C7F5B /* iTermCopyBackground.metal in Sources */,
A67960F81F8202C2008A42BC /* iTermBackgroundImage.metal in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Loading
Loading
@@ -8629,6 +8642,7 @@
files = (
A67960F51F8202C1008A42BC /* iTermMark.metal in Sources */,
A67960F01F8202C1008A42BC /* iTermBackgroundImage.metal in Sources */,
A60EBD181FAED953008C7F5B /* iTermCopyBackground.metal in Sources */,
A68AC8F61F0823100023A216 /* iTermFindOnPageHelperTest.m in Sources */,
90A1E13B186F9EA4003EC3E8 /* AppleScriptTest.m in Sources */,
A6C763FE1B45C72F00E3C992 /* iTermTests.m in Sources */,
Loading
Loading
@@ -8665,6 +8679,7 @@
A67960EE1F8202C0008A42BC /* iTermText.metal in Sources */,
A67960EB1F8202C0008A42BC /* iTermCursorGuide.metal in Sources */,
A67960E91F8202C0008A42BC /* iTermBadge.metal in Sources */,
A60EBD171FAED952008C7F5B /* iTermCopyBackground.metal in Sources */,
A67960E81F8202C0008A42BC /* iTermBackgroundImage.metal in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Loading
Loading
@@ -8700,6 +8715,7 @@
A65EC0301F31800300AC0A6B /* iTermUpdateCadenceController.m in Sources */,
A67960D51F81FCBB008A42BC /* iTermCursorRenderer.m in Sources */,
A66719661DCE3772000CE608 /* iTermWebSocketFrame.m in Sources */,
A63819D11FAECD3F00A9EF9E /* iTermCopyBackgroundRenderer.m in Sources */,
A65EC0341F3181E700AC0A6B /* NSTimer+iTerm.m in Sources */,
A623D9481F8972750011F8C3 /* iTermCallWithTimeout.m in Sources */,
A66EF82D1EF59CFC0005891A /* iTermRateLimitedUpdate.m in Sources */,
Loading
Loading
@@ -62,8 +62,7 @@
buildConfiguration = "Development"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
enableAddressSanitizer = "YES"
enableUBSanitizer = "YES"
disableMainThreadChecker = "YES"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
Loading
Loading
@@ -87,11 +86,6 @@
value = "YES"
isEnabled = "YES">
</AdditionalOption>
<AdditionalOption
key = "MallocScribble"
value = ""
isEnabled = "YES">
</AdditionalOption>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
Loading
Loading
Loading
Loading
@@ -9,22 +9,39 @@ extern const CGFloat BOTTOM_MARGIN;
 
@class iTermMetalCellRendererTransientState;
 
@interface iTermCellRenderConfiguration : iTermRenderConfiguration
@property (nonatomic, readonly) CGSize cellSize;
@property (nonatomic, readonly) VT100GridSize gridSize;
// This determines how subpixel antialiasing is done. It's unfortunate that one
// renderer's needs affect the configuration for so many renderers. I need to
// find a better way to pass this info around. The problem is that it's needed
// early on--before the transient state is created--in order for the text
// renderer to be able to set its fragment function.
@property (nonatomic, readonly) BOOL usingIntermediatePass;
- (instancetype)initWithViewportSize:(vector_uint2)viewportSize scale:(CGFloat)scale NS_UNAVAILABLE;
- (instancetype)initWithViewportSize:(vector_uint2)viewportSize
scale:(CGFloat)scale
cellSize:(CGSize)cellSize
gridSize:(VT100GridSize)gridSize
usingIntermediatePass:(BOOL)usingIntermediatePass NS_DESIGNATED_INITIALIZER;
@end
@protocol iTermMetalCellRenderer<NSObject>
 
- (void)drawWithRenderEncoder:(id<MTLRenderCommandEncoder>)renderEncoder
transientState:(__kindof iTermMetalCellRendererTransientState *)transientState;
 
- (void)createTransientStateForViewportSize:(vector_uint2)viewportSize
cellSize:(CGSize)cellSize
gridSize:(VT100GridSize)gridSize
commandBuffer:(id<MTLCommandBuffer>)commandBuffer
completion:(void (^)(__kindof iTermMetalCellRendererTransientState *transientState))completion;
- (void)createTransientStateForCellConfiguration:(iTermCellRenderConfiguration *)configuration
commandBuffer:(id<MTLCommandBuffer>)commandBuffer
completion:(void (^)(__kindof iTermMetalRendererTransientState *transientState))completion;
 
@end
 
@interface iTermMetalCellRendererTransientState : iTermMetalRendererTransientState
@property (nonatomic, readonly) VT100GridSize gridSize;
@property (nonatomic, readonly) CGSize cellSize;
@property (nonatomic, readonly) __kindof iTermCellRenderConfiguration *cellConfiguration;
@property (nonatomic, readonly) id<MTLBuffer> offsetBuffer;
@property (nonatomic, strong) id<MTLBuffer> pius;
 
Loading
Loading
@@ -33,7 +50,7 @@ extern const CGFloat BOTTOM_MARGIN;
 
@end
 
@interface iTermMetalCellRenderer : iTermMetalRenderer<iTermMetalCellRenderer>
@interface iTermMetalCellRenderer : iTermMetalRenderer
 
- (nullable instancetype)initWithDevice:(id<MTLDevice>)device NS_UNAVAILABLE;
 
Loading
Loading
@@ -50,9 +67,13 @@ extern const CGFloat BOTTOM_MARGIN;
piuElementSize:(size_t)piuElementSize
transientStateClass:(Class)transientStateClass NS_DESIGNATED_INITIALIZER;
 
- (void)createTransientStateForViewportSize:(vector_uint2)viewportSize
commandBuffer:(id<MTLCommandBuffer>)commandBuffer
completion:(void (^)(__kindof iTermMetalRendererTransientState *transientState))completion NS_UNAVAILABLE;
- (void)createTransientStateForCellConfiguration:(iTermCellRenderConfiguration *)configuration
commandBuffer:(id<MTLCommandBuffer>)commandBuffer
completion:(void (^)(__kindof iTermMetalRendererTransientState *transientState))completion;
- (void)createTransientStateForConfiguration:(iTermRenderConfiguration *)configuration
commandBuffer:(id<MTLCommandBuffer>)commandBuffer
completion:(void (^)(__kindof iTermMetalRendererTransientState *transientState))completion NS_UNAVAILABLE;
 
@end
 
Loading
Loading
Loading
Loading
@@ -6,9 +6,25 @@ const CGFloat BOTTOM_MARGIN = 2;
 
NS_ASSUME_NONNULL_BEGIN
 
@implementation iTermCellRenderConfiguration
- (instancetype)initWithViewportSize:(vector_uint2)viewportSize
scale:(CGFloat)scale
cellSize:(CGSize)cellSize
gridSize:(VT100GridSize)gridSize
usingIntermediatePass:(BOOL)usingIntermediatePass {
self = [super initWithViewportSize:viewportSize scale:scale];
if (self) {
_cellSize = cellSize;
_gridSize = gridSize;
_usingIntermediatePass = usingIntermediatePass;
}
return self;
}
@end
@interface iTermMetalCellRendererTransientState()
@property (nonatomic, readwrite) VT100GridSize gridSize;
@property (nonatomic, readwrite) CGSize cellSize;
@property (nonatomic, readwrite) id<MTLBuffer> offsetBuffer;
@property (nonatomic, readwrite) size_t piuElementSize;
@end
Loading
Loading
@@ -16,15 +32,19 @@ NS_ASSUME_NONNULL_BEGIN
@implementation iTermMetalCellRendererTransientState
 
- (void)setPIUValue:(void *)valuePointer coord:(VT100GridCoord)coord {
const size_t index = coord.x + coord.y * self.gridSize.width;
const size_t index = coord.x + coord.y * self.cellConfiguration.gridSize.width;
memcpy(self.pius.contents + index * _piuElementSize, valuePointer, _piuElementSize);
}
 
- (const void *)piuForCoord:(VT100GridCoord)coord {
const size_t index = coord.x + coord.y * self.gridSize.width;
const size_t index = coord.x + coord.y * self.cellConfiguration.gridSize.width;
return self.pius.contents + index * _piuElementSize;
}
 
- (iTermCellRenderConfiguration *)cellConfiguration {
return (iTermCellRenderConfiguration *)self.configuration;
}
@end
 
@implementation iTermMetalCellRenderer {
Loading
Loading
@@ -54,22 +74,18 @@ NS_ASSUME_NONNULL_BEGIN
return _transientStateClass;
}
 
- (void)createTransientStateForViewportSize:(vector_uint2)viewportSize
cellSize:(CGSize)cellSize
gridSize:(VT100GridSize)gridSize
commandBuffer:(nonnull id<MTLCommandBuffer>)commandBuffer completion:(nonnull void (^)(__kindof iTermMetalCellRendererTransientState * _Nonnull))completion {
[super createTransientStateForViewportSize:viewportSize commandBuffer:commandBuffer completion:^(__kindof iTermMetalRendererTransientState * _Nonnull transientState) {
- (void)createTransientStateForCellConfiguration:(iTermCellRenderConfiguration *)configuration
commandBuffer:(id<MTLCommandBuffer>)commandBuffer
completion:(void (^)(__kindof iTermMetalRendererTransientState *transientState))completion {
[super createTransientStateForConfiguration:configuration commandBuffer:commandBuffer completion:^(__kindof iTermMetalRendererTransientState * _Nonnull transientState) {
iTermMetalCellRendererTransientState *tState = transientState;
tState.piuElementSize = _piuElementSize;
tState.gridSize = gridSize;
tState.cellSize = cellSize;
CGSize usableSize = CGSizeMake(tState.viewportSize.x - MARGIN_WIDTH * 2,
tState.viewportSize.y - TOP_MARGIN - BOTTOM_MARGIN);
 
CGSize usableSize = CGSizeMake(tState.cellConfiguration.viewportSize.x - MARGIN_WIDTH * 2,
tState.cellConfiguration.viewportSize.y - TOP_MARGIN - BOTTOM_MARGIN);
vector_float2 offset = {
MARGIN_WIDTH,
fmod(usableSize.height, tState.cellSize.height) + BOTTOM_MARGIN
fmod(usableSize.height, tState.cellConfiguration.cellSize.height) + BOTTOM_MARGIN
};
tState.offsetBuffer = [self.device newBufferWithBytes:&offset
length:sizeof(offset)
Loading
Loading
Loading
Loading
@@ -8,27 +8,38 @@ NS_ASSUME_NONNULL_BEGIN
 
@class iTermMetalRendererTransientState;
 
@interface iTermRenderConfiguration : NSObject
@property (nonatomic, readonly) vector_uint2 viewportSize;
@property (nonatomic, readonly) CGFloat scale;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithViewportSize:(vector_uint2)viewportSize
scale:(CGFloat)scale NS_DESIGNATED_INITIALIZER;
@end
@protocol iTermMetalRenderer<NSObject>
 
- (void)drawWithRenderEncoder:(id<MTLRenderCommandEncoder>)renderEncoder
transientState:(__kindof iTermMetalRendererTransientState *)transientState;
 
- (void)createTransientStateForViewportSize:(vector_uint2)viewportSize
commandBuffer:(id<MTLCommandBuffer>)commandBuffer
completion:(void (^)(__kindof iTermMetalRendererTransientState *transientState))completion;
- (void)createTransientStateForConfiguration:(iTermRenderConfiguration *)configuration
commandBuffer:(id<MTLCommandBuffer>)commandBuffer
completion:(void (^)(__kindof iTermMetalRendererTransientState *transientState))completion;
 
@end
 
@interface iTermMetalRendererTransientState : NSObject
@property (nonatomic) vector_uint2 viewportSize;
@property (nonatomic, strong, readonly) __kindof iTermRenderConfiguration *configuration;
@property (nonatomic, strong) id<MTLBuffer> vertexBuffer;
@property (nonatomic, readonly, strong) id<MTLRenderPipelineState> pipelineState;
@property (nonatomic, readonly) BOOL skipRenderer;
@end
 
@interface iTermMetalRenderer : NSObject<iTermMetalRenderer>
@interface iTermMetalRenderer : NSObject
 
@property (nonatomic, readonly) id<MTLDevice> device;
@property (nonatomic, readonly) Class transientStateClass;
@property (nonatomic, copy) NSString *fragmentFunctionName;
 
- (nullable instancetype)initWithDevice:(id<MTLDevice>)device;
 
Loading
Loading
@@ -44,6 +55,9 @@ NS_ASSUME_NONNULL_BEGIN
 
- (id<MTLBuffer>)newQuadOfSize:(CGSize)size;
 
// Things in Metal are randomly upside down for no good reason. So make it easy to flip them back.
- (id<MTLBuffer>)newFlippedQuadOfSize:(CGSize)size;
- (void)drawWithTransientState:(iTermMetalRendererTransientState *)tState
renderEncoder:(id <MTLRenderCommandEncoder>)renderEncoder
numberOfVertices:(NSInteger)numberOfVertices
Loading
Loading
@@ -58,6 +72,10 @@ NS_ASSUME_NONNULL_BEGIN
vertexFunction:(id<MTLFunction>)vertexFunction
fragmentFunction:(id<MTLFunction>)fragmentFunction;
 
- (void)createTransientStateForConfiguration:(iTermRenderConfiguration *)configuration
commandBuffer:(id<MTLCommandBuffer>)commandBuffer
completion:(void (^)(__kindof iTermMetalRendererTransientState *transientState))completion;
@end
 
NS_ASSUME_NONNULL_END
Loading
Loading
@@ -2,17 +2,43 @@
 
#import "iTermShaderTypes.h"
 
@implementation iTermRenderConfiguration
- (instancetype)initWithViewportSize:(vector_uint2)viewportSize scale:(CGFloat)scale {
self = [super init];
if (self) {
_viewportSize = viewportSize;
_scale = scale;
}
return self;
}
@end
@interface iTermMetalRendererTransientState()
@property (nonatomic, readwrite, strong) id<MTLRenderPipelineState> pipelineState;
@property (nonatomic, readwrite) CGFloat scale;
@end
 
@implementation iTermMetalRendererTransientState
- (instancetype)initWithConfiguration:(iTermRenderConfiguration *)configuration {
self = [super init];
if (self) {
_configuration = configuration;
}
return self;
}
- (BOOL)skipRenderer {
return NO;
}
@end
 
@implementation iTermMetalRenderer {
BOOL _blending;
NSString *_vertexFunctionName;
NSString *_fragmentFunctionName;
}
 
- (instancetype)initWithDevice:(id<MTLDevice>)device {
Loading
Loading
@@ -45,21 +71,22 @@
dispatch_once(&onceToken, ^{
defaultLibrary = [_device newDefaultLibrary];
});
id <MTLFunction> textVertexShader = [defaultLibrary newFunctionWithName:_vertexFunctionName];
id <MTLFunction> textFragmentShader = [defaultLibrary newFunctionWithName:_fragmentFunctionName];
id <MTLFunction> vertexShader = [defaultLibrary newFunctionWithName:_vertexFunctionName];
assert(vertexShader);
id <MTLFunction> fragmentShader = [defaultLibrary newFunctionWithName:_fragmentFunctionName];
assert(fragmentShader);
return [self newPipelineWithBlending:_blending
vertexFunction:textVertexShader
fragmentFunction:textFragmentShader];
vertexFunction:vertexShader
fragmentFunction:fragmentShader];
}
 
#pragma mark - Protocol Methods
 
- (void)createTransientStateForViewportSize:(vector_uint2)viewportSize
commandBuffer:(id<MTLCommandBuffer>)commandBuffer
completion:(void (^)(__kindof iTermMetalRendererTransientState * _Nonnull))completion {
iTermMetalRendererTransientState *tState = [[self.transientStateClass alloc] init];
- (void)createTransientStateForConfiguration:(iTermRenderConfiguration *)configuration
commandBuffer:(id<MTLCommandBuffer>)commandBuffer
completion:(void (^)(__kindof iTermMetalRendererTransientState * _Nonnull))completion {
iTermMetalRendererTransientState *tState = [[self.transientStateClass alloc] initWithConfiguration:configuration];
tState.pipelineState = [self newPipelineState];
tState.viewportSize = viewportSize;
completion(tState);
}
 
Loading
Loading
@@ -84,6 +111,20 @@
return [_device newBufferWithBytes:vertices length:sizeof(vertices) options:MTLResourceStorageModeShared];
}
 
- (id<MTLBuffer>)newFlippedQuadOfSize:(CGSize)size {
const iTermVertex vertices[] = {
// Pixel Positions, Texture Coordinates
{ { size.width, 0 }, { 1.f, 1.f } },
{ { 0, 0 }, { 0.f, 1.f } },
{ { 0, size.height }, { 0.f, 0.f } },
{ { size.width, 0 }, { 1.f, 1.f } },
{ { 0, size.height }, { 0.f, 0.f } },
{ { size.width, size.height }, { 1.f, 0.f } },
};
return [_device newBufferWithBytes:vertices length:sizeof(vertices) options:MTLResourceStorageModeShared];
}
- (id<MTLRenderPipelineState>)newPipelineWithBlending:(BOOL)blending
vertexFunction:(id<MTLFunction>)vertexFunction
fragmentFunction:(id<MTLFunction>)fragmentFunction {
Loading
Loading
@@ -181,7 +222,7 @@
atIndex:[key unsignedIntegerValue]];
}];
 
const vector_uint2 viewportSize = tState.viewportSize;
const vector_uint2 viewportSize = tState.configuration.viewportSize;
[renderEncoder setVertexBytes:&viewportSize
length:sizeof(viewportSize)
atIndex:iTermVertexInputIndexViewportSize];
Loading
Loading
Loading
Loading
@@ -11,17 +11,13 @@
 
@interface iTermSubpixelModel : NSObject
 
// The table contains 256 RGBA values that map a reference value to a color value in this model.
// The gray value is a pixel in a non-subpixel-antialiased rendering, while the color value is
// the corresponding color in a subpixel-antialiased glyph in this model. Each combination of
// foreground and background color should have a separate model. Ignore the A, it's always 0.
// Each value is an unsigned short (2 bytes)
// The table contains 256 values that map a reference value to a color value in this channel.
@property (nonatomic, readonly) NSData *table;
@property (nonatomic, readonly) vector_float4 foregroundColor;
@property (nonatomic, readonly) vector_float4 backgroundColor;
@property (nonatomic, readonly) float foregroundColor;
@property (nonatomic, readonly) float backgroundColor;
 
+ (NSUInteger)keyForForegroundColor:(vector_float4)foregroundColor
backgroundColor:(vector_float4)backgroundColor;
+ (NSUInteger)keyForForegroundColor:(float)foregroundColor
backgroundColor:(float)backgroundColor;
 
- (NSUInteger)key;
- (NSString *)dump;
Loading
Loading
@@ -31,26 +27,12 @@
// Builds and keeps a cache of built models that map colors in a black-on-white reference glyph
// to the colors used in a glyph with an arbitrary foreground and background color. Thanks to this
// mapping, we only need black-on-white textures with subpixel antialiasing and the GPU can color
// them in the fragment shader. A color map is 768 bytes which is less memory than a single
// 5x12 pt retina glyph.
//
// Example usage:
// iTermSubpixelModel *model = [builder modelForForegroundColor:f backgroundColor:b];
// NSData *glyphBGRAData = [self drawGlyphForString:s];
//
// In practice, glyphBGRAData would be saved in a texture and the following code would run on the
// GPU, but it's presented as objective C for readability:
//
// for (int i = 0; i < glyphBGRAData.length; i += 4) {
// glyphBGRAData.bytes[i] = model.redTable.bytes[glyphBGRAData.bytes[i]];
// glyphBGRAData.bytes[i+1] = model.greenTable.bytes[glyphBGRAData.bytes[i+1]];
// glyphBGRAData.bytes[i+2] = model.blueTable.bytes[glyphBGRAData.bytes[i+2]];
// }
// them in the fragment shader.
@interface iTermSubpixelModelBuilder : NSObject
 
+ (instancetype)sharedInstance;
 
- (iTermSubpixelModel *)modelForForegoundColor:(vector_float4)foregroundColor
backgroundColor:(vector_float4)backgroundColor;
- (iTermSubpixelModel *)modelForForegoundColor:(float)foregroundComponent
backgroundColor:(float)backgroundComponent;
 
@end
Loading
Loading
@@ -28,22 +28,19 @@ static NSString *const iTermSubpixelModelString = @"O";
NSMutableData *_table;
}
 
+ (NSUInteger)keyForColor:(vector_float4)color {
const NSUInteger r = color.x * 255;
const NSUInteger g = color.y * 255;
const NSUInteger b = color.z * 255;
return (r << 24) | (g << 16) | (b << 8);
+ (NSUInteger)keyForColor:(float)color {
return color * 255;
}
 
+ (NSUInteger)keyForForegroundColor:(vector_float4)foregroundColor
backgroundColor:(vector_float4)backgroundColor {
return ([self keyForColor:foregroundColor] << 32) | [self keyForColor:backgroundColor];
+ (NSUInteger)keyForForegroundColor:(float)foregroundColor
backgroundColor:(float)backgroundColor {
return ([self keyForColor:foregroundColor] << 8) | [self keyForColor:backgroundColor];
}
 
- (instancetype)initWithForegroundColor:(vector_float4)foregroundColor
backgroundColor:(vector_float4)backgroundColor {
- (instancetype)initWithForegroundColor:(float)foregroundColor
backgroundColor:(float)backgroundColor {
if (self) {
_table = [NSMutableData dataWithLength:3 * 256 * sizeof(unsigned char)];
_table = [NSMutableData dataWithLength:256 * sizeof(unsigned char)];
_foregroundColor = foregroundColor;
_backgroundColor = backgroundColor;
}
Loading
Loading
@@ -51,14 +48,10 @@ static NSString *const iTermSubpixelModelString = @"O";
}
 
- (NSString *)description {
return [NSString stringWithFormat:@"<%@: %p fg=(%f, %f, %f) bg=(%f, %f, %f)>",
NSStringFromClass(self.class), self,
_foregroundColor.x,
_foregroundColor.y,
_foregroundColor.z,
_backgroundColor.x,
_backgroundColor.y,
_backgroundColor.z];
return [NSString stringWithFormat:@"<%@: %p fg=%f bg=%f>",
NSStringFromClass(self.class), self,
_foregroundColor,
_backgroundColor];
}
 
- (NSUInteger)key {
Loading
Loading
@@ -69,8 +62,8 @@ static NSString *const iTermSubpixelModelString = @"O";
- (NSString *)dump {
NSMutableArray *array = [NSMutableArray array];
const unsigned char *bytes = (const unsigned char *)_table.bytes;
for (int i = 0; i < 256 * 3; i += 3) {
NSString *s = [NSString stringWithFormat:@"%@ -> (%@, %@, %@)", @(i / 3), @(bytes[i]), @(bytes[i+1]), @(bytes[i+2])];
for (int i = 0; i < 256; i++) {
NSString *s = [NSString stringWithFormat:@"%@ -> (%@)", @(i / 3), @(bytes[i])];
[array addObject:s];
}
return [array componentsJoinedByString:@"\n"];
Loading
Loading
@@ -83,11 +76,13 @@ static NSString *const iTermSubpixelModelString = @"O";
@end
 
@implementation iTermSubpixelModelBuilder {
// Maps the index of a color in the reference image to the color in the reference image.
// An index is 4 * (x + width * y).
// A color is ((r << 16) | (g << 8) | b).
// Maps the index of a color element (which may be red, green or blue) in
// the reference image to the value of that element in the reference image.
// For example, if the first pixel had a blue value of 128, this would contain
// 0->128. The values in this map are unique.
std::unordered_map<int, int> *_indexToReferenceColor;
 
#warning TODO: There's no reason to cache this any more.
NSMutableDictionary<NSNumber *, iTermSubpixelModel *> *_models;
}
 
Loading
Loading
@@ -197,69 +192,59 @@ static NSString *const iTermSubpixelModelString = @"O";
int i = 0;
for (int y = 0; y < iTermSubpixelModelSize.height; y++) {
for (int x = 0; x < iTermSubpixelModelSize.width; x++) {
const int b = bytes[i];
const int g = bytes[i + 1];
const int r = bytes[i + 2];
const int c = ((b << 16) | (g << 8) | r);
if (referenceColors.insert(c).second) {
// The color `c` has not been seen before. Remember it and its index.
(*_indexToReferenceColor)[i] = c;
int value = bytes[i];
for (int j = 0; j < 3; j++) {
if (referenceColors.insert(value).second) {
(*_indexToReferenceColor)[i] = value;
}
i++;
}
i += 4;
// Skip over alpha
i++;
}
}
}
return self;
}
 
- (iTermSubpixelModel *)modelForForegoundColor:(vector_float4)foregroundColor
backgroundColor:(vector_float4)backgroundColor {
NSUInteger key = [iTermSubpixelModel keyForForegroundColor:foregroundColor
backgroundColor:backgroundColor];
- (iTermSubpixelModel *)modelForForegoundColor:(float)foregroundComponent
backgroundColor:(float)backgroundComponent {
NSUInteger key = [iTermSubpixelModel keyForForegroundColor:foregroundComponent
backgroundColor:backgroundComponent];
iTermSubpixelModel *cachedModel = _models[@(key)];
if (cachedModel) {
return cachedModel;
}
 
assert(backgroundColor.w == 1);
NSData *imageData = [iTermSubpixelModelBuilder dataForImageWithForegroundColor:foregroundColor
backgroundColor:backgroundColor];
NSData *imageData = [iTermSubpixelModelBuilder dataForImageWithForegroundColor:simd_make_float4(foregroundComponent, foregroundComponent, foregroundComponent, 1)
backgroundColor:simd_make_float4(backgroundComponent, backgroundComponent, backgroundComponent, 1)];
// Maps a reference color to a model color. We'll go back and fill in the gaps with linear
// interpolations, which is why we use a sorted container. When translating a black-on-white
// render to a color render, these mapping tables let us look up the proper color for a black
// on white sample.
std::map<unsigned char, unsigned char> redMap;
std::map<unsigned char, unsigned char> greenMap;
std::map<unsigned char, unsigned char> blueMap;
std::map<unsigned char, unsigned char> map;
const unsigned char *bytes = (const unsigned char *)imageData.bytes;
for (auto kv : *_indexToReferenceColor) {
auto index = kv.first;
auto color = kv.second;
 
const unsigned char refRed = (color & 0xff);
const unsigned char refGreen = ((color >> 8) & 0xff);
const unsigned char refBlue = ((color >> 16) & 0xff);
const unsigned char ref = (color & 0xff);
const unsigned char model = bytes[index];
 
const unsigned char modelRed = bytes[index + 2];
const unsigned char modelGreen = bytes[index + 1];
const unsigned char modelBlue = bytes[index];
map[ref] = model;
}
 
redMap[refRed] = modelRed;
greenMap[refGreen] = modelGreen;
blueMap[refBlue] = modelBlue;
iTermSubpixelModel *subpixelModel = [[iTermSubpixelModel alloc] initWithForegroundColor:foregroundComponent
backgroundColor:backgroundComponent];
[self interpolateValuesInMap:&map toByteArrayInData:subpixelModel.mutableTable offset:0 stride:1];
if (backgroundComponent == 0) {
NSLog(@"Generated model for %f/%f", foregroundComponent, backgroundComponent);
}
//NSLog(@"Generated model for %f/%f\n%@", foregroundComponent, backgroundComponent, subpixelModel.table);
_models[@(key)] = subpixelModel;
 
iTermSubpixelModel *model = [[iTermSubpixelModel alloc] initWithForegroundColor:foregroundColor
backgroundColor:backgroundColor];
DLog(@"Interpolate red values");
[self interpolateValuesInMap:&redMap toByteArrayInData:model.mutableTable offset:0 stride:3];
DLog(@"Interpolate green values");
[self interpolateValuesInMap:&greenMap toByteArrayInData:model.mutableTable offset:1 stride:3];
DLog(@"Interpolate blue values");
[self interpolateValuesInMap:&blueMap toByteArrayInData:model.mutableTable offset:2 stride:3];
_models[@(key)] = model;
return model;
return subpixelModel;
}
 
- (void)dealloc {
Loading
Loading
@@ -304,7 +289,7 @@ namespace iTerm2 {
for (auto kv : *modelToReferenceMap) {
const int referenceColor = kv.first;
const int modelColor = kv.second;
DLog(@"Reference color %d -> model color %d", referenceColor, modelColor);
// NSLog(@"%d -> %d", referenceColor, modelColor);
 
if (previousModelColor >= 0) {
slope = static_cast<double>(modelColor - previousModelColor) / static_cast<double>(referenceColor - previousReferenceColor);
Loading
Loading
Loading
Loading
@@ -3,18 +3,18 @@
@implementation iTermBackgroundColorRendererTransientState
 
- (NSUInteger)sizeOfNewPIUBuffer {
return sizeof(iTermBackgroundColorPIU) * self.gridSize.width * self.gridSize.height;
return sizeof(iTermBackgroundColorPIU) * self.cellConfiguration.gridSize.width * self.cellConfiguration.gridSize.height;
}
 
- (void)initializePIUBytes:(void *)bytes {
NSInteger i = 0;
vector_float2 cellSize = simd_make_float2(self.cellSize.width, self.cellSize.height);
vector_float2 cellSize = simd_make_float2(self.cellConfiguration.cellSize.width, self.cellConfiguration.cellSize.height);
vector_float4 defaultColor = simd_make_float4(1, 0, 0, 1);
iTermBackgroundColorPIU *pius = (iTermBackgroundColorPIU *)bytes;
for (NSInteger y = 0; y < self.gridSize.height; y++) {
const float rowOffset = (self.gridSize.height - y - 1);
for (NSInteger y = 0; y < self.cellConfiguration.gridSize.height; y++) {
const float rowOffset = (self.cellConfiguration.gridSize.height - y - 1);
vector_float2 gridCoord = simd_make_float2(0, rowOffset);
for (NSInteger x = 0; x < self.gridSize.width; x++) {
for (NSInteger x = 0; x < self.cellConfiguration.gridSize.width; x++) {
gridCoord.x = x;
pius[i].offset = gridCoord * cellSize;
pius[i].color = defaultColor;
Loading
Loading
@@ -52,23 +52,19 @@
return self;
}
 
- (void)createTransientStateForViewportSize:(vector_uint2)viewportSize
cellSize:(CGSize)cellSize
gridSize:(VT100GridSize)gridSize
commandBuffer:(id<MTLCommandBuffer>)commandBuffer
completion:(void (^)(__kindof iTermMetalCellRendererTransientState * _Nonnull))completion {
[_cellRenderer createTransientStateForViewportSize:viewportSize
cellSize:cellSize
gridSize:gridSize
commandBuffer:commandBuffer
completion:^(__kindof iTermMetalCellRendererTransientState * _Nonnull transientState) {
[self initializeTransientState:transientState];
completion(transientState);
}];
- (void)createTransientStateForCellConfiguration:(iTermCellRenderConfiguration *)configuration
commandBuffer:(id<MTLCommandBuffer>)commandBuffer
completion:(void (^)(__kindof iTermMetalRendererTransientState * _Nonnull))completion {
[_cellRenderer createTransientStateForCellConfiguration:configuration
commandBuffer:commandBuffer
completion:^(__kindof iTermMetalCellRendererTransientState * _Nonnull transientState) {
[self initializeTransientState:transientState];
completion(transientState);
}];
}
 
- (void)initializeTransientState:(iTermBackgroundColorRendererTransientState *)tState {
tState.vertexBuffer = [_cellRenderer newQuadOfSize:tState.cellSize];
tState.vertexBuffer = [_cellRenderer newQuadOfSize:tState.cellConfiguration.cellSize];
 
tState.pius = [_cellRenderer.device newBufferWithLength:tState.sizeOfNewPIUBuffer
options:MTLResourceStorageModeShared];
Loading
Loading
@@ -82,7 +78,7 @@
[_cellRenderer drawWithTransientState:tState
renderEncoder:renderEncoder
numberOfVertices:6
numberOfPIUs:tState.gridSize.width * tState.gridSize.height
numberOfPIUs:tState.cellConfiguration.gridSize.width * tState.cellConfiguration.gridSize.height
vertexBuffers:@{ @(iTermVertexInputIndexVertices): tState.vertexBuffer,
@(iTermVertexInputIndexPerInstanceUniforms): tState.pius,
@(iTermVertexInputIndexOffset): tState.offsetBuffer }
Loading
Loading
Loading
Loading
@@ -8,6 +8,9 @@ NS_ASSUME_NONNULL_BEGIN
- (nullable instancetype)initWithDevice:(id<MTLDevice>)device NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
 
// Call this before creating transient state.
- (void)setImage:(NSImage *)image blending:(CGFloat)blending tiled:(BOOL)tiled;
@end
 
NS_ASSUME_NONNULL_END
Loading
Loading
@@ -6,9 +6,17 @@ NS_ASSUME_NONNULL_BEGIN
 
@interface iTermBackgroundImageRendererTransientState : iTermMetalRendererTransientState
@property (nonatomic, strong) id<MTLTexture> texture;
#warning TODO: Add support for blending and tiled modes
@property (nonatomic) CGFloat blending;
@property (nonatomic) BOOL tiled;
@end
 
@implementation iTermBackgroundImageRendererTransientState
- (BOOL)skipRenderer {
return _texture == nil;
}
@end
 
@implementation iTermBackgroundImageRenderer {
Loading
Loading
@@ -17,6 +25,9 @@ NS_ASSUME_NONNULL_BEGIN
// The texture is shared because it tends to get reused. The transient state holds a reference
// to it, so when the image changes, this can be set to a new texture and it should just work.
id<MTLTexture> _texture;
CGFloat _blending;
BOOL _tiled;
}
 
- (nullable instancetype)initWithDevice:(id<MTLDevice>)device {
Loading
Loading
@@ -27,12 +38,16 @@ NS_ASSUME_NONNULL_BEGIN
fragmentFunctionName:@"iTermBackgroundImageFragmentShader"
blending:NO
transientStateClass:[iTermBackgroundImageRendererTransientState class]];
NSImage *image = [NSImage imageNamed:@"background"];
_texture = [_metalRenderer textureFromImage:image];
}
return self;
}
 
- (void)setImage:(NSImage *)image blending:(CGFloat)blending tiled:(BOOL)tiled {
_texture = image ? [_metalRenderer textureFromImage:image] : nil;
_blending = blending;
_tiled = tiled;
}
- (void)drawWithRenderEncoder:(nonnull id<MTLRenderCommandEncoder>)renderEncoder
transientState:(nonnull __kindof iTermMetalRendererTransientState *)transientState {
iTermBackgroundImageRendererTransientState *tState = transientState;
Loading
Loading
@@ -45,21 +60,23 @@ NS_ASSUME_NONNULL_BEGIN
textures:@{ @(iTermTextureIndexPrimary): tState.texture }];
}
 
- (void)createTransientStateForViewportSize:(vector_uint2)viewportSize
commandBuffer:(id<MTLCommandBuffer>)commandBuffer
completion:(void (^)(__kindof iTermMetalRendererTransientState * _Nonnull))completion {
[_metalRenderer createTransientStateForViewportSize:viewportSize
commandBuffer:commandBuffer
completion:^(__kindof iTermMetalRendererTransientState * _Nonnull transientState) {
[self initializeTransientState:transientState];
completion(transientState);
}];
- (void)createTransientStateForConfiguration:(iTermRenderConfiguration *)configuration
commandBuffer:(id<MTLCommandBuffer>)commandBuffer
completion:(void (^)(__kindof iTermMetalRendererTransientState * _Nonnull))completion {
[_metalRenderer createTransientStateForConfiguration:configuration
commandBuffer:commandBuffer
completion:^(__kindof iTermMetalRendererTransientState * _Nonnull transientState) {
[self initializeTransientState:transientState];
completion(transientState);
}];
}
 
- (void)initializeTransientState:(iTermBackgroundImageRendererTransientState *)tState {
tState.texture = _texture;
tState.vertexBuffer = [_metalRenderer newQuadOfSize:CGSizeMake(tState.viewportSize.x,
tState.viewportSize.y)];
tState.blending = _blending;
tState.tiled = _tiled;
tState.vertexBuffer = [_metalRenderer newQuadOfSize:CGSizeMake(tState.configuration.viewportSize.x,
tState.configuration.viewportSize.y)];
}
 
@end
Loading
Loading
Loading
Loading
@@ -11,7 +11,7 @@ NS_ASSUME_NONNULL_BEGIN
@implementation iTermBadgeRendererTransientState
 
- (id<MTLBuffer>)newOffsetBufferWithDevice:(id<MTLDevice>)device {
CGSize viewport = CGSizeMake(self.viewportSize.x, self.viewportSize.y);
CGSize viewport = CGSizeMake(self.configuration.viewportSize.x, self.configuration.viewportSize.y);
vector_float2 offset = {
viewport.width - _size.width - 20,
viewport.height - _size.height - 20
Loading
Loading
@@ -61,15 +61,15 @@ NS_ASSUME_NONNULL_BEGIN
textures:@{ @(iTermTextureIndexPrimary): tState.texture }];
}
 
- (void)createTransientStateForViewportSize:(vector_uint2)viewportSize
commandBuffer:(id<MTLCommandBuffer>)commandBuffer
completion:(void (^)(__kindof iTermMetalRendererTransientState * _Nonnull))completion {
[_metalRenderer createTransientStateForViewportSize:viewportSize
commandBuffer:commandBuffer
completion:^(__kindof iTermMetalRendererTransientState * _Nonnull transientState) {
[self initializeTransientState:transientState];
completion(transientState);
}];
- (void)createTransientStateForConfiguration:(iTermRenderConfiguration *)configuration
commandBuffer:(id<MTLCommandBuffer>)commandBuffer
completion:(void (^)(__kindof iTermMetalRendererTransientState * _Nonnull))completion {
[_metalRenderer createTransientStateForConfiguration:configuration
commandBuffer:commandBuffer
completion:^(__kindof iTermMetalRendererTransientState * _Nonnull transientState) {
[self initializeTransientState:transientState];
completion(transientState);
}];
 
}
 
Loading
Loading
Loading
Loading
@@ -43,22 +43,22 @@ NS_ASSUME_NONNULL_BEGIN
textures:@{ @(iTermTextureIndexPrimary): tState.texture }];
}
 
- (void)createTransientStateForViewportSize:(vector_uint2)viewportSize
commandBuffer:(id<MTLCommandBuffer>)commandBuffer
completion:(void (^)(__kindof iTermMetalRendererTransientState * _Nonnull))completion {
[_metalRenderer createTransientStateForViewportSize:viewportSize
commandBuffer:commandBuffer
completion:^(__kindof iTermMetalRendererTransientState * _Nonnull transientState) {
[self initializeTransientState:transientState];
completion(transientState);
}];
- (void)createTransientStateForConfiguration:(iTermRenderConfiguration *)configuration
commandBuffer:(id<MTLCommandBuffer>)commandBuffer
completion:(void (^)(__kindof iTermMetalRendererTransientState * _Nonnull))completion {
[_metalRenderer createTransientStateForConfiguration:configuration
commandBuffer:commandBuffer
completion:^(__kindof iTermMetalRendererTransientState * _Nonnull transientState) {
[self initializeTransientState:transientState];
completion(transientState);
}];
}
 
- (void)initializeTransientState:(iTermBroadcastStripesRendererTransientState *)tState {
tState.texture = _texture;
tState.size = _size;
 
const vector_uint2 viewportSize = tState.viewportSize;
const vector_uint2 viewportSize = tState.configuration.viewportSize;
const float maxX = viewportSize.x / _size.width;
const float maxY = viewportSize.y / _size.height;
const iTermVertex vertices[] = {
Loading
Loading
//
// iTermCopyBackgroundRenderer.h
// iTerm2SharedARC
//
// Created by George Nachman on 11/4/17.
//
#import <Foundation/Foundation.h>
#import "iTermMetalRenderer.h"
NS_ASSUME_NONNULL_BEGIN
@interface iTermCopyBackgroundRendererTransientState : iTermMetalRendererTransientState
// The texture to copy from.
@property (nonatomic, strong) id<MTLTexture> sourceTexture;
@end
// The purpose of this renderer is to copy a texture to another texture. When
// there is a background image, diagonal broadcast input lines, a badge, or any
// other stuff that appears behind text that isn't a text background color,
// that makes subpixel antialiasing complicated and the text renderer needs to
// be able to sample its background color to decide what color to use. That
// necessitates drawing the composited background image, bars, badge, etc., to
// a texture because for some reason Metal doesn't let you sample from the
// texture you're drawing to.
@interface iTermCopyBackgroundRenderer : NSObject<iTermMetalRenderer>
- (nullable instancetype)initWithDevice:(id<MTLDevice>)device NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
@end
NS_ASSUME_NONNULL_END
//
// iTermCopyBackgroundRenderer.m
// iTerm2SharedARC
//
// Created by George Nachman on 11/4/17.
//
#import "iTermCopyBackgroundRenderer.h"
@implementation iTermCopyBackgroundRendererTransientState
- (BOOL)skipRenderer {
return _sourceTexture == nil;
}
@end
@implementation iTermCopyBackgroundRenderer {
iTermMetalRenderer *_metalRenderer;
}
- (nullable instancetype)initWithDevice:(id<MTLDevice>)device {
self = [super init];
if (self) {
_metalRenderer = [[iTermMetalRenderer alloc] initWithDevice:device
vertexFunctionName:@"iTermCopyBackgroundVertexShader"
fragmentFunctionName:@"iTermCopyBackgroundFragmentShader"
blending:NO
transientStateClass:[iTermCopyBackgroundRendererTransientState class]];
}
return self;
}
- (void)drawWithRenderEncoder:(nonnull id<MTLRenderCommandEncoder>)renderEncoder
transientState:(nonnull __kindof iTermMetalRendererTransientState *)transientState {
iTermCopyBackgroundRendererTransientState *tState = transientState;
[_metalRenderer drawWithTransientState:tState
renderEncoder:renderEncoder
numberOfVertices:6
numberOfPIUs:0
vertexBuffers:@{ @(iTermVertexInputIndexVertices): tState.vertexBuffer }
fragmentBuffers:@{}
textures:@{ @(iTermTextureIndexPrimary): tState.sourceTexture }];
}
- (void)createTransientStateForConfiguration:(iTermRenderConfiguration *)configuration
commandBuffer:(id<MTLCommandBuffer>)commandBuffer
completion:(void (^)(__kindof iTermMetalRendererTransientState * _Nonnull))completion {
[_metalRenderer createTransientStateForConfiguration:configuration
commandBuffer:commandBuffer
completion:^(__kindof iTermMetalRendererTransientState * _Nonnull transientState) {
[self initializeTransientState:transientState];
completion(transientState);
}];
}
- (void)initializeTransientState:(iTermCopyBackgroundRendererTransientState *)tState {
tState.vertexBuffer = [_metalRenderer newFlippedQuadOfSize:CGSizeMake(tState.configuration.viewportSize.x,
tState.configuration.viewportSize.y)];
}
@end
Loading
Loading
@@ -12,13 +12,13 @@
@implementation iTermCursorGuideRendererTransientState
 
- (nonnull NSData *)newCursorGuidePerInstanceUniforms {
NSMutableData *data = [[NSMutableData alloc] initWithLength:sizeof(iTermCursorGuidePIU) * self.gridSize.width];
NSMutableData *data = [[NSMutableData alloc] initWithLength:sizeof(iTermCursorGuidePIU) * self.cellConfiguration.gridSize.width];
iTermCursorGuidePIU *pius = (iTermCursorGuidePIU *)data.mutableBytes;
for (size_t i = 0; i < self.gridSize.width; i++) {
for (size_t i = 0; i < self.cellConfiguration.gridSize.width; i++) {
pius[i] = (iTermCursorGuidePIU) {
.offset = {
i * self.cellSize.width,
(self.gridSize.height - _row - 1) * self.cellSize.height
i * self.cellConfiguration.cellSize.width,
(self.cellConfiguration.gridSize.height - _row - 1) * self.cellConfiguration.cellSize.height
},
};
}
Loading
Loading
@@ -49,28 +49,24 @@
return self;
}
 
- (void)createTransientStateForViewportSize:(vector_uint2)viewportSize
cellSize:(CGSize)cellSize
gridSize:(VT100GridSize)gridSize
commandBuffer:(id<MTLCommandBuffer>)commandBuffer
completion:(void (^)(__kindof iTermMetalCellRendererTransientState * _Nonnull))completion {
[_cellRenderer createTransientStateForViewportSize:viewportSize
cellSize:cellSize
gridSize:gridSize
commandBuffer:commandBuffer
completion:^(__kindof iTermMetalCellRendererTransientState * _Nonnull transientState) {
[self initializeTransientState:transientState];
completion(transientState);
}];
- (void)createTransientStateForCellConfiguration:(iTermCellRenderConfiguration *)configuration
commandBuffer:(id<MTLCommandBuffer>)commandBuffer
completion:(void (^)(__kindof iTermMetalRendererTransientState * _Nonnull))completion {
[_cellRenderer createTransientStateForCellConfiguration:configuration
commandBuffer:commandBuffer
completion:^(__kindof iTermMetalCellRendererTransientState * _Nonnull transientState) {
[self initializeTransientState:transientState];
completion(transientState);
}];
}
 
- (void)initializeTransientState:(iTermCursorGuideRendererTransientState *)tState {
tState.color = _color;
tState.row = _row;
tState.vertexBuffer = [_cellRenderer newQuadOfSize:tState.cellSize];
if (!CGSizeEqualToSize(tState.cellSize, _lastCellSize)) {
tState.vertexBuffer = [_cellRenderer newQuadOfSize:tState.cellConfiguration.cellSize];
if (!CGSizeEqualToSize(tState.cellConfiguration.cellSize, _lastCellSize)) {
_texture = [self newCursorGuideTextureWithTransientState:tState];
_lastCellSize = tState.cellSize;
_lastCellSize = tState.cellConfiguration.cellSize;
}
tState.texture = _texture;
[self updatePIUsInState:tState];
Loading
Loading
@@ -91,7 +87,7 @@
[_cellRenderer drawWithTransientState:tState
renderEncoder:renderEncoder
numberOfVertices:6
numberOfPIUs:tState.gridSize.width
numberOfPIUs:tState.cellConfiguration.gridSize.width
vertexBuffers:@{ @(iTermVertexInputIndexVertices): tState.vertexBuffer,
@(iTermVertexInputIndexPerInstanceUniforms): tState.pius,
@(iTermVertexInputIndexOffset): tState.offsetBuffer }
Loading
Loading
@@ -108,21 +104,21 @@
}
 
- (id<MTLTexture>)newCursorGuideTextureWithTransientState:(iTermCursorGuideRendererTransientState *)tState {
NSImage *image = [[NSImage alloc] initWithSize:tState.cellSize];
NSImage *image = [[NSImage alloc] initWithSize:tState.cellConfiguration.cellSize];
 
[image lockFocus];
{
[tState.color set];
NSRect rect = NSMakeRect(0,
0,
tState.cellSize.width,
tState.cellSize.height);
tState.cellConfiguration.cellSize.width,
tState.cellConfiguration.cellSize.height);
NSRectFillUsingOperation(rect, NSCompositingOperationSourceOver);
 
rect.size.height = 1;
NSRectFillUsingOperation(rect, NSCompositingOperationSourceOver);
 
rect.origin.y += tState.cellSize.height - 1;
rect.origin.y += tState.cellConfiguration.cellSize.height - 1;
NSRectFillUsingOperation(rect, NSCompositingOperationSourceOver);
}
[image unlockFocus];
Loading
Loading
Loading
Loading
@@ -11,6 +11,8 @@ NS_ASSUME_NONNULL_BEGIN
+ (instancetype)newUnderlineCursorRendererWithDevice:(id<MTLDevice>)device;
+ (instancetype)newBarCursorRendererWithDevice:(id<MTLDevice>)device;
+ (instancetype)newBlockCursorRendererWithDevice:(id<MTLDevice>)device;
+ (instancetype)newFrameCursorRendererWithDevice:(id<MTLDevice>)device;
+ (iTermCopyModeCursorRenderer *)newCopyModeCursorRendererWithDevice:(id<MTLDevice>)device;
 
- (instancetype)init NS_UNAVAILABLE;
Loading
Loading
@@ -27,4 +29,7 @@ NS_ASSUME_NONNULL_BEGIN
 
@end
 
@interface iTermFrameCursorRenderer : iTermCursorRenderer
@end
NS_ASSUME_NONNULL_END
Loading
Loading
@@ -15,22 +15,26 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic) BOOL selecting;
@end
 
@interface iTermFrameCursorRendererTransientState : iTermCursorRendererTransientState
@property (nonatomic, strong) id<MTLTexture> texture;
@end
@implementation iTermCopyModeCursorRendererTransientState
 
- (NSImage *)newImage {
NSImage *image = [[NSImage alloc] initWithSize:self.cellSize];
NSImage *image = [[NSImage alloc] initWithSize:self.cellConfiguration.cellSize];
 
[image lockFocus];
const CGFloat heightFraction = 1 / 3.0;
const CGFloat scale = 2;
const CGFloat scale = self.cellConfiguration.scale;
NSRect rect = NSMakeRect(scale / 2,
scale / 2,
self.cellSize.width,
self.cellSize.height - scale / 2);
self.cellConfiguration.cellSize.width,
self.cellConfiguration.cellSize.height - scale / 2);
NSRect cursorRect = NSMakeRect(scale / 2,
rect.size.height * (1 - heightFraction) + scale / 2,
rect.size.width,
self.cellSize.height * heightFraction - scale / 2);
self.cellConfiguration.cellSize.height * heightFraction - scale / 2);
const CGFloat r = (self.selecting ? 2 : 1) * scale;
 
NSBezierPath *path = [[NSBezierPath alloc] init];
Loading
Loading
@@ -54,6 +58,29 @@ NS_ASSUME_NONNULL_BEGIN
 
@end
 
@implementation iTermFrameCursorRendererTransientState
- (NSImage *)newImage {
NSImage *image = [[NSImage alloc] initWithSize:self.cellConfiguration.cellSize];
[image lockFocus];
NSRect rect = NSMakeRect(0,
0,
self.cellConfiguration.cellSize.width,
self.cellConfiguration.cellSize.height);
rect = NSInsetRect(rect, self.cellConfiguration.scale / 2, self.cellConfiguration.scale / 2);
NSBezierPath *path = [NSBezierPath bezierPathWithRect:rect];
[path setLineWidth:self.cellConfiguration.scale];
[self.color setStroke];
[path stroke];
[image unlockFocus];
return image;
}
@end
@interface iTermUnderlineCursorRenderer : iTermCursorRenderer
@end
 
Loading
Loading
@@ -88,6 +115,12 @@ NS_ASSUME_NONNULL_BEGIN
fragmentFunctionName:@"iTermTextureCursorFragmentShader"];
}
 
+ (instancetype)newFrameCursorRendererWithDevice:(id<MTLDevice>)device {
return [[iTermFrameCursorRenderer alloc] initWithDevice:device
vertexFunctionName:@"iTermTextureCursorVertexShader"
fragmentFunctionName:@"iTermTextureCursorFragmentShader"];
}
- (instancetype)initWithDevice:(id<MTLDevice>)device
vertexFunctionName:(NSString *)vertexFunctionName
fragmentFunctionName:(NSString *)fragmentFunctionName {
Loading
Loading
@@ -114,19 +147,15 @@ NS_ASSUME_NONNULL_BEGIN
fragmentFunctionName:@"iTermCursorFragmentShader"];
}
 
- (void)createTransientStateForViewportSize:(vector_uint2)viewportSize
cellSize:(CGSize)cellSize
gridSize:(VT100GridSize)gridSize
commandBuffer:(id<MTLCommandBuffer>)commandBuffer
completion:(void (^)(__kindof iTermMetalCellRendererTransientState * _Nonnull))completion {
[_cellRenderer createTransientStateForViewportSize:viewportSize
cellSize:cellSize
gridSize:gridSize
commandBuffer:commandBuffer
completion:^(__kindof iTermMetalCellRendererTransientState * _Nonnull transientState) {
[self initializeTransientState:transientState];
completion(transientState);
}];
- (void)createTransientStateForCellConfiguration:(iTermCellRenderConfiguration *)configuration
commandBuffer:(id<MTLCommandBuffer>)commandBuffer
completion:(void (^)(__kindof iTermMetalRendererTransientState * _Nonnull))completion {
[_cellRenderer createTransientStateForCellConfiguration:configuration
commandBuffer:commandBuffer
completion:^(__kindof iTermMetalRendererTransientState * _Nonnull transientState) {
[self initializeTransientState:transientState];
completion(transientState);
}];
}
 
- (void)initializeTransientState:(iTermCursorRendererTransientState *)tState {
Loading
Loading
@@ -147,8 +176,8 @@ NS_ASSUME_NONNULL_BEGIN
iTermCursorRendererTransientState *tState = transientState;
iTermCursorDescription description = {
.origin = {
tState.cellSize.width * tState.coord.x,
tState.cellSize.height * (tState.gridSize.height - tState.coord.y - 1),
tState.cellConfiguration.cellSize.width * tState.coord.x,
tState.cellConfiguration.cellSize.height * (tState.cellConfiguration.gridSize.height - tState.coord.y - 1),
},
.color = {
tState.color.redComponent,
Loading
Loading
@@ -163,7 +192,7 @@ NS_ASSUME_NONNULL_BEGIN
[_cellRenderer drawWithTransientState:tState
renderEncoder:renderEncoder
numberOfVertices:6
numberOfPIUs:tState.gridSize.width
numberOfPIUs:tState.cellConfiguration.gridSize.width
vertexBuffers:@{ @(iTermVertexInputIndexVertices): tState.vertexBuffer,
@(iTermVertexInputIndexCursorDescription): descriptionBuffer,
@(iTermVertexInputIndexOffset): tState.offsetBuffer }
Loading
Loading
@@ -177,7 +206,7 @@ NS_ASSUME_NONNULL_BEGIN
 
- (void)initializeTransientState:(iTermCursorRendererTransientState *)tState {
[super initializeTransientState:tState];
tState.vertexBuffer = [_cellRenderer newQuadOfSize:CGSizeMake(tState.cellSize.width, 2)];
tState.vertexBuffer = [_cellRenderer newQuadOfSize:CGSizeMake(tState.cellConfiguration.cellSize.width, 2)];
}
 
@end
Loading
Loading
@@ -186,7 +215,7 @@ NS_ASSUME_NONNULL_BEGIN
 
- (void)initializeTransientState:(iTermCursorRendererTransientState *)tState {
[super initializeTransientState:tState];
tState.vertexBuffer = [_cellRenderer newQuadOfSize:CGSizeMake(2, tState.cellSize.height)];
tState.vertexBuffer = [_cellRenderer newQuadOfSize:CGSizeMake(2, tState.cellConfiguration.cellSize.height)];
}
 
@end
Loading
Loading
@@ -195,7 +224,53 @@ NS_ASSUME_NONNULL_BEGIN
 
- (void)initializeTransientState:(iTermCursorRendererTransientState *)tState {
[super initializeTransientState:tState];
tState.vertexBuffer = [_cellRenderer newQuadOfSize:CGSizeMake(tState.cellSize.width, tState.cellSize.height)];
tState.vertexBuffer = [_cellRenderer newQuadOfSize:CGSizeMake(tState.cellConfiguration.cellSize.width, tState.cellConfiguration.cellSize.height)];
}
@end
@implementation iTermFrameCursorRenderer {
id<MTLTexture> _texture;
CGSize _textureSize;
}
- (Class)transientStateClass {
return [iTermFrameCursorRendererTransientState class];
}
- (void)initializeTransientState:(iTermFrameCursorRendererTransientState *)tState {
[super initializeTransientState:tState];
tState.vertexBuffer = [_cellRenderer newQuadOfSize:CGSizeMake(tState.cellConfiguration.cellSize.width,
tState.cellConfiguration.cellSize.height)];
tState.color = _color;
if (_texture == nil || !CGSizeEqualToSize(_textureSize, tState.cellConfiguration.cellSize)) {
_texture = [_cellRenderer textureFromImage:[tState newImage]];
_textureSize = tState.cellConfiguration.cellSize;
}
tState.texture = _texture;
}
- (void)drawWithRenderEncoder:(id<MTLRenderCommandEncoder>)renderEncoder
transientState:(__kindof iTermMetalCellRendererTransientState *)transientState {
iTermFrameCursorRendererTransientState *tState = transientState;
iTermCursorDescription description = {
.origin = {
tState.cellConfiguration.cellSize.width * tState.coord.x,
tState.cellConfiguration.cellSize.height * (tState.cellConfiguration.gridSize.height - tState.coord.y - 1),
},
};
id<MTLBuffer> descriptionBuffer = [_cellRenderer.device newBufferWithBytes:&description
length:sizeof(description)
options:MTLResourceStorageModeShared];
[_cellRenderer drawWithTransientState:tState
renderEncoder:renderEncoder
numberOfVertices:6
numberOfPIUs:tState.cellConfiguration.gridSize.width
vertexBuffers:@{ @(iTermVertexInputIndexVertices): tState.vertexBuffer,
@(iTermVertexInputIndexCursorDescription): descriptionBuffer,
@(iTermVertexInputIndexOffset): tState.offsetBuffer }
fragmentBuffers:@{}
textures:@{ @(iTermTextureIndexPrimary): tState.texture } ];
}
 
@end
Loading
Loading
@@ -211,13 +286,13 @@ NS_ASSUME_NONNULL_BEGIN
 
- (void)initializeTransientState:(iTermCopyModeCursorRendererTransientState *)tState {
[super initializeTransientState:tState];
tState.vertexBuffer = [_cellRenderer newQuadOfSize:CGSizeMake(tState.cellSize.width,
tState.cellSize.height)];
tState.vertexBuffer = [_cellRenderer newQuadOfSize:CGSizeMake(tState.cellConfiguration.cellSize.width,
tState.cellConfiguration.cellSize.height)];
tState.selecting = _selecting;
tState.color = _color;
if (_texture == nil || !CGSizeEqualToSize(_textureSize, tState.cellSize)) {
if (_texture == nil || !CGSizeEqualToSize(_textureSize, tState.cellConfiguration.cellSize)) {
_texture = [_cellRenderer textureFromImage:[tState newImage]];
_textureSize = tState.cellSize;
_textureSize = tState.cellConfiguration.cellSize;
}
tState.texture = _texture;
}
Loading
Loading
@@ -235,8 +310,8 @@ NS_ASSUME_NONNULL_BEGIN
iTermCopyModeCursorRendererTransientState *tState = transientState;
iTermCursorDescription description = {
.origin = {
tState.cellSize.width * tState.coord.x - tState.cellSize.width / 2,
tState.cellSize.height * (tState.gridSize.height - tState.coord.y - 1),
tState.cellConfiguration.cellSize.width * tState.coord.x - tState.cellConfiguration.cellSize.width / 2,
tState.cellConfiguration.cellSize.height * (tState.cellConfiguration.gridSize.height - tState.coord.y - 1),
},
};
id<MTLBuffer> descriptionBuffer = [_cellRenderer.device newBufferWithBytes:&description
Loading
Loading
@@ -245,7 +320,7 @@ NS_ASSUME_NONNULL_BEGIN
[_cellRenderer drawWithTransientState:tState
renderEncoder:renderEncoder
numberOfVertices:6
numberOfPIUs:tState.gridSize.width
numberOfPIUs:tState.cellConfiguration.gridSize.width
vertexBuffers:@{ @(iTermVertexInputIndexVertices): tState.vertexBuffer,
@(iTermVertexInputIndexCursorDescription): descriptionBuffer,
@(iTermVertexInputIndexOffset): tState.offsetBuffer }
Loading
Loading
Loading
Loading
@@ -19,7 +19,7 @@
pius[i] = (iTermMarkPIU) {
.offset = {
2,
(self.gridSize.height - rowNumber.intValue - 1) * self.cellSize.height
(self.cellConfiguration.gridSize.height - rowNumber.intValue - 1) * self.cellConfiguration.cellSize.height
},
.textureOffset = { origin.x, origin.y }
};
Loading
Loading
@@ -51,23 +51,19 @@
return self;
}
 
- (void)createTransientStateForViewportSize:(vector_uint2)viewportSize
cellSize:(CGSize)cellSize
gridSize:(VT100GridSize)gridSize
commandBuffer:(id<MTLCommandBuffer>)commandBuffer
completion:(void (^)(__kindof iTermMetalCellRendererTransientState * _Nonnull))completion {
[_cellRenderer createTransientStateForViewportSize:viewportSize
cellSize:cellSize
gridSize:gridSize
commandBuffer:commandBuffer
completion:^(__kindof iTermMetalCellRendererTransientState * _Nonnull transientState) {
[self initializeTransientState:transientState];
completion(transientState);
}];
- (void)createTransientStateForCellConfiguration:(iTermCellRenderConfiguration *)configuration
commandBuffer:(id<MTLCommandBuffer>)commandBuffer
completion:(void (^)(__kindof iTermMetalRendererTransientState * _Nonnull))completion {
[_cellRenderer createTransientStateForCellConfiguration:configuration
commandBuffer:commandBuffer
completion:^(__kindof iTermMetalCellRendererTransientState * _Nonnull transientState) {
[self initializeTransientState:transientState];
completion(transientState);
}];
}
 
- (void)initializeTransientState:(iTermMarkRendererTransientState *)tState {
CGSize markSize = CGSizeMake(MARGIN_WIDTH - 4, MIN(15, tState.cellSize.height - 2));
CGSize markSize = CGSizeMake(MARGIN_WIDTH - 4, MIN(15, tState.cellConfiguration.cellSize.height - 2));
if (!CGSizeEqualToSize(markSize, _markSize)) {
// Mark size has changed
_markSize = markSize;
Loading
Loading
Loading
Loading
@@ -7,6 +7,7 @@ NS_ASSUME_NONNULL_BEGIN
 
@interface iTermTextRendererTransientState : iTermMetalCellRendererTransientState
@property (nonatomic, strong) NSMutableData *modelData;
@property (nonatomic, strong) id<MTLTexture> backgroundTexture;
 
- (void)setGlyphKeysData:(NSData *)glyphKeysData
attributesData:(NSData *)attributesData
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment