Windows 7 での滑らかなアニメーションの実現 (3)
前回のエントリで、DWMにはアプリのフレームをバッファリングする機能があり、これを使うことでモニタリフレッシュレートと同期した完全なアニメーションが実現できそうだという感触を得ることができた。
これには、前回リンクした記事より、下記のAPIが関係していると考えられる:
- DwmEnableMMCSS -- allows the DWM to participate in the Multimedia Class Schedule Service to have stronger control over the use of CPU resources in conjunction with another app using MMCSS (typically for video playback)
- DwmGetCompositionTimingInfo -- provides information about the rate at which the DWM is composing, how many frames are completed, how many are pending, dropped frames, etc.
- DwmModifyPreviousDxFrameDuration, DwmSetDxFrameDuration, DwmSetPresentParameters -- these allow applications to queue up multiple DirectX surfaces for presentation and precisely control when they're to be presented. They're typically used by video presentation applications to counteract the latency that's inherent in a compositing system.
このうち、DwmEnableMMCSSは簡単である。要はDWMプロセスの優先度とタイマー精度を高品質アニメーションに必要なレベルまで引き上げるAPIで、呼べばいいだけ。
で、残りの4つがやたら難しい。当初は DwmSetPresentParameters に渡すパラメタの DWM_PRESENT_PARAMETERS.cBuffer (The number of frames the application would like DWM to queue) でフレームバッファの数を設定できるみたいなので、これを使って8枚にすれば終わりだと思っていた。
ところが、これを適用したあとのアプリケーションの状態を調べても、有効になっているか良く分からない。DwmGetCompositionTimingInfo の結果を以下に示す。
qpc = 1347430355610
qpf = 2766630
rateRefresh = 60/1
qpcRefreshPeriod = 46150
rateCompose = 60/1
qpcVBlank = 1347430254402
cRefresh = 29219767
cDXRefresh = 28985484
qpcCompose = 1347430254445
cFrame = 29218445
cRefreshFrame = 29219768
cFrameSubmitted = 29218444
cFrameConfirmed = 29218444
cDXPresent = 159780
cDXPresentSubmitted = 159779
cDXPresentConfirmed = 159779
cRefreshConfirmed = 29219767
cDXRefreshConfirmed = 28985484
cFramesLate = 3716
cFramesOutstanding = 0
cFrameDisplayed = 0
qpcFrameDisplayed = 0
cRefreshFrameDisplayed = 0
cFrameComplete = 0
qpcFrameComplete = 0
cFramePending = 0
qpcFramePending = 0
cFramesDisplayed = 0
cFramesComplete = 0
cFramesPending = 0
cFramesAvailable = 0
cFramesDropped = 0
cFramesMissed = 0
cRefreshNextDisplayed = 0
cRefreshNextPresented = 0
cRefreshesDisplayed = 0
cRefreshesPresented = 0
cRefreshStarted = 0
cPixelsReceived = 0
cPixelsDrawn = 0
cBuffersEmpty = 0
なお、qpc は 取得時点の QueryPerformanceCounter の値、qpf は PerformanceFrequency である。
一番最後のcBuffersEmptyが 0 になっていることが分かると思う。もしフレームバッファが複数あれば、フレームレートがちょっと落ちたタイミングで、この値は1とか2くらいにはなるはず。ところが、ずっと結果を見ても0のまま変化しない。DwmSetPresentParameters が想定通りに作用していないのでは…という気がする。
ところで、いろいろ調べていたら、
- フレームレート: アプリケーションのウインドウ更新レート単位は fps。
- リフレッシュレート: モニターの画面更新レート。単位はHz。
という用語が概念の区別に適切だなあ、と改めて分かりました。Vista以前のWindowsではリフレッシュレートを意識することはほぼ不可能だったので意識しませんでしたが、この使い分けはその手の業界では当たり前なのかなあ。
ちなみにDWMでは、
- DirectX Refresh
- Frame Refresh
という用語もあって、謎は深まるばかりです。。。はあ。