Skip to content

Commit e4fe14a

Browse files
acoates-msfacebook-github-bot
authored andcommitted
Add config version, and invalidate layout on config change (facebook#1674)
Summary: X-link: facebook/react-native#45259 This is a continuation of the previous PR: facebook/react-native#45047 I made the change more generic for allowing any kind of config change to invalidate layout. Changelog: [Internal] Pull Request resolved: facebook#1674 Reviewed By: rozele Differential Revision: D59286992 Pulled By: NickGerleman fbshipit-source-id: f46f35b03d5d9a743b798844ee3e1a02c271ccde
1 parent a1e9abb commit e4fe14a

File tree

7 files changed

+212
-5
lines changed

7 files changed

+212
-5
lines changed

tests/YGScaleChangeTest.cpp

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#include <gtest/gtest.h>
9+
#include <yoga/Yoga.h>
10+
11+
TEST(YogaTest, scale_change_invalidates_layout) {
12+
YGConfigRef config = YGConfigNew();
13+
14+
YGNodeRef root = YGNodeNewWithConfig(config);
15+
YGConfigSetPointScaleFactor(config, 1.0f);
16+
17+
YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow);
18+
YGNodeStyleSetWidth(root, 50);
19+
YGNodeStyleSetHeight(root, 50);
20+
21+
YGNodeRef root_child0 = YGNodeNewWithConfig(config);
22+
YGNodeStyleSetFlexGrow(root_child0, 1);
23+
YGNodeInsertChild(root, root_child0, 0);
24+
25+
YGNodeRef root_child1 = YGNodeNewWithConfig(config);
26+
YGNodeStyleSetFlexGrow(root_child1, 1);
27+
YGNodeInsertChild(root, root_child1, 1);
28+
29+
YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
30+
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0));
31+
ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child1));
32+
33+
YGConfigSetPointScaleFactor(config, 1.5f);
34+
YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
35+
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0));
36+
// Left should change due to pixel alignment of new scale factor
37+
ASSERT_FLOAT_EQ(25.333334f, YGNodeLayoutGetLeft(root_child1));
38+
39+
YGNodeFreeRecursive(root);
40+
YGConfigFree(config);
41+
}
42+
43+
TEST(YogaTest, errata_config_change_relayout) {
44+
YGConfig* config = YGConfigNew();
45+
YGConfigSetErrata(config, YGErrataStretchFlexBasis);
46+
YGNodeRef root = YGNodeNewWithConfig(config);
47+
YGNodeStyleSetWidth(root, 500);
48+
YGNodeStyleSetHeight(root, 500);
49+
50+
YGNodeRef root_child0 = YGNodeNewWithConfig(config);
51+
YGNodeStyleSetAlignItems(root_child0, YGAlignFlexStart);
52+
YGNodeInsertChild(root, root_child0, 0);
53+
54+
YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config);
55+
YGNodeStyleSetFlexGrow(root_child0_child0, 1);
56+
YGNodeStyleSetFlexShrink(root_child0_child0, 1);
57+
YGNodeInsertChild(root_child0, root_child0_child0, 0);
58+
59+
YGNodeRef root_child0_child0_child0 = YGNodeNewWithConfig(config);
60+
YGNodeStyleSetFlexGrow(root_child0_child0_child0, 1);
61+
YGNodeStyleSetFlexShrink(root_child0_child0_child0, 1);
62+
YGNodeInsertChild(root_child0_child0, root_child0_child0_child0, 0);
63+
YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
64+
65+
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root));
66+
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root));
67+
ASSERT_FLOAT_EQ(500, YGNodeLayoutGetWidth(root));
68+
ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root));
69+
70+
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0));
71+
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0));
72+
ASSERT_FLOAT_EQ(500, YGNodeLayoutGetWidth(root_child0));
73+
ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root_child0));
74+
75+
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0));
76+
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0));
77+
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0_child0));
78+
ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root_child0_child0));
79+
80+
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0_child0));
81+
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0_child0));
82+
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0_child0_child0));
83+
ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root_child0_child0_child0));
84+
85+
YGConfigSetErrata(config, YGErrataNone);
86+
YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
87+
88+
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root));
89+
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root));
90+
ASSERT_FLOAT_EQ(500, YGNodeLayoutGetWidth(root));
91+
ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root));
92+
93+
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0));
94+
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0));
95+
ASSERT_FLOAT_EQ(500, YGNodeLayoutGetWidth(root_child0));
96+
// This should be modified by the lack of the errata
97+
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0));
98+
99+
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0));
100+
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0));
101+
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0_child0));
102+
// This should be modified by the lack of the errata
103+
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0_child0));
104+
105+
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0_child0));
106+
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0_child0));
107+
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetWidth(root_child0_child0_child0));
108+
// This should be modified by the lack of the errata
109+
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetHeight(root_child0_child0_child0));
110+
111+
YGNodeFreeRecursive(root);
112+
113+
YGConfigFree(config);
114+
}
115+
116+
TEST(YogaTest, setting_compatible_config_maintains_layout_cache) {
117+
static uint32_t measureCallCount = 0;
118+
auto measureCustom = [](YGNodeConstRef /*node*/,
119+
float /*width*/,
120+
YGMeasureMode /*widthMode*/,
121+
float /*height*/,
122+
YGMeasureMode /*heightMode*/) {
123+
measureCallCount++;
124+
return YGSize{
125+
.width = 25.0f,
126+
.height = 25.0f,
127+
};
128+
};
129+
130+
YGConfigRef config = YGConfigNew();
131+
132+
YGNodeRef root = YGNodeNewWithConfig(config);
133+
YGConfigSetPointScaleFactor(config, 1.0f);
134+
135+
YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow);
136+
YGNodeStyleSetWidth(root, 50);
137+
YGNodeStyleSetHeight(root, 50);
138+
139+
YGNodeRef root_child0 = YGNodeNewWithConfig(config);
140+
EXPECT_EQ(0, measureCallCount);
141+
142+
YGNodeSetMeasureFunc(root_child0, measureCustom);
143+
YGNodeInsertChild(root, root_child0, 0);
144+
145+
YGNodeRef root_child1 = YGNodeNewWithConfig(config);
146+
YGNodeStyleSetFlexGrow(root_child1, 1);
147+
YGNodeInsertChild(root, root_child1, 1);
148+
149+
YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
150+
EXPECT_EQ(1, measureCallCount);
151+
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0));
152+
ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child1));
153+
154+
YGConfigRef config2 = YGConfigNew();
155+
// Calling YGConfigSetPointScaleFactor multiple times, ensures that config2
156+
// gets a different config version that config1
157+
YGConfigSetPointScaleFactor(config2, 1.0f);
158+
YGConfigSetPointScaleFactor(config2, 1.5f);
159+
YGConfigSetPointScaleFactor(config2, 1.0f);
160+
161+
YGNodeSetConfig(root, config2);
162+
YGNodeSetConfig(root_child0, config2);
163+
YGNodeSetConfig(root_child1, config2);
164+
165+
YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
166+
167+
// Measure should not be called again, as layout should have been cached since
168+
// config is functionally the same as before
169+
EXPECT_EQ(1, measureCallCount);
170+
ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0));
171+
ASSERT_FLOAT_EQ(25, YGNodeLayoutGetLeft(root_child1));
172+
173+
YGNodeFreeRecursive(root);
174+
YGConfigFree(config);
175+
YGConfigFree(config2);
176+
}

