15

ゲームエンジンをmacに移植しようとしているときに、1つの基本的な(しかし大きな)問題に遭遇しました。Windowsでは、私のメインコードは次のようになります(非常に単純化されています)。

PeekMessage(...) // check for windows messages
switch (msg.message)
{
case WM_QUIT: ...;
case WM_LBUTTONDOWN: ...;
...
}
TranslateMessage(&msg);
DispatchMessage (&msg);

for (std::vector<CMyBackgroundThread*>::iterator it = mythreads.begin(); it != mythreads.end(); ++it)
{
  (*it)->processEvents();
}

... do other useful stuff like look if the window has resized and other stuff...

drawFrame(); // draw a frame using opengl
SwapBuffers(hDC); // swap backbuffer/frontbuffer

if (g_sleep > 0) Sleep(g_sleep);

これはMacの方法ではないことをすでに読みました。Macの方法は、新しいフレームをレンダリングするために、画面のv-syncなどのある種のイベントをチェックすることです。しかし、ご覧のとおり、レンダリングだけでなく、他のスレッドにも機能させたいと思っています。一部のスレッドは、1/60秒ごとよりも速く呼び出す必要があります。(ちなみに、私のスレッドインフラストラクチャは次のとおりです。スレッドはイベントを同期キューに入れ、メインスレッドはプロセスイベントを呼び出します。これはメインスレッド内の同期キュー内のアイテムを処理します。これはネットワーク関連、画像の読み込み/処理、パーリンノイズに使用されます。世代など...など...)

同様の方法でこれを実行できるようにしたいと思いますが、これに関する情報はほとんどありません。レンダリングをv-syncイベントに配置してもかまいません(これはWindowsにも実装します)が、残りのコードの応答性をもう少し高めたいと思います。

このように見てください:GPUが処理を実行している間に残りを処理できるようにしたいと思います。v-syncが処理を開始するのを待ってから、すでに処理されているはずの処理を開始するのを待ちたくありません。 GPUにデータを送信します。私の言いたいことが分かりますか?

これをまったく別の視点から見る必要がある場合は、教えてください。

このために本/ガイド/チュートリアル/何かを読む必要がある場合は、何を読むべきか教えてください!

私はココア開発者ではなく、object-cプログラマーでもありません。また、ゲームエンジンは完全にC ++ですが、ウィンドウを表示し、そのウィンドウ内に描画したものを表示するのに十分なxcodeの使い方を知っています。私はそれを正しくする方法がわからないので、それは私のウィンドウズバージョンのように更新されません。

更新:垂直リトレースで同期したい場合でも、何らかのループが必要だとさえ信じています。MacOSXドキュメントのOpenGLプログラミングは、これがSwapIntervalを設定することによって行われることを示しています。したがって、正しく理解していれば、Macでリアルタイムにレンダリングするときは、swapInterval設定を使用して電力使用量を減らすために、常に何らかのループが必要になります。これは本当ですか?

4

2 に答える 2

19

これを達成しようとしているのは私だけではありませんが、誰もこれに答えたくないようです(stackoverflowの人を指さず、それがどのように機能するかを知っているMac開発者を指してください)。だから私はこの典型的な振る舞いを変えて、同じことを探している人たちを助けたいのです。言い換えれば、私は解決策を見つけました!私はまだこのコードをさらに改善する必要がありますが、これは基本的にそれです!

1 / Windowsのように、実際の独自のループが必要な場合は、自分で処理する必要があります。したがって、cocoaに標準コードをビルドさせますが、main.mを取得し(必要に応じてc ++の場合は.mmに変更します。これは、この例ではc ++を使用したためです)、コードの1行だけを削除します。cocoaがアプリケーション/ウィンドウ/ビューを設定することを許可しません。

2 / Cocoaは通常、AutoreleasePoolを作成します。メモリ管理に役立ちます。これで、これは発生しなくなるため、初期化する必要があります。main関数の最初の行は次のようになります。


    [[NSAutoreleasePool alloc] init];

3 /次に、アプリケーションデリゲートを設定する必要があります。XCodeウィザードはすでにAppDelegateクラスを設定しているので、それを使用する必要があります。また、ウィザードはすでにメインメニューを設定しており、おそらくMainMenu.xibと呼ばれています。開始するには、デフォルトのもので問題ありません。#import <Cocoa/Cocoa.h>行の後に必ず#import"YourAppDelegate.h"を付けてください。次に、メイン関数に次のコードを追加します。


    [NSApplication sharedApplication];
    [NSApp setDelegate:[[[YourAppDelegate alloc] init] autorelease]];
    [NSBundle loadNibNamed:@"MainMenu" owner:[NSApp delegate]];
    [NSApp finishLaunching];

