c++ - Ubuntu virtualbox 中的 OpenGL 多线程示例

标签 c++ multithreading opengl ubuntu virtualbox

我在启用 3D 加速的 vbox 上遇到一个 OpenGL 示例的奇怪问题

  • 访客:Ubuntu 12.04
  • 主机:Windows 7,nvidia 显卡
  • vbox 版本 4.3.6,安装了 guest 添加

此应用程序在禁用 3D 加速时可在 vbox 上正常运行,我也在独立的 Linux PC 上对此进行了检查。 在启用 3D 加速的情况下运行同样的程序时,它无法获取 GL 函数指针并给出错误 - function no-op


App 很简单,主线程创建 2 个线程。

  • 主线程 - 创建线程 1,创建线程 2
  • 线程 1 - 创建用于呈现的 X 窗口
  • 线程 2 - 创建渲染线程(在 X-Window 上绘制 OpenGL 四边形)。

这是示例应用程序的代码。

#include<stdio.h>
#include<stdlib.h>
#include<X11/X.h>
#include<X11/Xlib.h>
#include<GL/gl.h>
#include<GL/glx.h>
//#include<GL/glu.h>
#include <dlfcn.h> /*dlopen*/
#include <pthread.h>
#include <unistd.h> /*sleep*/

Display                 *dpy;
Display                 *dpy2;
Window                  root;
GLint                   att[] = { GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None };
XVisualInfo             *vi;
XVisualInfo             *vi2;
Colormap                cmap;
XSetWindowAttributes    swa;
Window                  win;
GLXContext              glc;
XWindowAttributes       gwa;
XEvent                  xev;
bool            render;


void DrawAQuad() 
{
    glClearColor(1.0, 1.0, 1.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-1., 1., -1., 1., 1., 20.);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    //gluLookAt(0., 0., 10., 0., 0., 0., 0., 1., 0.);
    glTranslatef(0.0, 0.0, -10.0);

    glBegin(GL_QUADS);
     glColor3f(1., 0., 0.); glVertex3f(-.75, -.75, 0.);
     glColor3f(0., 1., 0.); glVertex3f( .75, -.75, 0.);
     glColor3f(0., 0., 1.); glVertex3f( .75,  .75, 0.);
     glColor3f(1., 1., 0.); glVertex3f(-.75,  .75, 0.);
    glEnd();
}

void *CreateMainWindow(void* threadID)
{
    dpy = XOpenDisplay(NULL);
    if(dpy == NULL) 
    {
        printf("\n\tWindow Thread: cannot connect to X server\n\n");
        exit(0);
    }

    root = DefaultRootWindow(dpy);
    printf("\n *** CreateWindow: xopendisplay over *** \n");

    vi = (XVisualInfo*)glXChooseVisual(dpy, 0, att);
    if(vi == NULL) 
    {
        printf("\n\tWindow Thread: no appropriate visual found\n\n");
        exit(0);
    } 
    else 
    {
        printf("\n\tWindow Thread: visual %p selected\n", (void *)vi->visualid);
    }

    cmap = XCreateColormap(dpy, root, vi->visual, AllocNone);
    swa.colormap = cmap;
    swa.event_mask = ExposureMask | KeyPressMask;

    win = XCreateWindow(dpy, root, 0, 0, 600, 600, 0, vi->depth, InputOutput, vi->visual, CWColormap | CWEventMask, &swa);
    XMapWindow(dpy, win);
    XStoreName(dpy, win, "VERY SIMPLE APPLICATION");

    while(1) 
    {
        XNextEvent(dpy, &xev);
        printf("\nXEVENT\n");
        if(xev.type == Expose)
        {
            render = true;
        }
        else if(xev.type == KeyPress)
        {
            XDestroyWindow(dpy, win);
            XCloseDisplay(dpy);
            render = false;
            break;
            //exit(0);
        }
    }
}

void *RenderThread(void* threadID)
{
    vi2 = (XVisualInfo*)glXChooseVisual(dpy2, 0, att);
    printf("\n\tRenderThread : visual %p selected\n", (void *)vi2->visualid);

    glc = (GLXContext)glXCreateContext(dpy2, vi2, NULL, GL_TRUE);
    glXMakeCurrent(dpy2, win, glc);

    glEnable(GL_DEPTH_TEST); 

    while(render) 
    {
        //XGetWindowAttributes(dpy, win, &gwa);
        glViewport(0, 0, 600, 600);
        DrawAQuad(); 
        glXSwapBuffers(dpy2, win);
    } /* this closes while(render) */

    glXMakeCurrent(dpy2, None, NULL);
    glXDestroyContext(dpy2, glc);
    XCloseDisplay(dpy2);
}

int main(int argc, char *argv[]) 
{
    render = true;
    pthread_t thread1;
    pthread_t thread2;
    char *temp1;
    char *temp2;

    //For Async issue
    if(!XInitThreads())
    {
        fprintf(stderr, "XInitThread failed\n");
        return 0;
    }

    //Create Main Window
    int err = pthread_create(&thread1, NULL, CreateMainWindow, (void*)temp1);
    if (err != 0)
        printf("\n ERROR::can't create thread1 :[%d]", err);
    else
        printf("\n Thread1 created successfully\n");

    sleep(1); // Wait for thread 1 to complete

    dpy2 = XOpenDisplay(NULL);
    if(dpy2 == NULL) 
    {
        printf("\n\tMain : cannot connect to X server\n\n");
        exit(0);
    }

    //Create Render Thread
    err = pthread_create(&thread2, NULL, RenderThread, (void*)temp2);
    if (err != 0)
        printf("\n ERROR::can't create thread2 :[%d]", err);
    else
        printf("\n Thread2 created successfully\n");

    pthread_join( thread1, NULL);
    pthread_join( thread2, NULL);

} /* this is the } which closes int main(int argc, char *argv[]) { */

并编译代码-

g++ -o quad quad.cpp -lGL -lX11 -lXmu -lXi -lpthread -lm

帮助我了解问题出在哪里

最佳答案

不管你在做什么,停下来!

多线程 + X11 + OpenGL 是一件非常棘手的事情。如果您使用的是 Xlib,则几乎不可能做到正确。 Xlib 从来都不是真正的线程安全的。

无论如何,首先您的程序缺少对 XInitThreads 的调用以使其至少可以安全地用于多线程程序。然而,将 Xlib 调用分散到多个线程上仍然是不安全的。这一点非常重要:无论您做什么,都将所有 Xlib 调用保持在一个线程中。

OpenGL 本身并不那么棘手。但是因为 OpenGL 需要一个用 glX 创建的上下文,而 glX 又建立在 Xlib 之上。通常的方法是在 Xlib 线程中创建 OpenGL 上下文,但稍后使其在渲染器线程中成为当前线程。但是请注意,如果您有一个间接渲染上下文,所有 OpenGL 调用都会通过 X11,这可能意味着通过 Xlib,并且事情会再次变得不稳定。

由于所有的困惑,最简单的解决方案是:让所有图形和窗口都与一个线程相关。如果您将 OpenGL 操作放入与其余 GUI 操作分开的线程中(从技术上讲,OpenGL 也执行 GUI 操作),那将一事无成。如果你想使用多线程,那么使用 if 来同时执行有意义的事情,比如音频、物理模拟等等。

关于c++ - Ubuntu virtualbox 中的 OpenGL 多线程示例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21595649/

相关文章:

c++ - 将纹理应用于对象时 Opengl 颜色错误

c++ - 编译器提供语法错误,Visual Studio中未显示。我能做什么?

android - OpenCV Android 坐标错误

multithreading - 门环内外互斥锁速度差异

C#:Monitor.Pulse() 不起作用

c# - .NET 任务中的代码最终是否通过 native Windows 线程在 CPU 或 CPU 核心上运行?

c++ - 纹理平面上的 OpenGL 光照不工作

c++ - 如何在 C++ 中填充数据或访问 3 维 vector

c++ - UML 模型关系是如何用 C++ 编码的?

c++ - 将局部变量 move 到不同类型的返回值