Tech demo: Real-time sprite scaling

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Tech demo: Real-time sprite scaling
by on (#136825)
Sprite scaling wasn't a hardware feature of the NES or most other 2D consoles until the Super FX coprocessor and Game Boy Advance PPU. It was on the Neo Geo AES, but that's about it. But that doesn't mean there isn't a reasonably efficient way to shrink sprites in software. I got the idea from a post by psycopathicteen to look up each byte of the tile through a lookup table that shrinks a bit plane. The Sega Master System/Game Gear has to do something similar just to flip sprites; on that platform, sprite scaling would be effectively free.

Up and Down control size; Left and Right step through frames.
Re: Tech demo: Real-time sprite scaling
by on (#137146)
Wow :shock:
Re: Tech demo: Real-time sprite scaling
by on (#137166)
I have no idea how you managed to make it this fast, but it's impressive. Back when I tried to roto-scale an image on the NES it was extremely hard to do and very slow.

Why do you have a fetish for legless people recently ? I find personally this is kinda ugly but this doesn't matter for the demo.
Re: Tech demo: Real-time sprite scaling
by on (#137178)
Nice work!

I did some simple scaling on the PCE for a title screen (replicating the Lunar SegaCD title screen), but I used packed pixel scaling and then converted to planar in realtime. It was slow, but fast enough for title screen stuffs.

This LUT method works out pretty decent. I ran some numbers of some quick code (more PCE geared) after seeing this, and along with a special WORD element shift table, I was able to drastically cut down the number of cycles compared to my old code.

Anyway, stuff like this would have been nice to see back in the day. Relatively easy to use and would be great for non cpu intensive parts in games; more than just demo scene stuff.

Got any ideas for rotation of planar graphics?
Re: Tech demo: Real-time sprite scaling
by on (#137188)
Bregalad wrote:
I have no idea how you managed to make it this fast, but it's impressive. Back when I tried to roto-scale an image on the NES it was extremely hard to do and very slow.

That's because you tried rotation at the same time. Rotation is slow, but Neo Geo scaling is fast on NES. I precompute each 8x1 pixel strip scaled down to widths 7, 6, 5, and 4, and then I apply that to each bitplane. It's actually limited more by vblank time than by anything else.

tomaitheous wrote:
Got any ideas for rotation of planar graphics?

Did you have a chance to look at my topic about shearing?

Discussion about the appearance of the character in the demo can go here or here.
Re: Tech demo: Real-time sprite scaling
by on (#137203)
I took a second look at this. Ok, so you avoid shifting and ORing the 8 pixel down scaled segments by using sprite positions to compensate for this (horizontally)? I thought it was a more brute force method. Also, why stop at 50% shrink?

The method I was doing required a shift table with 8 positions (split table with LSB/MSB) for ORing the WORD (shifted BYTE) with the data buffer together. The 8pixel planar value (byte) was loaded directly into X and used to access a the shift table (ldy idx ; ldx 8pixel_data,y ; ldy scale,x ; ora shift.lsb,y ; etc),. The shift table is kinda big (4k), with MSB of the table address (both tables) using the offset value from a counter for quick dirty addressing. And a little bit of self modifying code to shave a few cycles. It doesn't save a whole lot of cycles, compared to doing a 4bit (byte expanded) linear scale and then converting planar afterwards (4bit planar). But for NES 2bit planar format, it'd probably be decent enough in savings. Still not as quick as this sprite offset method you have here, but would lend itself to background rendering.
Re: Tech demo: Real-time sprite scaling
by on (#137204)
tomaitheous wrote:
Ok, so you avoid shifting and ORing the 8 pixel down scaled segments by using sprite positions to compensate for this (horizontally)? I thought it was a more brute force method.

For Super NES, you'll need to use a bit more force by shifting the right half of each 16-pixel-wide unit by a power of 2 to the left. The memory-mapped multiplier should work fine for this: store the shift amount ($01, $02, $04, $08, or $10) in $4202 before starting, then store the (scaled) right half of each 16x1 sliver the sprite into $4203, scale the left half, and read the shifted right half out of $4216. For an architecture without a multiplier, you might have to use a self-modifying shiftslide for this part.

Quote:
Also, why stop at 50% shrink?

Because at that point you need to drop down to the next smaller prescaled step in your mipmap so as to avoid excessive overdraw.

Quote:
[My own technique was] Still not as quick as this sprite offset method you have here, but would lend itself to background rendering.

Know what else would lend itself to background rendering, at least on Super NES? Mode 7.
Re: Tech demo: Real-time sprite scaling
by on (#137212)
Meh - mode 7. It takes the fun out of everything :P
Re: Tech demo: Real-time sprite scaling
by on (#137224)
Tepples I understand how you can use lookup tables for this but I see two major drawbacks :

1) This requires a lot of lookup tables, or, it can only shrink to very few scales
2) This does not work for vertical shrinking
3) There is no way you can expand sprites this way

Nevertheless it's still awesomely simple, I can't believe no game ever used this technique when it was so simple. This would also apply on SNES or any other 2d console without sprite scaling capabilities but bitplane based graphics.

This technique could/should also be used to do other effects to sprites, for example mosaic.
Re: Tech demo: Real-time sprite scaling
by on (#137240)
The SNES could easily make due with a slower routine, and just cache the frames to WRAM (128k is a nice chunk) for whatever level/area.
Re: Tech demo: Real-time sprite scaling
by on (#137244)
Bregalad wrote:
I see two major drawbacks :

1) This requires a lot of lookup tables, or, it can only shrink to very few scales

You really just need tables for 8 (identity), 7, 6, 5, and 4, after which you jump down to the next size and use the 8, 7, 6, 5, 4 table on that.

Quote:
2) This does not work for vertical shrinking

Of course it does. After each row I copy, I add between 0/256 and 255/256 rows to the skip variable, skipping a row when it overflows.

Quote:
3) There is no way you can expand sprites this way

Or on a Neo Geo. (And that's three ;-) )
Re: Tech demo: Real-time sprite scaling
by on (#137343)
tepples wrote:
You really just need tables for 8 (identity), 7, 6, 5, and 4, after which you jump down to the next size and use the 8, 7, 6, 5, 4 table on that.

Can't you drop the 4 as well? (since 4 in the current size would be the same as 8 in the next size)
Re: Tech demo: Real-time sprite scaling
by on (#137379)
Do you really need a table for identity? It's identity, nothing is changing.
Re: Tech demo: Real-time sprite scaling
by on (#137383)
Dwedit wrote:
Do you really need a table for identity? It's identity, nothing is changing.

That or a second loop for not using a table at all. Also yes.