4 / Mac OSは、アプリケーションが何であるかを認識し、メインメニューを追加します。まだウィンドウがないため、これを実行してもあまり効果はありません。それを作成しましょう:


    [Window setDelegate:[NSApp delegate]];
    [Window setAcceptsMouseMovedEvents:TRUE];
    [Window setIsVisible:TRUE];
    [Window makeKeyAndOrderFront:nil];
    [Window setStyleMask:NSTitledWindowMask|NSClosableWindowMask];

ここで何ができるかを示すために、タイトルバーを追加し、閉じるボタンを有効にしました。もっとたくさんのことができますが、私もこれを最初に勉強する必要があります。

5 /これを実行すると、幸運にもマイクロ秒のウィンドウが表示される可能性がありますが、プログラムがまだループしていないため、おそらく何も表示されません。ループを追加して、着信イベントをリッスンしましょう。


    bool quit = false;
    while (!quit)
    {
        NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES];
        switch([(NSEvent *)event type])
        {
        case NSKeyDown:
            quit = true;
            break;
        default:
            [NSApp sendEvent:event];
            break;
        }
        [event release];
    }

これを実行すると、ウィンドウが表示され、キーを押すまで表示されたままになります。キーを押すと、アプリケーションはすぐに終了します。

これだよ!ただし、注意してください...ActivityMonitorを確認してください。アプリケーションが100%CPUを使用していることがわかります。なんで?ループはCPUをスリープモードにしないためです。それはあなた次第です。あなたはそれを自分で簡単にし、usleep(10000);を置くことができます。しばらくの間。これは多くのことを行いますが、最適ではありません。CPUが過度に使用されていないことを確認するために、おそらくopenglのvertical-syncを使用します。また、スレッドからのイベントを待ちます。たぶん、私は自分で経過時間をチェックし、計算されたusleepを実行して、フレームあたりの合計時間を作成し、適切なフレームレートを実現します。

Openglのヘルプについては:

1/cocoa.openglフレームワークをプロジェクトに追加します。

2 / [ウィンドウ...]の前に置く:


    NSOpenGLPixelFormat *format = [[NSOpenGLPixelFormat alloc] initWithAttributes:windowattribs];
    NSOpenGLContext *OGLContext = [[NSOpenGLContext alloc] initWithFormat:format shareContext:NULL];
    [format release];

3 / [Window setDelegate:...]の後put:


    [OGLContext setView:[Window contentView]];

4 /ウィンドウ処理後のどこかに、次のように配置します。


    [OGLContext makeCurrentContext];

5 / [イベントリリース]の後、次のように入力します。


    glClearColor(1, 0, 0, 1);
    glClear(GL_COLOR_BUFFER_BIT);
    [OGLContext flushBuffer];

これにより、私のMac BookPro2011のフレームレート3300でウィンドウが完全に赤くなります。

繰り返しますが、このコードは完全ではありません。[閉じる]ボタンをクリックして再度開くと、機能しなくなります。私はおそらくそのためにイベントをフックする必要がありますが、まだそれらを知りません(さらに調査した後、これらのことはAppDelegateで行うことができます)。また、他にもやるべきことや考慮すべきことがたくさんあるでしょう。しかし、これは始まりです。お気軽に訂正/記入してください/...

私がそれを完全に正しく理解すれば、誰も私に勝てないなら、私はこれのためのチュートリアルウェブページをセットアップするかもしれません。

これが皆さんのお役に立てば幸いです。

于 2011-07-12T13:36:58.727 に答える
1

代わりにCoreFoundationのrunloopを使用し、「スレッド」のイベントソースを設定して、runloopがウェイクアップしてprocessEvents()メソッドを呼び出すようにすることもできます。待機中のイベントがない場合、実行ループによってスレッドがスリープ状態になるため、これはポーリングコードよりも改善されています。

詳細についてCFRunLoopSourceCreate()は、、CFRunLoopSourceSignal()を参照CFRunLoopWakeUp()してください。

アプリケーションがCocoaフレームワーク上に構築されている場合は、デフォルトを使用できます(おそらく使用する必要があります)が、メッセージを送信することでNSRunLoop基盤となるCore Foundationを取得できるため、これは問題ではありません。CFRunLoop-getCFRunLoop

于 2011-07-11T13:15:12.163 に答える