CPU-Heavy Scrolling

Started by Danetta, 03 January 2017, 15:18:04

Danetta

Hello.

I used this example of how to create a scrollable panel, but adjusted it to work with TGUI 0.7 (connectEx instead of bindCallbackEx). I created panel with a size of 1080*620 and placed 6 widgets on it (all of them are labels with 5-10 symbols of text). I also set maximum for scrollbar to 1240 (double of panel size). It seems that scrolling is very cpu-heavy even in this case, but I intended to add more than just 6 label on panel. Lag is not very noticeable, but is still is compared to empty scrolling. Is it the constant moving of widgets causing this?

texus

Could you perhaps try with 0.8-dev and see if it is better there? The setPosition function works differently there and it should be much faster.

If not then I might be able to tell you how to implement the scrolling in a more performant way in 0.7 (because the example that you based yourself on was a very naive implementation), but it will require you to add a custom widget. So if the problem is solved by using 0.8-dev then maybe it is not worth the effort.

Danetta

Do you have precompiled version for Visual C++14 (2015)?

texus

#3
No, I don't have precompiled versions for it and it would take too long for me to generate them. So you would have to compile 0.8 yourself.

Edit: if it is not urgent then maybe I can generate them for you and send them in a few hours from now.

Danetta

#4
I am not in hurry.
I would probably try (don't yet know how to) to compile them myself, if you are too busy, but I can also just wait and focus on other things meanwhile.

texus

For me it is much easier to build them as I have everything set up for 0.7 and thus just have to change a few variables. But I run windows in a virtualbox on my laptop so building the library is extremely slow (but I just run it in the background so it doesn't matter much to me how long it takes).

I assume you need the 32-bit version: https://www.dropbox.com/s/e7kfugbyq3myfbp/TGUI-0.8-dev-vc14-32bit-for-SFML-2.4.1.zip?dl=1
But I also have the 64-bit version just in case: https://www.dropbox.com/s/se1xi0o7isyicqa/TGUI-0.8-dev-vc14-64bit-for-SFML-2.4.1.zip?dl=1

Danetta

#6
Thank you.
By the way, what is replacement for "setTextColor" and "setBackgroundColor"?

Update: Seems like the issue with lag and CPU usage is still here. Also, I noticed CPU-usage spikes even in idle, haven't seen it on 0.7.

texus

They are part of the renderer now, so you have to call widget->getRenderer()->setTextColor instead of widget->setTextColor.

If you use themes then there are indeed some differences with 0.7, other than that most things should be very similar.

texus

Could you send some (minimal) code in which the lag and high cpu usage occur?
I don't have time to look at it right now, but then I can have a look later and see if I can reproduce it and find out what is causing it.

Danetta

I am no sure I can do something minimal enough, but I will try to test it a bit with 0.7/0.8.
Anyway, it seems like problem with scrolling is still here, so you can share about custom widget and stuff for better scrolling.

texus

#10
The idea of using a custom widget is to avoid calling setPosition. Apparently the function call has too much overheat so the widgets have to be moved without the function being called for every pixel the scrollbar moves. To do this, we will translate the widgets when drawing. You will call setPosition once to place them in the panel, but when the scrollbar moves you will not call setPosition again and instead dynamically change where the widget is drawn.

Instead of using tgui::Panel, you use your own CustomPanel class that inherits from tgui::Panel. This class needs access to the scrollbar somehow (e.g. it has a pointer to the scrollbar as a member variable). You have to override the draw function (adding draw as a member function of your class) like this:
Code (cpp) Select
void CustomPanel::draw(sf::RenderTarget& target, sf::RenderStates states) const {
    states.transform.translate({0, -(float)scrollbar->getValue()});
    Panel::draw(target, states);
}


That will move all widgets up with scrollbar->getValue() pixels when drawing.

When handling events, the panel must also know about the fact that the widgets aren't located where they claim to be, so mouseOnWidget has to be implemented like this in your class:
Code (cpp) Select
bool CustomPanel::mouseOnWidget(float x, float y) const {
    Panel::mouseOnWidget(x, y + scrollbar->getValue());
}


The same has to be done for leftMousePressed and leftMouseReleased.

Note: this code is based in 0.7, to do the same in 0.8-dev just have a look at how the parameters of the functions are in the tgui::Panel class.

Edit: can you check one more thing? Does the lag also occur when you have a ListBox with some items in it (which is very similar situation to having a scrollable panel of labels)?

Danetta

Quote from: texus on 03 January 2017, 19:16:45
Edit: can you check one more thing? Does the lag also occur when you have a ListBox with some items in it (which is very similar situation to having a scrollable panel of labels)?
0.7.2 test:
Placed 20 items in listbox, but only 7 were visible without scrolling.
Yes, it's lagging when I try to move the scrollbar while holding left mouse button. However, if I use mouse wheel for scrolling, then it's hard to see a lag probably because of less frequent step.

0.8.dev:
No lag even with 2000 items in listbox.

texus

Ok, the custom widget should have a similar performance to the ListBox of 0.8-dev, so it should work smoothly.

It worries me a bit that you still saw lagging in 0.8 earlier when calling setPosition on every label. That would indicate that the layout system itself is too slow to handle the many updates per second. Or maybe your mouse is just sending more MouseMoved events than normal, it could also be an interesting measurement to see how many events are generated per seconds. But in the end I'll just have to profile the code to find out for certain where most time is really being spend.

Danetta

#13
Quote from: texus on 03 January 2017, 19:16:45
Note: this code is based in 0.7, to do the same in 0.8-dev just have a look at how the parameters of the functions are in the tgui::Panel class.
I looked into 0.8 source code and it seems like Panel::draw has the same parameters, why would not custom widget work without changing the code?

Update: I tried your code, but overrided "draw" function moves the panel itself, not the widgets on panel. Is it supposed to be like that?

texus

QuoteI looked into 0.8 source code and it seems like Panel::draw has the same parameters, why would not custom widget work without changing the code?
The draw function is the same, I was talking about mouseOnWidget, leftMousePressed and leftMouseReleased which take a sf::Vector2f instead of two floats.

QuoteI tried your code, but overrided "draw" function moves the panel itself, not the widgets on panel.
The code is indeed wrong (I didn't test it and just wrote what I expected it to be). You indeed can't just call Panel::draw, instead you should try to just copy the contents of Panel::draw into CustomPanel::draw and add the states.transform.translate line right above the drawWidgetContainer call. That should move just the widgets and not the panel itself. Note that you also have to include "TGUI/Clipping.hpp" in your source file.

Danetta

#15
I am not sure how to copy it, it seems like there are also Borders and Clipping classes used.

UPD: Huh, I am kinda stupid, forgot about namespace for Borders. But I don't see anything related to Clipping in tgui space.

texus

The Borders class should be available because you indirectly include Panel, for the Clipping class you have to add the following on top of your source file:
Code (css) Select
#include <TGUI/Clipping.hpp>

Danetta

Yes, realized that just one second before you answered. Sorry for these questions.

Danetta

#18
I have another question. It is more about me trying to learn and not some specific problem (because I found a workaround).

Here is my current code for initialization of panel and scrollbar for it:
Code (cpp) Select
CustomPanel pan;

tgui::Scrollbar::Ptr scroll = std::make_shared <tgui::Scrollbar>();
main_window.get_Gui()->add(scroll, "scroll_friends");
scroll->setPosition(1060, 100);
scroll->setSize(20, 620);
scroll->setLowValue(620);
scroll->setMaximum(1240);
scroll->hide();
pan.set_scroll(*main_window.get_Gui());

CustomPanel::Ptr panel = std::make_shared<CustomPanel>(pan);

panel->setSize(1060, 620);
panel->setPosition(0, 100);
//panel->setBackgroundColor(tgui::Color(0, 0, 0, 0));
panel->getRenderer()->setBackgroundColor(tgui::Color(70, 130, 180, 255));
panel->hide();
main_window.get_Gui()->add(panel, "panel_friends");


As you can see, I use my "set_scroll" function (which I use to store pointer of scrollbar inside CutomPanel class) of custom panel via panel object itself. I think it's a wrong way to do so, because we use shared pointers to widgets anyway, right? However, once I do "CustomPanel::Ptr panel = std::make_shared<CustomPanel>(pan);" or something like that (just declaring new panel for example and not sharing existing one) I have'nt access to "set_scroll" anymore, so anything like "panel->set_scroll" is not possible. Where is my mistake?


CustomPanel.h code:
Code (cpp) Select
class CustomPanel : public tgui::Panel {
public:
void set_scroll(tgui::Gui& gui);
void draw(sf::RenderTarget& target, sf::RenderStates states) const;
private:
tgui::Scrollbar::Ptr scrollbar;
};


texus

I really need to write a tutorial about how to write custom widgets some day, I didn't even understood why this went wrong until I saw the exact error.

You have to add the following in the declaration of your CustomPanelClass:
Code (cpp) Select
typedef std::shared_ptr<CustomPanel> Ptr;

texus

#20
QuoteAlso, I noticed CPU-usage spikes even in idle
I've made a large internal change in 0.8-dev, hopefully what I changed is related to this and will have fixed it.

I can't reproduce the cpu lag though, not even with calling setPosition on every widget every time the scrollbar moves.
The scrollbar generates up to 30-50 events per seconds here when being moved and each of my 50 labels is moved when this happens.
The 570 fps I have during idle only goes down to 530 fps while scrolling.

But having a CustomPanelClass that doesn't call setPosition the whole time will of course perform better in any case.