9

We have an application where we use both GDI and OpenGL to draw to the same HWND, but exclusively.

Example:

  • initially we are in 2d mode, so we draw on it using GDI
  • then we switch to 3d mode and we draw on it using OpenGL
  • then we switch back to 2d mode and we draw on it using GDI

When switching to 3d mode, we just create an OpenGL context for that HWND and we can draw on it using OpenGL. When switching back to 2d mode, we just destroy the OpenGL context and we can draw to the HWND using GDI.

This worked very well until recently. On Windows 10, for some NVidia cards, this does not work any more if the driver is more recent than 382.05. In this case, when we delete the OpenGL context and draw on the HWND using GDI, the window still displays the last content from OpenGL.

I have checked all available pixel formats. All have the same issue.

Are we doing something wrong, or is it an NVidia bug? Do you see solutions / workarounds?

There is possibility that it is related to NVidia + Intel dual GPU setups, but there is at least one counterexample. Cards on for which we have feedback:

NOT REPRODUCED:

  • GTX 980M, single GPU
  • GTX 1060, single GPU

REPRODUCED:

  • GTX 1060 (Forceware 397.31) + Intel HD Graphics 630
  • Quadro M3000M (Forceware 387.95) + Intel HD Graphics P530
  • Qudrao K110M + Intel HD 4600
  • Quadro P3000 + Intel HD 630
  • Quadro M4000 (Forceware 385.90), single GPU

It is not an option to draw 2d content in OpenGL or vice versa. Also, the application is very performance sensitive, such that it is not an option to draw 2d content to an offscreen GDI image in order to draw it as an OpenGL quad. It is also not an option to destroy and recreate the HWND.

Below is a sample application to reproduce the issue. By default, the application shows a blue background with some text in GDI mode. In OpenGL mode a rotating triangle is shown. Space key is used to switch between modes.

#include <windows.h>
#include <GL/gl.h>
#include <iostream>

#pragma comment( lib, "OpenGL32.lib" )


HWND s_hwnd = 0;
HDC s_hdc = 0;
HGLRC s_hglrc = 0;

bool s_quit = false;

static HGLRC createContext(HWND hwnd, HDC hdc)
{
    PIXELFORMATDESCRIPTOR pfd;
    memset(&pfd, 0, sizeof(pfd));
    pfd.nSize = sizeof(pfd);
    pfd.nVersion = 1;
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL |
        PFD_GENERIC_ACCELERATED /*| PFD_DOUBLEBUFFER*/;
    pfd.iPixelType = PFD_TYPE_RGBA;
    pfd.cColorBits = 32;

    int pf = ChoosePixelFormat(hdc, &pfd);
    SetPixelFormat(hdc, pf, &pfd);
    return wglCreateContext(hdc);
}

static void display()
{
    if (s_hglrc)
    {
        /* rotate a triangle around */
        glClear(GL_COLOR_BUFFER_BIT);
        glRotatef(1.0f, 0.0f, 0.0f, 1.0f);
        glBegin(GL_TRIANGLES);
        glIndexi(1);
        glColor3f(1.0f, 0.0f, 0.0f);
        glVertex2f(0.0f, 0.8f);
        glIndexi(2);
        glColor3f(0.0f, 1.0f, 0.0f);
        glVertex2f(-0.8f, -0.8f);
        glIndexi(3);
        glColor3f(0.0f, 0.0f, 1.0f);
        glVertex2f(0.8f, -0.8f);
        glEnd();
        glFlush();
        SwapBuffers(s_hdc);
    }
    else
    {
        HBRUSH brush = CreateSolidBrush(RGB(0, 0, 255));
        RECT rect;
        GetClientRect(s_hwnd, &rect);
        FillRect(s_hdc, &rect, brush);
        DeleteObject(brush);
        DrawText(s_hdc, L"This is GDI", -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
        GdiFlush();
    }
}

static void toggle_between_GDI_and_OpenGL()
{
    if (!s_hglrc)
    {
        s_hglrc = createContext(s_hwnd, s_hdc);
        wglMakeCurrent(s_hdc, s_hglrc);
        std::cout << "Renderer: " << glGetString(GL_RENDERER) << std::endl;
    }
    else
    {
        wglMakeCurrent(NULL, NULL);
        wglDeleteContext(s_hglrc);
        s_hglrc = 0;
    }
}


LONG WINAPI WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg) {
    case WM_ERASEBKGND:
        return 0;
    case WM_PAINT:
        display();
        PAINTSTRUCT ps;
        BeginPaint(hWnd, &ps);
        EndPaint(hWnd, &ps);
        return 0;

    case WM_TIMER:
        display();
        return 0;

    case WM_SIZE:
        glViewport(0, 0, LOWORD(lParam), HIWORD(lParam));
        PostMessage(hWnd, WM_PAINT, 0, 0);
        return 0;

    case WM_CHAR:
        switch (wParam) {
        case 27: /* ESC key */
            s_quit = true;
            break;
        case ' ':
            toggle_between_GDI_and_OpenGL();
            PostMessage(hWnd, WM_PAINT, 0, 0);
            break;
        }
        return 0;

    case WM_CLOSE:
        s_quit = true;
        return 0;

    case WM_QUIT:
        s_quit = true;
        return 0;
    }

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

static HWND CreateOpenGLWindow()
{
    HWND        hWnd;
    WNDCLASS    wc;
    static HINSTANCE hInstance = 0;

    /* only register the window class once - use hInstance as a flag. */
    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 = L"OpenGL";

        if (!RegisterClass(&wc)) {
            MessageBox(NULL, L"RegisterClass() failed:  Cannot register window class.", L"Error", MB_OK);
            return NULL;
        }
    }

    hWnd = CreateWindow(L"OpenGL", L"GDI / OpenGL switching", WS_OVERLAPPEDWINDOW |
        WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
        0, 0, 256, 256, NULL, NULL, hInstance, NULL);

    if (hWnd == NULL) {
        MessageBox(NULL, L"CreateWindow() failed:  Cannot create a window.",
            L"Error", MB_OK);
        return NULL;
    }

    return hWnd;
}

void executeApplication()
{
    s_hwnd = CreateOpenGLWindow();
    if (s_hwnd == NULL)
        exit(1);

    s_hdc = GetDC(s_hwnd);

    //toggle_between_GDI_and_OpenGL(); // initialize OpenGL

    ShowWindow(s_hwnd, SW_SHOW);
    UpdateWindow(s_hwnd);

    SetTimer(s_hwnd, 1, 50, NULL);

    while (1) {
        MSG msg;
        while (PeekMessage(&msg, s_hwnd, 0, 0, PM_NOREMOVE)) {
            if (!s_quit && GetMessage(&msg, s_hwnd, 0, 0)) {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
            else {
                goto quit;
            }
        }
        if (s_quit)
            goto quit;
    }

quit:

    wglMakeCurrent(NULL, NULL);
    if (s_hglrc)
        toggle_between_GDI_and_OpenGL(); // uninitialize OpenGL
    DestroyWindow(s_hwnd);
    DeleteDC(s_hdc);
}

int APIENTRY WinMain(HINSTANCE hCurrentInst, HINSTANCE hPreviousInst, LPSTR lpszCmdLine, int nCmdShow)
{
    executeApplication();
    return 0;
}

int main()
{
    executeApplication();
    return 0;
}

Update: @Ripi2 and @datenwolf suggested that switching back to GDI is not allowed once we set a pixel format for the window that does not have PFD_SUPPORT_GDI flag. This is an excerpt from SetPixelFormat documentation:

Setting the pixel format of a window more than once can lead to significant complications for the Window Manager and for multithread applications, so it is not allowed.

That is a strong sign that they are correct.


Update 2: I had stated that it is not an option to recreate the HWND. But after rethinking, that seems to be the easiest solution to me.

The code:

#include <windows.h>
#include <GL/gl.h>
#include <iostream>

#pragma comment( lib, "OpenGL32.lib" )


HWND s_mainWnd = 0;
HWND s_childWnd = 0;
HGLRC s_hglrc = 0;
bool s_isOpenGLMode = false;

bool s_quit = false;

static HWND CreateChildWindow(HWND hWndParent);

static HGLRC createContext(HWND hwnd, HDC hdc)
{
    PIXELFORMATDESCRIPTOR pfd;
    memset(&pfd, 0, sizeof(pfd));
    pfd.nSize = sizeof(pfd);
    pfd.nVersion = 1;
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL |
        PFD_GENERIC_ACCELERATED /*| PFD_DOUBLEBUFFER*/;
    pfd.iPixelType = PFD_TYPE_RGBA;
    pfd.cColorBits = 32;

    int pf = ChoosePixelFormat(hdc, &pfd);
    SetPixelFormat(hdc, pf, &pfd);
    return wglCreateContext(hdc);
}

static void display()
{
    HDC hdc = GetDC(s_childWnd);
    if (s_isOpenGLMode)
    {
        /* rotate a triangle around */
        glClear(GL_COLOR_BUFFER_BIT);
        glRotatef(1.0f, 0.0f, 0.0f, 1.0f);
        glBegin(GL_TRIANGLES);
        glIndexi(1);
        glColor3f(1.0f, 0.0f, 0.0f);
        glVertex2f(0.0f, 0.8f);
        glIndexi(2);
        glColor3f(0.0f, 1.0f, 0.0f);
        glVertex2f(-0.8f, -0.8f);
        glIndexi(3);
        glColor3f(0.0f, 0.0f, 1.0f);
        glVertex2f(0.8f, -0.8f);
        glEnd();
        glFlush();
        SwapBuffers(hdc);
    }
    else
    {
        HBRUSH brush = CreateSolidBrush(RGB(0, 0, 255));
        RECT rect;
        GetClientRect(s_childWnd, &rect);
        FillRect(hdc, &rect, brush);
        DeleteObject(brush);
        DrawText(hdc, L"This is GDI", -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
        GdiFlush();
    }
    DeleteDC(hdc);
}

static void toggle_between_GDI_and_OpenGL()
{
    if (!s_isOpenGLMode)
    {
        DestroyWindow(s_childWnd);
        s_childWnd = CreateChildWindow(s_mainWnd);
        ShowWindow(s_childWnd, SW_SHOW);
        HDC hdc = GetDC(s_childWnd);
        s_hglrc = createContext(s_childWnd, hdc);
        wglMakeCurrent(hdc, s_hglrc);
        DeleteDC(hdc);
        std::cout << "Renderer: " << glGetString(GL_RENDERER) << std::endl;

        RECT rect;
        GetClientRect(s_childWnd, &rect);
        glViewport(0, 0, max(rect.left, rect.right), max(rect.top, rect.bottom));
    }
    else
    {
        if (s_hglrc)
        {
            wglMakeCurrent(NULL, NULL);
            wglDeleteContext(s_hglrc);
            s_hglrc = 0;
        }
        DestroyWindow(s_childWnd);
        s_childWnd = CreateChildWindow(s_mainWnd);
        ShowWindow(s_childWnd, SW_SHOW);
    }
    s_isOpenGLMode = !s_isOpenGLMode;
}


LONG WINAPI WindowProc_MainWnd(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg) {
    case WM_TIMER:
        display();
        return 0;

    case WM_CHAR:
        switch (wParam) {
        case 27: /* ESC key */
            s_quit = true;
            break;
        case ' ':
            toggle_between_GDI_and_OpenGL();
            PostMessage(hWnd, WM_PAINT, 0, 0);
            break;
        }
        return 0;

    case WM_CLOSE:
    case WM_QUIT:
        s_quit = true;
        return 0;

    case WM_SIZE:
        if (s_childWnd)
            MoveWindow(s_childWnd, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
        break;
    }

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

LONG WINAPI WindowProc_ChildWnd(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg) {
    case WM_ERASEBKGND:
        return 0;
    case WM_PAINT:
        display();
        PAINTSTRUCT ps;
        BeginPaint(hWnd, &ps);
        EndPaint(hWnd, &ps);
        return 0;

    case WM_SIZE:
        if (s_hglrc && s_isOpenGLMode)
            glViewport(0, 0, LOWORD(lParam), HIWORD(lParam));
        PostMessage(hWnd, WM_PAINT, 0, 0);
        return 0;
    }

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

static HWND CreateMainWindow()
{
    static HINSTANCE hInstance = 0;

    if (!hInstance)
    {
        hInstance = GetModuleHandle(NULL);
        WNDCLASS    wc;
        wc.style = CS_VREDRAW | CS_HREDRAW;
        wc.lpfnWndProc = (WNDPROC)WindowProc_MainWnd;
        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 = L"MainWindow";

        if (!RegisterClass(&wc)) {
            MessageBox(NULL, L"RegisterClass() failed:  Cannot register window class.", L"Error", MB_OK);
            return NULL;
        }
    }

    HWND hWnd = CreateWindow(L"MainWindow", L"GDI / OpenGL switching", WS_OVERLAPPEDWINDOW |
        WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
        300, 300, 256, 256, NULL, NULL, hInstance, NULL);

    if (hWnd == NULL) {
        MessageBox(NULL, L"CreateWindow() failed:  Cannot create a window.",
            L"Error", MB_OK);
        return NULL;
    }

    return hWnd;
}

static HWND CreateChildWindow(HWND hWndParent)
{
    static HINSTANCE hInstance = 0;

    /* only register the window class once - use hInstance as a flag. */
    if (!hInstance)
    {
        hInstance = GetModuleHandle(NULL);
        WNDCLASS    wc;
        wc.style = CS_OWNDC;
        wc.lpfnWndProc = (WNDPROC)WindowProc_ChildWnd;
        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 = L"ChildWindow";

        if (!RegisterClass(&wc)) {
            MessageBox(NULL, L"RegisterClass() failed:  Cannot register window class.", L"Error", MB_OK);
            return NULL;
        }
    }

    RECT rect;
    GetClientRect(hWndParent, &rect);
    HWND hWnd = CreateWindow(L"ChildWindow", L"ChildWindow", WS_CHILD,
        0, 0, max(rect.left, rect.right), max(rect.top, rect.bottom), hWndParent, NULL, hInstance, NULL);

    if (hWnd == NULL) {
        MessageBox(NULL, L"CreateWindow() failed:  Cannot create a window.",
            L"Error", MB_OK);
        return NULL;
    }

    return hWnd;
}

void executeApplication()
{
    s_mainWnd = CreateMainWindow();
    if (s_mainWnd == NULL)
        exit(1);

    s_childWnd = CreateChildWindow(s_mainWnd);

    //toggle_between_GDI_and_OpenGL(); // initialize OpenGL

    ShowWindow(s_mainWnd, SW_SHOW);
    ShowWindow(s_childWnd, SW_SHOW);
    UpdateWindow(s_mainWnd);
    UpdateWindow(s_childWnd);

    SetTimer(s_mainWnd, 1, 50, NULL);

    while (1) {
        MSG msg;
        while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
            if (!s_quit && GetMessage(&msg, NULL, 0, 0)) {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
            else {
                goto quit;
            }
        }
        if (s_quit)
            goto quit;
    }

quit:

    if (s_hglrc)
    {
        wglMakeCurrent(NULL, NULL);
        wglDeleteContext(s_hglrc);
    }
    DestroyWindow(s_childWnd);
    DestroyWindow(s_mainWnd);
}

int APIENTRY WinMain(HINSTANCE hCurrentInst, HINSTANCE hPreviousInst, LPSTR lpszCmdLine, int nCmdShow)
{
    executeApplication();
    return 0;
}

int main()
{
    executeApplication();
    return 0;
}
myavuzselim
  • 365
  • 5
  • 8
  • You should measure the performance of creating/deleting the context vs using `glReadPixels` and use it as a bitmap in GDI vs passing the GDI as a quad to the GPU. – Ripi2 May 29 '18 at 15:02
  • I already know that glReadPixels leads to a big slowdown in our application, espacially on 4k displays. I expect similar performance for using a GDI buffer as a quad in OpenGL. This will hit every frame of the application. However, switching between 2d and 3d modes is not a frequent operation in our application. It will be done when the user explicitly requests (say once per minute, once per hour, or maybe never :)) – myavuzselim May 29 '18 at 15:06

4 Answers4

6

What happened to you is, that up until now you relied on undefined behaviour – actually it never was supposed to work in the first place, and you were just lucky. Once you've set a doublebuffered pixelformat on a window that does not set the PFD_SUPPORT_GDI flag, you no longer can use it for GDI operations.

The OpenGL rendering context does not matter! A lot of people – it behooves me for what reason those who believe it do believe it – suffer from the misconception, that OpenGL render contexts in some way were tied to a particular HDC or HWND. Nothing could be further from the truth. As long as a drawables pixelformat is compatible to a given OpenGL context, that context can be bound to it.

And because there're no ties between your OpenGL render contexts and the windows, all that little dance of destroying and re-creating the context has no meaningful effect whatsoever. Maybe, just maybe, that little dance triggered some codepath in the driver, which made your illegal actions somehow work. But what's more likely is, that you just did the raindance in expectation for it to do something useful, while it was completely bogus in the first place, and you could just have not done it in the first place, to the same effect.

Are we doing something wrong,

Yes, yes you are. You are doing something, that never was allowed or supposed to work in the first place. You just didn't notice it, because so far OSs/drivers didn't make use of the wiggle room afforded to them by this not being allowed for you. However recent developments in GPU/OS/drivers now do make use of the wiggle room, and just steamroll over your code.

It is not an option to draw 2d content in OpenGL .

Why?

It is not an option to draw 2d content in OpenGL or vice versa. Also, the application is very performance sensitive, such that it is not an option to draw 2d content to an offscreen GDI image in order to draw it as an OpenGL quad

Did you actually try and profile it? 10 bucks says this would perform just fine.

datenwolf
  • 159,371
  • 13
  • 185
  • 298
  • Thank you for your answer. I'm now convinced that what we do is undefined behavior. Now I have to find a solution for this. – myavuzselim May 30 '18 at 09:28
  • Our GDI mode is a result of years of development work. We have tried to mimic this mode in OpenGL. While it works, we were unable to get the same quality as GDI mode (think about font rendering). Performance can also get slower with OpenGL depending on content (think of OLE objects). These all could be improved if we could put enough development time to it, but that is a long term solution. – myavuzselim May 30 '18 at 09:38
  • Let's take an example of a complex case: we can render it in 120 ms using GDI, and 400 ms using OpenGL. Please note that our OpenGL case also got many optimizations for a few years, but we still could not get near GDI performance. Please note that this is not a general GDI/OpenGL comparison. Our needs and existing architecture just makes GDI more optimal here It is not vanilla GDI anyway: think of something like AGG used on top of GDI. – myavuzselim May 30 '18 at 09:54
  • 1
    We already have an option to draw 3d content to an FBO and to draw that FBO to GDI. The option is disabled by default because of client complaints about performance on 4k displays. On my system without a 4k display (window size 2200x1000), a simple case is drawn in 4 milliseconds by default. If I enable that option, it is drawn in 4 milliseconds to the FBO, and drawing the FBO to the GDI buffer takes another 22 milliseconds. It gets slower with increased resolution. – myavuzselim May 30 '18 at 10:07
  • I was thinking the other way round: Create a HDC on a DIBSECTION, draw with GDI to that and then use PBO streaming to update a GL texture which you then draw to a quad. – datenwolf May 30 '18 at 11:42
  • I can't apply this in production in near future. There are some existing mechanics that make it more difficult. But I can make a toy implementation to measure performance. In future, this approach can also help us to get rid of these "existing mechanics" (which is our plan). Thanks! – myavuzselim May 31 '18 at 09:10
4

A bit of search at OpenGL on Windows - Generic Implementation and Hardware Implementations reveals:

OpenGL and GDI graphics cannot be mixed in a double-buffered window.

An application can directly draw both OpenGL graphics and GDI graphics into a single-buffered window, but not into a double-buffered window.

And also from PIXELFORMATDESCRIPTOR structure

PFD_SUPPORT_GDI: The buffer supports GDI drawing. This flag and PFD_DOUBLEBUFFER are mutually exclusive in the current generic implementation.

Since you are using OpenGL 1.1, just add PFD_SUPPORT_GDI flag to your PIXELFORMATDESCRIPTOR pfd

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
Ripi2
  • 7,031
  • 1
  • 17
  • 33
  • The sample code was just an example to demonstrate the issue. We can't use PFD_SUPPORT_GDI because we need modern OpenGL (for example, we need geometry shaders). – myavuzselim May 29 '18 at 15:50
  • Ha! Then my advice is use only OpenGL, no GDI at all. – Ripi2 May 29 '18 at 15:52
  • It is said that GDI and OpenGL graphics can't be mixed. And I don't do it actually. I draw exclusively using OpenGL or exclusively using GDI at a time. It is as if new drivers forget to make the underlying GDI buffer active when OpenGL context is destroyed. – myavuzselim May 29 '18 at 15:53
  • But you want to draw to the same window buffer remembering its last state. It can be considered as "mixing". – Ripi2 May 29 '18 at 15:57
  • Yes, it could be considered as mixing. From the resources I researched the meaning of that "mixing" was not clear. The mixing in the less broad sense was already not possible since Vista, when DWM was introduced. But there was no problem with mixing in the broader sense, until this NVidia change. That's why I wonder whether it was intended. – myavuzselim May 29 '18 at 16:00
  • Think about it this way. Originally you can always draw using GDI. Then you create the OpenGL context and can draw using OpenGL. If you take the broader definition of mixing, that should also be mixing, isn't it? Then this should not be possible too. – myavuzselim May 29 '18 at 16:06
  • It's not neither your POV nor mine. It's Microsoft and graphics cards vendors POV. Windows 10 changed some things... – Ripi2 May 29 '18 at 16:09
  • Thank you for your input. With your answer and the one of @datenwolf, I'm convinced that what we do is undefined behavior. Now I have to find a solution for this. – myavuzselim May 30 '18 at 09:26
2

I would not destroy the GL context. Instead I would try to change the

glViewport(x0,y0,xs,ys);

to small area in some corner or even hidden like

glViewport(0,0,1,1);

That way even if bug persist the user will see only pixel artifact in corner somwhere most likely not even notice it (especially if rendered with the same color as background).

Spektre
  • 49,595
  • 11
  • 110
  • 380
  • Thank you for the idea. Unfortunately the behavior is still the same. GDI output is invisible even if the OpenGL viewport is resized to 1 pixel. The behavior is also the same if I resize OpenGL viewport to 1 pixel and destroy OpenGL context. – myavuzselim May 30 '18 at 09:13
  • @myavuzselim Then last thing I can think of is that your buffers did not swap back to the GDI one. Try to add button to your app and when you click it call `SwapBuffers(s_hdc);` if ti makes a difference?. Also try to check if Handle of the gfx area did not change ... (that is just wild guess as in Win10 is any bug possible). btw I sometimes have 2 windows for things like this and dock to main App workspace only the one I want to see (the other one is set to invisible) – Spektre May 30 '18 at 10:53
  • Unfortunately calling `SwapBuffers(s_hdc);` does not change the output, independent from whether I destroy the GL context or not, or whether I call `glViewport(0,0,1,1)` or not. – myavuzselim May 30 '18 at 12:05
0

I dont know. Maybe is because I am from the Android programming era. The solution to the problem is so simple. Let's say you have a main window. You then have a content window that covers the main window client area that you actually draw to. You can then destroy this content window and replace it with another content window to draw 2d graphics. And do this continually. Like; creating and destroying windows is very fast.

If you are on a super ridiculously slow machine; that is powered by water; and dont want to continuously create and destroy windows. You could have your main window draw 3d graphics. And the content window draw 2d graphics. Then you could hide the content window to display the 3d window. And show the content window, to hide the 2d window.

user13947194
  • 337
  • 5
  • 7