openGL swapping buffers only after the window resizes?

Has anyone had the problem after day 235 (initializing openGL on Windows), where they initialize openGL, however when it comes time to SwapBuffers, it seems to.... not? The screen is black, and doesn't actually clear the color until I either: resize the window, maximize the window, minimize and restore the window.

I do know: WM_PAINT IS being sent, that SwapBuffer IS being called, openGL IS initialized, doing the barebones cpu StretchDIBits method does NOT have this problem.

It could very well be a problem with my code, and this is a split of the code (Of course It's going to look a lot like the series).
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
void
initOpenGL(HWND window)
{
    HDC deviceContext = GetDC(window);

    PIXELFORMATDESCRIPTOR desiredFormat = {};
    desiredFormat.nSize = sizeof(desiredFormat);
    desiredFormat.nVersion = 1;
    desiredFormat.iPixelType = PFD_TYPE_RGBA;
    desiredFormat.dwFlags = PFD_SUPPORT_OPENGL|PFD_DRAW_TO_WINDOW|PFD_DOUBLEBUFFER;
    desiredFormat.cColorBits = 32;
    desiredFormat.cAlphaBits = 8;
    desiredFormat.iLayerType = PFD_MAIN_PLANE;

    //================ ._. ======================
    // TODO: Why does ChoosePixelFormat take ~1.7 seconds to call the first time? D:
    LARGE_INTEGER aqPerfFreq;
    QueryPerformanceFrequency(&aqPerfFreq);
    double perfFreq = (double)aqPerfFreq.QuadPart / 1000.0f;

    LARGE_INTEGER start;
    LARGE_INTEGER end;
    QueryPerformanceCounter(&start);

    int suggestedIndex = ChoosePixelFormat(deviceContext, &desiredFormat);

    QueryPerformanceCounter(&end);
    double time = (double)(end.QuadPart - start.QuadPart)/perfFreq;
    char buffer[256];
    sprintf(buffer, "ChoosePixelFormat call time (%0.1f)milliseconds\n", time);
    OutputDebugStringA(buffer);
    //================ ._. ======================

    PIXELFORMATDESCRIPTOR suggestedFormat;
    DescribePixelFormat(deviceContext, suggestedIndex, sizeof(suggestedFormat), &suggestedFormat);
    SetPixelFormat(deviceContext, suggestedIndex, &suggestedFormat);

    HGLRC openGLRC = wglCreateContext(deviceContext);
    if(wglMakeCurrent(deviceContext, openGLRC))
    {
        int a = 0xb; // Note: For a breakpoint
    }
    else
    {
        // Todo: :( , switch to software rendering?
        invalidCodePath;
    }

    ReleaseDC(window, deviceContext);
}

void
blitBackBuffer(win32_backbuffer *buffer, HDC deviceContext, int windowWidth, int windowHeight)
{
#if 0
    StretchDIBits(deviceContext, 
                  0, 0, windowWidth, windowHeight,
                  0, 0, buffer->width, buffer->height,
                  buffer->memory, &buffer->bitmapInfo, 
                  DIB_RGB_COLORS, SRCCOPY);
#endif

    glViewport(0, 0, windowWidth, windowHeight);
    glClearColor(0.75f, 0.75f, 0.75f, 0.0f);
    glClear(GL_COLOR_CLEAR_VALUE);
    SwapBuffers(deviceContext);
}


Of course I'll be on standby if anyone thinks it's a different section of code that's the problem. I'm at a loss on this :/
Where are you calling blitBackBuffer from?
Where does it get deviceContext value? In initOpenGL function you are calling ReleaseDC to release deviceContext handle, are you calling GetDC somewhere else?
In situation where you get black screen, does SwapBuffers return TRUE or FALSE? If it returns FALSE, what value does GetLastError() return?

Edited by Mārtiņš Možeiko on
I am calling blitBackBuffer in... WM_PAINT, and in the main loop.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
LRESULT CALLBACK
windowProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
{
    LRESULT result = 0;

    switch(message)
    {
        case WM_ACTIVATEAPP:
        {

        } break;
        case WM_CLOSE:
        case WM_DESTROY:
        case WM_QUIT:
        {
            GlobalRunning = false;
            AnimateWindow(window, 200, AW_SLIDE|AW_VER_NEGATIVE|AW_HIDE);
            PostQuitMessage(0);
        } break;
        case WM_SIZE:
        {
            int width = LOWORD(lParam);
            int height = HIWORD(lParam);
            resizeBackBuffer(&GlobalBackBuffer, width, height);
        }break;
        case WM_PAINT:
        {
            PAINTSTRUCT paint = {};
            HDC deviceContext = BeginPaint(window, &paint);
            int width =  paint.rcPaint.right - paint.rcPaint.left;
            int height = paint.rcPaint.bottom - paint.rcPaint.top;
            blitBackBuffer(&GlobalBackBuffer, deviceContext, width, height);
            EndPaint(window, &paint);
        } break;
        case WM_SYSKEYDOWN:
        case WM_KEYDOWN:
        {
            char buffer[256];
            sprintf(buffer, "Keydown: wParam(%d) lParam(%d)\n", wParam, lParam);
            OutputDebugStringA(buffer);
        } break;
        default:
        {
            result = DefWindowProcA(window, message, wParam, lParam);
        }
    }

    return result;
}

int CALLBACK 
WinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR commandLine, int showCommand)
{
    WNDCLASS windowClass = {};
    windowClass.style         = CS_HREDRAW | CS_VREDRAW;
    windowClass.lpfnWndProc   = windowProc;
    // windowClass.cbClsExtra    = ;
    // windowClass.cbWndExtra    = ;
    windowClass.hInstance     = instance;
    // windowClass.hIcon         = ;
    windowClass.hCursor       = LoadCursor(0, IDC_ARROW);
    windowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    // windowClass.lpszMenuName  = ;
    windowClass.lpszClassName = "EditorWindowClass";

    if(!RegisterClass(&windowClass))
    {
        int error = GetLastError();
        invalidCodePath;
        return -1;
    }

    HWND window = CreateWindowEx(0,
                                 windowClass.lpszClassName,
                                 "pixeleditor",
                                 WS_OVERLAPPEDWINDOW|WS_VISIBLE,
                                 CW_USEDEFAULT, CW_USEDEFAULT, 960, 540,
                                 0,
                                 0,
                                 instance,
                                 0);

    if(!window)
    {
        int error = GetLastError();
        invalidCodePath;
        return -2;
    }

    RECT windowDim;
    GetClientRect(window, &windowDim);
    int windowWidth = windowDim.right - windowDim.left;
    int windowHeight = windowDim.bottom - windowDim.top;

    initOpenGL(window);
    resizeBackBuffer(&GlobalBackBuffer, windowWidth, windowHeight);

    editor_code editorCode = loadEditorCode();
    editor_state editorState = {};
    editor_memory editorMemory = {};
    editorMemory.debugReadFile =  debugReadFile;
    editorMemory.debugWriteFile = debugWriteFile;
    editorMemory.debugFreeFile =  debugFreeFile;

    // ShowWindow(window, SW_SHOW);
    // Note: AnimateWindow Documentation
    // https://msdn.microsoft.com/en-us/library/windows/desktop/ms632669(v=vs.85).aspx
    // AnimateWindow(window, 200, AW_SLIDE|AW_VER_POSITIVE|AW_ACTIVATE);

    // TODO: Our window seems to appear black
    // This will be hopefully a temporary fix .........
    // ........... Never mind ... it still doesn't work.
    // InvalidateRect(window, &windowDim, false);

    GlobalRunning = true;
    while(GlobalRunning)
    {
        MSG message;

        while(PeekMessage(&message, 0, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&message);
            DispatchMessageA(&message);
        }

        if(editorCode.updateAndRender)
        {
            editor_backbuffer editorBackBuffer = {};
            editorBackBuffer.memory = GlobalBackBuffer.memory;
            editorBackBuffer.width = GlobalBackBuffer.width;
            editorBackBuffer.height = GlobalBackBuffer.height;

            editorCode.updateAndRender(&editorState, &editorBackBuffer, &editorMemory);
        }

        HDC deviceContext = GetDC(window);
        blitBackBuffer(&GlobalBackBuffer, deviceContext, GlobalBackBuffer.width, GlobalBackBuffer.height);
        ReleaseDC(window, deviceContext);
    }

    return 0;
}


