RenderStepped Issue at 240fps

Running at 240fps causes a view model system to “glitch” from what seems to be RenderStepped running too quickly. The RenderStepped loop in my game only does CFrame maths and spring calculations. The issue is (magically) resolved by adding random print & warn statements into RenderStepped.

Steps to reproduce:

  1. Set maximum frame rate to 240fps.
  2. Implement a view model that moves based on RenderStepped with spring math calculations and CFrame calculations.
  3. Observe the view model behavior without any print or warn statements in RenderStepped.
  4. Add random print and warn statements into RenderStepped (or a function called within RenderStepped.)
  5. Observe the view model behavior with the added print and warn statements.

Video of issue: https://youtu.be/JtStXgoCieg

This case is strange to say the least and I assume there is a technical reason behind adding print & warn statements that fixes the issue I am observing. I believe it is likely that print & warn statements are slowing down RenderStepped just enough so that it’s not actually running at 240fps. Furthermore, none of my code is FPS-bound, so this should not be causing any issues either, for example I am not measuring tick based on the last frame, I simply use tick for epoch time.

I’ve provided my place file in private content for staff.

Expected behavior

Running at 240fps does not cause issues with RenderStepped.

A private message is associated with this bug report

Edit: I should note, this issue only occurs at 240fps. No issues occur at 120fps or 144fps and obviously 60fps.

5 Likes

Are you using DeltaTime anywhere in your CFrame or math calculations? The problem seems to be that DeltaTime is sometimes equal to 0. Meaning the last frame took 0 seconds to execute. This shouldn’t really be possible or expected behavior. A temporary fix would to just return the function if Delta == 0.

I’ve tried this on a separate new file and haven’t had this issue happen, however on my main project file it does happen randomly, my first theory was that overloading connections could cause it to skip frames but that doesn’t seem to be the case.

This may also just be a studio issue, has this happened to you on the player?

1 Like

Delta isn’t used in CFrame calculations and I don’t use measured tick either (aka calculating delta.)

I simply use tick in CFrame and get the sine-value from it to get random oscillating behavior in my viewmodel offset calculation, simply put, this is for a “breathing” animation.

I spoke with @ocula earlier and the idea behind “skipping” seems possible. I think this is an issue with the engine not being able to handle CFrame calculations within RenderStepped at 240fps. This happens in both studio & the player too.

For reference, with what I can share, here is the pipeline for RenderStepped & applicable functions that are called.

Images



image

Spring module:
image

This is more than likely an engine bug unless there is an issue with my code that I don’t seem to find obvious, which you can review above.

3 Likes

I don’t believe there is any issues with your code, I believe it could be due to using tick() and it being affected by a frame being skipped. If the time between two tick() calls is longer than expected, then it would be however many frames passed in between.

Example: Instead of the expected 16.67 ms, the next tick() might occur after 33.34 ms (two frames worth of time). Now the opposite would most likely be that if Delta is 0 then it would do tick() - tick().

Well that’s my theory as to why at the very least, would you mind printing something if tick is = to zero.

1 Like

Like I stated in my previous reply, I am not measuring tick, as in I don’t calculate tick based on a previous tick. I simply get the sine-value of the current epoch time (which is what tick returns.) Thus, it is impossible for measured tick calculation or delta to be zero, because I don’t use either in CFrame calculations.

This is an engine bug and not related to my usage of tick.

1 Like

The reason why I said this is because skipping a frame may result in tick() being = to 0. Agreeably it is an engine bug but I am saying the issue is most likely due to a frame being skipped.

You use tick() as a multiplier in some of your CFrame operations which is why I brought up tick() being possibly affected by a frame being skipped.
image

1 Like

Ah sorry as one of my friend also mentioned you do also happen to be using DeltaTime with the argument (elapse: number) in UpdateOffset(). So my theory about tick() being off might’ve been wrong but you do still use a possible 0 as a multipler in your lerp.

1 Like

Lerping by zero wouldn’t cause any issues, it would simply prevent the offset from updating for that single frame (not default it to another random CFrame,) which wouldn’t cause a “glitch” effect. Furthermore, math.sin returns values between [-1, 1]. tick also can’t be set to zero, it’s epoch time. The jitter effect happening implies an issue with CFrame calculation, but there is no obvious issues in my calculations, hence why I think this is an engine bug related to 240fps, and possibly the engine not being able to handle CFrames being calculated at that framerate.

I understand delta can be set to zero in rare circumstances, but it would not occur as often as it does in the video I provided in my original post.

I appreciate your insight, although I would prefer a Roblox engineer to input on this.

2 Likes

Yes I know, I tested this as a theory and I was wrong about this. Delta (time elapsed between frames) being 0 (which could be affected by many things but I won’t get into that) is the one causing issues but as you said:

I will not push any information further. Since this was an issue I also encountered I was trying to add information hoping it would help engineers with the issue.

1 Like

Thanks to insight from another developer, it seems the issue stems from the physics engine stepping so quickly at 240fps that a race condition occurs when I check if the player is moving to decide whether movement CFrame-based animations or idle CFrame-based animations are handled.

This is because I am doing a magnitude check between the player’s existing position and previous position, but for a few frames, the player’s position hasn’t actually updated because the frames occur faster than the player actually moves.

This is solved by having x frames of grace period with the following code:

-- Variables
local MovementHistory = {}

-- Constants
local GRACE_PERIOD_FRAMES = 6
local PLAYER_MAGNITUDE_THRESHOLD = 0.01

-- In a RenderStepped loop
local playerMoving = false
local moveCount = 0

local movementMagnitude = (currentPosition - PlayerStateController.LastPosition).Magnitude
table.insert(MovementHistory, 1, movementMagnitude)

if #MovementHistory > GRACE_PERIOD_FRAMES then
    table.remove(MovementHistory)
end

for _, magnitude in ipairs(MovementHistory) do
    if magnitude > PLAYER_MAGNITUDE_THRESHOLD then
        moveCount = moveCount + 1
    end
end

if moveCount > GRACE_PERIOD_FRAMES / 2 then
    playerMoving = true
end

Whether this solution is perfect, I don’t know, but I hope this helps someone in the future who experiences this strange issue.

3 Likes