Author Topic: FF7/FF8 Steam Frame Limiter Bug  (Read 486 times)

meesbaker

  • Insane poster
  • *
  • Posts: 325
  • Karma: 36
    • View Profile
FF7/FF8 Steam Frame Limiter Bug
« on: 2017-05-01 10:38:46 »
Hi guys, I have a question regarding the randomly ranging fps on the steam ports of ff7 and 8. Apparently the games are supposed to run in 30fps in field jus like their original counterparts but run a bit higher under certain circumstances. While ff7 can go pretty much anywhere from 29-40 depending on the hardware (curious also why that is, usually the strawberriestier pc the faster) ff8 is a bit more “consistent“ but seems to have a glitch of similar nature where fps in win7 range between 31.3-7.

Now for ff7 dlbp has supplied us with a fix, apparently a single offset in memory at 0x787b48 has a bad value. Experimenting with it I experienced it to function like an fps multiplier, so one can set fps to perfect 30.00 tweaking it.

My main question is if there is a similar solution for ff8. Since the fps behave in a similar way, even if so in a smaller range I can imagine that the cause is similar and thus jus as easy to repair. Would be great if someone whos into that topic could have a look at ff8 field fps.
« Last Edit: 2017-05-01 10:41:34 by meesbaker »

DLPB

  • No life
  • *
  • Posts: 8568
  • Karma: 233
  • This rose will never die This rose will never die
    • View Profile
Re: FF7/FF8 Steam Frame Limiter Bug
« Reply #1 on: 2017-05-01 12:16:33 »
FF7 does indeed have a value that was probably once needed with older processors, but isn't now.  Or was completely wrong.  But FF7 also had really bad limiters regardless. FF8 is coded completely differently.  I don't recall any fps issues with the PC version of FF8.  Does the fps drop when recording?

It's unlikely to be exactly the same issue.

meesbaker

  • Insane poster
  • *
  • Posts: 325
  • Karma: 36
    • View Profile
Re: FF7/FF8 Steam Frame Limiter Bug
« Reply #2 on: 2017-05-01 12:26:16 »
The fps in ff8 do not drop when recording. The issue is that they are a bit higher than 30 in windows7 , around 31.3-31.7. In ff7 the issue can be fixed alternatively by disabling aero design for whatever reason , on ff8 it makes no difference. However ff8 runs at the correct 30 in windows 10 (again for unknown reasons)

The concern is mostly about speedrunning, getting the correcg and perfectly consistent gamespeed. FF7 we could establish 30fps very easily thx to your finding now Ive been asked if sth similar could be done for ff8. :)
« Last Edit: 2017-05-01 12:28:15 by meesbaker »

DLPB

  • No life
  • *
  • Posts: 8568
  • Karma: 233
  • This rose will never die This rose will never die
    • View Profile
Re: FF7/FF8 Steam Frame Limiter Bug
« Reply #3 on: 2017-05-01 13:58:32 »
I have had a look, and you're right that Steam version shows 31-32.  I really don't know why the assembly looks like this - or why they coded it the way they seem to have.  But whatever...

Each module has the target ticks calculated on entry. Seems to be QPC here, but not sure if that's how it was back when FF8 first came out.  Probably QPC forced with Steam version. Each module is passed a Double floating point number (field and world map are being set to 30.5).

call 004020C0

This is the function (in Steam English) that calculates the tick target. 

Field module value fed into this at 46FDA9.

Also, I don't think the frame rate was wrong with the original FF8 game?  I don't recall it being wrong with Aali's version either?

I don't think the FF8 limiter will be "perfect" without a significant rewrite, like I had to do with FF7.
« Last Edit: 2017-05-02 11:38:23 by DLPB »

meesbaker

  • Insane poster
  • *
  • Posts: 325
  • Karma: 36
    • View Profile
Re: FF7/FF8 Steam Frame Limiter Bug
« Reply #4 on: 2017-05-02 00:25:10 »
Thank you a lot!!! :)

DLPB

  • No life
  • *
  • Posts: 8568
  • Karma: 233
  • This rose will never die This rose will never die
    • View Profile
Re: FF7/FF8 Steam Frame Limiter Bug
« Reply #5 on: 2017-05-02 11:37:13 »
OK, I was a little tired last night and didn't see the obvious.  This isn't a 4 byte floating point value.  It's a Double (8 byte floating point), pushed to the stack.

The value in FF8 is set to 30.5 (instead of 30).  To fix the limiter to use 30 for world map and field, set the following:

WorldMap
53EFC1 = 68 00 00 3E 40

Field
46FDA9 = 68 00 00 3E 40


Still, this does not seem to fix the limiter to be exactly 30.  So there is more going on here.  But it's a start.  I don't know why they set to 30.5 originally.  That's not how a proper limiter should work at all. You can't just put an extra 0.5 on if things aren't working out for you lol. 
« Last Edit: 2017-05-02 15:10:46 by DLPB »

NFITC1

  • No life
  • *
  • Posts: 2739
  • Karma: 61
  • I just don't know what went wrong.
    • View Profile
    • WM/PrC Blog
Re: FF7/FF8 Steam Frame Limiter Bug
« Reply #6 on: 2017-05-02 22:36:14 »
That's not how a proper limiter should work at all. You can't just put an extra 0.5 on if things aren't working out for you lol. 
Since when has "proper" stopped Square from doing things?

DLPB

  • No life
  • *
  • Posts: 8568
  • Karma: 233
  • This rose will never die This rose will never die
    • View Profile
Re: FF7/FF8 Steam Frame Limiter Bug
« Reply #7 on: 2017-05-02 22:58:49 »
And, unfortunately, I also don't care enough about ff8 to go into why its limiter isn't working as expected. From what I've seen it's again doing a lot of unneeded nonsense that's causing problems.  And the menu doesn't appear to be using the same limiter... seems to be using different code, which is likely why it's getting a clean 60 (or so it seems).

I don't think the people porting these games had much clue about the difference between a console and a PC.

NFITC1

  • No life
  • *
  • Posts: 2739
  • Karma: 61
  • I just don't know what went wrong.
    • View Profile
    • WM/PrC Blog
Re: FF7/FF8 Steam Frame Limiter Bug
« Reply #8 on: 2017-05-03 19:05:07 »
0040AB4B has some interesting clues as to how the variable is formed. It's using timeGetTime to determine how many milliseconds it takes to perform a certain number of operations and how fast it can perform one operation and basing the time between frames on that.

Actually, no. It makes FAR less sense than that. It contains beautiful snippets like this:

Code: [Select]
  v3 = 1000;
  v4 = 1;
  if ( v4 >= 0 ) v3 = v3 / v4;
  for ( i = timeGetTime(); i OR FFFFFFFFh <= 2 * v3; i = timeGetTime() ); //purposefully wait until timeGetTime's lower DWord value is less than 2000; BUT WHY?!
  i = timeGetTime();
  while ( i == timeGetTime() ); //This will have a small difference, but it never does anything with this delta

That's the most meaningless use of timeGetTime I can imagine. It doesn't do anything and it looks like it's literally designed to waste time. The next snippet at least makes some sense:

Code: [Select]
  i = timeGetTime();
  sub_40AA12(&v2);
  while ( timeGetTime() - i <= v3 );
  sub_40AA12(&v5)

40AA12 gets the value from rdtsc (Read Time-Stamp Counter). Then it calls 40AA62 that then compares the two results and returns the delta. Essentially this is reporting the delta of rdtsc with a one second delay, but only sort of. From the documentation of rdtsc:

Quote
The RDTSC instruction is not a serializing instruction. Thus, it does not necessarily wait until all previous instructions have been executed before reading the counter. Similarly, subsequent instructions may begin execution before the read operation is performed.

That's a strange way of saying that what is returned may not be the exact value of the TSC (Time-Stamp Counter) by the time the instruction is completed.

In any case, this delta is what is the basis of the function DLPB mentioned at 4020C0. Because of the non-guaranteed read accuracy this won't directly represent a second and when this value is divided by whatever is passed to it.

1A78DC0 is the rdtsc delta divided by whatever passed value is.
1A78DC8 is 1000 divided by whatever passed value is.
The combination of these values and a few other sets a sleep function between frame draws (4020F0). That's where the inaccuracy lies. RDTSC and Sleep aren't serialized functions so their effects aren't exact. It probably works WAY differently on PSX because all the hardware is identical. Since there are hundreds (thousands maybe?) of PC processors you'd have to include a timing function to set the sleep function (which is the wrong way to do it).
« Last Edit: 2017-05-04 15:08:43 by NFITC1 »

DLPB

  • No life
  • *
  • Posts: 8568
  • Karma: 233
  • This rose will never die This rose will never die
    • View Profile
Re: FF7/FF8 Steam Frame Limiter Bug
« Reply #9 on: 2017-05-04 15:42:12 »
From what I could see 0040AB4B wasn't used by field or world map during play or when jumping field <> WM.  Is this another area?

And yeah, I noticed the sleep functions, which should be nowhere near a frame limiter function.  Sometimes a value of 0 is used with sleep in a main loop to allow other threads to gain control, but I have never ever seen sleep used as a main part of a limiter.
« Last Edit: 2017-05-04 15:44:29 by DLPB »

NFITC1

  • No life
  • *
  • Posts: 2739
  • Karma: 61
  • I just don't know what went wrong.
    • View Profile
    • WM/PrC Blog
Re: FF7/FF8 Steam Frame Limiter Bug
« Reply #10 on: 2017-05-04 16:23:08 »
it looks like 40AB4B is called once at the beginning of execution and the result is considered a global constant. It's stored in a big array along with certain graphics handler pointers and window setting values.

I understand why the sleep function was created in the first place and modern conventions say stay away from it, but this port dates back to '99 or '00 so it's not likely that the problems of these processor-level functions had been exposed.
« Last Edit: 2017-05-04 16:26:23 by NFITC1 »

DLPB

  • No life
  • *
  • Posts: 8568
  • Karma: 233
  • This rose will never die This rose will never die
    • View Profile
Re: FF7/FF8 Steam Frame Limiter Bug
« Reply #11 on: 2017-05-04 22:04:06 »
That function may simply be working out what QPC is.  The total ticks the processor is doing a second.  Maybe some other fancy stuff on top.  The Steam version seems to be using QPC to do limiting despite having calls to TGT (massive numbers).  It's just a mess in there as usual.
« Last Edit: 2017-05-04 22:30:51 by DLPB »