Day 472 - Resizing the OpenGL window

Hi everyone!

On Day 472, Casey separates the win32 message loop from the actual game loop
by moving the game loop to a different thread. This allowed the game
to still render, even when the window is being moved / resized.

I have noticed however that when setting up my window class like Casey did,

1
2
3
4
5
6
7
8
9
WNDCLASS window_class;
Zero_Struct(window_class); {
    window_class.style          = CS_HREDRAW|CS_VREDRAW|CS_OWNDC;
    window_class.lpfnWndProc    = win32_window_callback;
    window_class.hInstance      = instance;
    window_class.hCursor        = LoadCursor(0, IDC_ARROW);
    window_class.hbrBackground  = 0; // /* NOTE(Matyas): Don't redraw! */
    window_class.lpszClassName  = "openglapp";
    }


I still get blinking when resizing my OpenGL window.
Casey towards the end of the stream said that this issue was mainly because of the
way the rendering engine was architected.
In my case however, I have tried setting up a very bare-bones renderer that just draws
a single triangle and the issue still remains.

I have tried messing with WM_PAINT, WM_ERASEBKGND with no success.

Does anyone know about this issue and how it could be solved?

Edited by Matyas on Reason: Initial post
I believe CS_HREDRAW and CS_VREDRAW styles are making flickering to happen.

They send background erase/paint messages. I believe you can make OS ignore those by returning nonzero value for WM_ERASEBKGND message and calling ValidateRect in WM_PAINT.
Hi Mārtiņš,

Thank you for your help!
I tried what you suggested, I still getting flickering though unfortunately :(
Do you think this can be caused by something else?
I should probably add I have a direct3D11 backend, an I have none of these issues there.

 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
// WinMain(...)

WNDCLASS window_class;
Zero_Struct(window_class); {
    window_class.style          = /*CS_HREDRAW|CS_VREDRAW|*/CS_OWNDC;
    window_class.lpfnWndProc    = win32_window_callback;
    window_class.hInstance      = instance;
    window_class.hCursor        = LoadCursor(0, IDC_ARROW);
    window_class.hbrBackground  = 0; // (HBRUSH)GetStockObject(BLACK_BRUSH);
    window_class.lpszClassName  = "BottleBee";
}

// win32_window_callback(...)

inline LRESULT CALLBACK
win32_window_callback(HWND window, UINT message, WPARAM wparam, LPARAM lparam)
{
    LRESULT result = 0;
    switch (message) {

        case WM_DESTROY:
        case WM_CLOSE: {
            win32_running = false;
        } break;
        
        case WM_PAINT: {
            
            ValidateRect(window, 0);

        } break;
        
        case WM_ERASEBKGND: {
            
            result = 1; // NOTE(Matyas): Non-zero.
        } break;

        case WM_GETMINMAXINFO: {
           
            LPMINMAXINFO info = (LPMINMAXINFO)lparam;
            info->ptMinTrackSize.x = 640;
            info->ptMinTrackSize.y = 360;

        } break;

        default: {
            result = DefWindowProc(window, message, wparam, lparam);
        } break;
    }

    return(result);
}

Edited by Matyas on
Ok, then the issue is somewhere else. It's hard to guess where...
How are you reading current window size? Maybe it is set to something like (0,0) and you simply don't render anything? Where are you doing SwapBuffers call? If I'm not mistaken it should be done on same thread as where HWND was created.
Aha, the SwapBuffers part may be the issue; I am _not_ calling it on the same
thread where I created my window.
However, I am not sure how to move SwapBuffers out of my game loop... wouldn't that
halt the program waiting for vsync? Doesn't also need to be on the same thread as the GL context? (I create my GL context in win32_program_loop right now)

Here is my current code:

 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
inline DWORD
win32_program_loop(LPVOID user_pointer)
{
    Win32_State *win32_state = (Win32_State *)user_pointer;
    
    // NOTE(Matyas): initialize stuff...

    UpdateWindow(win32_state->window_handle);
    ShowWindow(win32_state->window_handle, SW_SHOW);

    while(win32_running) {

        uint32 width, height;
        RECT window_size;
        GetClientRect(state->window_handle, &window_size);
    
        width  = (window_size.right - window_size.left);
        height = (window_size.bottom - window_size.top);
        
        Render_Commands render_commands;
        render_commands = default_render_commands(width, height, render_memory);

        hotcode.update_and_render(&memory, &render_commands);

        Rect2 draw_region = rect2(0, 0, (f32)width, (f32)height);

        renderer_execute_commands(&renderer, &render_commands, draw_region);
        
        // NOTE(Matyas): This is just SwapBuffers (in a separate dll)
        renderer_present_buffers(&renderer, 1);
    }

    return(0);
}



int __stdcall
WinMain(HINSTANCE instance, HINSTANCE previous_instance, LPSTR, int)
{
    Win32_State win32_state;
    Zero_Struct(win32_state);

    WNDCLASS window_class;
    Zero_Struct(window_class); {
        window_class.style          = /*CS_HREDRAW|CS_VREDRAW|*/CS_OWNDC;
        window_class.lpfnWndProc    = win32_window_callback;
        window_class.hInstance      = instance;
        window_class.hCursor        = LoadCursor(0, IDC_ARROW);
        window_class.hbrBackground  = 0; // (HBRUSH)GetStockObject(BLACK_BRUSH);
        window_class.lpszClassName  = "BottleBee";
    }

    if (RegisterClass(&window_class)) {
        
        win32_state.window_handle = CreateWindowEx( 0,
                                                    window_class.lpszClassName,
                                                    Stringize(PROGRAM_NAME),
                                                    WS_OVERLAPPEDWINDOW,
                                                    CW_USEDEFAULT, CW_USEDEFAULT,
                                                    1280, 854,
                                                    0, 0, instance, 0);
    
        if (win32_state.window_handle) {
        
            DWORD worker_thread_id;
            HANDLE worker_thread = CreateThread(0, Megabytes(16), win32_program_loop, &win32_state, 0, &worker_thread_id);
            CloseHandle(worker_thread);

            win32_running = true;
            while(win32_running) {
                win32_process_pending_messages(&win32_state);
            }
        }

    } else {
        Assert(false, "failed to register win32 window class");
    }

    return(0);
}


Edited by Matyas on
I think another issue could be that you are not allowed to touch HWND from other threads. That means you can use HWND only in that thread which created it. I'm not 100% sure on this, but long time ago this was definitely an issue when you accessed HWND from other threads.

To have SwapBuffers on main thread you'll need to move all rendering commands to main thread (including GL context). Or you probably can share two GL context's created in different threads.

Edited by Mārtiņš Možeiko on