[Day 180] - Taking too much time to draw buffer to window?

Hello all!

I have just finished implementing the graphical debug display of day 180 (it's really cool :D ) and I found a weird result.

This is the image:


Blue is game update and render
Pink is the wait to hit 33.3ms
Cyan is the draw command to the Window's window

In the stream, Casey hits the 33.3ms mark perfectly, and the draw part doesn't overshoot it like mine. My sleep works nicely, though.

Does anyone have any idea what might it be?
Or maybe I should just wait to worry about in when I implement the hardware render?

For reference this is the code:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
            win32_record_time_stamp(&frame_end_info, "Framerate Wait Completed", win32_get_seconds_elapse(last_counter, win32_get_wall_clock()));
            
            {
                auto device_context = GetDC(window);
                win32_draw_buffer_to_window(&win32_backbuffer, device_context, win32_get_window_dimention(window));
                ReleaseDC(window, device_context);
            }
            
            flip_wall_clock = win32_get_wall_clock();
            
            auto temp = old_game_input;
            old_game_input = game_input;
            game_input     = temp;
            
            end_counter = win32_get_wall_clock();
            win32_record_time_stamp(&frame_end_info, "End Of Frame", win32_get_seconds_elapse(last_counter, win32_get_wall_clock()));
            


And this is win32_draw_buffer_to_window (at this point it is doing the 'false' if path):

 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
internal void
win32_draw_buffer_to_window(Win32_Offscreen_Buffer *buffer, HDC device_context, Win32_Window_Dimention window_dimention) {
    
    if (window_dimention.width  >= buffer->width*2 &&
        window_dimention.height >= buffer->height*2) {
        
        StretchDIBits(device_context,
                      0, 0, 2*buffer->width, 2*buffer->height,
                      0, 0, buffer->width, buffer->height,
                      buffer->memory, &buffer->info, DIB_RGB_COLORS, SRCCOPY);
    } else {
        
#if 0
        int offset_x = 10;
        int offset_y = 10;
        
        PatBlt(device_context, 0, 0, window_dimention.width, offset_y, BLACKNESS);
        PatBlt(device_context, 0, 0, offset_x, window_dimention.height, BLACKNESS);
        PatBlt(device_context, offset_x + buffer->width, 0, window_dimention.width, window_dimention.height, BLACKNESS);
        PatBlt(device_context, 0, offset_y + buffer->height, window_dimention.width, window_dimention.height, BLACKNESS);
        
#else
        int offset_x = 0;
        int offset_y = 0;
        
#endif
        
        StretchDIBits(device_context,
                      offset_x, offset_y, buffer->width, buffer->height,
                      0, 0, buffer->width, buffer->height,
                      buffer->memory, &buffer->info, DIB_RGB_COLORS, SRCCOPY);
    }
}


Thanks,
Dan Zaidan

Edited by Dan Zaidan on
The link to the image doesn't work, maybe it's a private dropbox link ?

Edited by Simon Anciaux on Reason: typo
Oops... :D
Thanks for the heads up. It was a problematic dropbox link indeed.
I didn't rewatch the episode so maybe I'm missing something.

If the code is waiting to hit 33ms and than flip the buffer (StertchDIBits), it seems right that the cyan part is above the line. The cyan part is quite small, about 1ms which seems plausible (since it's going through GDI I think). If that's what's happening the frame takes a little more time than 33ms.

If you want the flip to finish at 33ms, you would need to keep track of how long it takes, and wait that amount of time less before flipping, but it would probably not be accurate.

Another way to visualize the situation would be to consider the flip being at the start of the frame (flipping the previous frame). You would have:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
--- frame boundary
- flip frame n-1
- gather input
- game code
- rendering
- wait to hit 33ms.

--- frame boundary
- flip frame n
...

That way, your frames are 33ms, except the first one.

Without OpenGL/DirectX I don't think you can properly sync with the frame buffer flip.
On Windows 8/10 there is function DwmFlush() which will wait until vsync happens. So instead of trying to figure out how to long to Sleep() just call DwmFlush.

This function exists also on Windows 7 and Vista, but it works only when Aero compositor is enabled.
Thanks so much for your suggestions.
Given that I don't have much experience yet, it's nice to try out different solutions.

This image is the result of Simon's idea: flipping on the beginning of the loop.

The graph colors changed :P but the result was pretty nice: the wait lines up perfectly. :D


This is the graph showing Martin's recommendation (DwmFlush()).

In theory this is the only correct result, right?
Is it weird that it doesn't line up 100% correctly? Will the hardware vsync through Opengl line up perfectly every time or is a little bit of variation also expected?

Edited by Dan Zaidan on
I don't know how DwmFlush() works exactly. I thought it was returning after the flip, so it would actually present the image with one frame of lag (there is always more lag then you think). Maybe Martin will enlighten us ?

The graph not lining up correctly might be caused by the fact that, even if the vblank happens every 16.66ms, the moment you start timing things is not instantly after that. So it may be that the top of the graph actually lines up, but the bottom isn't. This is speculation on my part, don't take it as a fact.

There is always small variations in frame time, because you are sharing the CPU with other applications, and the operating system decides which application runs and how long it can run before giving back the hand.

There was a link to an article posted in the handmade.network forums (I believe) that was an in depth description of what is happening with Windows 8/10 Desktop Window Manager. I remember that it explain how windows handle vsync, how much frame of lag to expect... depending of if the application was windowed, exclusive fullscreen on fullscreen window. I can't find it. By any chance, does anyone have that link ?
Yeah, there always will be small variance. Even with OpenGL.

I believe this is the videos that talks about presentation modes on Windows 10: https://www.youtube.com/watch?v=E3wTajGZOsA
Thanks. I thought it was an article so I didn't search youtube.
Thanks guys! :D