jkgfxmod icon indicating copy to clipboard operation
jkgfxmod copied to clipboard

Request: Builtin frame limiter

Open bangstk opened this issue 5 years ago • 21 comments

An option to set an arbitrary Vsync or maximum FPS would be nice, because Jedi Knight has strange behavior. At 48fps and above, weapons start playing animations too fast and frames start becoming duplicated, resulting in cases such as the game looking like 30fps at 60fps with weapons animating twice as fast as they should.

A way to set the maximum framerate to 47 (or any user selectable number) would be optimum for Jedi Knight.

Other alternatives such as RivaTuner and Aqrit's ddraw wrapper allow this functionality for the base game but they don't work well with jkgfxmod as it is a wrapper of its own.

bangstk avatar Jul 03 '19 00:07 bangstk

Hi @bangstk

Thanks for the suggestion. This seems reasonable. I'm also inclined to make 48 FPS the default cap, since as you noted there is no real advantage to exceeding that rate.

jdmclark avatar Jul 08 '19 19:07 jdmclark

I'm also inclined to make 48 FPS the default cap.

Please don't. It'll actually make it worse for those of us without variable refresh rate monitors.

SirYodaJedi avatar Jul 08 '19 23:07 SirYodaJedi

You should try it with one of the utilities I mentioned (and without JkGfxMod). It's astronomically and objectively smoother than the game running at 60FPS. Also make sure it's 47FPS, I think 48FPS is noticeably much more stuttery because that is when in-game frame duplication starts happening, but it does not at 47.

If your monitor only displays at 60Hz, your OS is still going to duplicate frames to make it output at 60Hz anyways. But the number of duplicated frames will be much less than what the game itself does at 60FPS.

I know on paper forcing 47FPS into a 60FPS monitor seems like it would be wrong and inconsistent, but once you try it with Jedi Knight it's like being able to see again after having eye surgery.

I think Aqrit's wrapper was very inconsistent with frame timing until I modified the code myself to use a higher resolution timer, but maybe RivaTuner does better out of the box.

bangstk avatar Jul 09 '19 00:07 bangstk

I have tried those methods. I find the default method (frame duplication) to much less visually irritating than when it is running at a capped 47 FPS (which causes something similar to 3:2 pulldown). The camera updates every other refresh, rather than every 2-3 refreshes, resulting in hard pans (such as rotating your head, you do that a lot in this genre) not being juddery the way they are on NTSC DVDs.

If you are playing on a higher refresh rate, such as 120 or 144 (without VRR), the judder is much less noticeable, and therefore more bearable. So no, it isn't objectively better, but rather a tradeoff between judder and stutter.

SirYodaJedi avatar Jul 09 '19 01:07 SirYodaJedi

And how to make game run in 60 fps? Mine only works with 30 fps cap (and awful stuttering)

KainXVIII avatar Nov 29 '19 09:11 KainXVIII

I don't think it's possible, I've seen some tests elsewhere with COG scripting that definitively shows that 47FPS is the highest sim rate the game can achieve unless someone can figure out how to modify the .exe to remove the cap/up the sim rate. Once FPS gets higher than 47 it seems physics stop updating as often as FPS other than some camera movement in certain situations.

http://www.jkhub.net/forums/viewtopic.php?p=18681#18681

bangstk avatar Nov 29 '19 10:11 bangstk

I don't think it's possible, I've seen some tests elsewhere with COG scripting that definitively shows that 47FPS is the highest sim rate the game can achieve unless someone can figure out how to modify the .exe to remove the cap/up the sim rate. Once FPS gets higher than 47 it seems physics stop updating as often as FPS other than some camera movement in certain situations.

http://www.jkhub.net/forums/viewtopic.php?p=18681#18681

Well, i'm ok even with 30 fps cap, but smooth 30 fps, stuttering is terrible for me. UPD - capping 30 fps with RTSS helps a lot! Maybe capping fps through jkgm.json will be useful feature?

KainXVIII avatar Nov 29 '19 11:11 KainXVIII

@KainXVIII

Yes , but the Mod , and low Res , you can get about 100FPS , the main problem is the Physics are running too fast.

With 30FPS , it runs smooth

im for a Option 30 or 60FPS , but 60 with Adjust the Physics when possible , like the Weapons Move on fast Running , runs too fast in compare with 30FPS

Vsync is ok , but can Cause lags , when the Target FPS isn't stable , that is why it gives alternative Vsync Options like Adaptive , Fast , and even Freesync (AMD) , Gsync (Nvidia)

TP555 avatar Apr 21 '20 17:04 TP555

As an interim measure, I'm getting ready to push a change that will add a simple vsync option. Vsync will now be disabled by default. This means JkGfxMod will depart from the current default behavior, which enables vsync unless it has been explicitly disabled in the driver control panel.

Rationale is per the above discussion, but to elaborate:

