-
-
Notifications
You must be signed in to change notification settings - Fork 3.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
frameRate() doesn't accurately report frame rate, it is the drawRate #6013
Comments
Welcome! 👋 Thanks for opening your first issue here! And to ensure the community is able to respond to your issue, please make sure to fill out the inputs in the issue forms. Thank you! |
I can help do the implementation and/or documentation changes after a decision on this is reached. |
I think your use case is also a valid one, although I don't think it's necessarily more useful, or the main understanding of the term "frame rate." We have a few things that are all similar but slightly different, and all are valid things users might want to know:
We could maybe also add this measure in addition to the other two if we can think of a good name for it. Maybe if we measure the inverse we could call it something like In any case, this could benefit with some more docs! |
I made this sketch demonstrating the problem so that it can be seen visually: https://editor.p5js.org/quinton-ashley/sketches/3qbl_XPxp When the frameRate is scaled down it can be used to move objects each frame at what appears to be a constant velocity, because visually the difference is minimal. Mathematically it's still real bad but I do see this use case being totally fine visually which surprised me. Another bug I found was that console.log causes major frame timing issues in the p5.js web editor. I think it's somehow blocking the main JS thread. Using console.log is super useful for game development so this is a big problem. I'm going to open a separate issue report in the p5js web editor repo. processing/p5.js-web-editor#2116 |
I think keeping the current behavior of So perhaps a |
I'm not sure if I'm fully understanding your middle test, but here's one I've made to try to illustrate what I was describing a bit better: https://editor.p5js.org/davepagurek/sketches/K7cKw7bie The idea here is to have objects move at a constant distance per time, regardless of how much time has passed. The time we care about here is the time between frames drawn, not the time since the start of the current draw. Here I'm comparing a position calculated directly from In any case, I think maybe |
Problem with |
Sorry I don't quite understand this. FPS (or frames per second) is the measurement unit of frame rate, and for most intents and purposes are treated to be equivalent, eg. Wikipedia, Adobe. In performance testing or benchmarking we use execution per second (or sometimes operations per second) since the concept of a frame don't make sense when you are measuring code execution, or if you are only interested in the execution time of an animation frame a millisecond measure is used which is what the web browser performance profilers tend to use. |
Yeah the wikipedia page is incomplete. I will add to it. Among game developers and players, refresh rate or hertz (hz) is the rate at which a monitor can refresh per second. Frame rate or fps is the rate at which the game is able to render a frame, regardless of the actual refresh rate of the computer's monitor. Companies like Nvidia and Steam offer on screen FPS performance tools that show the FPS of a game and big Youtube channels like LinusTechTips also use the above definition of the terms frame rate and FPS. |
The reason that FPS (in gaming terms) can't be measured in p5.js accurately by users using Because frame rate among game devs and gamers has a different definition than frame rate among animators and visual artists, I think having separate |
Also I noticed a potential problem with the p5.js _this.redraw();
_this._frameRate = 1000 / (now - _this._lastFrameTime);
_this.deltaTime = now - _this._lastFrameTime;
_this._setProperty('deltaTime', _this.deltaTime);
_this._lastFrameTime = now; Am I wrong or is it wrong for it to update the frame rate calculation after I think that's why the bottom circle looks like it's lagging behind the top circle in @davepagurek 's sketch even though I think they should technically move at the same rate. The solution would be to move the calculation above I would reference the code directly but I can't seem to find the |
If we add a method for this measurement in addition to
Because of this, I think I'd still opt for calling this measurement something more descriptive but perhaps less standardized, like
Here's a sketch using the changes below, confirming that one can then use diff --git a/src/core/main.js b/src/core/main.js
index cb7a438b..41e4b215 100644
--- a/src/core/main.js
+++ b/src/core/main.js
@@ -385,13 +385,13 @@ class p5 {
time_since_last >= target_time_between_frames - epsilon
) {
//mandatory update values(matrixes and stack)
- this.redraw();
this._frameRate = 1000.0 / (now - this._lastRealFrameTime);
this.deltaTime = now - this._lastRealFrameTime;
this._setProperty('deltaTime', this.deltaTime);
this._lastTargetFrameTime = Math.max(this._lastTargetFrameTime
+ target_time_between_frames, now);
this._lastRealFrameTime = now;
+ this.redraw();
// If the user is actually using mouse module, then update
// coordinates, otherwise skip. We can test this by simply @limzykenneth can you think of anything that this change would break? |
Nice! @davepagurek We could keep redraw above the lastTargetFrameTime and lastRealFrameTime calculations. Also if the deltaTime calculation is done first it can be used in the _frameRate calculation. I can make these edits and submit the request now that I know the file it's in. |
I made it clear in the pr that it doesn't fix the initial issue here though. I want to try summarizing the issue again. If a sketch's fps is high enough for it to display at 60hz completely stable, verifiable in Chrome's dev tools, The name of Calculating accurate FPS (gaming definition) is definitely possible though, as it's just the time one frame takes to render, not including any delays. I think |
I just noticed @davepagurek you said |
How about this @davepagurek I like the name p5play could then use |
That sounds good to me! |
I'll try implementing |
Sorry I don't have much time to review everything at the moment but from what I gather, would something like stats.js be better suited for your use case if you are only interested in the execution peformance of the code within the draw function? It can be used like function draw(){
stats.begin();
// Your code
stats.end();
} |
@limzykenneth I think the issue is that the p5.play library registers pre-draw and post-draw hooks that @quinton-ashley would like to include in the measurement (non trivial stuff like physics I assume?) So the begin/end would have to surround this whole block: Lines 486 to 493 in 2f484fd
|
Also to note that for web canvas, there is no difference between the frame rate and the monitor refresh rate because according to the W3C recommendation, the frame rate at which
This may be different from some game engines where they may separate the update loop (which is usually CPU bound) and the render loop (which is GPU bound) in that in the browser the two loops are combined. |
yes exactly @davepagurek the idea is to included the registered methods: p5play by default does all the physics calculations in its post draw function which typically takes longer than drawing on the canvas. |
@limzykenneth that's interesting info. If that's the case then could it be possible for |
I think this is a good reason for making this a measurement of time instead of a measurement of rate, as it would simply be a measurement of how long your code takes to draw without implying that you could achieve |
The main thing is that Frame rate in p5.js is in actual fact an illusion. If you try to set frame rate above 60 on a 60Hz monitor, you will notice that your sketch still runs at 60 fps. For frame rate lower than 60 fps we basically skip frames to get an average of the request frame rate over time for the illusion of a lower frame rate. The example shared in #5354 is a particularly nice example of this in action: https://editor.p5js.org/delphi1024/sketches/lF1shtB_L |
@limzykenneth Yes that's what I thought. Even though the specifications say Here is a test I made of I think this issue can be closed once a simplified summary of this info is added to the |
Yes, that's right. A detailed description is here: https://javascript.info/event-loop and that |
Most appropriate sub-area of p5.js?
p5.js version
1.5.0 (but also all)
Web browser and version
Any
Operating System
Any
Steps to reproduce this
This may not be considered a bug that y'all would want to fix in p5.js but I at least wanted to point out that it's inaccurate to refer to the result of the p5.js
frameRate()
function as a measure of FPS, like it is on this page:https://github.com/processing/p5.js/wiki/Optimizing-p5.js-Code-for-Performance#frames-per-second-fps
It's also not correct to say "Calling
frameRate()
with no arguments returns the current framerate." which is on the reference page:https://p5js.org/reference/#/p5/frameRate
Here's the reasons why.
EDIT:
I want to try summarizing the issue again. If a sketch's fps is high enough for it to display at 60hz completely stable, verifiable in Chrome's dev tools,
frameRate()
will still not return 60 every time. Hence, it does not report the refresh rate of the sketch. It's actually a calculation of the difference in time between whenrequestAnimationFrame
runs the draw function and the previous time it did so. As far as I understand that's not always the same amount of time even if the frame rate always matches the computer display's refresh rate or target frame rate set in p5.js. So even if frames are always drawn on timeframeRate()
changes because there's some variation in the browser's delay between when a frame is drawn and whenrequestAnimationFrame
runs its callback function. This makes the valueframeRate()
returns kind of random, which can be seen in the example sketch I made. Yet, because it roughly will keep time with the real frame rate, though on average it loses time, even though its values are not technically useful for performance testing,frameRate()
values are useful visually as @davepagurek pointed out.The name of
frameRate()
is therefore technically a misnomer because it doesn't return values that are accurate by common definition or the gaming community definition. I think ironicallydrawRate
is an accurate name for it since it actually measures the rate between draw function calls and does not measure the frame rate by common definition. I did some research and with JS I think there's no way to get an accurate report of the actual frame rate of a sketch, which can only be viewed in browser dev tools after a profile recording is finished. Even ifframeRate
is not changed this info should be included in its documentation.Calculating accurate FPS (gaming definition) is definitely possible though, as it's just the time one frame takes to render, not including any delays. I've included a function in p5play called
getFPS()
to make this kind of performance testing more accessible.ORIGINAL POST:
FPS, in the context of performance testing, is typically a measure of how many frames renders could fit in one second if the code responsible for rendering a frame was run without delays, so it shouldn't be capped by the target frame rate or monitor refresh rate.
FPS should be calculated by subtracting the time immediately after a frame is drawn from the time immediately before its drawn. Whether a program is running slower or faster than the target frame rate, that result should be reflected in the FPS calculation.
The p5.js
frameRate()
function is calculated by including the delay between frames, capping the measure to roughly the target frame delay. So it only returns a roughly accurate measure of FPS if the current frame rate is below the target frame rate. If p5 finishes running the pre draw functions, the draw function, and post draw functions before the delay allotted by the target frame rate, then the remaining delay between draw calls is included in theframeRate()
calculation. If the sketch is running at a stable 60fps, which can be verified in the chrome dev tools, thenframeRate()
should just return 60 every time, but it doesn't. It returns wildly inaccurate values ranging from as low as 53 to as high as 68 in my testing. If there's some good reason for this which I can't think of, please let me know.When a sketch is running at a stable 60fps,
frameRate()
returns a value that isn't a useful measure of the real frame rate or the FPS.The text was updated successfully, but these errors were encountered: