Jump to content
sdawans

Double refresh of containers

Recommended Posts

Hello,

I'm currently developing a µGFX based GUI on ATSAMG55J19 running FreeRTOS, with a ILI9431 controller in SPI 4-wire mode. I've started prototyping the GUI in baremetal first to split the development into smaller problems. I'll integrate it in a FreeRTOS task later once I'm happy with the result.

The application has multiple screens, for which I'm using containers. Not relevant to my current question but maybe worth mentioning is that I have a sidebar that uses a unique instance of a container that doesn't need to be refreshed when changing pages, while the pages themselves are implemented in the remaining 230x240 portion of the screen (which is 320x240). Option #2 of this this answer was helpful in deciding how to implement this static sidebar without duplicating the widgets and it works fine. I'm also using this trick to make clickable zones over multiple widgets within the sidebar as I didn't want simple buttons but rather a combination of icons and dynamic labels in each clickable area. Again, not completely relevant to what I want to ask next.

The issue I'm facing is that although the page transitions are functional, the refresh of the screen is done twice. Since refresh operations are not very fast, this is quite an annoying glitch and I'm trying to figure out what could be causing two refreshes. The total wait time is actually 4 page loads, because the pages are first cleared before displaying a new one. I can clearly see that the page displays all widgets correctly after the first clear + redraw, but it gets cleared and re-drawn a second time.

Here is are the relevant portions of the code, you'll notice I used a uGFX Studio-generated project as initial template. I tried removing as much noise as possible to limit this post's length:

/* Called from mainloop after other platform initialisations 
 * and a call to gfxInit(); 
 */
int ugfx_test(void){
	gdispSetBacklight(100);
	gdispSetContrast(100);

	geventListenerInit(&glistener);
	gwinAttachListener(&glistener);

	guiCreate();

	while (1) {
		guiEventLoop();
	}
}

void guiCreate(void)
{
	GWidgetInit wi;

	// Prepare fonts
	dejavu_sans_16 = gdispOpenFont("DejaVuSans16");
	dejavu_sans_24 = gdispOpenFont("DejaVuSans24");
	dejavu_sans_32 = gdispOpenFont("DejaVuSans32");

	// GWIN settings
	gwinWidgetClearInit(&wi);
	gwinSetDefaultFont(dejavu_sans_16);
	gwinSetDefaultStyle(&white, FALSE);
	gwinSetDefaultColor(white_studio);
	gwinSetDefaultBgColor(white_studio);

	/* Create 6 pages */
	createPage00_standby();
	createPage01_main();
	createPage02_important();
	createPage03_info();
	createPage04_measure_menu();
	createPage05_measure_pressure();
    /* Create the sidebar in a unique container */
	create_sidebar();

	/* Display page 0 at the start */
	guiShowPage(0);
	gwinShow(ghContainer_sidebar);
}

void guiShowPage(unsigned pageIndex)
{
	// Hide all pages
	gwinHide(ghContainer00_standby);
	gwinHide(ghContainer01_main);
	gwinHide(ghContainer02_important);
	gwinHide(ghContainer03_info);
	gwinHide(ghContainer04_measure_menu);
	gwinHide(ghContainer05_measure_pressure);

	// Show page selected page
	switch (pageIndex) {
	case 0:
		gwinShow(ghContainer00_standby);
		break;

	case 1:
		gwinShow(ghContainer01_main);
		break;

    /* etc... cases 2 to 5 also handled here for remaining pages */
    /* ... */

	default:
		break;
	}
}

void guiEventLoop(void)
{
    GEvent* pe;
  
    while (1) {
        // Get an event
        pe = geventEventWait(&glistener, 10);
        switch (pe->type) {
            case GEVENT_GWIN_BUTTON:
                if ( ((GEventGWinButton*)pe)->gwin == ghButton1) {
                    guiShowPage(1);
                } else if ( ((GEventGWinButton*)pe)->gwin == ghButton2) {
                    guiShowPage(2);
                }
                /* etc, same thing for other pages */
                /* ... */
                break;
        }
    }
}

I left out the createPagexx functions as they are quite long, but the general idea for all of them is pretty much identical to what uGFX generates in a typical display page, for example:

