Page 1 of 1
Sprite ram updates in main thread
Posted: Mon Sep 10, 2018 8:44 am
by casprog
Hi all,
Since I'm using DMA transfer during NMI my sprite data is technically already buffered, so is there any harm in updating sprite ram data (position, attributes, etc), in my main loop instead of within NMI?
The only possible risk I can think of is I may be making changes while the DMA process is active but I don't know if that is a bad thing on the NES?
I tried searching for a definitive answer on this but could not find it.
Re: Sprite ram updates in main thread
Posted: Mon Sep 10, 2018 8:56 am
by tepples
Mostly your main loop needs to set a flag in RAM that tells your NMI handler that the sprite list is filled and ready to push to OAM, and then have the NMI handler clear that flag. If the flag is not set, the NMI handler would skip pushing the list to OAM. Otherwise, during lag frames, your NMI handler could end up pushing a half-built list, causing visible artifacts.
Re: Sprite ram updates in main thread
Posted: Mon Sep 10, 2018 9:58 am
by tokumaru
casprog wrote:Since I'm using DMA transfer during NMI my sprite data is technically already buffered, so is there any harm in updating sprite ram data (position, attributes, etc), in my main loop instead of within NMI?
Not at all. That's actually the preferred way to do things: main thread handles game logic and buffers PPU updates (which includes the OAM buffer), and then the NMI handler flushes the buffers to VRAM/OAM. You just need to use a flag to indicate to the NMI handler when the buffered data is valid - you don't want to send half-ready data to VRAM.
The only possible risk I can think of is I may be making changes while the DMA process is active but I don't know if that is a bad thing on the NES?
That won't happen, because the CPU stays busy while the DMA transfer takes place, execution only continues when the transfer is done.
Re: Sprite ram updates in main thread
Posted: Mon Sep 10, 2018 12:08 pm
by casprog
A-ha! thanks guys : )
Perfect, currently my NMI works with a PPU buffer and main thread updates sprite ram, so I just need to add the ready flag.
Re: Sprite ram updates in main thread
Posted: Mon Sep 10, 2018 12:28 pm
by koitsu
Not sure if it will matter to the OP, but doesn't doing this
cause problems with PAL consoles? (That 3rd paragraph required me to read it about 6 times...)
Re: Sprite ram updates in main thread
Posted: Mon Sep 10, 2018 12:42 pm
by lidnariq
(I just edited that paragraph and moved it to the end of the section; hopefully it's clearer now. The entire rest of the section is only about the 2C02, so I thought it made sense to move this bit about the 2C07 to the end)
Re: Sprite ram updates in main thread
Posted: Mon Sep 10, 2018 1:37 pm
by koitsu
lidnariq wrote:(I just edited that paragraph and moved it to the end of the section; hopefully it's clearer now. The entire rest of the section is only about the 2C02, so I thought it made sense to move this bit about the 2C07 to the end)
Yes, this is much clearer -- thank you!
Re: Sprite ram updates in main thread
Posted: Mon Sep 10, 2018 3:56 pm
by rainwarrior
koitsu wrote:Not sure if it will matter to the OP, but doesn't doing this
cause problems with PAL consoles? (That 3rd paragraph required me to read it about 6 times...)
The OP wasn't asking about doing the DMA during the main thread, so it's not really related to that problem, no.
Yes, please give that paragraph a copyedit/rewrite. I think I wrote it, but it was more of a first pass attempt to have the wiki describe the PAL situation
at all.
Re: Sprite ram updates in main thread
Posted: Mon Sep 10, 2018 8:45 pm
by casprog
I noticed a nice chunk of cycles saved adding the flag in, hovering around 1100 now under close to full load while processing my ppu buffer and redrawing sprites, and on average nmi is sitting around 99!
I added this at the end of my main thread, after my game logic runs:
Code: Select all
; check if we should update dma
LDA spriteram_updated
BEQ dma_check_done
LDA #$01
STA perform_sprite_dma
; reset spriteram update flag
LDA #$00
STA spriteram_updated
dma_check_done:
Then during nmi:
Code: Select all
LDA perform_sprite_dma
BEQ sprite_dma_done
LDA #$00
STA $2003
LDA #$02
STA $4014
; reset dma flag
LDA #$00
STA perform_sprite_dma
sprite_dma_done:
The NMI routine is the only place which resets the dma flag and my main thread is the only place that turns it on. My sprite updates appear to be working correctly, does this solution seem OK?