Poor/Inconsistent engine timing for HeartBeat, RenderStepped and Stepped deltaTimes

Hi!
While working on roblox I’ve noticed for a while that the “dt” values for Heartbeat, Stepped and RenderStepped, have unexpectedly high amounts of variability.

A smooth dt is very important for providing a good user experience, but it seems the timing of runtime services has some problems right now.

It makes things feel unusually gritty, even on very powerful hardware that could otherwise run at thousands of fps.

My monitor does not have vsync turned on, and is also at 60hz.

You can reproduce this by simply connecting to these runservice events and then printing the value of dt.

Expected Behavior
When running at 60fps I would expect the value of dt of all three systems to sit pretty firmly on 1/60, or 0.016666.

Actual Behavior
The value of dt fluctuate pretty wildly, for no good reason, and not even the same way between the different services. I took some gifs below to illustrate the issue.

Orange, RenderStepped (flux of about +/-3ms)
renderstepped

Yellow, Stepped (The best, but with occasional flux of +4ms followed by an immediate -4ms)
stepped

Green, Heartbeat (The worst, flux of +/-5ms!)
heartbeat_netgraph

Issue Area: Engine
Issue Type: Performance
Impact: Very High (At least I consider it very high, smooth gameplay is important to me!)
Frequency: Constantly

21 Likes

Baseplate.rbxl (40.2 KB)
A baseplate with the graph.

1 Like

The reason why frame times fluctuate is because the time it takes to perform certain engine functions both before and while setting up Lua events cannot be guaranteed. Additionally Roblox’s timers aren’t perfect, it’s not an RTOS, so it has a certain degree of imprecision. That’s why your FPS is not exactly 60.000000 all the time.

This is the physics timescale working to keep things real-time, making fine adjustments. The dt parameter of Stepped isn’t real time but physics time.

I’m pretty sure the impact is not nearly as great as you claim. If anything this encourages you to use dt correctly instead of relying on each step being the exact same length.

2 Likes

I’ll assume your hearts in a good place and you’re trying to be helpful. So I’ll answer in good faith here.

The reason why frame times fluctuate is because the time it takes to perform certain engine functions both before and while setting up Lua events cannot be guaranteed. Additionally Roblox’s timers aren’t perfect, it’s not an RTOS, so it has a certain degree of imprecision. That’s why your FPS is not exactly 60.000000 all the time.

No. Firstly, even if luau was adding a perceivable time to fire the events (it does not, luau is a boss) the actual dt realistically should be calculated on the C++ side and passed through. Any latency would be invisible to user space and not show up as flux.

Secondly, being a RTOS or not has zero to do with it. Timing a precise 60hz on a modern pc is beyond trivial. My frame time is 95% air, waiting around for the next frame. This seems more like something has been overlooked when the system is meant to be running at a precise interval, it should not be constantly overshooting and undershooting like this.

You’re correct that stepped is calculated differently, I’m well aware of it. What is probably not expected is the physics engine overstepping and under stepping consistently dozens of times per second, when the game should be precisely timing out a smooth 60hz. If you run a well timed 60hz, you can run 4 physics timesteps of the 240hz physics engine per frame, and have it divide out perfectly, with maybe a very occasional small hitch causing a flux.

That’s not what we’re seeing.

In fact it’s not what we’re seeing on any of the timers. Hence the bug report.

4 Likes

Roblox has so many things to do outside of Luau, scheduling, yes physics simulation, rendering, tending to input from hardware devices (pumping the event loop), etc. These take variable amounts of time, and happen interspersed with the RunService events.

The fact that your frame times are “95% air” is probably a contributing factor to the timing being not being “precise” enough. On Windows, the system timer is only accurate to about 15.625ms. I’m not aware of any measures that Roblox is taking to be any more precise, i.e. spinning/busy waiting. Have you tried with vsync on?

1 Like

Yes, I’ve tested it with vsync forced on. (60hz monitor) Heartbeat is actually a little worse, because occasionally it seems to be being delayed by a whole frame.
heartbeat_vsync

If you think I am asking for the framerate to be 60 all of the time, you’re completely missing my point.
My framerate IS 60 all of the time, and mostly empty space, less than 5% cpu frametime.

