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

发布于 2024-12-24 20:08:51 字数 692 浏览 0 评论 0原文

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

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.

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.

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

时光瘦了 2024-12-31 20:08:51

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

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

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

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

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.

浪菊怪哟 2024-12-31 20:08:51

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

如果您使用 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;
                                    });
                                  }];

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.

AFAIK this is because:

  1. Every iOS thread requires its own EAGLContext, so the background queue has its own thread with its own context.
  2. 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...)

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. Delete the texture on the main queue in the completion handler.

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;
                                    });
                                  }];
风和你 2024-12-31 20:08:51

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

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...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文