Skip to content

Commit ccd3640

Browse files
zwaldowskilucasderraugh
authored andcommitted
Graph View: Make performance livable for a large number of branches (#610)
* GIGraphView: Make drawing branch lines less expensive * GIGraphView: Fix occlusion check for a line on the boundary of a tile
1 parent 4df3579 commit ccd3640

File tree

1 file changed

+41
-45
lines changed

1 file changed

+41
-45
lines changed

GitUpKit/Interface/GIGraphView.m

Lines changed: 41 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -607,9 +607,12 @@ static inline CGFloat _SquareDistanceFromPointToLine(CGFloat x0, CGFloat y0, CGF
607607
return SQUARE(x1 * (y2 - y0) - y1 * (x2 - x0) + x2 * y0 - y2 * x0) / (SQUARE(y2 - y0) + SQUARE(x2 - x0));
608608
}
609609

610-
static void _DrawLine(GILine* line, CGContextRef context, CGFloat offset, CGFloat minY, CGFloat maxY) {
610+
- (void)drawLine:(GILine*)line inContext:(CGContextRef)context clampedToRect:(CGRect)dirtyRect {
611+
CGFloat offset = _graph.size.height;
611612
NSArray* nodes = line.nodes;
612613
NSUInteger count = nodes.count;
614+
CGFloat minY = CGRectGetMinY(dirtyRect);
615+
CGFloat maxY = CGRectGetMaxY(dirtyRect);
613616
BOOL recompute = YES;
614617

615618
// Generate list of node coordinates aka points
@@ -795,6 +798,7 @@ static void _DrawLine(GILine* line, CGContextRef context, CGFloat offset, CGFloa
795798
}
796799

797800
// Draw line
801+
CGContextBeginPath(context);
798802
BOOL visible = NO;
799803
size_t i = 0;
800804
while (1) {
@@ -819,7 +823,6 @@ static void _DrawLine(GILine* line, CGContextRef context, CGFloat offset, CGFloa
819823
if (!visible) {
820824
CGContextMoveToPoint(context, x0, y0);
821825
CGContextAddLineToPoint(context, x1, y1);
822-
CGContextStrokePath(context);
823826

824827
x0 = x1;
825828
y0 = y1;
@@ -831,7 +834,6 @@ static void _DrawLine(GILine* line, CGContextRef context, CGFloat offset, CGFloa
831834
// Draw line segment
832835
CGContextMoveToPoint(context, x0, y0);
833836
CGContextAddLineToPoint(context, x1, y1);
834-
CGContextStrokePath(context);
835837

836838
// Check if exiting visible area
837839
if (y0 < minY) {
@@ -846,8 +848,8 @@ static void _DrawLine(GILine* line, CGContextRef context, CGFloat offset, CGFloa
846848
y1 = pointList[i + 2].y;
847849
CGContextMoveToPoint(context, x0, y0);
848850
CGContextAddLineToPoint(context, x1, y1);
849-
CGContextStrokePath(context);
850851

852+
visible = YES;
851853
break; // We're done
852854
}
853855

@@ -860,12 +862,26 @@ static void _DrawLine(GILine* line, CGContextRef context, CGFloat offset, CGFloa
860862
CGFloat y2 = pointList[i + 3].y;
861863
CGContextMoveToPoint(context, x0, y0);
862864
CGContextAddQuadCurveToPoint(context, x1, y1, x2, y2);
863-
CGContextStrokePath(context);
864865

865866
i += 3;
866867
visible = YES;
867868
}
868869

870+
BOOL shouldDraw = visible && [self needsToDrawRect:CGRectInset(CGContextGetPathBoundingBox(context), -kMainLineWidth, -kMainLineWidth)];
871+
if (shouldDraw) {
872+
XLOG_DEBUG_CHECK(!line.virtual || [[(GINode*)line.nodes[0] layer] index] == 0);
873+
CGContextSaveGState(context);
874+
CGContextSetLineWidth(context, line.branchMainLine && !line.virtual ? kMainLineWidth : kSubLineWidth);
875+
CGContextSetLineJoin(context, kCGLineJoinRound);
876+
if (line.virtual) {
877+
const CGFloat pattern[] = {4, 2};
878+
CGContextSetLineDash(context, 0, pattern, 2);
879+
}
880+
CGContextSetStrokeColorWithColor(context, line.color.CGColor);
881+
CGContextStrokePath(context);
882+
CGContextRestoreGState(context);
883+
}
884+
869885
if (recompute) {
870886
data = [[NSData alloc] initWithBytesNoCopy:pointList length:(pointCount * sizeof(CGPoint)) freeWhenDone:YES];
871887
objc_setAssociatedObject(line, _associatedObjectDataKey, data, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
@@ -1433,8 +1449,9 @@ - (void)drawRect:(NSRect)dirtyRect {
14331449
NSUInteger layerCount = layers.count;
14341450
NSUInteger startIndex = layerCount ? [self _indexOfLayerContainingPosition:(dirtyRect.origin.y + dirtyRect.size.height + kOverdrawMargin)] : NSNotFound;
14351451
NSUInteger endIndex = layerCount ? [self _indexOfLayerContainingPosition:dirtyRect.origin.y - kOverdrawMargin] : 0;
1452+
NSIndexSet* indexes = layerCount ? [[NSIndexSet alloc] initWithIndexesInRange:NSMakeRange(startIndex, MIN(endIndex + 1, layerCount) - startIndex)] : [[NSIndexSet alloc] init];
14361453
CGFloat offset = _graph.size.height;
1437-
NSMutableArray* lines = [[NSMutableArray alloc] init];
1454+
NSMutableSet* lines = [[NSMutableSet alloc] init];
14381455

14391456
// Cache attributes
14401457
static NSDictionary* tagAttributes = nil;
@@ -1480,13 +1497,12 @@ - (void)drawRect:(NSRect)dirtyRect {
14801497
// Draw grid
14811498
CGContextSetLineWidth(context, 1);
14821499
CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 0.25);
1483-
for (NSUInteger index = startIndex; index <= endIndex; ++index) {
1484-
GILayer* layer = layers[index];
1500+
[layers enumerateObjectsAtIndexes:indexes options:0 usingBlock:^(GILayer* layer, NSUInteger index, BOOL *stop) {
14851501
CGFloat y = CONVERT_Y(offset - layer.y);
14861502
CGContextMoveToPoint(context, dirtyRect.origin.x, y);
14871503
CGContextAddLineToPoint(context, dirtyRect.origin.x + dirtyRect.size.width, y);
14881504
CGContextStrokePath(context);
1489-
}
1505+
}];
14901506
for (NSUInteger i = 0; i < 100; ++i) {
14911507
CGFloat x = CONVERT_X(i);
14921508
CGContextMoveToPoint(context, x, dirtyRect.origin.y);
@@ -1495,51 +1511,31 @@ - (void)drawRect:(NSRect)dirtyRect {
14951511
}
14961512
#endif
14971513

1498-
// Find all lines in the drawing area
1499-
for (NSUInteger index = startIndex; index <= endIndex; ++index) {
1500-
GILayer* layer = layers[index];
1501-
for (GILine* line in layer.lines) {
1502-
if (![lines containsObject:line]) {
1503-
[lines addObject:line];
1504-
}
1505-
}
1506-
}
1507-
1508-
// Draw lines
1509-
if (lines.count) {
1510-
CGContextSetLineJoin(context, kCGLineJoinMiter);
1511-
1514+
// Draw all lines in the drawing area
1515+
{
15121516
// Can’t multiply against a dark background.
15131517
if (!self.effectiveAppearance.matchesDarkAppearance) {
15141518
CGContextSetBlendMode(context, kCGBlendModeMultiply);
15151519
}
15161520

1517-
for (NSInteger i = 0, count = lines.count; i < count; ++i) {
1518-
GILine* line = lines[i];
1519-
BOOL onBranchMainLine = line.branchMainLine;
1520-
BOOL virtualLine = line.virtual;
1521-
XLOG_DEBUG_CHECK(!virtualLine || [[(GINode*)line.nodes[0] layer] index] == 0);
1522-
if (virtualLine) {
1523-
onBranchMainLine = NO;
1524-
}
1525-
CGContextSetLineWidth(context, onBranchMainLine ? kMainLineWidth : kSubLineWidth);
1526-
CGContextSetStrokeColorWithColor(context, line.color.CGColor);
1527-
if (virtualLine) {
1528-
const CGFloat pattern[] = {4, 2};
1529-
CGContextSetLineDash(context, 0, pattern, 2);
1530-
}
1531-
_DrawLine(line, context, offset, dirtyRect.origin.y, dirtyRect.origin.y + dirtyRect.size.height);
1532-
if (virtualLine) {
1533-
CGContextSetLineDash(context, 0, NULL, 0);
1534-
}
1535-
}
1521+
[layers enumerateObjectsAtIndexes:indexes
1522+
options:0
1523+
usingBlock:^(GILayer* layer, NSUInteger index, BOOL* stop) {
1524+
for (GILine* line in layer.lines) {
1525+
if ([lines containsObject:line]) {
1526+
continue;
1527+
}
1528+
[self drawLine:line inContext:context clampedToRect:dirtyRect];
1529+
[lines addObject:line];
1530+
}
1531+
}];
1532+
15361533
CGContextSetBlendMode(context, kCGBlendModeNormal);
15371534
}
15381535

15391536
// Draw nodes
15401537
CGContextSetLineWidth(context, 1);
1541-
for (NSUInteger index = startIndex; index <= endIndex; ++index) {
1542-
GILayer* layer = layers[index];
1538+
[layers enumerateObjectsAtIndexes:indexes options:0 usingBlock:^(GILayer* layer, NSUInteger index, BOOL *stop) {
15431539
CGFloat y = CONVERT_Y(offset - layer.y);
15441540
for (GINode* node in layer.nodes) {
15451541
CGFloat x = CONVERT_X(node.x);
@@ -1566,7 +1562,7 @@ - (void)drawRect:(NSRect)dirtyRect {
15661562

15671563
_DrawNode(node, context, x, y);
15681564
}
1569-
}
1565+
}];
15701566

15711567
#if __DEBUG_MAIN_LINE__ || __DEBUG_DESCENDANTS__ || __DEBUG_ANCESTORS__
15721568
// Draw highlighted debug nodes

0 commit comments

Comments
 (0)