8

彼らは提案します:

GCD を使用する場合は、専用のシリアル キューを使用してコマンドを OpenGL ES にディスパッチします。これは、従来のミューテックス パターンを置き換えるために使用できます。

この推奨事項がわかりません。私が解決できないこの競合があります:

アプリのアプリ デリゲートが呼び出しを受け取ると、すぐに OpenGL 関数の呼び出しを停止-applicationWillResignActiveする必要があります。

-applicationWillResignActive返された後もアプリが OpenGL 関数を呼び出し続けると、アプリがクラッシュします。

シリアル バックグラウンド キューで OpenGL 関数を呼び出すという Apple の推奨事項に従うと、この一見解決できない問題に直面します。

1) 受信したら、それ以上の OpenGL 関数の呼び出しをすぐに-applicationWillResignActive停止する必要があります。

2) しかし、シリアル キューはバックグラウンドでコード ブロックを処理している最中であるため、コード ブロックがafter -applicationWillResignActiveリターンの実行を終了し、アプリがクラッシュすることがあります。

これは、同時「ブロック」を示す図です。メイン スレッドは完全な停止メッセージを受信し、OpenGL ES へのさらなる呼び出しを防止する必要があります。しかし残念ながら、これらはバックグラウンド キューで発生し、ブロックの処理中に停止することはできません。

|_____main thread: "STOP calling OpenGL ES!"_____|
 _____|_____drawing queue: "Draw!"_____|_____drawing queue: "Draw!"_____|

技術的には、バックグラウンド キューを即座に停止し、バックグラウンドでの OpenGL のさらなる呼び出しを回避する方法を見つけられませんでした。一度実行されたサブミットされたコード ブロックは実行され続けます。

私が見つけた唯一の解決策は、バックグラウンドで OpenGL ES 関数を呼び出さないことでした。代わりに、メイン スレッドで呼び出して、アプリが GPU へのアクセスを失った後に呼び出されないようにします。

では、バックグラウンドで OpenGL ES 関数を呼び出しても問題ない場合、アプリがアクティブにリグジットした後に関数が呼び出されないようにするにはどうすればよいでしょうか?

4

2 に答える 2

7

applicationWillResignActiveディスパッチグループまたは同様のメカニズムを使用して、キューがキューに入れられたすべての操作を完了するのを待ちます。

ドキュメントで例を見つけることができます:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();

// Add a task to the group
dispatch_group_async(group, queue, ^{
   // Some asynchronous work
});

// Do some other work while the tasks execute.

// When you cannot make any more forward progress,
// wait on the group to block the current thread.
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

// Release the group when it is no longer needed.
dispatch_release(group);
于 2013-11-02T12:08:02.270 に答える
4

ディスパッチ グループを使用するという Sven の提案を超えて、私は過去に、以下のレンダリング シリアル キューへの同期ディスパッチを使用することで、より単純なルートに進みました-applicationWillResignActive

// Tell whatever is generating rendering operations to pause

dispatch_sync(openGLESSerialQueue, ^{
    [EAGLContext setCurrentContext:context];
    glFinish();

    // Whatever other cleanup is required
});

ここでの同期ディスパッチは、シリアル キュー内のすべてのアクションが完了するまでブロックし、その後、そのブロック内でコードを実行します。新しいレンダリング ブロックをトリガーするタイマーまたはその他のソースがある場合は、キューに最後のブロックが 1 つ置かれる場合に備えて、まずそれを一時停止します。

追加の安全対策としてglFinish()、GPU ですべてのレンダリングが完了するまでどのブロックを使用するかを検討しました (PowerVR GPU はレンダリングをできる限り延期することを好みます)。非常に長時間レンダリングされたフレームの場合、すべての責任ある OpenGL ES 呼び出しが終了した後でもフレーム レンダリングがまだ進行しているために、クラッシュが発生することがあります。これはそれを防ぎます。

私はこれをいくつかの異なるアプリケーションで使用していますが、バックグラウンドに移行する際のレンダリングによるクラッシュはほぼ解消されています。他の人が私の GPUImage ライブラリ (レンダリングにシリアル バックグラウンド ディスパッチ キューも使用) で同様のものを使用していることを知っており、それはうまく機能しているようです。

バックグラウンド プロセスが完了するまでブロックするこのソリューションまたはその他のソリューションで注意しなければならないことの 1 つは、注意を怠るとデッドロックが発生する可能性があることです。バックグラウンド キューのブロック内でメイン キューに戻る同期ディスパッチがある場合は、それらを削除するか、非同期にする必要があります。それらがメイン キューで待機していて、メイン キューがバックグラウンド キューで待機している場合は、うまくフリーズします。通常、それを回避するのは非常に簡単です。

于 2013-11-04T20:08:42.857 に答える