Androidアプリ開発 OpenGL queueEventを使う [Android OpenGL]
本日は、久しぶりにOpenGLネタである。といっても純粋にOpenGLのネタかというとそうでもなく、どちらかというとAndroidの方かな。
OpenGLで何か表示しようと思ったら、GLSurfaceViewを使う必要がある。GLSurfaceViewも、SurfaceViewもどちらも描画専用のスレッドを持っている。onDrawFrameはこの専用のスレッドで実行されている。
SurfaceViewでない単なるビューは。描画用のスレッドがない。アクティビティと同じUIスレッドで描画が行われる。マルチスレッドで動くことになると、スレッド間でのデータ通信が必要になってくる。
描画用のデータと、アクティビティで使うデータがきっちり分かれていれば、スレッド間でデータのやりとりを行う必要はない。しかし、同じアプリの中で動いているスレッドなので、普通に考えれば何かしらのデータ通信が必要。
データ通信といっても、単にメソッドを呼び出すだけの場合が多い。publicフィールドにして直接フィールドにアクセスしてしまう、という手もあるが「カプセル化」といった面からみてもこれは避けた方が無難。
アクティビティの方で、メニューのイベントやら、タップイベントやらをコールバックで処理していたとする。それを受けて「これは再描画が必要だな」っていうことで、viewのinvalidateを呼び出してはいけない。viewがSurfaceViewじゃなければ問題はない。viewがSurfaceViewだとinvalidateを呼び出した瞬間に例外となる。
じゃあ、どうすればよいかというと、postInvalidate()を呼び出せばOKみたい。しかし、GLSurfaceViewだと描画し続ける、っていうのが普通だと思う(「無効になって必要な時だけ」という描画方法にもできるみたいではあるが)。invalidate()はあまり行われない。
最初にやってエラーをくらうのは、メインスレッドの方で、OpenGLのちょっとした有効、無効を切り替えようと思った時。OpenGLのAPIを呼び出す場合、AndroidのOpenGL ESだとGL10のインスタンスが必要だが、これは適当にフィールドで覚えておいて、glRememberField.glEnable(GL10.GL_LIGHT)とかやってみたりすると、以下のようなエラーになってしまう。
よくありがちなのは(AREarthroidでもそうだが)、テクスチャ画像の読み込みは、メインのUIスレッドで行って、onDrawFrameはなるべく止めずに、っていう感じでスレッドの役割分担をしたとき。
onCreateSurfaceでテクスチャを読み込んでいる分には、OpenGLのスレッドでやっているので問題はない。読み込みをメインスレッドでやって、ビットマップをロードするために、texImage2Dを呼び出すとこのエラーが発生する。
参考記事
Androidアプリ開発 OpenGL テクスチャマッピング
では、どうすればよいかというと、queueEventを使う。
queueEventはGLSurfaceViewのメソッドで、OpenGLのスレッドに実行を依頼するもの。使い方が少々まどろっこしい。
どうするかというと、queueEventの引数に、Runnableインターフェースを実装したオブジェクトのインスタンスを作って渡してやる。クラスを作ってもよいが、名前なしでインスタンスを生成してしまうことが多い。
Runnableインターフェースには、メソッドvoid run()が必要なので、このメソッドを作ってやり、メソッド内でスレッドで実行させたいコードを書く。
以下、が実際の例。
SampleGLSurfaceViewのonLongPressで使ってみた。glRememberFieldは、onDrawFrameで引数のglをそのまま代入。
どこかのstaticフィールドにありそうではあるが、見つからなかったので。onDrawFrameで引数のglを代入して記憶している。使い方は間違っているかも知れない。ちょっと自信がない。
ライティングする、しないのスイッチだけなら、フィールド変数を作成して、onDrawFrameでそのフィールド変数を見て処理をわけた方が普通の作り方だと思う。
時間がかかりそうな処理を別スレッドでやらせて、処理が終わった時点で、OpenGL側にそれを伝えるわけだが。そのスレッド上でそのまま、OpenGLのAPI呼び出しをすると、call to OpenGL ES API with no current contextのエラーが発生する。なので、queueEventを使ってGLThreadで処理を行うようにすればOK。
もうひとつ。
onDrawFrameで複数のモデルを描画しているとする。ここにもうひとつ追加したいとする。リストになっているので、newしてaddすればOKなのだが、これをメインスレッドで行うと、リストが競合して、エラーになってしまう(場合がある)。
スレッドの処理タイミングによっては、競合が発生しないので、何事もなかったかのように動作する。マルチコアのCPUを持つ端末でやると発生しやすかったりする。
onDrawFrameでリストを参照して描画処理を行っている最中に、別のスレッドでリストへの追加が起きるとエラーになる。そこで、リストへの追加処理をqueueEventを使ってやるようにする。そうすれば、GLThreadで、リストへの追加が行われるので、うまくいくようになる。
サイト内を検索
OpenGLで何か表示しようと思ったら、GLSurfaceViewを使う必要がある。GLSurfaceViewも、SurfaceViewもどちらも描画専用のスレッドを持っている。onDrawFrameはこの専用のスレッドで実行されている。
SurfaceViewでない単なるビューは。描画用のスレッドがない。アクティビティと同じUIスレッドで描画が行われる。マルチスレッドで動くことになると、スレッド間でのデータ通信が必要になってくる。
描画用のデータと、アクティビティで使うデータがきっちり分かれていれば、スレッド間でデータのやりとりを行う必要はない。しかし、同じアプリの中で動いているスレッドなので、普通に考えれば何かしらのデータ通信が必要。
データ通信といっても、単にメソッドを呼び出すだけの場合が多い。publicフィールドにして直接フィールドにアクセスしてしまう、という手もあるが「カプセル化」といった面からみてもこれは避けた方が無難。
アクティビティの方で、メニューのイベントやら、タップイベントやらをコールバックで処理していたとする。それを受けて「これは再描画が必要だな」っていうことで、viewのinvalidateを呼び出してはいけない。viewがSurfaceViewじゃなければ問題はない。viewがSurfaceViewだとinvalidateを呼び出した瞬間に例外となる。
じゃあ、どうすればよいかというと、postInvalidate()を呼び出せばOKみたい。しかし、GLSurfaceViewだと描画し続ける、っていうのが普通だと思う(「無効になって必要な時だけ」という描画方法にもできるみたいではあるが)。invalidate()はあまり行われない。
最初にやってエラーをくらうのは、メインスレッドの方で、OpenGLのちょっとした有効、無効を切り替えようと思った時。OpenGLのAPIを呼び出す場合、AndroidのOpenGL ESだとGL10のインスタンスが必要だが、これは適当にフィールドで覚えておいて、glRememberField.glEnable(GL10.GL_LIGHT)とかやってみたりすると、以下のようなエラーになってしまう。
E/libEGL(21579): call to OpenGL ES API with no current context (logged once per thread)
よくありがちなのは(AREarthroidでもそうだが)、テクスチャ画像の読み込みは、メインのUIスレッドで行って、onDrawFrameはなるべく止めずに、っていう感じでスレッドの役割分担をしたとき。
onCreateSurfaceでテクスチャを読み込んでいる分には、OpenGLのスレッドでやっているので問題はない。読み込みをメインスレッドでやって、ビットマップをロードするために、texImage2Dを呼び出すとこのエラーが発生する。
参考記事
Androidアプリ開発 OpenGL テクスチャマッピング
では、どうすればよいかというと、queueEventを使う。
queueEventはGLSurfaceViewのメソッドで、OpenGLのスレッドに実行を依頼するもの。使い方が少々まどろっこしい。
どうするかというと、queueEventの引数に、Runnableインターフェースを実装したオブジェクトのインスタンスを作って渡してやる。クラスを作ってもよいが、名前なしでインスタンスを生成してしまうことが多い。
Runnableインターフェースには、メソッドvoid run()が必要なので、このメソッドを作ってやり、メソッド内でスレッドで実行させたいコードを書く。
以下、が実際の例。
@Override public void onLongPress(MotionEvent arg0) { queueEvent(new Runnable(){ public void run(){ glRememberField.glEnable(GL10.GL_LIGHT); } }); }
SampleGLSurfaceViewのonLongPressで使ってみた。glRememberFieldは、onDrawFrameで引数のglをそのまま代入。
どこかのstaticフィールドにありそうではあるが、見つからなかったので。onDrawFrameで引数のglを代入して記憶している。使い方は間違っているかも知れない。ちょっと自信がない。
ライティングする、しないのスイッチだけなら、フィールド変数を作成して、onDrawFrameでそのフィールド変数を見て処理をわけた方が普通の作り方だと思う。
時間がかかりそうな処理を別スレッドでやらせて、処理が終わった時点で、OpenGL側にそれを伝えるわけだが。そのスレッド上でそのまま、OpenGLのAPI呼び出しをすると、call to OpenGL ES API with no current contextのエラーが発生する。なので、queueEventを使ってGLThreadで処理を行うようにすればOK。
もうひとつ。
onDrawFrameで複数のモデルを描画しているとする。ここにもうひとつ追加したいとする。リストになっているので、newしてaddすればOKなのだが、これをメインスレッドで行うと、リストが競合して、エラーになってしまう(場合がある)。
スレッドの処理タイミングによっては、競合が発生しないので、何事もなかったかのように動作する。マルチコアのCPUを持つ端末でやると発生しやすかったりする。
onDrawFrameでリストを参照して描画処理を行っている最中に、別のスレッドでリストへの追加が起きるとエラーになる。そこで、リストへの追加処理をqueueEventを使ってやるようにする。そうすれば、GLThreadで、リストへの追加が行われるので、うまくいくようになる。
関連記事
Androidアプリ開発 OpenGL レンダーモードでonDrawFrameの呼び出しを制御
OpenGLの記事を最初から読みたいなら、以下からどうぞ。
Androidアプリ開発 OpenGLを使う GLSurfaceView
Androidアプリ開発 OpenGL レンダーモードでonDrawFrameの呼び出しを制御
OpenGLの記事を最初から読みたいなら、以下からどうぞ。
Androidアプリ開発 OpenGLを使う GLSurfaceView
サイト内を検索
2012-05-07 17:29
nice!(0)
Copyright Atsushi Asai Google+朝井淳
[データベースの気持ちがわかる]SQLはじめの一歩 (WEB+DB PRESS plus)
- 作者: 朝井 淳
- 出版社/メーカー: 技術評論社
- 発売日: 2015/03/03
- メディア: 単行本(ソフトカバー)
Access クエリ 徹底活用ガイド ~仕事の現場で即使える
- 作者: 朝井 淳
- 出版社/メーカー: 技術評論社
- 発売日: 2018/05/25
- メディア: 大型本