0

There is a problem with rendering target switch on Raspberry Pi. SDL_SetRenderTarget() hangs if multiple renderer switch occurs.

This is a working code that prints milliseconds since application starts before and after a rendering target switch:

#include <stdio.h>
#include "SDL.h"


static SDL_Window* gameWindow = NULL;
static SDL_Renderer* windowRenderer = NULL;
static SDL_Texture* gameTexture = NULL;
static SDL_Texture* texture_1 = NULL;
static SDL_Texture* texture_2 = NULL;
static SDL_Texture* texture_3 = NULL;
static SDL_Texture* texture_4 = NULL;
static SDL_Texture* texture_5 = NULL;
static int row;
static int column;


int main()
{
    // Initialize SDL Video
    if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
    {
        printf("%d ms - [INFO] - SDL fails to initialize video subsystem!\n%s", SDL_GetTicks(), SDL_GetError());
        return -1;
    }
    else
        printf("%d ms - [INFO] - SDL Video was initialized fine!\n", SDL_GetTicks());

    // Create window
    gameWindow = SDL_CreateWindow(
                "SDL Render Target Test",
                SDL_WINDOWPOS_CENTERED,
                SDL_WINDOWPOS_CENTERED,
                640,
                480,
                SDL_WINDOW_SHOWN);

    windowRenderer = SDL_CreateRenderer(gameWindow, -1, SDL_RENDERER_ACCELERATED);

    if (windowRenderer == NULL)
        return -1;

    // Set color (this format is RGBA) --> Black, Opaque
    SDL_SetRenderDrawColor(windowRenderer, 0x00, 0x00, 0x00, 0xff);

    // Clear renderer
    SDL_RenderClear(windowRenderer);

    printf("Window and renderer created!\n");

    gameTexture = SDL_CreateTexture(windowRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 320, 240);
    texture_1 = SDL_CreateTexture(windowRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, 1920, 1080);
    texture_2 = SDL_CreateTexture(windowRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, 1920, 1080);
    texture_3 = SDL_CreateTexture(windowRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, 1920, 1080);
    texture_4 = SDL_CreateTexture(windowRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, 1920, 1080);
    texture_5 = SDL_CreateTexture(windowRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, 1920, 1080);

    printf("Textures created!\n");

    if (gameTexture == NULL || texture_1 == NULL || texture_2 == NULL || texture_3 == NULL || texture_4 == NULL || texture_5 == NULL)
    {
        SDL_DestroyRenderer(windowRenderer);
        windowRenderer = NULL;

        return -1;
    }

    // Set textures blend mode
    SDL_SetTextureBlendMode(gameTexture, SDL_BLENDMODE_BLEND);
    SDL_SetTextureBlendMode(texture_1, SDL_BLENDMODE_BLEND);
    SDL_SetTextureBlendMode(texture_2, SDL_BLENDMODE_BLEND);
    SDL_SetTextureBlendMode(texture_3, SDL_BLENDMODE_ADD);
    SDL_SetTextureBlendMode(texture_4, SDL_BLENDMODE_BLEND);
    SDL_SetTextureBlendMode(texture_5, SDL_BLENDMODE_MOD);

    printf("Textures blend set!\n");


    // +++ Texture 1 +++ //

    printf("%d ms - [INFO] - Set renderer...\n", SDL_GetTicks());

    // Set target for drawing operation on texture_1
    SDL_SetRenderTarget(windowRenderer, texture_1);

    printf("%d ms - [INFO] - Renderer set!\n", SDL_GetTicks());

    // Set color (this format is RGBA) --> Black, Transparent
    SDL_SetRenderDrawColor(windowRenderer, 0x00, 0x00, 0x00, 0x00);

    // Clear texture_1
    SDL_RenderClear(windowRenderer);

    // Set color (this format is RGBA) --> Black, With some transparency
    SDL_SetRenderDrawColor(windowRenderer, 0x00, 0x00, 0x00, 0x5a); // SDL_BLENDMODE_BLEND

    for (row = 0; row < 1080; row += 4)
        SDL_RenderDrawLine(windowRenderer, 0, row, 1920, row);

    printf("Texture 1 created!\n");


    // +++ Texture 2 +++ //

    printf("%d ms - [INFO] - Set renderer...\n", SDL_GetTicks());

    // Set target for drawing operation on texture_2
    SDL_SetRenderTarget(windowRenderer, texture_2);

    printf("%d ms - [INFO] - Renderer set!\n", SDL_GetTicks());

    for (column = 0; column < 1920; column += 4)
    {
        // SDL_BLENDMODE_BLEND

        // Set color (this format is RGBA)
        SDL_SetRenderDrawColor(windowRenderer, 0x00, 0x00, 0x00, 0xff);
        SDL_RenderDrawLine(windowRenderer, column, 0, column, 1080);

        // Set color (this format is RGBA)
        SDL_SetRenderDrawColor(windowRenderer, 0xff, 0x00, 0x00, 0xff);
        SDL_RenderDrawLine(windowRenderer, column + 1, 0, column + 1, 1080);

        // Set color (this format is RGBA)
        SDL_SetRenderDrawColor(windowRenderer, 0x00, 0xff, 0x00, 0xff);
        SDL_RenderDrawLine(windowRenderer, column + 2, 0, column + 2, 1080);

        // Set color (this format is RGBA)
        SDL_SetRenderDrawColor(windowRenderer, 0x00, 0x00, 0xff, 0xff);
        SDL_RenderDrawLine(windowRenderer, column + 3, 0, column + 3, 1080);
    }

    printf("Texture 2 created!\n");


    // +++ Texture 3 for brightness +++ //

    printf("%d ms - [INFO] - Set renderer...\n", SDL_GetTicks());

    // Set target for drawing operation on texture_3
    SDL_SetRenderTarget(windowRenderer, texture_3);

    printf("%d ms - [INFO] - Renderer set!\n", SDL_GetTicks());

    // Set color (this format is RGBA)
    SDL_SetRenderDrawColor(windowRenderer, 0xff, 0xff, 0xff, 0xd0); // SDL_BLENDMODE_ADD

    // Clear texture_3
    SDL_RenderClear(windowRenderer);

    printf("Texture 3 created!\n");


    // +++ Combine textures to texture_5 +++ //

    printf("%d ms - [INFO] - Set renderer...\n", SDL_GetTicks());

    // Set target for drawing operation on texture_5
    SDL_SetRenderTarget(windowRenderer, texture_5);

    printf("%d ms - [INFO] - Renderer set!\n", SDL_GetTicks());

    // Set color (this format is RGBA) --> Black, Opaque
    SDL_SetRenderDrawColor(windowRenderer, 0x00, 0x00, 0x00, 0xff);

    // Clear texture_5
    SDL_RenderClear(windowRenderer);

    printf("Start texture combination!\n");

    // Render texture_2 to texture_5
    SDL_RenderCopy(windowRenderer, texture_2, NULL, NULL);

    printf("First combination done!\n");

    SDL_DestroyTexture(texture_2);


    // +++ Texture 4 +++ //

    printf("%d ms - [INFO] - Set renderer...\n", SDL_GetTicks());

    // Set target for drawing operation on texture_4
    SDL_SetRenderTarget(windowRenderer, texture_4);

    printf("%d ms - [INFO] - Renderer set!\n", SDL_GetTicks());

    // Set color (this format is RGBA) --> Black, Transparent
    SDL_SetRenderDrawColor(windowRenderer, 0x00, 0x00, 0x00, 0x00);

    // Clear texture_4
    SDL_RenderClear(windowRenderer);

    // Set color (this format is RGBA)
    SDL_SetRenderDrawColor(windowRenderer, 0x00, 0x00, 0x00, 0x15); // SDL_BLENDMODE_BLEND

    for (column = 0; column < 1920; column += 4)
    {
        SDL_RenderDrawLine(windowRenderer, column, 0, column, 1080);
    }

    for (row = 0; row < 1080; row += 4)
    {
        for (column = 1; column < 1920; column += 8)
        {
            SDL_RenderDrawPoint(windowRenderer, column, row);
            SDL_RenderDrawPoint(windowRenderer, column + 1, row);
            SDL_RenderDrawPoint(windowRenderer, column + 2, row);
            SDL_RenderDrawPoint(windowRenderer, column + 3, row);

            SDL_RenderDrawPoint(windowRenderer, column + 4, row + 2);
            SDL_RenderDrawPoint(windowRenderer, column + 4 + 1, row + 2);
            SDL_RenderDrawPoint(windowRenderer, column + 4 + 2, row + 2);
            SDL_RenderDrawPoint(windowRenderer, column + 4 + 3, row + 2);
        }
    }

    printf("Texture 4 created!\n");


    // +++ Combine texture to texture_5 +++ //

    printf("%d ms - [INFO] - Set renderer...\n", SDL_GetTicks());

    // Set target for drawing operation on texture_5
    SDL_SetRenderTarget(windowRenderer, texture_5);

    printf("%d ms - [INFO] - Renderer set!\n", SDL_GetTicks());

    printf("Start texture combination!\n");

    // Render texture_3 to texture_5
    SDL_RenderCopy(windowRenderer, texture_3, NULL, NULL);

    printf("Second combination done!\n");

    SDL_DestroyTexture(texture_3);

    printf("Texture 5 created!\n");


    // +++ Set default rendering target +++ //

    printf("%d ms - [INFO] - Set renderer...\n", SDL_GetTicks());

    // Set target for drawing operation on default rendering target
    SDL_SetRenderTarget(windowRenderer, NULL);

    printf("%d ms - [INFO] - Renderer set!\n", SDL_GetTicks());

    // Set color (this format is RGBA) --> Black, Transparent
    SDL_SetRenderDrawColor(windowRenderer, 0x00, 0x00, 0x00, 0x00);

    // Clear default renderer
    SDL_RenderClear(windowRenderer);

    SDL_DestroyTexture(texture_1);
    SDL_DestroyTexture(texture_4);
    SDL_DestroyTexture(texture_5);

    SDL_DestroyRenderer(windowRenderer);

    SDL_DestroyWindow(gameWindow);

    return 0;
}

