2017-05-10 72 views
5

我为Windows创建了一个简单的OpenGL应用程序。它创建一个窗口,然后使用OpenGL命令为它绘制一个三角形。这按预期工作。OpenGL在另一个线程中绘制

后来我想将我的绘图代码封装到一个DLL中,以便它可以在C#WinForms应用程序中用于绘制到WinForm。 为此,我将绘图代码移至单独的类和线程。我的想法是,我可以将我的课程“连接”到任何现有的窗口并让我的课程吸引它。

可悲的是事情似乎并不那么简单。一旦我将窗口创建和绘制工作分离到不同的线程中,屏幕将保持全黑。绘图调用似乎不再起作用。

有没有办法让我的绘画完全独立于窗口创建和主UI线程?

编辑:下面是一些代码:-)

这是我渲染(从UI线程时的作品叫,从后台线程调用时不工作):

// Constructor 
Renderer::Renderer(HWND hwnd, size_t windowWidth, size_t windowHeight) 
    : 
    mContext(hwnd) 
{ 
    mWindowWidth = windowWidth; 
    mWindowHeight = windowHeight; 
    mHdc = GetDC(hwnd); 

    // From now on everything is similar to initializing a context on any other hdc 
    PIXELFORMATDESCRIPTOR pfd; 
    ZeroMemory(&pfd, sizeof(pfd)); 
    pfd.nSize = sizeof(pfd); 
    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; 
    int iFormat = ChoosePixelFormat(mHdc, &pfd); 
    SetPixelFormat(mHdc, iFormat, &pfd); 

    mHrc = wglCreateContext(mHdc); 
    wglMakeCurrent(mHdc, mHrc); 


    // Set up OpenGL 
    glDisable(GL_DEPTH_TEST); 
    glDisable(GL_LIGHTING); 
    glDisable(GL_TEXTURE_2D); 
    glLoadIdentity(); 
    glViewport(0, 0, windowWidth, windowHeight); 
    glOrtho(0, windowWidth, windowHeight, 0, -1, 1); 
} 


// Draws the scene 
void Renderer::Draw() 
{ 
    wglMakeCurrent(mHdc, mHrc); 

    glClear(GL_COLOR_BUFFER_BIT); 
    glColor4f(1, 0, 1, 1); 

    glBegin(GL_QUADS); 
    glColor3f(1.0, 1.0, 1.0); 
    glVertex3f(0.0f, float(mWindowHeight/2), 0.0f); 
    glVertex3f(float(mWindowWidth/2), float(mWindowHeight/2), 0.0f); 
    glVertex3f(float(mWindowWidth/2), 0.0f, 0.0f); 
    glVertex3f(0.0f, 0.0f, 0.0f); 
    glEnd(); 

    glFlush(); 
    SwapBuffers(mHdc); 
} 

这是怎么了我打电话从后台线程渲染:

// Constructor 
BackgroundRenderer::BackgroundRenderer(HWND hwnd, uint32_t windowWidth, uint32_t windowHeight) 
    : 
    mCancelThread(false) 
{ 
    // Initialize OpenGL 
    mRenderer = std::make_shared<Renderer>(hwnd, windowWidth, windowHeight); 

    // Start rendering thread 
    mRenderingThread = std::thread(&BackgroundRenderer::BackgroundLoop, this); 
} 


// Destructor 
BackgroundRenderer::~BackgroundRenderer() 
{ 
    // Stop rendering thread 
    mCancelThread = true; 
    mRenderingThread.join(); 
} 


// The background rendering loop 
void BackgroundRenderer::BackgroundLoop() 
{ 
    while (!mCancelThread) 
    { 
    // Draw stuff 
    mRenderer->Draw(); 
    std::this_thread::sleep_for(std::chrono::milliseconds(10)); 
    } 
} 

而且这里是我的主要粘合一起:

// Message loop 
LONG WINAPI WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{ 
    switch (uMsg) 
    { 
    case WM_CLOSE: 
    PostQuitMessage(0); 
    return 0; 
    } 

    return DefWindowProc(hWnd, uMsg, wParam, lParam); 
} 


