diff --git a/CHANGELOG.md b/CHANGELOG.md index d4ce5781cd..8bef9a8520 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,4 +34,5 @@ - [Fix] Fix a major regression in our image node contents caching. [Adlai Holler](https://github.com/Adlai-Holler) [#287](https://github.com/TextureGroup/Texture/pull/287) - [Fix] Fixed a bug where ASVideoNodeDelegate error reporting callback would crash an app because of not responding to selector. [Sergey Petrachkov](https://github.com/Petrachkov) [#291](https://github.com/TextureGroup/Texture/issues/291) - [IGListKit] Add IGListKit headers to public section of Xcode project [Michael Schneider](https://github.com/maicki)[#286](https://github.com/TextureGroup/Texture/pull/286) -- [Layout] Ensure -layout and -layoutDidFinish are called only if a node is loaded. [Huy Nguyen](https://github.com/nguyenhuy) [#285](https://github.com/TextureGroup/Texture/pull/285) \ No newline at end of file +- [Layout] Ensure -layout and -layoutDidFinish are called only if a node is loaded. [Huy Nguyen](https://github.com/nguyenhuy) [#285](https://github.com/TextureGroup/Texture/pull/285) +- [Layout Debugger] Small changes needed for the coming layout debugger [Huy Nguyen](https://github.com/nguyenhuy) [#337](https://github.com/TextureGroup/Texture/pull/337) diff --git a/Source/ASDisplayNode+Layout.mm b/Source/ASDisplayNode+Layout.mm index e842973b78..e446cf6b61 100644 --- a/Source/ASDisplayNode+Layout.mm +++ b/Source/ASDisplayNode+Layout.mm @@ -911,6 +911,12 @@ ASPrimitiveTraitCollectionDeprecatedImplementation ASDisplayNodeAssertTrue(displayNodeLayout->layout.size.width >= 0.0); ASDisplayNodeAssertTrue(displayNodeLayout->layout.size.height >= 0.0); + // Flatten the layout if it wasn't done before (@see -calculateLayoutThatFits:). + if ([ASDisplayNode shouldStoreUnflattenedLayouts]) { + _unflattenedLayout = displayNodeLayout->layout; + displayNodeLayout->layout = [_unflattenedLayout filteredNodeLayoutTree]; + } + _calculatedDisplayNodeLayout = displayNodeLayout; } diff --git a/Source/ASDisplayNode.h b/Source/ASDisplayNode.h index b4f52140bd..f3745f91f6 100644 --- a/Source/ASDisplayNode.h +++ b/Source/ASDisplayNode.h @@ -553,6 +553,30 @@ extern NSInteger const ASDefaultDrawingPriority; */ @interface ASDisplayNode (Debugging) +/** + * Set to YES to tell all ASDisplayNode instances to store their unflattened layouts. + * + * The layout can be accessed via `-unflattenedCalculatedLayout`. + * + * Flattened layouts use less memory and are faster to lookup. On the other hand, unflattened layouts are useful for debugging + * because they preserve original information. + */ ++ (void)setShouldStoreUnflattenedLayouts:(BOOL)shouldStore; + +/** + * Whether or not ASDisplayNode instances should store their unflattened layouts. + * + * The layout can be accessed via `-unflattenedCalculatedLayout`. + * + * Flattened layouts use less memory and are faster to lookup. On the other hand, unflattened layouts are useful for debugging + * because they preserve original information. + * + * Defaults to NO. + */ ++ (BOOL)shouldStoreUnflattenedLayouts; + +@property (nonatomic, strong, readonly, nullable) ASLayout *unflattenedCalculatedLayout; + /** * @abstract Return a description of the node hierarchy. * diff --git a/Source/ASDisplayNode.mm b/Source/ASDisplayNode.mm index 453452dd87..1410861035 100644 --- a/Source/ASDisplayNode.mm +++ b/Source/ASDisplayNode.mm @@ -82,6 +82,7 @@ NSInteger const ASDefaultDrawingPriority = ASDefaultTransactionPriority; @synthesize threadSafeBounds = _threadSafeBounds; static BOOL suppressesInvalidCollectionUpdateExceptions = NO; +static std::atomic_bool storesUnflattenedLayouts = ATOMIC_VAR_INIT(NO); + (BOOL)suppressesInvalidCollectionUpdateExceptions { @@ -887,6 +888,8 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) if (_pendingDisplayNodeLayout != nullptr) { _pendingDisplayNodeLayout->invalidate(); } + + _unflattenedLayout = nil; #if YOGA_TREE_CONTIGUOUS [self invalidateCalculatedYogaLayout]; @@ -1013,7 +1016,6 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) // Manually propagate the trait collection here so that any layoutSpec children of layoutSpec will get a traitCollection { - ASDN::SumScopeTimer t(_layoutSpecTotalTime, measureLayoutSpec); ASTraitCollectionPropagateDown(layoutElement, self.primitiveTraitCollection); } @@ -1038,7 +1040,13 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) } ASDisplayNodeLogEvent(self, @"computedLayout: %@", layout); - return [layout filteredNodeLayoutTree]; + // Return the (original) unflattened layout if it needs to be stored. The layout will be flattened later on (@see _locked_setCalculatedDisplayNodeLayout:). + // Otherwise, flatten it right away. + if (! [ASDisplayNode shouldStoreUnflattenedLayouts]) { + layout = [layout filteredNodeLayoutTree]; + } + + return layout; } - (CGSize)calculateSizeThatFits:(CGSize)constrainedSize @@ -3286,6 +3294,22 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) { @implementation ASDisplayNode (Debugging) ++ (void)setShouldStoreUnflattenedLayouts:(BOOL)shouldStore +{ + storesUnflattenedLayouts.store(shouldStore); +} + ++ (BOOL)shouldStoreUnflattenedLayouts +{ + return storesUnflattenedLayouts.load(); +} + +- (ASLayout *)unflattenedCalculatedLayout +{ + ASDN::MutexLocker l(__instanceLock__); + return _unflattenedLayout; +} + - (NSString *)displayNodeRecursiveDescription { return [self _recursiveDescriptionHelperWithIndent:@""]; diff --git a/Source/ASDisplayNodeExtras.h b/Source/ASDisplayNodeExtras.h index 35217c656d..c49d57df82 100644 --- a/Source/ASDisplayNodeExtras.h +++ b/Source/ASDisplayNodeExtras.h @@ -98,7 +98,7 @@ extern ASDisplayNode * _Nullable ASLayerToDisplayNode(CALayer * _Nullable layer) extern ASDisplayNode * _Nullable ASViewToDisplayNode(UIView * _Nullable view) AS_WARN_UNUSED_RESULT; /** - Given a node, returns the root of the node heirarchy (where supernode == nil) + Given a node, returns the root of the node hierarchy (where supernode == nil) */ extern ASDisplayNode *ASDisplayNodeUltimateParentOfNode(ASDisplayNode *node) AS_WARN_UNUSED_RESULT; diff --git a/Source/Layout/ASLayout.h b/Source/Layout/ASLayout.h index c5d2c26f2b..82cd9d6053 100644 --- a/Source/Layout/ASLayout.h +++ b/Source/Layout/ASLayout.h @@ -176,6 +176,18 @@ ASDISPLAYNODE_EXTERN_C_END @interface ASLayout (Debugging) +/** + * Set to YES to tell all ASLayout instances to retain their sublayout elements. Defaults to NO. + * Can be overridden at instance level. + */ ++ (void)setShouldRetainSublayoutLayoutElements:(BOOL)shouldRetain; + +/** + * Whether or not ASLayout instances should retain their sublayout elements. + * Can be overridden at instance level. + */ ++ (BOOL)shouldRetainSublayoutLayoutElements; + /** * Recrusively output the description of the layout tree. */ diff --git a/Source/Layout/ASLayout.mm b/Source/Layout/ASLayout.mm index 7f047fab5d..ddc0fb2797 100644 --- a/Source/Layout/ASLayout.mm +++ b/Source/Layout/ASLayout.mm @@ -76,6 +76,18 @@ static inline NSString * descriptionIndents(NSUInteger indents) @dynamic frame, type; +static std::atomic_bool static_retainsSublayoutLayoutElements = ATOMIC_VAR_INIT(NO); + ++ (void)setShouldRetainSublayoutLayoutElements:(BOOL)shouldRetain +{ + static_retainsSublayoutLayoutElements.store(shouldRetain); +} + ++ (BOOL)shouldRetainSublayoutLayoutElements +{ + return static_retainsSublayoutLayoutElements.load(); +} + - (instancetype)initWithLayoutElement:(id)layoutElement size:(CGSize)size position:(CGPoint)position @@ -118,7 +130,7 @@ static inline NSString * descriptionIndents(NSUInteger indents) } _flattened = NO; - _retainSublayoutLayoutElements = NO; + self.retainSublayoutLayoutElements = [ASLayout shouldRetainSublayoutLayoutElements]; } return self; diff --git a/Source/Private/ASDisplayNodeInternal.h b/Source/Private/ASDisplayNodeInternal.h index b276728d17..83e2db737e 100644 --- a/Source/Private/ASDisplayNodeInternal.h +++ b/Source/Private/ASDisplayNodeInternal.h @@ -211,6 +211,9 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo NSString *_debugName; +#pragma mark - ASDisplayNode (Debugging) + ASLayout *_unflattenedLayout; + #if TIME_DISPLAYNODE_OPS @public NSTimeInterval _debugTimeToCreateView;