我一直在撞击这个人。情况是这样的:我在我自己的小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];