Disclaimer: I'm nowhere near by DC dev setup at the moment, so I've not tested all of this, and the only code I have to go on is the PVR renderer from Genesis Plus / DC (which is a bit weird, since it handles twiddling itself, and builds polygon headers directly). So, although I'm pretty sure this is all correct, it may need a bit of tweaking to get it entirely correct.
Texture formats
The PVR supports textures in 16bpp (ARGB444, ARGB1555, RGB565) truecolour formats. That's pretty much the most common format used in Dreamcast homebrew. We've also got a 4bpp compressed format (you can generate textures in this format using vqenc, which comes with KOS), a bumpmap format (someone wrote a demo using this), and a YUV422 format (which is probably used for videos - I know of no homebrew that uses it). It also supports 8-bit and 4-bit textures using a shared colour palette.
If you look in pvr.h, you'll see a list of these in the PVR_TXRFMT_ flags. These are the values you pass as the textureformat parameter to the pvr_poly_cxt_txr function (or whatever the equivalent is in Parallax).
There are also two flags you can set in combination with the texture format. They are PVR_TXRFMT_TWIDDLED / PVR_TXRFMT_NONTWIDDLED, and PVR_TXRFMT_NOSTRIDE / PVR_TXRFMT_STRIDE. The defaults, if you don't set them, are PVR_TXRFMT_TWIDDLED and PVR_TXRFMT_NOSTRIDE. I have absolutely no idea what the stride flag does. The twiddled flag means that the pixels have been rearranged into a format that's optimized for bilinear / trilinear filtering. Exactly how this works is unimportant, but it's mostly used in 3D games to speed up rendering when using bilinear / trilinear filtering and mipmaps.
Just below that are two macros - PVR_TXRFMT_8BPP_PAL and PVR_TXRFMT_4BPP_PAL. These are used to specify which colour palette to use for a texture (more on that later). As the notes above the macros state, the bits in the texture format used to specify which colour palette to use happen to overlap the twiddled / stride flags. This means that all 4bpp and 8bpp paletted textures must be twiddled.
Colour palettes
The PVR has an internal bank of colour palette entries. This bank contains a total of 1024 colour palette entires, each of which is 32 bits wide.
The colour palette can be configured in three modes which match the format of the standard 16-bit textures: ARGB1555, ARGB4444, RGB565. There's also a 32-bit mode, which allows you to use ARGB8888 colour palette entries.
The palette mode is a global setting - it affects all rendering of paletted textures, so it's a good idea to set this to something that fills all your paletted texture needs.
ARGB8888 is the more versatile format. The Dreamcast renders internally at 32bpp anyway, converting down to 16bpp RGB565 (with dithering). The ARGB8888 format allows higher colour precision than any of the 16bpp formats, and much higher alpha precision. Unless you have a very good reason to use another format, I'd recommend using this one.
To set the palette format, you use the pvr_set_pal_format function, with one of the PVR_PAL_ values. For example, to set up an ARGB8888 palette:
Code: Select all
pvr_set_pal_format(PVR_PAL_ARGB8888);
Code: Select all
#define PACK_ARGB8888(a,r,g,b) ( ((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF) )
pvr_set_pal_entry(0, PACK_ARGB8888(0, 0, 0, 0));
pvr_set_pal_entry(1, PACK_ARGB8888(255, 0, 0, 0));
pvr_set_pal_entry(2, PACK_ARGB8888(255, 255, 255, 255));
pvr_set_pal_entry(3, PACK_ARGB8888(255, 255, 0, 0));
We'll cover how to select which palette to use later. If you do not set a palette, you end up with palette 0.
Loading textures
You can load a paletted texture in the normal way, using pvr_txr_load_ex. You just need to make sure they're twiddled, which pvr_txr_load_ex will do for you.
Actually loading texture data is a bit trickier than it could be - the utility functions in KOS to load textures automatically convert everything to 16bpp RGB565, which is obviously not what we want.
I might post some sample code for reading a paletted image later. Most likely, either a PNG, PCX, or BMP file. But for now, just assume that you've managed to load an 8bpp image from somewhere...
Code: Select all
void *image_data;
pvr_ptr_t texture_pointer;
int width, height;
// To load an 8bpp image...
pvr_txr_load_ex(image_data, texture_pointer, width, height, PVR_TXRLOAD_8BPP);
// To load a 4bpp image...
pvr_txr_load_ex(image_data, texture_pointer, width, height, PVR_TXRLOAD_4BPP);
Now that we've come this far, we get to the easy part - using the textures.
To use a paletted texture, you just need to specify the texture format when building the polygon header. For example, for an 8-bit texture with no filtering, using the default palette, and being rendered into the punch-through display list:
Code: Select all
pvr_poly_hdr_t hdr;
pvr_poly_cxt_t cxt;
pvr_poly_cxt_txr(&cxt, PVR_LIST_PT_POLY, PVR_TXRFMT_PAL8BPP, width, height, texture_pointer, PVR_FILTER_NONE);
pvr_poly_compile(&hdr, &cxt);
If you want to use something other than the default palette, you need to use the PVR_TXRFMT_8BPP_PAL and PVR_TXRFMT_4BPP_PAL macros:
Code: Select all
pvr_poly_cxt_txr(&cxt, PVR_LIST_PT_POLY, PVR_TXRFMT_PAL8BPP | PVR_TXRFMT_8BPP_PAL(1), width, height, texture_pointer, PVR_FILTER_NONE);
Why bother?
The main reason is memory usage. An 8bpp image takes up half as much video RAM as a 16bpp image, and a 4bpp image takes up half as much again. This is useful for 2D games, particularly if the source images use colour palettes anyway.
You also get the ability to do palette animation, and use the same image multiple times with different colour palettes. These are tricks that a lot of old console games used to use to get animation from static images, and to provide a bit of variety to the graphics without needing to include more graphics data.
You can also use paletted images for fonts. The ROM fonts are natively 4bpp images. Typically, we would convert them to a 16bpp ARGB4444 image, with the colour of every pixel set to white. That's a huge waste of memory, when we could just load the font as a 4bpp texture. In fact, anywhere you might use an ARGB4444 texture with all the colours set to white can be replaced with a 4bpp texture.
It's also useful for simulating luminance-only and alpha-only textures. You can simulate a luminance-only (greyscale) texture by using an 8bpp image, and setting colour palette entry i to ARGB(255, i, i, i). Similarly, you can simulate an alpha-only texture by setting colour palette entry i to ARGB(i, 255, 255, 255). By itself, this isn't necessarily useful, since the PVR's framebuffer is only 5 or 6 bits per colour channel. However, if you combine this with blending, you can eliminate a lot of the rendering artefacts you'd get by using an ARGB4444 texture. For example, you might have a particle texture which is coloured by setting the vertex colours, and blended onto the scene, or a lightmap. Since the PVR renders everything internally in 32bpp, and only converts to RGB565 as the final step, rendering quality would be improved.
Not that anyone's likely to be using lightmaps on a DC homebrew game. It might be useful if you wanted to run something like Quake though. Think of the difference in lighting between software Quake and GLQuake. Software mode uses 16-level lightmaps, which creates a banding effect on the lighting. GLQuake uses 256-level lightmaps, and the lighting looks far better, even if you're rendering in 16-bit.
Y'know... It might be a good idea to write some kind of PVR reference guide somewhere. Maybe on the Wiki?
Edit: Corrected parameter order in pvr_txr_load_ex. Thanks BB Hood.