Wednesday, May 2, 2012

60 FPS Optim: 16bit Textures and Mipmapping

My game now runs at 60FPS on iPod! This is mainly thanks to 16bit texturing and mipmapping.

Previously, the game was running pretty slowly on the iPod touch 2G: 24 FPS while rendering 1028 triangles, with 7 different materials groups. My geometry is still far from optimized (my meshes are too high-res for the iPod), so only textures are optimized for now.

16 bit textures are a must on iPod: Not only they use half the amount of video memory and are handled really fast by the GPU, the quality loss is barely noticeable. However, I had to convert my 32 bit PNG data into 16 bit. Here is how to do it. Please note that this is done offline so I didn't bother to optimize it. If it was done in real-time, the divisions by 255 would have been the first thing to go.

u16* pData = snew u16[width*height];
switch (info_ptr->color_type)
{
    case PNG_COLOR_TYPE_RGBA: // convert RGBA8888 to RGBA4444
    {
        channels = 4;
        for (s32 i=0; i<height; ++i)
        {
            for (s32 j=0; j<row_bytes; j += channels)
            {
                 u8 r = pngData[i*row_bytes + j + 0];
                 u8 g = pngData[i*row_bytes + j + 1];
                 u8 b = pngData[i*row_bytes + j + 2];
                 u8 a = pngData[i*row_bytes + j + 3];
                 r = u8((f32(r)*0xf)/0xff)&0xf;
                 g = u8((f32(g)*0xf)/0xff)&0xf;
                 b = u8((f32(b)*0xf)/0xff)&0xf;
                 a = u8((f32(a)*0xf)/0xff)&0xf;
                 s32 column = j/channels;
                 s32 index = i*width + column;
                 pData[index] = (r<<12) | (g<<8) | (b<<4) | a;
             }
        }
     }            
     break;

     case PNG_COLOR_TYPE_RGB: // convert RGB888 to RGB565
     {
          channels = 3;
          for (s32 i=0; i<height; ++i)
          {
               for (s32 j=0; j<row_bytes; j += channels)
               {
                    u8 r = pngData[i*row_bytes + j + 0];
                    u8 g = pngData[i*row_bytes + j + 1];
                    u8 b = pngData[i*row_bytes + j + 2];
                    r = u8((f32(r)*0x1f)/0xff)&0x1f;
                    g = u8((f32(g)*0x3f)/0xff)&0x3f;
                    b = u8((f32(b)*0x1f)/0xff)&0x1f;
                    s32 column = j/channels;
                    s32 index = i*width + column;
                    pData[index] = (r<<11) | (g<<5) | b;
                }
           }
      }
      break;

      default: SHOOT_ASSERT(false, "Unsupported PNG format");
}


Finally, here is how to pass the 16bit texture data to OpenGL, with mipmaps enabled:


glEnable(GL_TEXTURE_2D);
glGenTextures(1, &m_GLTextureID);
glBindTexture(GL_TEXTURE_2D, m_GLTextureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); 
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
            
switch(m_eFormat)
{
    case TF_RGB: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, s32(m_vSize.X), s32(m_vSize.Y), 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, m_pData);
                break;

    case TF_RGBA: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, s32(m_vSize.X), s32(m_vSize.Y), 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, m_pData);
                break;            
}


On the windows port, 16bit textures become noticeable, so I will probably make them exclusive to iPod. Here is how 16bit textures with mipmapping look in the editor (click to enlarge):



No comments:

Post a Comment