cancel
Showing results for 
Search instead for 
Did you mean: 

Adding containers/widgets not declared in ScreenView.hpp

ferro
Senior III

Hi Gfx Team,

Could you comment on this please ?

I tried to View.add () heap allocated container and it seems to be working Okay.
The container might be statically allocated as well. The key point is, the View is not aware of it at compile time.

Before Transition to other screen, I remove the widget from the View and delete it.

void ScreenView::tearDownScreen ()
{
	if ( nullptr != pWgt )
	{
		remove ( * pWgt ); 
		delete pWgt; // heap allocated
		pWgt = nullptr;
	}
	ScreenView::tearDownScreen ();	
}

I found two (contradicting?) posts by ST team related to this approach.

One says this is not possible (should be avoided) @Osman SOYKURT 

"if you mean that you want to create a new instance of a widget during runtime, it's not possible."

https://community.st.com/t5/stm32-mcus-touchgfx-and-gui/dynamic-screens-how-to-create/m-p/593246


And second one provides an example (does not say it is not possible) @GaetanGodart 

"I have made an example that dynamically adds elements to the scrollable container"

https://community.st.com/t5/stm32-mcus-touchgfx-and-gui/scroll-list-with-3-columns/m-p/652495/highlight/true#M36748

 

FrontendHeapBase.hpp has got this comment :

/**
 * This class provides the memory that shall be used for memory allocations
 * in the frontend. A single instance of the FrontendHeap is allocated once (in heap
 * memory), and all other frontend objects such as views, presenters and data model are
 * allocated within the scope of this FrontendHeap. As such, the RAM usage of the entire
 * user interface is sizeof(FrontendHeap).
 *
 * @note The FrontendHeap reserves memory for the most memory-consuming presenter and
 * view only. The largest of these classes are determined at compile-time using template
 * magic. As such, it is important to add all presenters, views and transitions to the
 * type lists in this class.
 *
 */
class FrontendHeapBase : public touchgfx::MVPHeap
{

By dynamically adding/removing containers, I am clearly breaking this requirement. Am I risking memory corruption, resource leaks, or causing other issues with the Gfx Engine?

It seems as a considerable limitation. I am toying with an idea of switching to emWin besause of this.

Thank you

 

1 ACCEPTED SOLUTION

Accepted Solutions

Hello @ferro ,

 

"No, I am not concerned about (running out of) memory. The question is, why it is not recommended as you say next"

You should be concerned about running out of memory :)

It is not recommended because the memory is limited in embedded systems so you can quickly run out of memory.
In case you run out of memory (without protection / checks) your program will crash.
When adding widgets at runtime, if it is different widgets, you don't know how many widgets of type A or type B you will add so you cannot know how much memory you will require, all you can do is expect a worst case scenario. But if you don't have a maximum amount of widget to add it could be infinity.

So you understand that some precautions have to be taken to add elements at runtime, this is why it is not recommended, but also, not prohibited. See below for goo practices.

 

"Is Gfx Heap memory treated somehow differrent from global C++ runtime static/heap memory ?"

The frontendHeap is just used to store the views, presenters, model and transitions. (It is calculated at compilation, so if the widgets were stored in the frontendHeap, by adding a widget at runtime, the frontendHeap would overflow, but in my example I was able to add 1022 widgets.
So the widgets are stored in the C++ heap.

 

"At the moment there are two very different statements about this"

Yes, Osman gave a simpler answer, basically "You shouldn't do it" for more beginner level. I answered "It is possible but should be avoided" which is also a simplification because precautions should be taken (c.f. below).

The full answer is : It is possible to add widgets or elements at runtime but due to the limited amount of memory in embedded devices, precautions have to be taken such as allocating a specific memory region for the dynamically added widgets and only adding widgets at runtime in this region plus it is recommended to limit the amount of widget and to reuse the ones that are not visible instead of creating a lot of different widgets.

 

"STemWin"

Sure, I thought that you said STemWin was able to dynamically allocate memory, create widgets and clear them when they are not visible anymore.

In a way STemWin and TouchGFX work in the same way. You could add widgets at runtime in both but you have to do it smartly.

Are you creating widgets at runtime in your STemWin applications? If you do, what safety measures do you take?

 

How to properly add widgets at runtime

1) Using new

Create the widget using new.
Add the widget to the screen using add.
Don't forget to clean it in tearDownScreen(), for that you have to store your new widgets in a vector.

This method has no verification so your program can crash.

 

void Screen2View::handleTickEvent()
{
    touchgfx::Box* box2 = new touchgfx::Box();
    add(*box2);
    box2->invalidate();
}

 

 

2) malloc (heap TouchGFX or C++)

Allocate memory for the widget using malloc/calloc.
Verify that the malloc was successful (allocated memory is not 0).
Create the widget using new.
Add the widget to the screen using add.
Don't forget to clean it in tearDownScreen().

This will use up to all the heap available and might block some other processes if they require heap too.

 

void Screen2View::handleTickEvent()
{
    void* memory = malloc(sizeof(touchgfx::Box));
    if (memory == 0) return;
    touchgfx::Box* box2 = new (memory) touchgfx::Box;
    add(*box2);
    box2->invalidate();
}

 

 

