It is currently Sun Dec 17, 2017 2:25 pm

All times are UTC - 7 hours





Post new topic Reply to topic  [ 32 posts ]  Go to page Previous  1, 2, 3  Next
Author Message
PostPosted: Mon Jan 16, 2017 8:30 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10169
Location: Rio de Janeiro - Brazil
A lag frame is when the main loop takes too long and is interrupted by the NMI. The problem with reading the controllers the NMI in this case is that the part of the main loop that ran before the NMI will see one controller state, while the part that runs after the NMI will see another state, and this could cause inconsistencies in the game logic (a button might appear both pressed and not pressed in the same logical frame depending on when you check it).

Another thing to worry about in case of lag frames is the correctness of VRAM update buffers and PPU parameters such as the scroll. You must implement some means of not letting the NMI handler use partially filled buffers or new scroll values until all VRAM updates are properly carried out.


Top
 Profile  
 
PostPosted: Mon Jan 16, 2017 8:58 am 
Offline

Joined: Tue May 28, 2013 5:49 am
Posts: 934
Location: Sweden
I see, that's what I thought.

The graphics updates each have their own flag (except OAM-DMA) that has to be set before they run (as suggested the article The frame and NMIs) and the flag are only set in the main loop when there are pending updates in the corresponding buffer. But this also means an NMI is not always of the same length every frame, not sure if that is a big problem (music might get out of beat I guess).

The reason why OAM-DMA isn't conditional is because I thought earlier that it had to happen every frame. Maybe I should make that conditional too, to avoid it using a partially filled OAM buffer?


Top
 Profile  
 
PostPosted: Mon Jan 16, 2017 9:43 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10169
Location: Rio de Janeiro - Brazil
Pokun wrote:
The graphics updates each have their own flag (except OAM-DMA) that has to be set before they run (as suggested the article The frame and NMIs) and the flag are only set in the main loop when there are pending updates in the corresponding buffer.

I don't know if individual flags for each buffer is the way to go, since the different updates might be correlated in some way that only makes sense if they're all performed at the same time. For example, in a game where the player can change characters on the fly, it makes sense to change the character's patterns, palette and OAM entries all at the same time, otherwise you may end up with miscolored or glitchy-looking sprites for a brief moment. The individual updates should not depend on each other if it there's a chance that only some of them will be carried out.

Quote:
But this also means an NMI is not always of the same length every frame, not sure if that is a big problem (music might get out of beat I guess).

I don't know as much about audio as I'd like, but I assume this distortion is way too subtle for anyone to notice. At 15720 (262 * 60) scanlines per second, a period of 20 scanlines (the length of vblank) is less than 1.3ms. I hope I did the math right.

Quote:
The reason why OAM-DMA isn't conditional is because I thought earlier that it had to happen every frame.

OAM starts decaying if the PPU is off for too long. I don't know how long is "too long", but if you aren't using forced blanking, I don't think OAM will decay from one frame to the next.

Quote:
Maybe I should make that conditional too, to avoid it using a partially filled OAM buffer?

Using a partially filled OAM buffer is pretty bad, but even if it's completely filled, if the rest of the updates aren't carried out along with the OAM DMA, the sprites might look out of sync with the rest of the scene. Objects that switch between background and sprites (such as blocks that can be broken or picked up) might look duplicated or be missing for a frame, scrolling might look jittery, and so on.

Lag frames can cause pretty bad side effects if you aren't fully prepared for them. For example, if you have a status bar along with a scrolling background, you should double buffer the scroll values (along with any other raster effects you might have) and only swap the buffers in the NMI handler. Since raster effects have to be done even during lag frames, there's no such thing as a typical "ready flag" for them, if the NMI interrupts raster effect changes, you're screwed. This is why you should double buffer them, so that there's always a set of consistent raster effects the NMI handler can use. The "ready flag" in this case means that the NMI handler can switch the sets. If you don't have raster effects, you can simply not change the scroll (which means not using $2006 either) during lag frames and the scroll from last time will be used again.


Top
 Profile  
 
PostPosted: Mon Jan 16, 2017 11:10 am 
Online

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 19354
Location: NE Indiana, USA (NTSC)
tokumaru wrote:
A lag frame is when the main loop takes too long and is interrupted by the NMI. The problem with reading the controllers the NMI in this case is that the part of the main loop that ran before the NMI will see one controller state, while the part that runs after the NMI will see another state, and this could cause inconsistencies in the game logic (a button might appear both pressed and not pressed in the same logical frame depending on when you check it).

True.

