Skip to content
This repository was archived by the owner on Feb 2, 2023. It is now read-only.

Commit d6289d5

Browse files
committed
Merge pull request #993 from facebook/OptimizingTextAndBridge
Optimizations for ASTextNode renderer; Optimizations for ASDisplayNode Bridge.
2 parents 1ec1957 + 99fbc97 commit d6289d5

6 files changed

Lines changed: 107 additions & 25 deletions

File tree

AsyncDisplayKit/ASTextNode.mm

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,15 @@ - (instancetype)initWithRenderer:(ASTextKitRenderer *)renderer
6262
- (void)dealloc
6363
{
6464
CGColorRelease(_backgroundColor);
65+
66+
// Destruction of the layout managers/containers/text storage is quite
67+
// expensive, and can take some time, so we dispatch onto a bg queue to
68+
// actually dealloc.
69+
__block ASTextKitRenderer *renderer = _renderer;
70+
ASPerformBlockOnBackgroundThread(^{
71+
renderer = nil;
72+
});
73+
_renderer = nil;
6574
}
6675

6776
@end
@@ -157,6 +166,8 @@ - (void)dealloc
157166
if (_shadowColor != NULL) {
158167
CGColorRelease(_shadowColor);
159168
}
169+
170+
[self _invalidateRenderer];
160171

161172
if (_longPressGestureRecognizer) {
162173
_longPressGestureRecognizer.delegate = nil;
@@ -189,6 +200,8 @@ - (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
189200
return [[self _renderer] size];
190201
}
191202

203+
// FIXME: Re-evaluate if it is still the right decision to clear the renderer at this stage.
204+
// This code was written before TextKit and when 512MB devices were still the overwhelming majority.
192205
- (void)displayDidFinish
193206
{
194207
[super displayDidFinish];
@@ -263,16 +276,17 @@ - (ASTextKitAttributes)_rendererAttributes
263276
- (void)_invalidateRenderer
264277
{
265278
ASDN::MutexLocker l(_rendererLock);
279+
266280
if (_renderer) {
267281
// Destruction of the layout managers/containers/text storage is quite
268282
// expensive, and can take some time, so we dispatch onto a bg queue to
269283
// actually dealloc.
270284
__block ASTextKitRenderer *renderer = _renderer;
271-
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
285+
ASPerformBlockOnBackgroundThread(^{
272286
renderer = nil;
273287
});
288+
_renderer = nil;
274289
}
275-
_renderer = nil;
276290
}
277291

278292
- (void)_invalidateRendererIfNeeded
@@ -320,7 +334,8 @@ - (BOOL)_needInvalidateRenderer:(CGSize)newSize
320334

321335
#pragma mark - Modifying User Text
322336

323-
- (void)setAttributedString:(NSAttributedString *)attributedString {
337+
- (void)setAttributedString:(NSAttributedString *)attributedString
338+
{
324339
if (ASObjectIsEqual(attributedString, _attributedString)) {
325340
return;
326341
}

AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -446,19 +446,27 @@ - (UIViewContentMode)contentMode
446446
{
447447
_bridge_prologue;
448448
if (__loaded) {
449-
return ASDisplayNodeUIContentModeFromCAContentsGravity(_layer.contentsGravity);
449+
if (_flags.layerBacked) {
450+
return ASDisplayNodeUIContentModeFromCAContentsGravity(_layer.contentsGravity);
451+
} else {
452+
return _view.contentMode;
453+
}
450454
} else {
451455
return self.pendingViewState.contentMode;
452456
}
453457
}
454458

455-
- (void)setContentMode:(UIViewContentMode)mode
459+
- (void)setContentMode:(UIViewContentMode)contentMode
456460
{
457461
_bridge_prologue;
458462
if (__loaded) {
459-
_layer.contentsGravity = ASDisplayNodeCAContentsGravityFromUIContentMode(mode);
463+
if (_flags.layerBacked) {
464+
_layer.contentsGravity = ASDisplayNodeCAContentsGravityFromUIContentMode(contentMode);
465+
} else {
466+
_view.contentMode = contentMode;
467+
}
460468
} else {
461-
self.pendingViewState.contentMode = mode;
469+
self.pendingViewState.contentMode = contentMode;
462470
}
463471
}
464472

AsyncDisplayKit/Private/ASInternalHelpers.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ ASDISPLAYNODE_EXTERN_C_BEGIN
1818
BOOL ASSubclassOverridesSelector(Class superclass, Class subclass, SEL selector);
1919
BOOL ASSubclassOverridesClassSelector(Class superclass, Class subclass, SEL selector);
2020
void ASPerformBlockOnMainThread(void (^block)());
21+
void ASPerformBlockOnBackgroundThread(void (^block)()); // DISPATCH_QUEUE_PRIORITY_DEFAULT
2122

2223
CGFloat ASScreenScale();
2324

AsyncDisplayKit/Private/ASInternalHelpers.mm

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,18 @@ void ASPerformBlockOnMainThread(void (^block)())
5757
}
5858
}
5959

60+
void ASPerformBlockOnBackgroundThread(void (^block)())
61+
{
62+
if ([NSThread isMainThread]) {
63+
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
64+
block();
65+
});
66+
} else {
67+
block();
68+
}
69+
}
70+
71+
6072
CGFloat ASScreenScale()
6173
{
6274
static CGFloat _scale;

AsyncDisplayKit/Private/_ASCoreAnimationExtras.mm

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,31 @@ UIViewContentMode ASDisplayNodeUIContentModeFromNSString(NSString *string)
119119
return nil;
120120
}
121121

