-
Notifications
You must be signed in to change notification settings - Fork 353
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
WIP: Support >60Hz (2nd try) #585
base: master
Are you sure you want to change the base?
Conversation
This time I was lazy and kept |
Ok, I think I have fixed the "Switching game modes can trigger a fake black screen" issue, by fixing how com_frameTime is calculated. At least on my (Linux) PC, the framerate is extremely stable now (with vsync disabled), but not at the rate configured with com_gameHz, but the next multiple of I guess I should try using floating point (double) times at least for the async tics, but probably I should also try how that all works on Windows and how it interacts with vsync. However, all this somehow feels wrong: I can't find it right now, but I think there are comments in the code stating that the tics are supposed to be incremented at 60Hz, and that this is supposed to be independent of the render framerate. On the other other hand, I don't see the point of that whole async thread for incrementing tics, no matter if the renderer is running in sync with the game or not. |
the console cursor also changes the flashing frequency with different values for com_gameHz |
74f40bd
to
f8fa12f
Compare
true, I've also noticed this already, will add it to the list (I also know why it happens, just doing other changes first) |
f8fa12f
to
c7e184e
Compare
I've pushed lots of changes in the last hours. Another is that the framerates should now be very close to what's configured with com_gameHz (unless VSync or a slow computer slow it down), because the timing of frame starts is now done with much more precision than full milliseconds. Most times in the game and engine still use full (integer) milliseconds, like before, so I wonder if this causes any issues or if starting the frames at the correct time is good enough for things to run smoothly. |
0916ab0
to
0bffae3
Compare
looks goot so far, played up to 1st airlock in mars city underground. There is an issue with the air supply though. It can be manipulated by changing com_gameHz while being outside com_gameHz 500 -> cycle airlock then set com_gameHz to a lower value changing com_gameHz to a higher value than configured has ofc the opposide effectl |
(rebased this branch again, to current master, so it includes the fix for #587) I'll look at the oxygen issue. |
Thanks for testing by the way, I appreciate that you're doing this, and how thorough you are! :) By the way, does dhewm3 at higher framerates feel smooth? |
It was a coincidence, I changed the setting while i was still in the airlock, but after I already initiated the airlock cycle. Desktop is set to 144Hz. I would not want to go back to 60Hz. |
I do, it's just that some code (incl. some scripts) uses the game tics that are incremented each frame, and at a higher framerate they increment faster.. But maybe I could change the logic to use a floating point tic number and then scale the added value each frame (at 120Hz add 0.5 instead of 1 tic), so the target tic would remain the same.. I just hope this isn't used by too much code besides the oxygen. Or I'd have to re-evaluate going back to 60Hz tics for the game code and only render at a higher framerate, but I'm not sure if this is feasible (and if there is even a point to that: what use are 120fps if nothing changes every second frame? but maybe something can be done without increasing the tic, I'll have to look at how the prediction code for multiplayer works, it might be related to this..)
why not? |
aaaaah. damn. No stutter, nothing. Feels very good so far. |
Here is a build for Windows: dhewm3-1.5.4pre-highfps_win32.zip This contains both the soft particles changes and this high FPS stuff. Other new features:
|
Based on the comment from @dezo2 at #250 (comment) I tested d3xp Erebus4 and yes, that new Imp near the start of the level definitely doesn't spawn (or drop?) like it should when running at 120 or 144 or 240 fps (it still seems to work fine at 60). It should come out of the black hole that arrow is pointing to: It eventually is there, at least if I stand directly below that hole, but at 60fps it drops down that hole pretty soon after that panel from the ceiling falls down after entering the room. Something else I noticed is that things completely fall apart when I change com_gameHz while the game is running (loading a savegame again fixes it). No idea what's going on there, or if the issue is in the scripts or C++ code or whatever. |
I got finally some time before going back to work so I just want to thank you Daniel for working on this. I tested the base game briefly and the game movement seems fine. What I found so far using com_gameHz 200 and 240 with VSync on a 240Hz VRR monitor: The crane at the start of map alphalabs3 doesn' t come up when I press the button and crashes the game (something about binding an object to itself). Which is strange because the chaingun firing rate is OK, so the scripts should be adjusted. There is a slight problem with 240Hz using VSync in cutscenes, where the audio sync (lipsync) drifts after a while even with default com_fixedTic 0. This is minor and can be fixed by running the engine at 200Hz (5ms int) - you can test it with map mars_city2 at the start, the survivor up the ladder there speaks relativelly long. I also noticed very brief frame skips when the Hz does not match the integer framerate like 240Hz and again they dissapear when using 200Hz or by setting com_fixedTic 1. The frequency of the skips seems to match the difference between int and float frametimes so at 240Hz 4.1666ms they are realtivelly far between. The imp in RoE should come down if you kick the panel under the hole - seems like the spawn is somehow tied to the panel position as it lands differently when it falls down before the imp at FPS >60. I will continue testing when I have some time. |
The crane crash is not caused by the scripts after all, I tried it with my old code and it needs the SetSteerSpeed adjustment in Physics_AF.h, it was this line in the code: But I thought you did something similar it in your new code, so not sure what's going on. |
Thanks for testing!
Hmm interesting theory - maybe the panel blocks the spawn position or something?
I just realized: I replaced that code with |
Just pushed a commit that fixes the crane (I tested the crane and didn't have crashes with 120, 240 or 60fps) |
Updated Windows build: dhewm3-1.5.4pre-highfps2_win32.zip By the way, some suggestions for testing:
|
Tested RoE yesterday with com_gameHz 200 and was able to run from start to finish without problems (apart from that one blocked imp in erebus4). No crashes or game breaking bugs, enemies went up/down the stairs to find me, grabber/slowmo/oxygen/enviro suit/machinery/elevators/bridges all worked as expected, so to me the code is already very usable. I will also try the base game but don't really expect any major problems there. BTW thanks for the F10 hidden menu tip, didn't even know about it. As for the second blocked imp, it's in the base game, map recycling2 (-494.73 1410.72 -11.75) 190.2, right after interaction with the left screen. Imp should jump out of his closet, but with FPS >= 85 (11ms) he will be blocked with a panel he tried to kick out. So similar situation to than one in RoE. Not sure if this can be fixed without breaking something else in the physics code. |
I just wanna point out that the original Doom 3 can now run at higher fps than BFG edition. What a time to be alive in. |
I may have found a fix for the blocked imps and potentially other enemies hidden behind wals and panels. This was caused by the STOP_SPEED in physics being too low for higher FPS, so by replacing a line in the source files Physics_RigidBody.cpp (base and d3xp): |
Crap, celebrated too soon. It works with 200 FPS but not with 144 or 120. The physics code is seriously weird. The STOP_SPEED has to be the culprit for these problems. |
This fixes the issue of scaled frametimes in d3xp. As for some reason the events defined in C++ code must be defined again in scripts (script/doom_events.script), I had to add a little hack to the script compiler to inject the definition for getRawFrameTime() (it's done when the getFrameTime definition is parsed)
also do that when loading a savegame, so those values are correct even if com_gameHz has a different value than it had when saving
It used to be set with just com_frameTime = com_ticNumber * USERCMD_MSEC; where com_ticNumber is incremented in each idCommonLocal::SingleAsyncTic(). That worked well enough when USERCMD_MSEC was a fixed constant, but now changing com_gameHz could cause com_frameTime to decrease, which leads to all kinds of funny glitches like fading into the main menu when switching mods taking way too long, resulting in a black screen (when the mod has higher com_gameHz => lower USERCMD_MSEC). So now this is done in a function with slightly more logic that adds to com_frameTime instead of recalculating it with a multiplication. I also changed how SingleAsyncTic() (or idCommonLocal::Async()) is called, by using a proper thread instead of an SDL_Timer. This gives me more control over it so it can be more precise (and, as far as I can tell, it is)
So far dhewm3 ignored that CVar, now it tries to set that refreshrate Also display the current refreshrate in the Dhewm3SettingsMenu, but no way to configure it there yet.
Sys_MillisecondsPrecise() returns the milliseconds since start of the engine as a double, so it can also represent micro- or even nanoseconds. Sys_Milliseconds() now just returns that value casted to unsigned int, for backwards compatibility. Sys_SleepUntilPrecise( double targetTimeMS ) sleeps until Sys_MillisecondsPrecise() returns a value >= targetTimeMS. It aims to have a precision of about 0.01ms (10usec), and achieves that by using a busy loop (with pause instructions) for the last 1-2 ms
by using Sys_MillisecondsPrecise() and Sys_SleepUntilPrecise() and by adding com_preciseFrameLengthMS (as float instead of int). Renamed com_gameFrameTime to com_gameFrameLengthMS, so it's less confusing with com_frameTime (which is the timestamp of the current frame, not its length)
for the nsec part of struct timespec a simple long is enough
instead of the ones from parms, as they can be different
also made it more precise by using floats with Sys_MillisecondsPrecise() instead of ints with Sys_Milliseconds()
it uses com_ticNumber to decide when to (not) draw - that must be scaled for shorter tics (at higher FPS)
turns out ResetSlowTimeVars() also does things one absolutely does *not* want at that point, like resetting frame counters and times that are *always* used, no matter if slowmo is enabled or not
Smoothes things out a bit, but also makes deviations more visible in the min/max frametimes of com_showFPS 2
in dezo2's code it was SetSteerSpeed(const float speed) { steerSpeed = speed * 60.0f / USERCMD_HZ; } I replaced it with `steerSpeed = speed * gameLocal.gameTicScale;` but gameTicScale is gameHz/60, not 60/gameHz, so it must be `steerSpeed = speed / gameLocal.gameTicScale;` Same for STOP_TIME.
turns out it should be `10.0f * gameLocal.gameTicScale` instead of `10.0f / gameLocal.gameTicScale` after all, probably, at least it seems to work better according to dhewm#585 (comment) (Doesn't fix the issues with all framerates though, apparently 200fps work better now, but 144 or 200 don't) Moved the calculation into a function so if further adjustments are needed it's easier to do
in preparation of trying out TDM's fixes
they have *lots* of changes to physics code, partly to support mantling and movement in water, so it's not that easy to tell which are relevant for us. These are in things already identified as unstable here, so I'm hopeful that they help..
many thanks to dezo2 for suggesting these fixes! I think in the StepMove() case it's more of a hack than a fix - *maybe* the correct thing would be to accumulate the deltas over multiple frames when you got blocked, or something like this? - but as long as it doesn't break anything else I don't care..
at high framerates, grabbed stuff often fell down before being in front of the Grabber (where it could be thrown away again) Thanks to @dezo2 (again!) for pushing me in the right direction!
based on a patch from @dezo2, who took the general idea from D3BFG this works around the problem that for most framerates, with integer frametimes, gameHz * frametime doesn't add up to 1000ms (e.g. 120Hz * 8ms = 960ms), by making some frames a millisecond longer
it's still possible, but a warning is shown and the setting in the menu only goes up to 250 now. Apart from physics getting wonkier the higher the framerate is, >250Hz breaks slow motion effects in RoE (d3xp), because it divides frametimes by four, and as the frametimes are integers, for >250Hz they're <= 3, so divided by four that's 0, which isn't good. Furthermore, I moved that setting to the Video settings, so it's closer to related settings like VSync and display refreshrate. While at it, I made sure that `com_showFPS 2` can be configured in the menu as well (it still assumed it's a bool). Last but not least I fixed a misleading variable name in Win_InitTime()
to prevent them from jumping so much at high framerates
The changes in this branch breaks the Game API, so it won't be in a 1.5.x release.
it already mostly worked, but picking up oxygen bottles gave too little oxygen at >60fps and changing com_gameHz while being "outside" would screw up the remaining oxygen and the implementation was needlessly invasive anyway. I reverted most changes, turned idPlayer::airTics into a float (so they are now virtual 60Hz tics instead of actual number of tics) and adjusted the code that increases or decreases the airTics (idPlayer::UpdateAir()) to scale the values added/subtracted accordingly. Apart from that it's only a few changes to accomodate the fact that it's a float now.
it only returns Sys_MillisecondsPrecise() casted to unsigned int anyway
43f63af
to
f4f4894
Compare
FFS why do those expire :-/ I rebased to the current master code to trigger a new build, so downloading artifacts from those builds at https://github.com/dhewm/dhewm3/actions?query=branch%3Akickstart-my-hertz should work again |
Based on @dezo2's patch (see also), but adjusted to make the framerate configurable with the
com_gameHz
CVar + my own fixes, also some ideas from Stradex' branch.Very much WIP, and no guarantees this will work out
TODO
Potential bugs that have been observed with various attempts of running dhewm3 with >60Hz and that are worth testing:
com_fixedTic -1
, might not be relevant here?)I can still reproduce this bug. I think it's related to counting tics (com_ticNumber
?) and then multiplying it with USERCMD_MSEC and comparing the result to Sys_Milliseconds() or something, combined with one mod having a differentcom_gameHz
value than the other, so tics are longer/shorter. Needs more investigation..Should be fixed
But at least (unlike in Stradex/simonedibilio's branch) they don't break every time you change
com_gameHz
, so from now on they should be stable again.idGame::RunFrame()
multiple times if com_gameHz is too low