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

时间:2022-09-07 09:38:36

New to developing on iOS and in particular the new OpenGL related features on iOS 5, so I apologize if any of my questions are so basic.

在iOS 5上开发新功能,特别是iOS 5上新的OpenGL相关功能,所以如果我的任何问题都是如此基本,我会道歉。

The app I am working on is designed to receive camera frames and display them on screen via OpenGL ES (the graphic folks will take over this and add the actual OpenGL drawing about which I know very little). The application is developed XCode4, and the target is iPhone4 running iOS 5. For the moment, I used the ARC and the GLKit functionality and all is working fine except for the memory leak in loading the images as texture. The app receives a "memory warning" very soon.

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

Specifically, I would like to ask how to release the textures allocated by

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

@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);
    }
}

The image is a quartz image built from the camera frame (sample code from apple). I know the problem is not in that part of the code since if I disable the assignment, the app does not receive the warning.

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

3 个解决方案

#1


22  

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

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

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.

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

#2


5  

Not a direct answer, but something I noticed and it wont really fit in a comment.

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

If you're using GLKTextureLoader to load textures in the background to replace an existing texture, you have to delete the existing texture on the main thread. Deleting a texture in the completion handler will not work.

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

AFAIK this is because:

AFAIK这是因为:

  1. Every iOS thread requires its own EAGLContext, so the background queue has its own thread with its own context.
  2. 每个iOS线程都需要自己的EAGLContext,因此后台队列有自己的线程和自己的上下文。
  3. The completion handler is run on the queue you passed in, which is most likely not the main queue. (Else you wouldn't be doing the loading in the background...)
  4. 完成处理程序在您传入的队列上运行,该队列很可能不是主队列。 (否则你不会在后台进行加载...)

That is, this will leak memory.

也就是说,这会泄漏内存。

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

To get around this issue you can either:

要解决此问题,您可以:

  1. Delete the texture before the upload happens. Potentially sketchy depending on how your GL is architected.
  2. 在上传发生之前删除纹理。潜在的粗略取决于您的GL的架构方式。
  3. Delete the texture on the main queue in the completion handler.
  4. 删除完成处理程序中主队列上的纹理。

So, to fix the leak you need to do this:

因此,要修复泄漏,您需要这样做:

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

or

要么

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

#3


0  

Is there a way to simply replace the contents of the texture to the same GLKTextureInfo.name handle? When using glgentextures you can use the returned texture handle to load new texuture data using glteximage2d. But with GLKTextureLoader it seems that glgentextures is being called every time new texture data is loaded...

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

#1


22  

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

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

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.

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

#2


5  

Not a direct answer, but something I noticed and it wont really fit in a comment.

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

If you're using GLKTextureLoader to load textures in the background to replace an existing texture, you have to delete the existing texture on the main thread. Deleting a texture in the completion handler will not work.

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

AFAIK this is because:

AFAIK这是因为:

  1. Every iOS thread requires its own EAGLContext, so the background queue has its own thread with its own context.
  2. 每个iOS线程都需要自己的EAGLContext,因此后台队列有自己的线程和自己的上下文。
  3. The completion handler is run on the queue you passed in, which is most likely not the main queue. (Else you wouldn't be doing the loading in the background...)
  4. 完成处理程序在您传入的队列上运行,该队列很可能不是主队列。 (否则你不会在后台进行加载...)

That is, this will leak memory.

也就是说,这会泄漏内存。

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

To get around this issue you can either:

要解决此问题,您可以:

  1. Delete the texture before the upload happens. Potentially sketchy depending on how your GL is architected.
  2. 在上传发生之前删除纹理。潜在的粗略取决于您的GL的架构方式。
  3. Delete the texture on the main queue in the completion handler.
  4. 删除完成处理程序中主队列上的纹理。

So, to fix the leak you need to do this:

因此,要修复泄漏,您需要这样做:

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

or

要么

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

#3


0  

Is there a way to simply replace the contents of the texture to the same GLKTextureInfo.name handle? When using glgentextures you can use the returned texture handle to load new texuture data using glteximage2d. But with GLKTextureLoader it seems that glgentextures is being called every time new texture data is loaded...

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