This is the ouptut on Ubuntu x86:

108 ms - [INFO] - SDL Video was initialized fine!
Window and renderer created!
Textures created!
Textures blend set!
252 ms - [INFO] - Set renderer...
253 ms - [INFO] - Renderer set!
Texture 1 created!
253 ms - [INFO] - Set renderer...
254 ms - [INFO] - Renderer set!
Texture 2 created!
255 ms - [INFO] - Set renderer...
257 ms - [INFO] - Renderer set!
Texture 3 created!
257 ms - [INFO] - Set renderer...
257 ms - [INFO] - Renderer set!
Start texture combination!
First combination done!
258 ms - [INFO] - Set renderer...
258 ms - [INFO] - Renderer set!
Texture 4 created!
375 ms - [INFO] - Set renderer...
450 ms - [INFO] - Renderer set!
Start texture combination!
Second combination done!
Texture 5 created!
450 ms - [INFO] - Set renderer...
450 ms - [INFO] - Renderer set!

and this one is the result on Raspberry Pi:

94 ms - [INFO] - SDL Video was initialized fine!
Window and renderer created!
Textures created!
Textures blend set!
242 ms - [INFO] - Set renderer...
242 ms - [INFO] - Renderer set!
Texture 1 created!
243 ms - [INFO] - Set renderer...
286 ms - [INFO] - Renderer set!
Texture 2 created!
288 ms - [INFO] - Set renderer...
386 ms - [INFO] - Renderer set!
Texture 3 created!
386 ms - [INFO] - Set renderer...
386 ms - [INFO] - Renderer set!
Start texture combination!
First combination done!
396 ms - [INFO] - Set renderer...
415 ms - [INFO] - Renderer set!
Texture 4 created!
837 ms - [INFO] - Set renderer...
24786 ms - [INFO] - Renderer set!
Start texture combination!
Second combination done!
Texture 5 created!
24787 ms - [INFO] - Set renderer...
24787 ms - [INFO] - Renderer set!

The application hangs for 23 seconds!!! Any idea? Also on Ubuntu x86 there is a slowdown after message Texture 4 created! during rendering set

Francesco
  • 523
  • 4
  • 25
  • After investigation I found that the problem is the use of SDL_RenderDrawPoint(). If we move Texture 4 creation at the beginning of the code the slowdown occurs after that texture creation, so after a massive use of SDL_RenderDrawPoint(). Why? – Francesco May 29 '21 at 20:16
  • Have you tried flipping the [`SDL_RENDER_BATCHING`](https://github.com/libsdl-org/SDL/blob/main/include/SDL_hints.h#L1421-L1443) envvar/hint on/off? – genpfault May 30 '21 at 00:08
  • 1
    With `SDL_SetHintWithPriority(SDL_HINT_RENDER_BATCHING, "1", SDL_HINT_OVERRIDE);` application hangs for 23 seconds (default behaviour) but with `SDL_SetHintWithPriority(SDL_HINT_RENDER_BATCHING, "0", SDL_HINT_OVERRIDE);` it hangs for 76 seconds! – Francesco May 30 '21 at 06:39
  • Might try building SDL with debug symbols and running [`perf`](https://en.wikipedia.org/wiki/Perf_%28Linux%29) over it and your app to help identify callstacks in SDL that are absorbing all the time. – genpfault May 30 '21 at 17:22
  • 1
    Might also try the manually-batched variant of `SDL_RenderDrawPoint()`, [`SDL_RenderDrawPoints()`](https://github.com/libsdl-org/SDL/blob/main/include/SDL_render.h#L1021-L1044). – genpfault May 30 '21 at 17:24
  • Also, try to make sure you're using the OpenGL ES SDL_Renderer backend on the Pi and not the regular desktop OpenGL one; saves a translation layer. See [this demo program](https://stackoverflow.com/a/57684805/44729) for some envvars/hints you can set to control that. – genpfault May 30 '21 at 17:26
  • Compiled SDL 2.0.14 with `Video drivers : dummy rpi x11(dynamic) kmsdrm(dynamic) opengl opengl_es1 opengl_es2 vulkan`, running app with `SDL_VIDEODRIVER=KMSDRM ./SDLTest` but it fails with `KMSDRM not available` – Francesco May 31 '21 at 19:26

1 Answers1

0

After further investigation I found that the problem is the use of SDL_RenderDrawPoint(). If we move Texture 4 creation at the beginning of the code the slowdown occurs after that texture creation, so after a massive use of SDL_RenderDrawPoint().

I fixed the problem passing from SDL_RenderDrawPoint() to SDL_RenderDrawPoints().

In my case it is ok because that texture is created only one time at startup and in this way we pass from 23 seconds to 200 milliseconds

Francesco
  • 523
  • 4
  • 25