mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2026-02-15 23:36:05 +00:00
Merge pull request #905 from facebook/RemoveWorkingWindow
[ASRangeController] Implement #if that controls removal of the offscreen UIWindow used for render-ahead. Leave enabled for now.
This commit is contained in:
@@ -197,6 +197,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
|
||||
@@ -710,6 +733,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
- (void)recursivelyDisplayImmediately
|
||||
{
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
|
||||
for (ASDisplayNode *child in _subnodes) {
|
||||
[child recursivelyDisplayImmediately];
|
||||
}
|
||||
@@ -1453,7 +1477,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
|
||||
@@ -1472,26 +1496,26 @@ 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");
|
||||
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,
|
||||
@@ -1500,7 +1524,12 @@ void recursivelyEnsureDisplayForLayer(CALayer *layer)
|
||||
if ([layer needsLayout]) {
|
||||
[layer layoutIfNeeded];
|
||||
}
|
||||
recursivelyEnsureDisplayForLayer(layer);
|
||||
recursivelyTriggerDisplayForLayer(layer, shouldBlock);
|
||||
}
|
||||
|
||||
- (void)recursivelyEnsureDisplay
|
||||
{
|
||||
[self __recursivelyTriggerDisplayAndBlock:YES];
|
||||
}
|
||||
|
||||
- (void)setShouldBypassEnsureDisplay:(BOOL)shouldBypassEnsureDisplay
|
||||
@@ -2127,6 +2156,9 @@ static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer,
|
||||
return _replaceAsyncSentinel != nil;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
@@ -17,8 +17,9 @@
|
||||
@end
|
||||
|
||||
@implementation ASRangeHandlerRender
|
||||
@synthesize workingWindow = _workingWindow;
|
||||
|
||||
#if USE_WORKING_WINDOW
|
||||
@synthesize workingWindow = _workingWindow;
|
||||
- (UIWindow *)workingWindow
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
@@ -45,6 +46,7 @@
|
||||
[self node:node exitedRangeOfType:ASLayoutRangeTypeRender];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
- (void)node:(ASDisplayNode *)node enteredRangeOfType:(ASLayoutRangeType)rangeType
|
||||
{
|
||||
@@ -60,12 +62,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 +100,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 +112,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
|
||||
|
||||
@@ -147,11 +147,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];
|
||||
|
||||
@@ -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"];
|
||||
|
||||
@@ -304,10 +304,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];
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -221,6 +221,8 @@
|
||||
|
||||
- (void)setNeedsDisplay
|
||||
{
|
||||
_bridge_prologue;
|
||||
|
||||
if (_hierarchyState & ASHierarchyStateRasterized) {
|
||||
ASPerformBlockOnMainThread(^{
|
||||
// The below operation must be performed on the main thread to ensure against an extremely rare deadlock, where a parent node
|
||||
@@ -238,7 +240,19 @@
|
||||
[rasterizedContainerNode setNeedsDisplay];
|
||||
});
|
||||
} else {
|
||||
[_layer setNeedsDisplay];
|
||||
// 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 !USE_WORKING_WINDOW
|
||||
if (_layer && !self.isSynchronous) {
|
||||
[ASDisplayNode scheduleNodeForDisplay:self];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user