jeran
08/11/2022, 4:13 PMfmasa
08/11/2022, 4:57 PMjeran
08/11/2022, 5:00 PMMichael Paus
08/11/2022, 6:10 PMhfhbd
08/11/2022, 6:25 PMfmasa
08/11/2022, 6:26 PMMichael Paus
08/11/2022, 6:28 PMfmasa
08/11/2022, 6:32 PMOliver.O
08/11/2022, 8:49 PM<table>
or <div>
with CSS grid) and animate cells via CSS? This will quickly convince you how slow browser-based layout and rendering can be despite being around for decades. While I haven't checked the current state of affairs, Compose is highly optimized and with Skia running on WebAssembly I wouldn't be surprised if it is capable of significantly outperforming DOM/CSS, depending on the scenario.langara
08/18/2022, 7:03 AMOliver.O
08/18/2022, 10:04 AMfmasa
08/18/2022, 10:16 AMOliver.O
08/18/2022, 10:38 AMMichael Paus
08/18/2022, 11:24 AMlangara
08/18/2022, 12:11 PMfmasa
08/18/2022, 12:13 PMOliver.O
08/21/2022, 10:36 PMlangara
09/01/2022, 8:13 AMkey
should do it:
key(Configuration.animationsEnabled) {
Grid(grid, topLevelRecompositionTrigger, rowLevelRecompositionTrigger, cellLevelRecompositionTrigger)
}
But I checked it and its still slow. Still my understanding is that key
should make compose throw away old grid and build a new one. Maybe to make it really efficient it's necessary to make every cell always take the same amount of space in slot table regardless configuration. Then key
or if (showGrid)..
condition could be removed.David Herman
09/01/2022, 6:49 PMfmasa
09/01/2022, 7:08 PMDavid Herman
09/01/2022, 7:09 PMhfhbd
09/01/2022, 7:11 PM@Page
and static routing 🙂David Herman
09/01/2022, 7:13 PMOliver.O
09/07/2022, 11:12 AMStill my understanding is thatThe changing key would indicate that recomposition ofshould make compose throw away old grid and build a new one.key
Grid
is necessary. That, however, doesn't mean that the entire Composables tree below Grid
will be rebuilt as Compose can skip recomposition selectively:
https://www.jetpackcompose.app/articles/donut-hole-skipping-in-jetpack-composelangara
09/07/2022, 2:25 PMkey
change is not only for triggering recompositions. It changes identity of the subtree, so (if I'm not mistaken) it should make the old subtree leave the composition and the new one enter the composition. As in Figure 1 here:
https://developer.android.com/jetpack/compose/lifecycle#lifecycle-overviewOliver.O
09/07/2022, 3:40 PMGrid
wouldn't make Compose throw away the old `Grid`'s content and rebuilt everything anew. I have even tried to use a separate counter as a grid ID, incrementing on each animation setting change, but that's still slower than not showing the grid for a period of time. I can only guess, but Compose seems to infer that while there is now a new Grid
(identified by a new key), its content has not actually changed (at least initially), so it just "moves" all of its Composables to the "new" Grid
. Could someone from the Compose team enlighten us?Oleksandr Karpovich [JB]
09/08/2022, 1:33 PMkey
usually makes sense when the items of some collection could move within its container. The thing is when Compose runs recomposition and when it sees key(value)
it doesn’t know yet if this key (the content represented by this key) should be thrown away or it could’ve moved somewhere within a container (it can throw it away only after it completes the recomposition of that particular container).
I used this snippet in your project:
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
Button(onClick = { selectedGrid.value = null }) {
Text("Back")
}
Button(onClick = { startOrStop() }) {
Text(if (running) "Stop" else "Start")
}
Button(onClick = { grid.clear() }) {
Text("Clear")
}
Button(onClick = { k.value += 1 }) {
Text("Inc key=${k.value}")
}
Button(onClick = { configurationVisible = !configurationVisible }) {
Text(if (configurationVisible) "Hide Configuration" else "Show Configuration")
}
}
Info(grid, startMoment, configurationVisible)
key(Configuration.animationsEnabled.value) {
Grid(grid, topLevelRecompositionTrigger, rowLevelRecompositionTrigger, cellLevelRecompositionTrigger)
}
}
Tested with this:
@Composable
private fun CellText(cellContent: Int) {
if (cellContent != 0) {
Text("$cellContent", style = MaterialTheme.typography.h6)
}
DisposableEffect(Unit) { // or even better to add DisposableEffect in Cell
println("Cell created")
onDispose {
println("Cell disposed")
}
}
}
And when Configuration.animationsEnabled.value changes, every cell gets disposed and created again (so no smart move of content from the old Grid to a new one).
if (showGrid) { Grid() }
is better because Compose knows for sure, that there is no need to temporary keep the old content (the content with the old key).
As for the need for `if (showGrid) { Grid }`… My understanding is probably not detailed enough and could lack some accuracy:
It’s related to SlotTable write operations. Compose writes into SlotTable much faster when there is no need to change the position (to move the Gap here and there) - when it can write continuously one part after another. This happens when we create a new group and add all the new content into it (e.g. when showGrid changes from false to true, it adds everything related to Grid).
But when the write operations happen at many different positions, it requires moving the Gap back and forth. It happens when the content of many cells changes at one moment.
I think someone in #compose could explain the operations in SlotTable much better 🙂Oliver.O
09/08/2022, 2:38 PMkey
identifies a Composable whenever positional identification is insufficient. If my observations were correct, the interesting thing is: It is not just that if (showGrid) { Grid() }
helps, but also that the duration of hiding the grid matters. For larger grid sizes, a longer period seems beneficial.
If it's just the slot table, I'd expect no change in results between if (showGrid)
and key(...)
if in both cases the old grid and its components are thrown away completely before creating new slot table entries for the new grid and its components. Or did I miss something?Oleksandr Karpovich [JB]
09/08/2022, 4:20 PMif(showGrid) {}
and key(…)
with if: 1) create initial Grid , 2) showGrid changes to false -> initial Grid gets disposed 3) showGrid changes to true -> new Grid gets created and composed
with key: 1) create initial Grid, 2) key value changes -> new Grid gets created and composed 3) initial Grid gets disposed
The order of create/dispose is different. This still doesn’t really explain why key
performs worser than if(showGrid)
, but at least now we know there is a difference, which could mean different states of SlotTable (needs digging into it).
but also that the duration of hiding the grid matters. For larger grid sizes, a longer period seems beneficial.it appears that insertion of
AnimatedContent
makes the difference.
It’s slow not only when we set true to Enabled animations
state on a GridScene, but also when we enable the animations beforehand on GridChoiceScene and then open GridScene.
Turning the animations off (while on GridScene) goes faster than turning it on.
If we remove the AnimatedContent, it makes changes to “enabled animations” state equally fast/slow (either turn on or turn off):
if (Configuration.animationsEnabled.value) {
@OptIn(ExperimentalAnimationApi::class)
CellText(cell.content)
// AnimatedContent(cell.content, transitionSpec = {
// slideInVertically { height -> height } with
// slideOutVertically { height -> -height }
// }) {
// CellText(it)
// }
} else {
CellText(cell.content)
}
But still, if(showGrid)
is necessary for big grids, but no need to have larger delay for bigger grids.Oliver.O
09/08/2022, 4:50 PMjeran
09/08/2022, 4:51 PMOliver.O
09/13/2022, 10:51 AM