DirectXを使ってVSYNC待ちできると聞いたのですがよくわかりません。どなたか教えていただけないでしょうか。
環境は WinXP SP2 VC.Net2003 MFC です。
IDirectDraw::WaitForVerticalBlank というのを
昔使ったことがありますが、割と取りこぼしというか
抜けが多く発生したような記憶があります。
そのときは、結局, WaitForVerticalBlank で何回か
垂直ブランキングを待って 1フレームの時間を割り出して、
タイマーでタイミングを取って解決したはず。
解答ありがとうございます。それはDirectGraphicsでも使用できるのですか?
DirectGraphics は使ったことがないのでわかりませんが、
IDirectDraw::WaitForVerticalBlank でタイミングを取って
DirectGraphics の機能を使って描画、というやり方に
なるのではないかと想像します。
取り敢えず試してみてください。
WaitForVerticalBlankでVSYNCがとれるようになったのですが
やはりとりこぼしが多く発生します。
WaitForVerticalBlank で何回か
垂直ブランキングを待って 1フレームの時間を割り出して、
タイマーでタイミングを取って解決したはず。
っていう部分を具体的に教えてください。
過去のソースをサルベージしたら、とあるプロジェクトで
V待ちを行っていて、それは概略こんな感じになってました。
・時間を QueryPerformanceCounter で計測しながらVを3回待つ。
・VとVの間隔のうち、短いほうをV周期として採用。
(短いほうが正しいV周期と思われるので)
・描画ループの中で、時間を QueryPerformanceCounter で計測して、
V周期の間待ち、所定の時間に達したら描画する、というのを
繰り返す。
描画ループ中のV待ちを単純に時間計測で行っているので、
長い間には誤差が蓄積してしまうでしょうが、このプロジェクトでの
描画は 2秒くらいで一区切りつくような構成になっていたので
問題ありませんでした。
また、描画ループ中、CPU使用率が100%になったままになりますが、
スムーズな描画のためと、割り切りました。
なお、最初に時間計測するときのV待ちには
IDirectDraw::WaitForVerticalBlank ではなく
IDirectDraw::GetVerticalBlankStatus を使っていました。
どうも, IDirectDraw::WaitForVerticalBlank は
モニターパワーセーブに入ると帰ってこなくなってしまう、
という問題があって使うのをやめたようです。
IDirectDraw::GetVerticalBlankStatus によるV待ちは
次のようにしていました。
・まず現在時刻を計測。
・ループの中で時刻を計測して、設定したタイムアウトに
達していないか確認。(タイムアウトは200msを設定していました)
・IDirectDraw::GetVerticalBlankStatus で現在垂直帰線期間内か
確認すると同時に、その状態を保存。
・現在垂直帰線期間内で、前回は垂直帰線期間内ではなかったなら、
新たに垂直帰線期間に入ったということなので、ループを抜ける。
ところで、このプロジェクトのもっと後のバージョンでは、
別の方法でのV待ちを行っていました。
どうも、上記の方法でもまれに抜けが発生していたようで、
もっと確実にV待ちを行うため、ライン番号を監視することに
したようです。
具体的には IDirentDraw::GetScanLine を使って、
次のようにしていました。
・まず現在時刻を計測。
・ループの中で時刻を計測して、設定したタイムアウトに
達していないか確認。
(ここまでは GetVerticalBlankStatus による方法と同じ)
・IDirentDraw::GetScanLine で現在のライン番号を取得。
同時にこれを保存。
なお, DDERR_VERTICALBLANKINPROGRESS が返ってきたなら
ライン番号は 0 とする。
・前回のライン番号より今回のライン番号のほうが少なければ
帰線したということなのでループを抜ける。
IDirentDraw::GetScanLine によるV待ちでは抜けがほとんど
なかったので、描画ループの中でダイレクトに GetScanLine による
V待ち処理を呼び出してしました。
ただし, IDirentDraw::GetScanLine は環境によっては
正常な値を返さない(1画面スキャンする間に何度も増減を繰り返す)
ことがありました。
よって、最初に GetScanLine で2回V待ちを行ってその間隔を計測し、
想定する最小間隔(600分の1秒という値を想定していました)より
小さかったら GetScanLine は採用せず, GetVerticalBlankStatus
による待ちを行うようにしていました。
ツイート | ![]() |