122+
#define ContentModeCacheSize 10
122123
UIViewContentMode ASDisplayNodeUIContentModeFromCAContentsGravity(NSString *const contentsGravity)
123124
{
124-
for (int i=0; i < ARRAY_COUNT(UIContentModeCAGravityLUT); i++) {
125+
static int currentCacheIndex = 0;
126+
static NSMutableArray *cachedStrings = [NSMutableArray arrayWithCapacity:ContentModeCacheSize];
127+
static UIViewContentMode cachedModes[ContentModeCacheSize] = {};
128+
129+
NSInteger foundCacheIndex = [cachedStrings indexOfObjectIdenticalTo:contentsGravity];
130+
if (foundCacheIndex != NSNotFound && foundCacheIndex < ContentModeCacheSize) {
131+
return cachedModes[foundCacheIndex];
132+
}
133+
134+
for (int i = 0; i < ARRAY_COUNT(UIContentModeCAGravityLUT); i++) {
125135
if (ASObjectIsEqual(UIContentModeCAGravityLUT[i].string, contentsGravity)) {
126-
return UIContentModeCAGravityLUT[i].contentMode;
136+
UIViewContentMode foundContentMode = UIContentModeCAGravityLUT[i].contentMode;
137+
138+
if (currentCacheIndex < ContentModeCacheSize) {
139+
// Cache the input value. This is almost always a different pointer than in our LUT and will frequently
140+
// be the same value for an overwhelming majority of inputs.
141+
[cachedStrings addObject:contentsGravity];
142+
cachedModes[currentCacheIndex] = foundContentMode;
143+
currentCacheIndex++;
144+
}
145+
146+
return foundContentMode;
127147
}
128148
}
129149

AsyncDisplayKit/TextKit/ASTextKitRenderer.mm

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@
3232

3333
@implementation ASTextKitRenderer {
3434
CGSize _calculatedSize;
35+
BOOL _sizeIsCalculated;
3536
}
37+
@synthesize attributes = _attributes, context = _context, shadower = _shadower, truncater = _truncater;
3638

3739
#pragma mark - Initialization
3840

@@ -42,30 +44,50 @@ - (instancetype)initWithTextKitAttributes:(const ASTextKitAttributes &)attribute
4244
if (self = [super init]) {
4345
_constrainedSize = constrainedSize;
4446
_attributes = attributes;
47+
_sizeIsCalculated = NO;
48+
}
49+
return self;
50+
}
4551

52+
- (ASTextKitShadower *)shadower
53+
{
54+
if (!_shadower) {
55+
ASTextKitAttributes attributes = _attributes;
4656
_shadower = [[ASTextKitShadower alloc] initWithShadowOffset:attributes.shadowOffset
4757
shadowColor:attributes.shadowColor
4858
shadowOpacity:attributes.shadowOpacity
4959
shadowRadius:attributes.shadowRadius];
60+
}
61+
return _shadower;
62+
}
5063

64+
- (ASTextKitTailTruncater *)truncater
65+
{
66+
if (!_truncater) {
67+
ASTextKitAttributes attributes = _attributes;
5168
// We must inset the constrained size by the size of the shadower.
52-
CGSize shadowConstrainedSize = [_shadower insetSizeWithConstrainedSize:_constrainedSize];
69+
CGSize shadowConstrainedSize = [[self shadower] insetSizeWithConstrainedSize:_constrainedSize];
70+
_truncater = [[ASTextKitTailTruncater alloc] initWithContext:[self context]
71+
truncationAttributedString:attributes.truncationAttributedString
72+
avoidTailTruncationSet:attributes.avoidTailTruncationSet ?: _defaultAvoidTruncationCharacterSet()
73+
constrainedSize:shadowConstrainedSize];
74+
}
75+
return _truncater;
76+
}
5377

78+
- (ASTextKitContext *)context
79+
{
80+
if (!_context) {
81+
ASTextKitAttributes attributes = _attributes;
82+
CGSize shadowConstrainedSize = [[self shadower] insetSizeWithConstrainedSize:_constrainedSize];
5483
_context = [[ASTextKitContext alloc] initWithAttributedString:attributes.attributedString
5584
lineBreakMode:attributes.lineBreakMode
5685
maximumNumberOfLines:attributes.maximumNumberOfLines
5786
exclusionPaths:attributes.exclusionPaths
5887
constrainedSize:shadowConstrainedSize
5988
layoutManagerFactory:attributes.layoutManagerFactory];
60-
61-
_truncater = [[ASTextKitTailTruncater alloc] initWithContext:_context
62-
truncationAttributedString:attributes.truncationAttributedString
63-
avoidTailTruncationSet:attributes.avoidTailTruncationSet ?: _defaultAvoidTruncationCharacterSet()
64-
constrainedSize:shadowConstrainedSize];
65-
66-
[self _calculateSize];
6789
}
68-
return self;
90+
return _context;
6991
}
7092

7193
#pragma mark - Sizing
@@ -74,14 +96,14 @@ - (void)_calculateSize
7496
{
7597
// Force glyph generation and layout, which may not have happened yet (and isn't triggered by
7698
// -usedRectForTextContainer:).
77-
[_context performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
99+
[[self context] performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
78100
[layoutManager ensureLayoutForTextContainer:textContainer];
79101
}];
80102

81103

82104
CGRect constrainedRect = {CGPointZero, _constrainedSize};
83105
__block CGRect boundingRect;
84-
[_context performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
106+
[[self context] performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
85107
boundingRect = [layoutManager usedRectForTextContainer:textContainer];
86108
}];
87109

@@ -94,6 +116,10 @@ - (void)_calculateSize
94116

95117
- (CGSize)size
96118
{
119+
if (!_sizeIsCalculated) {
120+
[self _calculateSize];
121+
_sizeIsCalculated = YES;
122+
}
97123
return _calculatedSize;
98124
}
99125

@@ -104,13 +130,13 @@ - (void)drawInContext:(CGContextRef)context bounds:(CGRect)bounds;
104130
// We add an assertion so we can track the rare conditions where a graphics context is not present
105131
ASDisplayNodeAssertNotNil(context, @"This is no good without a context.");
106132

107-
CGRect shadowInsetBounds = [_shadower insetRectWithConstrainedRect:bounds];
133+
CGRect shadowInsetBounds = [[self shadower] insetRectWithConstrainedRect:bounds];
108134

109135
CGContextSaveGState(context);
110-
[_shadower setShadowInContext:context];
136+
[[self shadower] setShadowInContext:context];
111137
UIGraphicsPushContext(context);
112138

113-
[_context performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
139+
[[self context] performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
114140
NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer];
115141
[layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:shadowInsetBounds.origin];
116142
[layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:shadowInsetBounds.origin];
@@ -125,7 +151,7 @@ - (void)drawInContext:(CGContextRef)context bounds:(CGRect)bounds;
125151
- (NSUInteger)lineCount
126152
{
127153
__block NSUInteger lineCount = 0;
128-
[_context performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
154+
[[self context] performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
129155
for (NSRange lineRange = { 0, 0 }; NSMaxRange(lineRange) < [layoutManager numberOfGlyphs]; lineCount++) {
130156
[layoutManager lineFragmentRectForGlyphAtIndex:NSMaxRange(lineRange) effectiveRange:&lineRange];
131157
}
@@ -135,7 +161,7 @@ - (NSUInteger)lineCount
135161

136162
- (std::vector<NSRange>)visibleRanges
137163
{
138-
return _truncater.visibleRanges;
164+
return [self truncater].visibleRanges;
139165
}
140166

141167
@end

0 commit comments

Comments
 (0)