JK's internal update rate is 48 Hz, without state interpolation. If you play JK on a 60 Hz monitor with vsync enabled, the game will look like this:

Frame 0
Frame 0
Frame 1 (12.5 ms late)
Frame 2 (8.3 ms late)
Frame 4

Put simply, this looks disgusting: the game looks like it is constantly stuttering and dropping frames when vsync is enabled. It is almost certainly not what people want, but it's also not obvious to most people that it could be a source of this kind of problem.

Unfortunately, there is a trade-off. Besides tearing, JK also doesn't scale the first person weapon animation speed against frame time. This makes the first person weapon model waggle rapidly when the game is running at high framerates. On balance though, I think the stuttering/dropped frames issue is much worse than the tearing and animation problem.

jdmclark avatar Nov 17 '20 04:11 jdmclark

That sounds good. Any to plans to allow specifying arbitrary framerate cap? 48Hz folds neatly into today's 144Hz monitors, and being able to cap the game would at least allow for correct weapon animations

About interpolation, I do notice the camera that orbits the player when you stand idle too long does seem to be smooth compared to everything else. Interpolation would probably be the best way to 'properly' fix JK's framerate limit rather than hacking the game to adjust the sim rate. Doom source ports interpolate from 35Hz and the game feels plenty responsive enough; other projects like the Chimera patch for Halo PC also interpolate frames leaving the game's rim rate at 30FPS.

Something like Chimera's approach would be ideal, but of course also a huge undertaking. I think it reads the game's memory to determine object positions and do the interpolation accordingly for the render output.

bangstk avatar Nov 17 '20 07:11 bangstk

That sounds good. Any to plans to allow specifying arbitrary framerate cap? 48Hz folds neatly into today's 144Hz monitors, and being able to cap the game would at least allow for correct weapon animations

I think it's a good idea, but I don't have a good variable refresh rate display to properly test the change. I'll review the PR if someone submits one, but otherwise it will probably have to wait until the next time I replace a monitor.

Aside: I might be misunderstanding, but I think variable refresh rate is the principal use case for framerate capping. If you already have a 144 Hz monitor and can run the game at that rate, you should be good to go with just vsync already. It seems like this would mostly be useful for when you want to run the game at a non-native refresh rate, like 48 or 96 Hz.

jdmclark avatar Nov 17 '20 07:11 jdmclark

It seems like this would mostly be useful for when you want to run the game at a non-native refresh rate, like 48 or 96 Hz.

I think that is a very good reason; since 48fps is the highest possible fps the game can achieve without any bugs. I believe an effort should be made to support it since not many players of the game know about it at all, because currently the only way to get it is to use external tools like RivaTuner. Stacking on a bunch of patches and wrappers is not ideal. It should be an option in JkGfxMod.

bangstk avatar Dec 26 '20 09:12 bangstk

I actually just figured out how to unlimit the framerate when testing some OpenJKDF2 function rewrites with my 120FPS monitor, so I can actually explain a bit of the weird jitter:

The first issue is the game has its own framerate limiting where it will not update the game unless it reaches 20ms from the last update, seen here: https://github.com/shinyquagsire23/OpenJKDF2/blob/d7e836b6860c4d460829bb034f9eddf0d7a6ed1f/df2_reimpl/src/Main/jkMain.c#L136

The second issue is that player physics in particular are coded to run at 50FPS, presumably for multiplayer? seen here: https://github.com/shinyquagsire23/OpenJKDF2/blob/5793172c38d564f2e65280c9ad6d104f7c79bb3d/df2_reimpl/src/World/sithSector.c#L169

You can patch out the former by opening JK.EXE in a hex editor and searching for 83 C0 14 3B F0 and changing the 14 to 01 for all three occurrences (marked in my reversing at jkMain_gui_loop, jkMain_GameplayTick, and jkMain_EscapeMenuTick). The latter I patched by searching 83 78 0C 0A 75 0F 8B 54 24 08 52 50 E8 C8 0E 00 00 83 C4 08 c3 and replacing it with 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 (nopped out). It's roughly equivalent to commenting out these lines, but I think a better solution @jdmclark might be to hook and make the sithSector_ThingPhysPlayer call dependent on net_isMulti

Most of the game's logic already uses millisecond deltas so I haven't actually noticed any bugs in singleplayer, but the animations don't seem to interpolate that well in some places.

shinyquagsire23 avatar Jan 04 '21 04:01 shinyquagsire23

@shinyquagsire23

Interesting discovery! That isn't what I expected. I can't say for sure, but that code smells like a late bug fix to me (probably to hack around the kinds of simulation instability problems that we know a variable timestep causes). If that's the case, disabling it would cause problems, but I wouldn't expect them to be immediately obvious.

I think the best solution of all would be to use OpenJKDF2 to implement a correct fixed timestep and a renderer that can do state interpolation, when such an effort becomes feasible.

