cancel
Showing results for 
Search instead for 
Did you mean: 

Issues with SVG image rendering

apadom
Associate III

Hi,

I have a project derived from the demo6 from ST on the STM32U5G9J-DK1 devkit, where I want to display SVG images. The only difference is that the rounded display of the original kit has been substituted with another rounded display of the same resolution (480x480 pixels) but smaller. The PCB of the display is then different as well as the display driver chip which is driven through MIPI-DSI commands (and not using video mode of MIPI-DSI like the original demo is). I'm using latest version of TGFX 4.25.0.

All the subdemos of this demo6 project are working the same as on the original display, but not the SVG subdemo. All the .SVG images of this specific sub demo, which should animate and start bouncing on the screen, are simply not rendered at all. I only see the background image which is a .PNG file. And I can come back on the main screen as the touch display is working.

I tried to strip down this demo6 project and display a simple background .PNG image and on top of this last, some .SVG images. The background image is again displayed as it should, but none of the .SVG images.

As I've seen some other having difficulties with .SVG images I tried to change the portrait/landscape mode, without seeing any change. I tried to modify the canvas buffer size, and I didn't see any change. I checked the code inside MX_DCACHE2_Init() function and I have the two lines which were advised there.

I'm running out of ideas on the reasons why .SVG images cannot be rendered. So far, the only difference I can see is around these MIPI-DSI commands usage but since all the rest of subdemos are working as expected, I don't think that this can be related. There must be something not going well before this stage, when all the images should be rendered inside the framebuffer. But I don't understand why this is related to .SVG images rendering.

Any idea or hint are welcome.

31 REPLIES 31

@apadom wrote:

@mathiasmarkussen 

That said, I'm discovering as well that our project is based on ThreadX instead of FreeRTOS. 


You can run TouchGFX entirely without an OS. 

In our application we don't use an OS. We run our idle loop when TouchGFX is "busy". By default this runs a while(1) loop. But we added an idle thread where we run the rest of the code. You can easily port TouchGFX to another OS.
You claim your project was stripped, but it sounds like it's not stripped enough. I would start with a working TouchGFX and then add the rest of your code.

Kudo posts if you have the same problem and kudo replies if the solution works.
Click "Accept as Solution" if a reply solved your problem. If no solution was posted please answer with your own.

@mathiasmarkussen 
I noticed that these specific sections which are normally described in the linkerscript, are not used in our case. The LOCATION_PRAGMA_NOLOAD( ) is undefined for GCC anyway. This is only used by Keil and IAR compilers if I'm not mistaken.

Instead I have static allocations defined as such in nema_hal.c:

LOCATION_PRAGMA_NOLOAD("Nemagfx_Memory_Pool_Buffer")
static uint8_t nemagfx_pool_mem[NEMAGFX_MEM_POOL_SIZE] LOCATION_ATTRIBUTE_NOLOAD("Nemagfx_Memory_Pool_Buffer"); /* NemaGFX memory pool */
LOCATION_PRAGMA_NOLOAD("Nemagfx_Stencil_Buffer")
static uint8_t nemagfx_stencil_buffer_mem[NEMAGFX_STENCIL_POOL_SIZE] LOCATION_ATTRIBUTE_NOLOAD("Nemagfx_Stencil_Buffer"); /* NemaGFX stencil buffer memory */

And this is both true for the genuine demo6 and our modified project. 
Same logic for the frame buffer:

namespace
{
// Use the section "TouchGFX_Framebuffer" in the linker script to specify the placement of the buffer
LOCATION_PRAGMA_NOLOAD("TouchGFX_Framebuffer")
uint32_t frameBuf[(480 * 480 * 3 + 3) / 4 * 2] LOCATION_ATTRIBUTE_NOLOAD("TouchGFX_Framebuffer");
static uint16_t lcd_int_active_line;
static uint16_t lcd_int_porch_line;
}

So I don't think that this linkerscript definition has an impact for the SVG rendering issue, because anyway those buffers are declared and allocated at some point in RAM. There's maybe a question of bytes alignment but even regarding this, when looking at the code of nema_sys_init():

