Skip to content

Commit

Permalink
Move measure function out of LithoLayoutResult
Browse files Browse the repository at this point in the history
Summary: as per title

Reviewed By: adityasharat

Differential Revision: D52841032

fbshipit-source-id: 72cd67b6688a87b3abbe07d952ffd88b939a15a9
  • Loading branch information
Andrew Wang authored and facebook-github-bot committed Jan 19, 2024
1 parent 8ed1857 commit 68c8368
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 146 deletions.
88 changes: 2 additions & 86 deletions litho-core/src/main/java/com/facebook/litho/LithoLayoutResult.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ import com.facebook.litho.drawable.BorderColorDrawable
import com.facebook.rendercore.FastMath
import com.facebook.rendercore.LayoutContext
import com.facebook.rendercore.LayoutResult
import com.facebook.rendercore.MeasureResult
import com.facebook.rendercore.utils.hasEquivalentFields
import com.facebook.yoga.YogaConstants
import com.facebook.yoga.YogaDirection
import com.facebook.yoga.YogaEdge
Expand Down Expand Up @@ -90,7 +88,7 @@ open class LithoLayoutResult(
private set

var delegate: LayoutResult? = null
private set
internal set

var diffNode: DiffNode? = null

Expand Down Expand Up @@ -231,88 +229,6 @@ open class LithoLayoutResult(
}
}

internal open fun measureInternal(
context: LayoutContext<LithoLayoutContext>,
widthSpec: Int,
heightSpec: Int
): MeasureResult {
val isTracing: Boolean = ComponentsSystrace.isTracing
val node: LithoNode = node
val component: Component = node.tailComponent
val componentScopedContext: ComponentContext = node.tailComponentContext
val diffNode: DiffNode? = if (cachedMeasuresValid) diffNode else null
val width: Int
val height: Int
val delegate: LayoutResult?
val layoutData: Any?

// If diff node is set check if measurements from the previous pass can be reused
if (diffNode?.lastWidthSpec == widthSpec &&
diffNode.lastHeightSpec == heightSpec &&
!shouldAlwaysRemeasure(component)) {
width = diffNode.lastMeasuredWidth
height = diffNode.lastMeasuredHeight
layoutData = diffNode.layoutData
delegate = diffNode.delegate

// Measure the component
} else {
if (isTracing) {
ComponentsSystrace.beginSection("onMeasure:${component.simpleName}")
}
try {
val primitive = node.primitive
val newLayoutData: Any?
// measure Primitive
if (primitive != null) {
context.setPreviousLayoutDataForCurrentNode(this.layoutData)
context.layoutContextExtraData = LithoLayoutContextExtraData(yogaNode)
@Suppress("UNCHECKED_CAST")
delegate =
primitive.calculateLayout(context as LayoutContext<Any?>, widthSpec, heightSpec)
width = delegate.width
height = delegate.height
newLayoutData = delegate.layoutData
} else {
val size = Size(Int.MIN_VALUE, Int.MIN_VALUE)
// If the Layout Result was cached, but the size specs changed, then layout data
// will be mutated. To avoid that create new (layout data) interstage props container
// for mount specs to avoid mutating the currently mount layout data.
newLayoutData = (component as SpecGeneratedComponent).createInterStagePropsContainer()
component.onMeasure(
componentScopedContext,
SpecGeneratedComponentLayout(
yogaNode = yogaNode,
paddingSet = node.isPaddingSet,
background = node.background,
),
widthSpec,
heightSpec,
size,
newLayoutData)
delegate = null
width = size.width
height = size.height
}

// If layout data has changed then content render unit should be recreated
if (!hasEquivalentFields(this.layoutData, newLayoutData)) {
layoutData = newLayoutData
contentRenderUnit = null
} else {
layoutData = this.layoutData
}
} finally {
if (isTracing) {
ComponentsSystrace.endSection()
}
}
}
this.delegate = delegate
this.layoutData = layoutData
return MeasureResult(width, height, layoutData)
}

