multithreading opengl sdl

multithreading - Representación OpenGL en un hilo secundario



sdl (6)

Estoy escribiendo una aplicación de visor de modelo 3D como un proyecto de pasatiempo, y también como una plataforma de prueba para probar diferentes técnicas de renderizado. Estoy usando SDL para manejar la gestión de ventanas y eventos, y OpenGL para la representación 3D. La primera iteración de mi programa era de subproceso único y funcionaba lo suficientemente bien. Sin embargo, noté que el programa de un solo subproceso causaba que el sistema se volviera muy lento / lento. Mi solución fue mover todo el código de renderizado a un hilo diferente, liberando así el hilo principal para manejar los eventos y evitar que la aplicación no responda.

Esta solución funcionó de manera intermitente, el programa se bloqueó con frecuencia debido a un conjunto cambiante (y en mi mente bizarra) de errores provenientes principalmente del sistema de ventanas X. Esto me llevó a cuestionar mi suposición inicial de que, mientras todas mis llamadas OpenGL tuvieran lugar en el hilo donde se creó el contexto, todo debería funcionar. Después de pasar la mayor parte del día buscando una respuesta en Internet, estoy completamente perplejo.

Más sucintamente: ¿es posible realizar una representación 3D usando OpenGL en un hilo que no sea el hilo principal? ¿Puedo seguir utilizando una biblioteca de ventanas multiplataforma como SDL o GLFW con esta configuración? ¿Hay una mejor manera de hacer lo que estoy tratando de hacer?

Hasta ahora he estado desarrollando en Linux (Ubuntu 11.04) usando C ++, aunque también me siento cómodo con Java y Python si hay una solución que funciona mejor en esos idiomas.

ACTUALIZACIÓN: según lo solicitado, algunas aclaraciones:

  • Cuando digo "El sistema se vuelve lento", me refiero a interactuar con el escritorio (arrastrando ventanas, interactuando con el panel, etc.) se vuelve mucho más lento de lo normal. Mover la ventana de mi aplicación toma tiempo en el orden de segundos, y otras interacciones son lo suficientemente lentas como para ser molesto.
  • En cuanto a la interferencia con un gestor de ventanas de composición ... Estoy usando el shell de GNOME que se incluye con Ubuntu 11.04 (manteniéndome alejado de Unity por ahora ...) y no pude encontrar ninguna opción para desactivar los efectos de escritorio como los que había en distribuciones previas. Supongo que esto significa que no estoy usando un administrador de ventanas de composición ... aunque podría estar muy equivocado.
  • Creo que los "errores X" son errores del servidor debido a los mensajes de error que recibo en la terminal. Más detalles a continuación.

Los errores que obtengo con la versión de subprocesos múltiples de mi aplicación:

XIO: error IO fatal 11 (Recurso temporalmente no disponible) en el servidor X ": 0.0" después de 73 solicitudes (73 conocidas procesadas) con 0 eventos restantes.

X Error de solicitud fallida: BadColor (parámetro Colormap inválido) Opcode principal de solicitud fallida: 79 (X_FreeColormap) Identificación del recurso en la solicitud fallida: 0x4600001 Número de serie de la solicitud fallida: 72 Número de serie actual en la secuencia de salida: 73

