Log In  


I'm having a lot of issues getting my game to run smoothly at 60fps.

90% of the time it works fine at a smooth 60fps, but occasionally it'll will switch back and forth between 30fps and 60fps in quick succession. This would normally be okay, since from what I understand the update loop is supposed to run twice for every 30fps draw frame, if it's been throttled down. But this doesn't seem to work as intended, because what would normally be smooth movement will speed up and slow down during frequent 60 -> 30 -> 60 switches, so the updates and draw calls are definitely not called in a consistent manner. This problem seems to be even worse in the html export. Stat(1) is never above 0.35 in 60fps mode, so I'm not sure why Pico-8 is shifting down to 30 in the first place. This is on a very recent Mac laptop.

Has anyone else experienced this behavior? Any suggestions? I really want to use the 60fps mode, but smooth performance is a priority.

Thanks in advance! Love the Pico-8 platform so far, but this has been frustrating me.

2


Yeah. I don't think @zep or maybe SDL is handling vsync correctly. Probably there are issues with assuming the video refresh is exactly 60Hz and trying to do math according to that, instead of just taking as implicit that each time you get a vsync, that much time has passed. There can be problems with audio sync or pitch when you do that, though.

To be fair, it's a tricky thing to get right. Most of the time, it's done incorrectly, especially when the display runs at TV's 59.94Hz instead of true 60Hz. Computer monitors are pretty inconsistent about running true 60 vs. TV's pseudo-60. You really have to be flexible when deciding whether or not you're hitting the frame "on time" in code. It's easy to accumulate either a real or perceived cumulative error and handle it the wrong way.


Hmm, that definitely sounds like it could be the issue.

I'd love to have the option to turn off the fps locking if possible, and just deal with the occasionally slowdown. I think for some games that's preferable to locking the framerate, especially if the framerate switching is finicky.

I've been trying to write around this in code but haven't had much success.


I was wondering if some kind of hack unlocked framerate could be possible.

you could obviously do something like

while true do
 custom_update()
 custom_draw()
end

But then using flip() to draw the graphics buffer to the screen would lock the FPS at 30.

Would it be possible to pset or poke or memcpy or whatever to write graphics the screen outside of using any of the functions that limit the fps?

Then what about delta time? There's a stat([number]) that gets the current second but AFAIK not milliseconds. So I guess you could count how many times update is called before the seconds counter is updated then divide them to get an FPS and approximate delta time but that's not really much good for slowing down or speeding up drawing to have a consistant framrate...?

I dunno, not put any time into it just daydreaming.


I've had a problem that seems like this, when running a Pi (1B) and a (15kHz) CRT via HDMI > VGA (RGB).

I thought it might be due to the display mode not being exactly 60Hz...or possibly just that the Pi 1 is struggling (although stat doesn't indicate that is the case).

I was planning on getting a Pi 3 to try out with the same monitor to see if that helps...


I've been seeing this recently on an M1 Mac laptop - seems to happen for any 60fps cart, regardless of how heavy the cart's own processing is. It seems to happen at a rate of (very) roughly one second on and one second off, and exclusively for displays set to a 60 Hz refresh rate or below:

  • External display set to 60 Hz: periodic throttling
  • Built-in display set to 60 Hz: periodic throttling
  • Built-in display w/ ProMotion enabled (120 Hz?): no throttling

I've tested with lower refresh rates too and the behavior looks (to me) very similar to 60 Hz, until the display is set to 30 Hz at which point I get 30fps all the time. 60 Hz does look slightly more stable than 59.94 Hz (the built-in display offers separate settings for these, not sure how accurate it is), but the results aren't so distinct that I can't be sure I'm not just imagining the difference. Sometimes it can take a moment after starting the cart for the throttling to kick in. Sometimes the throttling won't kick in at all unless a non-Pico-8 window is the foreground window, but the throttling will happen 100% of the time once Pico-8 is not focused.

Also, like the OP, I've observed that the update/draw scheduling is somehow wonky here. In a cart that has a pretty clean update/draw split, objects that should be moving at constant speed seem to accelerate / decelerate roughly in time to the throttling.


1

You can lock the framerate down pretty tightly by having the two following arguments in your Pico-8 configuration guys:

-foreground_sleep_ms 1
-software_blit 0

Also try out this code to confirm it works:

function _init()
  patd={[0]=-23131,23130}
  f=0
end

function _update60()
  fillp(patd[f])
  f=1-f
  rectfill(0,0,127,127,7)
end

Thanks! Unfortunately, this configuration does not reduce throttling for me, I can't tell the difference between the behavior with the default config and with these changes.


Hi @epbarger -- apologies for the late reply to this. It is possible to turn off framerate control in config.txt, but I wonder if there is something else going on:

host_framerate_control 0

(edit config.txt while PICO-8 is closed and then run it again)

While it's off, PICO-8 will only drop from 60fps to 30fps when the virtual PICO-8 cpu is over 1.0. I'd be interested to know if turning host_framerate_control off helps for the cases in this thread. The vast majority of Windows and Mac machines should be able to handle 60fps, so perhaps there should be a separate default for those platforms.

Otherwise, there might be a general performance issue. To see the actual real-world FPS, open the cpu profile (ctrl-p) while running a cart and hold down LSHIFT. This FPS is derived from how long it took to produce a frame, so doesn't take vsync into account -- the number is a raw sample so jumps around a lot but you should get well over 100fps on any Windows / Mac.

@minsoft -- PICO-8 does struggle on the 1 series (I tried to optimise for it the best I could!), and that doesn't show up in the CPU profiler thing, which only shows the virtual PICO-8 CPU while ignore what's happening in the real world. Raspi 2 or higher should be no problem, but there is an issue with the default OpenGL driver that seems to cause blitting slowdown for any SDL2 programs, and I'd suggest reverting to the legacy one unless it produces other issues. It can be adjusted from commandline using $raspi-config -> Advanced -> GL Drivers.

It's interesting @luchak that setting the display's rate at > 60Hz makes a difference. I'd be curious to see what kind of results you get with "host_framerate_control 0" in config.txt, and what the framerate is (CTRL-P while running a cart, hold LSHIFT). The default background_sleep_ms is 10 which should really still allow 60fps on a modern machine. So I'm betting that there's a huge per-frame blit bottleneck or something.


@zep thank you for the response! I didn't know about host_framerate_control or about holding down shift with the performance monitor open. Here are the new results (all tests done with #notepool-0, since it never goes above 0.07 CPU for me):

  • Built-in display, 120 Hz, hfc=0: 166-333 FPS, constant 60/60
  • Built-in display, 60 Hz, hfc=0: 38-111 FPS, constant 60/60
  • External display, 60 Hz, hfc=0: 31-250 FPS, constant 60/60
  • Built-in display, 120 Hz, hfc=1: 166-333 FPS, constant 60/60
  • Built-in display, 60 Hz, hfc=1: 43-250 FPS, periodic drops to 30/60 (roughly correlated with lower FPS values)
  • External display, 60 Hz, hfc=1: 32-250 FPS, periodic drops to 30/60 (FPS seemed to be a leading indicator of whether a drop was coming?)

Methodology here was pretty lax, mostly just capturing a few seconds of video and noting the maximum/minimum FPS I observed. All other settings in my config.txt are at default values (or if there are any changes I didn't make them by hand...).

I redid the external monitor tests with a higher-CPU cart (median/mean CPU 0.86/0.78) and got:

  • External display, 60 Hz, hfc=0: 71-250 FPS, constant 60/60
  • External display, 60 Hz, hfc=1: 43-333 FPS, periodic drops to 30/60

Finally, not to undermine all of the above, but ... I've tried to repro a few of these numbers in a very sloppy way, and I think perhaps the only lesson here is that the FPS floor for the 120Hz refresh rate is consistently high.

I'm happy to run further tests on anything if that'd be helpful, and thank you again for the help! I might keep host framerate control turned off here for the time being....

edit: looking at the description of what the FPS-with-shift number should be representing, it feels like something is very definitely off here!


Not to blow up all of those results, but I'm now getting drops down to 30 on the external/60Hz condition with host_framerate_control=0.

I think I'm noticing this more than most other folks because I'm generating PCM audio in this cart, and when the framerate starts dropping, I get audio dropouts. If the cart runs at a constant 30fps it's fine, but I'd like to run at 60 since it makes the control feel a little nicer.

Also, it looks like if I downgrade my cart to 30fps (add a new update function that just calls the existing one twice), then it never drops below 125fps on the external display (60 Hz / hfc=0).

(.... aaaaand another edit, even that will sometimes drop to 15fps when bringing Pico-8 back into the foreground if it's been in the background for a while)


I've just started looking for a solution to this exact problem and this thread has been useful. I have a desktop and a laptop, both Ryzen 5 CPU and with 16GB of RAM. On the desktop anything I run at update60 drops to 30FPS every second or so.

Holding L-SHIFT shows me that the FPS are locked to 50 and during the drop to pico's 30 I get framerates over 200 which is bizarre. The same thing running at normal update() give me over 300 true frames all the time.

On my laptop the problem is non existent. No issues at all, smooth the whole time at update60()

I'm not sure what could be causing it but at least I know I'm not crazy now.


As I said above on this thread, years ago, counting vsyncs vs. the passage of real time is a really tricky thing to deal with.

I know this from working on both consoles and PCs simultaneously, where the console ran at 59.94Hz, my PC monitor ran at 60Hz, but then my co-worker's PC monitor ran at 59.94Hz. Our code had to run correctly on both refresh rates. Let's not even talk about 50Hz.

I don't remember all of the gotchas, but I want to emphasize (mostly to @zep) that vsync is not a simple thing and requires careful handling. Very few programmers actually get it right—I didn't at first, but it was my job to get it right, so I had to eventually—and it's quite possible that either PICO-8 or SDL, or the interaction between them (which is most likely) are not handling framerates that aren't exactly 60Hz.



[Please log in to post a comment]