kinsi55 / CS_BeatSaber_Camera2

Adds a lot of options / tools for creating more advanced desktop viewing experiences with multiple camera angles and much more in Beat Saber
MIT License
121 stars 17 forks source link

Frame drops every few seconds with a low fps limit #31

Closed DiSaber closed 3 years ago

DiSaber commented 3 years ago

I found that when the monitor is on 60hz and the camera2 fps limit is on 0 for vsync or 60 it has stutters every few seconds. However if I set the fps limit to 120 it reduces the stutters and if I increase it to 180 or 240 it further reduces the stutters. It shows up in recordings but the framerate and frametime graphs both are solid and never show any signs of lag whatsoever. As a note I set my 144hz monitor to 60hz for the recording and checking the graphs.

Camera2Config.zip

kinsi55 commented 3 years ago

Is your HMD connected while you're recording? Also whats your GPU and CPU? SteamVR? Latest version of Cam2?

DiSaber commented 3 years ago

No my HMD was not connected while recording. I have an i9-9900k and RTX 3090 so that should be fine. Yes I am using version 0.3.32. I think it might be something to do with how you are applying the frame limit on line 66 of the global fps cap script. I have a few forum posts talking about the vsync and targetframerate settings. https://forum.unity.com/threads/application-targetframerat-it-doesnt-work-in-version-2020-2-0b5-3233.982353/ https://forum.unity.com/threads/game-jittering-when-less-than-60fps-even-at-59.795111/#post-5334555. https://answers.unity.com/questions/1526637/fast-frame-rate-drops.html

Also a page from the scripting api for reference https://docs.unity3d.com/ScriptReference/Application-targetFrameRate.html

kinsi55 commented 3 years ago

Could you provide an example recording that you get when recording with 60 fps and no HMD?

DiSaber commented 3 years ago

Give this a few minutes to process so it is viewable in 60 fps to see the stuttering. https://www.youtube.com/watch?v=8WBS1LnusoE I also have a screenshot of the task manager window during the recording image I will be away from my computer for an hour so I might not respond as quickly

kinsi55 commented 3 years ago

I think what might be happening here is that your 3090 is just plain undertaxed, so the driver decides to clock it down, but when it does that it realizes the low clock isnt enough and boosts up again, which would explain why it happens much less the higher you set the FPS limit.

Could you try explicitly setting BS to max performance in the Nvidia driver? Also just for testing maybe yeet the render scale to the max.

I've a 3060 Ti and with no HMD connected, bs explicitly on max performance and my FPFC camera limited to 60 FPS I do not experience this issue, I also know a lot of people using Cam2 and havent heard anything in this regard to anyone from them. The Engine's FPS limit should not be causing this issue, when when the engine limit is the same as the cameras FPS target (Which is the case here) the camera will just render every frame with no further logic, so the stutters must be coming from the hardware side.

DiSaber commented 3 years ago

I already have everything set to max performance and the render scale didn't help so I added the line "Application.targetFrameRate = 60;" in the awake function in a game I am developing and I am experiencing the same kind of stutters shown in the video. I do not believe it is a hardware issue as HWiNFO shows the clocks to be stable the entire time. image I am using the profiler unity provides in the engine to see if I can identify the root of the problem that seems to be affecting my game and Beat Saber. I will report back shortly with my findings. I know it is not a problem with your plugin anymore but I'll still report back to help anyone else who might find the same issue later (Or maybe I just did something wrong)

kinsi55 commented 3 years ago

The driver version I'm using is 461.72, maybe that has something to do with it if you want to try that.

DiSaber commented 3 years ago

It seems that this is a major issue with the way unity handles garbage collection and the framerate. Unfortunately the only solution that I found is setting the frame limit in camera2 to 240 and record at 60 for minimal framedrops. I'm closing the issue for now and will reopen it if I can find a better solution.

DiSaber commented 3 years ago

@kinsi55 Can you change line 58 to read "Kapp = 0;" and send the compiled dll to Di Saber#8863. Just a theory and I will let you know if it fixes my issue :)

EDIT: My bad I meant to say "Kapp = -1;" EDIT2: It actually might be better to just attach the dll file here

kinsi55 commented 3 years ago

Camera2.zip

DiSaber commented 3 years ago

