Выпускать текстуры (объекты 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
- это кварцевое изображение, построенное из кадра камеры (пример кода из яблока). Я знаю, что проблема не в той части кода, поскольку, если я отключу назначение, приложение не получит предупреждение.
Ответы
Ответ 1
Супер хакерское решение, я считаю, но, похоже, работает:
Добавьте следующее перед назначением:
GLuint name = self.texture.name;
glDeleteTextures(1, &name);
Если есть более официальный способ (или если это официальный путь), я был бы признателен, если бы кто-нибудь мог сообщить мне.
Ответ 2
Не прямой ответ, но что-то, что я заметил, и он не будет действительно вписываться в комментарий.
Если вы используете 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;
});
}];
Ответ 3
Есть ли способ просто заменить содержимое текстуры на тот же дескриптор GLKTextureInfo.name? При использовании glgentextures вы можете использовать возвращенный обработчик текстуры для загрузки новых данных texuture с помощью glteximage2d. Но с GLKTextureLoader кажется, что glgentextures вызывается каждый раз, когда загружаются новые данные текстур...