-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ASCollectionView] Improve performance and behavior of rotation / bounds changes. #431
Conversation
…nds changes. See #430 for details.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a great improvement for flow layouts! Pinterest's collection view layout relies on calculatedSize
rather than layoutThatFits:
and so we will need to modify that code before landing this patch.
@nguyenhuy Would you be willing to spec out that change? I can get to it in a couple of days but I've got a lot of non-Texture work on my plate this week.
@Adlai-Holler @nguyenhuy thanks for looking! The most important question here: what caused this to start crashing? It could be something happening in my app, but it also might be one of the other refactors that has happened across the collection stack. Perhaps there is a different way to fix the crash that would not create an issue for any layout implementations using -calculatedSize. |
@appleguy To answer your question, looking at the crash log, I believe there were some blocks scheduled on the main queue before the relayout, and one of them is the completion block of a change set update ( |
Source/ASCollectionView.mm
Outdated
// Because -invalidateLayout doesn't trigger any operations by itself, and we answer queries from UICollectionView using layoutThatFits:, | ||
// we invalidate the layout before we have updated all of the cells. Any cells that the collection needs the size of immediately will get | ||
// -layoutThatFits: with a new constraint, on the main thread, and synchronously calculate them. Meanwhile, relayoutAllNodes will update | ||
// the layout of any remaining nodes on background threads (and fast-return for any nodes that the UICV got to first). | ||
[self.collectionViewLayout invalidateLayout]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think invalidating the collection layout should be done "in the future", inside -[ASDataController _relayoutAllNodes]
here. The reason is that there might be new layouts calculated before the size change, but still stuck on the main serial queue and not yet applied. And those layouts are invalid. But calling invalidating here is too soon and doesn't invalidate them.
So it looks like we need to change -[ASDataController relayotuAllNodes]
to accept a block that get called on the main serial queue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nguyenhuy Interesting; that sounds about right. This seems like a very important issue to fix. Do you have any ideas on how we would test it properly?
I'm a bit worried because I don't have time to dig into this right now. In fact, I haven't synced with upstream for almost 2 months, and I have to focus on some deadlines for Q4 and 2018 planning.
If you would be able to take over this diff, I'd certainly appreciate it. I bet this change would greatly help the Pinterest app, especially on iPad rotation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@appleguy Yeah, I can take over this diff, but not anytime soon though I'm afraid.
Source/ASCollectionView.mm
Outdated
// Because -invalidateLayout doesn't trigger any operations by itself, and we answer queries from UICollectionView using layoutThatFits:, | ||
// we invalidate the layout before we have updated all of the cells. Any cells that the collection needs the size of immediately will get | ||
// -layoutThatFits: with a new constraint, on the main thread, and synchronously calculate them. Meanwhile, relayoutAllNodes will update | ||
// the layout of any remaining nodes on background threads (and fast-return for any nodes that the UICV got to first). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Meanwhile, relayoutAllNodes will update the layout of any remaining nodes on background threads (and fast-return for any nodes that the UICV got to first).
I'm actually not aware of this behavior in the current codebase. The current implementation of -relayoutAllNodes
processes all nodes in the UIKit index space on the main thread. That being said, I think this is the right approach that we should pursue. Are you planning to implement it shortly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Heh, you're right. I suppose it is necessary for it to be synchronous in the current codebase without some kind of virtualization support (exposing only a subset of the datasource to UIKit).
@Adlai-Holler Sure, I'll prepare a change in Pinterest once this PR is merged. |
🚫 CI failed with log |
… to flush the ASMainSerialQueue before -invalidateLayout is called.
Hmm, this erroneous test failure is striking again. ASCollectionViewTests
|
🚫 CI failed with log |
🚫 CI failed with log |
Good catch by @djblake, if you scroll fast enough, you leave images set on the image node because didExitPreloadRange (which would have cleared it) was already called.
* Updating to support animated WebP * Fix a deadlock with display link * Fix playhead issue. * Fix up timing on iOS 10 and above * Don't redraw the same frame over and over * Clear out layer contents if we're an animated GIF on exit range * Clear out cover image on exit of visible range * Don't set cover image if we're no longer in display range. * Don't clear out image if we're not an animated image * Only set image if we're not already animating * Get rid of changes to podfile * Add CHANGELOG entry * Update license * Update PINRemoteImage * Remove commented out lines in example
@nguyenhuy we need to either land this one now, and then I can fix merge conflicts for #616 — or we can land that one first and I'll fix this one. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, and I really appreciate the detailed comments explaining your rationale. Let's go with this 💯
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like the right approach to me. Sorry for the late review!
🚫 CI failed with log |
CI passed. |
…nds changes. (TextureGroup#431) * [ASCollectionView] Improve performance and behavior of rotation / bounds changes. See TextureGroup#430 for details. * Edit CHANGELOG.md * [ASDataController] Implement -relayoutAllNodesWithInvalidationBlock:, to flush the ASMainSerialQueue before -invalidateLayout is called. * Don't set download results if no longer in preload range. (TextureGroup#606) Good catch by @djblake, if you scroll fast enough, you leave images set on the image node because didExitPreloadRange (which would have cleared it) was already called. * Animated WebP support (TextureGroup#605) * Updating to support animated WebP * Fix a deadlock with display link * Fix playhead issue. * Fix up timing on iOS 10 and above * Don't redraw the same frame over and over * Clear out layer contents if we're an animated GIF on exit range * Clear out cover image on exit of visible range * Don't set cover image if we're no longer in display range. * Don't clear out image if we're not an animated image * Only set image if we're not already animating * Get rid of changes to podfile * Add CHANGELOG entry * Update license * Update PINRemoteImage * Remove commented out lines in example * [ASDataController] Add nullable specifier to invalidationBlock for relayout of nodes.
See #430 for details.
Besides the unit tests, which do pass, is there any dedicated ASCollectionView thrash tester I should run? I see the ASTableViewStressTest in examples_extra/.