12

フレームをスキップしたり繰り返したりすることなく、垂直同期ごとに正確に 1 つのレンダリングが実行されるように、垂直同期レンダリングを実行しようとしています。これは、Windows 7 および (将来的には) Windows 8 で動作するために必要です。

基本的にはQUADS、元の画像のピクセルが画面上のピクセルと 1:1 で一致するように、画面に収まるシーケンスを描画することで構成されます。OpenGLでもDirectXでも、レンダリング部分は問題ありません。問題は正しい同期です。

WGL_EXT_swap_control私は以前、描画してから呼び出して、拡張機能を使用してOpenGLを使用しようとしました

SwapBuffers(g_hDC);
glFinish();

glFlush() と一緒にこれら 2 つの命令のすべての組み合わせと順列を試しましたが、信頼できるものではありませんでした。

次に、描画してから呼び出すことにより、Direct3D 10で試しました

g_pSwapChain->Present(1, 0);
pOutput->WaitForVBlank();

ここでg_pSwapChainIDXGISwapChain*pOutputそのIDXGIOutput*SwapChain に関連付けられています。

OpenGL と Direct3D の両方のバージョンの結果は同じです。たとえば、60 フレームの最初のシーケンスは、本来あるべき状態で持続しません (60hz で約 1000 ミリ秒ではなく、1030 ミリ秒または 1050 ミリ秒のように持続します)。正常に動作しますが (約 1000.40ms)、時々フレームがスキップされるようです。で採寸しQueryPerformanceCounterます。

Direct3D で、WaitForVBlank だけのループを試行すると、1000 回の反復の継続時間は一貫して 1000.40 で、変動はほとんどありません。

したがって、ここでの問題は、呼び出された各関数がいつ戻るか、およびスワップが垂直同期中に行われたかどうか (テアリングを避けるために以前ではない) を正確に把握していないことです。

理想的には (私が間違っていなければ)、私が望むものを達成するには、1 つのレンダリングを実行し、同期が開始するまで待ち、同期中にスワップし、同期が完了するまで待ちます。OpenGLまたはDirectXでそれを行う方法は?

編集: わずか 60x のテスト ループはWaitForVSync、1000.30ms から 1000.50ms まで一貫してかかります。Present(1,0)beforeを使用した同じループはWaitForVSync、他に何もレンダリングせず、同じ時間がかかりますが、フレームを繰り返したかのように、失敗して 1017 ミリ秒かかる場合があります。レンダリングがないため、ここに何か問題があります。

4

6 に答える 6

3

DX11でも同じ問題があります。マルチバッファリングのレイテンシを回避するために、フレーム レンダリング コードがモニターのリフレッシュ レートの正確な倍数になることを保証したいと考えています。

電話をかけるだけでpSwapChain->present(1,0)は不十分です。これにより、フルスクリーン モードでのテアリングが防止されますが、vblank が発生するまで待機しません。現在の呼び出しは非同期であり、埋められていないフレーム バッファが残っている場合はすぐに戻ります。したがって、レンダリング コードが非常に高速に新しいフレームを生成し (たとえば、すべてをレンダリングするのに 10 ミリ秒)、ユーザーがドライバーの「最大事前レンダリング フレーム」を 4 に設定している場合、ユーザーに表示されるフレームよりも 4 フレーム早くレンダリングされます。これは、マウス操作と画面応答の間の遅延が 4*16.7=67ms であることを意味し、これは許容できません。アプリが要求した場合でも、ドライバーの設定が優先されることに注意してくださいpOutput->setMaximumFrameLatency(1)、関係なく4フレームを取得します。したがって、ドライバの設定に関係なく、マウス ラグがないことを保証する唯一の方法は、レンダリング ループが次の垂直リフレッシュ間隔まで自発的に待機することです。

IDXGIOutput::WaitForVBlank()この目的のために意図されています。しかし、うまくいきません!以下を呼び出すと

<render something in ~10ms>
pSwapChain->present(1,0);
pOutput->waitForVBlank();

呼び出しが戻るまでの時間を測定するwaitForVBlank()と、おおよそ 6 ミリ秒と 22 ミリ秒の間で交互に表示されます。

どうすればそれが起こりますか?完了するまでに 16.7 ミリ秒以上かかることはありませwaitForVBlank()ん。getRasterState()DX9では、独自のはるかに正確なバージョンの waitForVBlank を実装することで、この問題を解決しました。しかし、その呼び出しは DX11 で廃止されました。

フレームがモニターのリフレッシュレートと正確に一致していることを保証する他の方法はありますか? getRasterState が行っていたように、現在のスキャンラインをスパイする別の方法はありますか?

于 2014-04-18T21:13:13.177 に答える
1

私は以前、描画してから呼び出して、WGL_EXT_swap_control 拡張機能を使用して OpenGL を使用しようとしました

SwapBuffers(g_hDC);
glFinish();

その glFinish() または glFlush は不要です。SwapBuffers は glFinish を意味します。

グラフィックス ドライバーの設定で、「V-Blank / V-Sync を強制的にオフ」に設定している可能性がありますか?

于 2012-05-08T14:28:38.003 に答える
1

Windows 8.1 および Windows 10 では、DXGI 1.3 を利用できますDXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECTMSDNを参照してください。ここにあるサンプルは Windows 8 ストア アプリ用ですが、クラス Win32 Windows スワップチェーンにも適応できるはずです。

このビデオも役に立つかもしれません。

于 2015-10-06T23:42:30.810 に答える
1

現在DX9を使用しており、DX11に切り替えたいと考えています。現在GetRasterState()、手動で画面に同期するために使用しています。これは DX11 ではなくなりますが、DirectDraw7 デバイスを作成しても DX11 が混乱することはないようです。これをコードに追加するだけで、スキャンラインの位置を取得できるはずです。

IDirectDraw7* ddraw = nullptr;
DirectDrawCreateEx( NULL, reinterpret_cast<LPVOID*>(&ddraw), IID_IDirectDraw7, NULL );
DWORD scanline = -1;
ddraw->GetScanLine( &scanline );
于 2015-09-22T22:24:01.337 に答える
0

Direct3D デバイスを作成するときは、構造体PresentationIntervalのパラメーターをに設定します。D3DPRESENT_PARAMETERSD3DPRESENT_INTERVAL_DEFAULT

于 2012-05-08T14:24:59.500 に答える
-1

カーネル モードまたはリング 0 で実行している場合は、VGA 入力レジスタ(03bah、03dah) からビット 3 を読み取ることができます。情報はかなり古いですが、ビットの位置が変更されたか、Windows 2000以降のバージョンで廃止された可能性があることをここでほのめかしましたが、実際にはこれを疑っています. 2 番目のリンクには、古い Windows バージョンの vblank 信号を公開しようとする非常に古いソース コードがあります。実行されなくなりましたが、理論的には最新の Windows SDK で再構築すると、これが修正されるはずです。

難しいのは、この情報を確実に公開するデバイス ドライバーを作成して登録し、それをアプリケーションから取得することです。

于 2015-09-02T10:49:25.143 に答える