Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LFX_MAX_FPS does not work #10

Open
ryao opened this issue Mar 19, 2022 · 9 comments
Open

LFX_MAX_FPS does not work #10

ryao opened this issue Mar 19, 2022 · 9 comments

Comments

@ryao
Copy link

ryao commented Mar 19, 2022

I have the following setup:

  • GeForce RTX 3080
  • Ryzen 7 5800X
  • 64GB ECC DDR4 RAM
  • Nvidia Driver 510.54
  • Linux 5.16.14-gentoo-x86_64
  • X11 with gsync compatibility enabled
  • KDE with the kwin compositor set to prefer lower latency
  • Eve Spectrum ES07D03 (144Hz 4K gsync-compatible monitor)

I am starting Apex Legends with LFX_MAX_FPS=140 PROTON_ENABLE_NVAPI=1 LFX=1 DXVK_HUD=compiler,scale=2 gamemoderun %command% -dev +fps_max=0. My dxvk.conf contains:

dxgi.nvapiHack = False
dxgi.maxFrameLatency = 1

I want to limit my FPS to 140 as per various recommendations for 144Hz gsync monitors, but when I do it via LFX_MAX_FPS in Apex Legends, my FPS often hits 144 or even rarely 145, which is outside of the gsync range and is said to disable gsync. Steam's FPS meter, Apex Legend's built-in FPS meter and DXVK's FPS meter all show FPS exceeding the value that I have set.

If I switch to DXVK_FRAME_RATE=140, then the frame rate is consistently limited to 140 FPS, but then I suspect that the DXVK limiter is causing latencyflex to misjudge how long it takes to render a frame.

I suspect that the frame rate limiter introduced in 7fed286 needs to be changed to do a better job of clamping the frame rate.

@ryao
Copy link
Author

ryao commented Mar 19, 2022

Also, my GPU cannot maintain 140 FPS most of the time in Apex Legends. That might be a contributing factor for the overshoot issue where FPS exceeds the limiter setting in areas where my GPU is able to do 140 FPS, although I am not 100% certain at this point.

@ryao
Copy link
Author

ryao commented Mar 19, 2022

I just set it to 120 and entered the firing range. I am seeing FPS up to 144 FPS according to the Steam overlay. The limiter seems to be completely broken. :/

@ryao
Copy link
Author

ryao commented Mar 19, 2022

PROTON_LOG=1 revealed the problem:

LatencyFleX: setting target frame time to 0

The game is requesting a target frame time of 0, which LatencyFlex will honor despite the environment variable being set. A small patch seems to fix this:

