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];
}
+ (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,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.
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.
// While blocking on one transaction, others may be completing concurrently, so it doesn't matter which blocks first.
BOOL waitUntilComplete = (!node.shouldBypassEnsureDisplay);
@ -1450,8 +1488,9 @@ void recursivelyEnsureDisplayForLayer(CALayer *layer)
}
}
}
}
- (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);

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
// 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];

View File

@ -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

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.
- (void)displayImmediately;
- (void)__setNeedsDisplay;
// Returns the ancestor node that rasterizes descendants, or nil if none.
- (ASDisplayNode *)__rasterizedContainerNode;