2017-05-10 100 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