processing / p5.js

p5.js is a client-side JS platform that empowers artists, designers, students, and anyone to learn to code and express themselves creatively on the web. It is based on the core principles of Processing. http://twitter.com/p5xjs —
http://p5js.org/
GNU Lesser General Public License v2.1
21.48k stars 3.29k forks source link

frameRate()'s messy behaviour #5354

Open 0xJonas opened 3 years ago

0xJonas commented 3 years ago

Most appropriate sub-area of p5.js?

Details about the bug:

Many values passed to frameRate() do not actually set the frame rate to that amount. Similarly, the values returned by frameRate() do not match the actual frame rate either. The only values at which frameRate() works well are ones which evenly divide 60. Most other values passed to frameRate() are clipped to one of these values.

As an example, try setting the frame rate to some values around 45 fps using the slider in the linked sketch. The pink line represents the value passed to frameRate() and the green line is the average value returned by frameRate() over the last 120 calls.

Of course the frame rate is ultimatly limited by the browser's refresh rate, but it should still be possible to reach an average frame rate which matches the value passed to frameRate().

balandinodidonato commented 2 years ago

Is there any news on this? I'm trying to get an FPS of 50 and it is impossible. Any advice?

0xJonas commented 2 years ago

I don't know of anything that has been done regarding this issue. However, even when this would be addressed, getting a true 50 FPS framerate would not be possible. The reason for this is that the browser itself runs (usually) at a framerate of 60 FPS and there is nothing p5.js could do to change that. The least p5.js could do is to periodically skip rendering frames, so that there is an average of 50 new frames in one second.

The difference here is the following: For a true 50 FPS framerate, the time between frames would be 20ms. For an average framerate of 50 FPS, the time between frames would be the following repeating sequence: [16.66ms, 16.66ms, 16.66ms, 16.66ms, 33.33ms]. The average of these values is 20ms, so the resulting framerate would feel like 50FPS.

The point of this issue is that p5.js does not even achieve an average framerate equal to the value passed to framerate(). For example, calling framerate(45), results in only every other frame being rendered, which is actually equal to 30FPS and not 45FPS. As another example, currently there is no difference between calling framerate(60) or framerate(50), both calls would produce a 60FPS framerate. This is almost certainly not the intended behaviour, hence this issue.

Qianqianye commented 1 year ago

Thank you @0xJonas. I am invite the Area: Core stewards to this discussion @limzykenneth, @davepagurek, @jeffawang

limzykenneth commented 1 year ago

@0xJonas With your provided example sketch, I'm only noticing problems with framerate between 30-60 fps, below 30 fps the average and indicated framerate seems to match or is very close to the target framerate (only about 1-1.5 fps out). Setting framerate of 24 gets framerate of 24 instead of 20. Tested with both Chrome and Firefox.

davepagurek commented 1 year ago

I also see some weird behaviour when setting frame rates in the 40s where I either get 60 or 30 fps.

I think this might be related to the fix in https://github.com/processing/p5.js/pull/5847, which is addressing the fact that each frame currently is able to be 5ms off and this error can compound. There hasn't been activity on that PR in a bit but I think it's most of the way there, I could make the changes that have been requested in it and see if it helps fix the sketch in this issue?

limzykenneth commented 1 year ago

@davepagurek If you can verify the fix in #5847 can address this we can possibly get it merged in once the requested fix are done by you.

davepagurek commented 1 year ago

Finally got around to cleaning up that PR! The PR is updated and the results are here: https://editor.p5js.org/davepagurek/sketches/7Zl3XWDRx

If you toggle back and forth between this and this issue's original tester sketch, I've noticed that this fixes some issues but not all. The original sketch shows difficulties hitting any frame rate between 30 and 60fps that isn't 30, 45, or 60. In the updated PR, the 45-60 range seems to work now. However, a target in the 30-45 range still results in 30fps as a result.

I made this alternative sketch that uses that PR's code + adds a slider to control epsilon, the buffer time (in ms) from the ideal next frame time in which where we're allowing our draw function to run. Bringing epsilon up to 10ms allows me to hit around 24fps, while at 5 it lags behind at 20. I'm not sure tweaking this value can help fix all these issues though.

limzykenneth commented 1 year ago

@davepagurek With your first sketch I'm only seeing deviation from average framerate in the 40-45 fps range (inclusive, getting 40 fps only), the rest are all able to have an average framerate matching requested framerate.

As for value of epsilon I can't find a goldilock value as it seems to just shift the problem around fps ranges so we may need to think about some other idea.

davepagurek commented 1 year ago

I can merge that PR in but leave this issue open, if it's OK with you, since it seems like it's at least an improvement.

limzykenneth commented 1 year ago

@davepagurek Sounds good, let's merge that for now in time for 1.6.0 and leave this issue open while we look for more solution.

laboletory commented 10 months ago

Hello team, I am reaching out to discuss and seek advice on some behaviors I've noticed with p5.js's framerate management, particularly in light of the insights shared by @limzykenneth. My concerns are as follows:

Inconsistent Framerate on 165Hz Monitors (Windows 10, Any Browser):

Issue: Using p5.js on a Windows 10 system with a 165Hz monitor, the framerate fluctuates unpredictably between 55 and 167 FPS. This differs from the stable framerate seen on a 60Hz monitor. Context: As per staff comments, I understand that p5.js's framerate is linked to requestAnimationFrame, and thus to the monitor's refresh rate. However, the extent of fluctuation on a 165Hz monitor is unexpected.

Framerate Capped at 30 FPS on Battery Power in Mac Laptops:

Issue: When a Mac laptop is used on battery power, p5.js's framerate seems to be capped at 30 FPS, increasing to 60 FPS when connected to power. Context: Considering requestAnimationFrame is tied to the screen refresh rate, this behavior might relate to Mac's power management and its impact on screen refresh rates. In light of the explanation that requestAnimationFrame is unlikely to be decoupled from the screen refresh rate, I understand the constraints of p5.js within the browser's environment and JavaScript's single-threaded nature. However, I'm interested in exploring how to best adapt to these limitations in p5.js. Would utilizing deltaTime in animation calculations help in these scenarios? Are there recommended practices for using p5.js on different hardware setups, especially high refresh rate monitors and laptops under varying power conditions?

Any further insights, clarifications, or suggestions to optimize p5.js sketches for diverse hardware environments would be highly valuable and appreciated. Your guidance will help in better understanding and navigating the current limitations.

Thank you for your dedication to the development and maintenance of p5.js!

limzykenneth commented 10 months ago

For 165Hz it is hard to tell what could be the cause, if you can share an example sketch that's causing problem I can try to test, although I only have a screen that goes up to 144Hz. A few unverified guess would be that maybe there's some automatic dynamic refresh rate stuff going on, or it could be that the code for each frame is taking longer than or close to 165Hz to execute with JS garbage collection making up the difference in fluctuations. Also sometimes the browser may choose not to actually run the loop at the screen refresh rate, either because of its own implementation details or the OS does not report the right value to the browser.

I couldn't find reference online about whether Mac changes the screen refresh rate as part of energy saving measures so can't confirm that's the case but I wouldn't be surprised if so. The main thing to know is that frame rate is not reliable or going to be constant from user to user (this applies to any interactive screen based medium unless the hardware can somehow be controlled/standardized, eg. game consoles). As such the best practice is to use something like deltaTime to scale animation calculations whenever necessary, that's precisely what it is designed for (Unity, Unreal, and basically every other game engines does the same as well, so tutorials about delta time from those more or less apply with p5.js too). In short, never assume there will be a constant frame rate.

laboletory commented 10 months ago

In continuation of our discussion on framerate issues in p5.js, I have conducted tests across different monitors with varying refresh rates. To illustrate the issues, I created two simplified p5.js sketches featuring a moving ball.

Key Aspects of the Sketches:

Ball Movement: The first sketch uses deltaTime to adapt the ball's movement to the frame rate. The second sketch uses frameRate() to explicitly set and change the frame rate.

FPS Monitoring: Both sketches display the current, minimum, and maximum FPS values on the canvas for real-time tracking. Observations Across Different Monitors:

60Hz Monitor: Min FPS: 36.23 Max FPS: 75.00 Ball Movement: Relatively smooth in both sketches. 100Hz Monitor: Min FPS: 1.10 Max FPS: 84.75 Ball Movement: Noticeable fluctuation and less smooth, particularly in the deltaTime sketch. 165Hz Monitor: Min FPS: 37.88 Max FPS: 84.03 Ball Movement: Less smooth than on the 60Hz monitor, again more pronounced in the deltaTime sketch.

These results highlight significant framerate variability on higher refresh rate monitors (100Hz and 165Hz), affecting the smoothness of the ball's motion, especially when using deltaTime for movement.

Links to the Sketches:

Sketch using deltaTime: https://editor.p5js.org/oleole/sketches/XPvUWRFfS Sketch using frameRate(): https://editor.p5js.org/oleole/sketches/TjYMjvB7i

Your expertise and any further guidance on optimizing for different hardware setups in p5.js would be highly valuable and appreciated.