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

Framerate slowdown & 60FPS issues #23

Open
emoose opened this issue Nov 27, 2021 · 29 comments
Open

Framerate slowdown & 60FPS issues #23

emoose opened this issue Nov 27, 2021 · 29 comments

Comments

@emoose
Copy link
Collaborator

emoose commented Nov 27, 2021

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.

@emoose emoose changed the title Rifle reload animation Rifle reload animation stuck at 30FPS Nov 27, 2021
@Keith94
Copy link

Keith94 commented Nov 27, 2021

Site note: Some (or all) other issues at 60 FPS I have documented here

@nipkownix
Copy link
Owner

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.
But it is probably something related to code, though.

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.

@nipkownix
Copy link
Owner

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 HermiteInterpolation. Makes sense, judging by the name.
Haven't investigated further yet.

@emoose
Copy link
Collaborator Author

emoose commented Dec 1, 2021

Changing one of the args (flags?) passed from cObjSniper::moveReload to MotionSetCore has an interesting effect, kinda similar to above:

01_12_21_21_29_21.webm.mp4

For some reason it's a bit subtle in that capture, but on screen it was flying around a lot more.
Even more interesting though, my screen recorder was originally recording at 30FPS, and seems on that recording the reload looks fine, even though on-screen it was flying around madly:

01_12_21_21_24_19.webm.mp4

Maybe an issue with the interpolated frames only, and that flag is for disabling the interpolation system, not sure.
You can try this by setting bio4.exe+522161 (4 bytes) to 0 (1.1.0), this is probably regular sniper only too.

Not sure if this is actually helping smooth it at all though, kinda hard to tell :P

@nipkownix
Copy link
Owner

2021-12-02.08-55-24.mp4

Leon'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
saves from ver 1.0.6 aren't compatible with ver 1.1.0 for some reason.)

So, there's a je instruction at 74 ? 80 7d ? ? 75 ? 8b 4d. if you change it to jmp, Leon's animation will be smooth, but the rifle itself will be all over the place (more easily visible if you change the game's fps to 120)

At 68 ? ? ? ? 6a ? 6a ? 50 8d 8e ? ? ? ? 51 56 e8 ? ? ? ? 8b 15 it pushes 0x4000 as an argument for MotionSetCore. Making it push 0 instead will reduce the rifle's jitter considerably, like in my video.

@nipkownix
Copy link
Owner

In other news, I found out how to fix the speed of falling things like this one:

2021-12-02.11-30-02.mp4

I 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.
Thanks QLOC.

@emoose
Copy link
Collaborator Author

emoose commented Dec 2, 2021

saves from ver 1.0.6 aren't compatible with ver 1.1.0 for some reason.

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

I 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. Thanks QLOC.

Nice find! Would be great if that float could be multiplied/divided by g_ConfigVariableFramerate / 30 or something like that.

(I'd think the game would store some kind of g_ConfigVariableFramerate / 30 value somewhere to fix other 30FPS things with, was hoping the MotionSetCore etc funcs would be using something like that at least, but haven't found anything like that yet - pretty sure game must have something like that though, unless it's making use of a deltaTime that I haven't found yet...)

@nipkownix
Copy link
Owner

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

Weeeell, the game acts as if I have already beaten it and unlocked everything:

image

But the load screen is empty:

image

Not a big deal tho. I'll just restart the game when the new version of the HD project launches :p

Would be great if that float could be multiplied/divided by g_ConfigVariableFramerate / 30 or something like that.

Yeah, I just pushed some changes that do something like that.
I named the option "60fpsFixes" hoping we can add more to it in the future :p

@emoose
Copy link
Collaborator Author

emoose commented Dec 2, 2021

Something really weird I just noticed, game seems to store a framecount at pG + 0x530C (so 0xC5A760+0x530C in 1.1.0), a bunch of things are derived from that counter, and seems the loop in WinMain increments it (0x654BA8 in 1.1.0)

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 Move & Trans (which are kinda like update/render functions for different objects afaik)

There's even a Render call there that seems to handle rendering, but the game is displaying at 60, strange.. I guess the rendering at 60Hz must be happening in a different thread, and this is just for updating certain things, which probably causes some of the 30FPS issues we have.
Updating that loop to run at 60 would probably break even more things though :/

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 GXExt_ScreenFlipBuffers

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.

@emoose emoose changed the title Rifle reload animation stuck at 30FPS Framerate slowdown & 60FPS issues Dec 2, 2021
@emoose
Copy link
Collaborator Author

emoose commented Dec 2, 2021

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)