3) FreeRTOS malloc (FreeRTOS heap)

Same as above but use pvPortMalloc() instead.

This will use up to all the heap of the task and may block other processes.

 

void Screen2View::handleTickEvent()
{
    void* memory = pvPortMalloc(sizeof(touchgfx::Box));
    if (memory == 0) return;
    touchgfx::Box* box2 = new (memory) touchgfx::Box;
    add(*box2);
    box2->invalidate();
}

 

 

4) Allocate a custom heap region (inside another heap)

Create a buffer where you will store your widgets' addresses that will act as a heap.
Add your widget as previously.

This way you won't run out of memory nor use all the available memory.
Also, you don't have to store your widgets in a vector, instead you can just move the heap index to the beginning of the heap.

uint32_t myheap[10000];
uint8_t* heapTop = 0;
uint8_t* heapEnd = 0;

void Screen2View::setupScreen()
{
    heapTop = (uint8_t*)myheap;
    heapEnd = heapTop + sizeof(myheap);
    Screen2ViewBase::setupScreen();
}

void Screen2View::handleTickEvent()
{
    void* memory = heapTop;
    heapTop += sizeof(touchgfx::Box);
    if (heapTop > heapEnd) return;
    touchgfx::Box* box2 = new (memory) touchgfx::Box;
    add(*box2);
    box2->invalidate();
}

 

Note that the code snippets provided are just here as examples, we tried them earlier but I am not sure they are exactly what we tried.

 

I hope this clears things up! :smiling_face_with_smiling_eyes:

 

Regards,

Gaetan Godart
Software engineer at ST (TouchGFX)

View solution in original post

5 REPLIES 5
GaetanGodart
ST Employee

Hello @ferro ,

 

In my post, I think I also mention that it is not recommended to add widgets at runtime because in embedded devices, memory is limited.

It is possible to add widgets at runtime. c.f. the example I give.
It crashes after adding over 1000 widgets so that is probably the risks you talk about.

Overall, you should know how many widgets you need at compile time.

I don't know what are the risks with adding widgets at runtime in TouchGFX because this is never recommended.
I can try to find that information if you really cannot find another way than adding widgets at runtime.

 

Can you add widgets at runtime with emWin? If so, how do they manage memory? What happens if you run out of memory space?

 

Regards,

Gaetan Godart
Software engineer at ST (TouchGFX)

Hi @GaetanGodart 

Thanks for prompt reply.

>>"It is possible to add widgets at runtime."
>>"It crashes after adding over 1000 widgets so that is probably the risks you talk about."

No, I am not concerned about (running out of) memory. The question is, why it is not recommended as you say next

>>"I don't know what are the risks with adding widgets at runtime in TouchGFX because this is never recommended."

Is Gfx Heap memory treated somehow differrent from global C++ runtime static/heap memory ?

A scenario might be: I want to show either widget A or widget B.

if ( true == bShow_A )
	View::add ( A );
else
	View::add ( B );

So clearly, especially with embedded system, memory footprint should be
memory_size = max (A,B).

Following Gfx recomendation/paradigm the footprint is
memory_size = A + B


At the moment there are two very different statements about this:

1. Osman : "if you want to create a new instance of a widget during runtime, it's not possible."
2. Gaetan : "It is possible to add widgets at runtime" - meaning to add C++ heap 'new' instance out of Gfx/View scope as in your example

 

Could you possibly discuss with team / Osman SOYKURT and give an explanation ?

 

STemWin:
I mentioned STemWin because there is already a product in my company with GUI fully implemented in it.
With next generation of that device we are trying to use Gfx. So I am trying to port/map previous generation product GUI to Gfx world... and struggling to find Gfx paradigm to mimick STemWin.

Today I learnt that emWin has got its own dedicated memory pool (similar to Gfx Heap) from which memory for any emWin object is acquired.

Here, page 174

https://www.segger.com/downloads/emwin/UM03001

 

 

Hello @ferro ,

 

"No, I am not concerned about (running out of) memory. The question is, why it is not recommended as you say next"

You should be concerned about running out of memory :)

It is not recommended because the memory is limited in embedded systems so you can quickly run out of memory.
In case you run out of memory (without protection / checks) your program will crash.
When adding widgets at runtime, if it is different widgets, you don't know how many widgets of type A or type B you will add so you cannot know how much memory you will require, all you can do is expect a worst case scenario. But if you don't have a maximum amount of widget to add it could be infinity.

So you understand that some precautions have to be taken to add elements at runtime, this is why it is not recommended, but also, not prohibited. See below for goo practices.

 

"Is Gfx Heap memory treated somehow differrent from global C++ runtime static/heap memory ?"

