Skip to content

Commit

Permalink
Introduce ReductionState to move towards the goal of an immutable `…
Browse files Browse the repository at this point in the history
…LayoutState`

Summary: As per title, this diff is to make `LayoutState` to be immutable step by step.

Reviewed By: adityasharat, luluwu2032

Differential Revision: D52657076

fbshipit-source-id: bf53720ecaa1f40285be44c815f85b86d9f4cc8c
  • Loading branch information
Andrew Wang authored and facebook-github-bot committed Jan 22, 2024
1 parent 2a4826d commit f6bfa4c
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 57 deletions.
40 changes: 22 additions & 18 deletions litho-core/src/main/java/com/facebook/litho/Layout.kt
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,9 @@ internal object Layout {
@JvmStatic
fun measurePendingSubtrees(
parentContext: ComponentContext,
lithoLayoutContext: LithoLayoutContext,
reductionState: ReductionState,
result: LithoLayoutResult,
layoutState: LayoutState,
lithoLayoutContext: LithoLayoutContext
) {
if (lithoLayoutContext.isFutureReleased || result.measureHadExceptions) {
// Exit early if the layout future as been released or if this result had exceptions.
Expand Down Expand Up @@ -154,61 +154,65 @@ internal object Layout {
}

Resolver.collectOutputs(nestedTree.node)?.let { outputs ->
layoutState.mAttachables
reductionState.attachables
.getOrCreate {
ArrayList<Attachable>(outputs.attachables.size).also { layoutState.mAttachables = it }
ArrayList<Attachable>(outputs.attachables.size).also {
reductionState.attachables = it
}
}
.addAll(outputs.attachables)

layoutState.mTransitions
reductionState.transitions
.getOrCreate {
ArrayList<Transition>(outputs.transitions.size).also { layoutState.mTransitions = it }
ArrayList<Transition>(outputs.transitions.size).also {
reductionState.transitions = it
}
}
.addAll(outputs.transitions)

layoutState.mScopedComponentInfosNeedingPreviousRenderData
reductionState.scopedComponentInfosNeedingPreviousRenderData
.getOrCreate {
ArrayList<ScopedComponentInfo>(outputs.componentsThatNeedPreviousRenderData.size)
.also { layoutState.mScopedComponentInfosNeedingPreviousRenderData = it }
.also { reductionState.scopedComponentInfosNeedingPreviousRenderData = it }
}
.addAll(outputs.componentsThatNeedPreviousRenderData)
}

measurePendingSubtrees(
parentContext = parentContext,
result = nestedTree,
layoutState = layoutState,
lithoLayoutContext = lithoLayoutContext)
lithoLayoutContext = lithoLayoutContext,
reductionState = reductionState,
result = nestedTree)
return
} else if (result.childrenCount > 0) {
val context: ComponentContext = result.node.tailComponentContext
for (i in 0 until result.childCount) {
val child: LithoLayoutResult = result.getChildAt(i)
measurePendingSubtrees(
parentContext = context,
result = child,
layoutState = layoutState,
lithoLayoutContext = lithoLayoutContext)
lithoLayoutContext = lithoLayoutContext,
reductionState = reductionState,
result = child)
}
}

LithoYogaLayoutFunction.onBoundsDefined(result)

registerWorkingRange(layoutState, result)
registerWorkingRange(reductionState, result)
}

