PS2 Linux Programming

 

Palletised Textures

 

 

Introduction

 

The loading of 256x256 pixel, 24 bit colour bitmap files as texture was introduced in the tutorial “2D Texture Mapping” In this tutorial the loading of both multiple texture sizes and 8 bit palletised textures will be presented.

 

 

Advanced Texturing

 

In order to simplify the management of textures, the texture loading and uploading functions have been rewritten in the form of a C++ class, with member variables that hold information about the texture. The texture class is contained within the files ctexture.cpp and ctexture.h.

 

The image dimensions and colour depth can be read from the bitmap info header and use in the loading process. The only tricky parameter is in the setting of TEX0, which requires the image size to be given as a power of 2 -this is where the function TextureLog2() is used. This function returns the size that is passed in as a power of 2. This function also has a hidden use, in that it checks to see that the bitmap is actually a supported size. The supported sizes are: 32, 64, 128, and 256. The bitmap does not have to be square, for example a 256 by 32 texture is fine. Note that the texture buffer width has been left at 256. The texture buffer width effectively sets the maximum width of a texture that can be loaded. Smaller images can be loaded into a larger texture buffer without any problem. The texture buffer width (TBW) should not be set too high though, as the wider the buffer, the shorter it becomes (given the same amount of memory) meaning that the maximum height of textures that can be load is reduced.

 

A palletised texture is different to a regular “true-colour” texture in that instead of storing pixels as actual RGBA values, they are stored in a table of RGBA colours and each pixel is used as an index into that table. This table is called a colour look up table (CLUT). 8 bit palletised textures will be used in this tutorial. An 8 bit texture consists of a 256-entry CLUT, and the image data which is the array of 8 bit indices. On the Playstation 2 the 8 bit CLUT can be considered as being a 16x16 32-bit texture.

 

Looking at the new LoadBitmap method in ctexture.cpp it can be seen that the code is basically the same as before, except there is now code to load the CLUT if the texture has a colour depth of 8 bits. (Note that there is also an additional parameter passed into the function [bool bRedAsAlpha], but just set this to false and ignore it for the purposes of this tutorial – it will be explained in the next tutorial when font rendering is investigated.) If a bitmap file has a CLUT, it will be stored just after the two headers and immediately before the image data. The CLUT is read as an array of 256 32-bit integers. As always with bitmaps, the data is stored in BGR format, so the R and B channels are swapped around to give the data in the correct RGB format. Again, just like 32 bit mode if the colour is RGB(0,0,0) and the parameter, bBlackTransparentand, passed into the method is true, the alpha channel is set to 0. Looking at the code is can be seen that the pixels in the palette must be “swizzled”. The reason for this is that the CLUT is not laid out linearly in GS Memory. The diagram on page 32 of GS users manual gives information on the layoutof the CLUT and it can be seen that pixels 0x08 to 0x0F must be swapped with pixels 0x10 to 0x17. This pattern repeats every 32 (0x20) pixels.

 

A operation must therefore be determined to  convert an ordinary index into a CLUT memory index.

 

It is known that 0x08 must map to 0x10:

 

0x08 = 000|01|000   ->   0x10 = 000|10|000

 

A few other examples:

 

0x09 = 000|01|001   ->   0x11 = 000|10|001

0x28 = 001|01|000   ->   0x30 = 001|10|000

0xF3 = 111|10|011   ->   0xEB = 111|01|011

 

As can be seen, by swapping bits 3 and 4 of the index the appropriate “swizzled” index is obtained. The code use to do this is as follows:

 

(p & 0xE7) + ((p & 0x08) << 1) + ((p & 0x10) >> 1)

 

Doing a bitwise AND on the index with 0xE7 gives p without bits 3 and 4. P & 0x08 gives the value of bit 3, shifting it left 1 place puts it into bit 4. P & 0x10 gives bit 4 which is shifting it right into bit 3.

 

Before the bitmap file is copied into SPS2 memory the colour depth of the image is checked. If the colour depth is not 8 bits then it must be 24 bits. The code for reading in the 24 bit pixels has been presented before. Reading in the 8 bit pixels is simply a matter of reading in the byte from the file and putting it directly into SPS2 memory – remember each byte is an index into the CLUT.

 

The final new addition to the LoadBitmap() method is right at the end of the function where a check is made to determine if the image has a CLUT. If it does, then the CLUT is copied into SPS2 memory. The CLUT is added to the next free 4K page of SPS2 memory that the user passed in to the class. Therefore when useing this function to load an 8 bit texture, always remember to have enough memory free for the CLUT. To be safe, always add 8 kilobytes to the memory needed for the image.

 

Now for the new upload function: This function hasn’t changed much, apart from the addition of the code to upload the CLUT. Almost all of the new code is enclosed in the section “if(m_bHasCLUT)” and this section uploads the CLUT into video memory. As was mentioned earlier a CLUT can be thought of as a 16x16 32-bit image, therefore uploading it is exactly the same as uploading a regular image. Checking the code it is seen that the method used is exactly the same as for uploading a 32 bit image except for now the width and height are set to 16. The code puts the CLUT into GS Page 511, which is the very last page of GS memory. Since at this time the maximum texture size supported is 256x256 pixels, with an 8 bit texture this page is guaranteed to be free. This is not the most efficient use of GSmem but it get the job done for the present – a tool to organise the textures in GSmem would be nice!

 

When the image is upload to GSmem, the DPSM parameter fo the BITBLTBUF register (destination pixel storage format) is set to PSMT8 when an 8-bit palletised texture is loaded or PSMCT32 otherwise. The final thing to change is TEX0. This register also requires the pixel storage format to be set in its PSM field. The CBM field of TEX0 must point to the CLUT’s position in GS memory, and finally the CLD field should be set to 1. The only other thing to mention is that when working out the QWC fields they are multiplied by 4 if when using 32 bit mode for the reason that 32 bit pixels take up 4 times more memory than 8 bit pixels.

 

 

CTexture overview

 

This section deals with how to use the CTexture class.

 

The first thing to be done is to set aside enough SPS2 memory for the texture. An easy way to do this is just to use 256K for every texture as this is guaranteed to be enough. This is fairly wasteful however, so it might be better to work out exactly how much memory is need. For a 24 bit bitmap this is simply WIDTH * HEIGHT * 4. For an 8 bit bitmap the image data will be WIDTH * HEIGHT, but then additional free page is needed for the CLUT. It is best to add on 8 kilobytes to the amount needed for the image to give the total amount needed.

 

A CTexture object should be created for each texture to be loaded and a pointer should be passed to the SPS2 memory that has been set aside for the texture, into the constructor.

 

The LoadBitmap() method is called to load the image from disk into SPS2 memory. This method should only be called once during the initialisation phase. Before using the texture for drawing purposes, the CTexture’s Upload method is called to upload the texture into GSmem. Remember to flush the SPS2 cache before uploading a texture that has just been loaded.

 

CTexture is fairly wasteful in that it only allows one texture in VMEM at a time. One way to increase the speed of an application would be allow 2 or more textures to be in VMEM at the same time. This way drawing could be performed using one texture, while another texture was being uploading.

 

 

 

Conclusion

 

In this tutorial the use of texture of different colour depth and size has been introduced. The methods presented illustrate the techniques involved, but there are many improvements that can be made, (some of which are outlined in the text) in order to optimise the process.

 

 

Dr Henry S Fortuna

University of Abertay Dundee

h.s.fortuna@abertay.ac.uk