Освободить текстуры (объекты GLKTextureInfo), выделенные GLKTextureLoader - PullRequest
11 голосов
/ 04 января 2012

Впервые в разработке на 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 ]

22 голосов
/ 04 января 2012

Я считаю, что это супер хакерское решение, но, похоже, оно работает:

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

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

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

5 голосов
/ 16 мая 2013

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

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

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

  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 голосов
/ 26 июля 2016

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

...