static void createPage01_main(void)
{
	GWidgetInit wi;
	gwinWidgetClearInit(&wi);


	// create container widget: ghContainer01_main
	wi.g.show = FALSE;
	wi.g.x = 0;
	wi.g.y = 0;
	wi.g.width = 230;
	wi.g.height = 240;
	wi.g.parent = 0;
	wi.text = "Container";
	wi.customDraw = 0;
	wi.customParam = 0;
	wi.customStyle = 0;
	ghContainer01_main = gwinContainerCreate(0, &wi, 0);

	// Create label widget: ghLabel_1_date
	wi.g.show = TRUE;
	wi.g.x = 10;
	wi.g.y = 10;
	wi.g.width = 100;
	wi.g.height = 20;
	wi.g.parent = ghContainer01_main;
	wi.text = "12/03/2016";
	wi.customDraw = gwinLabelDrawJustifiedCenter;
	wi.customParam = 0;
	wi.customStyle = 0;
	ghLabel_1_date = gwinLabelCreate(0, &wi);
	gwinLabelSetBorder(ghLabel_1_date, FALSE);
	gwinSetFont(ghLabel_1_date, dejavu_sans_16);
	gwinRedraw(ghLabel_1_date);

    /* Other child widgets of ghContainer01_main here, not shown */
    /* ... */
}

I'm trying to understand the source of the double refresh:

  1. Could be due to the actual button event being generated twice due to some sort of button debounce problem, althoughit seems unlikely. As I understand it, there is no queue for events so if multiple events are fired all subsequent events will be dropped until we come back to a blocking wait in geventEventWait(...).
  2. Could be due to some child widgets needing to be redrawn after the initial redraw of the page. Could child widgets needing to be redraw trigger a redraw of the parent container? Seems unlikely again, especially since I get this with even the most basic container with a single label as child.
  3. Brain fart from my part, highly likely ;)

Thanks for the great software and tools you are putting together, and for any support on this matter.

By the way, are there any plans to open source the uGFX Studio? I'm willing to contribute some minor bugfixes.

Have a nice day

Sébastien

Share this post


Link to post
Share on other sites

Hello Sébastien and welcome to the community!

First to of all, let me thank you for your very nice post. If everybody would post their questions like this, life would be a whole lot easier for us. The background information you give along the minimized examples are really helpful.

I'm on the road right now so I can't run your example - this might have to wait until tomorrow. I also don't know anything on top of my head that might help you - we're going to have a look at this and report back ASAP.
Would it be possible for you to attach your simplified code as a ready-to-run projects as a ZIP archive? This would further simplify the process of tracking down & fixing the issue for us.

 

9 minutes ago, sdawans said:

By the way, are there any plans to open source the uGFX Studio? I'm willing to contribute some minor bugfixes.

The current version of the uGFX-Studio is pretty much dead. We're not putting any more time into it unless a company requests bug fixes or new features as part of a paid support plan. We started working on a completely new version of the uGFX-Studio which will be a lot better in almost all aspects. You can have a look at that here: 

Whether or not that tool will be open-sourced has not been decided yet.

 

Share this post


Link to post
Share on other sites

Hi Joel,

That was quick :)

Well, I was mostly interested in a "top of your head" answer for something blatantly obvious that I was missing or misusing, or for a referral to a similar problem experienced by someone else that I may have missed when reading past posts in this forum. If this isn't the case and you are willing to dive into the code I can certainly share the project but since it's custom hardware that I'm designing you won't be able to run it as-is. It's also currently formatted as an Atmel Studio solution so you would need that to build it out of the box. Or maybe you were talking about me sharing an x86 compatible version - that I can definately do as well. In fact, for debugging reasons I will go ahead with that idea and see if the problem also appear in the x86 version (although maybe there the refresh rate will be too fast to see anything).

Thanks for your time,

Sébastien

Share this post


Link to post
Share on other sites

Hello Sébastien,

If you can share a stripped down version of the project that runs as an x86 project so we can run it on Windows that would be great.

