私は、ビデオバックバッファに描画されるオブジェクトと、そのオブジェクトが実際に画面に表示されるポイントとの間の時間差を(可能な限り)推定する必要がある科学アプリケーションを開発しています。つまり、WindowsXP+上のDirectXがモニターの垂直更新サイクルをどのように処理するかを示します。
私のビデオルーチンはSDL1.3ライブラリに基づいていると言うことから始めます。その結果、DirectX APIにすぐにアクセスすることはできませんが、必要に応じて変更することができます。DirectXは、フルスクリーンモードでD3DSWAPEFFECT_DISCARD、D3DPRESENT_INTERVAL_ONE、およびBackBufferCount=1で初期化されています。これらは最も重要なパラメーターのようですが、さらに情報が必要な場合は、残りのSDLコードを掘り下げて喜んでいます。
D3DPRESENT_INTERVAL_ONEフラグは、バックバッファーとフロントバッファーがリフレッシュサイクルごとに1回だけスワップされ、リフレッシュの途中では決してスワップされないことを保証します(基本的にvsyncを有効にします)。実際、IDirect3DDevice9 :: Present(私の場合はSDL_RenderPresent)を継続的に呼び出す単純なループがある場合、この関数は2つの更新サイクル(60Hzで16.67ms、100Hzで10msなど)の間のミリ秒数の間ブロックします。 。
これが私の質問です...バックバッファに白い四角を描き、SDL_RenderPresentを呼び出します。これは16.67ミリ秒の間ブロックします(60Hzのリフレッシュを想定)。SDL_RenderPresentの呼び出しが戻ったときに、モニターに表示されている画像の状態について何を結論付けることができますか?私が見ているように、ここに可能性があります:
- 白い四角がモニターに描かれたばかりです。
- 白い四角が描画されようとしています(1ミリ秒未満)。
- 以前のフロントバッファが描画されたばかりです。白い四角が表示されるまでに、さらに更新サイクル(16.67ミリ秒)がかかります(SDL_RenderPresentを再度呼び出すとケース1になります)。
- 前のフロントバッファは最後の16.67ミリ秒で描画されました。次は白い四角ですが、次の更新までの正確な時間は不明です。
私が行ったすべての読み取りから、オプション3に傾いていますが、4に対する保証は見つかりません。私の構成では、Present関数は、 2つの更新サイクルの間に一時停止します。目標はフロントバッファーとバックバッファーを交換することであるため、2番目の呼び出しでこれを実行できる最も早い時点は、モニターが更新された直後です(前のフロントバッファーが描画されたばかりです)。その時点で、白い四角を含むバックバッファーを前面に移動できますが、モニターが実際にバッファーの内容を読み取って表示するまで、(最大で)16.67ミリ秒待機する必要があります。理想的には、前回の更新サイクルが終了するとすぐに関数が戻るはずだと聞きたいです。
DirectXの経験が豊富な人は、このトピックに関する洞察を提供できますか?私の仮定は正しいですか、それとも何かが欠けていますか?これらの仮定は、DirectXをサポートしているシステムに対して常に正しいのでしょうか、それともビデオカード、モニター、またはその他のものに応じてロジックが変わる可能性がありますか?
最後のマイナーな質問として、SDL_RenderPresentを何度も呼び出すループに戻ると、最初の3つまたは4つの呼び出しがすぐに返され、後続のすべての呼び出しが更新サイクルを待機していることに気付きました。D3DPRESENT_INTERVAL_ONE制限が最初の更新の前に単に無視されていると仮定するのは正しいですか(2つ以上のバッファーで行われるある種のキューイングとは対照的です)?
つまり、次のリフレッシュサイクルまでのループが約8msで開始されたとします。この期間中にフロントバッファとバックバッファを4回交換できる可能性があります。その最初の更新が行われるまで、SDL_RenderPresentはすぐに戻ります(技術的には現在フロントバッファーがないため、バックバッファーは2つだけです)が、これらのバッファーの1つが画面に表示されるとすぐにブロックが開始されます。これは有効な説明ですか?
[編集]
以下の回答に基づくと、vsyncとPresentを使用した私のアプローチが機能しないことは明らかです。希望する結果を得る別の方法を見つけたと思うので、誰かが私の考えの誤りを見つけた場合に備えて、または同様の問題に取り組んでいる他の誰かの情報のために、ここに投稿します。
最初のステップは、D3DPRESENT_INTERVAL_ONEを取り除くことです。これにより、vsyncが無効になり、SDL_RenderPresentへの呼び出しがすぐに返されるようになります。次に、IDirect3DDevice9 :: GetRasterStatusを使用して、現在のモニターの状態に関する情報を取得できます。これは、2つのリフレッシュサイクル間の一時停止中にtrueに設定されるブールフィールドと、アクティブなリフレッシュ中の現在のスキャンラインを示す別のフィールドを提供します。これらの2つの情報を使用すると、モニターのステータスを常にポーリングしてCPUを100%消費するループを実行することで、独自の垂直同期ルーチンを実装できます。これは私のニーズには受け入れられます。
バッファリングの問題はまだあります-SDL_RenderPresentを呼び出したときに、画面に描画されるフレームをどのように知ることができますか?これを判断する方法を見つけたと思います。これは、モニター上のどの線が現在描画されているかを知る能力に依存しています。基本的なロジックは次のとおりです。
- 新しい更新サイクルが開始するのを待ちます(一時停止= false、スキャンライン= 0)。
- 次のバックバッファを赤い色で塗りつぶし、Presentを呼び出します。
- スキャンラインが32に達するまで待ちます。
- 次のバックバッファを緑色で塗りつぶし、Presentを呼び出します。
など...デモの実装では、赤、緑、青、そして最後に黒を使用しました。GetRasterStatusが更新ステータスに関する正確な情報を提供し、SDL_RenderPresentが呼び出されるとすぐにフロントバッファとバックバッファが反転する場合にのみ、RGBカラーパターンが表示されるという考え方です。これらの条件のいずれかが満たされない場合、何も表示されないか、色が入れ替わったり重なったりする可能性があります。一方、画面の上部に各フレームの一定のRGBパターンが表示される場合は、次のようになります。描画された画像を直接制御できることを証明します。
私は今日仕事をしているいくつかのコンピューターでこの理論をテストしたことを付け加えなければなりません。ほとんどがパターンを表示しましたが、少なくとも1つは画面全体が赤く塗られていました。いくつかは、カラーバンドが上下にジャンプすることで、バッファの交換に一貫性がないことを示しています。これは通常、古いマシンで発生しました。これは、ハードウェアがテスト目的に適しているかどうかを判断するための優れたキャリブレーションテストだと思います。