SDL2/OpenGL Threads



  • Einen guten Morgen wünsche ich,

    ich setze mich im Moment ein wenig mit C + SDL2 + OpenGL auseinander und habe auch ein wenig recherchiert, ob ich das Rendering in einen anderen Thread verschieben kann.

    In mehreren Einträgen die ich gelesen habe, viel davon in SO, wird immer nur darauf hingewiesen, dass es unsinnig sei und kein Performance Gewinn dabei heraus kommt, wenn man das Rendering auf Threads auslagert. Für mich hat es sich so gelesen, dass man jedoch versucht hat, von mehreren Threads in einen OpenGL Context zu rendern. Was für mich auch keinen Sinn macht.

    Ich möchte einfach das rendern vollständig in einen anderen Thread schieben, damit der Mainloop immer responsiv bleibt, selbst wenn der Renderthread etwas langsamer ist.

    Ich habe es auch geschafft, das rendern in einen anderen Thread zu schieben, die Frage ist jedoch: Sollte ich das überhaupt tun? In einer Quelle die ich gelesen habe (habe den Link aktuell nicht mehr, kann ich aber nochmal suchen falls gewünscht) wird behauptet, dass ein arbeiten mit der OpenGL Bibliothek außerhalb des Main Threads zu unerwartetem Verhalten führen kann.

    Ich konnte bisher mit einem kleinen Programm keine Probleme feststellen auf einer Debian Distribution mit OpenGL 2.1 (siehe weiter unten).

    Ich bin gespannt auf das Feedback. Danke im Voraus für die Mühe!

    #include <stdlib.h>
    #include <stdbool.h>
    #include <wchar.h>
    #include <locale.h>
    #include <stdint.h>
    #include <pthread.h>
    #include <GL/gl.h>
    #include <SDL2/SDL.h>
    #include <SDL2/SDL_image.h>
    #include <SDL2/SDL_ttf.h>
    /* Threading test */
    #include <SDL2/SDL_thread.h>
    
    typedef struct _data {
      SDL_Window *win;
      SDL_Event *event;
      bool running;
      float *r;
      float *g;
      float *b;
    } data_t;
    
    void logSDLError( const char *msg, const char *function_name )
    {
      fprintf( stderr, "[%s] Error: %s\n\tSDL: %s\n", function_name, msg, SDL_GetError() );
    }
    
    SDL_Window *Init_GL_Window( const char *window_title, const unsigned int size_x, const unsigned int size_y )
    {
      /*First of all, Init SDL Systems*/
      if( SDL_Init( SDL_INIT_EVERYTHING ) < 0 || TTF_Init() < 0 )
      {
        logSDLError( "Init of Systems failed.", __FUNCTION__ );
        return NULL;
      }
      /*Setup the OpenGL for SDL*/
      SDL_Window *tmp = SDL_CreateWindow( window_title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, \
        size_x, size_y, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN );
      if( tmp == NULL )
      {
        logSDLError( "Could not create Window", __FUNCTION__ );
        return tmp;
      }
      int major = 0;
      int minor = 0;
      SDL_GL_GetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, &major );
      SDL_GL_GetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, &minor );
      printf( "Default GL Version: %i.%i\n", major, minor );
      /* Core Profile for OpenGL */
      SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE );
      /* Doublebuffer */
      SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
      /* Sync with Vtrace */
      SDL_GL_SetSwapInterval( 1 );
      return tmp;
    }
    
    int thread_Renderer( void *data )
    {
      /* ToDo: Replace with Assert Statement */
      if( data == NULL ){ return -1; }
      data_t *mdata  = (data_t *)data;
      SDL_Window *window = Init_GL_Window( "Test Fenster", 480, 240 );
      mdata->win = window;
      SDL_GLContext glcontext = SDL_GL_CreateContext( window );
      if( glcontext == NULL )
      {
        logSDLError( "Could not create Context", __FUNCTION__ );
        return -2;
      }
      /*Start of Rendering Test*/
      glClearColor( *(mdata->r), *(mdata->g), *(mdata->b), 1.0 );
      glClear( GL_COLOR_BUFFER_BIT );
      SDL_GL_SwapWindow( window );
      while( mdata->running )
      {
        SDL_Delay( 250 );
        glClearColor( *(mdata->r), *(mdata->g), *(mdata->b), 1.0 );
        glClear( GL_COLOR_BUFFER_BIT );
        SDL_GL_SwapWindow( window );
      }
      SDL_DestroyWindow( window );
      SDL_GL_DeleteContext( glcontext );
      return 0;
    }
    
    int main( int argc, char **argv )
    {
      if( atexit( SDL_Quit ) && atexit( TTF_Quit ) )
      {
        return EXIT_FAILURE;
      }
      SDL_Event sevent;
      float r = 0.0;
      float g = 0.0;
      float b = 0.0;
      data_t main_data;
      main_data.event = &sevent;
      main_data.r = &r;
      main_data.g = &g;
      main_data.b = &b;
      main_data.running = true;
      SDL_Thread *thread_Ev = SDL_CreateThread( thread_Renderer, "rendererThread", &main_data );
      if( thread_Ev == NULL )
      {
        logSDLError( "Could not create Render Thread", __FUNCTION__ );
        exit( -3 );
      }
      bool loop = true;
      while( loop )
      {
        while( SDL_PollEvent( &sevent ) )
        {
          printf( SDL_GetError() );
          if( sevent.type == SDL_QUIT )
          {
            loop = false;
          }
          if( sevent.type == SDL_KEYUP )
          {
            printf( "Got Key: %c\n", sevent.key.keysym.sym );
            switch( sevent.key.keysym.sym )
            {
              case SDLK_ESCAPE:
                loop = false;
                main_data.running = false;
                break;
              case SDLK_r:
                *(main_data.r) += 0.01;
                printf( "Current R: %f\n", *(main_data.r) );
                break;
              case SDLK_g:
                *(main_data.g) += 0.01;
                printf( "Current G: %f\n", *(main_data.g) );
                break;
              case SDLK_b:
                *(main_data.b) += 0.01;
                printf( "Current B: %f\n", *(main_data.b) );
                break;
              default:
                printf( "Got Key: %c\n", sevent.key.keysym.sym );
                break;
            }
          }
        }
      }
      SDL_WaitThread( thread_Ev, NULL );
      TTF_Quit();
      SDL_Quit();
      return 0;
    }
    

  • |  Mod

    Responsiv in welchem Sinne? Responsiv ist normalerweise wenn der user auf seine Aktion, zeitnah, eine Antwort sehen kann. Inputs viel schneller zu verarbeiten als der Respond angezeigt wird, wird also in diesem Sinne nichts an Responsivitaet bringen.

    [quote]Sollte ich das überhaupt tun[/quote]Nein, du solltest dein Program nicht komplexer machen ohne einen gegenwärtigen Vorteil zu haben oder ein Problem zu loesen.

    Ja, manche OpenGL driver haben rumgesponnen wenn Rendern bzw Ausgabe nicht im main thread waren.