int32_t nema_sys_init(void)
{
 ...
    /* Initialise Mem Space */
    error_code = tsi_malloc_init_pool_aligned(0, (void*)nemagfx_pool_mem, (uintptr_t)nemagfx_pool_mem, NEMAGFX_MEM_POOL_SIZE, 1, 8);
    assert(error_code == 0);
    error_code = tsi_malloc_init_pool_aligned(1, (void*)nemagfx_stencil_buffer_mem, (uintptr_t)nemagfx_stencil_buffer_mem, NEMAGFX_STENCIL_POOL_SIZE, 1, 8);
    assert(error_code == 0);

    /* Allocate ring_buffer memory */
    ring_buffer_str.bo = nema_buffer_create(RING_SIZE);
    assert(ring_buffer_str.bo.base_virt);

    /* Initialize Ring Buffer */
    error_code = nema_rb_init(&ring_buffer_str, 1);
    if (error_code < 0)
    {
        return error_code;
    }
 ...
}

These calls to tsi_malloc_init_pool_aligned() are ensuring that memory pools are allocated and aligned.

The only side effect is that, compared to these sections in linkescript with "NOLOAD", the data might be initialized at 0 if not part of .bss. So it's not optimal, it may take a bit more time at startup, but this shouldn't be an issue vs SVG rendering.

So this missing section in linkerscript for these buffers shouldn't be an issue, do you agree?

Once thing I found odd is comparing TouchGFX\Target\generated\TouchGFXConfiguration.cpp

In the genuine demo6 code I have:

void touchgfx_components_init()
{
    nema_init();
    nema_reg_write(0xFFC, 0x7E); /* Enable bus error interrupts */
    nema_vg_init_stencil_pool(480, 480, 1);
    nema_vg_handle_large_coords(1, 1);
}

while in modified demo6 code I have:

void touchgfx_components_init()
{
    nema_init();
    nema_vg_init_stencil_pool(480, 480, 1);
    nema_vg_handle_large_coords(1, 1);
}

And I guess that the missing enabling of the bus error IT is the one you mentioned in an earlier post, and which is used for initiliazing SVG rendering?


That interrupt is not only used for errors, and is needed to initialize the SVG rendering logic

Is it related or it's something else in the end?

mathiasmarkussen
ST Employee

You can certainly add that line to your project, it would definitely not hurt. But as far as I remember this is related to us finding that the interrupt was not setup correctly to actually fire on some relevant errors, not related to this.

How do you update the screen? Have you set a window on the entire screen and sending the entire frame buffer on every vsync, or are you doing something else?

apadom
Associate III

@mathiasmarkussen 

Have you set a window on the entire screen and sending the entire frame buffer on every vsync

Yes, we have a TE (tearing effect) line coming back from the display module.
DSI interrupt is enabled to handle this TE signal. On every possible screen update we send one of the two complete buffers.
The MIPI-DSI works at 30fps.

Inside: \TouchGFX\Target\TouchGFXHAL.cpp

extern "C" {

    void HAL_DSI_TearingEffectCallback(DSI_HandleTypeDef* hdsi)
    {
        GPIO::set(GPIO::VSYNC_FREQ);  // sets the GPIO for debugging and performance measurement
        HAL::getInstance()->vSync();  // counts vSync

        // signalVSync should be called from the display driver when the display is ready for the next frame.
        // VSYNC period indicates the end of the actual frame display and that the two buffers need to be swapped.
        OSWrappers::signalVSync();

        if(refreshRequested && !displayRefreshing)
        {
            refreshRequested = false;
            if(frontBufferIdx < 0){
                // swap the frame buffers
                GPIO::toggle(GPIO::MCU_ACTIVE);  // used for debugging frame buffer swap
                frontBufferIdx = backBufferIdx;
                backBufferIdx = 1 - frontBufferIdx;
            }
            // Transfer pixel data.
            HAL_DSI_Refresh(hdsi);
            displayRefreshing = true;
        }else{
            if(!displayRefreshing){
                GPIO::clear(GPIO::VSYNC_FREQ);  // clear the GPIO for debugging and performance measurement
            }
        }
    }

    void HAL_DSI_EndOfRefreshCallback(DSI_HandleTypeDef* hdsi)
    {
        displayRefreshing = false;
        if(frontBufferIdx >= 0)
        {
            // set the LTDC frame buffer in preparation for the next transmission
            // of pixel data by the DSI host
            setLtdcFrameBuffer(frameBufferAddresses[frontBufferIdx]);
            frontBufferIdx = -1;
        }
        GPIO::clear(GPIO::VSYNC_FREQ);  // clear the GPIO for debugging and performance measurement
    }

    void HAL_LTDC_ErrorCallback(LTDC_HandleTypeDef *hltdc){
        // set the red LED in case of error
        HAL_GPIO_WritePin(LED_RED_GPIO_Port , LED_RED_Pin, GPIO_PIN_SET);
    }

    void HAL_DSI_ErrorCallback(DSI_HandleTypeDef *hdsi)
    {
        // set the red LED in case of error
        HAL_GPIO_WritePin(LED_RED_GPIO_Port , LED_RED_Pin, GPIO_PIN_SET);
    }

    void setLtdcFrameBuffer(uint32_t adr)
    {
        // sets the LTDC frame buffer
        // this frame buffer will be transmitted by the DSI host
        __HAL_DSI_WRAPPER_DISABLE(&hdsi);
        LTDC_LAYER(&hltdc, 0)->CFBAR = adr;
        __HAL_LTDC_RELOAD_CONFIG(&hltdc);
        __HAL_DSI_WRAPPER_ENABLE(&hdsi);
    }

} // extern c