/** Register working range for each node */
@JvmStatic
fun registerWorkingRange(layoutState: LayoutState, result: LithoLayoutResult) {
fun registerWorkingRange(reductionState: ReductionState, result: LithoLayoutResult) {
val registrations: List<WorkingRangeContainer.Registration> =
result.node.workingRangeRegistrations ?: return
if (CollectionsUtils.isEmpty(registrations)) {
return
}

val workingRange: WorkingRangeContainer =
layoutState.mWorkingRangeContainer.getOrCreate {
WorkingRangeContainer().also { layoutState.mWorkingRangeContainer = it }
reductionState.workingRangeContainer.getOrCreate {
WorkingRangeContainer().also { reductionState.workingRangeContainer = it }
}
val component: Component = result.node.tailComponent
for (registration in registrations) {
Expand Down
29 changes: 13 additions & 16 deletions litho-core/src/main/java/com/facebook/litho/LayoutState.java
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,6 @@ public static boolean isFromSyncLayout(@RenderSource int source) {
@Nullable LithoNode mRoot;
@Nullable LayoutResult mLayoutResult;
@Nullable TransitionId mRootTransitionId;
@Nullable LayoutCache.CachedData mLayoutCacheData;

@Nullable DiffNode mDiffTreeRoot;

int mWidth;
Expand All @@ -139,18 +137,18 @@ public static boolean isFromSyncLayout(@RenderSource int source) {

private final boolean mIsAccessibilityEnabled;

@Nullable List<ScopedComponentInfo> mScopedComponentInfosNeedingPreviousRenderData;
@Nullable TransitionId mCurrentTransitionId;
@Nullable OutputUnitsAffinityGroup<AnimatableItem> mCurrentLayoutOutputAffinityGroup;
final Map<TransitionId, OutputUnitsAffinityGroup<AnimatableItem>> mTransitionIdMapping =
new LinkedHashMap<>();
final Set<TransitionId> mDuplicatedTransitionIds = new HashSet<>();
@Nullable List<Transition> mTransitions;
private @Nullable RenderTree mCachedRenderTree = null;

@Nullable WorkingRangeContainer mWorkingRangeContainer;

@Nullable List<Attachable> mAttachables;
private final @Nullable List<Attachable> mAttachables;
private final @Nullable List<Transition> mTransitions;
private final @Nullable List<ScopedComponentInfo> mScopedComponentInfosNeedingPreviousRenderData;
private final @Nullable WorkingRangeContainer mWorkingRangeContainer;
final @Nullable LayoutCache.CachedData mLayoutCacheData;

// If there is any component marked with 'ExcludeFromIncrementalMountComponent'
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
Expand All @@ -169,26 +167,25 @@ public static boolean isFromSyncLayout(@RenderSource int source) {
int rootY,
int componentTreeId,
boolean isAccessibilityEnabled,
@Nullable LayoutState current) {
@Nullable LayoutState current,
@Nullable LayoutCache.CachedData layoutCacheData,
ReductionState reductionState) {
mId = sIdGenerator.getAndIncrement();
mResolveResult = resolveResult;
mSizeConstraints = sizeConstraints;
mRootX = rootX;
mRootY = rootY;
mPreviousLayoutStateId = current != null ? current.mId : NO_PREVIOUS_LAYOUT_STATE_ID;
mLayoutCacheData = current != null ? current.mLayoutCacheData : null;
mLayoutCacheData = layoutCacheData;
mTestOutputs = ComponentsConfiguration.isEndToEndTestRun ? new ArrayList<TestOutput>(8) : null;
mScopedSpecComponentInfos = new ArrayList<>();
mVisibilityOutputs = new ArrayList<>(8);

mAttachables =
resolveResult.outputs != null ? new ArrayList<>(resolveResult.outputs.attachables) : null;
mTransitions =
resolveResult.outputs != null ? new ArrayList<>(resolveResult.outputs.transitions) : null;
mAttachables = reductionState.getAttachables();
mTransitions = reductionState.getTransitions();
mScopedComponentInfosNeedingPreviousRenderData =
resolveResult.outputs != null
? new ArrayList<>(resolveResult.outputs.componentsThatNeedPreviousRenderData)
: null;
reductionState.getScopedComponentInfosNeedingPreviousRenderData();
mWorkingRangeContainer = reductionState.getWorkingRangeContainer();
mComponentTreeId = componentTreeId;
mRootTransitionId = LithoNodeUtils.createTransitionId(resolveResult.node);
mIsAccessibilityEnabled = isAccessibilityEnabled;
Expand Down
57 changes: 34 additions & 23 deletions litho-core/src/main/java/com/facebook/litho/LayoutTreeFuture.kt
Original file line number Diff line number Diff line change
Expand Up @@ -153,23 +153,34 @@ class LayoutTreeFuture(
c.setLithoLayoutContext(lsc)
val root = measureTree(lsc, c.androidContext, node, sizeConstraints, perfEvent)

layoutState =
LayoutState(
resolveResult,
sizeConstraints,
lsc.rootOffset.x,
lsc.rootOffset.y,
treeId,
lsc.isAccessibilityEnabled,
currentLayoutState,
)

val reductionState =
ReductionState(
attachables = resolveResult.outputs?.let { ArrayList(it.attachables) },
transitions = resolveResult.outputs?.let { ArrayList(it.transitions) },
scopedComponentInfosNeedingPreviousRenderData =
resolveResult.outputs?.let {
ArrayList(it.componentsThatNeedPreviousRenderData)
})
if (root != null) {
measurePendingSubtrees(c, root, layoutState, lsc)
measurePendingSubtrees(
parentContext = c,
lithoLayoutContext = lsc,
reductionState = reductionState,
result = root)
}

layoutState.mLayoutResult = root
layoutState.mLayoutCacheData = layoutCache.writeCacheData
layoutState =
LayoutState(
resolveResult,
sizeConstraints,
lsc.rootOffset.x,
lsc.rootOffset.y,
treeId,
lsc.isAccessibilityEnabled,
currentLayoutState,
layoutCache.writeCacheData,
reductionState)
.apply { mLayoutResult = root }

perfEvent?.markerPoint("start_collect_results")
setSizeAfterMeasureAndCollectResults(c, lsc, layoutState)
Expand All @@ -179,22 +190,22 @@ class LayoutTreeFuture(

layoutState.setCreatedEventHandlers(
mergeLists(resolveResult.eventHandlers, lsc.eventHandlers))

return layoutState
} finally {
c.calculationStateContext = prevContext
lsc.release()
}

LithoStats.incrementComponentCalculateLayoutCount()
LithoStats.incrementComponentCalculateLayoutCount()

if (ThreadUtils.isMainThread) {
LithoStats.incrementComponentCalculateLayoutOnUICount()
}
if (ThreadUtils.isMainThread) {
LithoStats.incrementComponentCalculateLayoutOnUICount()
}

if (DebugOverlay.isEnabled) {
updateLayoutHistory(treeId)
if (DebugOverlay.isEnabled) {
updateLayoutHistory(treeId)
}
}

return layoutState
} finally {

treeState.unregisterLayoutInitialState()
Expand Down
32 changes: 32 additions & 0 deletions litho-core/src/main/java/com/facebook/litho/ReductionState.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.facebook.litho

import com.facebook.kotlin.compilerplugins.dataclassgenerate.annotation.DataClassGenerate
import com.facebook.kotlin.compilerplugins.dataclassgenerate.annotation.Mode

/**
* A data structure that holds all the information needed to perform a reduction pass, which is
* going to be transferred to [LayoutState] in the end.
*/
@DataClassGenerate(toString = Mode.OMIT, equalsHashCode = Mode.KEEP)
data class ReductionState(
var attachables: MutableList<Attachable>? = null,
var transitions: MutableList<Transition>? = null,
var scopedComponentInfosNeedingPreviousRenderData: MutableList<ScopedComponentInfo>? = null,
var workingRangeContainer: WorkingRangeContainer? = null,
)

0 comments on commit f6bfa4c

Please sign in to comment.