diff --git a/layer/latencyflex_layer.cpp b/layer/latencyflex_layer.cpp
index ffa5a64..a2a9483 100644
--- a/layer/latencyflex_layer.cpp
+++ b/layer/latencyflex_layer.cpp
@@ -486,6 +486,12 @@ extern "C" VK_LAYER_EXPORT void lfx_WaitAndBeginFrame() {
 
 extern "C" VK_LAYER_EXPORT void lfx_SetTargetFrameTime(uint64_t target_frame_time) {
   scoped_lock l(global_lock);
+  if (getenv("LFX_MAX_FPS") != NULL) {
+      std::cerr << "LatencyFleX: Asked to set target frame time to "
+                << target_frame_time << ", but LFX_MAX_FPS is set. Ignoring request."
+                << std::endl;
+       return;
+  }
   manager.target_frame_time = target_frame_time;
   std::cerr << "LatencyFleX: setting target frame time to " << manager.target_frame_time
             << std::endl;

However, setting LFX_MAX_FPS to 140 requests in the frame rate actually being capped at 137, so the logic needs some adjustment for this to work properly. Still, this is a massive improvement over LFX_MAX_FPS not working at all.

@ryao
Copy link
Author

ryao commented Mar 19, 2022

This issue appears to also be present in Overwatch.

@ryao ryao changed the title LFX_MAX_FPS does not work well LFX_MAX_FPS does not work Mar 19, 2022
@ishitatsuyuki
Copy link
Owner

The frame limiter is intentionally undocumented due to the caveats (Reflex API also allows a frame limit to be communicated, and I don’t want to track both). Please just use +fps_max.

@ryao
Copy link
Author

ryao commented Mar 20, 2022

Reflex API also allows a frame limit to be communicated, and I don’t want to track both

If LFX_MAX_FPS is set, would ignoring the frame limit from the reflex API avoid that problem? The patch I put inline in my other comment does exactly that, which allows LFX_MAX_FPS to work despite both Apex Legends and Overwatch using the Reflex API to disable the Reflex frame rate limiter.

I have spent a few hours testing that patch and with the exception of the effective frame rate limit being slightly inaccurate (the limit seems to be 137 when I set 140), I have been rather happy with it. The inaccuracy seems like a bug that would also be present when a game sets a frame rate limit via the reflex API.

Please just use +fps_max.

There are complaints online that "Apex's frame rate limit tool can produce inconsistent/unstable frametimes", so people seem to be looking into alternatives. For example:

https://www.reddit.com/r/linux_gaming/comments/tghdv0/any_differences_between_dxvk_frame_rate_mangohuds/

Would not doing frame rate limiting in LatencyFleX be ideal because:

  • There is no chance of an external frame rate limiter causing LatencyFleX's frame time projections to be inaccurate
  • LatencyFleX's implementation of frame rate limiting would place the sleep at a point before input processing, such that it would have lower input latency than other frame rate limiters
  • Doing it in latencyflex would avoid making additional system calls per frame to implement the feature.

If it is not ideal, do you mean that using a different frame rate limiter would not hurt input latency?

@ishitatsuyuki
Copy link
Owner

If LFX_MAX_FPS is set, would ignoring the frame limit from the reflex API avoid that problem? The patch I put inline in my other comment does exactly that.

Yes, you're right. I'll import that patch some time later.

There are complaints online that "Apex's frame rate limit tool can produce inconsistent/unstable frametimes"

IIRC, Source engine's fps_max limiter is generally considered to be accurate. I believe It's rather some other issues with frame time consistency on Linux that is causing "unstable frametimes" for them. Driver-level limiters adds more latency and buffering, and therefore conceals this issue. (I also suffer from this too, but I also play with fps_max on Windows and it works very fine there).

I have spent a few hours testing it and with the exception of it being slightly inaccurate (the limit seems to be 137 when I set 140), I have been rather happy with it. That seems to be a bug that would also be present when a game sets a frame rate limit via the reflex API.

This is not a bug, this is an intended behavior/limitation that is required to determine the "bottleneck" parameter. Hope that with FreeSync the difference should be unnoticable.

Would not doing frame rate limiting in LatencyFleX be ideal because:

The general opinion is that it's neither worse or better than an in-game frame limiter.

  • There is no chance of an external frame rate limiter causing LatencyFleX's frame time projections to be inaccurate

I thought about this but in the experiments it didn't seem that the in-game frame limiter affects LFX's operation much.

  • LatencyFleX's implementation of frame rate limiting would place the sleep at a point before input processing, such that it would have lower input latency than other frame rate limiters

The game also sleeps before input processing. Driver-level limiters like the ones in DXVK, MangoHud will cause additional latency, though.

  • Doing it in latencyflex would avoid making additional system calls per frame to implement the feature.

Doesn't really matter, but there's another thing that accurate timers doesn't exist on Windows so the games go through great length emulating that through spinning...

@Calinou
Copy link

Calinou commented Jul 12, 2022

IIRC, Source engine's fps_max limiter is generally considered to be accurate.

I don't think that's the case. For example, to target 145 FPS consistently, I've found that I need to use fps_max 150 in CS:GO. No other engine I know of requires this – using a FPS limit of 145 will do what I want instead.

@user3287
Copy link

DO NOT use DXVK_FRAME_RATE=X (with superglide script)
I found DXVK_FRAME_RATE to lag my game every time the framerate changes substantially (e.g coming out of a bad frame area, like dropping, or changing the fps to 30 for a tiny short time in order to perform superglides more consistently).
It kind of takes a long time until the framerate catches up to the new situation, and I'm overall more happy with the performance of the ingame frame limiter

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants