mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-16 11:20:18 +00:00
ASStackLayoutSpec utilizes multiple threads if it runs on main (#3116)
This commit is contained in:
parent
bccfc6c779
commit
415005b6fc
@ -13,8 +13,10 @@
|
|||||||
#import <tgmath.h>
|
#import <tgmath.h>
|
||||||
#import <numeric>
|
#import <numeric>
|
||||||
|
|
||||||
|
#import <AsyncDisplayKit/ASDispatch.h>
|
||||||
#import <AsyncDisplayKit/ASLayoutSpecUtilities.h>
|
#import <AsyncDisplayKit/ASLayoutSpecUtilities.h>
|
||||||
#import <AsyncDisplayKit/ASLayoutElementStylePrivate.h>
|
#import <AsyncDisplayKit/ASLayoutElementStylePrivate.h>
|
||||||
|
#import <AsyncDisplayKit/ASThread.h>
|
||||||
|
|
||||||
CGFloat const kViolationEpsilon = 0.01;
|
CGFloat const kViolationEpsilon = 0.01;
|
||||||
|
|
||||||
@ -67,6 +69,28 @@ static ASLayout *crossChildLayout(const ASStackLayoutSpecChild &child,
|
|||||||
return layout ? : [ASLayout layoutWithLayoutElement:child.element size:{0, 0}];
|
return layout ? : [ASLayout layoutWithLayoutElement:child.element size:{0, 0}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void dispatchApplyIfNeeded(size_t iterationCount, void(^work)(size_t i))
|
||||||
|
{
|
||||||
|
if (iterationCount == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iterationCount == 1) {
|
||||||
|
work(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ASDisplayNodeThreadIsMain() == NO) {
|
||||||
|
for (size_t i = 0; i < iterationCount; i++) {
|
||||||
|
work(i);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
|
||||||
|
ASDispatchApply(iterationCount, queue, 0, work);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Computes the consumed cross dimension length for the given vector of lines and stacking style.
|
Computes the consumed cross dimension length for the given vector of lines and stacking style.
|
||||||
|
|
||||||
@ -175,21 +199,20 @@ static void stretchItemsAlongCrossDimension(std::vector<ASStackLayoutSpecItem> &
|
|||||||
const CGSize parentSize,
|
const CGSize parentSize,
|
||||||
const CGFloat crossSize)
|
const CGFloat crossSize)
|
||||||
{
|
{
|
||||||
for (auto &item : items) {
|
dispatchApplyIfNeeded(items.size(), ^(size_t i) {
|
||||||
|
auto &item = items[i];
|
||||||
const ASStackLayoutAlignItems alignItems = alignment(item.child.style.alignSelf, style.alignItems);
|
const ASStackLayoutAlignItems alignItems = alignment(item.child.style.alignSelf, style.alignItems);
|
||||||
if (alignItems != ASStackLayoutAlignItemsStretch) {
|
if (alignItems == ASStackLayoutAlignItemsStretch) {
|
||||||
continue;
|
const CGFloat cross = crossDimension(style.direction, item.layout.size);
|
||||||
|
const CGFloat stack = stackDimension(style.direction, item.layout.size);
|
||||||
|
const CGFloat violation = crossSize - cross;
|
||||||
|
|
||||||
|
// Only stretch if violation is positive. Compare against kViolationEpsilon here to avoid stretching against a tiny violation.
|
||||||
|
if (violation > kViolationEpsilon) {
|
||||||
|
item.layout = crossChildLayout(item.child, style, stack, stack, crossSize, crossSize, parentSize);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
const CGFloat cross = crossDimension(style.direction, item.layout.size);
|
|
||||||
const CGFloat stack = stackDimension(style.direction, item.layout.size);
|
|
||||||
const CGFloat violation = crossSize - cross;
|
|
||||||
|
|
||||||
// Only stretch if violation is positive. Compare against kViolationEpsilon here to avoid stretching against a tiny violation.
|
|
||||||
if (violation > kViolationEpsilon) {
|
|
||||||
item.layout = crossChildLayout(item.child, style, stack, stack, crossSize, crossSize, parentSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -405,7 +428,8 @@ static void layoutFlexibleChildrenAtZeroSize(std::vector<ASStackLayoutSpecItem>
|
|||||||
const ASSizeRange &sizeRange,
|
const ASSizeRange &sizeRange,
|
||||||
const CGSize parentSize)
|
const CGSize parentSize)
|
||||||
{
|
{
|
||||||
for (ASStackLayoutSpecItem &item : items) {
|
dispatchApplyIfNeeded(items.size(), ^(size_t i) {
|
||||||
|
auto &item = items[i];
|
||||||
if (isFlexibleInBothDirections(item.child)) {
|
if (isFlexibleInBothDirections(item.child)) {
|
||||||
item.layout = crossChildLayout(item.child,
|
item.layout = crossChildLayout(item.child,
|
||||||
style,
|
style,
|
||||||
@ -415,7 +439,7 @@ static void layoutFlexibleChildrenAtZeroSize(std::vector<ASStackLayoutSpecItem>
|
|||||||
crossDimension(style.direction, sizeRange.max),
|
crossDimension(style.direction, sizeRange.max),
|
||||||
parentSize);
|
parentSize);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -556,14 +580,25 @@ static void flexLinesAlongStackDimension(std::vector<ASStackUnpositionedLine> &l
|
|||||||
const CGFloat remainingViolation = std::accumulate(items.begin(), items.end(), violation, [&](CGFloat x, const ASStackLayoutSpecItem &item) {
|
const CGFloat remainingViolation = std::accumulate(items.begin(), items.end(), violation, [&](CGFloat x, const ASStackLayoutSpecItem &item) {
|
||||||
return x - flexAdjustment(item);
|
return x - flexAdjustment(item);
|
||||||
});
|
});
|
||||||
BOOL isFirstFlex = YES;
|
|
||||||
for (ASStackLayoutSpecItem &item : items) {
|
size_t firstFlexItem = -1;
|
||||||
|
for(size_t i = 0; i < items.size(); i++) {
|
||||||
|
// Children are consider inflexible if they do not need to make a flex adjustment.
|
||||||
|
if (flexAdjustment(items[i]) != 0) {
|
||||||
|
firstFlexItem = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ASDisplayNodeCAssert(firstFlexItem != -1, @"At this point there must be at least 1 flexible item");
|
||||||
|
|
||||||
|
dispatchApplyIfNeeded(items.size(), ^(size_t i) {
|
||||||
|
auto &item = items[i];
|
||||||
const CGFloat currentFlexAdjustment = flexAdjustment(item);
|
const CGFloat currentFlexAdjustment = flexAdjustment(item);
|
||||||
// Children are consider inflexible if they do not need to make a flex adjustment.
|
// Children are consider inflexible if they do not need to make a flex adjustment.
|
||||||
if (currentFlexAdjustment != 0) {
|
if (currentFlexAdjustment != 0) {
|
||||||
const CGFloat originalStackSize = stackDimension(style.direction, item.layout.size);
|
const CGFloat originalStackSize = stackDimension(style.direction, item.layout.size);
|
||||||
// Only apply the remaining violation for the first flexible child that has a flex grow factor.
|
// Only apply the remaining violation for the first flexible child that has a flex grow factor.
|
||||||
const CGFloat flexedStackSize = originalStackSize + currentFlexAdjustment + (isFirstFlex && item.child.style.flexGrow > 0 ? remainingViolation : 0);
|
const CGFloat flexedStackSize = originalStackSize + currentFlexAdjustment + (i == firstFlexItem && item.child.style.flexGrow > 0 ? remainingViolation : 0);
|
||||||
item.layout = crossChildLayout(item.child,
|
item.layout = crossChildLayout(item.child,
|
||||||
style,
|
style,
|
||||||
MAX(flexedStackSize, 0),
|
MAX(flexedStackSize, 0),
|
||||||
@ -571,9 +606,8 @@ static void flexLinesAlongStackDimension(std::vector<ASStackUnpositionedLine> &l
|
|||||||
crossDimension(style.direction, sizeRange.min),
|
crossDimension(style.direction, sizeRange.min),
|
||||||
crossDimension(style.direction, sizeRange.max),
|
crossDimension(style.direction, sizeRange.max),
|
||||||
parentSize);
|
parentSize);
|
||||||
isFirstFlex = NO;
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -619,28 +653,27 @@ static std::vector<ASStackUnpositionedLine> collectChildrenIntoLines(const std::
|
|||||||
Performs the first unconstrained layout of the children, generating the unpositioned items that are then flexed and
|
Performs the first unconstrained layout of the children, generating the unpositioned items that are then flexed and
|
||||||
stretched.
|
stretched.
|
||||||
*/
|
*/
|
||||||
static std::vector<ASStackLayoutSpecItem> layoutChildrenAlongUnconstrainedStackDimension(const std::vector<ASStackLayoutSpecChild> &children,
|
static void layoutItemsAlongUnconstrainedStackDimension(std::vector<ASStackLayoutSpecItem> &items,
|
||||||
const ASStackLayoutSpecStyle &style,
|
const ASStackLayoutSpecStyle &style,
|
||||||
const ASSizeRange &sizeRange,
|
const ASSizeRange &sizeRange,
|
||||||
const CGSize parentSize,
|
const CGSize parentSize,
|
||||||
const BOOL useOptimizedFlexing)
|
const BOOL useOptimizedFlexing)
|
||||||
{
|
{
|
||||||
const CGFloat minCrossDimension = crossDimension(style.direction, sizeRange.min);
|
const CGFloat minCrossDimension = crossDimension(style.direction, sizeRange.min);
|
||||||
const CGFloat maxCrossDimension = crossDimension(style.direction, sizeRange.max);
|
const CGFloat maxCrossDimension = crossDimension(style.direction, sizeRange.max);
|
||||||
return AS::map(children, [&](const ASStackLayoutSpecChild &child) -> ASStackLayoutSpecItem {
|
|
||||||
if (useOptimizedFlexing && isFlexibleInBothDirections(child)) {
|
dispatchApplyIfNeeded(items.size(), ^(size_t i) {
|
||||||
return {child, [ASLayout layoutWithLayoutElement:child.element size:{0, 0}]};
|
auto &item = items[i];
|
||||||
|
if (useOptimizedFlexing && isFlexibleInBothDirections(item.child)) {
|
||||||
|
item.layout = [ASLayout layoutWithLayoutElement:item.child.element size:{0, 0}];
|
||||||
} else {
|
} else {
|
||||||
return {
|
item.layout = crossChildLayout(item.child,
|
||||||
child,
|
style,
|
||||||
crossChildLayout(child,
|
ASDimensionResolve(item.child.style.flexBasis, stackDimension(style.direction, parentSize), 0),
|
||||||
style,
|
ASDimensionResolve(item.child.style.flexBasis, stackDimension(style.direction, parentSize), INFINITY),
|
||||||
ASDimensionResolve(child.style.flexBasis, stackDimension(style.direction, parentSize), 0),
|
minCrossDimension,
|
||||||
ASDimensionResolve(child.style.flexBasis, stackDimension(style.direction, parentSize), INFINITY),
|
maxCrossDimension,
|
||||||
minCrossDimension,
|
parentSize);
|
||||||
maxCrossDimension,
|
|
||||||
parentSize)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -663,14 +696,18 @@ ASStackUnpositionedLayout ASStackUnpositionedLayout::compute(const std::vector<A
|
|||||||
// We may be able to avoid some redundant layout passes
|
// We may be able to avoid some redundant layout passes
|
||||||
const BOOL optimizedFlexing = useOptimizedFlexing(children, style, sizeRange);
|
const BOOL optimizedFlexing = useOptimizedFlexing(children, style, sizeRange);
|
||||||
|
|
||||||
|
std::vector<ASStackLayoutSpecItem> items = AS::map(children, [&](const ASStackLayoutSpecChild &child) -> ASStackLayoutSpecItem {
|
||||||
|
return {child, nil};
|
||||||
|
});
|
||||||
|
|
||||||
// We do a first pass of all the children, generating an unpositioned layout for each with an unbounded range along
|
// We do a first pass of all the children, generating an unpositioned layout for each with an unbounded range along
|
||||||
// the stack dimension. This allows us to compute the "intrinsic" size of each child and find the available violation
|
// the stack dimension. This allows us to compute the "intrinsic" size of each child and find the available violation
|
||||||
// which determines whether we must grow or shrink the flexible children.
|
// which determines whether we must grow or shrink the flexible children.
|
||||||
std::vector<ASStackLayoutSpecItem> items = layoutChildrenAlongUnconstrainedStackDimension(children,
|
layoutItemsAlongUnconstrainedStackDimension(items,
|
||||||
style,
|
style,
|
||||||
sizeRange,
|
sizeRange,
|
||||||
parentSize,
|
parentSize,
|
||||||
optimizedFlexing);
|
optimizedFlexing);
|
||||||
|
|
||||||
// Collect items into lines (https://www.w3.org/TR/css-flexbox-1/#algo-line-break)
|
// Collect items into lines (https://www.w3.org/TR/css-flexbox-1/#algo-line-break)
|
||||||
std::vector<ASStackUnpositionedLine> lines = collectChildrenIntoLines(items, style, sizeRange);
|
std::vector<ASStackUnpositionedLine> lines = collectChildrenIntoLines(items, style, sizeRange);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user