mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-13 01:40:12 +00:00
Add an experimental framesetter cache in ASTextNode2 (#1063)
* Add an experimental framesetter cache in ASTextNode2, and stop keeping framesetters around * Update configuration schema * Fix imports * Fix import again and remove set statement
This commit is contained in:
parent
c5b1d09b49
commit
b136e84b4e
@ -29,6 +29,7 @@
|
|||||||
- Optimize ASDisplayNode -> ASNodeController reference by removing weak proxy and objc associated objects. [Adlai Holler](https://github.com/Adlai-Holler)
|
- Optimize ASDisplayNode -> ASNodeController reference by removing weak proxy and objc associated objects. [Adlai Holler](https://github.com/Adlai-Holler)
|
||||||
- Remove CA transaction signpost injection because it causes more transactions and is too chatty. [Adlai Holler](https://github.com/Adlai-Holler)
|
- Remove CA transaction signpost injection because it causes more transactions and is too chatty. [Adlai Holler](https://github.com/Adlai-Holler)
|
||||||
- Optimize display node accessibility by not creating attributed & non-attributed copies of hint, label, and value. [Adlai Holler](https://github.com/Adlai-Holler)
|
- Optimize display node accessibility by not creating attributed & non-attributed copies of hint, label, and value. [Adlai Holler](https://github.com/Adlai-Holler)
|
||||||
|
- Add an experimental feature that reuses CTFramesetter objects in ASTextNode2 to improve performance. [Adlai Holler](https://github.com/Adlai-Holler)
|
||||||
|
|
||||||
|
|
||||||
## 2.7
|
## 2.7
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
"exp_network_image_queue",
|
"exp_network_image_queue",
|
||||||
"exp_dealloc_queue_v2",
|
"exp_dealloc_queue_v2",
|
||||||
"exp_collection_teardown",
|
"exp_collection_teardown",
|
||||||
|
"exp_framesetter_cache"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,6 +27,7 @@ typedef NS_OPTIONS(NSUInteger, ASExperimentalFeatures) {
|
|||||||
ASExperimentalNetworkImageQueue = 1 << 5, // exp_network_image_queue
|
ASExperimentalNetworkImageQueue = 1 << 5, // exp_network_image_queue
|
||||||
ASExperimentalDeallocQueue = 1 << 6, // exp_dealloc_queue_v2
|
ASExperimentalDeallocQueue = 1 << 6, // exp_dealloc_queue_v2
|
||||||
ASExperimentalCollectionTeardown = 1 << 7, // exp_collection_teardown
|
ASExperimentalCollectionTeardown = 1 << 7, // exp_collection_teardown
|
||||||
|
ASExperimentalFramesetterCache = 1 << 8, // exp_framesetter_cache
|
||||||
ASExperimentalFeatureAll = 0xFFFFFFFF
|
ASExperimentalFeatureAll = 0xFFFFFFFF
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -23,7 +23,8 @@ NSArray<NSString *> *ASExperimentalFeaturesGetNames(ASExperimentalFeatures flags
|
|||||||
@"exp_infer_layer_defaults",
|
@"exp_infer_layer_defaults",
|
||||||
@"exp_network_image_queue",
|
@"exp_network_image_queue",
|
||||||
@"exp_dealloc_queue_v2",
|
@"exp_dealloc_queue_v2",
|
||||||
@"exp_collection_teardown"]));
|
@"exp_collection_teardown",
|
||||||
|
@"exp_framesetter_cache"]));
|
||||||
|
|
||||||
if (flags == ASExperimentalFeatureAll) {
|
if (flags == ASExperimentalFeatureAll) {
|
||||||
return allNames;
|
return allNames;
|
||||||
|
|||||||
@ -225,8 +225,6 @@ AS_EXTERN const CGSize ASTextContainerMaxSize;
|
|||||||
@property (nonatomic, readonly) NSAttributedString *text;
|
@property (nonatomic, readonly) NSAttributedString *text;
|
||||||
///< The text range in full text
|
///< The text range in full text
|
||||||
@property (nonatomic, readonly) NSRange range;
|
@property (nonatomic, readonly) NSRange range;
|
||||||
///< CTFrameSetter
|
|
||||||
@property (nonatomic, readonly) CTFramesetterRef frameSetter;
|
|
||||||
///< CTFrame
|
///< CTFrame
|
||||||
@property (nonatomic, readonly) CTFrameRef frame;
|
@property (nonatomic, readonly) CTFrameRef frame;
|
||||||
///< Array of `ASTextLine`, no truncated
|
///< Array of `ASTextLine`, no truncated
|
||||||
|
|||||||
@ -16,11 +16,15 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#import <AsyncDisplayKit/ASTextLayout.h>
|
#import <AsyncDisplayKit/ASTextLayout.h>
|
||||||
|
|
||||||
|
#import <AsyncDisplayKit/ASConfigurationInternal.h>
|
||||||
#import <AsyncDisplayKit/ASTextUtilities.h>
|
#import <AsyncDisplayKit/ASTextUtilities.h>
|
||||||
#import <AsyncDisplayKit/ASTextAttribute.h>
|
#import <AsyncDisplayKit/ASTextAttribute.h>
|
||||||
#import <AsyncDisplayKit/NSAttributedString+ASText.h>
|
#import <AsyncDisplayKit/NSAttributedString+ASText.h>
|
||||||
#import <AsyncDisplayKit/ASInternalHelpers.h>
|
#import <AsyncDisplayKit/ASInternalHelpers.h>
|
||||||
|
|
||||||
|
#import <pthread.h>
|
||||||
|
|
||||||
const CGSize ASTextContainerMaxSize = (CGSize){0x100000, 0x100000};
|
const CGSize ASTextContainerMaxSize = (CGSize){0x100000, 0x100000};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -320,7 +324,6 @@ dispatch_semaphore_signal(_lock);
|
|||||||
@property (nonatomic) NSAttributedString *text;
|
@property (nonatomic) NSAttributedString *text;
|
||||||
@property (nonatomic) NSRange range;
|
@property (nonatomic) NSRange range;
|
||||||
|
|
||||||
@property (nonatomic) CTFramesetterRef frameSetter;
|
|
||||||
@property (nonatomic) CTFrameRef frame;
|
@property (nonatomic) CTFrameRef frame;
|
||||||
@property (nonatomic) NSArray *lines;
|
@property (nonatomic) NSArray *lines;
|
||||||
@property (nonatomic) ASTextLine *truncatedLine;
|
@property (nonatomic) ASTextLine *truncatedLine;
|
||||||
@ -484,10 +487,71 @@ dispatch_semaphore_signal(_lock);
|
|||||||
frameAttrs[(id)kCTFrameProgressionAttributeName] = @(kCTFrameProgressionRightToLeft);
|
frameAttrs[(id)kCTFrameProgressionAttributeName] = @(kCTFrameProgressionRightToLeft);
|
||||||
}
|
}
|
||||||
|
|
||||||
// create CoreText objects
|
/*
|
||||||
ctSetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)text);
|
* Framesetter cache.
|
||||||
|
* Framesetters can only be used by one thread at a time.
|
||||||
|
* Create a CFSet with no callbacks (raw pointers) to keep track of which
|
||||||
|
* framesetters are in use on other threads. If the one for our string is already in use,
|
||||||
|
* just create a new one. This should be pretty rare.
|
||||||
|
*/
|
||||||
|
static pthread_mutex_t busyFramesettersLock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
static NSCache<NSAttributedString *, id> *framesetterCache;
|
||||||
|
static CFMutableSetRef busyFramesetters;
|
||||||
|
static dispatch_once_t onceToken;
|
||||||
|
dispatch_once(&onceToken, ^{
|
||||||
|
if (ASActivateExperimentalFeature(ASExperimentalFramesetterCache)) {
|
||||||
|
framesetterCache = [[NSCache alloc] init];
|
||||||
|
framesetterCache.name = @"org.TextureGroup.Texture.framesetterCache";
|
||||||
|
busyFramesetters = CFSetCreateMutable(NULL, 0, NULL);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
BOOL haveCached = NO, useCached = NO;
|
||||||
|
if (framesetterCache) {
|
||||||
|
// Check if there's one in the cache.
|
||||||
|
ctSetter = (__bridge_retained CTFramesetterRef)[framesetterCache objectForKey:text];
|
||||||
|
|
||||||
|
if (ctSetter) {
|
||||||
|
haveCached = YES;
|
||||||
|
|
||||||
|
// Check-and-set busy on the cached one.
|
||||||
|
pthread_mutex_lock(&busyFramesettersLock);
|
||||||
|
BOOL busy = CFSetContainsValue(busyFramesetters, ctSetter);
|
||||||
|
if (!busy) {
|
||||||
|
CFSetAddValue(busyFramesetters, ctSetter);
|
||||||
|
useCached = YES;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&busyFramesettersLock);
|
||||||
|
|
||||||
|
// Release if it was busy.
|
||||||
|
if (busy) {
|
||||||
|
CFRelease(ctSetter);
|
||||||
|
ctSetter = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a framesetter if needed.
|
||||||
|
if (!ctSetter) {
|
||||||
|
ctSetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)text);
|
||||||
|
}
|
||||||
|
|
||||||
if (!ctSetter) FAIL_AND_RETURN
|
if (!ctSetter) FAIL_AND_RETURN
|
||||||
ctFrame = CTFramesetterCreateFrame(ctSetter, ASTextCFRangeFromNSRange(range), cgPath, (CFDictionaryRef)frameAttrs);
|
ctFrame = CTFramesetterCreateFrame(ctSetter, ASTextCFRangeFromNSRange(range), cgPath, (CFDictionaryRef)frameAttrs);
|
||||||
|
|
||||||
|
// Return to cache.
|
||||||
|
if (framesetterCache) {
|
||||||
|
if (useCached) {
|
||||||
|
// If reused: mark available.
|
||||||
|
pthread_mutex_lock(&busyFramesettersLock);
|
||||||
|
CFSetRemoveValue(busyFramesetters, ctSetter);
|
||||||
|
pthread_mutex_unlock(&busyFramesettersLock);
|
||||||
|
} else if (!haveCached) {
|
||||||
|
// If first framesetter, add to cache.
|
||||||
|
[framesetterCache setObject:(__bridge id)ctSetter forKey:text];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!ctFrame) FAIL_AND_RETURN
|
if (!ctFrame) FAIL_AND_RETURN
|
||||||
lines = [NSMutableArray new];
|
lines = [NSMutableArray new];
|
||||||
ctLines = CTFrameGetLines(ctFrame);
|
ctLines = CTFrameGetLines(ctFrame);
|
||||||
@ -857,8 +921,7 @@ dispatch_semaphore_signal(_lock);
|
|||||||
if (attachments.count == 0) {
|
if (attachments.count == 0) {
|
||||||
attachments = attachmentRanges = attachmentRects = nil;
|
attachments = attachmentRanges = attachmentRects = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
layout.frameSetter = ctSetter;
|
|
||||||
layout.frame = ctFrame;
|
layout.frame = ctFrame;
|
||||||
layout.lines = lines;
|
layout.lines = lines;
|
||||||
layout.truncatedLine = truncatedLine;
|
layout.truncatedLine = truncatedLine;
|
||||||
@ -903,14 +966,6 @@ dispatch_semaphore_signal(_lock);
|
|||||||
return layouts;
|
return layouts;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setFrameSetter:(CTFramesetterRef)frameSetter {
|
|
||||||
if (_frameSetter != frameSetter) {
|
|
||||||
if (frameSetter) CFRetain(frameSetter);
|
|
||||||
if (_frameSetter) CFRelease(_frameSetter);
|
|
||||||
_frameSetter = frameSetter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setFrame:(CTFrameRef)frame {
|
- (void)setFrame:(CTFrameRef)frame {
|
||||||
if (_frame != frame) {
|
if (_frame != frame) {
|
||||||
if (frame) CFRetain(frame);
|
if (frame) CFRetain(frame);
|
||||||
@ -920,7 +975,6 @@ dispatch_semaphore_signal(_lock);
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc {
|
- (void)dealloc {
|
||||||
if (_frameSetter) CFRelease(_frameSetter);
|
|
||||||
if (_frame) CFRelease(_frame);
|
if (_frame) CFRelease(_frame);
|
||||||
if (_lineRowsIndex) free(_lineRowsIndex);
|
if (_lineRowsIndex) free(_lineRowsIndex);
|
||||||
if (_lineRowsEdge) free(_lineRowsEdge);
|
if (_lineRowsEdge) free(_lineRowsEdge);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user