yoga/algorithm/CalculateLayout.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2140,6 +2140,7 @@ bool calculateLayoutInternal(
21402140

21412141
const bool needToVisitNode =
21422142
(node->isDirty() && layout->generationCount != generationCount) ||
2143+
layout->configVersion != node->getConfig()->getVersion() ||
21432144
layout->lastOwnerDirection != ownerDirection;
21442145

21452146
if (needToVisitNode) {
@@ -2255,6 +2256,7 @@ bool calculateLayoutInternal(
22552256
reason);
22562257

22572258
layout->lastOwnerDirection = ownerDirection;
2259+
layout->configVersion = node->getConfig()->getVersion();
22582260

22592261
if (cachedResults == nullptr) {
22602262
layoutMarkerData.maxMeasureCache = std::max(

yoga/config/Config.cpp

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,10 @@ bool Config::useWebDefaults() const {
3131
void Config::setExperimentalFeatureEnabled(
3232
ExperimentalFeature feature,
3333
bool enabled) {
34-
experimentalFeatures_.set(static_cast<size_t>(feature), enabled);
34+
if (isExperimentalFeatureEnabled(feature) != enabled) {
35+
experimentalFeatures_.set(static_cast<size_t>(feature), enabled);
36+
version_++;
37+
}
3538
}
3639

3740
bool Config::isExperimentalFeatureEnabled(ExperimentalFeature feature) const {
@@ -43,15 +46,24 @@ ExperimentalFeatureSet Config::getEnabledExperiments() const {
4346
}
4447

4548
void Config::setErrata(Errata errata) {
46-
errata_ = errata;
49+
if (errata_ != errata) {
50+
errata_ = errata;
51+
version_++;
52+
}
4753
}
4854

4955
void Config::addErrata(Errata errata) {
50-
errata_ |= errata;
56+
if (!hasErrata(errata)) {
57+
errata_ |= errata;
58+
version_++;
59+
}
5160
}
5261

5362
void Config::removeErrata(Errata errata) {
54-
errata_ &= (~errata);
63+
if (hasErrata(errata)) {
64+
errata_ &= (~errata);
65+
version_++;
66+
}
5567
}
5668

5769
Errata Config::getErrata() const {
@@ -63,7 +75,10 @@ bool Config::hasErrata(Errata errata) const {
6375
}
6476

6577
void Config::setPointScaleFactor(float pointScaleFactor) {
66-
pointScaleFactor_ = pointScaleFactor;
78+
if (pointScaleFactor_ != pointScaleFactor) {
79+
pointScaleFactor_ = pointScaleFactor;
80+
version_++;
81+
}
6782
}
6883

6984
float Config::getPointScaleFactor() const {
@@ -78,6 +93,10 @@ void* Config::getContext() const {
7893
return context_;
7994
}
8095

96+
uint32_t Config::getVersion() const noexcept {
97+
return version_;
98+
}
99+
81100
void Config::setLogger(YGLogger logger) {
82101
logger_ = logger;
83102
}

yoga/config/Config.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ class YG_EXPORT Config : public ::YGConfig {
5353
void setContext(void* context);
5454
void* getContext() const;
5555

56+
uint32_t getVersion() const noexcept;
57+
5658
void setLogger(YGLogger logger);
5759
void log(
5860
const yoga::Node* node,
@@ -72,6 +74,7 @@ class YG_EXPORT Config : public ::YGConfig {
7274

7375
bool useWebDefaults_ : 1 = false;
7476

77+
uint32_t version_ = 0;
7578
ExperimentalFeatureSet experimentalFeatures_{};
7679
Errata errata_ = Errata::None;
7780
float pointScaleFactor_ = 1.0f;

yoga/node/LayoutResults.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ bool LayoutResults::operator==(LayoutResults layout) const {
2121
direction() == layout.direction() &&
2222
hadOverflow() == layout.hadOverflow() &&
2323
lastOwnerDirection == layout.lastOwnerDirection &&
24+
configVersion == layout.configVersion &&
2425
nextCachedMeasurementsIndex == layout.nextCachedMeasurementsIndex &&
2526
cachedLayout == layout.cachedLayout &&
2627
computedFlexBasis == layout.computedFlexBasis;

yoga/node/LayoutResults.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ struct LayoutResults {
3030
// Instead of recomputing the entire layout every single time, we cache some
3131
// information to break early when nothing changed
3232
uint32_t generationCount = 0;
33+
uint32_t configVersion = 0;
3334
Direction lastOwnerDirection = Direction::Inherit;
3435

3536
uint32_t nextCachedMeasurementsIndex = 0;

yoga/node/Node.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,11 @@ void Node::setConfig(yoga::Config* config) {
136136

137137
if (yoga::configUpdateInvalidatesLayout(*config_, *config)) {
138138
markDirtyAndPropagate();
139+
layout_.configVersion = 0;
140+
} else {
141+
// If the config is functionally the same, then align the configVersion so
142+
// that we can reuse the layout cache
143+
layout_.configVersion = config->getVersion();
139144
}
140145

141146
config_ = config;

0 commit comments

Comments
 (0)