我在使用在iPad上崩溃的OpenGL组件的应用程序时遇到问题。该应用程序抛出内存警告并崩溃,但它似乎没有使用那么多内存。我错过了什么吗?
该应用程序基于Vuforia增强现实系统(大量借鉴ImageTargets示例)。我需要在我的应用程序中包含大约40种不同的模型,因此为了保护内存,我在应用程序中动态加载对象(以及渲染纹理等),因为我需要它们。我试图复制UIScrollView延迟加载的想法。三个4mb分配是当用户选择要显示的不同模型时,我已经加载到内存中的纹理。
这里有什么奇怪的事吗?
我对OpenGL一点都不了解(我选择Vurforia引擎的部分原因)。这个屏幕中的任何内容都在下面拍摄,应该关注我吗?请注意,Vurforia的ImageTagets示例应用程序还具有未初始化的纹理数据(每帧大约一个),因此我认为这不是问题。
任何帮助都将不胜感激!!
以下是生成3D对象的代码(在EAGLView中):
// Load the textures for use by OpenGL
-(void)loadATexture:(int)texNumber {
if (texNumber >= 0 && texNumber < [tempTextureList count]) {
currentlyChangingTextures = YES;
[textureList removeAllObjects];
[textureList addObject:[tempTextureList objectAtIndex:texNumber]];
Texture *tex = [[Texture alloc] init];
NSString *file = [textureList objectAtIndex:0];
[tex loadImage:file];
[textures replaceObjectAtIndex:texNumber withObject:tex];
[tex release];
// Remove all old textures outside of the one we're interested in and the two on either side of the picker.
for (int i = 0; i < [textures count]; ++i) {
if (i < targetIndex - 1 || i > targetIndex + 1) {
[textures replaceObjectAtIndex:i withObject:@""];
}
}
// Render - Generate the OpenGL texture objects
GLuint nID;
Texture *texture = [textures objectAtIndex:texNumber];
glGenTextures(1, &nID);
[texture setTextureID: nID];
glBindTexture(GL_TEXTURE_2D, nID);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, [texture width], [texture height], 0, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)[texture pngData]);
// Set up objects using the above textures.
Object3D *obj3D = [[Object3D alloc] init];
obj3D.numVertices = rugNumVerts;
obj3D.vertices = rugVerts;
obj3D.normals = rugNormals;
obj3D.texCoords = rugTexCoords;
obj3D.texture = [textures objectAtIndex:texNumber];
[objects3D replaceObjectAtIndex:texNumber withObject:obj3D];
[obj3D release];
// Remove all objects except the one currently visible and the ones on either side of the picker.
for (int i = 0; i < [tempTextureList count]; ++i) {
if (i < targetIndex - 1 || i > targetIndex + 1) {
Object3D *obj3D = [[Object3D alloc] init];
[objects3D replaceObjectAtIndex:i withObject:obj3D];
[obj3D release];
}
}
if (QCAR::GL_20 & qUtils.QCARFlags) {
[self initShaders];
}
currentlyChangingTextures = NO;
}
}
这是纹理对象中的代码。
- (id)init
{
self = [super init];
pngData = NULL;
return self;
}
- (BOOL)loadImage:(NSString*)filename
{
BOOL ret = NO;
// Build the full path of the image file
NSString* resourcePath = [[NSBundle mainBundle] resourcePath];
NSString* fullPath = [resourcePath stringByAppendingPathComponent:filename];
// Create a UIImage with the contents of the file
UIImage* uiImage = [UIImage imageWithContentsOfFile:fullPath];
if (uiImage) {
// Get the inner CGImage from the UIImage wrapper
CGImageRef cgImage = uiImage.CGImage;
// Get the image size
width = CGImageGetWidth(cgImage);
height = CGImageGetHeight(cgImage);
// Record the number of channels
channels = CGImageGetBitsPerPixel(cgImage)/CGImageGetBitsPerComponent(cgImage);
// Generate a CFData object from the CGImage object (a CFData object represents an area of memory)
CFDataRef imageData = CGDataProviderCopyData(CGImageGetDataProvider(cgImage));
// Copy the image data for use by Open GL
ret = [self copyImageDataForOpenGL: imageData];
CFRelease(imageData);
}
return ret;
}
- (void)dealloc
{
if (pngData) {
delete[] pngData;
}
[super dealloc];
}
@end
@implementation Texture (TexturePrivateMethods)
- (BOOL)copyImageDataForOpenGL:(CFDataRef)imageData
{
if (pngData) {
delete[] pngData;
}
pngData = new unsigned char[width * height * channels];
const int rowSize = width * channels;
const unsigned char* pixels = (unsigned char*)CFDataGetBytePtr(imageData);
// Copy the row data from bottom to top
for (int i = 0; i < height; ++i) {
memcpy(pngData + rowSize * i, pixels + rowSize * (height - 1 - i), width * channels);
}
return YES;
}
答案 0 :(得分:2)
可能的情况是,您没有看到应用程序的真实内存使用情况。正如我在this answer中解释的那样,Allocations工具隐藏了OpenGL ES的内存使用情况,因此您无法使用它来衡量应用程序的大小。相反,使用Memory Monitor仪器,我打赌它会显示你的应用程序使用的RAM比你想象的多得多。这是人们在尝试使用Instruments优化iOS上的OpenGL ES时遇到的常见问题。
如果您担心哪些对象或资源可能在内存中累积,您可以使用Allocations工具的堆镜头功能来识别在应用程序中执行重复任务时分配但从未删除的特定资源。这就是我如何跟踪未正确删除的纹理和其他项目。
答案 1 :(得分:1)
看到一些代码会有所帮助,但我可以提出一些看法:
我需要在我的应用程序中包含大约40种不同的模型,因此为了保护内存,我在应用程序中动态加载对象(以及渲染纹理等),因为我需要它们。我试图复制UIScrollView延迟加载的想法。三个4mb分配是当用户选择要显示的不同模型时我已加载到内存中的纹理。 (...)
这种做法并不理想;如果内存未正确释放,则很可能是您遇到问题的原因。最终你会耗尽内存,如果你没有采取适当的预防措施,你的过程就会死亡。所使用的引擎很可能会因访问方案而暴露出一些内存泄漏。
今天的操作系统无法区分RAM和存储。对他们来说,这只是内存,所有地址空间都由块存储系统支持(如果实际上附加了一些存储设备并不重要)。
所以这就是你应该做的事情:你应该将内存映射到它们(mmap),而不是读取 - 将你的模型放入内存中。这告诉操作系统“这部分存储应该在地址空间中可见”,操作系统内核将在到期时进行所有必要的传输。
请注意,Vurforia的ImageTagets示例应用程序还具有未初始化的纹理数据(每帧大约一个),因此我认为这不是问题所在。
这是一个强有力的指标,OpenGL纹理对象无法正确删除。
任何帮助都将不胜感激!!
我的建议:停止编程,就像20世纪70年代一样。今天的计算机和操作系统的工作方式不同。另请参阅http://www.varnish-cache.org/trac/wiki/ArchitectNotes