-
Notifications
You must be signed in to change notification settings - Fork 30
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
Framerate slowdown & 60FPS issues #23
Comments
Site note: Some (or all) other issues at 60 FPS I have documented here |
Yeah, this is a big one. I don't think the problems is just the FCV anim, for the same reason as you. There are ways to import FCV anims into something like Blender nowadays, so I'll check them in comparison with other guns. Version 1.0 of the game had some other issues like this. The gates in the first el gigante fight would move erratically when closing, and the gate you explode using a cannon when you first get to the castle had a very odd anim. They have fixed this in ver 1.0.6, but I'm not sure if they fixed those using code or not. If they did use code, it could help us track something down by comparing what they changed. |
So.. I think I've found something about this: 2021-11-30.17-02-50.mp4(I intentionally slowed down my game to make the problem more apparent) This function is called |
Changing one of the args (flags?) passed from 01_12_21_21_29_21.webm.mp4For some reason it's a bit subtle in that capture, but on screen it was flying around a lot more. 01_12_21_21_24_19.webm.mp4Maybe an issue with the interpolated frames only, and that flag is for disabling the interpolation system, not sure. Not sure if this is actually helping smooth it at all though, kinda hard to tell :P |
2021-12-02.08-55-24.mp4Leon's anim here is actually very smooth now, and I was able to make the rifle jitter a little bit less, but it isn't perfect. (i'll post AOB sigs here because I'm having to use ver 1.0.6 to test, and I'm not sure if i'd convert the addresses correctly :p So, there's a At |
In other news, I found out how to fix the speed of falling things like this one: 2021-12-02.11-30-02.mp4I thought that was going to be the same issue as the rifle animation, but it has nothing to do with it. The game is just subtracting a float from the item position at every frame, but they didn't account for it when adding the 60 FPS option. |
Weird, what happens when you try using it? Never had any issue using my 1.1.0 save on 1.0.6 EXEs, maybe you should start game again with 1.1.0 :p
Nice find! Would be great if that float could be multiplied/divided by (I'd think the game would store some kind of |
Weeeell, the game acts as if I have already beaten it and unlocked everything: But the load screen is empty: Not a big deal tho. I'll just restart the game when the new version of the HD project launches :p
Yeah, I just pushed some changes that do something like that. |
Something really weird I just noticed, game seems to store a framecount at Some reason that counter is only going up by 30 every second though, even though game is running at 60FPS, so that must mean all the other things in that WinMain section are also only running at 30Hz - including stuff like calls to There's even a E: ah this might not be too big a deal actually, it's not fixed at 30 but actually just (framerate / 2), seems to be caused by it waiting for some D3D buffer flip thing to happen in Still kind of odd that game updates at half the rate of the display, but at least this might mean it's probably not related to the locked-to-30FPS issues, unless they also have issues when running in 30FPS mode too. |
btw here's a reimpl. of the (1.1.0) code in WinMain that causes slowdown if you go under variableframerate value (in #5 I posted some patches for that code, but patching x87 sucks, so reimplementing would be better): void __cdecl WinMain_DeltaTime_Hook()
{
// pointers to 1.1.0 funcs/vars
typedef int(__cdecl* ConfigGetVariableFramerate_Fn)();
ConfigGetVariableFramerate_Fn ConfigGetVariableFramerate = (ConfigGetVariableFramerate_Fn)0x93A480;
typedef bool(__fastcall* EventMgr__IsAliveEvt_Fn)(void* thisptr, void* unused, int a2, int a3, int a4);
EventMgr__IsAliveEvt_Fn EventMgr__IsAliveEvt = (EventMgr__IsAliveEvt_Fn)0x6220C0;
LONGLONG* g_PrevFrameTime = (LONGLONG*)0xC65A30;
LARGE_INTEGER* g_PerformanceFrequency = (LARGE_INTEGER*)0xC65A50;
uint8_t* pG = (uint8_t*)0xC5A760;
void* EvtMgr = (void*)0xC03C48;
uint8_t byte_C03C64 = *(uint8_t*)0xC03C64;
// reimplementation:
DWORD threadPrevAffinity = SetThreadAffinityMask(GetCurrentThread(), 0);
LARGE_INTEGER perfCount;
QueryPerformanceCounter(&perfCount);
int variableFramerate = ConfigGetVariableFramerate();
double configFramerateTime = 0;
if (variableFramerate)
configFramerateTime = double(1.0) / double(variableFramerate);
if (EventMgr__IsAliveEvt(EvtMgr, 0, byte_C03C64, 0, 0) && variableFramerate != 30)
configFramerateTime = 0;
double deltaTime;
do
{
QueryPerformanceCounter(&perfCount);
deltaTime = (double(perfCount.QuadPart) - double(*g_PrevFrameTime)) / double(g_PerformanceFrequency->QuadPart);
if (configFramerateTime > deltaTime || deltaTime < 0.01666666753590107)
{
Sleep(0);
}
} while (configFramerateTime > deltaTime || deltaTime < 0.01666666753590107);
if (configFramerateTime == 0.0)
{
if (deltaTime > 0.03333333507180214)
deltaTime = 0.03333333507180214;
}
else
{
deltaTime = configFramerateTime;
}
*(float*)(pG + 0x70) = float(deltaTime * 30);
SetThreadAffinityMask(GetCurrentThread(), threadPrevAffinity);
*g_PrevFrameTime = perfCount.QuadPart;
}
void Init_60fpsFixes()
{
injector::MakeCALL(0x6549C3, WinMain_DeltaTime_Hook);
uint8_t dec_esp_4[] = { 0x83, 0xC4, 0xFC }; // not sure why this is needed, but it is
injector::WriteMemoryRaw(0x6549C3 + 5, dec_esp_4, 3, true);
injector::MakeJMP(0x6549C3 + 5 + 3, 0x654B05);
[...]
} This is just a copy of the existing code in WinMain, need to find some way to edit this code so that slowdown doesn't happen, while still allowing new game to work fine (probably related to the EventMgr thing) |
Aha might have found the fix for it, seems the "new game" bug is something wrong with the Loading screen, looks like if deltaTime is set too high, the load gets aborted, or something like that. To fix that I just made it check if deltaTime is above deltaTime of lowest supported FPS (30FPS = 0.03333333507180214), if so then it'll force to 30FPS deltaTime instead, with that loading seems to be fine, and game shouldn't slowdown when going under whatever variableframerate is set to. void __cdecl WinMain_DeltaTime_Hook()
{
typedef int(__cdecl* ConfigGetVariableFramerate_Fn)();
ConfigGetVariableFramerate_Fn ConfigGetVariableFramerate = (ConfigGetVariableFramerate_Fn)0x93A480;
typedef bool(__fastcall* EventMgr__IsAliveEvt_Fn)(void* thisptr, void* unused, int a2, int a3, int a4);
EventMgr__IsAliveEvt_Fn EventMgr__IsAliveEvt = (EventMgr__IsAliveEvt_Fn)0x6220C0;
LONGLONG* g_PrevFrameTime = (LONGLONG*)0xC65A30;
LARGE_INTEGER* g_PerformanceFrequency = (LARGE_INTEGER*)0xC65A50;
uint8_t* pG = (uint8_t*)0xC5A760;
DWORD threadPrevAffinity = SetThreadAffinityMask(GetCurrentThread(), 0);
LARGE_INTEGER perfCount;
QueryPerformanceCounter(&perfCount);
int variableFramerate = ConfigGetVariableFramerate();
double configFramerateTime = 0;
if (variableFramerate)
configFramerateTime = double(1.0) / double(variableFramerate);
void* EvtMgr = (void*)0xC03C48;
uint8_t byte_C03C64 = *(uint8_t*)0xC03C64;
if (EventMgr__IsAliveEvt(EvtMgr, 0, byte_C03C64, 0, 0) && variableFramerate != 30)
configFramerateTime = 0;
double deltaTime;
QueryPerformanceCounter(&perfCount);
deltaTime = (double(perfCount.QuadPart) - double(*g_PrevFrameTime)) / double(g_PerformanceFrequency->QuadPart);
// RE4 has a bug with load screens, when deltaTime is set too high (which happens when files are loading)
// seems it aborts the load for some reason, so here we'll check if deltaTime is above the lowest working FPS (31FPS)
// and if so then tell game it was 31FPS instead
// (this means framerate dips below 31FPS will cause slowdown though, but those should be rare... TODO: find out what's causing the load to abort & adjust that instead)
double maxFrameTime = double(1) / double(31); // seems to cancel load if it was at 30FPS, so use 31FPS to check
// TODO: what triggers this code block? need to test whatever it is against the changes below...
if (configFramerateTime == 0.0)
{
if (deltaTime > 0.03333333507180214)
deltaTime = 0.03333333507180214;
}
// Setting deltaTime = configFramerateTime is the cause of slowdowns when actual FPS is less than variableFramerate
// (since that's telling game that it's running at eg. 60FPS, but if it's actually running at 30, things will then take twice as long)
// Ideally we want pG + 0x70 always set to the proper deltaTime, but load screen bug above may prevent that...
else if (deltaTime > maxFrameTime)
{
deltaTime = maxFrameTime;
}
// We want to limit game FPS to variableFramerate, so change deltaTime to configFramerateTime if deltaTime is lower
else if(deltaTime < configFramerateTime)
{
deltaTime = configFramerateTime;
}
*(float*)(pG + 0x70) = float(deltaTime * 30);
SetThreadAffinityMask(GetCurrentThread(), threadPrevAffinity);
*g_PrevFrameTime = perfCount.QuadPart;
}
void Init_60fpsFixes()
{
injector::MakeCALL(0x6549C3, WinMain_DeltaTime_Hook);
uint8_t dec_esp_4[] = { 0x83, 0xC4, 0xFC }; // not sure why this is needed, but it is
injector::WriteMemoryRaw(0x6549C3 + 5, dec_esp_4, 3, true);
injector::MakeJMP(0x6549C3 + 5 + 3, 0x654B05);
[...]
} You can try this out by making sure Maybe it'd be better if the E: seems the loading abort happens if deltaTime is above 30FPS frametime (so under 30FPS), if you set the |
Ooh! This looks pretty promising! Nice work! Edit: looks like the addresses are not working with my 1.1.0 exe. Might be because you have a patched .exe (I remember you saying that before, unless I'm crazy :p) |
Oh yeah, hm, I'm not sure where to get baseaddr from in your project yet, but if you hex edit 0x14E in the EXE from 02 01 to 03 01 that should let those addrs work hopefully. E: seems the issue might be in E2: oh I'm wrong, E3: hmm, I think they actually tried solving this the same way, the Looks like Would be good to find some way to tell if they're at the buggy new-game screen though, or if they're actually in gameplay or not, and then toggle this override stuff accordingly, so we could also fix under-30FPS slowdowns too... E4: seems removing the Sleep loop broke 30FPS mode, guess that's needed for FPS limit to work properly. void __cdecl WinMain_DeltaTime_Hook()
{
typedef int(__cdecl* ConfigGetVariableFramerate_Fn)();
ConfigGetVariableFramerate_Fn ConfigGetVariableFramerate = (ConfigGetVariableFramerate_Fn)0x93A480;
typedef bool(__fastcall* EventMgr__IsAliveEvt_Fn)(void* thisptr, void* unused, int a2, int a3, int a4);
EventMgr__IsAliveEvt_Fn EventMgr__IsAliveEvt = (EventMgr__IsAliveEvt_Fn)0x6220C0;
LONGLONG* g_PrevFrameTime = (LONGLONG*)0xC65A30;
LARGE_INTEGER* g_PerformanceFrequency = (LARGE_INTEGER*)0xC65A50;
uint8_t* pG = (uint8_t*)0xC5A760;
//void* EvtMgr = (void*)0xC03C48;
//uint8_t byte_C03C64 = *(uint8_t*)0xC03C64;
DWORD threadPrevAffinity = SetThreadAffinityMask(GetCurrentThread(), 0);
LARGE_INTEGER perfCount;
QueryPerformanceCounter(&perfCount);
int variableFramerate = ConfigGetVariableFramerate();
double configFramerateTime = 0;
if (variableFramerate)
configFramerateTime = double(1.0) / double(variableFramerate);
// Limit game FPS to variableFramerate
double deltaTime;
do
{
QueryPerformanceCounter(&perfCount);
deltaTime = (double(perfCount.QuadPart) - double(*g_PrevFrameTime)) / double(g_PerformanceFrequency->QuadPart);
if (configFramerateTime > deltaTime)
{
Sleep(0);
}
} while (configFramerateTime > deltaTime);
// RE4 has a bug with load screens, when deltaTime is set too high (which happens when files are loading)
// seems it aborts the load for some reason, so here we'll check if deltaTime is above the lowest working FPS (31FPS)
// and if so then tell game it was 31FPS instead
// (this means framerate dips below 31FPS will cause slowdown though, but those should be rare... TODO: find out what's causing the load to abort & adjust that instead)
double maxFrameTime = double(1) / double(31); // seems to cancel load if it was at 30FPS, so use 31FPS to check
if (variableFramerate != 30 && deltaTime > maxFrameTime
// TODO: seems this is trying to check for a problematic event, maybe trying to check for the new-game load screen?
// doesn't seem to work for new-game bug though sadly, so commented out for now so this can be applied anywhere
// (would be great to find a way to limit this so it's event-only, so that going under 31FPS would also have slowdown fixed)
// && EventMgr__IsAliveEvt(EvtMgr, 0, byte_C03C64, 0, 0)
)
{
deltaTime = maxFrameTime;
}
// We want to limit update-FPS to variableFramerate, so change deltaTime to configFramerateTime if deltaTime is lower
else if(deltaTime < configFramerateTime)
{
deltaTime = configFramerateTime;
}
*(float*)(pG + 0x70) = float(deltaTime * 30);
SetThreadAffinityMask(GetCurrentThread(), threadPrevAffinity);
*g_PrevFrameTime = perfCount.QuadPart;
}
void Init_60fpsFixes()
{
injector::MakeCALL(0x6549C3, WinMain_DeltaTime_Hook);
uint8_t dec_esp_4[] = { 0x83, 0xC4, 0xFC }; // not sure why this is needed, but it is
injector::WriteMemoryRaw(0x6549C3 + 5, dec_esp_4, 3, true);
injector::MakeJMP(0x6549C3 + 5 + 3, 0x654B05);
[...]
} |
In regards to the rifle reload animation: I'm convinced they intentionally made it run at 30 fps to mask a bug that shows up when running at other frame rates. The gun tries to.. uh.. "escape" Leon's hands: 2021-12-05.10-00-55.mp4I thought that maybe the rifle animation itself could be broken and, to test this, I disabled Leon's animation. With it disabled, the rifle animation itself works normally (it doesn't shake all over the place) This problem shows up at any FPS other than 30. |
As @nipkownix mentioned at #58 (comment), RE4VR might be good to look at for some FPS related things, since they probably had to update it for higher VR framerates. Had a check and it seems they still use Unfortunately as I mentioned there too:
Should still be possible to track stuff down by looking for func calls & other xrefs though. If anyone wants to look at RE4VR at all here's a IDA 7.6 x64 idb with it loaded in (2GB+ uncompressed btw, IDA doesn't handle this very well and a lot of stuff like functions window filtering will make it hang for a long while, usually unfreezes after some time though) I would make an IDA 7.7 db too but that one took literally hours to analyse (at least it's not ghidra where it would have taken weeks/months though :P) |
Interesting thing I just noticed, RE4VR seems to have some extra funcs that make use of the frametime for recalcing some things, |
Hmm, that makes me hopeful we can get the animation issues fixed. Thanks for the idb. The new funcs you mentioned sound pretty interesting. We might be on the right track here. |
Noting so I don't forget: RE4VR's E: also it looks like the On PC the move function is split into a bunch of other ones called with a funcptr array, but unfortunately in VR those have mostly all been inlined... I guess maybe could just apply this to all Muku uses inside cEmXX though. |
Been taking another look into the sniper issue, so it seems the 0x4000 flag given to In (E: ah just noticed after posting that you found the same thing earlier on too, should have searched the thread first lol) Removing the code in Gonna try looking into RE4VR to see if any of the stuff around this changed at all. (E: hm seems not, same 0x4000 flag, same abs/ceil code...) E: hmm, nopping the Tried nopping those with flag removed too but no luck there unfortunately. I guess the character & weapon anims must be interacting with each other somehow? -- |
Someone on re4hd reports an issue with chest opening speed at 60FPS, unfortunately not much info about which chests it happens to though: https://www.re4hd.com/?page_id=9317#comment-195477
If anyone finds an example of a chest this happens with please let me know, got an idea of where it might be caused. |
Seems BTW does anyone happen to know which FCV file is responsible for reload? I extracted wep09.udas but the FCV files are only named by index, no actual names (it also looks like |
BTW this should be fixed with the violence-level patches, empty load screen was caused by the field_9 check against 0x1E51, I guess your savegames were using a different violence-level to your 1.1.0 EXE or something, hopefully now it should work with your 1.0.6 save fine :) |
@emoose I may finally have something related to our reload fps issues: 2022-07-16.20-14-37.mp4Will have to research it a bit more, though, but it looks promising! Edit: Ah, interesting. I've tracked out what I was changing, and it turns out it was the same flags to Another thing I noticed is that Ada's crossbow also suffers the same 30fps-on-60fps bug. |
E: oh might be wrong about this, looking at the bugged anim in slow-mo without InverseKinematic patch the bugged movement is actually pretty small, probably easy to miss when comparing like this... Hmm, bug might be something related to InverseKinematics function, there's a section there which handles elbow/wrist positioning, if you patch it to skip that section (64BC48 Bio4.NoFlag.mp4In comparison, restoring the MotionSetCore flag but leaving the IK patch in place gives you this: Bio4.Withflag.mp4To me, looking at them both side-to-side they seem to be moving/rotating the exact same way, but the one with MotionSetCore flag is of course more jittery because of the flag. Guess it might mean the bug is either something inside InverseKinematics which isn't calculating wrist/elbow stuff properly, or maybe with whatever is actually attaching the gun to the hands/wrist. |
So, Ada's crossbow has the weird 2022-07-18.10-46-40.mp4I wonder what the intent of this flag actually is. If it is a thing on GC as well, then it isn't "meant" to make the anim run at 30 fps. Hmm. Edit: The crossbow is using Edit2: Ah, cMot3::set is a wrapper around MotionSetCore with some extra stuff. Meh. |
Could our problem be in the Edit: About Ada's crossbow again: If we remove the flag, the animation plays correctly when the game is slowed down, but, at normal speeds, we see a small bug in the last few frames: 2022-07-18.16-02-09.mp4Very odd that the speed itself would cause this issue. Even more odd is that while both the rifle and crossbow have issues, they are pretty different from one another, while being "fixed" by the same flag. |
Something I noticed about the rifle in the GC and Wii versions is that it's unstable and jittery just sitting in Leon's hands. Not in the PS2 version, though. Can anyone confirm if that happens on real hardware, or is it just a bug with Dolphin? Maybe whatever they did to stabilize it in the 360(?) port is somehow causing this bugged animation. |
Small thought I just had: the chandelier shaking/flickering bug in r117 had issues with frametimes varying a tiny amount, which would then cause values it calculated to change by a lot, causing the shaking/flickering. The sniper anim bug could be linked with that maybe, not with frametimes being varied, but maybe the 60FPS frametime is causing it to calculate some bad values which results in the huge anim issues. Wish I understood more about how hermite curves worked.. seems one of the main reasons to use it is for interpolation between points, this vid goes into how they work a little: https://youtu.be/jvPPXbo87ds?t=2380 Something interesting that the vid mentions is that hermites use velocities to describe how much to change by, could the velocities being used maybe not be getting adjusted for frametime properly, causing them to overshoot at 60FPS and then give us these awful results? 🤔 |
It is just me or does the cursor doing the Del Lago fight feel choppy on controller with 60 FPS enabled? Not only in the animation but movement speed as well. |
So the rifle/bow 30FPS reload animation is brought up a lot, I've tried naming & looking into all the sniper/Wep09 related funcs in my IDB, but couldn't really find much to help with it so far.
Looks like animations are stored as FCV format files, usually inside the weapons BIO4/Em/wepXX.udas.lfs file.
Apparently the FCV stores things like frame numbers & counts inside it, but does the EXE have anything to interpolate that to work with different framerates, or is that data just used directly?
Handgun/shotgun/etc reloading seems fine at both 60 & 30FPS, did they update the FCV to work at 60 for those, or is there just code somewhere to make it work at 60? (maybe the 120FPS-without-slowdown hack from #5 can help give some insight here)
(also if the FCV data for those was updated to 60, does that mean playing at 30FPS makes it run fast or something?)
If it is just FCV-data related I would have thought someone would have made a mod to fix it by now, which makes me think it's probably something code related too, that's just a wild guess though.
Maybe comparing code for Handgun/shotgun against GC could reveal something.
The text was updated successfully, but these errors were encountered: