2017-05-10 23 views
5

Stworzyłem prostą aplikację OpenGL dla systemu Windows. Tworzy okno, a następnie używa poleceń OpenGL do narysowania trójkąta. Działa to zgodnie z oczekiwaniami.Rysunek OpenGL w innym wątku

Później chciałbym zamknąć mój kod rysunek w DLL, tak aby można go było użyć w aplikacji WinForm C#, aby narysować na WinForm. W tym celu przeniosłem kod rysunku do oddzielnej klasy i wątku. Moim pomysłem jest, że mogę "przypiąć" moją klasę do dowolnego istniejącego okna i pozwolić, by nić do niej narysowała.

Niestety rzeczy wydają się nie takie proste. Kiedy odłączyłem tworzenie okna i rysowanie rzeczy na różne wątki, ekran pozostaje cały czarny. Połączenia rysunkowe wydają się już nie działać.

Czy istnieje sposób, aby mój rysunek całkowicie niezależny od tworzenia okna i głównego wątku UI?

EDIT: Oto niektóre kodu :-)

To mój renderujący (działa gdy wywołana z wątku UI, nie działa, gdy wywołana z wątku tła):

// 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); 
} 

ten sposób wzywam renderer z gwintem tle:

// 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)); 
    } 
} 

I Oto mój główny klejenia to wszystko razem:

// 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

Okay, twój opublikowany kod nie ma nic wspólnego z GLFW, więc dlaczego o tym wspominałeś? – InternetAussie

+0

Używałem wcześniej GLFW. Aby uprościć wszystko, całkowicie usunąłem go z mojego projektu (następnie dodałem powyższy kod do tego tematu). Ale problem pozostaje ten sam, z lub bez GLFW. – Boris

Odpowiedz

6

dwoma podstawowymi zasadami są:

  • Wszelkie podane OpenGL renderowanie kontekst może być aktywny tylko jeden wątek na raz.
  • I każdy wątek może mieć tylko jeden kontekst OpenGL dokonano aktualnie aktywne w danym momencie.

Nie ma jednak ścisłego powiązania między konkretnymi kontekstami OpenGL i poszczególnymi oknami (Win32 test program for using a single OpenGL context with multiple windows) lub konkretnym wątkiem. Zawsze możesz migrować kontekst OpenGL z jednego wątku do drugiego.

Dwa zwykłe podejścia są:

  • Tworzenie okna w wątku A, przekazać uchwyt okna do gwintowania B i tworzenia kontekstu OpenGL tam i wtedy.

lub

  • Tworzenie okien i kontekstów OpenGL w wątku A, sprawiają, że kontekst (y) nieaktywną w A, przechodzą uchwyty w wątku B i czynią je tam aktywny.
0

trzeba OpenGL kontekstu za każdym oknie docelowym ... i też trzeba zamienić konteksty przed renderowania ... zobacz jak wglMakeCurrent stosowany jest inaczej wszystko renderowanie jest wykonywane dla ostatnio wybranego kontekstu.

Więc na każdym renderowania wątku przed renderowania ustawić wglMakeCurrent(hdc, hrc) do okna trzeba ... a potem render.

Istnieją także inne kwestie, aby dbać o jak:

+0

Już używam wglMakeCurrent przed każdym rysowaniem na wątku tła (zobacz powyższy kod). – Boris

+0

@Boris nigdy nie sprawdza czy 'wglCreateContext, wglMakeCurrent' się powiódł W tym linku W mojej odpowiedzi mam problem dokładnie tam, więc warto go sprawdzić. Jaką kartę Gfx i sterownik masz? Czy próbowałeś innego producenta gfx (aby wykluczyć błąd sterownika)? Czy wypróbowałeś również 'glGetError();' Czy twoje wywołania GL działają (otrzymujesz ciąg, aby sprawdzić to bez renderowania)? Na przykład: [Określ wersję karty graficznej Intel HD za pośrednictwem WinAPI] (http://stackoverflow.com/a/35826975/2521214) – Spektre

+0

Po prostu wywołanie 'wglMakeCurrent' na wątku A nie spowoduje oszustwa, jeśli kontekst jest nadal aktywny każdy inny wątek. Należy go wcześniej odłączyć. – datenwolf