Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS Textures Taking 33% Extra Memory

I was testing my iOS game and noticed a discrepancy between how much memory it was taking up and how much it thought it should be taking up. Eventually I narrowed down the problem to textures taking 33% more memory than I thought they should be taking.

For instance, I would think a 256x256 uncompressed 32-bit texture should take 256*256*4 bytes = 256k. However, I would notice the app's memory grow by something like 340k when allocating a 256x256 texture. It was as if the device were allocating enough memory to store the texture and all of its mipmaps, but I'm not using mip maps or asking for the space in any way.

This extra memory stood out, because it would only happen on certain devices. I noticed the extra memory when testing the game on an iPod Touch 4. However, the problem didn't occur on an iPhone 3GS, iPod 3G, or iPad 1.

The os versions on the devices are:

iPod 3G - iOS 3.1.2 (7D11) iPhone 3GS - iOS 4.3.5 (8L1) iPod 4 - iOS 4.2.1 (8C148) iPad - iOS 4.3 (8F190)

EDIT

Here's a little more info. I measure the app's memory like this

int PlatformIOS::GetProcessMemUsage()
{
    task_basic_info info;
    mach_msg_type_number_t size = sizeof( info );
    kern_return_t kerr = task_info( mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &size );
    if ( kerr == KERN_SUCCESS ) 
        return info.resident_size;

    return 0;
}

This returns the same value that you will see in the Insturments program for Real Mem. It's actually very useful.

And here's how I allocate my textures:

bool GL3DTextureDataPiece::CreateTextureSurface(X3DInterfaceImpl *theInterface, int theWidth, int theHeight, PixelFormat theFormat, RefCount *thePalette, bool generateMipMap)
{
    glGenTextures(1,&mTexture);
    theInterface->SetCurTexture(this);

    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,mLinearFilter?GL_LINEAR:GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,mLinearFilter?GL_LINEAR:GL_NEAREST);

    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, theWidth, theHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);

    return true;
}

It's all very basic stuff. The only thing that led me to find the memory problems in the first place was the discrepancy between different devices. In fact, it was the discrepancy between total app memory and resource memory (I track images and sound memory myself) that made me investigate to find this bug.

like image 954
Brian Rothstein Avatar asked Feb 03 '23 07:02

Brian Rothstein


2 Answers

I found a workaround already, so I'm going to answer my own question. But I thought I'd post this because it seems to be an important piece of information to be aware of.

Other people have found this bug. For instance, see here:

http://www.cocos2d-iphone.org/forum/topic/29121

The good news is that there's a workaround for the bug. The workaround is to only use non-power-of-2 (npot) textures. NPOT textures can not be mip mapped so iOS doesn't try to allocate the extra mip map memory (at least that's the theory of why it works.)

Fortunately, this was easy to do in my engine, since it already divides images up into multiple textures if necessary just to fit into power-of-2 textures without using too much memory. So, I just tweaked my code to not divide up images to fit into power-of-2 textures, and further to force any image that happened to be a power of 2 in both dimensions to load into a texture that was 1 pixel bigger than necessary in width. So, for instance, I would put a 256x256 image into a 257x256 texture.

This eliminated the 33% extra memory growth.

Note that older devices such as the iPod 3G can't do npot textures so it's important to check whether its possible before doing this fix. To check for this you can query the GL_APPLE_texture_2D_limited_npot extension. Also, be careful not to exceed the maximum texture size in adding this extra pixel to force the texture to be npot.

like image 72
Brian Rothstein Avatar answered Feb 05 '23 14:02

Brian Rothstein


Make sure you're not automatically generating mipmaps: http://www.opengl.org/wiki/Texture#Automatic_mipmap_generation

The mipmap chain will take 33% extra space, as the mipmap chain approximates 1/3 extra - see the below series where t is the size of the original texture: = t + t/4 + t/16 + t/64 etc...

EDIT:

Note it would be also helpful to post the method you used to measure the app's memory, and what method you used to create the texture.

It could be that the method you used always creates an extra 33% buffer, or you may be just calling the method incorrectly.

like image 39
Justicle Avatar answered Feb 05 '23 15:02

Justicle