This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

Framerate change to reduce/avoid total slowdown

Framerate change to reduce/avoid total slowdown
by on (#27791)
What would be a good method to incorporate framerate changes for "too long" frames, say, change the framerate from 60Hz to 30Hz when a frame of gameplay needs two frames?

Would it be as simple as always running NMIs at 60Hz that update a frame counter, then during the second real frame of a gameplay frame, finish everything then don't do anything else until the next frame - like frameskip?

Maybe one could run code in NMIs that will never use up a lot of time. Then have the more potentially intense routines (enemies, projectiles, etc.) be executed in the reset code, which can alter in framerate when there's not enough time. Would that be not too difficult to do?

For some reason, I have a feeling that these are stupid questions...are they? I'm also wondering - do any other games use this method to avoid slowdown and alter framerate instead?
Re: Framerate change to reduce/avoid total slowdown
by on (#27792)
strangenesfreak wrote:
Maybe one could run code in NMIs that will never use up a lot of time. Then have the more potentially intense routines (enemies, projectiles, etc.) be executed in the reset code, which can alter in framerate when there's not enough time. Would that be not too difficult to do?

That's a possibility. Some games run everything in the main thread (which I think you're calling "reset"), only using NMI to signal to the main thread that a vertical blank has occurred.

by on (#27793)
Most games will double a displayed frame if processing is taking too long. I'm assuming that after they finish processing, they poll a frame counter until it changes, then trigger a screen update. They have to do this since graphics updates are usually either done for a particular 1/60 second, or not done; they can't be delayed or half-done. Some games just give up and run in 30 FPS all the time, and suffer for it. One that comes to mind (with no excuse for running half rate) is Rambo.

An interesting approach would be to dynamically adjust movement of some objects on screen, moving them smoothly if enough processor time is available, otherwise every other frame. I'm pretty sure Solomon's Key does this on rooms that have lots of enemies (without making player movement less-smooth). I posted a multi-threading demo a while back, which could be a basis for something like this.

by on (#27794)
blargg wrote:
They have to do this since graphics updates are usually either done for a particular 1/60 second, or not done; they can't be delayed or half-done.

Graphics updates can be delayed. For example, some games continuously update tiles in the background, such as gun turrets opening and closing in Contra. These games might delay the tile animation on frames when the transfer buffer is full of scroll information. A game with CHR RAM that continuously updates the sprite page might only update one sprite's cel per frame. Or a game could update backgrounds in even fields and sprites in odd fields; plenty of Game Boy games such as Wario Land: Super Mario Land 3 appear to do this.

Quote:
Some games just give up and run in 30 FPS all the time, and suffer for it. One that comes to mind (with no excuse for running half rate) is Rambo.

Movie license games have an excuse: the source material ran at 24 fps.

by on (#27796)
tepples wrote:
blargg wrote:
They have to do this since graphics updates are usually either done for a particular 1/60 second, or not done; they can't be delayed or half-done.

Graphics updates can be delayed. For example, some games continuously update tiles in the background, such as gun turrets opening and closing in Contra. These games might delay the tile animation on frames when the transfer buffer is full of scroll information. A game with CHR RAM that continuously updates the sprite page might only update one sprite's cel per frame. Or a game could update backgrounds in even fields and sprites in odd fields; plenty of Game Boy games such as Wario Land: Super Mario Land 3 appear to do this.



This works OK on the gameboy since the LCD is relatively slow, but on a CRT this looks terrible when the screen scrolls. The sprites appear to "float". For a good example of that, see most games on the Action-52.


* * *

To effect one way of auto-frame skipping would be to put your game code in the NMI, and then turn NMI's off thru the PPU when you enter the NMI, and then turn it back on again before leaving. This would be "automatic". If the code didn't finish in time, it will slip a screen until the NMI comes around again. I think SMB might do this.

by on (#27797)
kevtris wrote:
tepples wrote:
Or a game could update backgrounds in even fields and sprites in odd fields; plenty of Game Boy games such as Wario Land: Super Mario Land 3 appear to do this.


This works OK on the gameboy since the LCD is relatively slow, but on a CRT this looks terrible when the screen scrolls. The sprites appear to "float". For a good example of that, see most games on the Action-52.

And to solve that, update the scroll when you update the sprites; just don't update the tiles.

Quote:
To effect one way of auto-frame skipping would be to put your game code in the NMI, and then turn NMI's off thru the PPU when you enter the NMI, and then turn it back on again before leaving. This would be "automatic". If the code didn't finish in time, it will slip a screen until the NMI comes around again.

Code doing this would have to acknowledge pending NMI by reading PPUSTATUS ($2002) before it RTIs back to the main thread. Otherwise, if it takes between 262 and 282 lines to finish (i.e. within the next vblank), PPU updates definitely won't have time.

But just like in Perl, TIMTOWDI. If you're running everything in the main thread (as my engines do and as I seem to recall Final Fantasy doing), you can have NMI update a variable called "retraces" and do a loop like this to catch the start of a vblank:
Code:
  lda retraces
waitNMI:
  cmp retraces
  beq waitNMI
  ; update PPU now

EDIT: corrected

Quote:
I think SMB might do this.

You're right. After init, SMB1's main thread enters a loop like the following:
Code:
loop:
  jmp loop-

by on (#27798)
Most Konami games (probably all of them in fact) does everthing in the NMI routine. All 3 Final Fantasy games does everything in the Main thread and the NMI does nothing but return. I presonally find both extremes ways very unconfortable to programm (I've tried them both, in the order specified above), and doing a bit of work both in the NMI and the main thread is definitely the good way to do things I guess.

Multithreading on the NES seems interesting, but in fact any game loop that consist of a bunch of repeated calls could be considered multithreding (as long as the call themselves are unconditional).

When it comes to avoid slowdown, I'd say make optimised code to avoid it, and make sure your code can handle slowdowns without crashing anyways. You could always skip some enemies updates while not skipping graphics, muisc and players, why not ?

by on (#27799)
Bregalad wrote:
When it comes to avoid slowdown, I'd say make optimised code to avoid it, and make sure your code can handle slowdowns without crashing anyways. You could always skip some enemies updates while not skipping graphics, muisc and players, why not ?

Because you have to update the scroll and all 64 sprites during blanking, taking 23 percent of NTSC vblank time, or none at all.

by on (#27800)
Many games DON'T update the scroll and sprites each frame.

by on (#27802)
You don't HAVE to do it, as the sprites will remain the same one frame to another as long as the screen is turned on so that the DRAM won't fade, and the scrolling won't budge if $2006 is not acceded during VBlank. However, if you want smooth animation on screen, you have update at least sprites and scrolling each frame. If the game slows down and you stop to update them, the screen won't mess up but the game will effectively slow down (no surprise).

by on (#27806)
I was thinking more of this, sorry if the comments are a bit too long:

Code:
nmi:

  inc nmi_framecounter  ;increase frame counter for nmi's routines
 
  lda main_done  ;check if the main thread is done
  beq + ;if not set, the main_thread is not done yet
 
  lda nmi_framecounter
  sta main_framecounter ;skip frames for long main-thread "frames" when the main thread is ready for a new frame
  lda #1
  sta nmi_occured ;main thread can begin its new frame
  lda #0
  sta main_done
 
+ lda #whatever  ;sets NMI on, NMIs will always occur
  sta ppuctrl
 
  ;...update PPU, APU, check joypads, do a few gameplay routines, etc.
 
  rti
 
main_thread:
 
  lda nmi_occured
  beq main_thread ;check if the main_thread can start a new "code frame"
 
  lda #0
  sta nmi_occured

  ;...common and potentially numerous enemies and projectiles, etc.
 
  lda #1
  sta main_done ;the main thread will be ready to begin its next frame
 
  jmp main_thread


Multithreading sounds interesting, though. Maybe the NMI just updates PPU, APU, and joypad status, while the main thread has 2-3 gameplay threads to be swapped with each other. Framerate could alter between specific threads within the main thread instead of altering the framerate of the whole main thread.

by on (#27808)
Quote:
I was thinking more of this...


i do something similar, except i let my "main" thread run as fast as possible (collision, prep vram writes, etc.), everything else (player movement, enemy ai) is timer driven. these are initialized when needed and the nmi handler decreases them. when they reach 0, "main" will process what needs processing and reload the timer. my nmi handler only writes to vram(when the correct flag is set), updates PPU registers, reads input, and updates oam.