I've run into an odd situation in my game engine which I'm having trouble understanding what is going wrong. The problem only appears on real hardware and not on Nestopia or Nintendulator for example. Fortunately it is a very, very small problem and if I have to live with it, I will (much like the tiny flickering dash of pixels in the split screen in SMB3...not *ABSOLUTELY PERFECT* but close enough that I will just live with it if I have to).
The basic structure of my vblank is as follows (many details omitted):
1. Turn off background graphics (my vblank code is cycle timed and I turn them back on about 16 scanlines after vblank ends)
2. upload ppu data
3. set scroll
4. perform sprite dma IF we got to the end of a complete frame of sprites (so, no dma if frame is dropped)
5. cycle padding
then in my main loop, I set a flag telling the vblank when to upload various chunks of data (details omitted), including sprites.
In all emulators, everything's working absolutely pixel perfectly, not a glitch, not a stutter, nothing. Perfection. But on hardware,....ocasionally two sprites near the top left of the screen flicker in rare cases when frames drop. You might say, well why are frames dropping you BAD PROGRAMMER. Well, this only happens in very special circumstances, and main gameplay is paused, so I'm probably going to live with that as well. 99% of the time there is no frame-dropping in the game. Still though. If I'm protecting sprite dma transfers, if I do NOT perform one on a given frame, sprite memory should be left alone and look the same as it did on the previous frame, right? How on earth would any sprites randomly flicker in this case?
Since it only happens on real hardware, I'm a bit stumped. Again though, such a tiny problem, I'm probably gonna just accept it. OR maybe figure out why I'm dropping frames to begin with, which would be another solution...
It is possible that the OAM DRAM is decaying, given the extended length of your vblank and then not DMAing new OAM data...
Typing out my question here helped me think about it, and I think the problem might be that, in these special circumstances my vblank routine is hitting worst case, and may be pushing part of the sprite dma transfer past the end of vblank. I've seen things like this before where real hardware might have a glitch with a tightly packed vblank routine, but no glitches on emulators. Now I'm seeing it with sprites. Thankfully it's pretty small...prob gonna live with it, but...I'm so damn close to having this thing perfect
I thought for a moment I could turn off sprite rendering as well as bg rendering, but now I recall the dot crawl pattern can get all mucked up. Or at least, the funky scanline glitches I saw upon trying that certainly look like dots crawling
Is there a workaround for that? I'm guessing not. Probably my only options are:
1) Live with it
2) Find out why I'm dropping frames and stop doing it
3) Optimize whatever may be pushing the sprite dma transfer past the end of vblank slightly. I tried moving it up before other operations, but that's more dangerous because then I might be putting ppu uploads and scroll register setting past the end of vblank.
GradualGames wrote:
I've seen things like this before where real hardware might have a glitch with a tightly packed vblank routine, but no glitches on emulators.
Yeah, emulators can be pretty lenient about things like that, since they were written with mostly the good, working cases (= most games) in mind.
Again this is one of those areas where it would be useful to have diagnostic warnings in emulators. Maybe I'll add one in NintendulatorDX at some point ("writing to $2004 during rendering").
Yeah, you can't let an OAM DMA run long out the end of your NMI. It's not safe to do that, probably is just as bad as trying to write to $2007 in the same situation, just the problem manifests in sprites instead.
My general practise is for the OAM DMA to be the first thing in my NMI handler, but this is because someone found that on PAL it begins refreshing the OAM partway into the vblank, so it's not safe to do it late unless rendering is off. (This is a fuzzy recollection though, I don't remember all the details here.)
If you can't guarantee that you're done by the end of vblank, my suggestion would be:
1. Disable rendering at start of NMI via $2001.
2. Do your PPU uploads.
3. OAM DMA should be the last thing you do before re-enabling rendering. (If you do it first and run long, it might decay before rendering gets back on.)
4. Manually adjust scroll with $2006 rather than $2005.
5. Turn rendering on via $2001.
This will result in a background displaced downward by one or many lines if you run long. Obviously your sprites and background won't be aligned together in this case, but at least you won't have any corruption of the background or sprites. If you know how much you're running long (it should be predictable by the amount of PPU data uploaded) you can adjust the scroll to compensate and have no misalignment. (If you're over by less than 8 lines, probably won't even be seen, hidden by overscan areas.)
If you're disabling only the BG, you still have to be careful with VBlank timing, because the PPU is still doing everything normally, it's just not outputing BG pixels. So yeah, don't let updates spill past the end of VBlank. The alternate dot crawl pattern will inevitably be there if BG and sprites are disabled when rendering starts. You might try disabling sprites soon after rendering starts and then make use of the forced blanking time, but I personally wouldn't mess with that considering all the OAM oddities that have been discovered recently.
I used a similar structure to yours in the past, because I wanted to hide a scroll seam in the top 16 scanlines. The timed VRAM updates were a pain to manage, because every little change had to be compensated for elsewhere, so I ended up placing 9 transparent sprites at the very top of the screen and using the sprite overflow flag to detect the beginning of the frame before waiting 16 scanlines and reenabling the BG. As a bonus, the transparent sprites will clip any other sprites that might be wandering up there. The downside is that you waste 9 sprites, but being able to write normal VRAM update code is totally worth it IMO.
rainwarrior, interesting, this morning I was thinking of possibly trying to set $2006 manually. I forget how to do that, nor could I find a good example searching on this site. Anybody know a post with a good example?
The fun thing is, this might be a fine case for misaligning sprites and bg, because a very brief, but intense animation is going on and I'm actually shaking the scroll at the same time---so visually any misalignment I think would be believable.
tokumaru, I remember your 9 sprite trick, you had explained it to me when I first started down this rabbit hole, haha. The timed vblank code hasn't been so bad in my case, due to having constrained my vblank routine to being relatively simple. It is a bit time consuming to "re-tweak" my LUT for cycle-pad values, but, it only really needs to be done before release, I usually just let it get all bouncy during development. (the graphics hiding bar) I periodically re-tweak it when I have an OCD episode, heheh.
GradualGames wrote:
rainwarrior, interesting, this morning I was thinking of possibly trying to set $2006 manually. I forget how to do that, nor could I find a good example searching on this site. Anybody know a post with a good example?
The definitive guide:
http://wiki.nesdev.com/w/index.php/The_skinny_on_NES_scrolling
I've tried so many times to read that doc, and still can't. I'm just glad smarter people have deciphered it well enough to make tutorials that enabled a lesser mind like mine to be able to grasp enough to actually build games..! I've been going a little nuts over perfecting this one very tiny edge case in my game I'm probably just going to change the animation so I'm not maxing out my vblank routine. I could probably pull it off if I rewrote everything that I've already done, but that'd take me another half year or more...I'd rather just finish this and move on to something else, and apply what I've learned later.
GradualGames wrote:
I've tried so many times to read that doc, and still can't.
There's a lot of stuff on that page, but the most important part for practical use is under the header "Split X/Y scroll":
http://wiki.nesdev.com/w/index.php/The_ ... 2FY_scrollThat example shows you how to encode X and Y scroll values (+ the nametable select bits) into a set of $2005/$2006 writes, and how to time the writes. A bit misleadingly perhaps, the second code block in there lists the register values in different order than the order that the writes should be performed in, but the table has the writes in the correct order.
Thanks. It looks like that is kinda tough to get right. I made a brief attempt to do it with my heavy-handed vblank routine, but, the sad thing is, the truly correct way of fixing this is probably to write a new vblank routine specific to the sort of animation I'm doing (sad because well...its hard to get any of this stuff right period). I imagine unless one really NEEDS to do this sort of scroll manipulation for a desirable split screen effect for example, that the best choice would be to write a better vblank routine in order to use the simpler and standard $2006, $2005 writes within the alotted time. The bit shifting required to do this manually makes my head hurt. I'll probably revisit it some day if I do any split screen effects. I did see the background shift as rainwarrior mentioned, and unfortunately it was too much, I think. Back to the drawing board!
I am happy to report I was able to successfully write a new vblank routine to support the animations I referred to (including cycle padding for the scroll update hiding bar...I'm getting so used to this it may become my preferred method.). What I was doing before was insanity. I had tried to piggyback on my map decode routines because they already took care of computing nametable coordinates, etc. and OVERWRITE tiles into the buffers those routines used...but since they always decode an entire row or entire column, this introduced various interesting edge cases (this is for animating dungeon exits) that have dogged me for months. Now I have a nice clean vblank routine that just uploads a simple buffer of columns. Why on earth did I not do it this way before? I dunno. All one can do is keep striving and keep improving. Anyway, all sprite glitches gone.