RAM and CPU time consumption

Started by Nafffen, 19 December 2022, 19:38:55

Nafffen

Hello, first of all thank you for developping TGUI !

I have an object "ItemOnUIWidget" which is a subclass of SubwidgetContainer. There is a Panel, Picture and a Label within.
sizeof(ItemOnUIWidget) returns ~1000
I create around 250 of it but my RAM goes from 50Mo to 110Mo, I was wondering if there is a way to limit this RAM consumption ? I assume one of the component of ItemOnUIWidget allocate some memory to do stuff inside. Does this RAM allocation is linear ? What if I'm creating 10 000 of it ? Will my RAM go to stars ?

Secondly, when adding those ItemOnUIWidget in a ScrollPanel, with some size and position binded, it is very time consumming, around 500 000 microsec, same questions, is there a way to limit that ? Is it linear with number of widget ?

Thank you

texus

I think you can expect it to be rather linear, but if you really need that many objects of type ItemOnUIWidget then I don't recommend making it a widget.

Realistically you would never have more than a few hundred widgets on your screen, so memory consumption or performance of creating widgets is usually not important.

Internally TGUI doesn't use the Picture or Label classes to render things, widgets use the Sprite and Text classes directly. So if you need an object with many text objects and pictures then I would suggest turning that big object into one custom widget, and have that one widget render all components in it.

Classes such as SubwidgetContainer are nice to get something simple working, but if you really need the performance then you will have to create something on a lower level. You can look at existing widget to get some idea, in e.g. the EditBox::draw function you can see that it is rendering all rectangles and texts directly to the render target and it isn't using subwidgets to do so.

Nafffen

Thank you for your answer,

Basically I am making a UI to show an inventory in a ScrollPanel. I thought I could use ItemOnUIWidget and add them in the ScrollPanel. (which is perfect without ram and time to set up layout issues)

I just tried to replace the Picture in my ItemOnUIWidget and use a simple Sprite instead. Indeed, Sprite store a reference to a texture but Picture copies the texture, that why the RAM went up, because I copied the texture for each item. However the time to set up the layout is still to high for me with this way.

I really want to have ScrollPanel features so I made a class InventoryWidget, subclass of ScrollPanel but now I am struggling to find the right way to handle item inside. If I add as member of this class sf::Sprite and sf::Text, how will I manage drawing my items in the ScrollPanel with the offset of scrollbar and without going over the edge ? Is sf::RenderTexture a good idea ?

texus

#3
QuoteSprite store a reference to a texture but Picture copies the texture, that why the RAM went up
This is incorrect, unless you are passing an sf::Texture to the Picture (in which case you have the same issue with Sprite). The tgui::Texture class is a lightweight wrapper that points to the texture recource. As long as TGUI loaded the texture (i.e. you passed a filename to TGUI instead of an sf::Texture), the texture resource will internally be reused.

Btw, I'm talking about tgui::Text and tgui::Sprite. You mention that Sprite stores a reference, so maybe you are looking at sf::Sprite instead of tgui::Sprite then? TGUI mostly does not use SFML classes internally, it has its own classes which then get rendered with sf::VertexArray when using the SFML_GRAPHICS backend.

QuoteIf I add as member of this class sf::Sprite and sf::Text, how will I manage drawing my items in the ScrollPanel with the offset of scrollbar and without going over the edge ? Is sf::RenderTexture a good idea ?
While it could be done, I would not recommend bringing SFML rendering into this. You would have to render the sf::Sprite and sf::Text to a tgui::CanvasSFML widget, which internally uses an sf::RenderTexture as buffer.

QuoteI really want to have ScrollPanel features so I made a class InventoryWidget, subclass of ScrollPanel but now I am struggling to find the right way to handle item inside
I'm not sure if it is a good idea to subclass ScrollablePanel, if all you need is a vertical scrollbar. But you can use it and just reuse the existing scrollbar. The alternative would be to copy multiple parts of the ScrollablePanel widget into your own custom widget, so I guess using ScrollablePanel makes sense as it is easier to set up.

The draw function of ScrollablePanel can't be used directly because it can only position widgets. You have to implement a draw function in your custom widget, but it is very similar to the one found in ScrollablePanel (https://github.com/texus/TGUI/blob/0.10/src/Widgets/ScrollablePanel.cpp#L615-L662).

Quotehow will I manage drawing my items in the ScrollPanel with the offset of scrollbar and without going over the edge
Have a look at these lines for an example: https://github.com/texus/TGUI/blob/0.10/src/Widgets/ScrollablePanel.cpp#L648-L654

The target.addClippingLayer and target.removeClippingLayer calls define an area to draw in, everything outside would be clipped, so the second parameter to addClippingLayer should be something like "{0, 0, width - scrollbarWidth, height}".

With states.transform.translate you move all your rendering calls to a different location. By passing the scrollbar value, all draw calls behind this line (those that are given the "states" as parameter) will be shifted by the scrollbar.

Other than that you need to use "target.drawFilledRect", "target.drawSprite" and "target.drawText" lines on the place where the example code above has "Container::draw(target, states);".

Nafffen

QuoteI'm talking about tgui::Text and tgui::Sprite. You mention that Sprite stores a reference, so maybe you are looking at sf::Sprite instead of tgui::Sprite then?
My bad I was confusing sf::Sprite and tgui::Sprite, same with Text. Yes I was talking about sf::Sprite which stores a reference to a texture. My game load all textures in a specific time, and then I can access it via a shared_ptr. Is there a way to pass sf::Texture to tgui::Picture without copying the data? What if I pass multiple time the same sf::Texture to different tgui::Picture, will it be copied everytime ?

 
QuoteWhile it could be done, I would not recommend bringing SFML rendering into this. You would have to render the sf::Sprite and sf::Text to a tgui::CanvasSFML widget, which internally uses an sf::RenderTexture as buffer.
Ok it seems a bit tricky for me.

QuoteThe alternative would be to copy multiple parts of the ScrollablePanel widget into your own custom widget
I think I will go that way, if I understand it well, according to your tutorial, InventoryWidget will be subclass of tgui::Widget. I redefined functions I need and in draw function I can reuse (I mean copy appropriate parts of code) the draw function of ScrollPanel, drawing my custom parts inside directly with the BackendRenderTarget reference.

Tell me if I am wrong, either way thank you for your helpful answers.

texus

QuoteIs there a way to pass sf::Texture to tgui::Picture without copying the data
No.

But if this is what you are doing then you may already be able to reduce the memory usage and maybe even improve performance by letting TGUI load the texture. Instead of passing the sf::Texture to TGUI, just pass the filename of the texture to load. If you are also using the image in your own program then it will be loaded twice (once by you and once by TGUI), but passing the same filename to TGUI multiple times will result in the cached image being used instead of it being loaded again (as TGUI internally caches all textures based on the filename for as long as there is still a widget that uses the texture). So TGUI will never have the same texture in memory multiple times.

If you pass an sf::Texture to TGUI then it can't cache it, it will copy it every time you pass the texture so you end up with much more duplicate textures in memory (because TGUI doesn't realize it has been given the same texture each time).

Quoteif I understand it well, according to your tutorial, InventoryWidget will be subclass of tgui::Widget. I redefined functions I need and in draw function I can reuse (I mean copy appropriate parts of code) the draw function of ScrollPanel, drawing my custom parts inside directly with the BackendRenderTarget reference.
Yes, that is how I would do it. It's more work because you have to implement more functions (but those can mostly be copied from existing TGUI widgets), but then you only calculate and draw what you ned.