当我在OpenGLES中加载纹理时,实际内存使用量增加了2倍

时间:2011-11-10 17:03:32

标签: iphone memory opengl-es textures

我一直在撞击这个人。情况是这样的:我在我自己的小OpenGL 2D游戏引擎上制作了一款iphone的二维游戏(我作为一个实验建立但实际上可以发货)。我正在努力让记忆得到控制。我正在使用纹理地图集,我熟悉PVRTC(但截至目前我还没有使用它)。问题是,如果我加载1024x1024 png纹理图集,我希望在扩展到内存时需要大约4兆(每像素1024 x 1024 x 4字节 - RBGA8888 = 4兆),实际内存使用量(根据仪器 - 内存监视器)增加8兆。啊哈!

我知道OpenGLES获取纹理数据,将其扩展到内存中,然后重新排序像素以在PowerVR芯片上工作,然后从中制作纹理(或类似的东西)。这个内存有可能没有被释放吗?所以我在内存中有两个每个纹理副本?从ObjectiveC方面来看,我看到一切都正确释放。但是我不知道OpenGL API背后的原因。我可能错过了一些东西。

我从O'Reilly的iPhone游戏开发书中加载纹理的实现。以下是我用于实施的关键点:

步骤1 - 获取正确的图像数据(2的幂):

- (id) initWithImage:(UIImage *)uiImage
{
    NSUInteger            width, height, i;
    CGContextRef          context = nil;
    void*                 data = nil;
    CGColorSpaceRef       colorSpace;
    void*                 tempData;
    unsigned int*         inPixel32;
    unsigned short*       outPixel16;
    BOOL                  hasAlpha;
    CGImageAlphaInfo      info;
    CGAffineTransform     transform;
    CGSize                imageSize;
    GLTexturePixelFormat  pixelFormat;
    CGImageRef            image;
    UIImageOrientation    orientation;
    BOOL                  sizeToFit = NO;

    image = [uiImage CGImage];
    orientation = [uiImage imageOrientation]; 

    if(image == NULL) {
        [self release];
        NSLog(@"Image is Null");
        return nil;
    }


    info = CGImageGetAlphaInfo(image);
    hasAlpha = ((info == kCGImageAlphaPremultipliedLast) || (info == kCGImageAlphaPremultipliedFirst) || (info == kCGImageAlphaLast) || (info == kCGImageAlphaFirst) ? YES : NO);
    if(CGImageGetColorSpace(image)) {
        if(hasAlpha)
            pixelFormat = kGLTexturePixelFormat_RGBA8888;
        else
            pixelFormat = kGLTexturePixelFormat_RGB565;
    } else { //NOTE: No colorspace means a mask image
        pixelFormat = kGLTexturePixelFormat_A8;
    }

    imageSize = CGSizeMake(CGImageGetWidth(image), CGImageGetHeight(image));
    transform = CGAffineTransformIdentity;

    width = imageSize.width;
    if((width != 1) && (width & (width - 1))) {
        i = 1;
        while((sizeToFit ? 2 * i : i) < width)
        i *= 2;
        width = i;
    }

    height = imageSize.height;
    if((height != 1) && (height & (height - 1))) {
        i = 1;
        while((sizeToFit ? 2 * i : i) < height)
        i *= 2;
        height = i;
    }

    while((width > kMaxTextureSize) || (height > kMaxTextureSize)) {
        width /= 2;
        height /= 2;
        transform = CGAffineTransformScale(transform, 0.5, 0.5);
        imageSize.width *= 0.5;
        imageSize.height *= 0.5;
    }

    switch(pixelFormat) {       
        case kGLTexturePixelFormat_RGBA8888:
            colorSpace = CGColorSpaceCreateDeviceRGB();
            data = malloc(height * width * 4);
            context = CGBitmapContextCreate(data, width, height, 8, 4 * width, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
            CGColorSpaceRelease(colorSpace);
            break;
        case kGLTexturePixelFormat_RGB565:
            colorSpace = CGColorSpaceCreateDeviceRGB();
            data = malloc(height * width * 4);
            context = CGBitmapContextCreate(data, width, height, 8, 4 * width, colorSpace, kCGImageAlphaNoneSkipLast | kCGBitmapByteOrder32Big);
            CGColorSpaceRelease(colorSpace);
            break;

        case kGLTexturePixelFormat_A8:
            data = malloc(height * width);
            context = CGBitmapContextCreate(data, width, height, 8, width, NULL, kCGImageAlphaOnly);
            break;              
        default:
            [NSException raise:NSInternalInconsistencyException format:@"Invalid pixel format"];
    }


    CGContextClearRect(context, CGRectMake(0, 0, width, height));
    CGContextTranslateCTM(context, 0, height - imageSize.height);

    if(!CGAffineTransformIsIdentity(transform))
        CGContextConcatCTM(context, transform);
    CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image)), image);

    //Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRGGGGGGBBBBB"
    if(pixelFormat == kGLTexturePixelFormat_RGB565) {
        tempData = malloc(height * width * 2);
        inPixel32 = (unsigned int*)data;
        outPixel16 = (unsigned short*)tempData;
        for(i = 0; i < width * height; ++i, ++inPixel32)
            *outPixel16++ = ((((*inPixel32 >> 0) & 0xFF) >> 3) << 11) | ((((*inPixel32 >> 8) & 0xFF) >> 2) << 5) | ((((*inPixel32 >> 16) & 0xFF) >> 3) << 0);
        free(data);
        data = tempData;

    }

    self = [self initWithData:data pixelFormat:pixelFormat pixelsWide:width pixelsHigh:height contentSize:imageSize];

    CGContextRelease(context);
    free(data); 

    return self;
}

