mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-16 03:09:56 +00:00
Efficiency improvements to consolidate display events for the window-less preload range.
This commit is contained in:
parent
7f59beb195
commit
9669f147ba
@ -192,6 +192,29 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
return [_ASDisplayLayer class];
|
||||
}
|
||||
|
||||
+ (void)scheduleNodeForDisplay:(ASDisplayNode *)node
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
static NSMutableSet *nodesToDisplay = nil;
|
||||
static BOOL displayScheduled = NO;
|
||||
if (!nodesToDisplay) {
|
||||
nodesToDisplay = [[NSMutableSet alloc] init];
|
||||
}
|
||||
[nodesToDisplay addObject:node];
|
||||
if (!displayScheduled) {
|
||||
displayScheduled = YES;
|
||||
// It's essenital that any layout pass that is scheduled during the current
|
||||
// runloop has a chance to be applied / scheduled, so always perform this after the current runloop.
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
displayScheduled = NO;
|
||||
for (ASDisplayNode *node in nodesToDisplay) {
|
||||
[node __recursivelyTriggerDisplayAndBlock:NO];
|
||||
}
|
||||
nodesToDisplay = nil;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
|
||||
- (void)_staticInitialize
|
||||
@ -705,6 +728,20 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
[self displayImmediately];
|
||||
}
|
||||
|
||||
- (void)__setNeedsDisplay
|
||||
{
|
||||
ASDisplayNode *rasterizedContainerNode = [self __rasterizedContainerNode];
|
||||
if (rasterizedContainerNode) {
|
||||
[rasterizedContainerNode setNeedsDisplay];
|
||||
} else {
|
||||
[_layer setNeedsDisplay];
|
||||
|
||||
if (_layer && !self.isSynchronous && self.displaysAsynchronously) {
|
||||
[ASDisplayNode scheduleNodeForDisplay:self];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)__setNeedsLayout
|
||||
{
|
||||
ASDisplayNodeAssertThreadAffinity(self);
|
||||
@ -1417,7 +1454,7 @@ static NSInteger incrementIfFound(NSInteger i) {
|
||||
[_placeholderLayer removeFromSuperlayer];
|
||||
}
|
||||
|
||||
void recursivelyEnsureDisplayForLayer(CALayer *layer)
|
||||
void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
||||
{
|
||||
// This recursion must handle layers in various states:
|
||||
// 1. Just added to hierarchy, CA hasn't yet called -display
|
||||
@ -1436,22 +1473,24 @@ void recursivelyEnsureDisplayForLayer(CALayer *layer)
|
||||
|
||||
// Kick off the recursion first, so that all necessary display calls are sent and the displayQueue is full of parallelizable work.
|
||||
for (CALayer *sublayer in layer.sublayers) {
|
||||
recursivelyEnsureDisplayForLayer(sublayer);
|
||||
recursivelyTriggerDisplayForLayer(sublayer, shouldBlock);
|
||||
}
|
||||
|
||||
// As the recursion unwinds, verify each transaction is complete and block if it is not.
|
||||
// While blocking on one transaction, others may be completing concurrently, so it doesn't matter which blocks first.
|
||||
BOOL waitUntilComplete = (!node.shouldBypassEnsureDisplay);
|
||||
if (waitUntilComplete) {
|
||||
for (_ASAsyncTransaction *transaction in [layer.asyncdisplaykit_asyncLayerTransactions copy]) {
|
||||
// Even if none of the layers have had a chance to start display earlier, they will still be allowed to saturate a multicore CPU while blocking main.
|
||||
// This significantly reduces time on the main thread relative to UIKit.
|
||||
[transaction waitUntilComplete];
|
||||
if (shouldBlock) {
|
||||
// As the recursion unwinds, verify each transaction is complete and block if it is not.
|
||||
// While blocking on one transaction, others may be completing concurrently, so it doesn't matter which blocks first.
|
||||
BOOL waitUntilComplete = (!node.shouldBypassEnsureDisplay);
|
||||
if (waitUntilComplete) {
|
||||
for (_ASAsyncTransaction *transaction in [layer.asyncdisplaykit_asyncLayerTransactions copy]) {
|
||||
// Even if none of the layers have had a chance to start display earlier, they will still be allowed to saturate a multicore CPU while blocking main.
|
||||
// This significantly reduces time on the main thread relative to UIKit.
|
||||
[transaction waitUntilComplete];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)recursivelyEnsureDisplay
|
||||
- (void)__recursivelyTriggerDisplayAndBlock:(BOOL)shouldBlock
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
// ASDisplayNodeAssert(self.isNodeLoaded, @"Node must have layer or view loaded to use -recursivelyEnsureDisplay");
|
||||
@ -1464,7 +1503,12 @@ void recursivelyEnsureDisplayForLayer(CALayer *layer)
|
||||
if ([layer needsLayout]) {
|
||||
[layer layoutIfNeeded];
|
||||
}
|
||||
recursivelyEnsureDisplayForLayer(layer);
|
||||
recursivelyTriggerDisplayForLayer(layer, shouldBlock);
|
||||
}
|
||||
|
||||
- (void)recursivelyEnsureDisplay
|
||||
{
|
||||
[self __recursivelyTriggerDisplayAndBlock:YES];
|
||||
}
|
||||
|
||||
- (void)setShouldBypassEnsureDisplay:(BOOL)shouldBypassEnsureDisplay
|
||||
@ -1997,6 +2041,7 @@ static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer,
|
||||
return _replaceAsyncSentinel != nil;
|
||||
}
|
||||
|
||||
// FIXME: This method doesn't appear to be called, and should be removed.
|
||||
- (ASSentinel *)_asyncReplaceSentinel
|
||||
{
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
|
||||
@ -303,10 +303,9 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync,
|
||||
// for async display, capture the current displaySentinel value to bail early when the job is executed if another is
|
||||
// enqueued
|
||||
// for sync display, just use nil for the displaySentinel and go
|
||||
//
|
||||
// REVIEW: what about the degenerate case where we are calling setNeedsDisplay faster than the jobs are dequeuing
|
||||
// from the displayQueue? do we want to put in some kind of timer to not cancel early fails from displaySentinel
|
||||
// changes?
|
||||
|
||||
// FIXME: what about the degenerate case where we are calling setNeedsDisplay faster than the jobs are dequeuing
|
||||
// from the displayQueue? Need to not cancel early fails from displaySentinel changes.
|
||||
ASSentinel *displaySentinel = (asynchronously ? _displaySentinel : nil);
|
||||
int64_t displaySentinelValue = [displaySentinel increment];
|
||||
|
||||
|
||||
@ -218,20 +218,12 @@
|
||||
|
||||
- (void)setNeedsDisplay
|
||||
{
|
||||
ASDisplayNode *rasterizedContainerNode = [self __rasterizedContainerNode];
|
||||
if (rasterizedContainerNode) {
|
||||
[rasterizedContainerNode setNeedsDisplay];
|
||||
} else {
|
||||
[_layer setNeedsDisplay];
|
||||
|
||||
if (_layer && !self.isSynchronous && self.displaysAsynchronously) {
|
||||
// It's essenital that any layout pass that is scheduled during the current
|
||||
// runloop has a chance to be applied / scheduled.
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self recursivelyEnsureDisplay];
|
||||
});
|
||||
}
|
||||
}
|
||||
_bridge_prologue;
|
||||
// Send the message to the layer first, as __setNeedsDisplay may call -displayIfNeeded.
|
||||
// REVIEW: Audit if this is necessary or if it can be called after like __setNeedsLayout
|
||||
// -> Likely possible because of the aggregation / trampoline to occur on a later runloop.
|
||||
_messageToLayer(setNeedsDisplay);
|
||||
[self __setNeedsDisplay];
|
||||
}
|
||||
|
||||
- (void)setNeedsLayout
|
||||
|
||||
@ -149,6 +149,8 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) {
|
||||
// Display the node's view/layer immediately on the current thread, bypassing the background thread rendering. Will be deprecated.
|
||||
- (void)displayImmediately;
|
||||
|
||||
- (void)__setNeedsDisplay;
|
||||
|
||||
// Returns the ancestor node that rasterizes descendants, or nil if none.
|
||||
- (ASDisplayNode *)__rasterizedContainerNode;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user