The frontendHeap is just used to store the views, presenters, model and transitions. (It is calculated at compilation, so if the widgets were stored in the frontendHeap, by adding a widget at runtime, the frontendHeap would overflow, but in my example I was able to add 1022 widgets.
So the widgets are stored in the C++ heap.

 

"At the moment there are two very different statements about this"

Yes, Osman gave a simpler answer, basically "You shouldn't do it" for more beginner level. I answered "It is possible but should be avoided" which is also a simplification because precautions should be taken (c.f. below).

The full answer is : It is possible to add widgets or elements at runtime but due to the limited amount of memory in embedded devices, precautions have to be taken such as allocating a specific memory region for the dynamically added widgets and only adding widgets at runtime in this region plus it is recommended to limit the amount of widget and to reuse the ones that are not visible instead of creating a lot of different widgets.

 

"STemWin"

Sure, I thought that you said STemWin was able to dynamically allocate memory, create widgets and clear them when they are not visible anymore.

In a way STemWin and TouchGFX work in the same way. You could add widgets at runtime in both but you have to do it smartly.

Are you creating widgets at runtime in your STemWin applications? If you do, what safety measures do you take?

 

How to properly add widgets at runtime

1) Using new

Create the widget using new.
Add the widget to the screen using add.
Don't forget to clean it in tearDownScreen(), for that you have to store your new widgets in a vector.

This method has no verification so your program can crash.

 

void Screen2View::handleTickEvent()
{
    touchgfx::Box* box2 = new touchgfx::Box();
    add(*box2);
    box2->invalidate();
}

 

 

2) malloc (heap TouchGFX or C++)

Allocate memory for the widget using malloc/calloc.
Verify that the malloc was successful (allocated memory is not 0).
Create the widget using new.
Add the widget to the screen using add.
Don't forget to clean it in tearDownScreen().

This will use up to all the heap available and might block some other processes if they require heap too.

 

void Screen2View::handleTickEvent()
{
    void* memory = malloc(sizeof(touchgfx::Box));
    if (memory == 0) return;
    touchgfx::Box* box2 = new (memory) touchgfx::Box;
    add(*box2);
    box2->invalidate();
}

 

 

3) FreeRTOS malloc (FreeRTOS heap)

Same as above but use pvPortMalloc() instead.

This will use up to all the heap of the task and may block other processes.

 

void Screen2View::handleTickEvent()
{
    void* memory = pvPortMalloc(sizeof(touchgfx::Box));
    if (memory == 0) return;
    touchgfx::Box* box2 = new (memory) touchgfx::Box;
    add(*box2);
    box2->invalidate();
}

 

 

4) Allocate a custom heap region (inside another heap)

Create a buffer where you will store your widgets' addresses that will act as a heap.
Add your widget as previously.

This way you won't run out of memory nor use all the available memory.
Also, you don't have to store your widgets in a vector, instead you can just move the heap index to the beginning of the heap.

uint32_t myheap[10000];
uint8_t* heapTop = 0;
uint8_t* heapEnd = 0;

void Screen2View::setupScreen()
{
    heapTop = (uint8_t*)myheap;
    heapEnd = heapTop + sizeof(myheap);
    Screen2ViewBase::setupScreen();
}

void Screen2View::handleTickEvent()
{
    void* memory = heapTop;
    heapTop += sizeof(touchgfx::Box);
    if (heapTop > heapEnd) return;
    touchgfx::Box* box2 = new (memory) touchgfx::Box;
    add(*box2);
    box2->invalidate();
}

 

Note that the code snippets provided are just here as examples, we tried them earlier but I am not sure they are exactly what we tried.

 

I hope this clears things up! :smiling_face_with_smiling_eyes:

 

Regards,

Gaetan Godart
Software engineer at ST (TouchGFX)

Hi @GaetanGodart 

Very thorough answer, thank you.

>"You should be concerned about running out of memory"
What I meant was; "Apart from the memory limitation, are there some 'behind the scene' mechanisms running by Gfx a user need to be aware of ?"


>"Yes, Osman gave a simpler answer, basically "You shouldn't do it" for more beginner level. I answered "It is possible but should be avoided" which is also a simplification because precautions should be taken (c.f. below)."

Okay, things are starting to make sense now.


>"Are you creating widgets at runtime in your STemWin applications?"
I am not familiar with STemWin. Want to focus on Gfx. Might add to this answer later when I know more.

>"How to properly add widgets at runtime"
Splendid, thank you for these examples.

 

What I do is:

 

 

// ScreenView.hpp
#include <memory>  // std::unique_ptr

class ScreenView : public ScreenViewBase
{
public:
    ScreenView ();
    virtual ~ScreenView ();
    virtual void setupScreen ();
    virtual void tearDownScreen ();

protected:
	std::unique_ptr<touchgfx::Box> m_ptrBox {};
}

 

 

 

// ScreenView.cpp
//---------------------------------
ScreenView
::~ScreenView()
{
    // touchgfx::Box dtor called automaticaly here i.e. memory allocated
    // for it is released
}

//---------------------------------
void ScreenView
::setupScreen ()
{
    ScreenViewBase::setupScreen();

    m_ptrBox = std::make_unique<touchgfx::Box> ();

    if ( nullptr == m_ptrBox )
    {
       // deal with failed memory allocation here
    }

    m_ptrBox->setPosition ( 10, 10, 50, 50 );
    add ( * m_ptrBox.get () );
}

 

 

Ferro