0

我在使用带有 OpenGL 组件的应用程序在 iPad 上崩溃时遇到问题。该应用程序会引发内存警告并崩溃,但它似乎并没有使用那么多内存。我错过了什么吗?

该应用程序基于 Vuforia 增强现实系统(大量借鉴 ImageTargets 示例)。我需要在我的应用程序中包含大约 40 个不同的模型,因此为了节省内存,我会根据需要在应用程序中动态加载对象(和渲染纹理等)。我试图复制 UIScrollView 延迟加载的想法。三个 4mb 的分配是我已经加载到内存中的纹理,当用户选择不同的模型来显示时。

这里有什么奇怪的吗?

仪器 - 泄漏

我对 OpenGL 了解不多(我选择 Vurforia 引擎的部分原因)。下面这个屏幕截图中的任何内容都应该与我有关吗?请注意,Vurforia 的 ImageTagets 示例应用程序也有未初始化的纹理数据(每帧大约一个),所以我认为这不是问题所在。

仪器 - OpenGL ES 分析器

任何帮助,将不胜感激!!

这是生成 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;
}
4

2 回答 2

2

奇怪的是,您没有看到应用程序的真实内存使用情况。正如我在这个答案中解释的那样,Allocations 工具对 OpenGL ES 隐藏了内存使用情况,因此您不能使用它来测量应用程序的大小。相反,请使用 Memory Monitor 工具,我打赌它会显示您的应用程序使用的 RAM 比您想象的要多得多。这是人们在尝试使用 Instruments 在 iOS 上优化 OpenGL ES 时遇到的常见问题。

如果您担心哪些对象或资源可能会在内存中累积,您可以使用 Allocations 工具的堆快照功能来识别在应用程序中执行重复任务时已分配但从未删除的特定资源。这就是我追踪未正确删除的纹理和其他项目的方式。

于 2012-06-08T14:49:37.187 回答
1

看到一些代码会有所帮助,但我可以做一些猜测:

我需要在我的应用程序中包含大约 40 个不同的模型,因此为了节省内存,我会根据需要在应用程序中动态加载对象(和渲染纹理等)。我试图复制 UIScrollView 延迟加载的想法。三个 4mb 的分配是我已经加载到内存中的纹理,当用户选择不同的模型来显示时。(...)

这种做法并不理想;如果内存没有正确释放,这很可能是您的问题的原因。最终,如果您不采取适当的预防措施,您将耗尽内存,然后您的进程就会终止。使用的引擎很可能有一些内存泄漏,由您的访问方案暴露。

今天的操作系统不区分 RAM 和存储。对他们来说,这只是内存,所有地址空间都由块存储系统支持(如果实际上连接了一些存储设备并不重要)。

所以这就是你应该做的:而不是将你的模型入内存,你应该对它们进行内存映射(mmap)。这告诉操作系统“这部分存储应该在地址空间中可见”并且操作系统内核将在它们到期时执行所有必要的传输。

请注意,Vurforia 的 ImageTagets 示例应用程序也有未初始化的纹理数据(每帧大约一个),所以我认为这不是问题所在。

这是一个强有力的指标,表明 OpenGL 纹理对象没有被正确删除。

任何帮助,将不胜感激!!

我的建议:停止像 1970 年代那样编程。今天的计算机和操作系统的工作方式不同。另见http://www.varnish-cache.org/trac/wiki/ArchitectNotes

于 2012-06-08T07:44:09.423 回答