【问题标题】:wglMakeCurrent in second thread fails?第二个线程中的 wglMakeCurrent 失败?
【发布时间】:2014-03-04 01:03:22
【问题描述】:

所以我正在我的 Win32 应用程序中设置我的 OpenGL 上下文。我正在设置一个多线程环境,窗口消息在一个线程中处理,OpenGL 渲染调用在另一个线程中。到目前为止,我的工作流程如下:

线程 A:

  • 创建窗口
  • 获取显示上下文
  • 选择像素格式
  • 创建一个临时的 OpenGL 上下文并使其成为当前的
  • 获取扩展函数指针
  • 将当前上下文设置为 NULL 并删除临时上下文
  • 使用 wglCreateContextAttribsARB 创建新上下文
  • 启动线程 B

然后,在线程 B 中:

  • wglMakeCurrent 使用线程 A 中的 hdc 和 hglrc

问题是,线程 B 中的 wglMakeCurrent 返回 false,getLastError 返回 170(正在使用的资源)。我读到的所有内容都意味着这意味着渲染上下文已经在线程 A 中使用,但我在线程 B 甚至创建之前在线程 A 中明确调用了“wglMakeCurrent(NULL, NULL)”。

可能出了什么问题?

PS。为了清楚起见,我不想从不同的并发线程运行 OpenGL 调用。除了在线程 B 存在之前创建渲染上下文之外,每个单独的 OpenGL 调用都将从线程 B 进行。

编辑:这是一些源代码。这是在线程 A 中初始化窗口的函数:

Window* Window::init(void)
{
    /* If the singleton exists already, just return a pointer to it */
    if (singleton)
        return singleton;

    /* Allocate the singleton and check for errors */
    singleton = new Window();
    if (singleton == NULL)
    {
        fatalerror("In Window::init():\n");
        fatalmore("Memory allocation failure.\n");
        fatalmore("Could not allocate singleton.\n");
        _getch();
        exit(1);
    }

    /* Register window class */
    WNDCLASSEX wc;
    memset(&wc, 0, sizeof(WNDCLASSEX));

    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = wndProc;
    wc.hInstance = GetModuleHandle(NULL);
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.lpszClassName = APP_NAME;

    if (!RegisterClassEx(&wc))
    {
        fatalerror("In Window::init():\n");
        fatalmore("Failed to register window class.\n");
        fatalmore("Error code: '%i'\n", GetLastError());
        clean();
        _getch();
        exit(1);
    }

    /* Read settings */
    int width = Settings::get()->screenWidth;
    int height = Settings::get()->screenHeight;
    DWORD exStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
    DWORD style = WS_OVERLAPPEDWINDOW;

    /* Are we using fullscreen mode? */
    if (Settings::get()->fullscreen)
    {
        width = Settings::get()->fullscreenWidth;
        height = Settings::get()->fullscreenHeight;

        DEVMODE dm;
        memset(&dm, 0, sizeof(DEVMODE));

        dm.dmSize = sizeof(DEVMODE);
        dm.dmPelsWidth = width;
        dm.dmPelsHeight = height;
        dm.dmBitsPerPel = 32;
        dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;

        if (ChangeDisplaySettings(&dm, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
        {
            warning("in Window::init():\n");
            warnmore("The requested fullscreen mode is not supported.\n");
            warnmore("Will continue in windowed mode.\n");

            width = Settings::get()->screenWidth;
            height = Settings::get()->screenHeight;
            Settings::get()->fullscreen = false;
        }
        else
        {
            exStyle = WS_EX_APPWINDOW;
            style = WS_POPUP;
        }
    }

    /* Create the window */
    singleton->wnd = CreateWindowEx(    exStyle,
                                                APP_NAME, APP_NAME,
                                                WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | style,
                                                CW_USEDEFAULT, CW_USEDEFAULT,
                                                width, height,
                                                NULL,
                                                NULL,
                                                GetModuleHandle(NULL),
                                                NULL );

    if (!singleton->wnd)
    {
        fatalerror("In Window::init():\n");
        fatalmore("Could not create window.\n");
        fatalmore("Error code: '%i'\n", GetLastError());
        clean();
        _getch();
        exit(1);
    }

    /* Get the DC */
    singleton->dc = GetDC(singleton->wnd);
    if (!singleton->dc)
    {
        fatalerror("In Window::init():\n");
        fatalmore("Failed to get display context.\n");
        fatalmore("Error code: '%i'\n", GetLastError());
        clean();
        _getch();
        exit(1);
    }

    /* Choose a pixel format */
    PIXELFORMATDESCRIPTOR pfd;
    memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));

    pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
    pfd.nVersion = 1;
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
    pfd.iPixelType = PFD_TYPE_RGBA;
    pfd.cColorBits = 32;
    pfd.cDepthBits = 24;
    pfd.cStencilBits = 8;
    pfd.iLayerType = PFD_MAIN_PLANE;

    int pf = ChoosePixelFormat(singleton->dc, &pfd);
    if (!pf)
    {
        fatalerror("In Window::init():\n");
        fatalmore("Failed to choose a pixel format.\n");
        fatalmore("Error code: '%i'\n", GetLastError());
        clean();
        _getch();
        exit(1);
    }

    /* Set the pixel format */
    if (!SetPixelFormat(singleton->dc, pf, &pfd))
    {
        fatalerror("In Window::init():\n");
        fatalmore("Failed to set pixel format '%i'.\n", pf);
        fatalmore("Error code: '%i'\n", GetLastError());
        clean();
        _getch();
        exit(1);
    }

    /* Create our fake, temporary OpenGL context */
    HGLRC temprc = wglCreateContext(singleton->dc);
    if (!temprc)
    {
        fatalerror("In Window::init():\n");
        fatalmore("Failed to create a temporary context.\n");
        fatalmore("Error code: '%i'\n", GetLastError());
        clean();
        _getch();
        exit(1);
    }
    wglMakeCurrent(singleton->dc, temprc);

    /* Initialize the OpenGL extensions we need */
    if (!initGLCreationExtensions())
    {
        fatalerror("In Window::init():\n");
        fatalmore("Failed to initialize OpenGL context creation extensions.\n");
        clean();
        _getch();
        exit(1);
    }

    /* Check for OpenGL version */
    int majorver = 0, minorver = 0;
    glGetIntegerv(GL_MAJOR_VERSION, &majorver);
    glGetIntegerv(GL_MINOR_VERSION, &minorver);

    if (majorver < 3 || (majorver == 3 && minorver < 1))
    {
        fatalerror("In Window::init():\n");
        fatalmore("OpenGL version 3.1 or higher is required.\n");
        clean();
        _getch();
        exit(1);
    }


    /* Define context attributes */
    int contextAttribs[] =
    {
        WGL_CONTEXT_MAJOR_VERSION_ARB, majorver,
        WGL_CONTEXT_MINOR_VERSION_ARB, minorver,
        WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
        WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
        0
    };

    /* Create the real OpenGL context */
    singleton->rc = wglCreateContextAttribsARB(singleton->dc, NULL, contextAttribs);
    if (!singleton->rc)
    {
        fatalerror("In Window::init():\n");
        fatalmore("Failed to create OpenGL context.\n");
        fatalmore("Error code: '%i'\n", GetLastError());
        clean();
        _getch();
        exit(1);
    }

    ShowWindow(singleton->wnd, SW_SHOW);

    /* Delete the fake context */
    wglMakeCurrent(NULL, NULL);
    wglDeleteContext(temprc);

    info("Successfully created window.\n");
    printf("\n");

    return singleton;
}

