Raster effects and lag frames

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

Post Reply
User avatar
tokumaru
Posts: 11643
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Raster effects and lag frames

Post by tokumaru » Wed Sep 30, 2015 7:21 pm

If you have raster effects in your game (status bars, parallax scrolling, etc.) and you don't want them to glitch out during lag frames, you have to setup and/or time these effects from the NMI handler. In addition to that, you must make sure that every variable related to these raster effects (scroll values, $2000/$2001 configurations, scanline offsets, etc.) must be in a state consistent with the frame that's being displayed (for the second - or third? fourth? - time), otherwise you might end up displaying the old frame with the next frame's effects, or, even worse, the partially updated next frame's effects (since this is a lag frame we're talking about).

To avoid that kind of trouble, you need to implement some sort of double buffer for these effects, so you can freely update one set of variables while the other is used for display, and once all frame processing is done, the temporary values get committed.

Back when I didn't support any raster effects, the only variables I had to care about where the general scroll position. The NMI handler itself would copy the temporary scroll values to the actual variables used to set the scroll, only when it wasn't dealing with a lag frame. But now that I'm thinking about implementing something more complex, there might be a lot more variables to commit, and this way doesn't sound so sensible anymore.

Do you think maybe indexing the two sets would work better? Variables would indicate which is the temporary set and which is the committed one, so that flipping between them would just be a matter of changing these indices? Has anyone here ever given some thought to this kind of problem?

User avatar
rainwarrior
Posts: 7801
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Raster effects and lag frames

Post by rainwarrior » Wed Sep 30, 2015 8:00 pm

It's a problem, but I don't think it's really that complicated to solve. This is a pretty normal thread synchronization problem, and it comes up in modern graphics work frequently too.

You came up with two sensible methods of buffering (i.e. indexed, or copy-at-NMI). I don't think it makes much difference which you use, unless you have a lot of variables to buffer, in which case the index might be easier?

User avatar
tokumaru
Posts: 11643
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Raster effects and lag frames

Post by tokumaru » Wed Sep 30, 2015 9:14 pm

rainwarrior wrote:It's a problem, but I don't think it's really that complicated to solve.
Yeah, not particularly complicated, but it's something I hadn't thought about much until now, so I came here to check if anyone else did, and also to write something about the subject for future reference.
I don't think it makes much difference which you use, unless you have a lot of variables to buffer, in which case the index might be easier?
If it's only the general scroll value, copying them in the NMI when the buffered updates are flagged as stable sounds simple enough, and it's what I've been doing so far. But when there are a variable number of raster effects, that might be present or not depending on what the game is doing, switching between 2 lists using an index or a pointer sounds more appropriate.

ccovell
Posts: 1011
Joined: Sun Mar 19, 2006 9:44 pm
Location: Japan
Contact:

Re: Raster effects and lag frames

Post by ccovell » Thu Oct 01, 2015 2:20 am

My mini-game for the Turbografx/PCE, HuZero, had to calculate per-scanline tables for palette/copperbars (really for the road stripes), vertical scaling & perspective pointers to road strips, and horizontal warping/scrolling, and I wanted it totally free of lag (60fps) so I used double-buffering for these tables.

Indexing into two separate buffers works, but each indexed read/write (once added up for many scanlines, for instance) takes up too much time on slow CPUs (compared to a direct/absolute access) that it might be more worth your while to have the code that accesses these tables in RAM itself so that you can use self-modifying code to change the pointers to the tables each VBlank.

Alternately, you can have 2 identical routines in ROM that each accesses a different table, and select between them each VBlank.

Here you can see on the left side of the screen I have a couple of tiny red dotted sprites showing the raster time for the current field/frame, and the previous one. (Never mind the border colours; that just shows where other scrolling splits are happening.)
Attachments
mode7-0050.png
mode7-0050.png (5.83 KiB) Viewed 1664 times

Celius
Posts: 2157
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Re: Raster effects and lag frames

Post by Celius » Fri Oct 02, 2015 1:45 pm

If you just have a status bar, all you need are "old scroll" variables and a locking system that locks all PPU updates from happening until the next frame is ready. That's pretty much what I have now, where a single variable controls whether or not the PPU is allowed to make any updates (set to false before updating buffers, back to true after) with the exception of resetting the scroll after the status bar is drawn.

Beyond that, it sounds to me though like the most reliable solution would be the paging/double buffer set up you mentioned. It sort of reminds me of the raycasting demos we did. I don't remember if you used this method, but basically I made updates to the name table that wasn't being displayed and cut over to it once it was fully updated. After that, I do the same thing, but to the name table I was previously showing. It's basically the same concept you're talking about implementing, just with variables.

The most important thing is to ensure that the switching of buffers won't get interrupted. If you keep the buffers together and just use an index variable to switch between them, you're pretty much safe, as you can commit to using the new buffer in a single instruction:

Code: Select all

lda NewBufferIndex
sta BufferIndex  <--
Though there are many ways to ensure committing to the new buffer happens in one instruction outside of using an index...

User avatar
thefox
Posts: 3141
Joined: Mon Jan 03, 2005 10:36 am
Location: Tampere, Finland
Contact:

Re: Raster effects and lag frames

Post by thefox » Fri Oct 02, 2015 2:18 pm

Indexing sounds like a decent idea, although I never used it myself.
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi

Post Reply