But on the other hand, if a lag frame is long enough, not polling controllers at all during lag frames might cause a press and release during a lag frame to be missed. This is the case, for example, with the Vs. coin switches.

Perhaps this is the most robust method: NMI reads the controller, accumulates presses and releases, and writes the current state to a variable. When the next frame begins, it copies the current state, presses, and releases to its own variable, and clears the NMI's copy of presses and releases.

tokumaru wrote:
For example, in a game where the player can change characters on the fly, it makes sense to change the character's patterns, palette and OAM entries all at the same time, otherwise you may end up with miscolored or glitchy-looking sprites for a brief moment.

That and if you're using CHR RAM, ensure that the "changing" cel is loaded first.

tokumaru wrote:
OAM starts decaying if the PPU is off for too long. I don't know how long is "too long", but if you aren't using forced blanking, I don't think OAM will decay from one frame to the next.

Correct: it won't decay so long as it's refreshed at least every 20-some scanlines. In particular, it won't decay between last line of one frame and the first of the next.


Top
 Profile  
 
PostPosted: Mon Jan 16, 2017 11:58 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10169
Location: Rio de Janeiro - Brazil
tepples wrote:
But on the other hand, if a lag frame is long enough, not polling controllers at all during lag frames might cause a press and release during a lag frame to be missed. This is the case, for example, with the Vs. coin switches.

Can humans press a button this fast though? If the coin switch signals are this fast, then you should probably have special code to handle that specific aspect and set a flag that the main loop can check (and clear) when it's most convenient.

Quote:
Correct: it won't decay so long as it's refreshed at least every 20-some scanlines. In particular, it won't decay between last line of one frame and the first of the next.

If I'm not mistaken, this is also the reason why OAM DMA must happen during the first 20-some scanlines of vblank in PAL consoles: OAM refresh starts after that many scanlines, even though vblank continues for another 50 scanlines or so.


Top
 Profile  
 
PostPosted: Mon Jan 16, 2017 5:14 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5899
Location: Canada
Pokun wrote:
Also what is a "lagframe"?

Maybe that was the wrong term to use, but a frame that takes too long and gets displayed twice. Slowdown. 2 NMIs but only one game update.

Edit: oh, sorry. Didn't see the second page, this has already been answered.


Top
 Profile  
 
PostPosted: Mon Jan 16, 2017 9:17 pm 
Online

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 19354
Location: NE Indiana, USA (NTSC)
tokumaru wrote:
tepples wrote:
if a lag frame is long enough, not polling controllers at all during lag frames might cause a press and release during a lag frame to be missed.

Can humans press a button this fast though?

I think I can do 14 presses per second without turbo controller assistance. That's about 2 frames on, 2 frames off. Mix in some Micronics and missed presses become the rule.


Top
 Profile  
 
PostPosted: Mon Jan 16, 2017 10:11 pm 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 3192
Location: Mountain View, CA, USA
tokumaru wrote:
Can humans press a button this fast though? If the coin switch signals are this fast, then you should probably have special code to handle that specific aspect and set a flag that the main loop can check (and clear) when it's most convenient.

Getting off-topic, but I believe it's time to introduce tokumaru to Takahashi Meijin (video). (Random trivia: Hudson's Adventure Island is actually called Takahashi Meijin no Bouken Jima -- yes, Master Higgins is supposed to be Takahashi Meijin)

Whether or not a game needs to be able to handle this kind of "input rate" depends on the game. Choose poorly, however, and you may find angry retro gamers forever loathing you -- nobody likes non-responsive controls.


Top
 Profile  
 
PostPosted: Tue Jan 17, 2017 5:16 am 
Offline

Joined: Tue May 28, 2013 5:49 am
Posts: 934
Location: Sweden
Oh this was all so easy before I was aware of all the possible problems. But thanks for all the tips!

OAM only decays (on NTSC PPU) when rendering is off? In other words when both BG and sprites are turned off in $2001? Then I don't see that as much of a problem as I'm mainly turning it off when changing screens and so on.

tokumaru wrote:
Pokun wrote:
The graphics updates each have their own flag (except OAM-DMA) that has to be set before they run (as suggested the article The frame and NMIs) and the flag are only set in the main loop when there are pending updates in the corresponding buffer.

I don't know if individual flags for each buffer is the way to go, since the different updates might be correlated in some way that only makes sense if they're all performed at the same time. For example, in a game where the player can change characters on the fly, it makes sense to change the character's patterns, palette and OAM entries all at the same time, otherwise you may end up with miscolored or glitchy-looking sprites for a brief moment. The individual updates should not depend on each other if it there's a chance that only some of them will be carried out.

