PPU sprite priority: Difference between revisions
m (Make sentence less run-on) |
(Two-sentence summary of priority rules, requested by puke7 in Discord) |
||
(5 intermediate revisions by 4 users not shown) | |||
Line 1: | Line 1: | ||
In the [[PPU|NES PPU]], each sprite has two values that affect | [[File:Sprite priority quirk.png||thumb|right|Piranha plant sprite with lower index and back priority overlaps bullet sprite with higher index and front priority]] | ||
In the [[PPU|NES PPU]], sprites may overlap each other and the background. The PPU decides what pixel to display based on '''priority''' rules. Each sprite has two values that affect priority: the index of the sprite within [[PPU_OAM|OAM]] (0 to 63), and the sprite-to-background priority bit ([[PPU#Byte 2|attribute 2]] bit 5, set to 0 for front or 1 for back). | |||
In short: | |||
* Sprites with lower OAM indices are drawn in front. For example, sprite 0 is in front of sprite 1, which is in front of sprite 63. | |||
* At any given pixel, if the ''frontmost'' opaque sprite's priority bit is true (1), an opaque background pixel is drawn in front of it. | |||
Putting a back-priority sprite at a lower OAM index than a front-priority sprite can cover up the the front-priority sprite and let the background show through. | Putting a back-priority sprite at a lower OAM index than a front-priority sprite can cover up the the front-priority sprite and let the background show through. | ||
Line 5: | Line 10: | ||
(You can see the corners of the back-priority sprite when Mario hits a note block in World 1-2, as the note block becomes more squared off.) The advantage of this approach is that the power-up can be hidden behind the block and still have front priority, meaning the area above the block doesn't have to be pure bg pixels like in Super Mario Bros. | (You can see the corners of the back-priority sprite when Mario hits a note block in World 1-2, as the note block becomes more squared off.) The advantage of this approach is that the power-up can be hidden behind the block and still have front priority, meaning the area above the block doesn't have to be pure bg pixels like in Super Mario Bros. | ||
The Nintendo DS PPU handles priority the "obvious" way,[http:// | The Nintendo DS PPU handles priority the "obvious" way,<ref>[http://problemkaputt.de/gbatek.htm#dsvideoobjs GBATEK reference: DS Video OBJs]</ref> and some NES emulator developers incorrectly think the NES PPU handles it the same manner, i.e.: | ||
# Front priority sprites in front | # Front priority sprites in front | ||
# The background plane in the middle | # The background plane in the middle | ||
Line 11: | Line 17: | ||
What really happens in the NES PPU is conceptually more like this: | What really happens in the NES PPU is conceptually more like this: | ||
# During [[ | |||
# During [[PPU sprite evaluation|sprite evaluation]] for each scanline (cycles 65 through 240), the PPU searches for the eight frontmost sprites on this line. Then during sprite pattern fetch (cycles 257 to 320), these eight sprites 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. | |||
# The background gets drawn to a separate buffer. | # The background gets drawn to a separate buffer. | ||
# For each pixel in the background buffer, the corresponding sprite pixel replaces it only if the sprite pixel is opaque and front priority. | # For each pixel in the background buffer, the corresponding sprite pixel replaces it only if the sprite pixel is opaque and front priority or if the background pixel is transparent. | ||
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, as [[PPU rendering]] explains. | 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, as [[PPU rendering]] explains. | ||
Line 21: | Line 28: | ||
Hence, when a back-priority sprite is hidden behind non-bg background pixels, it will still hide output from higher-numbered sprite output units wherever it has a non-transparent pixel. | Hence, when a back-priority sprite is hidden behind non-bg background pixels, it will still hide output from higher-numbered sprite output units wherever it has a non-transparent pixel. | ||
== References == |
Latest revision as of 16:14, 13 February 2022
In the NES PPU, sprites may overlap each other and the background. The PPU decides what pixel to display based on priority rules. Each sprite has two values that affect priority: the index of the sprite within OAM (0 to 63), and the sprite-to-background priority bit (attribute 2 bit 5, set to 0 for front or 1 for back).
In short:
- Sprites with lower OAM indices are drawn in front. For example, sprite 0 is in front of sprite 1, which is in front of sprite 63.
- At any given pixel, if the frontmost opaque sprite's priority bit is true (1), an opaque background pixel is drawn in front of it.
Putting a back-priority sprite at a lower OAM index than a front-priority sprite can cover up the the front-priority sprite and let the background show through. Super Mario Bros. 3 uses this for power-ups sprouting from blocks, by putting a non-transparent back-priority sprite "behind" the block at a low index and putting the power-up at a higher index. (You can see the corners of the back-priority sprite when Mario hits a note block in World 1-2, as the note block becomes more squared off.) The advantage of this approach is that the power-up can be hidden behind the block and still have front priority, meaning the area above the block doesn't have to be pure bg pixels like in Super Mario Bros.
The Nintendo DS PPU handles priority the "obvious" way,[1] and some NES emulator developers incorrectly think the NES PPU handles it the same manner, i.e.:
- Front priority sprites in front
- The background plane in the middle
- Back priority sprites in back
What really happens in the NES PPU is conceptually more like this:
- During sprite evaluation for each scanline (cycles 65 through 240), the PPU searches for the eight frontmost sprites on this line. Then during sprite pattern fetch (cycles 257 to 320), these eight sprites 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.
- The background gets drawn to a separate buffer.
- For each pixel in the background buffer, the corresponding sprite pixel replaces it only if the sprite pixel is opaque and front priority or if the background pixel is transparent.
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, as PPU rendering explains.
Detailed internals of the sprite priority quirk
During sprite evaluation the PPU copies the sprites that are in y range from the primary to the secondary OAM, from which eight internal sprite output units are then initialized. These sprite output units are wired such that the lowest-numbered unit that outputs a non-transparent pixel always wins, regardless of front/back sprite priority and regardless of what the background pixel at the corresponding location is.
Hence, when a back-priority sprite is hidden behind non-bg background pixels, it will still hide output from higher-numbered sprite output units wherever it has a non-transparent pixel.