diff --git a/java/com/facebook/yoga/YogaNodeJNIBatching.java b/java/com/facebook/yoga/YogaNodeJNIBatching.java new file mode 100644 index 00000000..7760fa80 --- /dev/null +++ b/java/com/facebook/yoga/YogaNodeJNIBatching.java @@ -0,0 +1,177 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the LICENSE + * file in the root directory of this source tree. + */ +package com.facebook.yoga; + +import javax.annotation.Nullable; + +import com.facebook.proguard.annotations.DoNotStrip; + +@DoNotStrip +public class YogaNodeJNIBatching extends YogaNodeJNIBase { + + /* Those flags needs be in sync with YGJNI.cpp */ + private static final byte MARGIN = 1; + private static final byte PADDING = 2; + private static final byte BORDER = 4; + private static final byte DOES_LEGACY_STRETCH_BEHAVIOUR = 8; + private static final byte HAS_NEW_LAYOUT = 16; + + private static final byte LAYOUT_EDGE_SET_FLAG_INDEX = 0; + private static final byte LAYOUT_WIDTH_INDEX = 1; + private static final byte LAYOUT_HEIGHT_INDEX = 2; + private static final byte LAYOUT_LEFT_INDEX = 3; + private static final byte LAYOUT_TOP_INDEX = 4; + private static final byte LAYOUT_DIRECTION_INDEX = 5; + private static final byte LAYOUT_MARGIN_START_INDEX = 6; + private static final byte LAYOUT_PADDING_START_INDEX = 10; + private static final byte LAYOUT_BORDER_START_INDEX = 14; + + @DoNotStrip + private @Nullable float[] arr = null; + + @DoNotStrip + private int mLayoutDirection = 0; + + private boolean mHasNewLayout = true; + + public YogaNodeJNIBatching() { + super(); + } + + public YogaNodeJNIBatching(YogaConfig config) { + super(config); + } + + @Override + public void reset() { + super.reset(); + arr = null; + mHasNewLayout = true; + mLayoutDirection = 0; + } + + @Override + public float getLayoutX() { + return arr != null ? arr[LAYOUT_LEFT_INDEX] : 0; + } + + @Override + public float getLayoutY() { + return arr != null ? arr[LAYOUT_TOP_INDEX] : 0; + } + + @Override + public float getLayoutWidth() { + return arr != null ? arr[LAYOUT_WIDTH_INDEX] : 0; + } + + @Override + public float getLayoutHeight() { + return arr != null ? arr[LAYOUT_HEIGHT_INDEX] : 0; + } + + @Override + public boolean getDoesLegacyStretchFlagAffectsLayout() { + return arr != null && (((int) arr[LAYOUT_EDGE_SET_FLAG_INDEX] & DOES_LEGACY_STRETCH_BEHAVIOUR) == DOES_LEGACY_STRETCH_BEHAVIOUR); + } + + @Override + public float getLayoutMargin(YogaEdge edge) { + if (arr != null && ((int) arr[LAYOUT_EDGE_SET_FLAG_INDEX] & MARGIN) == MARGIN) { + switch (edge) { + case LEFT: + return arr[LAYOUT_MARGIN_START_INDEX]; + case TOP: + return arr[LAYOUT_MARGIN_START_INDEX + 1]; + case RIGHT: + return arr[LAYOUT_MARGIN_START_INDEX + 2]; + case BOTTOM: + return arr[LAYOUT_MARGIN_START_INDEX + 3]; + case START: + return getLayoutDirection() == YogaDirection.RTL ? arr[LAYOUT_MARGIN_START_INDEX + 2] : arr[LAYOUT_MARGIN_START_INDEX]; + case END: + return getLayoutDirection() == YogaDirection.RTL ? arr[LAYOUT_MARGIN_START_INDEX] : arr[LAYOUT_MARGIN_START_INDEX + 2]; + default: + throw new IllegalArgumentException("Cannot get layout margins of multi-edge shorthands"); + } + } else { + return 0; + } + } + + @Override + public float getLayoutPadding(YogaEdge edge) { + if (arr != null && ((int) arr[LAYOUT_EDGE_SET_FLAG_INDEX] & PADDING) == PADDING) { + int paddingStartIndex = LAYOUT_PADDING_START_INDEX - ((((int)arr[LAYOUT_EDGE_SET_FLAG_INDEX] & MARGIN) == MARGIN) ? 0 : 4); + switch (edge) { + case LEFT: + return arr[paddingStartIndex]; + case TOP: + return arr[paddingStartIndex + 1]; + case RIGHT: + return arr[paddingStartIndex + 2]; + case BOTTOM: + return arr[paddingStartIndex + 3]; + case START: + return getLayoutDirection() == YogaDirection.RTL ? arr[paddingStartIndex + 2] : arr[paddingStartIndex]; + case END: + return getLayoutDirection() == YogaDirection.RTL ? arr[paddingStartIndex] : arr[paddingStartIndex + 2]; + default: + throw new IllegalArgumentException("Cannot get layout paddings of multi-edge shorthands"); + } + } else { + return 0; + } + } + + @Override + public float getLayoutBorder(YogaEdge edge) { + if (arr != null && ((int) arr[LAYOUT_EDGE_SET_FLAG_INDEX] & BORDER) == BORDER) { + int borderStartIndex = LAYOUT_BORDER_START_INDEX - ((((int) arr[LAYOUT_EDGE_SET_FLAG_INDEX] & MARGIN) == MARGIN) ? 0 : 4) - ((((int) arr[LAYOUT_EDGE_SET_FLAG_INDEX] & PADDING) == PADDING) ? 0 : 4); + switch (edge) { + case LEFT: + return arr[borderStartIndex]; + case TOP: + return arr[borderStartIndex + 1]; + case RIGHT: + return arr[borderStartIndex + 2]; + case BOTTOM: + return arr[borderStartIndex + 3]; + case START: + return getLayoutDirection() == YogaDirection.RTL ? arr[borderStartIndex + 2] : arr[borderStartIndex]; + case END: + return getLayoutDirection() == YogaDirection.RTL ? arr[borderStartIndex] : arr[borderStartIndex + 2]; + default: + throw new IllegalArgumentException("Cannot get layout border of multi-edge shorthands"); + } + } else { + return 0; + } + } + + @Override + public YogaDirection getLayoutDirection() { + return YogaDirection.fromInt(arr != null ? (int) arr[LAYOUT_DIRECTION_INDEX] : mLayoutDirection); + } + + @Override + public boolean hasNewLayout() { + if (arr != null) { + return (((int) arr[LAYOUT_EDGE_SET_FLAG_INDEX]) & HAS_NEW_LAYOUT) == HAS_NEW_LAYOUT; + } else { + return mHasNewLayout; + } + } + + @Override + public void markLayoutSeen() { + if (arr != null) { + arr[LAYOUT_EDGE_SET_FLAG_INDEX] = ((int) arr[LAYOUT_EDGE_SET_FLAG_INDEX]) & ~(HAS_NEW_LAYOUT); + } + mHasNewLayout = false; + } +} diff --git a/java/jni/YGJNI.cpp b/java/jni/YGJNI.cpp index 7f703c89..fa4e897d 100644 --- a/java/jni/YGJNI.cpp +++ b/java/jni/YGJNI.cpp @@ -62,6 +62,16 @@ enum YGStyleInput { IsReferenceBaseline, }; +const short int LAYOUT_EDGE_SET_FLAG_INDEX = 0; +const short int LAYOUT_WIDTH_INDEX = 1; +const short int LAYOUT_HEIGHT_INDEX = 2; +const short int LAYOUT_LEFT_INDEX = 3; +const short int LAYOUT_TOP_INDEX = 4; +const short int LAYOUT_DIRECTION_INDEX = 5; +const short int LAYOUT_MARGIN_START_INDEX = 6; +const short int LAYOUT_PADDING_START_INDEX = 10; +const short int LAYOUT_BORDER_START_INDEX = 14; + class PtrJNodeMap { using JNodeArray = JArrayClass; std::map ptrsToIdxs_; @@ -97,6 +107,9 @@ union YGNodeContext { void* asVoidPtr; }; +const int DOES_LEGACY_STRETCH_BEHAVIOUR = 8; +const int HAS_NEW_LAYOUT = 16; + class YGNodeEdges { uintptr_t edges_; @@ -127,6 +140,10 @@ public: edges_ |= edge; return *this; } + + int get() { + return edges_; + } }; struct YogaValue { @@ -177,80 +194,140 @@ static void YGTransferLayoutOutputsRecursive( auto edgesSet = YGNodeEdges{root}; - static auto widthField = obj->getClass()->getField("mWidth"); - static auto heightField = obj->getClass()->getField("mHeight"); - static auto leftField = obj->getClass()->getField("mLeft"); - static auto topField = obj->getClass()->getField("mTop"); + if (false) { + bool marginFieldSet = edgesSet.has(YGNodeEdges::MARGIN); + bool paddingFieldSet = edgesSet.has(YGNodeEdges::PADDING); + bool borderFieldSet = edgesSet.has(YGNodeEdges::BORDER); - static auto marginLeftField = - obj->getClass()->getField("mMarginLeft"); - static auto marginTopField = obj->getClass()->getField("mMarginTop"); - static auto marginRightField = - obj->getClass()->getField("mMarginRight"); - static auto marginBottomField = - obj->getClass()->getField("mMarginBottom"); + int fieldFlags = edgesSet.get(); + fieldFlags |= HAS_NEW_LAYOUT; + if (YGNodeLayoutGetDidLegacyStretchFlagAffectLayout(root)) { + fieldFlags |= DOES_LEGACY_STRETCH_BEHAVIOUR; + } - static auto paddingLeftField = - obj->getClass()->getField("mPaddingLeft"); - static auto paddingTopField = - obj->getClass()->getField("mPaddingTop"); - static auto paddingRightField = - obj->getClass()->getField("mPaddingRight"); - static auto paddingBottomField = - obj->getClass()->getField("mPaddingBottom"); + const int arrSize = 6 + (marginFieldSet ? 4 : 0) + + (paddingFieldSet ? 4 : 0) + (borderFieldSet ? 4 : 0); + float arr[18]; + arr[LAYOUT_EDGE_SET_FLAG_INDEX] = fieldFlags; + arr[LAYOUT_WIDTH_INDEX] = YGNodeLayoutGetWidth(root); + arr[LAYOUT_HEIGHT_INDEX] = YGNodeLayoutGetHeight(root); + arr[LAYOUT_LEFT_INDEX] = YGNodeLayoutGetLeft(root); + arr[LAYOUT_TOP_INDEX] = YGNodeLayoutGetTop(root); + arr[LAYOUT_DIRECTION_INDEX] = + static_cast(YGNodeLayoutGetDirection(root)); + if (marginFieldSet) { + arr[LAYOUT_MARGIN_START_INDEX] = YGNodeLayoutGetMargin(root, YGEdgeLeft); + arr[LAYOUT_MARGIN_START_INDEX + 1] = + YGNodeLayoutGetMargin(root, YGEdgeTop); + arr[LAYOUT_MARGIN_START_INDEX + 2] = + YGNodeLayoutGetMargin(root, YGEdgeRight); + arr[LAYOUT_MARGIN_START_INDEX + 3] = + YGNodeLayoutGetMargin(root, YGEdgeBottom); + } + if (paddingFieldSet) { + int paddingStartIndex = + LAYOUT_PADDING_START_INDEX - (marginFieldSet ? 0 : 4); + arr[paddingStartIndex] = YGNodeLayoutGetPadding(root, YGEdgeLeft); + arr[paddingStartIndex + 1] = YGNodeLayoutGetPadding(root, YGEdgeTop); + arr[paddingStartIndex + 2] = YGNodeLayoutGetPadding(root, YGEdgeRight); + arr[paddingStartIndex + 3] = YGNodeLayoutGetPadding(root, YGEdgeBottom); + } - static auto borderLeftField = - obj->getClass()->getField("mBorderLeft"); - static auto borderTopField = obj->getClass()->getField("mBorderTop"); - static auto borderRightField = - obj->getClass()->getField("mBorderRight"); - static auto borderBottomField = - obj->getClass()->getField("mBorderBottom"); + if (borderFieldSet) { + int borderStartIndex = LAYOUT_BORDER_START_INDEX - + (marginFieldSet ? 0 : 4) - (paddingFieldSet ? 0 : 4); + arr[borderStartIndex] = YGNodeLayoutGetBorder(root, YGEdgeLeft); + arr[borderStartIndex + 1] = YGNodeLayoutGetBorder(root, YGEdgeTop); + arr[borderStartIndex + 2] = YGNodeLayoutGetBorder(root, YGEdgeRight); + arr[borderStartIndex + 3] = YGNodeLayoutGetBorder(root, YGEdgeBottom); + } - static auto hasNewLayoutField = - obj->getClass()->getField("mHasNewLayout"); - static auto doesLegacyStretchBehaviour = obj->getClass()->getField( - "mDoesLegacyStretchFlagAffectsLayout"); + static auto arrField = obj->getClass()->getField("arr"); + local_ref arrFinal = make_float_array(arrSize); + arrFinal->setRegion(0, arrSize, arr); + obj->setFieldValue(arrField, arrFinal.get()); - obj->setFieldValue(widthField, YGNodeLayoutGetWidth(root)); - obj->setFieldValue(heightField, YGNodeLayoutGetHeight(root)); - obj->setFieldValue(leftField, YGNodeLayoutGetLeft(root)); - obj->setFieldValue(topField, YGNodeLayoutGetTop(root)); - obj->setFieldValue( - doesLegacyStretchBehaviour, - YGNodeLayoutGetDidLegacyStretchFlagAffectLayout(root)); - obj->setFieldValue(hasNewLayoutField, true); - YGTransferLayoutDirection(root, obj); + } else { + static auto widthField = obj->getClass()->getField("mWidth"); + static auto heightField = obj->getClass()->getField("mHeight"); + static auto leftField = obj->getClass()->getField("mLeft"); + static auto topField = obj->getClass()->getField("mTop"); - if (edgesSet.has(YGNodeEdges::MARGIN)) { - obj->setFieldValue( - marginLeftField, YGNodeLayoutGetMargin(root, YGEdgeLeft)); - obj->setFieldValue(marginTopField, YGNodeLayoutGetMargin(root, YGEdgeTop)); - obj->setFieldValue( - marginRightField, YGNodeLayoutGetMargin(root, YGEdgeRight)); - obj->setFieldValue( - marginBottomField, YGNodeLayoutGetMargin(root, YGEdgeBottom)); - } + static auto marginLeftField = + obj->getClass()->getField("mMarginLeft"); + static auto marginTopField = + obj->getClass()->getField("mMarginTop"); + static auto marginRightField = + obj->getClass()->getField("mMarginRight"); + static auto marginBottomField = + obj->getClass()->getField("mMarginBottom"); - if (edgesSet.has(YGNodeEdges::PADDING)) { - obj->setFieldValue( - paddingLeftField, YGNodeLayoutGetPadding(root, YGEdgeLeft)); - obj->setFieldValue( - paddingTopField, YGNodeLayoutGetPadding(root, YGEdgeTop)); - obj->setFieldValue( - paddingRightField, YGNodeLayoutGetPadding(root, YGEdgeRight)); - obj->setFieldValue( - paddingBottomField, YGNodeLayoutGetPadding(root, YGEdgeBottom)); - } + static auto paddingLeftField = + obj->getClass()->getField("mPaddingLeft"); + static auto paddingTopField = + obj->getClass()->getField("mPaddingTop"); + static auto paddingRightField = + obj->getClass()->getField("mPaddingRight"); + static auto paddingBottomField = + obj->getClass()->getField("mPaddingBottom"); - if (edgesSet.has(YGNodeEdges::BORDER)) { - obj->setFieldValue( - borderLeftField, YGNodeLayoutGetBorder(root, YGEdgeLeft)); - obj->setFieldValue(borderTopField, YGNodeLayoutGetBorder(root, YGEdgeTop)); - obj->setFieldValue( - borderRightField, YGNodeLayoutGetBorder(root, YGEdgeRight)); - obj->setFieldValue( - borderBottomField, YGNodeLayoutGetBorder(root, YGEdgeBottom)); + static auto borderLeftField = + obj->getClass()->getField("mBorderLeft"); + static auto borderTopField = + obj->getClass()->getField("mBorderTop"); + static auto borderRightField = + obj->getClass()->getField("mBorderRight"); + static auto borderBottomField = + obj->getClass()->getField("mBorderBottom"); + + static auto hasNewLayoutField = + obj->getClass()->getField("mHasNewLayout"); + static auto doesLegacyStretchBehaviour = + obj->getClass()->getField( + "mDoesLegacyStretchFlagAffectsLayout"); + + obj->setFieldValue(widthField, YGNodeLayoutGetWidth(root)); + obj->setFieldValue(heightField, YGNodeLayoutGetHeight(root)); + obj->setFieldValue(leftField, YGNodeLayoutGetLeft(root)); + obj->setFieldValue(topField, YGNodeLayoutGetTop(root)); + obj->setFieldValue( + doesLegacyStretchBehaviour, + YGNodeLayoutGetDidLegacyStretchFlagAffectLayout(root)); + obj->setFieldValue(hasNewLayoutField, true); + YGTransferLayoutDirection(root, obj); + + if (edgesSet.has(YGNodeEdges::MARGIN)) { + obj->setFieldValue( + marginLeftField, YGNodeLayoutGetMargin(root, YGEdgeLeft)); + obj->setFieldValue( + marginTopField, YGNodeLayoutGetMargin(root, YGEdgeTop)); + obj->setFieldValue( + marginRightField, YGNodeLayoutGetMargin(root, YGEdgeRight)); + obj->setFieldValue( + marginBottomField, YGNodeLayoutGetMargin(root, YGEdgeBottom)); + } + + if (edgesSet.has(YGNodeEdges::PADDING)) { + obj->setFieldValue( + paddingLeftField, YGNodeLayoutGetPadding(root, YGEdgeLeft)); + obj->setFieldValue( + paddingTopField, YGNodeLayoutGetPadding(root, YGEdgeTop)); + obj->setFieldValue( + paddingRightField, YGNodeLayoutGetPadding(root, YGEdgeRight)); + obj->setFieldValue( + paddingBottomField, YGNodeLayoutGetPadding(root, YGEdgeBottom)); + } + + if (edgesSet.has(YGNodeEdges::BORDER)) { + obj->setFieldValue( + borderLeftField, YGNodeLayoutGetBorder(root, YGEdgeLeft)); + obj->setFieldValue( + borderTopField, YGNodeLayoutGetBorder(root, YGEdgeTop)); + obj->setFieldValue( + borderRightField, YGNodeLayoutGetBorder(root, YGEdgeRight)); + obj->setFieldValue( + borderBottomField, YGNodeLayoutGetBorder(root, YGEdgeBottom)); + } } root->setHasNewLayout(false);