PPU sprite priority

From NESdev Wiki
Revision as of 14:04, 11 June 2009 by Ndwiki (talk | contribs) (Created page with 'In the NES PPU, each sprite has two values that affect '''priority''', or what appears behind what: the index of the sprite within OAM (0 to 63), and the prio...')
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigationJump to search

In the NES PPU, each sprite has two values that affect priority, or what appears behind what: the index of the sprite within OAM (0 to 63), and the priority bit (attribute 2 bit 5, set to 0 for front or 1 for back). In some cases, putting a back-priority sprite in front of a front-priority sprite can let the background show through and cover up the front-priority sprite. Super Mario Bros. 3 uses this for power-ups sprouting from blocks, putting a blank block "behind" the block at a low index and then putting the power-up "behind" that at a high index. (You can see the corners of this blank block when Mario hits a note block in World 1-2, as the note block becomes more squared off.)

The Nintendo DS PPU handles priority the "obvious" way,[1] and some NES emulator developers initially think the NES PPU handles it the same way:

  1. Front priority sprites in front
  2. The background plane in the middle
  3. Back priority sprites in back

What really happens in the NES PPU is conceptually more like this:

  1. During sprite evaluation for each scanline (cycles 256 to 319), the eight frontmost sprites on this line get drawn front (lower index) to back (higher index) into a buffer, taking only the first opaque pixel that matches each X coordinate. Priority does not affect ordering in this buffer but is saved with each pixel.
  2. The background gets drawn to a separate buffer.
  3. For each pixel in the background buffer, the corresponding sprite pixel replaces it only if the sprite pixel is opaque and front priority.

(Low-level hardware detail: The buffers don't actually exist as full-scanline buffers inside the PPU but instead as a set of counters and shift registers. The above logic is implemented a pixel at a time.)