Skip to content
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

Merged
merged 9 commits into from
Oct 24, 2017

Conversation

appleguy
Copy link
Member

@appleguy appleguy commented Jul 9, 2017

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/.

@appleguy appleguy self-assigned this Jul 9, 2017
Copy link
Member

@Adlai-Holler Adlai-Holler left a 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.

@appleguy
Copy link
Member Author

@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.

@nguyenhuy
Copy link
Member

nguyenhuy commented Jul 13, 2017

@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 (-dataController:didUpdateWithChangeSet:updates:). And since -[ASMainSerialQueue performBlockOnMainThread:] flushes all scheduled blocks at once, the completion block might be executed at an unexpected time.

// 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];
Copy link
Member

@nguyenhuy nguyenhuy Jul 13, 2017

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.

Copy link
Member Author

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.

Copy link
Member

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.

// 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).
Copy link
Member

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?

Copy link
Member Author

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).

@nguyenhuy
Copy link
Member

@Adlai-Holler Sure, I'll prepare a change in Pinterest once this PR is merged.

@nguyenhuy nguyenhuy self-assigned this Sep 18, 2017
@ghost
Copy link

ghost commented Oct 8, 2017

🚫 CI failed with log

… to flush the ASMainSerialQueue before -invalidateLayout is called.
@appleguy
Copy link
Member Author

appleguy commented Oct 8, 2017

Hmm, this erroneous test failure is striking again.

ASCollectionViewTests
testThatMultipleBatchFetchesDontHappenUnnecessarily, failed - Too many batch fetches!
/Users/Shared/buildkite/builds/iosf-garrett-VMrmmvuqKsO4.dyn.pinadmin.com-1/pinterest/texture/Tests/ASCollectionViewTests.mm:894

  if (batchFetchCount > 1) {
    XCTFail(@"Too many batch fetches!");

@ghost
Copy link

ghost commented Oct 8, 2017

🚫 CI failed with log

@ghost
Copy link

ghost commented Oct 9, 2017

🚫 CI failed with log

garrettmoon and others added 3 commits October 9, 2017 23:10
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
@appleguy
Copy link
Member Author

@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.

Copy link
Member

@Adlai-Holler Adlai-Holler left a 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 💯

Copy link
Member

@nguyenhuy nguyenhuy left a 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!

@ghost
Copy link

ghost commented Oct 24, 2017

🚫 CI failed with log

@nguyenhuy nguyenhuy merged commit 68f3468 into master Oct 24, 2017
@nguyenhuy
Copy link
Member

CI passed.

@Adlai-Holler Adlai-Holler deleted the BoundsInvalidate branch November 20, 2017 21:29
bernieperez pushed a commit to AtomTickets/Texture that referenced this pull request Apr 25, 2018
…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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants