Search code examples
cmultithreadingopenglsdl-2

SDL2 rendering with multithreading on Raspberry Pi 2


So, we're trying to build a Real-Time System with preemption within a process, executing each task as a separate thread. To build the GUI, SDL was the library chosen and we've separated the GUI initialization from the rendering so that the latter could be a different task. Even though SDL has threads itself, we used pthreads. Information found is contradictory as some sources state that rendering cannot be executed within a different process and others stating otherwise. However, using a virtual machine with Debian, the rendering was executed properly but doing so in the Raspberry Pi, through changing contexts betweeen the main and the rendering thread, SDL_GetError() does not return error but does not render anything either leading to a black window with a cursor. It is important to state that rendering within the main thread works as intended both in the Pi and the virtual machine.

In order to solve this issue, several alternatives were used in the Pi and SDL configuration. Regarding the Pi itself, using sudo raspi-config, both Full and Fake KMS to enable OpenGL do not work with different errors being presented:

Full KMS - Could not initialize OpenGL / GLES library*

Fake KMS - * failed to add service - already in use? (keyboard stops working).

OpenGL disabled (origin) - No error, black window unrendered.

Following, the code is presented per threads:

Main thread:

SDL_Window* GUI_init(int w, int h) {
  if (SDL_Init(SDL_INIT_VIDEO) != 0) {
    fprintf(stderr, "Cannot initialise SDL: %s\n", SDL_GetError());
    exit(1);
  }

  window = SDL_CreateWindow(
    "Tron", 
    SDL_WINDOWPOS_CENTERED, 
    SDL_WINDOWPOS_CENTERED, 
    w, h,
    SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL
  );
  if (window == NULL) {
    fprintf(stderr, "Unable to create window: %s\n", SDL_GetError());
    exit(1);
  }

  globalRenderer = SDL_CreateRenderer(
    window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC
  );
  if (globalRenderer == NULL) {
    fprintf(stderr, "Unable to create renderer: %s\n", SDL_GetError());
    exit(1);
  }

  if (SDL_SetRenderDrawBlendMode(globalRenderer, SDL_BLENDMODE_BLEND) != 0) {
    fprintf(stderr, "SDL_BlendMode failed: %s\n", SDL_GetError());
    exit(1);
  };

  ctx = SDL_GL_CreateContext(window);
  if(ctx == NULL) {
    fprintf(stderr, "Unable to create context: %s\n", SDL_GetError());
    exit(1);
  }

  int ctx1 = SDL_GL_MakeCurrent(window,NULL);
  if(ctx1!=0) {
    fprintf(stderr, "Unable to make currents context: %s\n", SDL_GetError());
    exit(1);
  }

  //return globalRenderer;
  return window;
}

Rendering Thread:

void* GUI_update(void* params) {
  SDL_GL_MakeCurrent(window,ctx);

  GUI_setRenderDrawColor(globalRenderer);

  SDL_RenderClear(globalRenderer);

  GUI_fillBoardBorders(globalRenderer);
  GUI_fillBoard(globalRenderer);

  SDL_RenderPresent(globalRenderer);
}

with use of 3 global variables:

SDL_Window *window = NULL;
SDL_GLContext ctx = NULL;
SDL_Renderer* globalRenderer = NULL;

We're using Raspbian Jessie Lite as the OS. What could be the issue here?


Solution

  • SDL_Renderer top-level documentation:

    This API is not designed to be used from multiple threads, see SDL bug #986 for details.

    More authoritatively:

    /**
     *  \file SDL_render.h
     *
     *  Header file for SDL 2D rendering functions.
     *
     *  This API supports the following features:
     *      * single pixel points
     *      * single pixel lines
     *      * filled rectangles
     *      * texture images
     *
     *  The primitives may be drawn in opaque, blended, or additive modes.
     *
     *  The texture images may be drawn in opaque, blended, or additive modes.
     *  They can have an additional color tint or alpha modulation applied to
     *  them, and may also be stretched with linear interpolation.
     *
     *  This API is designed to accelerate simple 2D operations. You may
     *  want more functionality such as polygons and particle effects and
     *  in that case you should use SDL's OpenGL/Direct3D support or one
     *  of the many good 3D engines.
     *
     *  These functions must be called from the main thread.
     *  See this bug for details: http://bugzilla.libsdl.org/show_bug.cgi?id=1995
     */
    

    Also, don't try to use OpenGL & a SDL_RENDERER_ACCELERATED SDL_Renderer at the same time. SDL_Renderer doesn't provide any way to save/restore the OpenGL state it might be using.