I see, maybe sprite and sprite palettes should share one flag and the BG buffer and the BG palettes should share another one. That way sprites and nametables will always be updated the same time as their corresponding palettes. Either that or one flag for all graphic updates so that the whole screen is updated consistently when all buffers are filled.

I haven't done any scrolling yet. My main project is a single screen game anyway, so the only scrolling I might do is for certain effects.
I might start a new thread about my particular problems to avoid derailing the thread though.


Top
 Profile  
 
PostPosted: Tue Jan 17, 2017 6:31 am 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5899
Location: Canada
Pokun wrote:
OAM only decays (on NTSC PPU) when rendering is off? In other words when both BG and sprites are turned off in $2001? Then I don't see that as much of a problem as I'm mainly turning it off when changing screens and so on.

I believe it decays on PAL as well. It was my understanding that PAL just has a couple of forced refreshes during the extended vblank so that it doesn't end up decaying before the end of vblank. Outside of vblank it should still decay if rendering is disabled, AFAIK. (Someone with a PAL system might comment on this, though.)

i.e. the practise I understand is that after rendering is off for any length of time longer than an NTSC vblank, OAM must be fully reinitialized. OAM should only be uploaded during vblank, or on PAL specifically during the beginning part of vblank.


Top
 Profile  
 
PostPosted: Tue Jan 17, 2017 8:36 am 
Offline

Joined: Tue May 28, 2013 5:49 am
Posts: 934
Location: Sweden
Oh I see, vblank time is longer than 20 scanlines on PAL so they had to refresh it after 20 scanlines, so that it doesn't decay during vblank like it does in a forced blanking.

So if you always makes sure an OAM-DMA is run before you turn on rendering, you will never have this problem.


Top
 Profile  
 
PostPosted: Tue Jan 17, 2017 8:47 am 
Offline
User avatar

Joined: Mon Jan 03, 2005 10:36 am
Posts: 2983
Location: Tampere, Finland
rainwarrior wrote:
Pokun wrote:
OAM only decays (on NTSC PPU) when rendering is off? In other words when both BG and sprites are turned off in $2001? Then I don't see that as much of a problem as I'm mainly turning it off when changing screens and so on.

I believe it decays on PAL as well. It was my understanding that PAL just has a couple of forced refreshes during the extended vblank so that it doesn't end up decaying before the end of vblank. Outside of vblank it should still decay if rendering is disabled, AFAIK. (Someone with a PAL system might comment on this, though.)

OAM does actually seem to maintain its state on PAL NES even when rendering is off. (Presumably this means that if you do OAM DMA when rendering is off, you should do it within a sufficient time after NMI.) This is all hinging solely on my (possibly flawed) tests, so I really hope somebody else with PAL NES eventually tries to duplicate these results.

Reference: viewtopic.php?f=9&t=11041&p=127180

_________________
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: kkfos.aspekt.fi


Top
 Profile  
 
PostPosted: Tue Jan 17, 2017 8:57 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10169
Location: Rio de Janeiro - Brazil
Pokun wrote:
So if you always makes sure an OAM-DMA is run before you turn on rendering, you will never have this problem.

You'd think that a fresh OAM DMA would solve all issues, but there's one problem that not even this can fix: if you disable rendering while sprites are being evaluated, the PPU goes batshit crazy when rendering is enabled again and corrupts a few OAM bytes. For this reason, avoid turning rendering off mid-screen, but if you really need to, avoid doing it in scanlines with many sprites, and do it as close to the end of the scanline as possible.


Top
 Profile  
 
PostPosted: Wed Jan 18, 2017 3:30 am 
Offline

Joined: Tue May 28, 2013 5:49 am
Posts: 934
Location: Sweden
OK so OAM decay on PAL is still unclear.

Normally I turn off rendering using a subroutine like this:
Code:
ppu_off:
  lda #$00
  sta shadow_2001           ;turn off rendering in buffer
  lda #$01
  sta ppu_flag              ;indicate that there are PPU setting updates
  jsr nmi_wait              ;wait for an NMI to update from buffer
  rts

Then the NMI handler copies the buffer to the real $2001.
Buffering PPU setting updates to $2000 and $2001 is also as suggested from "The frame and NMI". This should make sure rendering is always turned off in vblank if I'm not mistaken.


Top
 Profile  
 
PostPosted: Wed Jan 18, 2017 4:19 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10169
Location: Rio de Janeiro - Brazil
Pokun wrote:
This should make sure rendering is always turned off in vblank if I'm not mistaken.

Yes, but does this have anything to do with OAM decay?


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 32 posts ]  Go to page Previous  1, 2, 3  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 7 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group