How does VX Ace limit frame rate?

Mhin Ra

Veteran
Veteran
Joined
Aug 17, 2015
Messages
65
Reaction score
19
First Language
English
Primarily Uses
RMVXA
I know there's a Graphics.frame_rate member; I'm not talking about that (at least not directly). I'm trying to figure out how VX Ace prevents itself from calling Scene_Base.update much faster than 60 fps. Looking at the code in Scene_Base.main, update is called in an unconstrained while loop:

update until scene_changing?​

Inside that update() call, we just call Graphics.update, Input.update and update_all_windows in sequence then return. In order to restrict the game to 60 fps, somebody needs to be doing a sleep before that update method returns.

The docs under help talk a little bit about frame limiting in the Graphics module section, so my working hypothesis is that perhaps Graphics.update is doing that sleep internally and won't return until the right amount of time has passed? But, I can't verify that because that module is in the compiled dll, I believe. Does anyone know where the sleep is happening that ensures the game's frame rate is limited?

Why I care
I've noticed that a completely unmodified default project (in that empty initial level) has incredibly choppy framerates on certain laptops (like on a version of the MS Surface I have). Even on my relatively powerful desktop, there will pretty consistently be frame hitches.

Looking at the CPU utilization, even on the hitchy laptop, shows no core on the machine ever getting above 15 or 20% usage. I've written a per-frame profiler to try and see if there's expensive work being done on some frames, but it doesn't seem like there's a smoking gun there (not surprising since most of the VX Ace code base just does brute force updating of all game state on every frame... there's not really much deferred work).

So, given that the CPU isn't utilized and I haven't discovered some occasional expensive operation, I'm wondering if something in the frame limiting code is working incorrectly on that laptop. It's not clear I can _do_ anything about it (if it's in the maker dll) but at least knowing where the problem is would save me a bunch of time writing ever more sophisticated profilers ;) And, if I know where the issue is, there's always the possibility I could come up with some esoteric workaround.
 

Kes

Veteran
Veteran
Joined
Aug 3, 2012
Messages
22,299
Reaction score
11,713
First Language
English
Primarily Uses
RMVXA
'Scripts' is where people who have written completed scripts which they want to share with others can post them.

[move]RPG Maker VX Ace[/move]
 

bgillisp

Global Moderators
Global Mod
Joined
Jul 2, 2014
Messages
13,522
Reaction score
14,255
First Language
English
Primarily Uses
RMVXA
Using ACE as I have, I have found a few things which affect frame rates:

1) The step counter. My game started to get choppy when the step counter got over 30,000 or so and my playtime was over 10 hours. Since I couldn't control the playtime I hardcoded my step counter to 9,703 or something like that, as for some reason forcing it to stay 0 also caused the game to get choppy? Maybe there is something in the dll that tries to stop it from having 0 steps or something?

2) Windows Update, though any other automatic update program will count. I had an instance where I dropped all the way to 2 FPS on ACE and did a CTRL+ALT+DEL and found that something was using 99% of my HD (and not the game). A few hours later I got a notice that my computer was ready to update. Seems Windows was downloading an update to the HD and that was affecting everything on my computer. And I mean everything, even Microsoft Paint couldn't run well while it was doing that download.

BTW, I think this belongs in learning Ruby actually, as you will need to explore the code. I'll move it there.
 

Another Fen

Veteran
Veteran
Joined
Jan 23, 2013
Messages
565
Reaction score
276
First Language
German
Primarily Uses
I still haven't figured out how Ruby and dlls work together and don't have information about the insides, but from my observations:

Generally "Graphics.update" is responsible for both refreshing the screen contents and for sleeping until the frame time is over. If executing the Ruby part plus drawing the frame takes too long the module will fall behind on its internal timer. After reaching a certain treshold Graphics.update will skip actually drawing the frame in order to catch up, which might end in an uneven framerate.

You can reset the internal timer using "Graphics.frame_reset", so the end of the next "Graphics.update" will be scheduled in one frame time (might slow down the game for a better frame rate).
Not advocating overusing that feature, but may help getting to the bottom here maybe?
 

Mhin Ra

Veteran
Veteran
Joined
Aug 17, 2015
Messages
65
Reaction score
19
First Language
English
Primarily Uses
RMVXA
Sorry about putting this in the wrong place. There's a lot of categories on the forums that felt very similar, and I took my best guess.

As to the actual question: I've made progress. When I wrote the first post, I stuck in that last sentence about 'coming up with an esoteric workaround' based on pure hope. However, it got me thinking: if I was correct and something in one of the dll calls (let's say Graphics.update, for kicks, but it could be any of them) was enforcing the framerate, could I work around that?

