Summary: Pull Request resolved: https://github.com/facebook/yoga/pull/1575 If we want to replay layouts for benchmark, we should also capture the inputs. This diff does that as well as changing the API in CaptureTree.h. We now expose YGCalculateLayoutWithCapture designed to be a drop-in replacement for YGCalculateLayout. This allows us to have a bit more control on the order of everything and lets us capture measure functions in the next diff much easier. Reviewed By: NickGerleman Differential Revision: D53444261 fbshipit-source-id: 616e39153c21e7b472911502b6a717e92c88a4d1
323 lines
9.9 KiB
C++
323 lines
9.9 KiB
C++
/*
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
#include <memory>
|
|
|
|
#include <capture/NodeToString.h>
|
|
|
|
namespace facebook::yoga {
|
|
|
|
using namespace nlohmann;
|
|
|
|
static void appendFloatIfNotDefault(
|
|
json& j,
|
|
std::string_view key,
|
|
float num,
|
|
float defaultNum) {
|
|
if (num != defaultNum && !YGFloatIsUndefined(num)) {
|
|
j[key] = num;
|
|
}
|
|
}
|
|
|
|
static void appendYGValueIfNotDefault(
|
|
json& j,
|
|
std::string_view key,
|
|
const YGValue& value,
|
|
const YGValue& defaultValue) {
|
|
if (value != defaultValue) {
|
|
if (value.unit == YGUnitAuto) {
|
|
j[key] = "auto";
|
|
} else if (value.unit == YGUnitUndefined) {
|
|
j[key] = "undefined";
|
|
} else {
|
|
std::string unit = value.unit == YGUnitPoint ? "px" : "pct";
|
|
j[key]["value"] = value.value;
|
|
j[key]["unit"] = unit;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void appendEnumValueIfNotDefault(
|
|
json& j,
|
|
std::string_view key,
|
|
std::string_view value,
|
|
std::string_view defaultValue) {
|
|
if (value != defaultValue) {
|
|
j[key] = value;
|
|
}
|
|
}
|
|
|
|
static void appendBoolIfNotDefault(
|
|
json& j,
|
|
std::string_view key,
|
|
bool value,
|
|
bool defaultValue) {
|
|
if (value != defaultValue) {
|
|
j[key] = value;
|
|
}
|
|
}
|
|
|
|
template <auto Field>
|
|
static void appendEdges(
|
|
json& j,
|
|
const std::string& key,
|
|
YGNodeRef node,
|
|
YGNodeRef defaultNode) {
|
|
appendYGValueIfNotDefault(
|
|
j["style"],
|
|
key + "-left",
|
|
(*Field)(node, YGEdgeLeft),
|
|
(*Field)(defaultNode, YGEdgeLeft));
|
|
appendYGValueIfNotDefault(
|
|
j["style"],
|
|
key + "-right",
|
|
(*Field)(node, YGEdgeRight),
|
|
(*Field)(defaultNode, YGEdgeRight));
|
|
appendYGValueIfNotDefault(
|
|
j["style"],
|
|
key + "-top",
|
|
(*Field)(node, YGEdgeTop),
|
|
(*Field)(defaultNode, YGEdgeTop));
|
|
appendYGValueIfNotDefault(
|
|
j["style"],
|
|
key + "-bottom",
|
|
(*Field)(node, YGEdgeBottom),
|
|
(*Field)(defaultNode, YGEdgeBottom));
|
|
appendYGValueIfNotDefault(
|
|
j["style"],
|
|
key + "-all",
|
|
(*Field)(node, YGEdgeAll),
|
|
(*Field)(defaultNode, YGEdgeAll));
|
|
appendYGValueIfNotDefault(
|
|
j["style"],
|
|
key + "-start",
|
|
(*Field)(node, YGEdgeStart),
|
|
(*Field)(defaultNode, YGEdgeStart));
|
|
appendYGValueIfNotDefault(
|
|
j["style"],
|
|
key + "-end",
|
|
(*Field)(node, YGEdgeEnd),
|
|
(*Field)(defaultNode, YGEdgeEnd));
|
|
appendYGValueIfNotDefault(
|
|
j["style"],
|
|
key + "-vertical",
|
|
(*Field)(node, YGEdgeVertical),
|
|
(*Field)(defaultNode, YGEdgeVertical));
|
|
appendYGValueIfNotDefault(
|
|
j["style"],
|
|
key + "-horizontal",
|
|
(*Field)(node, YGEdgeHorizontal),
|
|
(*Field)(defaultNode, YGEdgeHorizontal));
|
|
}
|
|
|
|
YGValue borderFloatToYGValue(YGNodeRef node, YGEdge edge) {
|
|
float val = YGNodeStyleGetBorder(node, edge);
|
|
YGUnit unit = YGFloatIsUndefined(val) ? YGUnitUndefined : YGUnitPoint;
|
|
return YGValue{val, unit};
|
|
}
|
|
|
|
void serializeTree(json& j, YGNodeRef node, PrintOptions options) {
|
|
if ((options & PrintOptions::Layout) == PrintOptions::Layout) {
|
|
j["layout"]["width"] = YGNodeStyleGetWidth(node).value;
|
|
j["layout"]["height"] = YGNodeStyleGetHeight(node).value;
|
|
j["layout"]["top"] = YGNodeStyleGetPosition(node, YGEdgeTop).value;
|
|
j["layout"]["left"] = YGNodeStyleGetPosition(node, YGEdgeLeft).value;
|
|
}
|
|
std::unique_ptr<YGNode, decltype(&YGNodeFree)> defaultNode(
|
|
YGNodeNew(), YGNodeFree);
|
|
|
|
if ((options & PrintOptions::Style) == PrintOptions::Style) {
|
|
appendEnumValueIfNotDefault(
|
|
j["style"],
|
|
"flex-direction",
|
|
YGFlexDirectionToString(YGNodeStyleGetFlexDirection(node)),
|
|
YGFlexDirectionToString(
|
|
YGNodeStyleGetFlexDirection(defaultNode.get())));
|
|
appendEnumValueIfNotDefault(
|
|
j["style"],
|
|
"justify-content",
|
|
YGJustifyToString(YGNodeStyleGetJustifyContent(node)),
|
|
YGJustifyToString(YGNodeStyleGetJustifyContent(defaultNode.get())));
|
|
appendEnumValueIfNotDefault(
|
|
j["style"],
|
|
"align-items",
|
|
YGAlignToString(YGNodeStyleGetAlignItems(node)),
|
|
YGAlignToString(YGNodeStyleGetAlignItems(defaultNode.get())));
|
|
appendEnumValueIfNotDefault(
|
|
j["style"],
|
|
"align-content",
|
|
YGAlignToString(YGNodeStyleGetAlignContent(node)),
|
|
YGAlignToString(YGNodeStyleGetAlignContent(defaultNode.get())));
|
|
appendEnumValueIfNotDefault(
|
|
j["style"],
|
|
"align-self",
|
|
YGAlignToString(YGNodeStyleGetAlignSelf(node)),
|
|
YGAlignToString(YGNodeStyleGetAlignSelf(defaultNode.get())));
|
|
appendEnumValueIfNotDefault(
|
|
j["style"],
|
|
"flex-wrap",
|
|
YGWrapToString(YGNodeStyleGetFlexWrap(node)),
|
|
YGWrapToString(YGNodeStyleGetFlexWrap(defaultNode.get())));
|
|
appendEnumValueIfNotDefault(
|
|
j["style"],
|
|
"overflow",
|
|
YGOverflowToString(YGNodeStyleGetOverflow(node)),
|
|
YGOverflowToString(YGNodeStyleGetOverflow(defaultNode.get())));
|
|
appendEnumValueIfNotDefault(
|
|
j["style"],
|
|
"display",
|
|
YGDisplayToString(YGNodeStyleGetDisplay(node)),
|
|
YGDisplayToString(YGNodeStyleGetDisplay(defaultNode.get())));
|
|
appendEnumValueIfNotDefault(
|
|
j["style"],
|
|
"position-type",
|
|
YGPositionTypeToString(YGNodeStyleGetPositionType(node)),
|
|
YGPositionTypeToString(YGNodeStyleGetPositionType(defaultNode.get())));
|
|
|
|
appendFloatIfNotDefault(
|
|
j["style"],
|
|
"flex-grow",
|
|
YGNodeStyleGetFlexGrow(node),
|
|
YGNodeStyleGetFlexGrow(defaultNode.get()));
|
|
appendFloatIfNotDefault(
|
|
j["style"],
|
|
"flex-shrink",
|
|
YGNodeStyleGetFlexShrink(node),
|
|
YGNodeStyleGetFlexShrink(defaultNode.get()));
|
|
appendFloatIfNotDefault(
|
|
j["style"],
|
|
"flex",
|
|
YGNodeStyleGetFlex(node),
|
|
YGNodeStyleGetFlex(defaultNode.get()));
|
|
appendYGValueIfNotDefault(
|
|
j["style"],
|
|
"flex-basis",
|
|
YGNodeStyleGetFlexBasis(node),
|
|
YGNodeStyleGetFlexBasis(defaultNode.get()));
|
|
|
|
appendEdges<&YGNodeStyleGetMargin>(j, "margin", node, defaultNode.get());
|
|
appendEdges<&YGNodeStyleGetPadding>(j, "padding", node, defaultNode.get());
|
|
appendEdges<&borderFloatToYGValue>(j, "border", node, defaultNode.get());
|
|
appendEdges<&YGNodeStyleGetPosition>(
|
|
j, "position", node, defaultNode.get());
|
|
|
|
appendFloatIfNotDefault(
|
|
j["style"],
|
|
"gap",
|
|
YGNodeStyleGetGap(node, YGGutterAll),
|
|
YGNodeStyleGetGap(defaultNode.get(), YGGutterAll));
|
|
appendFloatIfNotDefault(
|
|
j["style"],
|
|
"column-gap",
|
|
YGNodeStyleGetGap(node, YGGutterColumn),
|
|
YGNodeStyleGetGap(defaultNode.get(), YGGutterColumn));
|
|
appendFloatIfNotDefault(
|
|
j["style"],
|
|
"row-gap",
|
|
YGNodeStyleGetGap(node, YGGutterRow),
|
|
YGNodeStyleGetGap(defaultNode.get(), YGGutterRow));
|
|
|
|
appendYGValueIfNotDefault(
|
|
j["style"],
|
|
"width",
|
|
YGNodeStyleGetWidth(node),
|
|
YGNodeStyleGetWidth(defaultNode.get()));
|
|
appendYGValueIfNotDefault(
|
|
j["style"],
|
|
"height",
|
|
YGNodeStyleGetHeight(node),
|
|
YGNodeStyleGetHeight(defaultNode.get()));
|
|
appendYGValueIfNotDefault(
|
|
j["style"],
|
|
"max-width",
|
|
YGNodeStyleGetMaxWidth(node),
|
|
YGNodeStyleGetMaxWidth(defaultNode.get()));
|
|
appendYGValueIfNotDefault(
|
|
j["style"],
|
|
"max-height",
|
|
YGNodeStyleGetMaxHeight(node),
|
|
YGNodeStyleGetMaxHeight(defaultNode.get()));
|
|
appendYGValueIfNotDefault(
|
|
j["style"],
|
|
"min-width",
|
|
YGNodeStyleGetMinWidth(node),
|
|
YGNodeStyleGetMinWidth(defaultNode.get()));
|
|
appendYGValueIfNotDefault(
|
|
j["style"],
|
|
"min-height",
|
|
YGNodeStyleGetMinHeight(node),
|
|
YGNodeStyleGetMinHeight(defaultNode.get()));
|
|
}
|
|
|
|
if ((options & PrintOptions::Config) == PrintOptions::Config) {
|
|
YGConfigConstRef config = YGNodeGetConfig(node);
|
|
YGConfigConstRef defaultConfig = YGConfigGetDefault();
|
|
|
|
appendBoolIfNotDefault(
|
|
j["config"],
|
|
"use-web-defaults",
|
|
YGConfigGetUseWebDefaults(config),
|
|
YGConfigGetUseWebDefaults(defaultConfig));
|
|
appendFloatIfNotDefault(
|
|
j["config"],
|
|
"point-scale-factor",
|
|
YGConfigGetPointScaleFactor(config),
|
|
YGConfigGetPointScaleFactor(defaultConfig));
|
|
YGErrata errata = YGConfigGetErrata(config);
|
|
if (errata == YGErrataNone || errata == YGErrataAll ||
|
|
errata == YGErrataClassic) {
|
|
appendEnumValueIfNotDefault(
|
|
j["config"],
|
|
"errata",
|
|
YGErrataToString(errata),
|
|
YGErrataToString(YGConfigGetErrata(defaultConfig)));
|
|
}
|
|
|
|
if (YGConfigIsExperimentalFeatureEnabled(
|
|
config, YGExperimentalFeatureWebFlexBasis) !=
|
|
YGConfigIsExperimentalFeatureEnabled(
|
|
defaultConfig, YGExperimentalFeatureWebFlexBasis)) {
|
|
j["config"]["experimental-features"].push_back(
|
|
YGExperimentalFeatureToString(YGExperimentalFeatureWebFlexBasis));
|
|
}
|
|
}
|
|
|
|
if ((options & PrintOptions::Node) == PrintOptions::Node) {
|
|
appendBoolIfNotDefault(
|
|
j["node"],
|
|
"always-forms-containing-block",
|
|
YGNodeGetAlwaysFormsContainingBlock(node),
|
|
YGNodeGetAlwaysFormsContainingBlock(defaultNode.get()));
|
|
if (YGNodeHasMeasureFunc(node)) {
|
|
j["node"]["has-custom-measure"] = true;
|
|
}
|
|
}
|
|
|
|
const size_t childCount = YGNodeGetChildCount(node);
|
|
if ((options & PrintOptions::Children) == PrintOptions::Children &&
|
|
childCount > 0) {
|
|
for (size_t i = 0; i < childCount; i++) {
|
|
j["children"].push_back({});
|
|
serializeTree(j["children"][i], YGNodeGetChild(node, i), options);
|
|
}
|
|
}
|
|
}
|
|
|
|
void serializeLayoutInputs(
|
|
json& j,
|
|
float availableWidth,
|
|
float availableHeight,
|
|
YGDirection ownerDirection) {
|
|
j["layout-inputs"] = {
|
|
{"available-width", availableWidth},
|
|
{"available-height", availableHeight},
|
|
{"owner-direction", YGDirectionToString(ownerDirection)},
|
|
};
|
|
}
|
|
|
|
} // namespace facebook::yoga
|