24

私たちのアプリでは、CAEAGLLayer の上に UIScrollView があります。UIScrollView には、いくつかの UIView (赤い四角形) が含まれています。CAEAGLLayer では、白い四角形を描画します。白い四角形の中心は、赤い四角形の中心と同じです。UIScrollView がスクロールするとき、CAEAGLLayer 内の白い四角形の位置を更新してレンダリングします。

期待どおりの結果が得られています。白い四角形の中心は、赤い四角形の中心と常に同じです。

しかし、CAEAGLLayer の更新を UIScrollView 内のビューの動きと同期させることはできません。ある種のタイミングのずれがあります – 赤い四角形は白い四角形より遅れています。

大雑把に言えば、CAEAGLLayer を UIScollView と一緒にラグさせたいのです。

サンプルコードをご用意しました。デバイスで実行してスクロールすると、白い四角形 (OpenGL で描画) が赤い四角形 (通常の UIView) よりも速く動いていることがわかります。OpenGL は scrollViewDidScroll: デリゲート呼び出し内で更新されています。

https://www.dropbox.com/s/kzybsyu10825ikw/ios-opengl-scrollview-test.zip

iOS シミュレーターでも同じように動作します。ビデオをご覧ください: http://www.youtube.com/watch?v=1T9hsAVrEXw

赤=UIKit、白=OpenGL

コードは次のとおりです。

- (void)scrollViewDidScroll:(UIScrollView *)aScrollView {
    // reuses red squares that are gone outside thw bounds
    [overlayView updateVisibleRect:CGRectMake(...)];
    // draws white squares using OpenGL under the red squares
    [openGlView updateVisibleRect:CGRectMake(...)];
}

編集:

同じ問題は、非常に単純化されたサンプルで簡単に実証できます。動作中の xcodeproj は次の場所にあります。

https://www.dropbox.com/s/vznzccibhlf8bon/simple_sample.zip

サンプル プロジェクトは基本的に、OpenGL で一連の白い正方形を描画してアニメーション化し、同じことを一連の RED UIViews に対して行います。遅れは、赤と白の四角の間で簡単に確認できます。

4

6 に答える 6

5

少し掘り下げた後、以前の回答を拡張したいと思います:

OpenGL レンダリングはscrollViewDidScroll:メソッド内からすぐに実行されますが、UIKit 描画は後で実行ループからの通常の CATransaction 更新中に実行されます。

UIKit の更新を OpenGL レンダリングと同期するには、両方を 1 つの明示的なトランザクションに含めてフラッシュし、UIKit に変更をbackbaorddすぐにコミットさせます。

- (void)scrollViewDidScroll:(UIScrollView *)aScrollView {
    [CATransaction begin];
    [overlayView updateVisibleRect:CGRectMake(...)];
    [openGlView updateVisibleRect:CGRectMake(...)];
    [CATransaction flush]; // trigger a UIKit and Core Animation graphics update
    [CATransaction commit];
}
于 2012-11-12T12:58:21.973 に答える
2

適切な答えがないので、私は私の考えを共有したいと思います:

描画には、Core Animation(UIKit)とOpenGLの2つの方法があります。結局、すべての描画はOpenGLによって行われますが、Core Animationの部分は、一種のウィンドウサーバーとして機能するバックボード(またはiOS 6より前のSpringboard.app)でレンダリングされます。

これを機能させるために、アプリのプロセスはレイヤー階層をシリアル化し、そのプロパティを変更してデータをバックボードに渡します。バックボードはレイヤーをレンダリングして構成し、結果を表示します。

OpenGLをUIKitと混合する場合、CAEAGLLayerのレンダリング(アプリで実行されます)を残りのCoreAnimationレイヤーと合成する必要があります。CAEAGLLayerで使用されるレンダリングバッファは、アプリケーションのレンダリングをすばやく転送するために、何らかの形でbackboarddと共有されていると思います。このメカニズムは、必ずしもCoreAnimationからの更新と同期する必要はありません。

問題を解決するには、OpenGLレンダリングをCore Animationレイヤーのシリアル化と同期し、バックボードに転送する方法を見つける必要があります。解決策を提示できず申し訳ありませんが、これらの考えと推測が問題の理由を理解するのに役立つことを願っています。

于 2012-10-24T16:29:21.270 に答える
0

私はまったく同じ問題を解決しようとしています。上記のすべての方法を試しましたが、何も受け入れられませんでした。

理想的には、これは Core Animation の最終合成 GL コンテキストにアクセスすることで解決できます。しかし、プライベート API にはアクセスできません。

もう 1 つの方法は、GL の結果を CA に転送することです。フレームバッファピクセルを読み取ってCALayerにディスパッチすることでこれを試しましたが、遅すぎました。約 200MB/秒以上のデータを転送する必要があります。今、私はこれを最適化しようとしています。これが私が試すことができる最後の方法だからです。

アップデート

まだ重いですが、フレーム バッファを読み取って CALayer.content に設定することはうまく機能しており、iPhone 4S で許容できるパフォーマンスを示しています。いずれにせよ、CPU 負荷の 70% 以上は、この読み取りと設定操作だけに使用されます。特にglReadPixels私は、GPU メモリからデータを読み取るための待ち時間がほとんどです。

Update2

私は最後の方法をあきらめました。そのコストは、ほぼ純粋にピクセル転送であり、それでも遅すぎます。すべての動的オブジェクトを GL で描画することにしました。一部の静的ポップアップ ビューのみが CA で描画されます。

于 2012-12-27T09:28:38.930 に答える
-1

UIKitとOpenGLの描画を同期するには、Core Animationが描画を期待している場所、つまり次のような場所でレンダリングを試みる必要があります。

- (void)displayLayer:(CALayer *)layer
{
    [self drawFrame];
}

- (void)updateVisibleRect:(CGRect)rect {
    _visibleRect = rect;
    [self.layer setNeedsDisplay];
}
于 2012-10-24T21:55:35.290 に答える