Simple Hires Blitter

Strictly for discussing ZSNES development and for submitting code. You can also join us on IRC at irc.libera.chat in #zsnes.
Please, no requests here.

Moderator: ZSNES Mods

Post Reply
blargg
Regular
Posts: 327
Joined: Thu Jun 30, 2005 1:54 pm
Location: USA
Contact:

Simple Hires Blitter

Post by blargg »

Byuu has mentioned that my NTSC filter handles the SNES 512-width graphics mode ("hires") well compared to the other ones in bsnes. There's nothing special my filter is doing; it just shifts the odd pixels 1/2 a unit to the right and mixes that with the even pixels. The following code does the same thing, and handles the dual image blending from Jurassic Park as well as normal hires graphics as in Secret of Mana and similar.

This shows how each pixel contributes to two output pixels, with the even pixels being at one offset, and the odd ones being shifted to the right by one output pixel:

Code: Select all

0022446688...
-113355779...
I've provided two implementations, one if your SNES pixels are already in the final order (as in, they'd look somewhat correct if you just drew them as-is), the other if they are still in separate buffers as I understand the feature to work internally. These will work if your SNES pixels are in the native 15-bit BGR format, the standard 15-bit RGB format, or the standard 16-bit RGB format. Both read and write 512 total pixels.

It can be optimized quite a bit. The biggest win would come from mixing pairs of pixels in a 32-bit integer and unrolling the loop a few times.

I just wanted to show that you don't need the NTSC filter to get decet hires.

Code: Select all

/* Use for 16-bit RGB pixels */
#define MASK 0x0821

/* Use for 15-bit BGR/RGB pixels */
#define MASK 0x0421

/* Use if you've already interleved your source SNES pixels */
void blit_hires_row( const unsigned short* in, unsigned short* out )
{
    unsigned prev = 0;
    int n = 512;
    while ( n-- )
    {
        unsigned cur = *in++;
        *out++ = (prev + cur - ((prev ^ cur) & MASK)) >> 1;
        prev = cur;
    }
}

/* Use if you have the even and odd pixels drawn into separate buffers */
void blit_hires_row( const unsigned short* even_in,
		const unsigned short* odd_in, unsigned short* out )
{
    unsigned odd = 0;
    int n = 256;
    while ( n-- )
    {
        unsigned even = *even_in++;
        *out++ = (odd + even - ((odd ^ even) & MASK)) >> 1;
        odd = *odd_in++;
        *out++ = (odd + even - ((odd ^ even) & MASK)) >> 1;
    }
}
EDIT: clarified variable names in second version.
Last edited by blargg on Sat Mar 04, 2006 6:17 pm, edited 1 time in total.
pagefault
ZSNES Developer
ZSNES Developer
Posts: 812
Joined: Tue Aug 17, 2004 5:24 am
Location: In your garden

Post by pagefault »

Interesting. Thanks for your code. I am going to try to see if I can get ZSNES rendering with it. I'll get back to you on it.
byuu

Post by byuu »

Byuu has mentioned that my NTSC filter handles the SNES 512-width graphics mode ("hires") well compared to the other ones in bsnes.
Yep, that's because I'm an idiot. I was downsampling to 256 colors, blending even and odd at 50% each into a single pixel. This was causing a lot more downsampling, especially on hires fonts.

So I changed it to leave the width at 512, and blended every pixel with the previous (full, unblended) pixel, and voila. It looks more than adequate. Infinitesimal degradation of hires effects, and really good looking blending for pseudo-hires effects.

One question though, what about your NTSC filter now? The hires effects still look fine with this filter /and/ your filter both applied, but shouldn't the NTSC filter no longer perform this mixing loop now? If that's the case, is there an easy way I can change this? I left this blending right in the main rendering loop so I don't need an additional loop through every scanline, so the resulting data is already in the PPU video output before filters ever get a chance to touch it.
blargg
Regular
Posts: 327
Joined: Thu Jun 30, 2005 1:54 pm
Location: USA
Contact:

Post by blargg »

One question though, what about your NTSC filter now? The hires effects still look fine with this filter /and/ your filter both applied, but shouldn't the NTSC filter no longer perform this mixing loop now?
The NTSC filter basically does the equivalent, with of course the artifacts. Feeding it the output of this hires filter would be equivalent to feeding the output of the hires filter back to itself, which would produce an even more blurred result. The lores NTSC blitter function only reads 256 pixels per row, so it wouldn't work on the output of this hires filter since it outputs 512 pixels. Think of what would happen to a near-vertical line and you'll see that the hires NTSC blitter is still necessary.
byuu

Post by byuu »

Ok, so by using the simple hires blur, and then calling your NTSC filter, the result is blurrier than it should be? I guess I could force your simple hires blur off when your NTSC filter is enabled.
blargg
Regular
Posts: 327
Joined: Thu Jun 30, 2005 1:54 pm
Location: USA
Contact:

Post by blargg »

If they look the same to you, use whatever is most convenient. It'd be good to run some tests on hardware to see how close either is to the SNES. One thing I need tested is the artifact colors on a white line near-vertical line. The NTSC filter hires code I gave to the ZSNES team is slightly different, and I'm not sure which way it should look. You should feed a hires image like this, where - is black and X is a white pixel (I guess you have to convert this to the even/odd interleved format):

Code: Select all

X----
-----
-----
-X---
-----
-----
--X--
-----
-----
---X-
-----
-----
----X
I want to know the general color of each pixel (it'll be a slight pastel). I need to pull out my SNES devcart and test it myself, but I'd have to review how to do SNES graphics since it's been a while.
Post Reply