-
-
Notifications
You must be signed in to change notification settings - Fork 64
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
[Bug]: ListView performance & a11y #500
Comments
Hey @nekohayo & @bugaevc, whenever you get some free time, could you give me a hand on this? There's info on sysprofing the status creation widgets on #410. All my attempts at solving either have led to nowhere. Sysprofing the status widget construction would point me to non-expensive functions - that even after removing wouldn't solve it. As for listviews, splitting the construction of items to setup -> bind -> unbind -> teardown led to, surprisingly, even worse performance. I'm pretty sure a big portion of the listview performance issues are caused by the other issue since it creates and destroys widgets constantly. As for keyboard accessibility, I have no idea, everything should be focusable. Nothing really changed in that area between listbox -> listview. Either way, the results are disappointing and I might have to revert back to listboxes for now :/ |
So I'm not the only one seeing performance issues and it's being worked on? That's great! I don't think I have the necessary skills to make sense of sysprof (it was only today that I attempted doing that last, still to no avail), but I'll see what I can do. Creating widgets in bind() certainly sounds wrong; the whole point is that you don't do that. |
They've been around for a long time unfortunately... On 0.4.1 / the current release that uses ListBoxes, it's very noticeable when loading posts: Screencast.from.2023-09-14.23-15-44.webm(notice the scrollbar update its position after a delay)
I know Jeff (nekohayo) is skilled at it so I'll wait for his findings! If you notice anything that sticks out on Widgets.Status, let me know!
You are right, I don't think we are ready for ListView until the other issue is fixed. The combination of both setup + bind is too performance intense with it. I'll revert back to ListBox until further notice 😞 |
So this is a branch I would have to build myself (how, with gnome builder I suppose) and then set up / authorize for accounts etc. and test (presuming you can solve the bind issue), or there would be some flatpak I could use for this? I'm tempted to say that in any case, it might be worth waiting a week or so for the Fedora 39 beta that I could try upgrading to, in order to obtain the much awaited Sysprof 45 that would again give us better data (and nicer presentation)... but sysprof boils down to:
|
Unfortunately, Sysprof is F39 (and in Rawhide) is still Sysprof 44, at least as of yesterday morning. What I did was profiled the programs with
Yes, that much I understand. The issue is what you then to with that data, how do you find what's causing dropped frames using this: That being said, if it's really CPU intensive, the "time profiler" tab does display results that are similar to other profiling tools & that I can make sense of. |
You can test against the current release since it occurs there as well but if you need the nightlies, you can get them from here: https://nightly.link/GeopJr/Tuba/workflows/build/main/dev.geopjr.Tuba.Devel-x86_64.zip
Sounds good to me, thanks!
These two blog posts might be helpful:
Specifically, since the problem looks like main loop blocking, the "Finding main loop slow downs" and "Avoiding Main Loop Stalls" sections |
So from looking at the code, I see that there's the I'm not sure if this is the source of all the performance issues (that's actually unlikely), but it definitely shouldn't be done like that. A list view should be used with a specific kind of widget, that widget should be created in |
It's possible to extend Widgetizable to include setup, bind, unbind, e.g. for status (incomplete):// Widgets.Status
public Status.empty () {
Object ();
}
public void unbind () {
// reset everything that happened in bind ()
} // Widgetizable
public virtual void unbind (Gtk.Widget widget) {}
public virtual void bind (Gtk.Widget widget) {} // API.Status
public override void unbind (Gtk.Widget widget) {
((Widgets.Status) widget).unbind ();
}
public override void bind (Gtk.Widget widget) {
((Widgets.Status) widget).bind = this;
}
public override Gtk.Widget to_widget () {
return new Widgets.Status.empty ();
} // Views.ContentBase
... But it is a bit tedious to do for every Widgetizable 🤔
I have only three concerns:
(I might be wrong on the above, I'm going off of the assumption that we won't have access to the widgets built using the Builder factory)
🤔 FWIW, removing our custom styles does not seem to have any impact on performance |
It could be possible to go that route, yeah. But my point is that we should try to move away from that abstraction and from that way of structuring the logic altogether. The view that includes the list should know which specific kind of items it contains (not some generic widgetazables, but say, statuses), and only support those, and work with those directly, instead of trying to be generic over it. At least, in my understanding, that's how the lists framework is supposed to used in gtk4. Maybe that was different with a list box.
Well yes, that needs to be thought over. I don't know whether it's better/faster to create and destroy auxiliary subwidgets (like polls and quotes) dynamically each time you're re-binding a
I currently don't have any follow requests so I can't even check, but it sounds wrong for a follow request to inherit from status? Like it's a different thing entirely?
Why not? To hide an action, we should just use But otherwise, the builder factory does not limit us in binding a status entity to the widget? You'd do this (Blueprint syntax for readability):
Again, I don't see why not? (Perhaps there is a reason and I'm just not understanding it, you surely have way more experience with the codebase and know what the tradeoffs are and where the gotchas lie.) |
It makes sense but it's also kind-of limiting. For example profiles need to have a cover/header and since we can't have boxes in the scrolledwindow anymore, the cover ended up being a Widgetizable https://github.com/GeopJr/Tuba/blob/main/src/Views/Profile.vala#L2 Notifications timeline can also have different widgets, one for status related events and another for profiles (if we move them to a different widget) (used for "x user followed you" events)
It's a profile / account which inherits from status + 2 new buttons for accept/decline
Sounds good! I need to take a deeper dive into ListViews, but from my (limited) understanding, the factories act like the dropdown ones where you can only bind objects to them / you can't create actions for them, no idea about signals. What I gathered from https://blog.gtk.org/2020/06/07/scalable-lists-in-gtk-4/ and the linked examples, when using factories, the widgets are exclusively handled by the UI files, there won't be a Widgets.Status anymore and for something like expanded widgets to work we'd have to include for example two privacy indicator icons, one at the top right and one at the bottom (for expanded) and conditionally show them based on the object we bind to the factory |
It should be very possible to put more widgets (such as profile headers or whatever) into the
...and have it just work. But making it a list view item is also a reasonable approach.
In general, I think this is something that the scalable lists framework should provide explicit support for: having heterogeneous items (and item views instantiated for them), so that it can recycle/reuse views of the appropriate kind. In Android's So maybe this is something that we should discuss with GTK developers, to get GTK to support the same. Otherwise we'd indeed have either to create and tear down item view on each bind (which goes against how the scalable lists framework is intended to be used), or put a That being said, one optimization we could do without changes to GTK is only recreating the view on
I see, thanks. A design question then: should a profile card look exactly like a status? Is that not confusing?
Uhh, I don't understand why you'd get that impression? It's just Anyways, as we've seen above, the real reason for the slowness seems to be CSS (in)validation; we should be looking into what's causing that. |
During the migration to ListView, anything that wouldn't match the structure of
I don't blame tootle for having it like that since it matches Mastodon web but it makes sense to be a different one, similar to the mobile app's, I'll explore it after the next release!
Ah, I thought the whole widget had to be included in the builder file for the expressions to work. Thanks for the clarification! |
NOTE: Main has reverted the ListView change (partially) for the upcoming release, further development on the ListView issue has to enable it in Meson and revert some UI file and CSS changes as described on #504 (comment) |
I made a small experiment with this (but I'm stuck, mostly because I need to learn about GLib, GTK, Tuba and Vala on the fly and debugging the problems that arise seems to be quite hard and time consuming). Nevertheless, I made kind of a non-working proposal how it maybe could be done without too much effort. The changes that I made so far are in https://github.com/farao/Tuba/tree/feat/listbox-listview. Ideas:
Even though this is in a non-working state and I currently feel stuck: maybe this helps anyways? As a start for somebody who is more experienced in debugging Tuba code or generally as an solution proposal to discuss? |
I like the idea, please open a draft pr so we can discuss there and I can push to it too! |
This is a sibling of #410
During the cleanup PR #424, we replaced the ListBox with ListView. This was suggested a while ago to solve some of the performance issues #179.
How it was implemented
ListView items can either be generated and bound using UI files or with signal factories. Since many parts of the status widgets are dynamically constructed, I went with signal factories.
There are 4 stages and signals: setup -> bind -> unbind -> teardown
The docs explain them in detail https://valadoc.org/gtk4/Gtk.SignalListItemFactory.html.
This might be where I screwed up, by creating the widget on bind rather than going through the process of setting it up, binding it and unbinding it when needed. However after some more testing and actually implementing it properly, I noticed the exact same issues listed below.
The pros
The cons
Screencast.from.2023-09-12.16-24-54.webm
The text was updated successfully, but these errors were encountered: