Frame rate limiting using juce::Time can lead to frame pacing issues. I did some basic frame analysis on your code and found that it drops frames even with a light workload. Using a 500 frame sample size, I checked for dropped frames by utilizing your FrameLimitedVBlankAttachment
to call repaint on a component with an empty paint function.
Test setup:
Windows 11
JUCE 8 Release Build (Dev branch)
120 Hz Refresh rate
Frame Skip Interval:
Min: 0
Max: 3
Mean: 1.06702
Mode: 1 (60 Hz effective)
SD: 0.26566
Dropped: 51
Elapsed Times:
Min: 0.4264 ms
Max: 28.9183 ms
Mean: 12.8009 ms
Mode: 16.6702 ms
SD: 4.57221 ms
As shown in the data, the elapsedTime
local variable in your update()
function fluctuates significantly, leading to inconsistent frame pacing and visible judder.
Another issue with your code is that if the current monitor refresh rate does not divide evenly by 60, then your actual frame rate will be much different from your target.
Here is the same test but with a 144hz refresh rate:
Frame Skip Interval:
Min: 0
Max: 3
Mean: 1.97346
Mode: 2 (36 Hz effective)
SD: 0.179455
Dropped: 2
Elapsed Times:
Min: 1.2825 ms
Max: 29.8596 ms
Mean: 13.7132 ms
Mode: 6.8919 ms
SD: 5.78849 ms
The test results at 144 Hz demonstrate that the effective frame rate drops to 36 Hz, which is far below the intended 60 Hz target. Since each frame at 144 Hz is 6.94 ms compared to 8.33 ms at 120 Hz, skipping just one frame results in 13.88 ms, prompting the code to skip 2 frames, leading to a lower frame rate than intended.
Additionally, multi-monitor setups with different refresh rates are not handled correctly. If the editor window is dragged from a 120 Hz monitor to a 144 Hz monitor, then the issue I described above will occur.
A more robust solution would be to dynamically obtain the current monitor refresh rate and use that for your calculations. On Windows, you can retrieve this information from the HWND
handle of the editor window. On macOS, you can obtain it from the CGDisplayModeRef
or CVDisplayLinkRef
associated with the monitor.