Search Unity

Camera jitter at 30 FPS - even with interpolation?

Discussion in 'General Graphics' started by crandellbr, Aug 16, 2020.

  1. crandellbr

    crandellbr

    Joined:
    Apr 3, 2013
    Posts:
    137
    Hello. I'm developing for mobile and trying to limit the framerate to 30 due to performance concerns. I've followed all the usual tips, but I'm still struggling with jitter, so I created a simple test project to try to isolate the issue.

    The video below (at fullscreen) is representative of what I'm seeing in a standalone build. (This is in 2018.4.16.) vSync is off, and the fixed timestep matches the framerate at 30 updates per second. To me, the edges of the boxes look almost...out of focus.


    The camera has a Rigidbody set to Interpolate, with its velocity assigned once in Start(). The only other code resets the camera's position when it passes a given threshold. (I realize now setting transform.position is inappropriate, but it's rarely done.)

    Code (CSharp):
    1. private float offset;
    2. public new Rigidbody rigidbody;
    3. public float speed;
    4.  
    5. private void Start()
    6. {
    7.     QualitySettings.vSyncCount = 0;
    8.     Application.targetFrameRate = 30;
    9.  
    10.     offset = Mathf.Abs(transform.position.x);
    11.     rigidbody.velocity = Vector3.right * speed;
    12. }
    13.  
    14. private void FixedUpdate()
    15. {
    16.     if (transform.position.x >= offset) {
    17.         Vector3 newPos = transform.position;
    18.         newPos.x = -offset;
    19.         transform.position = newPos;
    20.     }
    21. }
    The only improvement I've been able to make is increasing the framerate to 60 - then everything becomes silky smooth and stutter is occasional at worst. Here's a comparison video:


    I realize there's always going to be a difference and 60 has become standard for a reason, but it doesn't seem like the difference should be this dramatic. Am I doing something wrong? Do I just not have a good eye for this? Or do other engines handle lower framerates better?

    Edit: Forgot to omit audio from the videos...whoops! Reuploaded.
     
    Last edited: Aug 16, 2020
  2. Oxeren

    Oxeren

    Joined:
    Aug 14, 2013
    Posts:
    121
    Not exactly sure if this is your issure, but I once hade similar problems, and moving all camera movement to LateUpdate fixed it.
     
  3. crandellbr

    crandellbr

    Joined:
    Apr 3, 2013
    Posts:
    137
    That's the thing, there isn't any manual movement in this setup. The camera's Rigidbody is given an initial push and then runs its course. After your post I tried a more typical setup - separating the Rigidbody from the camera, applying force, and having the camera snap to it in Late Update. I'm afraid I didn't see any change.

    People often suggest setting the Rigidbody to Interpolate and that's all. I don't get why that doesn't seem to work.
     
    Last edited: Jan 11, 2024
  4. cosmochristo

    cosmochristo

    Joined:
    Sep 24, 2018
    Posts:
    250
    I'm curious, how does it compare to using Update?

    Also, although I can't tell you what the jit for unity c# does, local variables that are short-lived are normally put onto the heap. You don't want to have garbage collector jumping in every now and then to free up a whole lot of temporary allocations.
     
  5. Oxeren

    Oxeren

    Joined:
    Aug 14, 2013
    Posts:
    121
    Ah, I see. That's weird, rigidbody interpolation should have fixed the issues. Maybe it's still not enough to sync position in update and fixed update. The only awkward solution that comes to mind is to use Cinemachine and create a camera with small damping that follows the rigidbody.

    Vector3 is a struct, and afaik local structs are allocated on the stack.
     
  6. crandellbr

    crandellbr

    Joined:
    Apr 3, 2013
    Posts:
    137
    Unfortunately, switching from Late Update to Update didn't have any effect.

    Shouldn't it be the opposite - local variables are put onto the stack, not the heap? I've seen multiple sources claiming that, although I haven't seen anything official. It makes sense when the scope of a local variable is so narrow, limited to the containing method.

    I may check out Cinemachine if I get desperate enough. For now, I'm taking the unconventional approach of changing Application.targetFrameRate based on whether or not the camera is moving. My game isn't a shooter or anything, and the player's view is static most of the time.
     
  7. cosmochristo

    cosmochristo

    Joined:
    Sep 24, 2018
    Posts:
    250
    Yes, although I have a vague memory that there in some cases with a vector the reference/pointer is stacked and the rest is allocated on the heap. Sounds like this is not the case so I can't see any other reason for the jumpiness. There would need to be another thread in your code, Unity pipeline, or another app on the system that occasionally grabs cycles - perhaps causing temporal aliasing with the lower fixed frame rate. What else is running?
    The thing that made me look at that line is that I change lines like that to a single reference (e.g. in the class declaration section) instead of dynamically allocating/deallocating every frame. I did not mention this because I doubt it is making a big difference.
     
  8. crandellbr

    crandellbr

    Joined:
    Apr 3, 2013
    Posts:
    137
    I forgot to mention, that line is only called once every few seconds to pull the camera target back. Still, I got rid of the local variable.

    The code I posted earlier is the only custom code in the project; I'm doing all I can to strip this problem down. I built and ran the test again, closing all other applications. I even restarted my computer to make sure no background processes were interfering. Sadly, none of this made a difference.
     
    Last edited: Jan 11, 2024
  9. crandellbr

    crandellbr

    Joined:
    Apr 3, 2013
    Posts:
    137
    In case anyone is willing to take a closer look, here is a link to the project:

    https://drive.google.com/file/d/1qutP1MfQFC5MJFYhwNpcH9PRfQ10M7_h/view?usp=sharing

    I may have solved this by changing the framerate based on whether or not my camera is moving, but that hacky workaround won't be applicable in most future projects where the camera will be moving much more often. This is a bizarre issue, and I'd appreciate any further feedback on what might be causing it.
     
  10. cosmochristo

    cosmochristo

    Joined:
    Sep 24, 2018
    Posts:
    250
    ok, got it, having a look now.
    BTW - there was nothing wrong with your code - I was just saying avoid unnecessary dynamic memory allocation. I.E. change (the new version) to this:
    Code (CSharp):
    1.             newPos = transform.position;
    2.             newPos.x = -offset;
    3.             transform.position = newPos;
    4.             // Avoid new memory allocaiton: rigidbody.transform.position = new Vector3(-offset, rigidbody.transform.position.y, rigidbody.transform.position.z);
    5.  
    Where the Vector3 is declared once in the class header.
     
  11. cosmochristo

    cosmochristo

    Joined:
    Sep 24, 2018
    Posts:
    250
    ok, so I tested it on my corei5 laptop, with other apps running and it did appear to do bigger jumps every now and then.
    I slowed the velocity to make the motion steps smaller and the movement appears to be constant speed with no hesitations or jumps.

    I set the speed to 1 in the editor then tried different dividers, from about 1/5th:
    in the start method:
    Code (CSharp):
    1. rigidbody.velocity = Vector3.right * speed/5f;
    it now appears constant speed, without jumpiness, but not exactly smooth at 30fps.

    Considering this result, I think we have been chasing shadows and the real issue is at certain combinations of displacement per frame (without interpolation) and framerate you get a form of visual aliasing.
     
  12. crandellbr

    crandellbr

    Joined:
    Apr 3, 2013
    Posts:
    137
    All good, thanks for investigating this further.

    I see what you mean about the improvement at lower movement speeds. All higher speeds I've tested have the same jitter, though, and I generally prefer action games with higher speeds. In those games I also give the player acceleration, so the follow camera is going to be moving at a range of speeds; I can't settle on a magic number, if there is one.

    Interpolation doesn't appear to make a difference. I don't have much experience with post-processing effects, but anti-aliasing also doesn't seem to matter. I suppose as long as the player's movement is smooth, that's what matters most. I haven't had any trouble with that, and I may still be able to achieve 60 FPS.
     
  13. cosmochristo

    cosmochristo

    Joined:
    Sep 24, 2018
    Posts:
    250
    No problem.
    I find that I run different components at different rates. Motion needs to be a higher rate, good luck with the rest :)