这是在线程 B 中运行的启动函数:

bool Window::renderThreadStartupFunc(void)
{
    if (!singleton)
        return false;

    if (!singleton->dc)
        return false;


    if (!wglMakeCurrent(singleton->dc, singleton->rc))
    {
        error("In Window::renderThreadStartupFunc():\n");
        errormore("Could not make context current.\n");
        errormore("Error code: '%i'\n", GetLastError());
        return false;
    }

    if (!initGLExtensions())
    {
        error("In Window::renderThreadStartupFunc():\n");
        errormore("Failed to initialize OpenGL extensions.\n");
        return false;
    }

    return true;
}

这是负责启动和关闭线程 B 的代码。它在单独的类中运行,并使用 c++11 的 std::thread 库。

Renderer* Renderer::init(void)
{
    /* If the singleton exists already, just return a pointer to it */
    if (singleton)
        return singleton;

    /* Allocate the singleton and check for errors */
    singleton = new Renderer();
    if (singleton == NULL)
    {
        fatalerror("In Renderer::init():\n");
        fatalmore("Memory allocation failure.\n");
        fatalmore("Could not allocate singleton.\n");
        _getch();
        exit(1);
    }

    Window::get();

    singleton->running = true;

    renderThread = new thread(main);
    if (renderThread == NULL)
    {
        fatalerror("In Renderer::init():\n");
        fatalmore("Memory allocation failure.\n");
        fatalmore("Could not allocate rendering thread.\n");
        _getch();
        exit(1);
    }

    Message startup;
    startup.type = MT_STARTUP;
    singleton->queue.push(startup);

    return singleton;
}

void Renderer::clean(void)
{
    if (!singleton)
        return;

    Message shutdown;
    shutdown.type = MT_SHUTDOWN;
    singleton->queue.push(shutdown);

    if (renderThread)
    {
        renderThread->join();
        delete renderThread;
    }
}

【问题讨论】:

  • 您从原始上下文加载了哪些扩展指针?希望只是创建新上下文所必需的。您应该使用在 WGL 中的另一个上下文中加载的函数指针。在 GLX 中这确实无关紧要,但在 WGL 中基本上是这样。
  • 现在它只加载 wglChoosePixelFormatARB(我什至没有使用)和 wglCreateContextAttribsARB。我不知道我必须为新上下文重新加载函数指针,谢谢!
  • 能否在删除旧上下文、创建新上下文并启动线程 B 时显示几行代码?
  • 我已经在原始问题中发布了代码。我没有使用 wglChoosePixelFormatARB,因为我不需要 MSAA。
  • 在创建新上下文之前尝试删除原始上下文。这就是您最初的问题所说的您正在做的事情,但是您的代码正在做一些不同的事情。我也没有在这里看到任何实际创建您的线程的代码。

标签: c++ multithreading winapi opengl c++11


【解决方案1】:

一些建议:

  • 使用临时帮助上下文作为当前上下文创建适当的上下文(您的扩展函数指针与 Windows 中的活动上下文相关联),即不要从当前上下文中释放帮助上下文,也不要释放辅助上下文,在创建适当的上下文之前。

  • 在启动线程 B 之前,在正确的上下文和 hdc 上调用 wglMakeCurrent(…),调用 glFinish(),然后才调用 wglMakeCurrent(NULL, NULL) 并删除临时上下文。对于一些有问题的驱动程序来说,这是一个肮脏的解决方法。

【讨论】:

  • 我尝试了你的两个建议,但都没有成功 =(
猜你喜欢
  • 2015-04-24
  • 1970-01-01
  • 2014-07-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-17
相关资源
最近更新 更多