But most probably the SVG renderer doesn't produce anything in any of these two buffers.

I hope to get my hardware issue fixed by tomorrow so I can really try new things on the target.

apadom
Associate III

@mathiasmarkussen 
My hardware is now fixed, so I could test again your hints. Unfortunately, it doesn't change the behaviour.
I did a very simple application with a background containing a .PNG image and on top of this last a .SVG with the preexisting SVG assets of TGFX (the TGFX logo).

On simulator everything is rendered. On target only the background appears.

We have an NDA signed with you. How can I privately send you a zip archive of our code?

have you tried rendering a touchgfx shape?

Kudo posts if you have the same problem and kudo replies if the solution works.
Click "Accept as Solution" if a reply solved your problem. If no solution was posted please answer with your own.

@unsigned_char_array 
Thank you for your suggestion. I added a circle shape to my project and it's rendered as expected on the target.
Would this mean that anything linked to vector rendering would finally work at HW level?


@apadom wrote:

@unsigned_char_array 
Thank you for your suggestion. I added a circle shape to my project and it's rendered as expected on the target.
Would this mean that anything linked to vector rendering would finally work at HW level?


I guess. But I don't have enough knowledge of the internal workings of the vector library. But to my understanding the vector library works by rendering primitive shapes.

Kudo posts if you have the same problem and kudo replies if the solution works.
Click "Accept as Solution" if a reply solved your problem. If no solution was posted please answer with your own.
mathiasmarkussen
ST Employee

TouchGFX shapes are rendered in software.

@apadom I've sent you a message through the forum, can you attach the project there, then I'll have a look?

mathiasmarkussen
ST Employee

One thing you could try is to simplify your transfer setup. A setup similar to yours is used for the current board setup for STM32F4769-DK.

TouchGFX sets the LTDC framebuffer base on vsync using TouchGFXGeneratedHAL::setTFTFrameBuffer(address).

You should basically be able to replace the code you pasted above with this:

void HAL_DSI_TearingEffectCallback(DSI_HandleTypeDef* hdsi)
{
    GPIO::set(GPIO::VSYNC_FREQ);
    HAL::getInstance()->vSync();
    OSWrappers::signalVSync();
    if (refreshRequested)
    {
        // Start transfer
        HAL_DSI_Refresh(hdsi);
        refreshRequested = false;
    }
    else
    {
        GPIO::clear(GPIO::VSYNC_FREQ);
    }
}

void HAL_DSI_EndOfRefreshCallback(DSI_HandleTypeDef* hdsi)
{
    GPIO::clear(GPIO::VSYNC_FREQ);
}

 Edit: I see you have replaced the call to TouchGFXGeneratedHAL::setTFTFrameBuffer(), but the actual call from the framework is to setLtdcFrameBuffer(uint32_t adr) in TouchGFXHAL.cpp, so you do track that correctly, and the code above should work still.

You should not call setLtdcFrameBuffer() manually.

This may actually be your issue, the frame buffers are swapped twice on every frame, so the copy of SVGs from the stencil buffer to the framebuffer happens after your manual swap.