12

在 iOS 上开发的新手,特别是 iOS 5 上与 OpenGL 相关的新功能,所以如果我的任何问题都非常基本,我深表歉意。

我正在开发的应用程序旨在接收相机帧并通过 OpenGL ES 在屏幕上显示它们(图形人员将接管它并添加我知之甚少的实际 OpenGL 绘图)。该应用程序是 XCode4 开发的,目标是运行 iOS 5 的 iPhone4。目前,我使用了 ARC 和 GLKit 功能,除了将图像加载为纹理时出现内存泄漏外,一切正常。该应用程序很快就会收到“内存警告”。

具体来说,我想问一下如何释放分配的纹理

@property(retain) GLKTextureInfo *texture;

-(void)setTextureCGImage:(CGImageRef)image 
{
    NSError *error;

    self.texture = [GLKTextureLoader textureWithCGImage:image options:nil error:&error];

    if (error) 
    {
        NSLog(@"Error loading texture from image: %@",error);
    }
}

image是从相机框架构建的石英图像(来自苹果的示例代码)。我知道问题不在代码的那部分,因为如果我禁用分配,应用程序不会收到警告。

4

3 回答 3

23

Super hacky solution I believe, but it seems to work:

Add the following before the assignment:

GLuint name = self.texture.name;
glDeleteTextures(1, &name);

If there's a more official way (or if this is the official way), I would appreciate if someone could let me know.

于 2012-01-03T23:48:09.170 回答
6

不是直接的答案,而是我注意到的一些东西,它不会真正适合评论。

如果您使用GLKTextureLoader在后台加载纹理来替换现有纹理,则必须删除主线程上的现有纹理。在完成处理程序中删除纹理将不起作用。

AFAIK 这是因为:

  1. 每个 iOS 线程都需要自己的 EAGLContext,因此后台队列有自己的线程和自己的上下文。
  2. 完成处理程序在您传入的队列上运行,该队列很可能不是主队列。(否则你不会在后台加载......)

也就是说,这会泄漏内存

NSDictionary *options = @{GLKTextureLoaderOriginBottomLeft:@YES};
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
[self.asyncTextureLoader textureWithContentsOfFile:@"my_texture_path.png"
                                           options:options
                                             queue:queue
                                 completionHandler:^(GLKTextureInfo *texture, NSError *e){
                                    GLuint name = self.myTexture.name;
                                    //
                                    // This delete textures call has no effect!!!
                                    //
                                    glDeleteTextures(1, &name);
                                    self.myTexture = texture;
                                  }];

要解决此问题,您可以:

  1. 在上传之前删除纹理。可能很粗略,具体取决于您的 GL 的架构方式。
  2. 在完成处理程序中删除主队列上的纹理。

因此,要修复泄漏,您需要执行以下操作:

//
// Method #1, delete before upload happens.
// Executed on the main thread so it works as expected.
// Potentially leaves some GL content untextured if you're still drawing it
// while the texture is being loaded in.
//

// Done on the main thread so it works as expected
GLuint name = self.myTexture.name;
glDeleteTextures(1, &name)

NSDictionary *options = @{GLKTextureLoaderOriginBottomLeft:@YES};
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
[self.asyncTextureLoader textureWithContentsOfFile:@"my_texture_path.png"
                                           options:options
                                             queue:queue
                                 completionHandler:^(GLKTextureInfo *texture, NSError *e){
                                    // no delete required, done previously.
                                    self.myTexture = texture;
                                  }];

或者

//
// Method #2, delete in completion handler but do it on the main thread.
//
NSDictionary *options = @{GLKTextureLoaderOriginBottomLeft:@YES};
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
[self.asyncTextureLoader textureWithContentsOfFile:@"my_texture_path.png"
                                           options:options
                                             queue:queue
                                 completionHandler:^(GLKTextureInfo *texture, NSError *e){
                                    // you could potentially do non-gl related work here, still in the background
                                    // ...

                                    // Force the actual texture delete and re-assignment to happen on the main thread.
                                    dispatch_sync(dispatch_get_main_queue(), ^{
                                      GLuint name = self.myTexture.name;
                                      glDeleteTextures(1, &name);
                                      self.myTexture = texture;
                                    });
                                  }];
于 2013-05-16T04:55:20.853 回答
0

有没有办法简单地将纹理的内容替换为相同的 GLKTextureInfo.name 句柄?使用 glgentextures 时,您可以使用返回的纹理句柄通过 glteximage2d 加载新的纹理数据。但是使用 GLKTextureLoader 似乎每次加载新纹理数据时都会调用 glgentextures ......

于 2016-07-26T14:00:05.633 回答