Other than that, if you want to dive into it yourself you can simply put a breakpoint at the redrawing function and look at the backtrace to figure out what causes the 2nd redraw.

Share this post


Link to post
Share on other sites

Here is a working build on Win32

Some notes on usability:

  • Make sure to change the path to your ugfx library in Makefile (sorry for stating the obvious, this message is for anyone else in the future).
  • The project has 6 pages numbered 00 to 05.
    • Page 00 is displayed at startup
    • Pages 01 to 04 are displayed using the 4 clickable zones in the sidebar on the right. These zones are not currently delimited graphically, it's something I will fix later by applying a texture to the container background but it is low priority.
    • Page 05 is not reachable currently.
  • The battery icon changes between 25% and 100% based on the page number, obviously later I must connect this to a real notification of battery % coming from another peripheral.
  • The battery % text is not implemented. Just saying so you don't think it's a bug.

Since this is for an embedded platform I kept images in the C-style format, not using the .bmp directly. These were generated using file2c with -d option.

As you'll notice, there is no visible glitch between page transitions.

Something I noticed for the x86 version is that geventEventWait returns a null pointer on timeout when there is no event, triggering a segfault. I made a small modification to avoid this:

pe = geventEventWait(&glistener, 0);
if(pe == NULL){
    continue;
}

 

05-ugfx-template-x86.zip

 

Thanks for bearing with me, I'll admit I hacked this together overnight (and the x86 version just now) so not properly documented but I think you'll get the general idea of what this is about.

best regards,

Sébastien

Edited by sdawans

Share this post


Link to post
Share on other sites

I didn't step through the code yet simply because I don't have a proper debugging setup yet, having some issues with the Atmel EDBG thing that I'll look into now.
In the meantime I did browse through some of the code involved in redrawing and I noticed that #define GWIN_REDRAW_IMMEDIATE TRUE fixes my problem, although other glitches appear such as the sidebar widgets being hidden under the buttons.

Will get back when I get a working debugging setup (should have done that before posting, sorry).

Edited by sdawans

Share this post


Link to post
Share on other sites

The reason your geventEventWait is returning NULL is that you have passed in 0 which is TIME_IMMEDIATE. You are effectively telling it to not wait for an event, so if there is no event ready it returns 0. If you pass TIME_FOREVER (Ithink that is what it is called) you will not get back a NULL, instead it will wait for an event. Any value in between those numbers is the maximum milliseconds to wait for the event.

Share this post


Link to post
Share on other sites

Hi Joel,

I did realize the second parameter was a timeout and there is no reason for me to use anything other than TIME_INFINITE in my case. What I meant to say is that accessing the null pointer on the x86 build does not behave in the same way as the embedded platform so I added the safeguard to allow it to run. I mentioned it to point exhaustively all the differences between my embedded code and this one. 

Cheers,

Sébastien

Share this post


Link to post
Share on other sites

Hi Guys.

Going back to the original question - did you solve the problem of the double redraw? I'm having something similar on multi-container UIs. Switching between 'screens' using the guiShowPage(...) type approach generates two redraws, the first after the gwinHide, the second after the gwinShow I think. You mention trying different GWIN_REDRAW_IMMEDIATE modes, what did you conclude?

Many thanks

Share this post


Link to post
Share on other sites

Hello,

I found a workaround for the double redraw issue and thought I would share.
I'm working on an STM32L4 MCU running FreeRTOS.
My workaround basically consist in modifying the guiShowPage function this way:

void guiShowPage(unsigned pageIndex)
{
	// Hide all pages
	gwinHide( page1 );
	gwinHide( page2 );
	gwinHide( page3 );
	/* ... */

	// Allow the rendering thread to clear the screen
	gfxYield();				// ADD THIS LINE!

	// Show page selected page
	switch ( pageIndex ) {
	case 0:
		gwinRaise( page1 );		// ADD THIS LINE!
		gwinShow( page1 );
		break;

	case 1:
		gwinRaise( page2 );		// ADD THIS LINE!
		gwinShow( page2 );
		break;

    /* ... */

	default:
		break;
	}
}

I hope this will help someone else in the future

Edited by Matteo Vigni

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...