It is currently Tue Jul 23, 2019 6:01 pm

All times are UTC - 7 hours





Post new topic Reply to topic  [ 6 posts ] 
Author Message
PostPosted: Wed Sep 30, 2015 7:21 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11376
Location: Rio de Janeiro - Brazil
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?


Top
 Profile  
 
PostPosted: Wed Sep 30, 2015 8:00 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 7528
Location: Canada
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?


Top
 Profile  
 
PostPosted: Wed Sep 30, 2015 9:14 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11376
Location: Rio de Janeiro - Brazil
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.

Quote:
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.


Top
 Profile  
 
PostPosted: Thu Oct 01, 2015 2:20 am 
Offline

Joined: Sun Mar 19, 2006 9:44 pm
Posts: 999
Location: Japan
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 1435 times ]

_________________
http://www.chrismcovell.com
Top
 Profile  
 
PostPosted: Fri Oct 02, 2015 1:45 pm 
Offline
User avatar

Joined: Sun Jun 05, 2005 2:04 pm
Posts: 2157
Location: Minneapolis, Minnesota, United States
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:
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...


Top
 Profile  
 
PostPosted: Fri Oct 02, 2015 2:18 pm 
Offline
User avatar

Joined: Mon Jan 03, 2005 10:36 am
Posts: 3141
Location: Tampere, Finland
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


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 6 posts ] 

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 2 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