mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
782e004e49
Summary: Without getting into the weeds too much, RawPropParser "requires" that props be accessed in the same order every time a Props struct is parsed in order to most optimally fetch the values in a linear-ish fashion, basically ensuring that each rawProps.at() call is an O(1) operation, and overall getting all props for a particular component is O(n) in the number of props for a given struct. If props are called out of order, this breaks and worst-case we can end up with an O(n^2) operation. Unfortunately, calling .at(x) twice with the same prop name triggers the deoptimized behavior. So as much as possible, always fetch exactly once and in the same order every time. In this case, we move initialization of two fields into the constructor body so that we can call .at() a single time instead of twice. In the debug props of ViewProps I'm also reordering the fields to fetch them in the same order the constructor fetches them in, which will make this (debug-only) method slightly faster. What's the impact of this? If you dig into the Tracery samples, the average/median RawPropsParser::at takes 1us or less. However, in /every single/ call to createNode for View components, there is at least one RawPropsParser::at call that takes 250+us. This was a huge red flag when analyzing traces, after which it was trivial (for View) to find the offending out-of-order calls. Since this is happening for every View and every type of component that derives from View, that's 1ms lost per every 4 View-type ShadowNodes created by ReactJS. After just 100 views created, that's 25ms. Etc. There are other out-of-order calls lurking in the codebase that can be addressed separately. Impact scales with the size of the screen, the number of Views they render, etc. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D36889794 fbshipit-source-id: 91e0a7ca39ed10778e60a0f0339a4b4dc8b14436
295 lines
8.9 KiB
C++
295 lines
8.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 "ViewProps.h"
|
|
|
|
#include <algorithm>
|
|
|
|
#include <react/renderer/components/view/conversions.h>
|
|
#include <react/renderer/components/view/propsConversions.h>
|
|
#include <react/renderer/core/propsConversions.h>
|
|
#include <react/renderer/debug/debugStringConvertibleUtils.h>
|
|
#include <react/renderer/graphics/conversions.h>
|
|
|
|
namespace facebook {
|
|
namespace react {
|
|
|
|
ViewProps::ViewProps(
|
|
const PropsParserContext &context,
|
|
ViewProps const &sourceProps,
|
|
RawProps const &rawProps,
|
|
bool shouldSetRawProps)
|
|
: YogaStylableProps(context, sourceProps, rawProps, shouldSetRawProps),
|
|
AccessibilityProps(context, sourceProps, rawProps),
|
|
opacity(convertRawProp(
|
|
context,
|
|
rawProps,
|
|
"opacity",
|
|
sourceProps.opacity,
|
|
(Float)1.0)),
|
|
foregroundColor(convertRawProp(
|
|
context,
|
|
rawProps,
|
|
"foregroundColor",
|
|
sourceProps.foregroundColor,
|
|
{})),
|
|
backgroundColor(convertRawProp(
|
|
context,
|
|
rawProps,
|
|
"backgroundColor",
|
|
sourceProps.backgroundColor,
|
|
{})),
|
|
borderRadii(convertRawProp(
|
|
context,
|
|
rawProps,
|
|
"border",
|
|
"Radius",
|
|
sourceProps.borderRadii,
|
|
{})),
|
|
borderColors(convertRawProp(
|
|
context,
|
|
rawProps,
|
|
"border",
|
|
"Color",
|
|
sourceProps.borderColors,
|
|
{})),
|
|
borderStyles(convertRawProp(
|
|
context,
|
|
rawProps,
|
|
"border",
|
|
"Style",
|
|
sourceProps.borderStyles,
|
|
{})),
|
|
shadowColor(convertRawProp(
|
|
context,
|
|
rawProps,
|
|
"shadowColor",
|
|
sourceProps.shadowColor,
|
|
{})),
|
|
shadowOffset(convertRawProp(
|
|
context,
|
|
rawProps,
|
|
"shadowOffset",
|
|
sourceProps.shadowOffset,
|
|
{})),
|
|
shadowOpacity(convertRawProp(
|
|
context,
|
|
rawProps,
|
|
"shadowOpacity",
|
|
sourceProps.shadowOpacity,
|
|
{})),
|
|
shadowRadius(convertRawProp(
|
|
context,
|
|
rawProps,
|
|
"shadowRadius",
|
|
sourceProps.shadowRadius,
|
|
{})),
|
|
transform(convertRawProp(
|
|
context,
|
|
rawProps,
|
|
"transform",
|
|
sourceProps.transform,
|
|
{})),
|
|
backfaceVisibility(convertRawProp(
|
|
context,
|
|
rawProps,
|
|
"backfaceVisibility",
|
|
sourceProps.backfaceVisibility,
|
|
{})),
|
|
shouldRasterize(convertRawProp(
|
|
context,
|
|
rawProps,
|
|
"shouldRasterize",
|
|
sourceProps.shouldRasterize,
|
|
{})),
|
|
zIndex(
|
|
convertRawProp(context, rawProps, "zIndex", sourceProps.zIndex, {})),
|
|
pointerEvents(convertRawProp(
|
|
context,
|
|
rawProps,
|
|
"pointerEvents",
|
|
sourceProps.pointerEvents,
|
|
{})),
|
|
hitSlop(convertRawProp(
|
|
context,
|
|
rawProps,
|
|
"hitSlop",
|
|
sourceProps.hitSlop,
|
|
{})),
|
|
onLayout(convertRawProp(
|
|
context,
|
|
rawProps,
|
|
"onLayout",
|
|
sourceProps.onLayout,
|
|
{})),
|
|
events(convertRawProp(context, rawProps, sourceProps.events, {})),
|
|
collapsable(convertRawProp(
|
|
context,
|
|
rawProps,
|
|
"collapsable",
|
|
sourceProps.collapsable,
|
|
true)),
|
|
removeClippedSubviews(convertRawProp(
|
|
context,
|
|
rawProps,
|
|
"removeClippedSubviews",
|
|
sourceProps.removeClippedSubviews,
|
|
false))
|
|
#ifdef ANDROID
|
|
,
|
|
elevation(convertRawProp(
|
|
context,
|
|
rawProps,
|
|
"elevation",
|
|
sourceProps.elevation,
|
|
{})),
|
|
nativeBackground(convertRawProp(
|
|
context,
|
|
rawProps,
|
|
"nativeBackgroundAndroid",
|
|
sourceProps.nativeBackground,
|
|
{})),
|
|
nativeForeground(convertRawProp(
|
|
context,
|
|
rawProps,
|
|
"nativeForegroundAndroid",
|
|
sourceProps.nativeForeground,
|
|
{})),
|
|
focusable(convertRawProp(
|
|
context,
|
|
rawProps,
|
|
"focusable",
|
|
sourceProps.focusable,
|
|
{})),
|
|
hasTVPreferredFocus(convertRawProp(
|
|
context,
|
|
rawProps,
|
|
"hasTVPreferredFocus",
|
|
sourceProps.hasTVPreferredFocus,
|
|
{})),
|
|
needsOffscreenAlphaCompositing(convertRawProp(
|
|
context,
|
|
rawProps,
|
|
"needsOffscreenAlphaCompositing",
|
|
sourceProps.needsOffscreenAlphaCompositing,
|
|
{})),
|
|
renderToHardwareTextureAndroid(convertRawProp(
|
|
context,
|
|
rawProps,
|
|
"renderToHardwareTextureAndroid",
|
|
sourceProps.renderToHardwareTextureAndroid,
|
|
{}))
|
|
#endif
|
|
{};
|
|
|
|
#pragma mark - Convenience Methods
|
|
|
|
static BorderRadii ensureNoOverlap(BorderRadii const &radii, Size const &size) {
|
|
// "Corner curves must not overlap: When the sum of any two adjacent border
|
|
// radii exceeds the size of the border box, UAs must proportionally reduce
|
|
// the used values of all border radii until none of them overlap."
|
|
// Source: https://www.w3.org/TR/css-backgrounds-3/#corner-overlap
|
|
|
|
auto insets = EdgeInsets{
|
|
/* .left = */ radii.topLeft + radii.bottomLeft,
|
|
/* .top = */ radii.topLeft + radii.topRight,
|
|
/* .right = */ radii.topRight + radii.bottomRight,
|
|
/* .bottom = */ radii.bottomLeft + radii.bottomRight,
|
|
};
|
|
|
|
auto insetsScale = EdgeInsets{
|
|
/* .left = */
|
|
insets.left > 0 ? std::min((Float)1.0, size.height / insets.left) : 0,
|
|
/* .top = */
|
|
insets.top > 0 ? std::min((Float)1.0, size.width / insets.top) : 0,
|
|
/* .right = */
|
|
insets.right > 0 ? std::min((Float)1.0, size.height / insets.right) : 0,
|
|
/* .bottom = */
|
|
insets.bottom > 0 ? std::min((Float)1.0, size.width / insets.bottom) : 0,
|
|
};
|
|
|
|
return BorderRadii{
|
|
/* topLeft = */
|
|
radii.topLeft * std::min(insetsScale.top, insetsScale.left),
|
|
/* topRight = */
|
|
radii.topRight * std::min(insetsScale.top, insetsScale.right),
|
|
/* bottomLeft = */
|
|
radii.bottomLeft * std::min(insetsScale.bottom, insetsScale.left),
|
|
/* bottomRight = */
|
|
radii.bottomRight * std::min(insetsScale.bottom, insetsScale.right),
|
|
};
|
|
}
|
|
|
|
BorderMetrics ViewProps::resolveBorderMetrics(
|
|
LayoutMetrics const &layoutMetrics) const {
|
|
auto isRTL =
|
|
bool{layoutMetrics.layoutDirection == LayoutDirection::RightToLeft};
|
|
|
|
auto borderWidths = CascadedBorderWidths{
|
|
/* .left = */ optionalFloatFromYogaValue(yogaStyle.border()[YGEdgeLeft]),
|
|
/* .top = */ optionalFloatFromYogaValue(yogaStyle.border()[YGEdgeTop]),
|
|
/* .right = */
|
|
optionalFloatFromYogaValue(yogaStyle.border()[YGEdgeRight]),
|
|
/* .bottom = */
|
|
optionalFloatFromYogaValue(yogaStyle.border()[YGEdgeBottom]),
|
|
/* .start = */
|
|
optionalFloatFromYogaValue(yogaStyle.border()[YGEdgeStart]),
|
|
/* .end = */ optionalFloatFromYogaValue(yogaStyle.border()[YGEdgeEnd]),
|
|
/* .horizontal = */
|
|
optionalFloatFromYogaValue(yogaStyle.border()[YGEdgeHorizontal]),
|
|
/* .vertical = */
|
|
optionalFloatFromYogaValue(yogaStyle.border()[YGEdgeVertical]),
|
|
/* .all = */ optionalFloatFromYogaValue(yogaStyle.border()[YGEdgeAll]),
|
|
};
|
|
|
|
return {
|
|
/* .borderColors = */ borderColors.resolve(isRTL, {}),
|
|
/* .borderWidths = */ borderWidths.resolve(isRTL, 0),
|
|
/* .borderRadii = */
|
|
ensureNoOverlap(borderRadii.resolve(isRTL, 0), layoutMetrics.frame.size),
|
|
/* .borderStyles = */ borderStyles.resolve(isRTL, BorderStyle::Solid),
|
|
};
|
|
}
|
|
|
|
bool ViewProps::getClipsContentToBounds() const {
|
|
return yogaStyle.overflow() != YGOverflowVisible;
|
|
}
|
|
|
|
#ifdef ANDROID
|
|
bool ViewProps::getProbablyMoreHorizontalThanVertical_DEPRECATED() const {
|
|
return yogaStyle.flexDirection() == YGFlexDirectionRow;
|
|
}
|
|
#endif
|
|
|
|
#pragma mark - DebugStringConvertible
|
|
|
|
#if RN_DEBUG_STRING_CONVERTIBLE
|
|
SharedDebugStringConvertibleList ViewProps::getDebugProps() const {
|
|
const auto &defaultViewProps = ViewProps();
|
|
|
|
return AccessibilityProps::getDebugProps() +
|
|
YogaStylableProps::getDebugProps() +
|
|
SharedDebugStringConvertibleList{
|
|
debugStringConvertibleItem(
|
|
"opacity", opacity, defaultViewProps.opacity),
|
|
debugStringConvertibleItem(
|
|
"foregroundColor",
|
|
foregroundColor,
|
|
defaultViewProps.foregroundColor),
|
|
debugStringConvertibleItem(
|
|
"backgroundColor",
|
|
backgroundColor,
|
|
defaultViewProps.backgroundColor),
|
|
debugStringConvertibleItem(
|
|
"zIndex", zIndex, defaultViewProps.zIndex.value_or(0)),
|
|
};
|
|
}
|
|
#endif
|
|
|
|
} // namespace react
|
|
} // namespace facebook
|