释放由GLKTextureLoader分配的纹理(GLKTextureInfo对象)

alo*_*oko 11 iphone ios opengl-es-2.0 ios5 glkit

在iOS 5上开发新功能,特别是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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

alo*_*oko 22

我相信超级hacky解决方案,但它似乎工作:

在分配之前添加以下内容:

GLuint name = self.texture.name;
glDeleteTextures(1, &name);
Run Code Online (Sandbox Code Playgroud)

如果有更正式的方式(或者如果这是官方的方式),如果有人能让我知道,我将不胜感激.

  • 我相信这是正确的做法.问题是在将纹理绑定到GL后创建textureInfo之后,GL然后拥有内存,因此您需要使用GL来删除内存 (5认同)
  • 不是hacky.从`GLKTextureLoader`类引用:"`GLKTextureLoader`和`GLKTextureInfo`类不会为你管理OpenGL纹理.一旦纹理返回到你的应用程序,你负责它.这意味着你的应用程序完成后使用OpenGL纹理,它必须通过调用`glDeleteTextures`函数显式释放它. (2认同)

Sou*_*oup 5

不是直接的答案,而是我注意到的东西,它不适合评论.

如果您正在使用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;
                                  }];
Run Code Online (Sandbox Code Playgroud)

要解决此问题,您可以:

  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;
                                  }];
Run Code Online (Sandbox Code Playgroud)

要么

//
// 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;
                                    });
                                  }];
Run Code Online (Sandbox Code Playgroud)