Освободить текстуры (объекты GLKTextureInfo), выделенные GLKTextureLoader

новое в разработке на iOS и, в частности, новые функции OpenGL, связанные с iOS 5, поэтому я прошу прощения, если какие-либо из моих вопросов настолько просты.

приложение, над которым я работаю, предназначено для получения кадров камеры и отображения их на экране через OpenGL ES (графические люди возьмут на себя это и добавят фактический рисунок OpenGL, о котором я знаю очень мало). Приложение разработано XCode4, а целью является iPhone4 под управлением iOS 5. На данный момент я использовал 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 - это кварцевое изображение, построенное из кадра камеры (пример кода от apple). Я знаю, что проблема не в этой части кода, так как если я отключу назначение, приложение не получит предупреждение.

3 ответов


супер hacky решение я считаю, но, кажется, работает:

перед назначением добавьте следующее:

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

Если есть более официальный способ (или если это официальный способ), я был бы признателен, если бы кто-нибудь дал мне знать.


не прямой ответ, но что-то я заметил, и это не очень вписывается в комментарий.

если вы используете 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;
                                  }];

чтобы обойти эту проблему вы можете либо:

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

есть ли способ просто заменить содержимое текстуры на тот же GLKTextureInfo.имя ручка? При использовании glgentextures вы можете использовать возвращенный дескриптор текстуры для загрузки новых данных texuture с помощью glteximage2d. Но с GLKTextureLoader кажется, что glgentextures вызывается каждый раз при загрузке новых текстурных данных...