rust_minifb icon indicating copy to clipboard operation
rust_minifb copied to clipboard

How can I query the display framerate, and/or hook into vsync?

Open ghost opened this issue 4 years ago • 7 comments

For various reasons, my game logic has to be decoupled from rendering. It updates much faster than 60hz and it sometimes rolls back and recomputes game frames as part of network prediction code.

A simplified version of my game loop looks like this:

    let frame_length = std::time::Duration::new(0, 4_166_166);
    let mut timer = std::time::Instant::now();

    while window.is_open() && !window.is_key_down(Key::Escape) {
        let elapsed_time = timer.elapsed();

        if elapsed_time >= frame_length {
            canvas.clear();
            let our_input = Input::from_window(&window);

            game.update([our_input; 8]);

            // TODO: let frame_ready = elapsed_time >= some_runtime_determined_refresh_rate;
            // if frame_ready {
            graphics::draw(&game, &mut canvas);
            // }

            window.update_with_buffer(&canvas.buffer).unwrap();
            timer = std::time::Instant::now();
        }
    }

I believe I can just check the refresh rate at application startup and check the timestep against the corresponding display frame length. It might be better to check for an explicit signal that vsync is ready. I'm not sure.

I think it would be a bad idea to couple my game loop to vsync, and that it's better to let the game logic run with its own counter.

I think this might be a good first issue for me to contribute to if there's not currently a way to expose this. Let me know if that's something you want.

ghost avatar Dec 18 '19 17:12 ghost

Hey,

Yeah so currently minifb doesn't expose any vsync features as you may have noticed.

(just as a side not to the code above: if you only want to do the message bump for the window you can call update() instead of update_with_buffer() as update_with_buffer takes a bit more time (as it needs to display it while update just does the message/input stuff)

I haven't worked much with vsync in windowed mode in general (just full screen) so I'm not sure what the implications are. That being said if in general using vsync in windowed mode works good there is no reason why minifb can't support it. Also there are likely diffrences between the platforms here to consider also. If you want to contribute this support that would be great!

I think what would be best is to start looking around a bit how it works on macOS, Linux and Windows to just get a bit of feel what is involved and then start with one platform to implement the support.

emoon avatar Dec 18 '19 17:12 emoon

Okay, I'll have to do some research. I'm seeing some judder and I also only want to spend the CPU on software rendering when I have to, so I figured this would be a good path.

ghost avatar Dec 18 '19 18:12 ghost

Hi guys,

Indeed that would be an awesome feature, some time ago I wrote some C++ demos / utilities with glfw, all I had to do was to set the swap interval (the "frameskip") at the start with glfwSwapInterval(), and the swap buffer function waited the VBL to swap the buffers and go on with the program, no need to count elapsed time, which will never be as smooth.

nicolasbauw avatar Jan 13 '20 12:01 nicolasbauw

From what I've seen in the source / read on the web:

  • in minifb, for the windows OS, the rendering is done via GDI
  • I found that post on msdn:

You'd switch to DirectX-based UI technology if you want to get vsync. In modern versions of Windows, if desktop composition is not turned off, your GDI program would draw to a memory surface where there is no refresh rate. Windows then decides when to flip the surface to the video memory.

I also exhumed that interesting blog post.

Unfortunately I can't help much with that, I'm kind of a rust noob and just have some older experience with GLFW in C/C++.

nicolasbauw avatar Jan 15 '20 10:01 nicolasbauw

I guess switching to OpenGL would work as well

emoon avatar Jan 15 '20 11:01 emoon

I think that it's possible to avoid texture creation, update, and render of textured rectangle (who would here act as a simili framebuffer) at every frame with functions like glCopyImageSubData or glBlitFramebuffer which act more like a low-level framebuffer blit. I did never try that though. That said, a texture update at every frame is maybe not a real issue. Basic pixel drawing (like glDrawPixels) seems now deprecated.

nicolasbauw avatar Jan 15 '20 12:01 nicolasbauw

on macOS I use Metal and texture updates so this works fine. I also have an experimental branch here https://github.com/emoon/rust_minifb/tree/opengl-support for OpenGL support on Linux. I'm pretty sure this could be generalized to support Windows and Linux.

emoon avatar Jan 15 '20 14:01 emoon