Освободить текстуры (объекты 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 это потому, что:
- каждый поток iOS требует своего собственного EAGLContext, поэтому фоновая очередь имеет свой собственный поток со своим собственным контекстом.
- обработчик завершения выполняется в переданной очереди, которая, скорее всего, не является основной очередью. (Иначе вы не делали бы загрузку в фоновом режиме...)
то есть это будет утечка памяти.
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;
}];
чтобы обойти эту проблему вы можете либо:
- удалите текстуру перед загрузкой. Потенциально отрывочный в зависимости от того, как ваш GL архитектор.
- удалите текстуру в главной очереди обработчика завершения.
Итак, чтобы исправить утечку, вам нужно сделать это:
//
// 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 вызывается каждый раз при загрузке новых текстурных данных...