English 中文(简体)
OpenGL在辅助线程中的渲染
原标题:OpenGL Rendering in a secondary thread

我正在编写一个3D模型查看器应用程序,作为一个业余项目,也作为一个测试平台来尝试不同的渲染技术。我正在使用SDL处理窗口管理和事件,并使用OpenGL进行3D渲染。我的程序的第一次迭代是单线程的,运行得很好。然而,我注意到单线程程序导致系统变得非常缓慢/滞后。我的解决方案是将所有呈现代码移到不同的线程中,从而释放主线程来处理事件,并防止应用程序变得没有响应。

这个解决方案间歇性地工作,由于主要来自X窗口系统的一组不断变化(在我看来很奇怪)的错误,程序经常崩溃。这让我质疑了我最初的假设,即只要我所有的OpenGL调用都发生在创建上下文的线程中,一切都应该正常。在花了一天的大部分时间在互联网上搜索答案后,我彻底被难住了。

更简洁地说:是否可以在主线程之外的线程中使用OpenGL执行3D渲染?在这种配置下,我仍然可以使用跨平台窗口库(如SDL或GLFW)吗?有没有更好的方法来做我想做的事?

到目前为止,我一直在使用C++在Linux(Ubuntu 11.04)上进行开发,尽管如果有更好的解决方案,我对Java和Python也很满意。

更新:根据要求,一些澄清:

  • When I say "The system becomes sluggish" I mean interacting with the desktop (dragging windows, interacting with the panel, etc) becomes much slower than normal. Moving my application s window takes time on the order of seconds, and other interactions are just slow enough to be annoying.
  • As for interference with a compositing window manager... I am using the GNOME shell that ships with Ubuntu 11.04 (staying away from Unity for now...) and I couldn t find any options to disable desktop effects such as there was in previous distributions. I assume this means I m not using a compositing window manager...although I could be very wrong.
  • I believe the "X errors" are server errors due to the error messages I m getting at the terminal. More details below.

我在应用程序的多线程版本中遇到的错误:

XIO: fatal IO error 11 (Resource temporarily unavailable) on X server ":0.0" after 73 requests (73 known processed) with 0 events remaining.

X Error of failed request: BadColor (invalid Colormap parameter) Major opcode of failed request: 79 (X_FreeColormap) Resource id in failed request: 0x4600001 Serial number of failed request: 72 Current serial number in output stream: 73

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

我总是会出现上面三个错误中的一个,我得到的错误各不相同,显然是随机的,(在我看来)这似乎证实了我的问题实际上源于我对线程的使用。请记住,我是边学习边学习的,所以很有可能在我的无知中,我一路上做了一些相当愚蠢的事情。

解决方案:对于任何有类似问题的人,我通过将对SDL_Init(SDL_Init_VIDEO)的调用移动到呈现线程并使用互斥锁锁定上下文初始化来解决我的问题。这样可以确保在将要使用它的线程中创建上下文,并防止在初始化任务完成之前启动主循环。启动程序的简化概述:

1) Main thread initializes struct which will be shared between the two threads, and which contains a mutex.
2) Main thread spawns render thread and sleeps for a brief period (1-5ms), giving the render thread time to lock the mutex. After this pause, the main thread blocks while trying to lock the mutex.
3) Render thread locks mutex, initializes SDL s video subsystem and creates OpenGL context.
4) Render thread unlocks mutex and enters its "render loop".
5) The main thread is no longer blocked, so it locks and unlocks the mutex before finishing its initialization step.

一定要阅读答案和评论,那里有很多有用的信息。

最佳回答

只要OpenGL上下文一次只从一个线程进行触摸,就不应该遇到任何问题。你说过,即使是单线程程序也会让你的系统变得迟钝。这是指整个系统还是仅指您自己的应用程序?单线程OpenGL程序中最糟糕的情况是,为该程序处理用户输入会变得滞后,但系统的其他部分不会受到影响。

如果你使用一些合成窗口管理器(Compiz,KDE4 kwin),请尝试一下如果你禁用所有合成效果会发生什么。

当您说X错误时,您的意思是客户端错误,还是X服务器日志中报告的错误?后一种情况不应该发生,因为X服务器必须能够处理任何类型的格式错误的X命令流,并且最多发出警告。如果它(X服务器)崩溃,这是一个错误,应该向X.org报告。

如果你的程序崩溃,那么它与X的交互就有问题;在这种情况下,请向我们提供其变体的错误输出。

问题回答

在类似的情况下,我所做的是将OpenGL调用保留在主线程中,但将顶点数组准备移动到一个单独的线程中。

基本上,如果你设法从OpenGL调用中分离出cpu密集型的东西,你就不必担心OpenGL多线程的问题。

对我来说效果很好。

Just in case - the X-Server has its own sync subsystem. Try following while drawing: man XInitThreads - for initialization
man XLockDisplay/XUnlockDisplay -- for drawing (not sure for events processing);

我收到了你的一个错误:

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

还有一大堆不同的。事实证明SDL_PollEvent需要一个具有初始化内存的指针。因此,这失败了:

SDL_Event *event;
SDL_PollEvent(event);

而这是有效的:

SDL_Event event;
SDL_PollEvent(&event);

以防其他人从谷歌上看到这个。

这是半个答案半个问题。

可以在单独的线程中在SDL中进行渲染。它通常适用于任何操作系统。您需要做的是,确保在渲染线程接管时使GL上下文为当前上下文。同时,在执行此操作之前,您需要从主线程中释放它,例如:

从主线程调用:

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
}

从渲染线程调用:

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
", glewGetErrorString(err) );

不幸的是,这里的风险是SDL没有本地MakeCurrent()函数。因此,我们必须对SDL内部进行一些探讨(1.2、1.3现在可能已经解决了这个问题)。

还有一个问题,由于某种原因,当SDL关闭时,我遇到了一个问题。也许有人可以告诉我如何在线程终止时安全地释放上下文。

  1. C++, SDL, OpenGl:::
    1. on main thread: SDL_CreateWindow( );
    2. SDL_CreateSemaphore( );
    3. SDL_SemWait( );
    4. on renderThread: SDL_CreateThread( run, "rendererThread", (void*)this )
    5. SDL_GL_CreateContext( )
    6. "initialize the rest of openGl and glew"
    7. SDL_SemPost( ) //unlock the previously created semaphore
    8. P.S: SDL_CreateThread( ) only takes functions as its first parameter not methods, if a method is wanted than you simulate a method/function in your class by making it a friend function. this way it will have method traits while still able to be used as a functor for the SDL_CreateThread( ).
    9. P.S.S: inside of the "run( void* data )" created for the thread, the "(void*)" this is important and in order to re-obtain "this" inside of the function this line is needed "ClassName* me = (ClassName*)data;"




相关问题
Silverlight, Updating the UI during processing

I have a simple silverlight multifile upload application, and i want to provide the user with some feedback, right now its only in a test phase and i dont have the webservice. Somehow i cant get the ...

Is reading from an XmlDocument object thread safe?

I was wondering if i could safely read from an XmlDocument object using SelectNodes() and SelectSingleNode() from multiple threads with no problems. MSDN says that they are not guaranteed to be ...

Terminating a thread gracefully not using TerminateThread()

My application creates a thread and that runs in the background all the time. I can only terminate the thread manually, not from within the thread callback function. At the moment I am using ...

热门标签