Setting FPS



  • I am encountering unavoidable slowdown in my project, and I would like to limit it to (e.g.) 30fps to have extra time each frame.

    Is the best way to do that similar to the example code shown for the time() function? Namely, at the end of each frame, make the program "wait" until 1/30 of a second has passed?

    (I tried with the sleep() function, but that resulted in more showdown)



  • The following should do the trick, but the fps number indicator does not seem to be influenced by the sleep command:

    int fps = 30
    loop
        sleep((1/fps)  - deltaTime())
        update()
    repeat
    

  • Fuze Team

    I'm not entirely following what you are trying to acheive because using sleep won't give you any extra usable time since Fuze will just sleep during that time and you won't be able to do anything. Although the technical answer I was given wasn't quite this straightforward, you should consider that update() will automatically wait for the next vblank if you want to use old-school terms. But that's only really of any consequence if you have any spare time left which it sounds like you don't.

    I think I get what you want, and the simple answer is that I don't think you can do it. The example in the help for time is allowing you to wait for a period of seconds to pass and that's certainly not what you want.


  • F

    @PB____
    I like how you've done that it's very clever



  • @Martin
    Originally I read the question the same way. I did notice that FUZE doesn't completely pause during a sleep command (you can still use + to close the game, and the FPS numbers keep refreshing). But I didn't assume that FUZE would schedule garbage collection tasks during a sleep command, or anything fancy like that.

    However, if the performance of your game varies between 30 and 60 fps all the time, then the user may notice performance issues. If you cap the fps at a consistent 30 fps, then the user gets used to it for the game and have a better user experience overall. So on second thought, I assumed this was the nature of the original question :)



  • @PB___ Thank you for your code snippet! It makes sense, and I look forward to trying it when I am on front of my Switch.

    And you summarized my issue well: Rather than let the game fluctuate between about 45 and 60 fps (depending on things like how many enemies are present), I thought "pinning" the rate to 30 fps would be smoother. I saw that suggested on another thread, but could not find that nor how to implement.



  • Found out something that might help if it's not too late. FUZE buffers at least one frame. By 'buffer', I mean that there is a queue. If you're pumping out 60 FPS with ease, you'll have the frame you're currently working on and the one being displayed; but in between those, there can be at least one waiting to be displayed. When you call update(), your current frame doesn't get displayed. You do have to wait for the next vertical sync, but when that comes, only the frame from the queue gets displayed, your then current frame goes to the queue, you get given back control and can work on the next frame. Given that we can get tearing free 45 FPS, I suppose it's just fully triple buffering all of the time? That would make sense, it's definitely a very beginner friendly setup.

    What this means is: If you do nothing every other frame, you can achieve a solid lock to effectively 30 FPS (FUZE will happily display 60 because it counts the empty frames, too, and the frame time graph will go a bit crazy)

    loop
      float dt = deltaTime()
      // empty frame
      update()
      dt += deltaTime() // dt will now be 1/30 in the best case
      clear()
      // do usual work, using dt as timestep
      update()
    repeat
    

    Or more robustly, you can do all or some of your non-rendering tasks during the empty frame. This requires a bit more care with the deltaTime since you need a good value during both steps:

    float dt = 1/30
    loop
      float nextDeltaTime = deltaTime()
    
     // do computational only work, using dt as timestep
    
      update()
      nextDeltaTime += deltaTime()
      clear()
    
      // do rest of the work, still using dt as timestep
    
      update()
      dt = nextDeltaTime
    repeat
    

    Other schemes may work, too. Key is to call update() one extra time per iteration with no preceding draw calls and take care to collect all deltaTime() values.

    Needless to say, this relies on undocumented behavior. It's not clear that an empty update() call doesn't cause issues (inserted black screens, for example), and the frame buffering behavior can also change.


  • F

    Interesting post, thanks.



  • @Z-Mann Thank you for this idea! It makes sense. I think I ran into this buffer queue sideways when I tried the approach @PB_____ suggested above. Somehow, I was not able to figure out the "right" dt to correctly get a "do-nothing" frame.

    I actually tried something similar to your suggestion, the division of computation and rendering across two frames. Unfortunately, I messed it up and got "flashing" back and forth between blank frames and my scene. I think I will try again - it seems like the most promising approach, as far as I can understand right now!


Log in to reply