Juego: ../../src/xcb_io.c:140: dequeue_pending_request: Assertion `req == dpy-> xcb-> pending_requests ''failed. Abortado

Siempre recibo uno de los tres errores anteriores, el que obtengo varía, al parecer al azar, lo que (a mi parecer) parecería confirmar que mi problema en realidad se deriva de mi uso de los hilos. Tenga en cuenta que estoy aprendiendo a medida que avanzo, por lo que es muy probable que en mi ignorancia tenga algo bastante estúpido en el camino.

SOLUCIÓN: Para cualquier persona que tenga un problema similar, resolví mi problema moviendo mi llamada a SDL_Init(SDL_INIT_VIDEO) al hilo de representación, y bloqueando la inicialización de contexto usando un mutex. Esto asegura que el contexto se crea en el hilo que lo usará e impide que el ciclo principal se inicie antes de que las tareas de inicialización hayan finalizado. Un esquema simplificado del procedimiento de inicio:

1) El hilo principal inicializa la struct que será compartida entre los dos hilos, y que contiene un mutex.
2) El engendro del hilo principal procesa el hilo y duerme durante un breve período (1-5 ms), dando tiempo al hilo de renderizado para bloquear el mutex. Después de esta pausa, el hilo principal se bloquea al intentar bloquear el mutex.
3) El hilo de procesamiento bloquea el mutex, inicializa el subsistema de video de SDL y crea el contexto de OpenGL.
4) El hilo de procesamiento desbloquea el mutex y entra en su "bucle de renderizado".
5) El hilo principal ya no está bloqueado, por lo que bloquea y desbloquea el mutex antes de finalizar su etapa de inicialización.

Asegúrese de leer las respuestas y comentarios, hay mucha información útil allí.


  1. C ++, SDL, OpenGl :::
    1. en el hilo principal: SDL_CreateWindow ();
    2. SDL_CreateSemaphore ();
    3. SDL_SemWait ();
    4. en renderThread: SDL_CreateThread (run, "rendererThread", (void *) this)
    5. SDL_GL_CreateContext ()
    6. "inicializar el resto de openGl y glew"
    7. SDL_SemPost () // desbloquea el semáforo creado previamente
    8. PD: SDL_CreateThread () solo toma funciones como su primer parámetro, no como métodos, si se desea un método, entonces se simula un método / función en su clase haciendo que sea una función amiga. de esta manera tendrá rasgos de método mientras aún se puede usar como un funtor para SDL_CreateThread ().
    9. PSS: dentro del "run (void * data)" creado para el hilo, el "(void *)" esto es importante y para volver a obtener "this" dentro de la función esta línea es necesaria "ClassName * me = (ClassName *) data; "

Esta es media respuesta y media pregunta.

La representación en SDL en un hilo separado es posible. Funciona generalmente en cualquier sistema operativo. Lo que debe hacer es asegurarse de hacer que el contexto GL esté al día cuando el hilo de renderizado se hace cargo. Al mismo tiempo, antes de hacerlo, debe liberarlo del hilo principal, por ejemplo:

Llamado desde el hilo principal:

void Renderer::Init() { #ifdef _WIN32 m_CurrentContext = wglGetCurrentContext(); m_CurrentDC = wglGetCurrentDC(); // release current context wglMakeCurrent( nullptr, nullptr ); #endif #ifdef __linux__ if (!XInitThreads()) { THROW( "XLib is not thread safe." ); } SDL_SysWMinfo wm_info; SDL_VERSION( &wm_info.version ); if ( SDL_GetWMInfo( &wm_info ) ) { Display *display = wm_info.info.x11.gfxdisplay; m_CurrentContext = glXGetCurrentContext(); ASSERT( m_CurrentContext, "Error! No current GL context!" ); glXMakeCurrent( display, None, nullptr ); XSync( display, false ); } #endif }

Llamado desde el hilo de renderizado:

void Renderer::InitGL() { // This is important! Our renderer runs its own render thread // All #ifdef _WIN32 wglMakeCurrent(m_CurrentDC,m_CurrentContext); #endif #ifdef __linux__ SDL_SysWMinfo wm_info; SDL_VERSION( &wm_info.version ); if ( SDL_GetWMInfo( &wm_info ) ) { Display *display = wm_info.info.x11.gfxdisplay; Window window = wm_info.info.x11.window; glXMakeCurrent( display, window, m_CurrentContext ); XSync( display, false ); } #endif // Init GLEW - we need this to use OGL extensions (e.g. for VBOs) GLenum err = glewInit(); ASSERT( GLEW_OK == err, "Error: %s/n", glewGetErrorString(err) );

El riesgo aquí es que, por desgracia, SDL no tiene una función nativa MakeCurrent (). Entonces, tenemos que hurgar un poco en las partes internas de SDL (1.2, 1.3 podría haber resuelto esto por ahora).

Y queda un problema, que por alguna razón, me encuentro con un problema cuando SDL se está apagando. Tal vez alguien me puede decir cómo liberar de forma segura el contexto cuando termina el hilo.


Estaba recibiendo uno de tus errores:

../../src/xcb_io.c:140: dequeue_pending_request: Assertion `req == dpy->xcb->pending_requests'' failed. Aborted

y una gran cantidad de diferentes también. Resulta que SDL_PollEvent necesita un puntero a con memoria inicializada. Entonces esto falla:

SDL_Event *event; SDL_PollEvent(event);

mientras esto funciona:

SDL_Event event; SDL_PollEvent(&event);

En caso de que alguien más se encuentre con esto desde google.


Lo que hice en una situación similar fue mantener mis llamadas OpenGL en el hilo principal, pero mover la preparación de matrices Vértice a un hilo (o hilos) separados.

Básicamente, si logras separar las cosas intensivas de la CPU de las llamadas OpenGL, no tienes que preocuparte por el lamentablemente dudoso OpenGL multithreading.

Funcionó maravillosamente para mí.


Por las dudas, el X-Server tiene su propio subsistema de sincronización. Intente seguir al dibujar: man XInitThreads - para inicialización
man XLockDisplay/XUnlockDisplay - para dibujar (no estoy seguro para el procesamiento de eventos);


Siempre que el contexto de OpenGL se toque solo de un hilo a la vez, no debería encontrarse con ningún problema. Usted dijo que incluso su programa de un solo hilo hacía que su sistema fuera lento. ¿Eso significa todo el sistema o solo tu propia aplicación? Lo peor que debería suceder en un programa OpenGL de un solo hilo es que el procesamiento de las entradas de usuario para ese programa se ralentiza, pero el resto del sistema no se ve afectado.

Si usa algún gestor de ventanas de composición (Compiz, KDE4 kwin), pruebe lo que sucede si deshabilita todos los efectos de composición.

Cuando dice X ¿quiere decir errores del lado del cliente o errores informados en el registro del servidor X? El último caso no debería ocurrir, porque cualquier clase de secuencia de comando X malformada que el servidor X debe ser capaz de soportar y, como máximo, emitir una advertencia. Si se bloquea (el servidor X), se trata de un error y se debe informar a X.org.

Si su programa falla, entonces hay algo mal en su interacción con X; en ese caso, por favor envíenos la salida de error en sus variaciones.