fun setSizeSpec(widthSpec: Int, heightSpec: Int) {
this.widthSpec = widthSpec
this.heightSpec = heightSpec
Expand Down Expand Up @@ -436,7 +352,7 @@ open class LithoLayoutResult(
fun LithoLayoutResult.getLayoutBorder(edge: YogaEdge?): Int =
FastMath.round(yogaNode.getLayoutBorder(edge))

private fun shouldAlwaysRemeasure(component: Component): Boolean =
internal fun shouldAlwaysRemeasure(component: Component): Boolean =
if (component is SpecGeneratedComponent) {
component.shouldAlwaysRemeasure()
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,13 @@ object LithoYogaLayoutFunction {
.flush()
}
try {
size = layoutResult.measureInternal(context, widthSpec, heightSpec)
size =
if (layoutResult is NestedTreeHolderResult) {
measureNestedTreeHolder(context, widthSpec, heightSpec, layoutResult)
} else {
measureLithoNode(context, widthSpec, heightSpec, layoutResult)
}

check(!(size.width < 0 || size.height < 0)) {
("MeasureOutput not set, Component is: $component " +
"WidthSpec: ${MeasureSpecUtils.getMeasureSpecDescription(widthSpec)} " +
Expand Down Expand Up @@ -579,6 +585,143 @@ object LithoYogaLayoutFunction {
layoutResult.diffNode)
}
}

private fun measureLithoNode(
context: LayoutContext<LithoLayoutContext>,
widthSpec: Int,
heightSpec: Int,
lithoLayoutResult: LithoLayoutResult
): MeasureResult {
val isTracing: Boolean = ComponentsSystrace.isTracing
val node: LithoNode = lithoLayoutResult.node
val component: Component = node.tailComponent
val componentScopedContext: ComponentContext = node.tailComponentContext
val diffNode: DiffNode? =
if (lithoLayoutResult.cachedMeasuresValid) lithoLayoutResult.diffNode else null
val width: Int
val height: Int
val delegate: LayoutResult?
val layoutData: Any?

// If diff node is set check if measurements from the previous pass can be reused
if (diffNode?.lastWidthSpec == widthSpec &&
diffNode.lastHeightSpec == heightSpec &&
!LithoLayoutResult.shouldAlwaysRemeasure(component)) {
width = diffNode.lastMeasuredWidth
height = diffNode.lastMeasuredHeight
layoutData = diffNode.layoutData
delegate = diffNode.delegate

// Measure the component
} else {
if (isTracing) {
ComponentsSystrace.beginSection("onMeasure:${component.simpleName}")
}
try {
val primitive = node.primitive
val newLayoutData: Any?
// measure Primitive
if (primitive != null) {
context.setPreviousLayoutDataForCurrentNode(lithoLayoutResult.layoutData)
context.layoutContextExtraData = LithoLayoutContextExtraData(lithoLayoutResult.yogaNode)
@Suppress("UNCHECKED_CAST")
delegate =
primitive.calculateLayout(context as LayoutContext<Any?>, widthSpec, heightSpec)
width = delegate.width
height = delegate.height
newLayoutData = delegate.layoutData
} else {
val size = Size(Int.MIN_VALUE, Int.MIN_VALUE)
// If the Layout Result was cached, but the size specs changed, then layout data
// will be mutated. To avoid that create new (layout data) interstage props container
// for mount specs to avoid mutating the currently mount layout data.
newLayoutData = (component as SpecGeneratedComponent).createInterStagePropsContainer()
component.onMeasure(
componentScopedContext,
SpecGeneratedComponentLayout(
yogaNode = lithoLayoutResult.yogaNode,
paddingSet = node.isPaddingSet,
background = node.background,
),
widthSpec,
heightSpec,
size,
newLayoutData)
delegate = null
width = size.width
height = size.height
}

// If layout data has changed then content render unit should be recreated
if (!hasEquivalentFields(lithoLayoutResult.layoutData, newLayoutData)) {
layoutData = newLayoutData
lithoLayoutResult.contentRenderUnit = null
} else {
layoutData = lithoLayoutResult.layoutData
}
} finally {
if (isTracing) {
ComponentsSystrace.endSection()
}
}
}
lithoLayoutResult.delegate = delegate
lithoLayoutResult.layoutData = layoutData
return MeasureResult(width, height, layoutData)
}

private fun measureNestedTreeHolder(
context: LayoutContext<LithoLayoutContext>,
widthSpec: Int,
heightSpec: Int,
lithoLayoutResult: NestedTreeHolderResult
): MeasureResult {
val isTracing = ComponentsSystrace.isTracing
val component = lithoLayoutResult.node.tailComponent
val renderContext = checkNotNull(context.renderContext)

check(!renderContext.isReleased) {
(component.simpleName +
": To measure a component outside of a layout calculation use" +
" Component#measureMightNotCacheInternalNode.")
}

val count = lithoLayoutResult.node.componentCount
val parentContext: ComponentContext? =
if (count == 1) {
val parentFromNode = lithoLayoutResult.node.parentContext
parentFromNode ?: renderContext.rootComponentContext
} else {
lithoLayoutResult.node.getComponentContextAt(1)
}

checkNotNull(parentContext) { component.simpleName + ": Null component context during measure" }

if (isTracing) {
ComponentsSystrace.beginSection("resolveNestedTree:" + component.simpleName)
}

return try {
val nestedTree =
Layout.measure(
renderContext,
parentContext,
lithoLayoutResult,
widthSpec,
heightSpec,
)

if (nestedTree != null) {
MeasureResult(nestedTree.width, nestedTree.height, nestedTree.layoutData)
} else {
MeasureResult(0, 0)
}
} finally {
if (isTracing) {
ComponentsSystrace.endSection()
}
}
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,6 @@

package com.facebook.litho

import com.facebook.litho.ComponentsSystrace.beginSection
import com.facebook.litho.ComponentsSystrace.endSection
import com.facebook.litho.ComponentsSystrace.isTracing
import com.facebook.litho.Layout.measure
import com.facebook.rendercore.LayoutContext
import com.facebook.rendercore.MeasureResult
import com.facebook.yoga.YogaNode

/** This is an output only [NestedTreeHolderResult]; this is created by a [NestedTreeHolder]. */
Expand Down Expand Up @@ -54,59 +48,6 @@ class NestedTreeHolderResult(
return nestedResult?.yogaNode?.layoutY?.toInt() ?: 0
}

override fun measureInternal(
context: LayoutContext<LithoLayoutContext>,
widthSpec: Int,
heightSpec: Int
): MeasureResult {

val isTracing = isTracing
val component = node.tailComponent
val renderContext = checkNotNull(context.renderContext)

check(!renderContext.isReleased) {
(component.simpleName +
": To measure a component outside of a layout calculation use" +
" Component#measureMightNotCacheInternalNode.")
}

val count = node.componentCount
val parentContext: ComponentContext? =
if (count == 1) {
val parentFromNode = node.parentContext
parentFromNode ?: renderContext.rootComponentContext
} else {
node.getComponentContextAt(1)
}

checkNotNull(parentContext) { component.simpleName + ": Null component context during measure" }

if (isTracing) {
beginSection("resolveNestedTree:" + component.simpleName)
}

return try {
val nestedTree =
measure(
renderContext,
parentContext,
this,
widthSpec,
heightSpec,
)

if (nestedTree != null) {
MeasureResult(nestedTree.width, nestedTree.height, nestedTree.layoutData)
} else {
MeasureResult(0, 0)
}
} finally {
if (isTracing) {
endSection()
}
}
}

override fun releaseLayoutPhaseData() {
super.releaseLayoutPhaseData()
nestedResult?.releaseLayoutPhaseData()
Expand Down

0 comments on commit 68c8368

Please sign in to comment.