@emoose
Copy link
Collaborator Author

emoose commented Dec 2, 2021

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.
(except if it goes under 30 then it will slow down, because of this forcing stuff above, maybe the deltaTime it uses here could be tweaked to something better instead, or would be even nicer to track down whatever code causes it to abort...)

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 vsync = 1 is set, and put variableframerate = 120, if your monitor is 60Hz this should make game try running at 120, but get forced to display at 60, vanilla game would run at half-speed like this, but the hook above should fix it 😃

Maybe it'd be better if the deltaTime >= 0.03333333507180214 check only happened if it detected load screen was active too, so going under 30 during gameplay would also have slowdown fixed, not sure where to check for that atm though.

E: seems the loading abort happens if deltaTime is above 30FPS frametime (so under 30FPS), if you set the deltaTime >= 0.03333333507180214 to 1 / 30 then this still works fine, but 1 / 29 causes the load bug, hmm..

@nipkownix
Copy link
Owner

nipkownix commented Dec 2, 2021

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)

@emoose
Copy link
Collaborator Author

emoose commented Dec 2, 2021

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 R120Event, in there there's a call to NowLoadingOn that shows load screen - seems without the deltaTime fix applied R120Event doesn't even get ran at all tho, huh...

E2: oh I'm wrong, R120Event is ran, it's started before the intro movie though, seems the problem is the pG[0x52D0] & 0x10 check before it calls NowLoadingOn, some reason that doesn't get set... might be some movie thing for checking if movie ended maybe. (E: ah looks like it's set right above it, if (Sofdec[0] & 0x20) is set, so it likely is movie related, crap...)

E3: hmm, I think they actually tried solving this the same way, the EventMgr__IsAliveEvt line sets configFramerateTime to 0 if IsAliveEvt returned true, then later if configFramerateTime is 0, it does the same kind of deltaTime check/override.

Looks like EventMgr__IsAliveEvt never returns true for the new game stuff though, strange...

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...
Did try changing it so only overrides if g_IsNowLoading / Sofdec flags are set, but no luck with either of them.

E4: seems removing the Sleep loop broke 30FPS mode, guess that's needed for FPS limit to work properly.
Here's fixed version:

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);
	[...]
}

@nipkownix
Copy link
Owner

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.mp4

I 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.
There must be some timing issue going on.
From what I've seen, it looks like the rifle is trying to adjust it's position to match Leon's hand during that animation..? Maybe with the timing issue, it is always slightly off the intended position.
Since it affects both rifles, it might be some code that's common beteween them?

@emoose
Copy link
Collaborator Author

emoose commented Feb 11, 2022

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 MotionMove/MotionSetCore/etc for a bunch of stuff too, might be interesting to compare them.

Unfortunately as I mentioned there too:

the symbols in RE4VR are semi-stripped and a ton of names that were in GC debug are missing here, so might be difficult to find where sniper etc calls are made.

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)
https://mirrorace.org/m/36Iw3 (use 1fichier link for best speed)

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)

@emoose
Copy link
Collaborator Author

emoose commented Feb 13, 2022

Interesting thing I just noticed, RE4VR seems to have some extra funcs that make use of the frametime for recalcing some things, FArmMath::LerpRateCorrected & FArmMath::LerpToZeroRateCorrected seem to be new to RE4VR with a bunch of things updated to make use of them, there's also the FixedFrame funcs which seem to have been added in X360, but I've seen a lot more extra calls to them in VR. Might be a good lead on things that could be fixed up.

@nipkownix
Copy link
Owner

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.

@emoose
Copy link
Collaborator Author

emoose commented Feb 18, 2022

Noting so I don't forget: RE4VR's MotionHokan & MotionGetSpeed funcs have extra uses of pG->deltaTime which isn't in UHD release, not sure exactly what they're affecting though, might be working on a new field added to MOTION_INFO struct...

E: also it looks like the FArmMath::LerpRateCorrected calls are usually added to enemy cEmXX::move functions, after call to Muku (Muku can mean "turn toward" apparently), so maybe those are for fixing the increased turn speed that higher FPS has?

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.

@emoose
Copy link
Collaborator Author

emoose commented Feb 22, 2022

Been taking another look into the sniper issue, so it seems the 0x4000 flag given to MotionSetCore ends up getting checked inside MotionMoveCore, if it's set then the last param to the HermiteInterpolation calls inside that func gets set to true.