// Window creation 
HWND CreateApplicationWindow(char* title, int x, int y, int width, int height, int nCmdShow) 
{ 
    HWND  hWnd; 
    WNDCLASS wc; 
    static HINSTANCE hInstance = 0; 

    if (!hInstance) 
    { 
    hInstance = GetModuleHandle(NULL); 
    wc.style = CS_OWNDC; 
    wc.lpfnWndProc = (WNDPROC)WindowProc; 
    wc.cbClsExtra = 0; 
    wc.cbWndExtra = 0; 
    wc.hInstance = hInstance; 
    wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); 
    wc.hCursor = LoadCursor(NULL, IDC_ARROW); 
    wc.hbrBackground = NULL; 
    wc.lpszMenuName = NULL; 
    wc.lpszClassName = "OpenGL"; 

    RegisterClass(&wc); 
    } 

    hWnd = CreateWindowA("OpenGL", title, WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, x, y, width, height, NULL, NULL, hInstance, NULL); 
    ShowWindow(hWnd, nCmdShow); 

    return hWnd; 
} 


// Main entry point of application 
int APIENTRY WinMain(HINSTANCE hCurrentInst, HINSTANCE hPreviousInst, LPSTR lpszCmdLine, int nCmdShow) 
{ 
    HWND hWnd = CreateApplicationWindow("Test", 0, 0, 640, 480, nCmdShow); 

    // This renders from another thread (not working) 
    auto backgroundRenderer = std::make_shared<BackgroundRenderer>(hWnd, 640, 480); 

    // This would render in the UI thread (works) 
    //auto renderer = std::make_shared<Renderer>(hWnd, 640, 480); 

    MSG msg; 
    while (GetMessage(&msg, hWnd, 0, 0)) 
    { 
    // This would render in the UI thread (works) 
    //renderer->Draw(); 

    TranslateMessage(&msg); 
    DispatchMessage(&msg); 
    } 

    DestroyWindow(hWnd); 
    return msg.wParam; 
} 
+0

好的,您的发布代码与GLFW无关,那么您为什么提到它呢? – InternetAussie

+0

我之前使用过GLFW。为了简化事情,我将其从我的项目中彻底删除(然后我将上面的代码添加到此主题中)。但问题依然存在,无论有无GLFW。 – Boris

回答

6

两个基本规则是:

  • 任何给定的OpenGL渲染上下文可以只被激活一次一个线程。
  • 任何线程一次只能创建一个当前处于活动状态的OpenGL上下文。

但是,在特定的OpenGL上下文和特定的窗口(Win32 test program for using a single OpenGL context with multiple windows)或特定的线程之间没有严格的关联。您总是可以将OpenGL上下文从一个线程迁移到另一个线程。

两种常用的方法是:

  • 创建线程A的窗口,通过窗口句柄到线程B和创建OpenGL上下文,然后有。

  • 在线程A创建的窗口和OpenGL上下文中,使在非活性甲上下文(多个),传递给线程B,并使其活性有手柄。
0

你需要的OpenGL每各目标窗口背景......,你也需要渲染之前交换环境......怎么看wglMakeCurrent使用,否则所有的渲染你正在执行的是为最后选择的上下文完成的。

因此,在渲染设置为wglMakeCurrent(hdc, hrc)之前,您需要的每个渲染线程......然后渲染。

还有其他问题采取般的关怀:

+0

我已经在后台线程上的每个绘图之前使用wglMakeCurrent(请参阅上面的代码)。 – Boris

+0

@Boris你永远不会检查'wglCreateContext,wglMakeCurrent'是否成功在那个链接在我的答案我确实存在问题,所以它值得检查。你还有什么gfx卡和驱动程序?你有没有尝试另一个供应商gfx(排除驱动程序错误)?你还试过'glGetError();'你的GL调用是否工作(获取一些字符串来检查它,而不渲染)?例如,如下所示:[确定英特尔高清显卡通过WinAPI版本](http://stackoverflow.com/a/35826975/2521214) – Spektre

+0

只要在线程A上调用'wglMakeCurrent'就不会使用技巧,如果上下文仍处于活动状态任何其他线程。它必须事先分离。 – datenwolf