-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Yoga] Rewrite YOGA_TREE_CONTIGUOUS mode with improved behavior and cleaner integration #343
Conversation
… ASLayoutSpec. After experimentation with the ASYogaLayoutSpec (or non-contiguous) approach to integrating Yoga, test results and feedback from the authors of Yoga have shown that this approach can't be made completely correct, There are issues with some of the features required to represent Web-style flexbox; in particular: padding, margins, and border handling have varience. This diff is a first step towards a truly correct and elegant implementation of Yoga integration with Texture. In addition to reducing the footprint of the integration, which is an explicit goal of work at this stage, these changes already support improved behavior - including mixing between ASLayoutSpecs even as subnodes of Yoga layout-driven nodes, in addition to above them. Yoga may be used for any set of nodes. Because Yoga usage is limited at this time, it's safe to merge this diff and further improvements will be refinements in this direction.
} | ||
#endif /* YOGA */ | ||
|
||
// Manual size calculation via calculateSizeThatFits: | ||
if (((_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits) || | ||
(_layoutSpecBlock != NULL)) == NO) { | ||
if (_layoutSpecBlock == NULL && (_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits) == 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This condition is equivalent to the previous one, but easier to read the intention of.
@@ -391,7 +391,7 @@ - (void)_insertElementsIntoMap:(ASMutableElementMap *)map | |||
nodeBlock = [dataSource dataController:self supplementaryNodeBlockOfKind:kind atIndexPath:indexPath]; | |||
} | |||
|
|||
ASSizeRange constrainedSize; | |||
ASSizeRange constrainedSize = ASSizeRangeUnconstrained; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This fixes an Xcode 9 static analyzer warning. I think it's the right default - if it is ever encountered - but please take a moment to consider it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nguyenhuy would know better, but I believe this is fine because if we don't fetch size ranges at this stage, it means that they're using async layout and they'll perform measurement (and provide a size range) on their own if they want to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, that's a sensible default value. If shouldFetchSizeRanges
is false, that means they're using the async layout and the default value will never be used.
else if (propertyName == ASYogaPositionProperty) { | ||
ASEdgeInsets position = self.position; | ||
YGEdge edge = YGEdgeLeft; | ||
for (int i = 0; i < YGEdgeAll + 1; ++i) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the future, I can macro-ize / simplify the 4 looping cases below. However, since each of them use a few different property-specific invocations, this level of boilerplate seems reasonable for now.
Source/Layout/ASLayoutElement.mm
Outdated
+ (void)initialize | ||
{ | ||
[super initialize]; | ||
YGConfigSetUseWebDefaults(YGConfigGetDefault(), true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have tested this codepath carefully in the debugger to confirm this code works exactly as intended, so this is ready to go once un-commented. I'd like to enable this in the next ~2-3 weeks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 👍
return _yogaNode; | ||
} | ||
|
||
- (YGNodeRef)yogaNodeCreateIfNeeded |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A call to this method is required for Yoga to be used on any given node. Without it, the style properties are not set up on the Yoga node either. This provides an extra level of gating as well.
__weak ASDisplayNode *_yogaParent; | ||
ASLayout *_yogaCalculatedLayout; | ||
BOOL _yogaLayoutInProgress; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This replaces the ASHierarchyState options. ASHierarchyState propagation turned out to not be desirable, as the Yoga layout may be used on any individual parent+children combination, or contiguous subtrees separated by non-Yoga nodes too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reasonable. Should we thread-affine this property? Can yoga layouts proceed in parallel?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, ideally this would be both thread-affined and node-affined.
Considering the other PR doing this with ASLayoutElementContext, I wonder if instead we should have a table inside each ASDisplayNode with an unordered_map from pthread ID to an object context?
Source/ASDisplayNode+Yoga.mm
Outdated
ASDisplayNodeAssert(yogaNode, @"-[ASDisplayNode setYogaMeasureFuncIfNeeded] called with a NULL yogaNode! %@", self); | ||
if (yogaNode) { | ||
// TODO(appleguy): Use __bridge_transfer to retain nodes and clean up appropriately. | ||
YGNodeSetContext(yogaNode, (__bridge void *)self); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it is appropriate to use ASWeakProxy here, and strong-retain the proxy itself? Otherwise it seems a retain cycle would be created.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, ASWeakProxy seems to me like the best way to go here. I believe we could only avoid it if we could guarantee that Yoga will never perform any measurement after a node begins deallocation, probably not possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great! I know you're busy as hell so it's great to see significant progress on this front. No objections from me for landing – we should get this in the field as soon as possible and gather info to guide the next step.
Source/ASDisplayNode+Yoga.mm
Outdated
ASDisplayNodeAssert(yogaNode, @"-[ASDisplayNode setYogaMeasureFuncIfNeeded] called with a NULL yogaNode! %@", self); | ||
if (yogaNode) { | ||
// TODO(appleguy): Use __bridge_transfer to retain nodes and clean up appropriately. | ||
YGNodeSetContext(yogaNode, (__bridge void *)self); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, ASWeakProxy seems to me like the best way to go here. I believe we could only avoid it if we could guarantee that Yoga will never perform any measurement after a node begins deallocation, probably not possible.
@@ -391,7 +391,7 @@ - (void)_insertElementsIntoMap:(ASMutableElementMap *)map | |||
nodeBlock = [dataSource dataController:self supplementaryNodeBlockOfKind:kind atIndexPath:indexPath]; | |||
} | |||
|
|||
ASSizeRange constrainedSize; | |||
ASSizeRange constrainedSize = ASSizeRangeUnconstrained; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nguyenhuy would know better, but I believe this is fine because if we don't fetch size ranges at this stage, it means that they're using async layout and they'll perform measurement (and provide a size range) on their own if they want to.
Source/Layout/ASLayoutElement.mm
Outdated
+ (void)initialize | ||
{ | ||
[super initialize]; | ||
YGConfigSetUseWebDefaults(YGConfigGetDefault(), true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 👍
__weak ASDisplayNode *_yogaParent; | ||
ASLayout *_yogaCalculatedLayout; | ||
BOOL _yogaLayoutInProgress; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reasonable. Should we thread-affine this property? Can yoga layouts proceed in parallel?
f579fd6
to
9d378c4
Compare
9d378c4
to
91a9c77
Compare
Generated by 🚫 Danger |
…leaner integration (TextureGroup#343) * [Yoga] Rewrite YOGA_TREE_CONTIGUOUS mode with support for mixing with ASLayoutSpec. After experimentation with the ASYogaLayoutSpec (or non-contiguous) approach to integrating Yoga, test results and feedback from the authors of Yoga have shown that this approach can't be made completely correct, There are issues with some of the features required to represent Web-style flexbox; in particular: padding, margins, and border handling have varience. This diff is a first step towards a truly correct and elegant implementation of Yoga integration with Texture. In addition to reducing the footprint of the integration, which is an explicit goal of work at this stage, these changes already support improved behavior - including mixing between ASLayoutSpecs even as subnodes of Yoga layout-driven nodes, in addition to above them. Yoga may be used for any set of nodes. Because Yoga usage is limited at this time, it's safe to merge this diff and further improvements will be refinements in this direction. * [ASDKgram] Add Yoga layout implementation for PhotoCellNode. * [Yoga] Final fixes for the upgraded implementation of the Contiguous layout mode. * [Yoga] Add CHANGELOG.md entry and fix for Yoga rounding to screen scale. * [Yoga] Minor cleanup to remove old comments and generalize utility methods.
After experimentation with the ASYogaLayoutSpec (or non-contiguous) approach to
integrating Yoga, test results and feedback from the authors of Yoga have shown
that this approach can't be made completely correct,
There are issues with some of the features required to represent Web-style
flexbox; in particular: padding, margins, and border handling have varience.
This diff is a first step towards a truly correct and elegant implementation of
Yoga integration with Texture. In addition to reducing the footprint of
the integration, which is an explicit goal of work at this stage, these changes
already support improved behavior - including mixing between ASLayoutSpecs
even as subnodes of Yoga layout-driven nodes, in addition to above them. Yoga
may be used for any set of nodes.
Because Yoga usage is limited at this time, it's safe to merge this diff and
further improvements will be refinements in this direction.