In HermiteInterpolation it seems that param only gets used for a check near the beginning, if set then it converts first param to int & back to float, I guess a way of doing abs or ceil or something - if you edit that check to always pass it actually makes every anim in the game have same stutter as sniper - except changing to 30FPS mode makes it have no difference at all...

(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 HermiteInterpolation still results in sniper bugging out unfortunately, still not sure what would cause that, wonder if there's any other weapons that have the same effect.

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 cModel::motionMove call inside wep09_r2_reload makes sniper bug out without the flag needing to be removed - nopping MotionSetCore in the same function also disables the character anim while reloading.

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?

--
Seems to be something to do with the position-updating part of HermiteInterpolation, if you patch cObjSniper::moveReload flag from 0x4000 to 0, patch wep09_r2_reload flag from 0x4005 to 0x5, and then patch in a jump over the code in HermiteInterpolation at 0x662217 (0x74 -> 0xEB), characters arms will go really long & messed up, but if you reload sniper (and use CE to set speed to 0.2x), you can see the sniper stays pretty stable in chars hand.

@emoose
Copy link
Collaborator Author

emoose commented Feb 24, 2022

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

I’ve noticed some item chests that open at double speed. Some open at the correct speed, but some are still double speed. Are there plans to correct those that still open at double speed?

If anyone finds an example of a chest this happens with please let me know, got an idea of where it might be caused.

@emoose
Copy link
Collaborator Author

emoose commented Feb 24, 2022

Seems InverseKinematics is interacting with the sniper reload somehow, if you disable part of the function (64BC48 jz to jmp) you can see most anims seem fine, but reloading sniper becomes broken - also looks like some weapons still reload fine even with this change, so I guess not all are using IK for reload anim.

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 objSniper::setMotion function sets up pointers to each FCV based on offsets inside udas header, haven't figured that out much yet though)

@emoose
Copy link
Collaborator Author

emoose commented Mar 27, 2022

Weeeell, the game acts as if I have already beaten it and unlocked everything:

image

But the load screen is empty:

image

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 :)

@nipkownix
Copy link
Owner

nipkownix commented Jul 16, 2022

@emoose I may finally have something related to our reload fps issues:

2022-07-16.20-14-37.mp4

Will 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 MotionSetCore you mentioned earlier, only it fixes the auto rifle, and breaks the regular rifle (same usual jitter). I'm thinking they changed some of this stuff in the UHD port to "fix" (read: mask) the problems in the regular rifle, but since the auto rifle also uses the same function, both of them ended up running at 30 fps.
So we might be able to, at least, fix the auto rifle.

Another thing I noticed is that Ada's crossbow also suffers the same 30fps-on-60fps bug.

@emoose
Copy link
Collaborator Author

emoose commented Jul 18, 2022

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 jz to jmp), and also remove the MotionSetCore flag, you get this (with game slowed to 1/5):

Bio4.NoFlag.mp4

In comparison, restoring the MotionSetCore flag but leaving the IK patch in place gives you this:

Bio4.Withflag.mp4

To 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.

@nipkownix
Copy link
Owner

nipkownix commented Jul 18, 2022

So, Ada's crossbow has the weird 00004000 flag as well, but it doesn't seem to need it. (At least, not for the initial "pull up" animation, haven't tested the reload one yet).
Removing it makes it play at higher fps with no problems:

2022-07-18.10-46-40.mp4

I 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 cMot3::set to set the reload anim, while the rifle uses MotionSetCore. They seem to mostly accept the same parameters. I wonder if using one over the other would show different results.

Edit2: Ah, cMot3::set is a wrapper around MotionSetCore with some extra stuff. Meh.

@nipkownix
Copy link
Owner

nipkownix commented Jul 18, 2022

Could our problem be in the hermite function? If you mess with some of the fadds/fmuls in there, every animation starts to get jittery like the rifle does.
After reading about hermite interpolation, maybe these functions are used to "convert" the original 30 fps anims to higher frame rates by calculating the "in-betweens". Just a guess, though.

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.mp4

Very 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.

@pas-de-2
Copy link
Contributor

pas-de-2 commented Dec 6, 2022

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.

@emoose
Copy link
Collaborator Author

emoose commented Dec 9, 2022

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.
(not a huge revelation I know, but pretty sure those are linked at least - could be useful for testing in future maybe)

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? 🤔
(I imagine if we did adjust velocities for frametime, we'd also need some way of letting it run with the same velocity value across multiple frames, just like the interval stuff that dropped items needed...)

@ViperAcidZX
Copy link

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.

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

5 participants