The problem is like this:

[ Frame Time (5%)] [ Empty Time (95%) ]
[ Frame Time (5%)] [ Empty Time (95%) ]
[ Frame Time (5%)] [ Empty Time (95%) ]
[ Frame Time (5%)] [ Empty Time (95%) ]

There is more than enough time of the engine doing literally nothing, for it to be able to precisely start the new frame at at exactly 16.66ms after the previous frame.

sigh!

Let’s see what a real roblox engineer has to say.

2 Likes

Got out of bed on the wrong side this morning? Let’s try to stay civil on bug report responses. Nobody is here to intentionally spread false information, you should assume posters are posting in good faith. It’s possible to share knowledge while being respectful of the other person.

17 Likes

Why do you need smooth dt?

It’s purely a quality thing. Plenty of game systems rely on dt being smooth. Personally, I can easily see that moving an object via heartbeat dt is gritty and not glossy like 60fps should be, and I am sure others can see it, too, even if they’re not trained graphics engineers.

The thing is, I raised it as a bug report because its unusual for dt in a game engine to be this choppy. Hence the bug report.

Here is what unity gets for the same test:
image

Some advice: making up random things like “impossible without a RTOS” or “This is just a limitation of windows” like it’s a fact just makes you come across as both hostile and ignorant.

7 Likes

Thanks for the report! We’ll follow up when we have an update for you.

7 Likes

I completely agree,
precision timing on delta times is paramount to majority of game engines. Inconsistent timings can have have a effect even if minor. It needs to be more consistent. This ensures quality!

Well done for you pointing this out!

Yeah, this bug has been bothering me for years. I’ve been told it’s just my computer, but I swear it’s the same bug across every computer I’ve ever tried to play roblox on. The framerate counter will say 60 fps, but it feels choppy. When measuring the amount of time that passes between frames using a script in studio (on an empty place with only the default baseplate), I see that it’s wildly inconsistent, bouncing between 55 and 60 fps with dips down to 52 fps for 1 frame sometimes. I don’t think it’s my computer because I have an Intel i7 CPU and a GTX 1660 Ti with 16 GB of RAM and an NVMe SSD. Roblox is doing some wacky stuff behind the scenes when it renders stuff, and I wish they would fix it to be more consistent when rendering frames.

Any updates? I’d really like for this issue to be resolved, too.

They did something(?) recently, that reduced my average stutter on an empty baseplate from 8ms down to 3-4ms, but it’s not entirely resolved yet.

3 Likes

yeah, it’s still very much noticeable even on a fresh place with just a baseplate. I have no idea how the timing is so inconsistent.

Idk if this is connected but my game’s projectiles have been going slower on some servers than what is supposed to be their proper speed and this is probably because the loop goes to repeat with a wait time longer than usual because of this inconsistency with heartbeat. This is really affecting my game which relies on projectiles which use a loop to advance and it is a recent issue idk if it has always been like this but it only recently became noticeable and it highly affects my game because the projectiles turn extremely slow sometimes

1 Like

I am noticing this as well, and it seems that it happens in about 1/3 of all servers. In my game Football Universe, the football’s code looks something like this:

while freeFalling.Status and fb.Parent ~= nil do
	spin += 0.2
	bodyv.Velocity += Vector3.new(0, -24 * 1/60, 0)
	fb.CFrame = CFrame.new(fb.CFrame.Position, fb.CFrame.Position + bodyv.Velocity) * CFrame.Angles(math.pi/2, math.pi * spin,0)
	heartbeat:wait()
end

I believe that heartbeat is taking way too long to process (up to 0.5 seconds), and causes the football to float in a single direction. Some servers it is fine, others it is a disaster.

4 Likes

It’s your code that is wrong. The Developer Hub clearly states that RunService events are variable frequency:


https://developer.roblox.com/en-us/api-reference/event/RunService/Heartbeat

Same here:

Not a bug.

Yeah, sorry you’re having issues with your projectiles, but it is unrelated to this particular bug report, which is about engine timing consistency.

Turns out it was a bug, but yeah that code is kinda janky. It’s what happens when its five years old :stuck_out_tongue:

Anywho, it has been resolved here:

1 Like