deviceContext in initOpenGL is acquired from the first line of code in the function.

1
2
3
4
5
6
7
8
void
initOpenGL(HWND window)
{
    HDC deviceContext = GetDC(window);

    ...
    ...
}


SwapBuffers while this problem occurs returns true, and never returns false.

[Thanks for the quick reply too]

Edited by Jesse Coyle on Reason: bad comments!
Hmmm alright, well I got it working just by continuing with the series...

Turns out it wanted me to start drawing... I guess...

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
void
blitBackBuffer(win32_backbuffer *buffer, HDC deviceContext, int windowWidth, int windowHeight)
{
#if 0
    StretchDIBits(deviceContext, 
                  0, 0, windowWidth, windowHeight,
                  0, 0, buffer->width, buffer->height,
                  buffer->memory, &buffer->bitmapInfo, 
                  DIB_RGB_COLORS, SRCCOPY);
#endif

    glViewport(0, 0, windowWidth, windowHeight);
    
    glClearColor(0.75f, 0.75f, 0.75f, 0.0f);
    glClear(GL_COLOR_CLEAR_VALUE);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    glBegin(GL_TRIANGLES);
    glVertex2f(-1.0f, -1.0f);
    glVertex2f(1.0f, 1.0f);
    glVertex2f(1.0f, -1.0f);

    glVertex2f(-1.0f, -1.0f);
    glVertex2f(1.0f, 1.0f);
    glVertex2f(-1.0f, 1.0f);
    glEnd();

    SwapBuffers(deviceContext);
}


So, I guess I needed to add a glBegin and do some vertex stuff before it would SwapBuffers? Not sure why it would after I resized the window though.

Thank you for being quick on the draw Martins.

Edited by Jesse Coyle on
I may be wrong, but I think I remember StrageZak had similar issue. The problem is with deviceContext. You create OpenGL context with one HDC handle, but when you call SwapBuffers you are passing handle from different GetDC call. I think in Zak's case he got memory leak because of that. Maybe black screen is related to same mistake.

Try calling GetDC only once - before while(GlobalRunning) loop, and use same HDC value for creating OpenGL context and when you are calling blitBackBuffer function. And never call ReleaseDC for it.

If this is really the problem, then it makes sense that it works in situation where resize window. Because when window is resized then WM_PAINT message is generated. In WM_PAINT it is OK to use HDC value from BeginPaint call. But when you are blitting normally (from global running while loop), there you are calling GetDC.

Although if you use OpenGL, then I would recommend simply avoid calling BeginPaint/EndPaint. Just blit whole screen, and call ValidateRect(wnd, NULL) to inform Windows that you have repainted window. It's simpler.

Edited by Mārtiņš Možeiko on