2012-01-03 83 views
11

对于在iOS上开发新特性,特别是iOS 5上新的OpenGL相关特性,我很抱歉如果我的任何问题都非常基本。由GLKTextureLoader分配的发布纹理(GLKTextureInfo对象)

我正在使用的应用程序旨在接收相机框架并通过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以释放分配的纹理是从照相机框架(由苹果样本代码)建立了一个石英图像。我知道问题不在代码的那部分,因为如果我禁用了这个任务,应用程序不会收到警告。

回答

22

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

分配之前添加以下内容:

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

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

+1

嗯。到目前为止没有回应,所以这是正式的,因为它现在对我来说是正式的。 :-)我的想法是'GLKTextureInfo'会管理这个(即释放它在解除分配时指向的纹理的句柄),但显然情况并非如此。 – alokoko 2012-01-14 20:20:54

+5

我相信这是做到这一点的正确方法。问题是,在将纹理绑定到GL之后创建textureInfo后,GL将拥有内存,因此您需要使用GL来删除内存 – 2012-02-21 19:36:15

+0

Anthoys的解释对我来说很有意义。 +1。 – Soup 2012-09-10 10:02:47

5

不是一个直接的答案,但我注意到,它不会真正适合评论。

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

据我所知,这是因为:

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

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