jdmclark avatar Jan 04 '21 06:01 jdmclark

I actually just figured out how to unlimit the framerate when testing some OpenJKDF2 function rewrites with my 120FPS monitor, so I can actually explain a bit of the weird jitter:

The first issue is the game has its own framerate limiting where it will not update the game unless it reaches 20ms from the last update, seen here: https://github.com/shinyquagsire23/OpenJKDF2/blob/d7e836b6860c4d460829bb034f9eddf0d7a6ed1f/df2_reimpl/src/Main/jkMain.c#L136

The second issue is that player physics in particular are coded to run at 50FPS, presumably for multiplayer? seen here: https://github.com/shinyquagsire23/OpenJKDF2/blob/5793172c38d564f2e65280c9ad6d104f7c79bb3d/df2_reimpl/src/World/sithSector.c#L169

You can patch out the former by opening JK.EXE in a hex editor and searching for 83 C0 14 3B F0 and changing the 14 to 01 for all three occurrences (marked in my reversing at jkMain_gui_loop, jkMain_GameplayTick, and jkMain_EscapeMenuTick). The latter I patched by searching 83 78 0C 0A 75 0F 8B 54 24 08 52 50 E8 C8 0E 00 00 83 C4 08 c3 and replacing it with 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 (nopped out). It's roughly equivalent to commenting out these lines, but I think a better solution @jdmclark might be to hook and make the sithSector_ThingPhysPlayer call dependent on net_isMulti

Most of the game's logic already uses millisecond deltas so I haven't actually noticed any bugs in singleplayer, but the animations don't seem to interpolate that well in some places.

Awesome work! Just one question though, what happens to first person weapon animations above 47fps? With the old logic they start playing faster but only above 47fps.

I think the best solution of all would be to use OpenJKDF2 to implement a correct fixed timestep and a renderer that can do state interpolation, when such an effort becomes feasible.

Probably the best study example for this would be the Chimera mod for Halo, which is another dll injection, but fully interpolates model animations and positions between 30fps up to whatever fps you want.

bangstk avatar Jan 10 '21 01:01 bangstk

Awesome work! Just one question though, what happens to first person weapon animations above 47fps? With the old logic they start playing faster but only above 47fps.

I had someone else test the patch and on their end it did have issues, but apparently framelimiting it externally w/ dgvoodoo to 48FPS felt smoother to them? Which makes sense to me at least, I feel like the physics stepping probably is the worst offender since player input applies acceleration to the player object -> head rot + movement might skip frames. I haven't actually noticed weapon animations being too fast on my end though, might be a WINE vs Windows thing somehow? Or I misremember how fast it should be.

Probably the best study example for this would be the Chimera mod for Halo, which is another dll injection, but fully interpolates model animations and positions between 30fps up to whatever fps you want.

Yeah I got OpenJKDF2 to work with WINE at least so I might just maintain something similar, ended up fixing aspect ratios too w/ some ifdefs and I feel like weapon/other interpolation wouldn't be too difficult to fix. My vague model though is something between the N64 decomps and OpenRCT, eventually replacing the client entirely if I can.

shinyquagsire23 avatar Jan 10 '21 01:01 shinyquagsire23

Just applied the hex changes myself and can confirm this also fixes the weapon animations speed. They play at the correct speed even at 60fps.

I used the saber altfire as a test, with the correct speed you can see that the second slash matches up with the third person animation.

Awesome news and thanks again! The game feels so much fresher.

bangstk avatar Jan 10 '21 01:01 bangstk

Last thing I noticed then, these hex strings don't seem to exist in the Steam/GoG versions of the JK.EXE executables. Do they include addresses that might be different in them? Any other instructions/hex strings nearby that are likely to be the same?

EDIT: Nevermind, it's there in the GoG version, the Steam EXE seems to have its code section compressed or encrypted in some way. Strange

bangstk avatar Jan 10 '21 04:01 bangstk

And lastly: to bring the discussion back to JkGfxMod, I would still advocate for allowing user to set an arbitrary framerate limit. Reason being that now with the fixed FPS patching code found by @shinyquagsire23 the game does appear to speed up at extreme framerates (my computer can run it at about 800 FPS without vsync and at that speed the game physics are certainly about 4-5x faster than they should be) so vsync is required for JkGfxMod to work with it.

bangstk avatar Jan 10 '21 05:01 bangstk

Throwing my hat into the ring for allowing the user to set a frame rate limit.

DaerosTrollkiller avatar Sep 27 '22 16:09 DaerosTrollkiller

Throwing my hat into the ring for allowing the user to set a frame rate limit.

You should move to this project https://github.com/shinyquagsire23/OpenJKDF2 (which includes jkgfxmod)

KainXVIII avatar Sep 28 '22 09:09 KainXVIII