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

Basic proof of concept is working.

parent d88b43ff
No related branches found
No related tags found
No related merge requests found
Showing
with 1540 additions and 0 deletions
#import "iTermBadgeRenderer.h"
#import "iTermMetalRenderer.h"
NS_ASSUME_NONNULL_BEGIN
@interface iTermBadgeRendererTransientState : iTermMetalRendererTransientState
@property (nonatomic, strong) id<MTLTexture> texture;
@property (nonatomic) CGSize size;
@end
@implementation iTermBadgeRendererTransientState
- (id<MTLBuffer>)newOffsetBufferWithDevice:(id<MTLDevice>)device {
CGSize viewport = CGSizeMake(self.viewportSize.x, self.viewportSize.y);
vector_float2 offset = {
viewport.width - _size.width - 20,
viewport.height - _size.height - 20
};
return [device newBufferWithBytes:&offset
length:sizeof(offset)
options:MTLResourceStorageModeShared];
}
@end
@implementation iTermBadgeRenderer {
iTermMetalRenderer *_metalRenderer;
id<MTLTexture> _texture;
CGSize _size;
}
- (nullable instancetype)initWithDevice:(id<MTLDevice>)device {
self = [super init];
if (self) {
_metalRenderer = [[iTermMetalRenderer alloc] initWithDevice:device
vertexFunctionName:@"iTermBadgeVertexShader"
fragmentFunctionName:@"iTermBadgeFragmentShader"
blending:YES
transientStateClass:[iTermBadgeRendererTransientState class]];
[self setBadgeImage:[NSImage imageNamed:@"badge"]];
}
return self;
}
- (void)setBadgeImage:(NSImage *)image {
_size = image.size;
_texture = [_metalRenderer textureFromImage:image];
}
- (void)drawWithRenderEncoder:(id<MTLRenderCommandEncoder>)renderEncoder
transientState:(nonnull __kindof iTermMetalRendererTransientState *)transientState {
iTermBadgeRendererTransientState *tState = transientState;
id<MTLBuffer> offsetBuffer = [tState newOffsetBufferWithDevice:_metalRenderer.device];
[_metalRenderer drawWithTransientState:tState
renderEncoder:renderEncoder
numberOfVertices:6
numberOfPIUs:0
vertexBuffers:@{ @(iTermVertexInputIndexVertices): tState.vertexBuffer,
@(iTermVertexInputIndexOffset): offsetBuffer }
fragmentBuffers:@{}
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)initializeTransientState:(iTermBadgeRendererTransientState *)tState {
tState.texture = _texture;
tState.size = _size;
tState.vertexBuffer = [_metalRenderer newQuadOfSize:_size];
}
@end
NS_ASSUME_NONNULL_END
#import <Foundation/Foundation.h>
#import "iTermMetalRenderer.h"
NS_ASSUME_NONNULL_BEGIN
@interface iTermBroadcastStripesRenderer : NSObject<iTermMetalRenderer>
- (nullable instancetype)initWithDevice:(id<MTLDevice>)device NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
@end
NS_ASSUME_NONNULL_END
#import "iTermBroadcastStripesRenderer.h"
NS_ASSUME_NONNULL_BEGIN
@interface iTermBroadcastStripesRendererTransientState : iTermMetalRendererTransientState
@property (nonatomic, strong) id<MTLTexture> texture;
@property (nonatomic) CGSize size;
@end
@implementation iTermBroadcastStripesRendererTransientState
@end
@implementation iTermBroadcastStripesRenderer {
iTermMetalRenderer *_metalRenderer;
id<MTLTexture> _texture;
CGSize _size;
}
- (nullable instancetype)initWithDevice:(id<MTLDevice>)device {
self = [super init];
if (self) {
_metalRenderer = [[iTermMetalRenderer alloc] initWithDevice:device
vertexFunctionName:@"iTermBroadcastStripesVertexShader"
fragmentFunctionName:@"iTermBroadcastStripesFragmentShader"
blending:YES
transientStateClass:[iTermBroadcastStripesRendererTransientState class]];
NSImage *image = [NSImage imageNamed:@"BackgroundStripes"];
_size = image.size;
_texture = [_metalRenderer textureFromImage:image];
}
return self;
}
- (void)drawWithRenderEncoder:(id<MTLRenderCommandEncoder>)renderEncoder
transientState:(nonnull __kindof iTermMetalRendererTransientState *)transientState {
iTermBroadcastStripesRendererTransientState *tState = transientState;
[_metalRenderer drawWithTransientState:tState
renderEncoder:renderEncoder
numberOfVertices:6
numberOfPIUs:0
vertexBuffers:@{ @(iTermVertexInputIndexVertices): tState.vertexBuffer }
fragmentBuffers:@{}
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)initializeTransientState:(iTermBroadcastStripesRendererTransientState *)tState {
tState.texture = _texture;
tState.size = _size;
const vector_uint2 viewportSize = tState.viewportSize;
const float maxX = viewportSize.x / _size.width;
const float maxY = viewportSize.y / _size.height;
const iTermVertex vertices[] = {
// Pixel Positions, Texture Coordinates
{ { viewportSize.x, 0 }, { maxX, 0 } },
{ { 0, 0 }, { 0, 0 } },
{ { 0, viewportSize.y }, { 0, maxY } },
{ { viewportSize.x, 0 }, { maxX, 0 } },
{ { 0, viewportSize.y }, { 0, maxY } },
{ { viewportSize.x, viewportSize.y }, { maxX, maxY } },
};
tState.vertexBuffer = [_metalRenderer.device newBufferWithBytes:vertices
length:sizeof(vertices)
options:MTLResourceStorageModeShared];
}
@end
NS_ASSUME_NONNULL_END
#import <Foundation/Foundation.h>
#import "iTermMetalCellRenderer.h"
NS_ASSUME_NONNULL_BEGIN
@interface iTermCursorGuideRenderer : NSObject<iTermMetalCellRenderer>
- (nullable instancetype)initWithDevice:(id<MTLDevice>)device NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
- (void)setColor:(NSColor *)color;
- (void)setRow:(int)row;
@end
NS_ASSUME_NONNULL_END
#import "iTermCursorGuideRenderer.h"
@interface iTermCursorGuideRendererTransientState : iTermMetalCellRendererTransientState
@property (nonatomic, strong) id<MTLTexture> texture;
@property (nonatomic, strong) NSColor *color;
@property (nonatomic) int row;
- (nonnull NSData *)newCursorGuidePerInstanceUniforms;
@end
@implementation iTermCursorGuideRendererTransientState
- (nonnull NSData *)newCursorGuidePerInstanceUniforms {
NSMutableData *data = [[NSMutableData alloc] initWithLength:sizeof(iTermCursorGuidePIU) * self.gridSize.width];
iTermCursorGuidePIU *pius = (iTermCursorGuidePIU *)data.mutableBytes;
for (size_t i = 0; i < self.gridSize.width; i++) {
pius[i] = (iTermCursorGuidePIU) {
.offset = {
i * self.cellSize.width,
(self.gridSize.height - _row - 1) * self.cellSize.height
},
};
}
return data;
}
@end
@implementation iTermCursorGuideRenderer {
iTermMetalCellRenderer *_cellRenderer;
id<MTLTexture> _texture;
NSColor *_color;
int _row;
CGSize _lastCellSize;
}
- (instancetype)initWithDevice:(id<MTLDevice>)device {
self = [super init];
if (self) {
_color = [[NSColor blueColor] colorWithAlphaComponent:0.7];
_cellRenderer = [[iTermMetalCellRenderer alloc] initWithDevice:device
vertexFunctionName:@"iTermCursorGuideVertexShader"
fragmentFunctionName:@"iTermCursorGuideFragmentShader"
blending:YES
piuElementSize:sizeof(iTermCursorGuidePIU)
transientStateClass:[iTermCursorGuideRendererTransientState class]];
}
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)initializeTransientState:(iTermCursorGuideRendererTransientState *)tState {
tState.color = _color;
tState.row = _row;
tState.vertexBuffer = [_cellRenderer newQuadOfSize:tState.cellSize];
if (!CGSizeEqualToSize(tState.cellSize, _lastCellSize)) {
_texture = [self newCursorGuideTextureWithTransientState:tState];
_lastCellSize = tState.cellSize;
}
tState.texture = _texture;
[self updatePIUsInState:tState];
}
- (void)setColor:(NSColor *)color {
_color = color;
_lastCellSize = CGSizeZero;
}
- (void)setRow:(int)row {
_row = row;
}
- (void)drawWithRenderEncoder:(id<MTLRenderCommandEncoder>)renderEncoder
transientState:(__kindof iTermMetalCellRendererTransientState *)transientState {
iTermCursorGuideRendererTransientState *tState = transientState;
[_cellRenderer drawWithTransientState:tState
renderEncoder:renderEncoder
numberOfVertices:6
numberOfPIUs:tState.gridSize.width
vertexBuffers:@{ @(iTermVertexInputIndexVertices): tState.vertexBuffer,
@(iTermVertexInputIndexPerInstanceUniforms): tState.pius,
@(iTermVertexInputIndexOffset): tState.offsetBuffer }
fragmentBuffers:@{}
textures:@{ @(iTermTextureIndexPrimary): tState.texture } ];
}
#pragma mark - Private
- (void)updatePIUsInState:(iTermCursorGuideRendererTransientState *)tState {
NSData *data = [tState newCursorGuidePerInstanceUniforms];
tState.pius = [_cellRenderer.device newBufferWithLength:data.length options:MTLResourceStorageModeShared];
memcpy(tState.pius.contents, data.bytes, data.length);
}
- (id<MTLTexture>)newCursorGuideTextureWithTransientState:(iTermCursorGuideRendererTransientState *)tState {
NSImage *image = [[NSImage alloc] initWithSize:tState.cellSize];
[image lockFocus];
{
[tState.color set];
NSRect rect = NSMakeRect(0,
0,
tState.cellSize.width,
tState.cellSize.height);
NSRectFillUsingOperation(rect, NSCompositingOperationSourceOver);
rect.size.height = 1;
NSRectFillUsingOperation(rect, NSCompositingOperationSourceOver);
rect.origin.y += tState.cellSize.height - 1;
NSRectFillUsingOperation(rect, NSCompositingOperationSourceOver);
}
[image unlockFocus];
return [_cellRenderer textureFromImage:image];
}
@end
#import <Foundation/Foundation.h>
#import "iTermMetalCellRenderer.h"
NS_ASSUME_NONNULL_BEGIN
@class iTermCopyModeCursorRenderer;
@interface iTermCursorRenderer : NSObject<iTermMetalCellRenderer>
+ (instancetype)newUnderlineCursorRendererWithDevice:(id<MTLDevice>)device;
+ (instancetype)newBarCursorRendererWithDevice:(id<MTLDevice>)device;
+ (instancetype)newBlockCursorRendererWithDevice:(id<MTLDevice>)device;
+ (iTermCopyModeCursorRenderer *)newCopyModeCursorRendererWithDevice:(id<MTLDevice>)device;
- (instancetype)init NS_UNAVAILABLE;
- (void)setColor:(NSColor *)color;
- (void)setCoord:(VT100GridCoord)coord;
@end
@interface iTermCopyModeCursorRenderer : iTermCursorRenderer
// This must be set before setting the viewport size, if it is to be changed for this frame.
@property (nonatomic) BOOL selecting;
@end
NS_ASSUME_NONNULL_END
#import "iTermCursorRenderer.h"
NS_ASSUME_NONNULL_BEGIN
@interface iTermCursorRendererTransientState : iTermMetalCellRendererTransientState
@property (nonatomic, strong) NSColor *color;
@property (nonatomic) VT100GridCoord coord;
@end
@implementation iTermCursorRendererTransientState
@end
@interface iTermCopyModeCursorRendererTransientState : iTermCursorRendererTransientState
@property (nonatomic, strong) id<MTLTexture> texture;
@property (nonatomic) BOOL selecting;
@end
@implementation iTermCopyModeCursorRendererTransientState
- (NSImage *)newImage {
NSImage *image = [[NSImage alloc] initWithSize:self.cellSize];
[image lockFocus];
const CGFloat heightFraction = 1 / 3.0;
const CGFloat scale = 2;
NSRect rect = NSMakeRect(scale / 2,
scale / 2,
self.cellSize.width,
self.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);
const CGFloat r = (self.selecting ? 2 : 1) * scale;
NSBezierPath *path = [[NSBezierPath alloc] init];
[path moveToPoint:NSMakePoint(NSMinX(cursorRect), NSMaxY(cursorRect))];
[path lineToPoint:NSMakePoint(NSMidX(cursorRect) - r, NSMinY(cursorRect))];
[path lineToPoint:NSMakePoint(NSMidX(cursorRect) - r, NSMinY(rect))];
[path lineToPoint:NSMakePoint(NSMidX(cursorRect) + r, NSMinY(rect))];
[path lineToPoint:NSMakePoint(NSMidX(cursorRect) + r, NSMinY(cursorRect))];
[path lineToPoint:NSMakePoint(NSMaxX(cursorRect), NSMaxY(cursorRect))];
[path lineToPoint:NSMakePoint(NSMinX(cursorRect), NSMaxY(cursorRect))];
[self.color set];
[path fill];
[[NSColor blackColor] set];
[path setLineWidth:scale];
[path stroke];
[image unlockFocus];
return image;
}
@end
@interface iTermUnderlineCursorRenderer : iTermCursorRenderer
@end
@interface iTermBarCursorRenderer : iTermCursorRenderer
@end
@interface iTermBlockCursorRenderer : iTermCursorRenderer
@end
@implementation iTermCursorRenderer {
@protected
iTermMetalCellRenderer *_cellRenderer;
NSColor *_color;
VT100GridCoord _coord;
}
+ (instancetype)newUnderlineCursorRendererWithDevice:(id<MTLDevice>)device {
return [[iTermUnderlineCursorRenderer alloc] initWithDevice:device];
}
+ (instancetype)newBarCursorRendererWithDevice:(id<MTLDevice>)device {
return [[iTermBarCursorRenderer alloc] initWithDevice:device];
}
+ (instancetype)newBlockCursorRendererWithDevice:(id<MTLDevice>)device {
return [[iTermBlockCursorRenderer alloc] initWithDevice:device];
}
+ (instancetype)newCopyModeCursorRendererWithDevice:(id<MTLDevice>)device {
return [[iTermCopyModeCursorRenderer alloc] initWithDevice:device
vertexFunctionName:@"iTermTextureCursorVertexShader"
fragmentFunctionName:@"iTermTextureCursorFragmentShader"];
}
- (instancetype)initWithDevice:(id<MTLDevice>)device
vertexFunctionName:(NSString *)vertexFunctionName
fragmentFunctionName:(NSString *)fragmentFunctionName {
self = [super init];
if (self) {
_color = [NSColor colorWithRed:1 green:1 blue:1 alpha:1];
_cellRenderer = [[iTermMetalCellRenderer alloc] initWithDevice:device
vertexFunctionName:vertexFunctionName
fragmentFunctionName:fragmentFunctionName
blending:YES
piuElementSize:0
transientStateClass:self.transientStateClass];
}
return self;
}
- (Class)transientStateClass {
return [iTermCursorRendererTransientState class];
}
- (instancetype)initWithDevice:(id<MTLDevice>)device {
return [self initWithDevice:device
vertexFunctionName:@"iTermCursorVertexShader"
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)initializeTransientState:(iTermCursorRendererTransientState *)tState {
tState.color = _color;
tState.coord = _coord;
}
- (void)setColor:(NSColor *)color {
_color = color;
}
- (void)setCoord:(VT100GridCoord)coord {
_coord = coord;
}
- (void)drawWithRenderEncoder:(id<MTLRenderCommandEncoder>)renderEncoder
transientState:(nonnull __kindof iTermMetalRendererTransientState *)transientState {
iTermCursorRendererTransientState *tState = transientState;
iTermCursorDescription description = {
.origin = {
tState.cellSize.width * tState.coord.x,
tState.cellSize.height * (tState.gridSize.height - tState.coord.y - 1),
},
.color = {
tState.color.redComponent,
tState.color.greenComponent,
tState.color.blueComponent,
1
}
};
id<MTLBuffer> descriptionBuffer = [_cellRenderer.device newBufferWithBytes:&description
length:sizeof(description)
options:MTLResourceStorageModeShared];
[_cellRenderer drawWithTransientState:tState
renderEncoder:renderEncoder
numberOfVertices:6
numberOfPIUs:tState.gridSize.width
vertexBuffers:@{ @(iTermVertexInputIndexVertices): tState.vertexBuffer,
@(iTermVertexInputIndexCursorDescription): descriptionBuffer,
@(iTermVertexInputIndexOffset): tState.offsetBuffer }
fragmentBuffers:@{}
textures:@{ } ];
}
@end
@implementation iTermUnderlineCursorRenderer
- (void)initializeTransientState:(iTermCursorRendererTransientState *)tState {
[super initializeTransientState:tState];
tState.vertexBuffer = [_cellRenderer newQuadOfSize:CGSizeMake(tState.cellSize.width, 2)];
}
@end
@implementation iTermBarCursorRenderer
- (void)initializeTransientState:(iTermCursorRendererTransientState *)tState {
[super initializeTransientState:tState];
tState.vertexBuffer = [_cellRenderer newQuadOfSize:CGSizeMake(2, tState.cellSize.height)];
}
@end
@implementation iTermBlockCursorRenderer
- (void)initializeTransientState:(iTermCursorRendererTransientState *)tState {
[super initializeTransientState:tState];
tState.vertexBuffer = [_cellRenderer newQuadOfSize:CGSizeMake(tState.cellSize.width, tState.cellSize.height)];
}
@end
@implementation iTermCopyModeCursorRenderer {
id<MTLTexture> _texture;
CGSize _textureSize;
}
- (Class)transientStateClass {
return [iTermCopyModeCursorRendererTransientState class];
}
- (void)initializeTransientState:(iTermCopyModeCursorRendererTransientState *)tState {
[super initializeTransientState:tState];
tState.vertexBuffer = [_cellRenderer newQuadOfSize:CGSizeMake(tState.cellSize.width,
tState.cellSize.height)];
tState.selecting = _selecting;
tState.color = _color;
if (_texture == nil || !CGSizeEqualToSize(_textureSize, tState.cellSize)) {
_texture = [_cellRenderer textureFromImage:[tState newImage]];
_textureSize = tState.cellSize;
}
tState.texture = _texture;
}
- (void)setSelecting:(BOOL)selecting {
if (selecting != _selecting) {
_selecting = selecting;
_color = selecting ? [NSColor colorWithRed:0xc1 / 255.0 green:0xde / 255.0 blue:0xff / 255.0 alpha:1] : [NSColor whiteColor];
_texture = nil;
}
}
- (void)drawWithRenderEncoder:(id<MTLRenderCommandEncoder>)renderEncoder
transientState:(__kindof iTermMetalCellRendererTransientState *)transientState {
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),
},
};
id<MTLBuffer> descriptionBuffer = [_cellRenderer.device newBufferWithBytes:&description
length:sizeof(description)
options:MTLResourceStorageModeShared];
[_cellRenderer drawWithTransientState:tState
renderEncoder:renderEncoder
numberOfVertices:6
numberOfPIUs:tState.gridSize.width
vertexBuffers:@{ @(iTermVertexInputIndexVertices): tState.vertexBuffer,
@(iTermVertexInputIndexCursorDescription): descriptionBuffer,
@(iTermVertexInputIndexOffset): tState.offsetBuffer }
fragmentBuffers:@{}
textures:@{ @(iTermTextureIndexPrimary): tState.texture } ];
}
@end
NS_ASSUME_NONNULL_END
#import <Foundation/Foundation.h>
#import "iTermMetalCellRenderer.h"
typedef NS_ENUM(int, iTermMarkStyle) {
iTermMarkStyleNone = -1,
iTermMarkStyleSuccess = 0,
iTermMarkStyleFailure = 1,
iTermMarkStyleOther = 2
};
NS_ASSUME_NONNULL_BEGIN
@interface iTermMarkRenderer : NSObject<iTermMetalCellRenderer>
- (nullable instancetype)initWithDevice:(id<MTLDevice>)device NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
// Call this before setting the viewport size for it to take effect in this frame.
- (void)setMarkStyle:(iTermMarkStyle)markStyle row:(int)row;
@end
NS_ASSUME_NONNULL_END
#import "iTermMarkRenderer.h"
#import "iTermTextureArray.h"
#import "iTermMetalCellRenderer.h"
@interface iTermMarkRendererTransientState : iTermMetalCellRendererTransientState
@property (nonatomic, strong) iTermTextureArray *marksArrayTexture;
@property (nonatomic) CGSize markSize;
@property (nonatomic, copy) NSDictionary<NSNumber *, NSNumber *> *marks;
@end
@implementation iTermMarkRendererTransientState
- (nonnull NSData *)newMarkPerInstanceUniforms {
NSMutableData *data = [[NSMutableData alloc] initWithLength:sizeof(iTermMarkPIU) * _marks.count];
iTermMarkPIU *pius = (iTermMarkPIU *)data.mutableBytes;
__block size_t i = 0;
[_marks enumerateKeysAndObjectsUsingBlock:^(NSNumber * _Nonnull rowNumber, NSNumber * _Nonnull styleNumber, BOOL * _Nonnull stop) {
MTLOrigin origin = [_marksArrayTexture offsetForIndex:styleNumber.integerValue];
pius[i] = (iTermMarkPIU) {
.offset = {
2,
(self.gridSize.height - rowNumber.intValue - 1) * self.cellSize.height
},
.textureOffset = { origin.x, origin.y }
};
i++;
}];
return data;
}
@end
@implementation iTermMarkRenderer {
iTermMetalCellRenderer *_cellRenderer;
iTermTextureArray *_marksArrayTexture;
CGSize _markSize;
NSMutableDictionary<NSNumber *, NSNumber *> *_marks;
}
- (instancetype)initWithDevice:(id<MTLDevice>)device {
self = [super init];
if (self) {
_marks = [NSMutableDictionary dictionary];
_cellRenderer = [[iTermMetalCellRenderer alloc] initWithDevice:device
vertexFunctionName:@"iTermMarkVertexShader"
fragmentFunctionName:@"iTermMarkFragmentShader"
blending:YES
piuElementSize:sizeof(iTermMarkPIU)
transientStateClass:[iTermMarkRendererTransientState class]];
}
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)initializeTransientState:(iTermMarkRendererTransientState *)tState {
CGSize markSize = CGSizeMake(MARGIN_WIDTH - 4, MIN(15, tState.cellSize.height - 2));
if (!CGSizeEqualToSize(markSize, _markSize)) {
// Mark size has changed
_markSize = markSize;
_marksArrayTexture = [[iTermTextureArray alloc] initWithTextureWidth:_markSize.width
textureHeight:_markSize.height
arrayLength:3
device:_cellRenderer.device];
[_marksArrayTexture addSliceWithImage:[self newImageWithMarkOfColor:[NSColor blueColor] size:_markSize]];
[_marksArrayTexture addSliceWithImage:[self newImageWithMarkOfColor:[NSColor redColor] size:_markSize]];
[_marksArrayTexture addSliceWithImage:[self newImageWithMarkOfColor:[NSColor yellowColor] size:_markSize]];
}
tState.marksArrayTexture = _marksArrayTexture;
tState.markSize = _markSize;
tState.marks = [_marks copy];
tState.vertexBuffer = [_cellRenderer newQuadOfSize:_markSize];
if (_marks.count > 0) {
NSData *data = [tState newMarkPerInstanceUniforms];
tState.pius = [_cellRenderer.device newBufferWithLength:data.length options:MTLResourceStorageModeShared];
memcpy(tState.pius.contents, data.bytes, data.length);
}
}
- (void)setMarkStyle:(iTermMarkStyle)markStyle row:(int)row {
if (markStyle == iTermMarkStyleNone) {
[_marks removeObjectForKey:@(row)];
} else {
_marks[@(row)] = @(markStyle);
}
}
- (void)drawWithRenderEncoder:(id<MTLRenderCommandEncoder>)renderEncoder
transientState:(__kindof iTermMetalCellRendererTransientState *)transientState {
iTermMarkRendererTransientState *tState = transientState;
if (tState.marks.count == 0) {
return;
}
[_cellRenderer drawWithTransientState:tState
renderEncoder:renderEncoder
numberOfVertices:6
numberOfPIUs:_marks.count
vertexBuffers:@{ @(iTermVertexInputIndexVertices): tState.vertexBuffer,
@(iTermVertexInputIndexPerInstanceUniforms): tState.pius,
@(iTermVertexInputIndexOffset): tState.offsetBuffer }
fragmentBuffers:@{}
textures:@{ @(iTermTextureIndexPrimary): tState.marksArrayTexture.texture } ];
}
#pragma mark - Private
- (NSImage *)newImageWithMarkOfColor:(NSColor *)color size:(CGSize)size {
NSImage *image = [[NSImage alloc] initWithSize:size];
NSBezierPath *path = [NSBezierPath bezierPath];
[image lockFocus];
[path moveToPoint:NSMakePoint(0,0)];
[path lineToPoint:NSMakePoint(size.width - 1, size.height / 2)];
[path lineToPoint:NSMakePoint(0, size.height - 1)];
[path lineToPoint:NSMakePoint(0,0)];
[color setFill];
[path fill];
[image unlockFocus];
return image;
}
@end
#import "iTermMetalCellRenderer.h"
#import "iTermMetalGlyphKey.h"
NS_ASSUME_NONNULL_BEGIN
@class iTermTextureMap;
@interface iTermTextRendererTransientState : iTermMetalCellRendererTransientState
@property (nonatomic, strong) NSMutableData *modelData;
- (void)setGlyphKeysData:(NSData *)glyphKeysData
attributesData:(NSData *)attributesData
row:(int)row
creation:(NSImage *(NS_NOESCAPE ^)(int x))creation;
- (void)willDraw;
- (void)didComplete;
@end
@interface iTermTextRenderer : NSObject<iTermMetalCellRenderer>
@property (nonatomic, readonly) BOOL canRenderImmediately;
- (instancetype)initWithDevice:(id<MTLDevice>)device NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
@end
NS_ASSUME_NONNULL_END
#import "iTermTextRenderer.h"
extern "C" {
#import "DebugLogging.h"
}
#import "iTermMetalCellRenderer.h"
#import "iTermSubpixelModelBuilder.h"
#import "iTermTextureArray.h"
#import "iTermTextureMap.h"
#import <unordered_map>
@interface iTermTextRendererTransientState ()
@property (nonatomic, readonly) NSIndexSet *indexes;
@property (nonatomic, strong) NSData *subpixelModelData;
@property (nonatomic, weak) iTermTextureMap *textureMap;
- (void)addIndex:(NSInteger)index;
@end
@implementation iTermTextRendererTransientState {
iTermTextureMapStage *_stage;
NSMutableIndexSet *_indexes;
NSMutableArray<iTermSubpixelModel *> *_models;
std::unordered_map<NSUInteger, NSUInteger> *_modelMap; // Maps a 48 bit fg/bg color to an index into _models.
dispatch_group_t _group;
id<MTLCommandBuffer> _commandBuffer;
}
- (instancetype)init {
self = [super init];
if (self) {
_indexes = [NSMutableIndexSet indexSet];
_models = [NSMutableArray array];
_modelMap = new std::unordered_map<NSUInteger, NSUInteger>();
_group = dispatch_group_create();
}
return self;
}
- (void)dealloc {
delete _modelMap;
}
- (void)willDraw {
self.subpixelModelData = [self newSubpixelModelData];
[_stage blitNewTexturesFromStagingAreaWithCommandBuffer:_commandBuffer];
}
- (void)prepareForDrawWithCommandBuffer:(id<MTLCommandBuffer>)commandBuffer
completion:(void (^)(void))completion {
_commandBuffer = commandBuffer;
[_textureMap requestStage:^(iTermTextureMapStage *stage) {
_stage = stage;
completion();
}];
}
- (void)setGlyphKeysData:(NSData *)glyphKeysData
attributesData:(NSData *)attributesData
row:(int)row
creation:(NSImage *(NS_NOESCAPE ^)(int x))creation {
const int width = self.gridSize.width;
const iTermMetalGlyphKey *glyphKeys = (iTermMetalGlyphKey *)glyphKeysData.bytes;
const iTermMetalGlyphAttributes *attributes = (iTermMetalGlyphAttributes *)attributesData.bytes;
const float w = 1.0 / _textureMap.array.atlasSize.width;
const float h = 1.0 / _textureMap.array.atlasSize.height;
iTermTextureArray *array = _textureMap.array;
iTermTextPIU *pius = [self textPIUs];
NSInteger lastIndex = 0;
for (int x = 0; x < width; x++) {
NSInteger index;
if (x > 0 && !memcmp(&glyphKeys[x], &glyphKeys[x-1], sizeof(*glyphKeys))) {
index = lastIndex;
} else {
index = [_stage findOrAllocateIndexOfLockedTextureWithKey:&glyphKeys[x]
column:x
creation:creation];
}
if (index >= 0) {
// Update the PIU with the session index. It may not be a legit value yet.
const size_t i = x + row * self.gridSize.width;
iTermTextPIU *piu = &pius[i];
MTLOrigin origin = [array offsetForIndex:index];
piu->textureOffset = (vector_float2){ origin.x * w, origin.y * h };
piu->colorModelIndex = [self colorModelIndexForAttributes:&attributes[x]];
[self addIndex:index];
}
lastIndex = index;
}
}
- (void)didComplete {
[_textureMap returnStage:_stage];
_stage = nil;
}
- (iTermTextPIU *)textPIUs {
return (iTermTextPIU *)self.modelData.mutableBytes;
}
- (nonnull NSMutableData *)modelData {
if (_modelData == nil) {
_modelData = [[NSMutableData alloc] initWithLength:sizeof(iTermTextPIU) * self.gridSize.width * self.gridSize.height];
}
return _modelData;
}
- (void)initializePIUData {
iTermTextPIU *pius = self.textPIUs;
NSInteger i = 0;
for (NSInteger y = 0; y < self.gridSize.height; y++) {
for (NSInteger x = 0; x < self.gridSize.width; x++) {
const iTermTextPIU uniform = {
.offset = {
static_cast<float>(x * self.cellSize.width),
static_cast<float>((self.gridSize.height - y - 1) * self.cellSize.height)
},
.textureOffset = { 0, 0 }
};
memcpy(&pius[i++], &uniform, sizeof(uniform));
}
}
}
- (void)addIndex:(NSInteger)index {
[_indexes addIndex:index];
}
- (NSData *)newSubpixelModelData {
const size_t tableSize = _models.firstObject.table.length;
NSMutableData *data = [NSMutableData dataWithLength:_models.count * tableSize];
unsigned char *output = (unsigned char *)data.mutableBytes;
[_models enumerateObjectsUsingBlock:^(iTermSubpixelModel * _Nonnull model, NSUInteger idx, BOOL * _Nonnull stop) {
const size_t offset = idx * tableSize;
memcpy(output + offset, model.table.bytes, tableSize);
}];
return data;
}
- (int)colorModelIndexForAttributes:(const iTermMetalGlyphAttributes *)attributes {
NSUInteger key = [iTermSubpixelModel keyForForegroundColor:attributes->foregroundColor
backgroundColor:attributes->backgroundColor];
auto it = _modelMap->find(key);
if (it == _modelMap->end()) {
// TODO: Expire old models
const NSUInteger index = _models.count;
iTermSubpixelModel *model = [[iTermSubpixelModelBuilder sharedInstance] modelForForegoundColor:attributes->foregroundColor
backgroundColor:attributes->backgroundColor];
[_models addObject:model];
(*_modelMap)[model.key] = index;
DLog(@"Assign model %@ to index %@", model, @(index));
return index;
} else {
return it->second;
}
}
#pragma mark - Debugging
- (iTermTextPIU *)piuArray {
return (iTermTextPIU *)self.pius.contents;
}
- (iTermVertex *)vertexArray {
return (iTermVertex *)self.vertexBuffer.contents;
}
@end
@implementation iTermTextRenderer {
iTermMetalCellRenderer *_cellRenderer;
iTermTextureMap *_textureMap;
}
- (instancetype)initWithDevice:(id<MTLDevice>)device {
self = [super init];
if (self) {
_cellRenderer = [[iTermMetalCellRenderer alloc] initWithDevice:device
vertexFunctionName:@"iTermTextVertexShader"
fragmentFunctionName:@"iTermTextFragmentShader"
blending:YES
piuElementSize:sizeof(iTermTextPIU)
transientStateClass:[iTermTextRendererTransientState class]];
}
return self;
}
- (BOOL)canRenderImmediately {
return _textureMap.haveStageAvailable;
}
- (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
commandBuffer:commandBuffer
completion:completion];
}];
}
- (void)initializeTransientState:(iTermTextRendererTransientState *)tState
commandBuffer:(id<MTLCommandBuffer>)commandBuffer
completion:(void (^)(__kindof iTermMetalCellRendererTransientState * _Nonnull))completion {
const NSInteger capacity = tState.gridSize.width * tState.gridSize.height * 2;
if (_textureMap == nil ||
!CGSizeEqualToSize(_textureMap.cellSize, tState.cellSize) ||
_textureMap.capacity != capacity) {
_textureMap = [[iTermTextureMap alloc] initWithDevice:_cellRenderer.device
cellSize:tState.cellSize
capacity:capacity];
_textureMap.label = [NSString stringWithFormat:@"[texture map for %p]", self];
_textureMap.array.texture.label = @"Texture grid for session";
}
tState.textureMap = _textureMap;
// The vertex buffer's texture coordinates depend on the texture map's atlas size so it must
// be initialized after the texture map.
tState.vertexBuffer = [self newQuadOfSize:tState.cellSize];
[tState initializePIUData];
[tState prepareForDrawWithCommandBuffer:commandBuffer completion:^{
completion(tState);
}];
}
- (id<MTLBuffer>)newQuadOfSize:(CGSize)size {
const float vw = static_cast<float>(size.width);
const float vh = static_cast<float>(size.height);
const float w = size.width / _textureMap.array.atlasSize.width;
const float h = size.height / _textureMap.array.atlasSize.height;
const iTermVertex vertices[] = {
// Pixel Positions, Texture Coordinates
{ { vw, 0 }, { w, 0 } },
{ { 0, 0 }, { 0, 0 } },
{ { 0, vh }, { 0, h } },
{ { vw, 0 }, { w, 0 } },
{ { 0, vh }, { 0, h } },
{ { vw, vh }, { w, h } },
};
return [_cellRenderer.device newBufferWithBytes:vertices
length:sizeof(vertices)
options:MTLResourceStorageModeShared];
}
- (void)drawWithRenderEncoder:(id<MTLRenderCommandEncoder>)renderEncoder
transientState:(__kindof iTermMetalCellRendererTransientState *)transientState {
iTermTextRendererTransientState *tState = transientState;
tState.pius = [_cellRenderer.device newBufferWithBytes:tState.modelData.mutableBytes
length:tState.modelData.length
options:MTLResourceStorageModeShared];
tState.vertexBuffer.label = @"text vertex buffer";
tState.pius.label = @"text PIUs";
tState.offsetBuffer.label = @"text offset";
id<MTLBuffer> subpixelModels = [_cellRenderer.device newBufferWithBytes:tState.subpixelModelData.bytes
length:tState.subpixelModelData.length
options:MTLResourceStorageModeShared];
[_cellRenderer drawWithTransientState:tState
renderEncoder:renderEncoder
numberOfVertices:6
numberOfPIUs:tState.gridSize.width * tState.gridSize.height
vertexBuffers:@{ @(iTermVertexInputIndexVertices): tState.vertexBuffer,
@(iTermVertexInputIndexPerInstanceUniforms): tState.pius,
@(iTermVertexInputIndexOffset): tState.offsetBuffer }
fragmentBuffers:@{ @(iTermFragmentBufferIndexColorModels): subpixelModels }
textures:@{ @(iTermTextureIndexPrimary): tState.textureMap.array.texture }];
}
@end
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
#import "iTermShaderTypes.h"
typedef struct {
float4 clipSpacePosition [[position]];
float4 color;
} iTermBackgroundColorVertexFunctionOutput;
vertex iTermBackgroundColorVertexFunctionOutput
iTermBackgroundColorVertexShader(uint vertexID [[ vertex_id ]],
constant float2 *offset [[ buffer(iTermVertexInputIndexOffset) ]],
constant iTermVertex *vertexArray [[ buffer(iTermVertexInputIndexVertices) ]],
constant vector_uint2 *viewportSizePointer [[ buffer(iTermVertexInputIndexViewportSize) ]],
constant iTermBackgroundColorPIU *perInstanceUniforms [[ buffer(iTermVertexInputIndexPerInstanceUniforms) ]],
unsigned int iid [[instance_id]]) {
iTermBackgroundColorVertexFunctionOutput out;
float2 pixelSpacePosition = vertexArray[vertexID].position.xy + perInstanceUniforms[iid].offset.xy + offset[0];
float2 viewportSize = float2(*viewportSizePointer);
out.clipSpacePosition.xy = pixelSpacePosition / viewportSize;
out.clipSpacePosition.z = 0.0;
out.clipSpacePosition.w = 1;
out.color = perInstanceUniforms[iid].color;
return out;
}
fragment float4
iTermBackgroundColorFragmentShader(iTermBackgroundColorVertexFunctionOutput in [[stage_in]]) {
return in.color;
}
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
#import "iTermShaderTypes.h"
typedef struct {
float4 clipSpacePosition [[position]];
float2 textureCoordinate;
} iTermBackgroundImageVertexFunctionOutput;
vertex iTermBackgroundImageVertexFunctionOutput
iTermBackgroundImageVertexShader(uint vertexID [[ vertex_id ]],
constant iTermVertex *vertexArray [[ buffer(iTermVertexInputIndexVertices) ]],
constant vector_uint2 *viewportSizePointer [[ buffer(iTermVertexInputIndexViewportSize) ]]) {
iTermBackgroundImageVertexFunctionOutput out;
float2 pixelSpacePosition = vertexArray[vertexID].position.xy;
float2 viewportSize = float2(*viewportSizePointer);
out.clipSpacePosition.xy = pixelSpacePosition / viewportSize;
out.clipSpacePosition.z = 0.0;
out.clipSpacePosition.w = 1;
out.textureCoordinate = vertexArray[vertexID].textureCoordinate;
return out;
}
fragment float4
iTermBackgroundImageFragmentShader(iTermBackgroundImageVertexFunctionOutput in [[stage_in]],
texture2d<half> texture [[ texture(iTermTextureIndexPrimary) ]]) {
constexpr sampler textureSampler (mag_filter::linear,
min_filter::linear);
const half4 colorSample = texture.sample(textureSampler, in.textureCoordinate);
return float4(colorSample);
}
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
#import "iTermShaderTypes.h"
typedef struct {
float4 clipSpacePosition [[position]];
float2 textureCoordinate;
} iTermBadgeVertexFunctionOutput;
vertex iTermBadgeVertexFunctionOutput
iTermBadgeVertexShader(uint vertexID [[ vertex_id ]],
constant float2 *offset [[ buffer(iTermVertexInputIndexOffset) ]],
constant iTermVertex *vertexArray [[ buffer(iTermVertexInputIndexVertices) ]],
constant vector_uint2 *viewportSizePointer [[ buffer(iTermVertexInputIndexViewportSize) ]]) {
iTermBadgeVertexFunctionOutput out;
float2 pixelSpacePosition = vertexArray[vertexID].position.xy + offset[0];
float2 viewportSize = float2(*viewportSizePointer);
out.clipSpacePosition.xy = pixelSpacePosition / viewportSize;
out.clipSpacePosition.z = 0.0;
out.clipSpacePosition.w = 1;
out.textureCoordinate = vertexArray[vertexID].textureCoordinate;
return out;
}
fragment float4
iTermBadgeFragmentShader(iTermBadgeVertexFunctionOutput in [[stage_in]],
texture2d<half> texture [[ texture(iTermTextureIndexPrimary) ]]) {
constexpr sampler textureSampler(mag_filter::linear,
min_filter::linear);
const half4 colorSample = texture.sample(textureSampler, in.textureCoordinate);
colorSample.w *= 0.75;
return float4(colorSample);
}
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
#import "iTermShaderTypes.h"
typedef struct {
float4 clipSpacePosition [[position]];
float2 textureCoordinate;
} iTermBroadcastStripesVertexFunctionOutput;
vertex iTermBroadcastStripesVertexFunctionOutput
iTermBroadcastStripesVertexShader(uint vertexID [[ vertex_id ]],
constant iTermVertex *vertexArray [[ buffer(iTermVertexInputIndexVertices) ]],
constant vector_uint2 *viewportSizePointer [[ buffer(iTermVertexInputIndexViewportSize) ]]) {
iTermBroadcastStripesVertexFunctionOutput out;
float2 pixelSpacePosition = vertexArray[vertexID].position.xy;
float2 viewportSize = float2(*viewportSizePointer);
out.clipSpacePosition.xy = pixelSpacePosition / viewportSize;
out.clipSpacePosition.z = 0.0;
out.clipSpacePosition.w = 1;
out.textureCoordinate = vertexArray[vertexID].textureCoordinate;
return out;
}
fragment float4
iTermBroadcastStripesFragmentShader(iTermBroadcastStripesVertexFunctionOutput in [[stage_in]],
texture2d<half> texture [[ texture(iTermTextureIndexPrimary) ]]) {
constexpr sampler textureSampler(mag_filter::linear,
min_filter::linear);
const float2 remainder = float2(fmod(in.textureCoordinate.x, 1), fmod(in.textureCoordinate.y, 1));
const half4 colorSample = texture.sample(textureSampler, remainder);
return float4(colorSample);
}
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
#import "iTermShaderTypes.h"
typedef struct {
float4 clipSpacePosition [[position]];
float2 textureCoordinate;
} iTermCursorGuideVertexFunctionOutput;
vertex iTermCursorGuideVertexFunctionOutput
iTermCursorGuideVertexShader(uint vertexID [[ vertex_id ]],
constant float2 *offset [[ buffer(iTermVertexInputIndexOffset) ]],
constant iTermVertex *vertexArray [[ buffer(iTermVertexInputIndexVertices) ]],
constant vector_uint2 *viewportSizePointer [[ buffer(iTermVertexInputIndexViewportSize) ]],
constant iTermCursorGuidePIU *perInstanceUniforms [[ buffer(iTermVertexInputIndexPerInstanceUniforms) ]],
unsigned int iid [[instance_id]]) {
iTermCursorGuideVertexFunctionOutput out;
float2 pixelSpacePosition = vertexArray[vertexID].position.xy + perInstanceUniforms[iid].offset.xy + offset[0];
float2 viewportSize = float2(*viewportSizePointer);
out.clipSpacePosition.xy = pixelSpacePosition / viewportSize;
out.clipSpacePosition.z = 0.0;
out.clipSpacePosition.w = 1;
out.textureCoordinate = vertexArray[vertexID].textureCoordinate;
return out;
}
fragment float4
iTermCursorGuideFragmentShader(iTermCursorGuideVertexFunctionOutput in [[stage_in]],
texture2d<half> texture [[ texture(iTermTextureIndexPrimary) ]]) {
constexpr sampler textureSampler (mag_filter::linear,
min_filter::linear);
const half4 colorSample = texture.sample(textureSampler, in.textureCoordinate);
return float4(colorSample);
}
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
#import "iTermShaderTypes.h"
#pragma mark - Solid color cursor
typedef struct {
float4 clipSpacePosition [[position]];
float4 color;
} iTermCursorVertexFunctionOutput;
vertex iTermCursorVertexFunctionOutput
iTermCursorVertexShader(uint vertexID [[ vertex_id ]],
constant float2 *offset [[ buffer(iTermVertexInputIndexOffset) ]],
constant iTermVertex *vertexArray [[ buffer(iTermVertexInputIndexVertices) ]],
constant vector_uint2 *viewportSizePointer [[ buffer(iTermVertexInputIndexViewportSize) ]],
constant iTermCursorDescription *description [[ buffer(iTermVertexInputIndexCursorDescription) ]]) {
iTermCursorVertexFunctionOutput out;
float2 pixelSpacePosition = vertexArray[vertexID].position.xy + description->origin + offset[0];
float2 viewportSize = float2(*viewportSizePointer);
out.clipSpacePosition.xy = pixelSpacePosition / viewportSize;
out.clipSpacePosition.z = 0.0;
out.clipSpacePosition.w = 1;
out.color = description->color;
return out;
}
fragment float4
iTermCursorFragmentShader(iTermCursorVertexFunctionOutput in [[stage_in]]) {
return in.color;
}
#pragma mark - Texture-based cursor
typedef struct {
float4 clipSpacePosition [[position]];
float2 textureCoordinate;
} iTermTextureCursorVertexFunctionOutput;
vertex iTermTextureCursorVertexFunctionOutput
iTermTextureCursorVertexShader(uint vertexID [[ vertex_id ]],
constant float2 *offset [[ buffer(iTermVertexInputIndexOffset) ]],
constant iTermVertex *vertexArray [[ buffer(iTermVertexInputIndexVertices) ]],
constant vector_uint2 *viewportSizePointer [[ buffer(iTermVertexInputIndexViewportSize) ]],
constant iTermCursorDescription *description [[ buffer(iTermVertexInputIndexCursorDescription) ]]) {
iTermTextureCursorVertexFunctionOutput out;
float2 pixelSpacePosition = vertexArray[vertexID].position.xy + description->origin + offset[0];
float2 viewportSize = float2(*viewportSizePointer);
out.clipSpacePosition.xy = pixelSpacePosition / viewportSize;
out.clipSpacePosition.z = 0.0;
out.clipSpacePosition.w = 1;
out.textureCoordinate = vertexArray[vertexID].textureCoordinate;
return out;
}
fragment float4
iTermTextureCursorFragmentShader(iTermTextureCursorVertexFunctionOutput in [[stage_in]],
texture2d<half> texture [[ texture(iTermTextureIndexPrimary) ]]) {
constexpr sampler textureSampler (mag_filter::linear,
min_filter::linear);
const half4 colorSample = texture.sample(textureSampler, in.textureCoordinate);
return float4(colorSample);
}
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
#import "iTermShaderTypes.h"
typedef struct {
float4 clipSpacePosition [[position]];
float2 textureCoordinate;
} iTermMarkVertexFunctionOutput;
vertex iTermMarkVertexFunctionOutput
iTermMarkVertexShader(uint vertexID [[ vertex_id ]],
constant float2 *offset [[ buffer(iTermVertexInputIndexOffset) ]],
constant iTermVertex *vertexArray [[ buffer(iTermVertexInputIndexVertices) ]],
constant vector_uint2 *viewportSizePointer [[ buffer(iTermVertexInputIndexViewportSize) ]],
constant iTermMarkPIU *perInstanceUniforms [[ buffer(iTermVertexInputIndexPerInstanceUniforms) ]],
unsigned int iid [[instance_id]]) {
iTermMarkVertexFunctionOutput out;
float2 pixelSpacePosition = vertexArray[vertexID].position.xy + perInstanceUniforms[iid].offset.xy;
float2 viewportSize = float2(*viewportSizePointer);
out.clipSpacePosition.xy = pixelSpacePosition / viewportSize;
out.clipSpacePosition.z = 0.0;
out.clipSpacePosition.w = 1;
out.textureCoordinate = vertexArray[vertexID].textureCoordinate + perInstanceUniforms[iid].textureOffset;
return out;
}
fragment float4
iTermMarkFragmentShader(iTermMarkVertexFunctionOutput in [[stage_in]],
texture2d<half> texture [[ texture(iTermTextureIndexPrimary) ]]) {
constexpr sampler textureSampler(mag_filter::linear,
min_filter::linear);
const half4 colorSample = texture.sample(textureSampler, in.textureCoordinate);
return float4(colorSample);
}
#ifndef ITERM_
#define ShaderTypes_h
#include <simd/simd.h>
typedef enum iTermVertexInputIndex {
iTermVertexInputIndexVertices,
iTermVertexInputIndexViewportSize,
iTermVertexInputIndexPerInstanceUniforms,
iTermVertexInputIndexOffset,
iTermVertexInputIndexCursorDescription,
} iTermVertexInputIndex;
typedef enum iTermTextureIndex {
iTermTextureIndexPrimary = 0,
} iTermTextureIndex;
typedef enum {
iTermFragmentBufferIndexColorModels = 1 // Array of triples of 256-byte color tables in rgb order
} iTermFragmentBufferIndex;
typedef struct {
// Distance in pixel space from origin
vector_float2 position;
// Distance in texture space from origin
vector_float2 textureCoordinate;
} iTermVertex;
typedef struct {
// Offset from vertex
vector_float2 offset;
// Offset of source texture
vector_float2 textureOffset;
int colorModelIndex;
} iTermTextPIU;
typedef struct {
// Offset from vertex
vector_float2 offset;
// Offset of source texture
vector_float2 textureOffset;
} iTermMarkPIU;
typedef struct {
// Offset from vertex
vector_float2 offset;
// Background color
vector_float4 color;
} iTermBackgroundColorPIU;
typedef struct {
// Offset from vertex
vector_float2 offset;
} iTermCursorGuidePIU;
typedef struct {
vector_float4 color;
vector_float2 origin;
} iTermCursorDescription;
#endif
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
#import "iTermShaderTypes.h"
typedef struct {
float4 clipSpacePosition [[position]];
float2 textureCoordinate;
int colorModelIndex;
} iTermTextVertexFunctionOutput;
vertex iTermTextVertexFunctionOutput
iTermTextVertexShader(uint vertexID [[ vertex_id ]],
constant float2 *offset [[ buffer(iTermVertexInputIndexOffset) ]],
constant iTermVertex *vertexArray [[ buffer(iTermVertexInputIndexVertices) ]],
constant vector_uint2 *viewportSizePointer [[ buffer(iTermVertexInputIndexViewportSize) ]],
constant iTermTextPIU *perInstanceUniforms [[ buffer(iTermVertexInputIndexPerInstanceUniforms) ]],
unsigned int iid [[instance_id]]) {
iTermTextVertexFunctionOutput out;
float2 pixelSpacePosition = vertexArray[vertexID].position.xy + perInstanceUniforms[iid].offset.xy + offset[0];
float2 viewportSize = float2(*viewportSizePointer);
out.clipSpacePosition.xy = pixelSpacePosition / viewportSize;
out.clipSpacePosition.z = 0.0;
out.clipSpacePosition.w = 1;
out.textureCoordinate = vertexArray[vertexID].textureCoordinate + perInstanceUniforms[iid].textureOffset;
out.colorModelIndex = perInstanceUniforms[iid].colorModelIndex;
return out;
}
fragment float4
iTermTextFragmentShader(iTermTextVertexFunctionOutput in [[stage_in]],
texture2d<half> texture [[ texture(iTermTextureIndexPrimary) ]],
constant unsigned char *colorModels [[ buffer(iTermFragmentBufferIndexColorModels) ]]) {
constexpr sampler textureSampler(mag_filter::linear,
min_filter::linear);
const half4 bwColor = texture.sample(textureSampler, in.textureCoordinate);
if (bwColor.x == 1 && bwColor.y == 1 && bwColor.z == 1) {
// TODO
// This doesn't draw the background when the pixel doesn't contain any of the glyph.
// But this will look terrible over a background image, diagonal stripes, or badge. I should
// build a fixed number of color models that evenly sample the space of possible color
// models, then sample the color in the drawable and have the fragment shader interpolate
// between the nearest models for the background color of this pixel.
discard_fragment();
}
const short4 bwIntColor = static_cast<short4>(bwColor * 255);
const short4 bwIntIndices = bwIntColor * 3 + short4(0, 1, 2, 0);
// Base index for this color model
const int i = in.colorModelIndex * 256 * 3;
// Find RGB values to map colors in the black-on-white glyph to
const uchar4 rgba = uchar4(colorModels[i + bwIntIndices.x],
colorModels[i + bwIntIndices.y],
colorModels[i + bwIntIndices.z],
255);
return static_cast<float4>(rgba) / 255;
}
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