第2步 - 绑定并加载纹理:

- (id) initWithData:(const void*)data pixelFormat:(GLTexturePixelFormat)pixelFormat pixelsWide:(NSUInteger)width pixelsHigh:(NSUInteger)height contentSize:(CGSize)size
{
    GLint saveName;
    if((self = [super init])) {
        glGenTextures(1, &_name); //get a new texture id.  _name increases as more textures are loaded
        glGetIntegerv(GL_TEXTURE_BINDING_2D, &saveName); //generally, saveName==1.  gets existing bound texture, so we can restore it after load.
        glBindTexture(GL_TEXTURE_2D, _name); //start working with our new texture id
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);  //added by ijames

       //associate pixel data with the texture id.
        switch(pixelFormat) {
            case kGLTexturePixelFormat_RGBA8888:
                glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
                break;
            case kGLTexturePixelFormat_RGB565:
                glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data);
                break;
            case kGLTexturePixelFormat_A8:
                glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, data);
                break;
            default:
                [NSException raise:NSInternalInconsistencyException format:@""];

        }

        glBindTexture(GL_TEXTURE_2D, saveName); //restore the previous texture binding.

        //NSLog(@"name %d, savename %d", _name, saveName);

        _size = size;
        _width = width;
        _height = height;
        _format = pixelFormat;
        _maxS = size.width / (float)width;
        _maxT = size.height / (float)height;
    }

    return self;
}
你知道有什么可怕的错吗?有没有人遇到过这个问题?这个幻象记忆究竟来自哪里?

感谢您的时间和想法!

编辑1:

我刚刚在调用initWithImage:之前向initWithData:添加了一些行,以便在加载时动态地将任何RGBA8888纹理转换为RGBA4444,只是为了看看会发生什么以及图形有多糟糕将会。结果是实际内存使用量减少了近2倍。这意味着无论神秘加倍发生在哪里,都会发生在initWithData:步骤中或之后。再次感谢您的想法!

编辑2:

回答其中一条评论 - 这里是如何调用initWithImage:(这是它发生的唯一地方 - 来自管理纹理缓存的ResourceManager类):

//NOTE: 'texture' and '_textures' are declared earlier...

//if the texture doesn't already exist, create it and add it to the cache
NSString * fullPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent: fileName];

UIImage * textureImg = [[UIImage alloc] initWithContentsOfFile: fullPath];
texture = [[GLTexture alloc] initWithImage: textureImg];  //here's the call
[textureImg release];

[_textures setValue: texture forKey: fileName];
return [texture autorelease];

0 个答案:

没有答案