12

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は、カメラフレームから作成されたクォーツ画像です(アップルのサンプルコード)。割り当てを無効にするとアプリが警告を受け取らないため、コードのその部分に問題がないことはわかっています。

4

3 に答える 3

23

私は信じている超ハッキーな解決策ですが、それはうまくいくようです:

割り当ての前に以下を追加します。

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

もっと公式な方法がある場合(またはこれが公式な方法である場合)、誰かが私に知らせてくれれば幸いです。

于 2012-01-03T23:48:09.170 に答える
6

直接的な答えではありませんが、私が気付いたことがあり、コメントにはあまり当てはまりません。

バックグラウンドでテクスチャをロードして既存のテクスチャを置き換えるために使用している場合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;
                                    });
                                  }];
于 2013-05-16T04:55:20.853 に答える
0

テクスチャのコンテンツを同じGLKTextureInfo.nameハンドルに単純に置き換える方法はありますか?glgentexturesを使用する場合、返されたテクスチャハンドルを使用して、glteximage2dを使用して新しいテクスチャデータをロードできます。しかし、GLKTextureLoaderを使用すると、新しいテクスチャデータがロードされるたびにglgentexturesが呼び出されるようです...

于 2016-07-26T14:00:05.633 に答える