Skip to content

Commit

Permalink
Reduce viewport updates in LazyList for improved perf with Zipline
Browse files Browse the repository at this point in the history
  • Loading branch information
veyndan committed Sep 11, 2023
1 parent 1eec9f9 commit 95f3864
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,24 +45,24 @@ internal fun LazyList(
content: LazyListScope.() -> Unit,
) {
val itemProvider = rememberLazyListItemProvider(content)
var lastVisibleItemIndex by remember { mutableStateOf(0) }
var lastPagedItemIndex by remember { mutableStateOf(0) }
val itemsBefore = remember(state.firstVisibleItemIndex) { (state.firstVisibleItemIndex - OffscreenItemsBufferCount / 2).coerceAtLeast(0) }
val itemsAfter = remember(lastVisibleItemIndex, itemProvider.itemCount) { (itemProvider.itemCount - (lastVisibleItemIndex + OffscreenItemsBufferCount / 2).coerceAtMost(itemProvider.itemCount)).coerceAtLeast(0) }
val itemsAfter = remember(lastPagedItemIndex, itemProvider.itemCount) { (itemProvider.itemCount - (lastPagedItemIndex + OffscreenItemsBufferCount / 2).coerceAtMost(itemProvider.itemCount)).coerceAtLeast(0) }
val scrollItemIndex = remember(state.scrollToItemTriggeredId) { ScrollItemIndex(state.scrollToItemTriggeredId, state.firstVisibleItemIndex) }
var placeholderPoolSize by remember { mutableStateOf(20) }
LazyList(
isVertical,
itemsBefore = itemsBefore,
itemsAfter = itemsAfter,
onViewportChanged = { localFirstVisibleItemIndex, localLastVisibleItemIndex ->
val visibleItemCount = localLastVisibleItemIndex - localFirstVisibleItemIndex
onViewportChanged = { localFirstPagedItemIndex, localLastPagedItemIndex ->
val visibleItemCount = localLastPagedItemIndex - localFirstPagedItemIndex
val proposedPlaceholderPoolSize = visibleItemCount + visibleItemCount / 2
// We only ever want to increase the pool size.
if (placeholderPoolSize < proposedPlaceholderPoolSize) {
placeholderPoolSize = proposedPlaceholderPoolSize
}
state.firstVisibleItemIndex = localFirstVisibleItemIndex
lastVisibleItemIndex = localLastVisibleItemIndex
state.firstVisibleItemIndex = localFirstPagedItemIndex
lastPagedItemIndex = localLastPagedItemIndex
},
width = width,
height = height,
Expand Down Expand Up @@ -97,24 +97,24 @@ internal fun RefreshableLazyList(
content: LazyListScope.() -> Unit,
) {
val itemProvider = rememberLazyListItemProvider(content)
var lastVisibleItemIndex by remember { mutableStateOf(0) }
var lastPagedItemIndex by remember { mutableStateOf(0) }
val itemsBefore = remember(state.firstVisibleItemIndex) { (state.firstVisibleItemIndex - OffscreenItemsBufferCount / 2).coerceAtLeast(0) }
val itemsAfter = remember(lastVisibleItemIndex, itemProvider.itemCount) { (itemProvider.itemCount - (lastVisibleItemIndex + OffscreenItemsBufferCount / 2).coerceAtMost(itemProvider.itemCount)).coerceAtLeast(0) }
val itemsAfter = remember(lastPagedItemIndex, itemProvider.itemCount) { (itemProvider.itemCount - (lastPagedItemIndex + OffscreenItemsBufferCount / 2).coerceAtMost(itemProvider.itemCount)).coerceAtLeast(0) }
val scrollItemIndex = remember(state.scrollToItemTriggeredId) { ScrollItemIndex(state.scrollToItemTriggeredId, state.firstVisibleItemIndex) }
var placeholderPoolSize by remember { mutableStateOf(20) }
RefreshableLazyList(
isVertical,
itemsBefore = itemsBefore,
itemsAfter = itemsAfter,
onViewportChanged = { localFirstVisibleItemIndex, localLastVisibleItemIndex ->
val visibleItemCount = localLastVisibleItemIndex - localFirstVisibleItemIndex
onViewportChanged = { localFirstPagedItemIndex, localLastPagedItemIndex ->
val visibleItemCount = localLastPagedItemIndex - localFirstPagedItemIndex
val proposedPlaceholderPoolSize = visibleItemCount + visibleItemCount / 2
// We only ever want to increase the pool size.
if (placeholderPoolSize < proposedPlaceholderPoolSize) {
placeholderPoolSize = proposedPlaceholderPoolSize
}
state.firstVisibleItemIndex = localFirstVisibleItemIndex
lastVisibleItemIndex = localLastVisibleItemIndex
state.firstVisibleItemIndex = localFirstPagedItemIndex
lastPagedItemIndex = localLastPagedItemIndex
},
refreshing = refreshing,
onRefresh = onRefresh,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ internal class ComposeUiLazyList :
LazyList<@Composable () -> Unit>,
RefreshableLazyList<@Composable () -> Unit> {
private var isVertical by mutableStateOf(false)
private var onViewportChanged: ((firstVisibleItemIndex: Int, lastVisibleItemIndex: Int) -> Unit)? by mutableStateOf(null)
private var onViewportChanged: ((firstPagedItemIndex: Int, lastPagedItemIndex: Int) -> Unit)? by mutableStateOf(null)
private var itemsBefore by mutableStateOf(0)
private var itemsAfter by mutableStateOf(0)
private var isRefreshing by mutableStateOf(false)
Expand All @@ -78,7 +78,7 @@ internal class ComposeUiLazyList :
this.isVertical = isVertical
}

override fun onViewportChanged(onViewportChanged: (firstVisibleItemIndex: Int, lastVisibleItemIndex: Int) -> Unit) {
override fun onViewportChanged(onViewportChanged: (firstPagedItemIndex: Int, lastPagedItemIndex: Int) -> Unit) {
this.onViewportChanged = onViewportChanged
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ internal open class HTMLLazyList(document: Document) : LazyList<HTMLElement> {
value.style.flexDirection = if (isVertical) "column" else "row"
}

override fun onViewportChanged(onViewportChanged: (firstVisibleItemIndex: Int, lastVisibleItemIndex: Int) -> Unit) {
override fun onViewportChanged(onViewportChanged: (firstPagedItemIndex: Int, lastPagedItemIndex: Int) -> Unit) {
}

override fun itemsBefore(itemsBefore: Int) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import app.cash.redwood.ui.Margin
@Widget(1)
public data class LazyList(
@Property(1) val isVertical: Boolean,
@Property(2) val onViewportChanged: (firstVisibleItemIndex: Int, lastVisibleItemIndex: Int) -> Unit,
@Property(2) val onViewportChanged: (firstPagedItemIndex: Int, lastPagedItemIndex: Int) -> Unit,
@Property(3) val itemsBefore: Int,
@Property(4) val itemsAfter: Int,
@Property(5) val width: Constraint,
Expand All @@ -41,7 +41,7 @@ public data class LazyList(
@Widget(2)
public data class RefreshableLazyList(
@Property(1) val isVertical: Boolean,
@Property(2) val onViewportChanged: (firstVisibleItemIndex: Int, lastVisibleItemIndex: Int) -> Unit,
@Property(2) val onViewportChanged: (firstPagedItemIndex: Int, lastPagedItemIndex: Int) -> Unit,
@Property(3) val itemsBefore: Int,
@Property(4) val itemsAfter: Int,
@Property(5) val refreshing: Boolean,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ public abstract class WindowedLazyList<W : Any>(
private val listUpdateCallback: ListUpdateCallback,
) : LazyList<W> {

private var firstVisibleItemIndex = 0
private var lastVisibleItemIndex = 0
private var onViewportChanged: ((firstVisibleItemIndex: Int, lastVisibleItemIndex: Int) -> Unit)? = null
private var firstPagedItemIndex = 0
private var lastPagedItemIndex = 0
private var onViewportChanged: ((firstPagedItemIndex: Int, lastPagedItemIndex: Int) -> Unit)? = null

final override val items: WindowedChildren<W> = WindowedChildren(listUpdateCallback)

Expand All @@ -30,10 +30,16 @@ public abstract class WindowedLazyList<W : Any>(
}

protected fun updateViewport(firstVisibleItemIndex: Int, lastVisibleItemIndex: Int) {
if (firstVisibleItemIndex != this.firstVisibleItemIndex || lastVisibleItemIndex != this.lastVisibleItemIndex) {
this.firstVisibleItemIndex = firstVisibleItemIndex
this.lastVisibleItemIndex = lastVisibleItemIndex
onViewportChanged?.invoke(firstVisibleItemIndex, lastVisibleItemIndex)
// Paginate the results so `onViewportChanged` isn't blasted with a bunch of updates. This is
// particularly important when using `LazyList` in Treehouse (compared to plain Redwood), as
// the serialization cost between the host-guest bridge can be expensive.
val viewportItemCount = lastVisibleItemIndex - firstVisibleItemIndex
val firstPagedItemIndex = (((firstVisibleItemIndex / viewportItemCount) * viewportItemCount) - viewportItemCount).coerceAtLeast(0)
val lastPagedItemIndex = ((lastVisibleItemIndex / viewportItemCount) * viewportItemCount) + viewportItemCount
if (firstPagedItemIndex != this.firstPagedItemIndex || lastPagedItemIndex != this.lastPagedItemIndex) {
this.firstPagedItemIndex = firstPagedItemIndex
this.lastPagedItemIndex = lastPagedItemIndex
onViewportChanged?.invoke(firstPagedItemIndex, lastPagedItemIndex)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,26 @@ private fun Item(
.margin(Margin(8.dp)),
onClick = onClick,
)
Text(text = emojiImage.label)
Column {
Image(
url = emojiImage.url,
modifier = Modifier
.margin(Margin(8.dp)),
onClick = onClick,
)
Text(text = emojiImage.label)
Text(text = emojiImage.label)
Text(text = emojiImage.label)
Text(text = emojiImage.label)
Text(text = emojiImage.label)
Text(text = emojiImage.label)
Text(text = emojiImage.label)
Text(text = emojiImage.label)
Text(text = emojiImage.label)
Text(text = emojiImage.label)
Text(text = emojiImage.label)
Text(text = emojiImage.label)
}
}
}

Expand Down

0 comments on commit 95f3864

Please sign in to comment.