From 7f59beb19588f0c5fd830f71414213f019723da5 Mon Sep 17 00:00:00 2001 From: Scott Goodson Date: Sun, 8 Nov 2015 12:56:17 -0800 Subject: [PATCH 1/6] Initial experimental commit with functioning preloading, without the working window. --- AsyncDisplayKit/ASCellNode.m | 23 +++++++++++++++++++ AsyncDisplayKit/ASDisplayNode.mm | 6 ++--- .../Details/ASRangeHandlerRender.mm | 19 ++++++++++++++- AsyncDisplayKit/Details/_ASDisplayLayer.mm | 8 +++---- AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.m | 2 +- .../Private/ASDisplayNode+UIViewBridge.mm | 8 +++++++ 6 files changed, 56 insertions(+), 10 deletions(-) diff --git a/AsyncDisplayKit/ASCellNode.m b/AsyncDisplayKit/ASCellNode.m index ef6652b700..dbd11e0523 100644 --- a/AsyncDisplayKit/ASCellNode.m +++ b/AsyncDisplayKit/ASCellNode.m @@ -62,6 +62,29 @@ } } +- (void)clearFetchedData +{ + [super clearFetchedData]; + NSLog(@"%p - clearFetchedData", self); +} + +- (void)fetchData +{ + [super fetchData]; + NSLog(@"%p - fetchData", self); +} + +- (void)clearContents +{ + [super clearContents]; + NSLog(@"%p - clearContents", self); +} + +- (void)dealloc +{ + NSLog(@"%p - dealloc", self); +} + - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { ASDisplayNodeAssertMainThread(); diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 9c52f08c21..f70d336b88 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -665,7 +665,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) [self _addSubnodeViewsAndLayers]; } - [self recursivelyDisplayImmediately]; + [self recursivelyEnsureDisplay]; } } @@ -1454,8 +1454,8 @@ void recursivelyEnsureDisplayForLayer(CALayer *layer) - (void)recursivelyEnsureDisplay { ASDisplayNodeAssertMainThread(); - ASDisplayNodeAssert(self.isNodeLoaded, @"Node must have layer or view loaded to use -recursivelyEnsureDisplay"); - ASDisplayNodeAssert(self.inHierarchy && (self.isLayerBacked || self.view.window != nil), @"Node must be in a hierarchy to use -recursivelyEnsureDisplay"); +// ASDisplayNodeAssert(self.isNodeLoaded, @"Node must have layer or view loaded to use -recursivelyEnsureDisplay"); +// ASDisplayNodeAssert(self.inHierarchy && (self.isLayerBacked || self.view.window != nil), @"Node must be in a hierarchy to use -recursivelyEnsureDisplay"); CALayer *layer = self.layer; // -layoutIfNeeded is recursive, and even walks up to superlayers to check if they need layout, diff --git a/AsyncDisplayKit/Details/ASRangeHandlerRender.mm b/AsyncDisplayKit/Details/ASRangeHandlerRender.mm index b2a751a8bd..ee320d7e32 100644 --- a/AsyncDisplayKit/Details/ASRangeHandlerRender.mm +++ b/AsyncDisplayKit/Details/ASRangeHandlerRender.mm @@ -12,13 +12,16 @@ #import "ASDisplayNode+Subclasses.h" #import "ASDisplayNodeInternal.h" +#define USE_WORKING_WINDOW 0 + @interface ASRangeHandlerRender () @property (nonatomic,readonly) UIWindow *workingWindow; @end @implementation ASRangeHandlerRender -@synthesize workingWindow = _workingWindow; +#if USE_WORKING_WINDOW +@synthesize workingWindow = _workingWindow; - (UIWindow *)workingWindow { ASDisplayNodeAssertMainThread(); @@ -45,6 +48,7 @@ [self node:node exitedRangeOfType:ASLayoutRangeTypeRender]; } } +#endif - (void)node:(ASDisplayNode *)node enteredRangeOfType:(ASLayoutRangeType)rangeType { @@ -60,12 +64,17 @@ // The node un-suspends display. [node enterInterfaceState:ASInterfaceStateDisplay]; + +#if USE_WORKING_WINDOW // Add the node's layer to an off-screen window to trigger display and mark its contents as non-volatile. // Use the layer directly to avoid the substantial overhead of UIView heirarchy manipulations. // Any view-backed nodes will still create their views in order to assemble the layer heirarchy, and they will // also assemble a view subtree for the node, but we avoid the much more significant expense triggered by a view // being added or removed from an onscreen window (responder chain setup, will/DidMoveToWindow: recursive calls, etc) [[[self workingWindow] layer] addSublayer:node.layer]; +#else + [node recursivelyEnsureDisplay]; // Need to do this without waiting +#endif } - (void)node:(ASDisplayNode *)node exitedRangeOfType:(ASLayoutRangeType)rangeType @@ -93,6 +102,7 @@ // The node calls clearCurrentContents and suspends display [node exitInterfaceState:ASInterfaceStateDisplay]; +#if USE_WORKING_WINDOW if (node.layer.superlayer != [[self workingWindow] layer]) { // In this case, the node has previously passed through the working range (or it is zero), and it has now fallen outside the working range. if (![node isLayerBacked]) { @@ -104,6 +114,13 @@ // At this point, the node's layer may validly be present either in the workingWindow, or in the contentsView of a cell. [node.layer removeFromSuperlayer]; +#else + if (![node isLayerBacked]) { + [node.view removeFromSuperview]; + } else { + [node.layer removeFromSuperlayer]; + } +#endif } @end diff --git a/AsyncDisplayKit/Details/_ASDisplayLayer.mm b/AsyncDisplayKit/Details/_ASDisplayLayer.mm index 99e4bf68e8..b69d935677 100644 --- a/AsyncDisplayKit/Details/_ASDisplayLayer.mm +++ b/AsyncDisplayKit/Details/_ASDisplayLayer.mm @@ -146,11 +146,9 @@ - (void)displayImmediately { - // REVIEW: Should this respect isDisplaySuspended? If so, we'd probably want to synchronously display when - // setDisplaySuspended:No is called, rather than just scheduling. The thread affinity for the displayImmediately - // call will be tricky if we need to support this, though. It probably should just execute if displayImmediately is - // called directly. The caller should be responsible for not calling displayImmediately if it wants to obey the - // suspended state. + // This method is a low-level bypass that avoids touching CA, including any reset of the + // needsDisplay flag, until the .contents property is set with the result. + // It is designed to be able to block the thread of any caller and fully execute the display. ASDisplayNodeAssertMainThread(); [self display:NO]; diff --git a/AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.m b/AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.m index 9aa3bd8fdb..5f57b5aa86 100644 --- a/AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.m +++ b/AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.m @@ -111,7 +111,7 @@ typedef NS_ENUM(NSUInteger, PIDebugBoxPaddingLocation) [paddedLines addObject:paddedLine]; } concatenatedLines = paddedLines; - totalLineLength += difference; + // totalLineLength += difference; } concatenatedLines = [self appendTopAndBottomToBoxString:concatenatedLines parent:parent]; return [concatenatedLines componentsJoinedByString:@"\n"]; diff --git a/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm b/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm index 28a35a1c22..65ad2a63e6 100644 --- a/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm +++ b/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm @@ -223,6 +223,14 @@ [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]; + }); + } } } From 9669f147ba9042ca6f58e185ddcc9ee9cfe1b9a0 Mon Sep 17 00:00:00 2001 From: Scott Goodson Date: Sun, 8 Nov 2015 17:04:13 -0800 Subject: [PATCH 2/6] Efficiency improvements to consolidate display events for the window-less preload range. --- AsyncDisplayKit/ASDisplayNode.mm | 69 +++++++++++++++---- .../Private/ASDisplayNode+AsyncDisplay.mm | 7 +- .../Private/ASDisplayNode+UIViewBridge.mm | 20 ++---- .../Private/ASDisplayNodeInternal.h | 2 + 4 files changed, 68 insertions(+), 30 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index f70d336b88..9baec3b662 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -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); diff --git a/AsyncDisplayKit/Private/ASDisplayNode+AsyncDisplay.mm b/AsyncDisplayKit/Private/ASDisplayNode+AsyncDisplay.mm index e74cd00e35..e53f30bccb 100644 --- a/AsyncDisplayKit/Private/ASDisplayNode+AsyncDisplay.mm +++ b/AsyncDisplayKit/Private/ASDisplayNode+AsyncDisplay.mm @@ -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]; diff --git a/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm b/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm index 65ad2a63e6..6f0ecd9dac 100644 --- a/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm +++ b/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm @@ -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 diff --git a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h index 0d3ab7efe3..2d3c389dcd 100644 --- a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h +++ b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h @@ -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; From 8bc65a96248c79c31d0d112eae80059a229e2124 Mon Sep 17 00:00:00 2001 From: Scott Goodson Date: Sun, 8 Nov 2015 17:37:35 -0800 Subject: [PATCH 3/6] A few more debug changes for the removal of the working window. --- AsyncDisplayKit/ASDisplayNode.mm | 7 +++++-- AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm | 6 ++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 9baec3b662..b00ecc405d 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -586,6 +586,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize { + NSLog(@"About to measure lock for %@", self); ASDN::MutexLocker l(_propertyLock); return [self __measureWithSizeRange:constrainedSize]; } @@ -666,6 +667,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) - (BOOL)shouldRasterizeDescendants { ASDisplayNodeAssertThreadAffinity(self); + NSLog(@"About to descendants lock for %p, %@", self, [self class]); ASDN::MutexLocker l(_propertyLock); return _flags.shouldRasterizeDescendants; } @@ -722,6 +724,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) - (void)recursivelyDisplayImmediately { ASDN::MutexLocker l(_propertyLock); + for (ASDisplayNode *child in _subnodes) { [child recursivelyDisplayImmediately]; } @@ -730,12 +733,12 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) - (void)__setNeedsDisplay { + ASDN::MutexLocker l(_propertyLock); + NSLog(@"About to find parent rasterize for %@", self); ASDisplayNode *rasterizedContainerNode = [self __rasterizedContainerNode]; if (rasterizedContainerNode) { [rasterizedContainerNode setNeedsDisplay]; } else { - [_layer setNeedsDisplay]; - if (_layer && !self.isSynchronous && self.displaysAsynchronously) { [ASDisplayNode scheduleNodeForDisplay:self]; } diff --git a/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm b/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm index 6f0ecd9dac..a6d4c3b0da 100644 --- a/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm +++ b/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm @@ -219,10 +219,8 @@ - (void)setNeedsDisplay { _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); + // Send the message to the view/layer first, as __setNeedsDisplay may call -displayIfNeeded. + _messageToViewOrLayer(setNeedsDisplay); [self __setNeedsDisplay]; } From ee917ae2105b04a639240710949acaecc9fb1e46 Mon Sep 17 00:00:00 2001 From: Scott Goodson Date: Tue, 1 Dec 2015 16:48:59 -0800 Subject: [PATCH 4/6] Disable verbose logging, re-enable working window to merge WIP and avoid conflicts. --- AsyncDisplayKit/ASCellNode.m | 11 +++++++---- AsyncDisplayKit/ASDisplayNode.mm | 6 +++--- AsyncDisplayKit/Details/ASRangeHandlerRender.mm | 2 +- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/AsyncDisplayKit/ASCellNode.m b/AsyncDisplayKit/ASCellNode.m index dbd11e0523..5278bd9b75 100644 --- a/AsyncDisplayKit/ASCellNode.m +++ b/AsyncDisplayKit/ASCellNode.m @@ -18,6 +18,9 @@ #pragma mark - #pragma mark ASCellNode +//#define LOG(...) NSLog(__VA_ARGS__) +#define LOG(...) + @implementation ASCellNode - (instancetype)init @@ -65,24 +68,24 @@ - (void)clearFetchedData { [super clearFetchedData]; - NSLog(@"%p - clearFetchedData", self); + LOG(@"%p - clearFetchedData", self); } - (void)fetchData { [super fetchData]; - NSLog(@"%p - fetchData", self); + LOG(@"%p - fetchData", self); } - (void)clearContents { [super clearContents]; - NSLog(@"%p - clearContents", self); + LOG(@"%p - clearContents", self); } - (void)dealloc { - NSLog(@"%p - dealloc", self); + LOG(@"%p - dealloc", self); } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index b00ecc405d..687d6c5d2e 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -586,7 +586,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize { - NSLog(@"About to measure lock for %@", self); + //NSLog(@"About to measure lock for %@", self); ASDN::MutexLocker l(_propertyLock); return [self __measureWithSizeRange:constrainedSize]; } @@ -667,7 +667,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) - (BOOL)shouldRasterizeDescendants { ASDisplayNodeAssertThreadAffinity(self); - NSLog(@"About to descendants lock for %p, %@", self, [self class]); + //NSLog(@"About to descendants lock for %p, %@", self, [self class]); ASDN::MutexLocker l(_propertyLock); return _flags.shouldRasterizeDescendants; } @@ -734,7 +734,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) - (void)__setNeedsDisplay { ASDN::MutexLocker l(_propertyLock); - NSLog(@"About to find parent rasterize for %@", self); + //NSLog(@"About to find parent rasterize for %@", self); ASDisplayNode *rasterizedContainerNode = [self __rasterizedContainerNode]; if (rasterizedContainerNode) { [rasterizedContainerNode setNeedsDisplay]; diff --git a/AsyncDisplayKit/Details/ASRangeHandlerRender.mm b/AsyncDisplayKit/Details/ASRangeHandlerRender.mm index ee320d7e32..b531a778fc 100644 --- a/AsyncDisplayKit/Details/ASRangeHandlerRender.mm +++ b/AsyncDisplayKit/Details/ASRangeHandlerRender.mm @@ -12,7 +12,7 @@ #import "ASDisplayNode+Subclasses.h" #import "ASDisplayNodeInternal.h" -#define USE_WORKING_WINDOW 0 +#define USE_WORKING_WINDOW 1 @interface ASRangeHandlerRender () @property (nonatomic,readonly) UIWindow *workingWindow; From 872b3588d8a09511bad8f194f3c06ebb6acbc84d Mon Sep 17 00:00:00 2001 From: Scott Goodson Date: Fri, 11 Dec 2015 22:15:13 -0800 Subject: [PATCH 5/6] Complete merge of working window removal with ASHierarchyState changes in master. --- AsyncDisplayKit/ASDisplayNode.mm | 14 -------------- .../Private/ASDisplayNode+UIViewBridge.mm | 10 ++++++++++ AsyncDisplayKit/Private/ASDisplayNodeInternal.h | 2 ++ 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 47683a8bb4..710b5b61e7 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -742,20 +742,6 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) [self displayImmediately]; } -- (void)__setNeedsDisplay -{ - ASDN::MutexLocker l(_propertyLock); - //NSLog(@"About to find parent rasterize for %@", self); - ASDisplayNode *rasterizedContainerNode = [self __rasterizedContainerNode]; - if (rasterizedContainerNode) { - [rasterizedContainerNode setNeedsDisplay]; - } else { - if (_layer && !self.isSynchronous && self.displaysAsynchronously) { - [ASDisplayNode scheduleNodeForDisplay:self]; - } - } -} - - (void)__setNeedsLayout { ASDisplayNodeAssertThreadAffinity(self); diff --git a/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm b/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm index 9fa3258997..a34d1fd1ec 100644 --- a/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm +++ b/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm @@ -240,7 +240,17 @@ [rasterizedContainerNode setNeedsDisplay]; }); } else { + // If not rasterized (and therefore we certainly have a view or layer), + // Send the message to the view/layer first, as scheduleNodeForDisplay may call -displayIfNeeded. + // Wrapped / synchronous nodes created with initWithView/LayerBlock: do not need scheduleNodeForDisplay, + // as they don't need to display in the working range at all - since at all times onscreen, one + // -setNeedsDisplay to the CALayer will result in a synchronous display in the next frame. + _messageToViewOrLayer(setNeedsDisplay); + if (_layer && !self.isSynchronous) { + [ASDisplayNode scheduleNodeForDisplay:self]; + } + } } diff --git a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h index f30361a3ab..830c400dfb 100644 --- a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h +++ b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h @@ -108,6 +108,8 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) } ++ (void)scheduleNodeForDisplay:(ASDisplayNode *)node; + // The _ASDisplayLayer backing the node, if any. @property (nonatomic, readonly, retain) _ASDisplayLayer *asyncLayer; From 578d0f4f78a8e4b2460bbe63f69b3758a649e2d3 Mon Sep 17 00:00:00 2001 From: Scott Goodson Date: Fri, 18 Dec 2015 18:47:08 -0800 Subject: [PATCH 6/6] Final cleanup for initial version of removing the UIWindow-based render range. The UIWindow mode remains enabled by default, but rendering and the test suite works correctly both enabled and disabled. --- AsyncDisplayKit/ASCellNode.m | 26 ------------------- AsyncDisplayKit/ASDisplayNode.mm | 8 +++--- .../Details/ASRangeHandlerRender.mm | 2 -- .../Private/ASDisplayNode+FrameworkPrivate.h | 4 +++ .../Private/ASDisplayNode+UIViewBridge.mm | 4 ++- 5 files changed, 10 insertions(+), 34 deletions(-) diff --git a/AsyncDisplayKit/ASCellNode.m b/AsyncDisplayKit/ASCellNode.m index 1428682b53..a4357479f9 100644 --- a/AsyncDisplayKit/ASCellNode.m +++ b/AsyncDisplayKit/ASCellNode.m @@ -18,9 +18,6 @@ #pragma mark - #pragma mark ASCellNode -//#define LOG(...) NSLog(__VA_ARGS__) -#define LOG(...) - @implementation ASCellNode - (instancetype)init @@ -67,29 +64,6 @@ } } -- (void)clearFetchedData -{ - [super clearFetchedData]; - LOG(@"%p - clearFetchedData", self); -} - -- (void)fetchData -{ - [super fetchData]; - LOG(@"%p - fetchData", self); -} - -- (void)clearContents -{ - [super clearContents]; - LOG(@"%p - clearContents", self); -} - -- (void)dealloc -{ - LOG(@"%p - dealloc", self); -} - - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { ASDisplayNodeAssertMainThread(); diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index b10ffa7521..1ad6732d64 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -567,7 +567,6 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize { - //NSLog(@"About to measure lock for %@", self); ASDN::MutexLocker l(_propertyLock); return [self __measureWithSizeRange:constrainedSize]; } @@ -648,7 +647,6 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) - (BOOL)shouldRasterizeDescendants { ASDisplayNodeAssertThreadAffinity(self); - //NSLog(@"About to descendants lock for %p, %@", self, [self class]); ASDN::MutexLocker l(_propertyLock); ASDisplayNodeAssert(!((_hierarchyState & ASHierarchyStateRasterized) && _flags.shouldRasterizeDescendants), @"Subnode of a rasterized node should not have redundant shouldRasterizeDescendants enabled"); @@ -1518,8 +1516,6 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock) - (void)__recursivelyTriggerDisplayAndBlock:(BOOL)shouldBlock { ASDisplayNodeAssertMainThread(); -// ASDisplayNodeAssert(self.isNodeLoaded, @"Node must have layer or view loaded to use -recursivelyEnsureDisplay"); -// ASDisplayNodeAssert(self.inHierarchy && (self.isLayerBacked || self.view.window != nil), @"Node must be in a hierarchy to use -recursivelyEnsureDisplay"); CALayer *layer = self.layer; // -layoutIfNeeded is recursive, and even walks up to superlayers to check if they need layout, @@ -2160,7 +2156,9 @@ static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer, return _replaceAsyncSentinel != nil; } -// FIXME: This method doesn't appear to be called, and should be removed. +// FIXME: This method doesn't appear to be called, and could be removed. +// However, it may be useful for an API similar to what Paper used to create a new node hierarchy, +// trigger asynchronous measurement and display on it, and have it swap out and replace an old hierarchy. - (ASSentinel *)_asyncReplaceSentinel { ASDN::MutexLocker l(_propertyLock); diff --git a/AsyncDisplayKit/Details/ASRangeHandlerRender.mm b/AsyncDisplayKit/Details/ASRangeHandlerRender.mm index 756338f8ea..4872707e7e 100644 --- a/AsyncDisplayKit/Details/ASRangeHandlerRender.mm +++ b/AsyncDisplayKit/Details/ASRangeHandlerRender.mm @@ -12,8 +12,6 @@ #import "ASDisplayNode+Subclasses.h" #import "ASDisplayNode+FrameworkPrivate.h" -#define USE_WORKING_WINDOW 1 - @interface ASRangeHandlerRender () @property (nonatomic,readonly) UIWindow *workingWindow; @end diff --git a/AsyncDisplayKit/Private/ASDisplayNode+FrameworkPrivate.h b/AsyncDisplayKit/Private/ASDisplayNode+FrameworkPrivate.h index 58cc12c35c..0ad88aafcb 100644 --- a/AsyncDisplayKit/Private/ASDisplayNode+FrameworkPrivate.h +++ b/AsyncDisplayKit/Private/ASDisplayNode+FrameworkPrivate.h @@ -19,6 +19,10 @@ #import "ASThread.h" #import "ASLayoutOptions.h" +// Project-wide control for whether the offscreen UIWindow is used for display, or if +// ASDK's internal system for coalescing and triggering display events is used. +#define USE_WORKING_WINDOW 1 + /** Hierarchy state is propogated from nodes to all of their children when certain behaviors are required from the subtree. Examples include rasterization and external driving of the .interfaceState property. diff --git a/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm b/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm index a34d1fd1ec..2239cc038a 100644 --- a/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm +++ b/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm @@ -247,10 +247,12 @@ // -setNeedsDisplay to the CALayer will result in a synchronous display in the next frame. _messageToViewOrLayer(setNeedsDisplay); + +#if !USE_WORKING_WINDOW if (_layer && !self.isSynchronous) { [ASDisplayNode scheduleNodeForDisplay:self]; } - +#endif } }