28

まず、私の質問:iOS Run-Loopをどのように管理しますか?

次の私の理由:私はさまざまなプロトタイプ(v。初期段階の開発)でこれを研究していて、多くの厄介な問題を発見しました。

  • まず、入力の問題と実行ループにより、次のことを試してみます。
    • 最も推奨されるシステム(CADisplayLink)を使用する場合、CPUの負荷によってバッファーフリップ(presentRenderBuffer)がフレームを待機する必要があると、特定のタッチ入力がドロップされることに注意しました。これはデバイスでのみ発生し、シミュレーターでは発生しません(迷惑なことに、これはメインスレッドでのvsyncブロッキングの待機と、アプリの実行ループプロセスが入力にタッチしてメッセージを食べる方法に関連しているようです)
    • 次に推奨されるシステム(NSTimer)を使用する場合、CPU負荷がシミュレーターの特定のポイントに達すると、特定のタッチ入力がドロップされるが、デバイスではドロップされないことに気付きました(これも迷惑です)。NSTimerを使用すると、更新が発生したときの精度も大幅に低下します。
    • 最も推奨されていないシステムを使用する場合(mach_absolute_timeから構築された高精度タイマーを使用して内部で管理されている独自のスレッドで実行ループを実行すると、タッチ入力の問題はすべて解消されますが、ASSERTコードは間違ったスレッドにトラップされます。ソフトウェア割り込みに続いて(私のアサートコードはhttp://iphone.m20.nl/wp/?p=1に似ています)問題の原因となった行ですぐにアサートコードトラップを実行するのが本当に好きなので、この解決策は次のとおりです。私にとっては実際には機能しません:デバッグが難しいです。
  • 第二に、失われた時間:
    • システムを調査していると、フレームレートに関係なく(奇妙なことに、統計的にはvsyncを使用しても意味があると思います)、vsyncで約22%の時間待機していることがわかりました。これは、glFlush / glFinishを移動し、presentRenderBuffer呼び出しを実行する頻度を試して確認しました。これは、ブロックしているgl呼び出しで単にストールするのではなく、AIなどを処理したい重要な時期です。これについて私が考えることができる唯一の方法は、レンダリングをそれ自体のスレッドに移動することですが、シングルプロセッサデバイスでマルチスレッドの再設計を開始することが保証されているかどうかはわかりません。

それで、誰かがこれらの問題の周りに魔法の弾丸を見つけましたか?誰かがこのプラットフォームでキックアスであるキラーランループアーキテクチャを持っていますか?現時点では、私は悪の少ない方を選ばなければならないようです。

4

1 に答える 1

6

私自身の iOS プロジェクトでは、従来のアプローチを使用します (ウィンドウ .nib を作成し、継承するクラスを作成し、独自の .nib に配置されたビュー コントローラーのビューにEAGLView追加します)。EAGLView

仕事では、SDL に触発された少し異なるアプローチを採用しました。これは、オープンソース ライブラリAPRILで調べることができます。APRIL の主な目標は、単純さ (ウィンドウと入力の管理のみ) を維持し、ライセンスの問題を明確にし、自由に使用できるようにしながら、できるだけ多くのプラットフォームをサポートすることです。私たちの開発者は、1 つのプラットフォーム (好みや要望に応じて、Windows、Mac、または Linux) でアプリを作成したいと考えており、コードは私に渡され、他のプラットフォームに適応します。

APRIL で使用するアプローチでは、.nib を作成せず、 を呼び出すときUIApplicationMainにデリゲート クラスを 4 番目の引数として指定します。ゲームのメイン コードは各プラットフォームでまったく同じままであり、プラットフォーム固有のものだけ#ifdefがコードに組み込まれるか、ヘルパー ライブラリで抽象化されます。

アプリ デリゲートで、View Controller とウィンドウを作成します。

- (void)applicationDidFinishLaunching:(UIApplication *)application {
    // create a window.
    // early creation so Default.png can be displayed while we're waiting for 
    // game initialization
    window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    // viewcontroller will automatically add imageview
    viewController = [[AprilViewController alloc] initWithWindow:window];
    [viewController loadView];

    // set window color
    [window setBackgroundColor:[UIColor blackColor]];

    // display the window
    [window makeKeyAndVisible];

    // thanks to Kyle Poole for this trick
    // also used in latest SDL
    // quote:
    // KP: using a selector gets around the "failed to launch application in time" if the startup code takes too long
    // This is easy to see if running with Valgrind

    [self performSelector:@selector(runMain:) withObject:nil afterDelay:0.2f];
}

起動を 0.2 遅らせていることに注目してください。そのため、上記の画像ビューについて言及します。この 0.2 秒間、Default.png の直後に空白の画面が表示され、制御がメイン アプリに解放される runMain: に制御が転送される前に余分な遅延が発生します。

- (void)runMain:(id)sender
{       
    // thanks to Kyle Poole for this trick
    char *argv[] = {"april_ios"};
    int status = april_RealMain (1, argv); //gArgc, gArgv);
#pragma unused(status)
}

そのため、コントロールが UIApplication の実際のメイン ループに戻されることはありません。次に、独自のメイン ループを作成します。

    void iOSWindow::enterMainLoop()
    {
            while (mRunning) 
            {
                    // parse UIKit events
                    doEvents();
                    handleDisplayAndUpdate();
            }
    }

    void iOSWindow::doEvents()
    {
            SInt32 result;
            do {
                    result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, TRUE);
            } while(result == kCFRunLoopRunHandledSource);
    }

(ちなみに、もちろんビュー コントローラーは、デバイスの向きに合わせて UI の回転を単純化するために使用されます。)

これらのアプローチは両方ともCADisplayLink、OS でサポートされている場合に使用されます。私の個人的なプロジェクトは主に加速度計ベースですが、どちらの方法でも問題に気づいていません。エイプリル社のアプローチによって、いくつかの問題も解消されるのではないかと思います。

于 2011-02-04T19:11:35.877 に答える