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

时间:2012-01-03 23:39:19

标签: iphone ios ios5 opengl-es-2.0 glkit

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

3 个答案:

答案 0 :(得分:22)

我相信超级hacky解决方案,但似乎有效:

在作业之前添加以下内容:

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

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

答案 1 :(得分:5)

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

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

AFAIK这是因为:

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

    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. 删除完成处理程序中主队列上的纹理。
    3. 因此,要解决泄漏问题,您需要这样做:

      //
      // 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;
                                          });
                                        }];
      

答案 2 :(得分:0)

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