After some knocking about, I figured out I could. Since we can set the framerate via Graphics.frame_rate, you could imagine a solution where I tell the dll to run at some crazy high frame rate (okay 120, since that's the max). Then in the ruby code, I could enforce that we don't update the game logic at more than 60 fps. As it turns out, this was fairly trivial to do:

Graphics.frame_rate = 120

class Scene_Base

alias :updateBasicFrameFixer :update_basic
def update_basic

if @lastUpdateCallStart != nil
goodNextStart = @lastUpdateCallStart + @@frameTime

# While we're waiting around to call game update, we might as well update
# the graphics and input, so things appear responsive
while Time.now.to_f < goodNextStart.to_f

updateBasicFrameFixer()
end
else
@@frameTime = 1.to_f / 60.to_f
goodNextStart = Time.now.to_f
end

@lastUpdateCallStart = goodNextStart
# Now do one more update, since it's time and return
updateBasicFrameFixer()
end
end


On my speedy desktop, this locks the game to 60 fps absolutely on even my worst maps (which is dramatically better than before).

On the jerky laptop, it dramatically improved the situation, but I suspect that the calls to update_basic (i.e. Graphics.update) are a little too slow to just blindly put them into the busy wait... so some little bit of logic that looks at how long until the next frame the picks whether to busy wait or call the original update_basic might help. Or, maybe it would be smarter to not call update_basic again at the end if we already called it in the busy wait. Lots of ways to potentially improve.

Also, since I'm calling Graphics.update at a non-regular interval, the frame_count variable isn't going to be super accurate. That means a little more work would be needed to replace usages of it (for timers, play time and a few other things). You'd also need to mock out the frame_rate uses, since the Graphics module's opinion of the frame rate isn't really accurate anymore.

Anyway, I'm going to keep iterating for myself, but I thought I'd share it because this has been a pretty endemic problem in my usage of VX Ace and it could help a lot of folks to eliminate it. If anyone finds this valuable, let me know and I can post whatever final version I end up with.
 

Heirukichi

Veteran
Veteran
Joined
Sep 24, 2015
Messages
1,421
Reaction score
596
First Language
Italian
Primarily Uses
RMVXA
I had to do something similar and I used a frame skip for slow computers. Basically I run the game faster but only update the graphics if the last cycle was fast enough, otherwise I simply update the Input. Just store the last update time, then add the frame time, if the next update is supposed to happen after the calculated amount, skip it and only update the Input, if not, update the graphics. Of course, in the next iteration it will update much faster so the check will likely return true.

The logic is pretty much the same, but since RGSS is quite slow at handling Bitmaps, updating Graphics every time is not exactly an easy task.
 

TheoAllen

Self-proclaimed jack of all trades
Veteran
Joined
Mar 16, 2012
Messages
5,594
Reaction score
6,525
First Language
Indonesian
Primarily Uses
RMVXA
This was the thing I never thought of so thank you!
I wonder if it's valuable enough to include it in my anti-lag script.
 

bgillisp

Global Moderators
Global Mod
Joined
Jul 2, 2014
Messages
13,522
Reaction score
14,255
First Language
English
Primarily Uses
RMVXA
@Mhin Ra : I'd be interested to see your final version myself.

@TheoAllen : I'd say do it! It would help many if it doesn't cause many issues.
 

TheoAllen

Self-proclaimed jack of all trades
Veteran
Joined
Mar 16, 2012
Messages
5,594
Reaction score
6,525
First Language
Indonesian
Primarily Uses
RMVXA
@bgillisp the only effect I could think is if the anti-lag fail to save the fps, it will slow down the game instead of beginning to skipping frames but the FPS will still stay at 60. i.e, your character sprite moves slower.
 

Heirukichi

Veteran
Veteran
Joined
Sep 24, 2015
Messages
1,421
Reaction score
596
First Language
Italian
Primarily Uses
RMVXA
@TheoAllen in the system I wrote movement is handled differently: each time a character is updated, the time is stored and both the sprite rectangle and the sprite coordinates are updated based on that. If the graphics haven not been updated for 3 frames, the position is calculated as x (respectively y) plus pixels per frame times 3. The same happens for the sprite rectangle. It does look less smooth, but the game runs fast.
 

Mhin Ra

Veteran
Veteran
Joined
Aug 17, 2015
Messages
65
Reaction score
19
First Language
English
Primarily Uses
RMVXA
Will do; I'm working on fixing up all the references I mentioned earlier. It looks like instead of calling the full base update routine, it's better to just call Graphics.update in the busy loop (closer to what Heirukichi did). Calling Input.update makes the key detection super touchy (b/c the window for detecting key presses becomes really short on fast machines, I believe) and makes text windows draw text too fast. Anyway, I'll definitely post my final version.

TheoAllen - you're welcome to include it in your Anti-lag script; I used your script as the basis for most of the anti-lag stuff in my game, so I'm flattered you think this might be useful. But, do wait until I've got the final version (obv) because this is a fairly deep change and warrants pretty thorough testing before publishing it to the world. I might recommend you give it a whirl in a few of your projects as well, since there are things (like timers) which I don't think I use in my game. So, it's possible there are issues I'd never see.
 

TheoAllen

Self-proclaimed jack of all trades
Veteran
Joined
Mar 16, 2012
Messages
5,594
Reaction score
6,525
First Language
Indonesian
Primarily Uses
RMVXA
Deciding to call or not update input is a bit tricky, but I think it's better to also update for the input mainly because if you only call Graphics.update, the window becomes irresponsive to input.

Let's call two kinds of updates as the "minimized update" and the "regular update". In the regular update, it will update all the objects and in the minimized update, it's the minimalized to only Graphics update call. Let's also say that somehow the game lagged that minimized update is called for like 10 times before the regular update kicks in. I tested in a sandbox mode using this code.
Code:
i = 0
input = nil
loop do
  Graphics.update
  i += 1
  if i % 10 == 0
    Input.update
    if Input.trigger?(:C)
      input = "triggered"
    end
  end
  p input
  unless input.nil?
    input = nil
  end
end
If your input gets lucky that it enters the regular update, "triggered" is printed in the console, if it doesn't the input gets ignored. My proposal is to include in the "minimized update", however after it detects any input, it's either stop updating the input or somehow store the trigger somewhere else and also updating for another input (I hope my words are clear enough). Unfortunately, I'm not sure how to checks all input unless using a messy brute force code.

@Mhin Ra the fact that my anti-lag was made from a contribution from various scripter, you sure have a place in there as well. I'll be sure to put your name in the credit list. I'm waiting for the full version.
 

Mhin Ra

Veteran
Veteran
Joined
Aug 17, 2015
Messages
65
Reaction score
19
First Language
English
Primarily Uses
RMVXA
@TheoAllen I agree with your intuition about the input stuff; that's the same reason I included it originally. What I found, weirdly, is that if I did update Input on every graphics frame, the game ended up becoming unresponsive to key presses. Specifically, hitting enter to talk to an event would tend to require me to hold down enter for a longer period (or just not work at all). I don't have a great working theory for why that's true... (it's got to be something in the Input processing internals, but I'm stumped as to how they structured it to get that result... I could see button presses being treated as holds too often, but button holds should still cause key presses on release).

It is very possible that what you proposed (i.e. once we read the first key, stop calling Input.update) would work. As you said, the code is little messy, so I've not written it so far. My thinking, though, is that this script is designed to ensure frame updates happen at 60FPS. Polling input at that frequency (for an RPG) should be 100% fine. If the script is lagging, we might end up polling less - but the original RPG Maker would have done the same. So, I don't think the script introduces a new problem, and the better solution is to make your game not lag (b/c detecting key presses which we can't respond to in a timely fashion will also not be a great experience). Anyway - just a bunch of musings. For now, the version I made does not include a mitigation for calling Input more frequently than game update.

I've attached the frameRateFixer that's been cleaned up. I've not done a bunch of playtesting with it to uncover weird issues, but I've unit tested it enough to give my fairly high confidence. I've also attached an fpsDisplay script that I've been using (which I've modified to show both the game update framerate and the graphics update framerate). Seems useful for debugging :)

Let me know if you've got comments, questions or if you find any issues. I'm doing a full playthrough test of my game right now, so I should get fairly good in situ test coverage for this work over the next week or so. Obviously, I'll holler ASAP if I find anything wrong.
 

Attachments

TheoAllen

Self-proclaimed jack of all trades
Veteran
Joined
Mar 16, 2012
Messages
5,594
Reaction score
6,525
First Language
Indonesian
Primarily Uses
RMVXA
Quick question, what is the difference between the two aside from a different header? It looks identical to me unless I'm seeing it wrong because it's already at midnight here.
 

Mhin Ra

Veteran
Veteran
Joined
Aug 17, 2015
Messages
65
Reaction score
19
First Language
English
Primarily Uses
RMVXA
Quick question, what is the difference between the two aside from a different header? It looks identical to me unless I'm seeing it wrong because it's already at midnight here.
Nope... you're totally right. I added that little warning at the top about placing the frame rate fixer above other script modifications, but I actually pasted that into the wrong script file (the fps one instead of frame fixer) and then copied that wrong one over into the file I uploaded. I have, I believe, now corrected the problem. I have also turned a mild shade of red ;)

I fixed the file in situ in the above post, to hopefully avoid future confusion.
 

Roninator2

Gamer
Veteran
Joined
May 22, 2016
Messages
2,661
Reaction score
563
First Language
English
Primarily Uses
RMVXA
I put it in my project and only saw a small improvement, but then changed the frame rate to 90 and now it looks like other smaller rpg maker vx ace games. A definite improvement. But I was just testing battles.
When I tested the game itself, the walking and autorun were very fast.
I may try to modify this to work for battles only.

Works for going from normal map to battle, but then it stays at the battle framerate.
Need to figure out how to get it to downgrade after battle
 
Last edited:

bgillisp

Global Moderators
Global Mod
Joined
Jul 2, 2014
Messages
13,522
Reaction score
14,255
First Language
English
Primarily Uses
RMVXA
Tested in mine, no issues that I could see. Though the battle transistion animation is now blazing fast, that took me off guard the first time. I think the battle transistion animation might be running at 120 FPS actually.
 

Mhin Ra

Veteran
Veteran
Joined
Aug 17, 2015
Messages
65
Reaction score
19
First Language
English
Primarily Uses
RMVXA
Ah - I will take a look at battles. I have entirely replaced the menu-based battle system in my game, so it's entirely possible there's some issues around the transitions and in that experience. I did have to be relatively careful with some other transitions because a few of them have one-off logic for determining their speed. I think tile flashing might also move at hyper speed because I think that rate is entirely determined in the dll. I'll test that when I get into some combat areas of my own game.

Thank you so much for testing!

Update: I think the issue is calls to Graphics.transition and a couple other things; I'm working on a fix. The code around it is surprisingly... intricate ;)
 
Last edited:

Mhin Ra

Veteran
Veteran
Joined
Aug 17, 2015
Messages
65
Reaction score
19
First Language
English
Primarily Uses
RMVXA
@bgillisp kk; I think I've fixed the other graphics calls, which should make transitions to battle (and a few other transitions) work as intended. I am a little worried that movies will play at double speed, but every movie I tried to play just crashed the game. I can test that if anyone has a sample movie that actually works; I suspect I know what the fix would be.

@Roninator2 You should not see walking or running speeds change in the main game; they definitely don't in mine. What version of the script are you using? The code I had way up at the top definitely had that issue, but the code in the attached TXT files further down should not. If you're still seeing it, can you make a vanilla RPG Maker project with this script in it that has the issue and send it to me? What this script is doing is fairly invasive, so if you have other scripts that also do anything 'deep', they could conflict. At a basic level, if any of your installed scripts make any calls directly against the graphics module (Ctrl+Shift+F in the scripts window and look for 'Graphics.' - if any non-default script files show up, they reference it), then all bets are off on how things will behave.

Oh, and in terms of improvement: if you're running on a fairly fast machine, I think the difference you'll see from this is minor. The game will frame skip less and rendering\movement will feel a bit smoother, but it's not going to take a map that's actually stressing the machine from 30 FPS to 60. But, if your machine was struggling to maintain a consistent framerate before AND that wasn't due to the machine being overloaded (as I was seeing on the surface laptop), I think you'll see pretty dramatic improvement. So, I guess for me this fix is mostly about improved hardware compatibility so that fewer folks who eventually play my game will end up with a crappy experience, rather than something which will offer a big performance improvement on typical developer machines.

<As per usual, I update the TXT files above. I also added a version number into the frame fixer file (this is v0.3) so you can tell if you've got the most recent version. Fps script is unchanged.>
 
Last edited:

Roninator2

Gamer
Veteran
Joined
May 22, 2016
Messages
2,661
Reaction score
563
First Language
English
Primarily Uses
RMVXA
the most recent version
I just put that into my game and I get an error. Line 101 'logfile' If i comment that section out I get the error on line 87 'logfile'
Commenting that out gets it to work.

I have a lot of scripts in my project. Your framefix effectively brings me to 500 script slot entries.
In my battles (before your script) the frame rate varied from 10-35(my system specs are down below)
With your script I'm getting 45-50 in battle. So it's almost normal. So I was just trying to bump the framerate a little higher for battles, but then the normal game was really fast. The title screen is double (since you initially set the framerate to 120) after starting the game the speed is normal. Pretty much 60fps in normal game play.
 

Users Who Are Viewing This Thread (Users: 0, Guests: 1)

Latest Threads

Latest Profile Posts

I should realize that error was produced by a outdated version of MZ so that's why it pop up like that
Ami
i can't wait to drink some ice after struggling with my illness in 9 days. 9 days is really bad for me,i can't focus with my shop and even can't do something with my project
How many hours have you got in mz so far?

A bit of a "sparkle" update to the lower portion of the world map. :LZSexcite:
attack on titan final season is airing tomorrow, I'm excited and scared at the same time!

Forum statistics

Threads
105,882
Messages
1,017,230
Members
137,607
Latest member
Maddo
Top