@kinsi55 One last thing, could you add "QualitySettings.vSyncCount = 1;" on the line right after line 58 and "QualitySettings.vSyncCount = 0;" right after line 61. This paired with "Kapp = -1" will effectively make it go into vsync when the fps limit is 0 which is better than setting the targetFrameRate to the screens refresh rate, although I have no idea why this is the case.

kinsi55 commented 3 years ago

vSyncCount 0 is already set as a basegame default

DiSaber commented 3 years ago

@kinsi55 I don't think you understand, when the targetFrameRate is set to -1 in the code the framerate has no limit making it run at absurd numbers like 1000 fps because the vsynccount is 0 like you said. You need to set the vsync count to 1 to make sure that vsync is activated when the targetFrameRate is -1. When you set the frame limit to something other than -1 you must set the vSyncCount back to 0 otherwise it will be ignored. This is what I was thinking


if (CamManager.cams?.Count > 0) {
    foreach(var cam in CamManager.cams.Values.Where(x => x.gameObject.activeInHierarchy)) {
        if(cam.settings.FPSLimiter.fpsLimit <= 0) {
            Kapp = -1;
            // Setting the vSyncCount to 1 while the targetFrameRate is -1 enables vsync.
            QualitySettings.vSyncCount = 1;
            break;
        } else if(Kapp < cam.settings.FPSLimiter.fpsLimit) {
            Kapp = cam.settings.FPSLimiter.fpsLimit;
            // Setting the vSyncCount to 0 while the targetFrameRate is anything other than -1 disables vsync.
            QualitySettings.vSyncCount = 0;
        }
    }
}
Application.targetFrameRate = Kapp;
kinsi55 commented 3 years ago

I've changed it up a bit due to various reasons.

public static void ApplyFPSCap(bool isHmdPresent) {
    QualitySettings.vSyncCount = 0;

    if(isHmdPresent && (!isOculus || isOculusUserPresent)) {
        Application.targetFrameRate = -1;
    } else {
        var Kapp = 30;

        if(CamManager.cams?.Count > 0) {
            QualitySettings.vSyncCount = 1;
            foreach(var cam in CamManager.cams.Values.Where(x => x.gameObject.activeInHierarchy)) {
                if(cam.settings.FPSLimiter.fpsLimit <= 0 || cam.settings.FPSLimiter.fpsLimit == Screen.currentResolution.refreshRate) {
                    Kapp = Screen.currentResolution.refreshRate;
                    break;
                } else if(Kapp < cam.settings.FPSLimiter.fpsLimit) {
                    Kapp = cam.settings.FPSLimiter.fpsLimit;
                    QualitySettings.vSyncCount = 0;
                }
            }
        }

        Application.targetFrameRate = Kapp;
    }
}

See if this makes a difference Camera2.zip

Docs for vSyncCount:

If this setting is set to a value other than 'Don't Sync' (0), the value of Application.targetFrameRate will be ignored.

DiSaber commented 3 years ago

Yep this worked 100% thank you for spending your time helping me! I would also suggest adding the changes to the main plugin. If you do add it remember to change the wiki to say that 0 activates vsync. Also in your above code you need to set the vSyncCount back to 0 inside the "isHmdPresent && (!isOculus || isOculusUserPresent)" statement otherwise it might do something weird with a vr connected if you had it on vsync before but then again it might not so idk. Thank you again and I hope you consider adding it the plugin :)

kinsi55 commented 3 years ago

Like I said, the basegame sets vSyncCount to 0 by default, the respective VR implementations are what handle frame pacing, hence that, and hence me shuffeling around things like that. (I set it to 0 in the first line incase you missed that)

Technically this implementation does not differ from what it was before, just that instead of capping the FPS at the screen refresh rate it uses vsync to accomplish the same with the added bonus of better frame pacing, but that only kicks into effect when there is no HMD so I'm not too much concerned about input lag or any downsides of vsync.

DiSaber commented 3 years ago

Yeah I only noticed a little input lag when using fpfc to control the camera but no one uses fpfc for playing so it's perfectly fine. I didn't see that vSyncCount was set to 0 at the top so thanks for pointing that out. By far you are one of the most cooperative developers I've ever talked with, keep up the good work 👍