Use native helpers to accelerate transform processing (#52603)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52603

Processing transforms is expensive in Java, as it requires bridging the entire ReadableNativeArray/Map. Instead, we can use the existing parser logic `resolveTransform` logic to perform this operation in C++.

Ideally, we actually re-use the existing parsed transform from Props, that could be something we revisit after Props 2.0.

As a follow-up, we should consider also moving the matrix decomposition logic from MatrixMathHelper here, and make that the only information we send back to Java.

Changelog: [Internal]

Reviewed By: NickGerleman

Differential Revision: D78298588

fbshipit-source-id: a698ac8587ccfb2be04665747082398ccdde9294
This commit is contained in:
Pieter De Baets
2025-09-02 16:40:14 +02:00
committed by Nicola Corti
parent 1b5c183bf3
commit 8bac865b5a
29 changed files with 271 additions and 31 deletions
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<57a128d47d69507e15f768970753c89e>>
* @generated SignedSource<<c52f3977ea07f976e36177f13c1ec684>>
*/
/**
@@ -348,6 +348,12 @@ public object ReactNativeFeatureFlags {
@JvmStatic
public fun useNativeEqualsInNativeReadableArrayAndroid(): Boolean = accessor.useNativeEqualsInNativeReadableArrayAndroid()
/**
* Use a native implementation of TransformHelper
*/
@JvmStatic
public fun useNativeTransformHelperAndroid(): Boolean = accessor.useNativeTransformHelperAndroid()
/**
* When enabled, the native view configs are used in bridgeless mode.
*/
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<aeca459215a07627be807ef85d97e6e6>>
* @generated SignedSource<<8e0125e82b359e6a175ffc49a4df5537>>
*/
/**
@@ -73,6 +73,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
private var useAlwaysAvailableJSErrorHandlingCache: Boolean? = null
private var useFabricInteropCache: Boolean? = null
private var useNativeEqualsInNativeReadableArrayAndroidCache: Boolean? = null
private var useNativeTransformHelperAndroidCache: Boolean? = null
private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null
private var useOptimizedEventBatchingOnAndroidCache: Boolean? = null
private var useRawPropsJsiValueCache: Boolean? = null
@@ -558,6 +559,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
return cached
}
override fun useNativeTransformHelperAndroid(): Boolean {
var cached = useNativeTransformHelperAndroidCache
if (cached == null) {
cached = ReactNativeFeatureFlagsCxxInterop.useNativeTransformHelperAndroid()
useNativeTransformHelperAndroidCache = cached
}
return cached
}
override fun useNativeViewConfigsInBridgelessMode(): Boolean {
var cached = useNativeViewConfigsInBridgelessModeCache
if (cached == null) {
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<1729eb15adfaaf4daec98240ddc78ca0>>
* @generated SignedSource<<a5d9d11cc2a6529641243dc47a61f201>>
*/
/**
@@ -134,6 +134,8 @@ public object ReactNativeFeatureFlagsCxxInterop {
@DoNotStrip @JvmStatic public external fun useNativeEqualsInNativeReadableArrayAndroid(): Boolean
@DoNotStrip @JvmStatic public external fun useNativeTransformHelperAndroid(): Boolean
@DoNotStrip @JvmStatic public external fun useNativeViewConfigsInBridgelessMode(): Boolean
@DoNotStrip @JvmStatic public external fun useOptimizedEventBatchingOnAndroid(): Boolean
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<ef873d8fc11ffabd9f9d82d4e95ee8d8>>
* @generated SignedSource<<10d708ce4449eede46d750a1ed48d02e>>
*/
/**
@@ -129,6 +129,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
override fun useNativeEqualsInNativeReadableArrayAndroid(): Boolean = false
override fun useNativeTransformHelperAndroid(): Boolean = false
override fun useNativeViewConfigsInBridgelessMode(): Boolean = false
override fun useOptimizedEventBatchingOnAndroid(): Boolean = false
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<5f923f20a4d5da0935058ab99fd264b3>>
* @generated SignedSource<<b04948c792c5db63decf1df80d3a867e>>
*/
/**
@@ -77,6 +77,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
private var useAlwaysAvailableJSErrorHandlingCache: Boolean? = null
private var useFabricInteropCache: Boolean? = null
private var useNativeEqualsInNativeReadableArrayAndroidCache: Boolean? = null
private var useNativeTransformHelperAndroidCache: Boolean? = null
private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null
private var useOptimizedEventBatchingOnAndroidCache: Boolean? = null
private var useRawPropsJsiValueCache: Boolean? = null
@@ -615,6 +616,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
return cached
}
override fun useNativeTransformHelperAndroid(): Boolean {
var cached = useNativeTransformHelperAndroidCache
if (cached == null) {
cached = currentProvider.useNativeTransformHelperAndroid()
accessedFeatureFlags.add("useNativeTransformHelperAndroid")
useNativeTransformHelperAndroidCache = cached
}
return cached
}
override fun useNativeViewConfigsInBridgelessMode(): Boolean {
var cached = useNativeViewConfigsInBridgelessModeCache
if (cached == null) {
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<e3ee9fa62380e175a564d2cd94539335>>
* @generated SignedSource<<0bafb0a2fb79c4220d21f1736894af14>>
*/
/**
@@ -26,4 +26,6 @@ public open class ReactNativeFeatureFlagsOverrides_RNOSS_Experimental_Android :
override fun preventShadowTreeCommitExhaustion(): Boolean = true
override fun useNativeEqualsInNativeReadableArrayAndroid(): Boolean = true
override fun useNativeTransformHelperAndroid(): Boolean = true
}
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<95a5bbbe7e05ff51434ff060ef94b74f>>
* @generated SignedSource<<21704207ce520def05b05f89dfba1048>>
*/
/**
@@ -129,6 +129,8 @@ public interface ReactNativeFeatureFlagsProvider {
@DoNotStrip public fun useNativeEqualsInNativeReadableArrayAndroid(): Boolean
@DoNotStrip public fun useNativeTransformHelperAndroid(): Boolean
@DoNotStrip public fun useNativeViewConfigsInBridgelessMode(): Boolean
@DoNotStrip public fun useOptimizedEventBatchingOnAndroid(): Boolean
@@ -8,10 +8,12 @@
package com.facebook.react.uimanager
import com.facebook.common.logging.FLog
import com.facebook.react.bridge.NativeArray
import com.facebook.react.bridge.ReadableArray
import com.facebook.react.bridge.ReadableMap
import com.facebook.react.bridge.ReadableType
import com.facebook.react.common.ReactConstants
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
public object TransformHelper {
@@ -69,6 +71,14 @@ public object TransformHelper {
transformOrigin: ReadableArray?,
allowPercentageResolution: Boolean
) {
if (allowPercentageResolution &&
ReactNativeFeatureFlags.useNativeTransformHelperAndroid() &&
transforms is NativeArray &&
transformOrigin is NativeArray?) {
nativeProcessTransform(transforms, result, viewWidth, viewHeight, transformOrigin)
return
}
val helperMatrix = helperMatrix.get()!!
MatrixMathHelper.resetIdentityMatrix(result)
val offsets =
@@ -220,4 +230,13 @@ public object TransformHelper {
return doubleArrayOf(newTranslateX, newTranslateY, newTranslateZ)
}
@JvmStatic
private external fun nativeProcessTransform(
transforms: NativeArray,
result: DoubleArray,
viewWidth: Float,
viewHeight: Float,
transformOrigin: NativeArray?
)
}
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<0c0ccd76192aba5c7bf22b1655f53b62>>
* @generated SignedSource<<bbad4ee8cacd33099874d0c3078ea716>>
*/
/**
@@ -357,6 +357,12 @@ class ReactNativeFeatureFlagsJavaProvider
return method(javaProvider_);
}
bool useNativeTransformHelperAndroid() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("useNativeTransformHelperAndroid");
return method(javaProvider_);
}
bool useNativeViewConfigsInBridgelessMode() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("useNativeViewConfigsInBridgelessMode");
@@ -668,6 +674,11 @@ bool JReactNativeFeatureFlagsCxxInterop::useNativeEqualsInNativeReadableArrayAnd
return ReactNativeFeatureFlags::useNativeEqualsInNativeReadableArrayAndroid();
}
bool JReactNativeFeatureFlagsCxxInterop::useNativeTransformHelperAndroid(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::useNativeTransformHelperAndroid();
}
bool JReactNativeFeatureFlagsCxxInterop::useNativeViewConfigsInBridgelessMode(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::useNativeViewConfigsInBridgelessMode();
@@ -893,6 +904,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
makeNativeMethod(
"useNativeEqualsInNativeReadableArrayAndroid",
JReactNativeFeatureFlagsCxxInterop::useNativeEqualsInNativeReadableArrayAndroid),
makeNativeMethod(
"useNativeTransformHelperAndroid",
JReactNativeFeatureFlagsCxxInterop::useNativeTransformHelperAndroid),
makeNativeMethod(
"useNativeViewConfigsInBridgelessMode",
JReactNativeFeatureFlagsCxxInterop::useNativeViewConfigsInBridgelessMode),
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<2330cbab72534dd16218a09c5d5c8bf5>>
* @generated SignedSource<<57f2dcf4b71512c6b15e8021258d6036>>
*/
/**
@@ -189,6 +189,9 @@ class JReactNativeFeatureFlagsCxxInterop
static bool useNativeEqualsInNativeReadableArrayAndroid(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool useNativeTransformHelperAndroid(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool useNativeViewConfigsInBridgelessMode(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
@@ -21,6 +21,10 @@ class NativeArray : public jni::HybridClass<NativeArray> {
jni::local_ref<jstring> toString();
const folly::dynamic& getArray() const {
return array_;
}
RN_EXPORT folly::dynamic consume();
// Whether this array has been added to another array or map and no longer
@@ -11,6 +11,7 @@
#include "JReactMarker.h"
#include "NativeArray.h"
#include "NativeMap.h"
#include "TransformHelper.h"
#include "WritableNativeArray.h"
#include "WritableNativeMap.h"
@@ -27,6 +28,7 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
ReadableNativeMap::registerNatives();
WritableNativeArray::registerNatives();
WritableNativeMap::registerNatives();
TransformHelper::registerNatives();
});
}
@@ -0,0 +1,60 @@
/*
* 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 "TransformHelper.h"
#include <react/renderer/components/view/BaseViewProps.h>
#include <react/renderer/components/view/conversions.h>
#include "NativeArray.h"
using namespace facebook::jni;
namespace facebook::react {
namespace {
void processTransform(
jni::alias_ref<jclass> /*unused*/,
NativeArray* jTransforms,
jni::alias_ref<jni::JArrayDouble> jResult,
float viewWidth,
float viewHeight,
NativeArray* jTransformOrigin) {
// Assuming parsing transforms doesn't require a real PropsParserContext
static ContextContainer contextContainer;
static PropsParserContext context(0, contextContainer);
RawValue transformValue(jTransforms->getArray());
Transform transform;
fromRawValue(context, transformValue, transform);
TransformOrigin transformOrigin;
if (jTransformOrigin != nullptr) {
RawValue transformOriginValue(jTransformOrigin->getArray());
fromRawValue(context, transformOriginValue, transformOrigin);
}
auto result = BaseViewProps::resolveTransform(
Size(viewWidth, viewHeight), transform, transformOrigin);
// Convert from matrix of floats to double matrix
constexpr size_t MatrixSize = std::tuple_size_v<decltype(result.matrix)>;
std::array<double, MatrixSize> doubleTransform{};
std::copy(
result.matrix.begin(), result.matrix.end(), doubleTransform.begin());
jResult->setRegion(0, MatrixSize, doubleTransform.data());
}
} // namespace
void TransformHelper::registerNatives() {
javaClassLocal()->registerNatives({
makeNativeMethod("nativeProcessTransform", processTransform),
});
}
} // namespace facebook::react
@@ -0,0 +1,22 @@
/*
* 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.
*/
#pragma once
#include <fbjni/fbjni.h>
namespace facebook::react {
class TransformHelper : public jni::JavaClass<TransformHelper> {
public:
static auto constexpr* kJavaDescriptor =
"Lcom/facebook/react/uimanager/TransformHelper;";
static void registerNatives();
};
} // namespace facebook::react
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<b4ea7fdf458f3933176d5769a0774949>>
* @generated SignedSource<<2cabd888b74b84201ff027457efc6007>>
*/
/**
@@ -238,6 +238,10 @@ bool ReactNativeFeatureFlags::useNativeEqualsInNativeReadableArrayAndroid() {
return getAccessor().useNativeEqualsInNativeReadableArrayAndroid();
}
bool ReactNativeFeatureFlags::useNativeTransformHelperAndroid() {
return getAccessor().useNativeTransformHelperAndroid();
}
bool ReactNativeFeatureFlags::useNativeViewConfigsInBridgelessMode() {
return getAccessor().useNativeViewConfigsInBridgelessMode();
}
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<0eff4b5f07581cdf3f200924a9a3bb42>>
* @generated SignedSource<<e26a0c35f1499abf24e46275bbcbe06d>>
*/
/**
@@ -304,6 +304,11 @@ class ReactNativeFeatureFlags {
*/
RN_EXPORT static bool useNativeEqualsInNativeReadableArrayAndroid();
/**
* Use a native implementation of TransformHelper
*/
RN_EXPORT static bool useNativeTransformHelperAndroid();
/**
* When enabled, the native view configs are used in bridgeless mode.
*/
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<2240305552c24ef91dcd4b2c87267e28>>
* @generated SignedSource<<7ae9a203a94e3a22197bc9eda69b741c>>
*/
/**
@@ -983,6 +983,24 @@ bool ReactNativeFeatureFlagsAccessor::useNativeEqualsInNativeReadableArrayAndroi
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::useNativeTransformHelperAndroid() {
auto flagValue = useNativeTransformHelperAndroid_.load();
if (!flagValue.has_value()) {
// This block is not exclusive but it is not necessary.
// If multiple threads try to initialize the feature flag, we would only
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(53, "useNativeTransformHelperAndroid");
flagValue = currentProvider_->useNativeTransformHelperAndroid();
useNativeTransformHelperAndroid_ = flagValue;
}
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::useNativeViewConfigsInBridgelessMode() {
auto flagValue = useNativeViewConfigsInBridgelessMode_.load();
@@ -992,7 +1010,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeViewConfigsInBridgelessMode() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(53, "useNativeViewConfigsInBridgelessMode");
markFlagAsAccessed(54, "useNativeViewConfigsInBridgelessMode");
flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode();
useNativeViewConfigsInBridgelessMode_ = flagValue;
@@ -1010,7 +1028,7 @@ bool ReactNativeFeatureFlagsAccessor::useOptimizedEventBatchingOnAndroid() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(54, "useOptimizedEventBatchingOnAndroid");
markFlagAsAccessed(55, "useOptimizedEventBatchingOnAndroid");
flagValue = currentProvider_->useOptimizedEventBatchingOnAndroid();
useOptimizedEventBatchingOnAndroid_ = flagValue;
@@ -1028,7 +1046,7 @@ bool ReactNativeFeatureFlagsAccessor::useRawPropsJsiValue() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(55, "useRawPropsJsiValue");
markFlagAsAccessed(56, "useRawPropsJsiValue");
flagValue = currentProvider_->useRawPropsJsiValue();
useRawPropsJsiValue_ = flagValue;
@@ -1046,7 +1064,7 @@ bool ReactNativeFeatureFlagsAccessor::useShadowNodeStateOnClone() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(56, "useShadowNodeStateOnClone");
markFlagAsAccessed(57, "useShadowNodeStateOnClone");
flagValue = currentProvider_->useShadowNodeStateOnClone();
useShadowNodeStateOnClone_ = flagValue;
@@ -1064,7 +1082,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModuleInterop() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(57, "useTurboModuleInterop");
markFlagAsAccessed(58, "useTurboModuleInterop");
flagValue = currentProvider_->useTurboModuleInterop();
useTurboModuleInterop_ = flagValue;
@@ -1082,7 +1100,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModules() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(58, "useTurboModules");
markFlagAsAccessed(59, "useTurboModules");
flagValue = currentProvider_->useTurboModules();
useTurboModules_ = flagValue;
@@ -1100,7 +1118,7 @@ double ReactNativeFeatureFlagsAccessor::virtualViewPrerenderRatio() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(59, "virtualViewPrerenderRatio");
markFlagAsAccessed(60, "virtualViewPrerenderRatio");
flagValue = currentProvider_->virtualViewPrerenderRatio();
virtualViewPrerenderRatio_ = flagValue;
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<257e707937b438617004798572dfac2a>>
* @generated SignedSource<<74560113d0b23c05d7822eeba1c0dee4>>
*/
/**
@@ -85,6 +85,7 @@ class ReactNativeFeatureFlagsAccessor {
bool useAlwaysAvailableJSErrorHandling();
bool useFabricInterop();
bool useNativeEqualsInNativeReadableArrayAndroid();
bool useNativeTransformHelperAndroid();
bool useNativeViewConfigsInBridgelessMode();
bool useOptimizedEventBatchingOnAndroid();
bool useRawPropsJsiValue();
@@ -103,7 +104,7 @@ class ReactNativeFeatureFlagsAccessor {
std::unique_ptr<ReactNativeFeatureFlagsProvider> currentProvider_;
bool wasOverridden_;
std::array<std::atomic<const char*>, 60> accessedFeatureFlags_;
std::array<std::atomic<const char*>, 61> accessedFeatureFlags_;
std::atomic<std::optional<bool>> commonTestFlag_;
std::atomic<std::optional<bool>> animatedShouldSignalBatch_;
@@ -158,6 +159,7 @@ class ReactNativeFeatureFlagsAccessor {
std::atomic<std::optional<bool>> useAlwaysAvailableJSErrorHandling_;
std::atomic<std::optional<bool>> useFabricInterop_;
std::atomic<std::optional<bool>> useNativeEqualsInNativeReadableArrayAndroid_;
std::atomic<std::optional<bool>> useNativeTransformHelperAndroid_;
std::atomic<std::optional<bool>> useNativeViewConfigsInBridgelessMode_;
std::atomic<std::optional<bool>> useOptimizedEventBatchingOnAndroid_;
std::atomic<std::optional<bool>> useRawPropsJsiValue_;
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<0fb260ec04c9d43bebfac5a5d7726bd0>>
* @generated SignedSource<<1a54000b8eb51cb91304902c7f722d45>>
*/
/**
@@ -239,6 +239,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider {
return false;
}
bool useNativeTransformHelperAndroid() override {
return false;
}
bool useNativeViewConfigsInBridgelessMode() override {
return false;
}
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<d4e73cc21058df3b15d0550fb211b64c>>
* @generated SignedSource<<244db790cc754f31402981967cd902b5>>
*/
/**
@@ -522,6 +522,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef
return ReactNativeFeatureFlagsDefaults::useNativeEqualsInNativeReadableArrayAndroid();
}
bool useNativeTransformHelperAndroid() override {
auto value = values_["useNativeTransformHelperAndroid"];
if (!value.isNull()) {
return value.getBool();
}
return ReactNativeFeatureFlagsDefaults::useNativeTransformHelperAndroid();
}
bool useNativeViewConfigsInBridgelessMode() override {
auto value = values_["useNativeViewConfigsInBridgelessMode"];
if (!value.isNull()) {
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<ccc72ace017fc6a3a09a56d7eb0a3fd8>>
* @generated SignedSource<<a045579d42e45fa80831856734a063aa>>
*/
/**
@@ -34,6 +34,10 @@ class ReactNativeFeatureFlagsOverridesOSSExperimental : public ReactNativeFeatur
bool useNativeEqualsInNativeReadableArrayAndroid() override {
return true;
}
bool useNativeTransformHelperAndroid() override {
return true;
}
};
} // namespace facebook::react
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<4b2d5467c78636794139aa5b67fa5c0c>>
* @generated SignedSource<<2387ed12fe46fb5b606ad13a17511f03>>
*/
/**
@@ -78,6 +78,7 @@ class ReactNativeFeatureFlagsProvider {
virtual bool useAlwaysAvailableJSErrorHandling() = 0;
virtual bool useFabricInterop() = 0;
virtual bool useNativeEqualsInNativeReadableArrayAndroid() = 0;
virtual bool useNativeTransformHelperAndroid() = 0;
virtual bool useNativeViewConfigsInBridgelessMode() = 0;
virtual bool useOptimizedEventBatchingOnAndroid() = 0;
virtual bool useRawPropsJsiValue() = 0;
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<d3fa8a8cd5a5bb65ec2dae61b4e9c486>>
* @generated SignedSource<<45e1b08fd2438b27af82591b5cfa5744>>
*/
/**
@@ -309,6 +309,11 @@ bool NativeReactNativeFeatureFlags::useNativeEqualsInNativeReadableArrayAndroid(
return ReactNativeFeatureFlags::useNativeEqualsInNativeReadableArrayAndroid();
}
bool NativeReactNativeFeatureFlags::useNativeTransformHelperAndroid(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::useNativeTransformHelperAndroid();
}
bool NativeReactNativeFeatureFlags::useNativeViewConfigsInBridgelessMode(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::useNativeViewConfigsInBridgelessMode();
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<439c0e9cf31e443b5678665a9cc8e789>>
* @generated SignedSource<<d1862a9ce3cfeda5d140941fcd3b25bc>>
*/
/**
@@ -142,6 +142,8 @@ class NativeReactNativeFeatureFlags
bool useNativeEqualsInNativeReadableArrayAndroid(jsi::Runtime& runtime);
bool useNativeTransformHelperAndroid(jsi::Runtime& runtime);
bool useNativeViewConfigsInBridgelessMode(jsi::Runtime& runtime);
bool useOptimizedEventBatchingOnAndroid(jsi::Runtime& runtime);
@@ -550,6 +550,13 @@ BorderMetrics BaseViewProps::resolveBorderMetrics(
Transform BaseViewProps::resolveTransform(
const LayoutMetrics& layoutMetrics) const {
const auto& frameSize = layoutMetrics.frame.size;
return resolveTransform(frameSize, transform, transformOrigin);
}
Transform BaseViewProps::resolveTransform(
const Size& frameSize,
const Transform& transform,
const TransformOrigin& transformOrigin) {
auto transformMatrix = Transform{};
if (frameSize.width == 0 && frameSize.height == 0) {
return transformMatrix;
@@ -562,8 +569,7 @@ Transform BaseViewProps::resolveTransform(
} else {
for (const auto& operation : transform.operations) {
transformMatrix = transformMatrix *
Transform::FromTransformOperation(
operation, layoutMetrics.frame.size, transform);
Transform::FromTransformOperation(operation, frameSize, transform);
}
}
@@ -115,6 +115,11 @@ class BaseViewProps : public YogaStylableProps, public AccessibilityProps {
Transform resolveTransform(const LayoutMetrics& layoutMetrics) const;
bool getClipsContentToBounds() const;
static Transform resolveTransform(
const Size& frameSize,
const Transform& transform,
const TransformOrigin& transformOrigin);
#if RN_DEBUG_STRING_CONVERTIBLE
SharedDebugStringConvertibleList getDebugProps() const override;
#endif
@@ -606,6 +606,16 @@ const definitions: FeatureFlagDefinitions = {
},
ossReleaseStage: 'experimental',
},
useNativeTransformHelperAndroid: {
defaultValue: false,
metadata: {
dateAdded: '2025-07-15',
description: 'Use a native implementation of TransformHelper',
expectedReleaseValue: true,
purpose: 'experimentation',
},
ossReleaseStage: 'experimental',
},
useNativeViewConfigsInBridgelessMode: {
defaultValue: false,
metadata: {
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<f34427cdba45c84df525d3352a7136d4>>
* @generated SignedSource<<f17b8e4e33228e19b346837e7a33a2dd>>
* @flow strict
* @noformat
*/
@@ -104,6 +104,7 @@ export type ReactNativeFeatureFlags = $ReadOnly<{
useAlwaysAvailableJSErrorHandling: Getter<boolean>,
useFabricInterop: Getter<boolean>,
useNativeEqualsInNativeReadableArrayAndroid: Getter<boolean>,
useNativeTransformHelperAndroid: Getter<boolean>,
useNativeViewConfigsInBridgelessMode: Getter<boolean>,
useOptimizedEventBatchingOnAndroid: Getter<boolean>,
useRawPropsJsiValue: Getter<boolean>,
@@ -409,6 +410,10 @@ export const useFabricInterop: Getter<boolean> = createNativeFlagGetter('useFabr
* Use a native implementation of equals in NativeReadableArray.
*/
export const useNativeEqualsInNativeReadableArrayAndroid: Getter<boolean> = createNativeFlagGetter('useNativeEqualsInNativeReadableArrayAndroid', false);
/**
* Use a native implementation of TransformHelper
*/
export const useNativeTransformHelperAndroid: Getter<boolean> = createNativeFlagGetter('useNativeTransformHelperAndroid', false);
/**
* When enabled, the native view configs are used in bridgeless mode.
*/
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<1c47c6a2547badec31526142e4fc1798>>
* @generated SignedSource<<8b4f1275a16d5b83f5594da1eb89c6c1>>
* @flow strict
* @noformat
*/
@@ -78,6 +78,7 @@ export interface Spec extends TurboModule {
+useAlwaysAvailableJSErrorHandling?: () => boolean;
+useFabricInterop?: () => boolean;
+useNativeEqualsInNativeReadableArrayAndroid?: () => boolean;
+useNativeTransformHelperAndroid?: () => boolean;
+useNativeViewConfigsInBridgelessMode?: () => boolean;
+useOptimizedEventBatchingOnAndroid?: () => boolean;
+useRawPropsJsiValue?: () => boolean;