Microchip-MPLAB-Harmony / gfx

Harmony 3 Graphics library
https://microchip-mplab-harmony.github.io/gfx/
Other
16 stars 14 forks source link

Legato_Tasks() preemption #40

Open ghnicolas opened 3 months ago

ghnicolas commented 3 months ago

Hello, I am using the library on a PIC32MZDA without OS, and trying to optimize everything I can to fulfil other real time constraints like CAN periodicity messages. Up to here I don't, the Legato_Tasks() at the preemtion level 1 or 2 keeps the hand during more than 100 ms to paint image widget of 128x128 pixels in RGBA8888 mode. I am using the stream interface, but the time needed to stream image from external memory seems not significant in the complete drawing operation. Looking into the code I wonder if one of the preemption define (LE_PREEMPTION_LEVEL) used to avoid blocking was correctly placed. The one in the function _leImageWidget_Paint (legato_widget_image_skin_classic.c) seems to be useless as a deeper function is actually blocking. The deeper function blocking is the _exec function in file legato_imagedecoder_raw.c (while loop). Is there a way to make this function not blocking ? Regards Nicolas

ghnicolas commented 3 months ago

Maybe unlocking the function after the cache buffer have been completely treated (before returning back to stream a new cache bufffer from external memory) could be great. Users could tune the preemption time depending on the size of the cache buffer.

microchip-gfx commented 3 months ago

To make the raw image decoder loop non-blocking, you should be able to add:

#if LE_PREEMPTION_LEVEL > 0
        return LE_SUCCESS;
#endif

to the end of the loop like so:

// any stage can set done and exit the loop
    while(_state.done == LE_FALSE)
    {
        // stages can return false if they stall
        if(_state.stages[_state.currentStage]->exec(_state.stages[_state.currentStage]) == LE_FAILURE)
            return LE_SUCCESS;

        // increment stage
        _state.currentStage++;

        if(_state.stages[_state.currentStage] == NULL)
        {
            // restart the pipeline
            _state.currentStage = 0;
        }

#if LE_PREEMPTION_LEVEL > 0
        return LE_SUCCESS;
#endif
    }
ghnicolas commented 3 months ago

Thanks for your answer. Based on your suggestion I return LE_SUCCESS but when the pipeline is finished (stages var is null) and if a 5 ms time limit has elapsed. Works ok, 5 ms tolerance on TX message is ok, and UI is quite responsive.

MCHP-MCU32DC0 commented 3 months ago

Hi Nicolas - a few things to try:

  1. Optimize your design - refer to these guides: UI optimization
  2. If CAN comms and tasks need higher priority over the GUI, you can consider using an RTOS (e.g., FreeRTOS) so you can prioritize the CAN application task using the RTOS scheduler.
ghnicolas commented 3 months ago

Hello Ed, Finally unselecting "Update on VBLANK" in the LE GLCD harmony configuration solved that problem. For people reading the thread, despite the good solution to break the image drawing preemting pipeline, I had still another ~10-15 ms preemption each time a widget image was updated. Could you explain the difference of choosing "Update on VBLANK" or not please. What is intended by "update" ? Looking at the code it changes the vblankSync bool to true (when set) and false (when not set), which is used in the GLCD interrupt routine GLCD_Interrupt_Handler : if (vblankSync && PLIB_GLCD_IsVerticalBlankingActive() == true) return;
Also as the interrupt is triggered by VSYNC, I don't understand how PLIB_GLCD_IsVerticalBlankingActive() could return false at that moment. Thanks