Efficiency improvements to consolidate display events for the window-less preload range.

This commit is contained in:
Scott Goodson 2015-11-08 17:04:13 -08:00
parent 7f59beb195
commit 9669f147ba
4 changed files with 68 additions and 30 deletions

View File

@ -192,6 +192,29 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
return [_ASDisplayLayer class]; 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 #pragma mark - Lifecycle
- (void)_staticInitialize - (void)_staticInitialize
@ -705,6 +728,20 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
[self displayImmediately]; [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 - (void)__setNeedsLayout
{ {
ASDisplayNodeAssertThreadAffinity(self); ASDisplayNodeAssertThreadAffinity(self);
@ -1417,7 +1454,7 @@ static NSInteger incrementIfFound(NSInteger i) {
[_placeholderLayer removeFromSuperlayer]; [_placeholderLayer removeFromSuperlayer];
} }
void recursivelyEnsureDisplayForLayer(CALayer *layer) void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
{ {
// This recursion must handle layers in various states: // This recursion must handle layers in various states:
// 1. Just added to hierarchy, CA hasn't yet called -display // 1. Just added to hierarchy, CA hasn't yet called -display
@ -1436,9 +1473,10 @@ 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. // 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) { for (CALayer *sublayer in layer.sublayers) {
recursivelyEnsureDisplayForLayer(sublayer); recursivelyTriggerDisplayForLayer(sublayer, shouldBlock);
} }
if (shouldBlock) {
// As the recursion unwinds, verify each transaction is complete and block if it is not. // 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. // While blocking on one transaction, others may be completing concurrently, so it doesn't matter which blocks first.
BOOL waitUntilComplete = (!node.shouldBypassEnsureDisplay); BOOL waitUntilComplete = (!node.shouldBypassEnsureDisplay);
@ -1450,8 +1488,9 @@ void recursivelyEnsureDisplayForLayer(CALayer *layer)
} }
} }
} }
}
- (void)recursivelyEnsureDisplay - (void)__recursivelyTriggerDisplayAndBlock:(BOOL)shouldBlock
{ {
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();
// ASDisplayNodeAssert(self.isNodeLoaded, @"Node must have layer or view loaded to use -recursivelyEnsureDisplay"); // ASDisplayNodeAssert(self.isNodeLoaded, @"Node must have layer or view loaded to use -recursivelyEnsureDisplay");
@ -1464,7 +1503,12 @@ void recursivelyEnsureDisplayForLayer(CALayer *layer)
if ([layer needsLayout]) { if ([layer needsLayout]) {
[layer layoutIfNeeded]; [layer layoutIfNeeded];
} }
recursivelyEnsureDisplayForLayer(layer); recursivelyTriggerDisplayForLayer(layer, shouldBlock);
}
- (void)recursivelyEnsureDisplay
{
[self __recursivelyTriggerDisplayAndBlock:YES];
} }
- (void)setShouldBypassEnsureDisplay:(BOOL)shouldBypassEnsureDisplay - (void)setShouldBypassEnsureDisplay:(BOOL)shouldBypassEnsureDisplay
@ -1997,6 +2041,7 @@ static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer,
return _replaceAsyncSentinel != nil; return _replaceAsyncSentinel != nil;
} }
// FIXME: This method doesn't appear to be called, and should be removed.
- (ASSentinel *)_asyncReplaceSentinel - (ASSentinel *)_asyncReplaceSentinel
{ {
ASDN::MutexLocker l(_propertyLock); ASDN::MutexLocker l(_propertyLock);

View File

@ -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 // for async display, capture the current displaySentinel value to bail early when the job is executed if another is
// enqueued // enqueued
// for sync display, just use nil for the displaySentinel and go // 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 // FIXME: 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 // from the displayQueue? Need to not cancel early fails from displaySentinel changes.
// changes?
ASSentinel *displaySentinel = (asynchronously ? _displaySentinel : nil); ASSentinel *displaySentinel = (asynchronously ? _displaySentinel : nil);
int64_t displaySentinelValue = [displaySentinel increment]; int64_t displaySentinelValue = [displaySentinel increment];

View File

@ -218,20 +218,12 @@
- (void)setNeedsDisplay - (void)setNeedsDisplay
{ {
ASDisplayNode *rasterizedContainerNode = [self __rasterizedContainerNode]; _bridge_prologue;
if (rasterizedContainerNode) { // Send the message to the layer first, as __setNeedsDisplay may call -displayIfNeeded.
[rasterizedContainerNode setNeedsDisplay]; // REVIEW: Audit if this is necessary or if it can be called after like __setNeedsLayout
} else { // -> Likely possible because of the aggregation / trampoline to occur on a later runloop.
[_layer setNeedsDisplay]; _messageToLayer(setNeedsDisplay);
[self __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];
});
}
}
} }
- (void)setNeedsLayout - (void)setNeedsLayout

View File

@ -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. // Display the node's view/layer immediately on the current thread, bypassing the background thread rendering. Will be deprecated.
- (void)displayImmediately; - (void)displayImmediately;
- (void)__setNeedsDisplay;
// Returns the ancestor node that rasterizes descendants, or nil if none. // Returns the ancestor node that rasterizes descendants, or nil if none.
- (ASDisplayNode *)__rasterizedContainerNode; - (ASDisplayNode *)__rasterizedContainerNode;