Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,6 @@ open class SkikoComposeUiTest @InternalTestApi constructor(
return !Snapshot.current.hasPendingChanges()
&& !Snapshot.isApplyObserverNotificationPending
&& !scene.hasPendingMeasureOrLayout
&& !scene.hasPendingSnapshotCommands
&& areAllResourcesIdle()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ import org.junit.Assert.assertFalse
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.rules.Timeout

@OptIn(InternalTestApi::class, ExperimentalComposeUiApi::class)
class ComposeSceneTest {
Expand All @@ -117,6 +118,9 @@ class ComposeSceneTest {
@get:Rule
val composeRule = createComposeRule()

@get:Rule // A timeout inside @Test annotation does not always work
val timeout: Timeout = Timeout.seconds(60)

private fun ScreenshotTestRule.snap(surface: Surface, idSuffix: String? = null) {
assertImageAgainstGolden(surface.makeImageSnapshot(), idSuffix)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ internal fun OffsetToFocusedRect(
// Intentionally update state within composition to trigger second measure and
// layout because focus rect may be miscalculated due to simultaneous offset and
// window insets changes.
//
// FIXME: this "second measure" only settles in-frame because BaseComposeScene.draw()
// currently calls Snapshot.sendApplyNotifications() between the measure and draw phases -
// a temporary, un-Android workaround kept solely for this code path.
currentOffset = startOffset + (endOffset - startOffset) * offsetProgress

val placeables = measurables.fastMap { it.measure(constraints) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ import androidx.compose.ui.util.trace
import androidx.compose.ui.viewinterop.InteropPointerInputModifier
import androidx.compose.ui.viewinterop.InteropView
import androidx.compose.ui.viewinterop.pointerInteropFilter
import kotlin.concurrent.Volatile
import kotlin.coroutines.CoroutineContext
import kotlin.math.max
import kotlin.math.min
Expand All @@ -122,14 +123,15 @@ internal class RootNodeOwner(
size: IntSize?,
coroutineContext: CoroutineContext,
val platformContext: PlatformContext,
private val snapshotInvalidationTracker: SnapshotInvalidationTracker,
private val inputHandler: ComposeSceneInputHandler,
private val invalidate: () -> Unit,
onChangedExecutor: (callback: () -> Unit) -> Unit,
) {
val focusOwner: FocusOwner get() = _owner.focusOwner
val dragAndDropOwner = DragAndDropOwner(platformContext.dragAndDropManager)

private val rootSemanticsNode = EmptySemanticsModifier()
private val snapshotObserver = snapshotInvalidationTracker.snapshotObserver()
private val snapshotObserver = OwnerSnapshotObserver(onChangedExecutor)
private val graphicsContext = SkiaGraphicsContext(platformContext.measureDrawLayerBounds)
private val coroutineScope =
CoroutineScope(coroutineContext + Job(parent = coroutineContext[Job]))
Expand All @@ -155,6 +157,14 @@ internal class RootNodeOwner(
owner.root.layoutDirection = value
}

@Volatile
var hasPendingMeasureOrLayout: Boolean = true
private set

@Volatile
var hasPendingDraw: Boolean = true
private set

private val rootForTest by lazy(LazyThreadSafetyMode.NONE) {
PlatformRootForTestImpl()
}
Expand Down Expand Up @@ -233,6 +243,7 @@ internal class RootNodeOwner(

fun measureAndLayout() {
require(!isDisposed) { "RootNodeOwner is already disposed" }
hasPendingMeasureOrLayout = false
owner.measureAndLayout(sendPointerUpdate = true)
updatePositionCacheAndDispatch()
}
Expand Down Expand Up @@ -293,20 +304,31 @@ internal class RootNodeOwner(
fun draw(canvas: Canvas) {
require(!isDisposed) { "RootNodeOwner is already disposed" }
trace("RootNodeOwner:draw") {
hasPendingDraw = false
ownedLayerManager.draw(canvas)
clearInvalidObservations()
owner.rectManager.dispatchCallbacks()
}
}

private fun requestMeasureAndLayout() {
hasPendingMeasureOrLayout = true
invalidate()
}

private fun requestDraw() {
hasPendingDraw = true
invalidate()
}

fun setRootModifier(modifier: Modifier) {
owner.root.modifier = _owner.rootModifier then modifier
}

private fun onRootSizeChanged(size: IntSize?) {
measureAndLayoutDelegate.updateRootConstraints(size.toMaxConstraints())
if (measureAndLayoutDelegate.hasPendingMeasureOrLayout) {
snapshotInvalidationTracker.requestMeasureAndLayout()
requestMeasureAndLayout()
}
}

Expand Down Expand Up @@ -573,7 +595,7 @@ internal class RootNodeOwner(
val resend = if (sendPointerUpdate) onPointerUpdateCallback else null
val rootNodeResized = measureAndLayoutDelegate.measureAndLayout(resend)
if (rootNodeResized) {
snapshotInvalidationTracker.requestDraw()
requestDraw()
}
measureAndLayoutDelegate.dispatchOnPositionedCallbacks()
rectManager.dispatchCallbacks()
Expand Down Expand Up @@ -609,12 +631,12 @@ internal class RootNodeOwner(
if (measureAndLayoutDelegate.requestLookaheadRemeasure(layoutNode, forceRequest) &&
scheduleMeasureAndLayout
) {
snapshotInvalidationTracker.requestMeasureAndLayout()
requestMeasureAndLayout()
}
} else if (measureAndLayoutDelegate.requestRemeasure(layoutNode, forceRequest) &&
scheduleMeasureAndLayout
) {
snapshotInvalidationTracker.requestMeasureAndLayout()
requestMeasureAndLayout()
}
}

Expand All @@ -625,18 +647,18 @@ internal class RootNodeOwner(
) {
if (affectsLookahead) {
if (measureAndLayoutDelegate.requestLookaheadRelayout(layoutNode, forceRequest)) {
snapshotInvalidationTracker.requestMeasureAndLayout()
requestMeasureAndLayout()
}
} else {
if (measureAndLayoutDelegate.requestRelayout(layoutNode, forceRequest)) {
snapshotInvalidationTracker.requestMeasureAndLayout()
requestMeasureAndLayout()
}
}
}

override fun requestOnPositionedCallback(layoutNode: LayoutNode) {
measureAndLayoutDelegate.requestOnPositionedCallback(layoutNode)
snapshotInvalidationTracker.requestMeasureAndLayout()
requestMeasureAndLayout()
}

override fun createLayer(
Expand Down Expand Up @@ -703,13 +725,6 @@ internal class RootNodeOwner(
private val endApplyChangesListeners = mutableVectorOf<(() -> Unit)?>()

override fun onEndApplyChanges() {
// Android's OwnerSnapshotObserver runs callbacks immediately when apply changes
// happens on the view handler thread. Non-Android queues off-thread owner callbacks in
// the scene-local tracker, so drain them here before clearing invalid observations and
// invoking end-apply listeners.
// This preserves the previous render-time synchronous observer ordering
// after recomposition moved to FrameRecomposer.
snapshotInvalidationTracker.performSnapshotChanges()
clearInvalidObservations()

// Listeners can add more items to the list and we want to ensure that they
Expand All @@ -736,7 +751,7 @@ internal class RootNodeOwner(

override fun registerOnLayoutCompletedListener(listener: Owner.OnLayoutCompletedListener) {
measureAndLayoutDelegate.registerOnLayoutCompletedListener(listener)
snapshotInvalidationTracker.requestMeasureAndLayout()
requestMeasureAndLayout()
}

override fun voteFrameRate(frameRate: Float) {
Expand Down Expand Up @@ -942,7 +957,7 @@ internal class RootNodeOwner(
}

override fun invalidate() {
snapshotInvalidationTracker.requestDraw()
requestDraw()
}

private var currentFrameRate = Float.NaN
Expand Down

This file was deleted.

Loading
Loading