Windows 7 におけるULWの挙動 (2)
DWMでもなく、タイマーでもない。原因になる得るものがなく、困ってしまった。
Direct2Dによる高速描画の試行
ということで、とりあえず原因究明はあきらめ、 「人事を尽くす」方向でアプローチすることにした。Windows Vista/7からはGDIはレガシーであり、はっきり言って遅い。時代はGPUをフル活用したグラフィックス描画であり、Vista/7にはそのためのDirect2Dというグラフィックスレイヤーが存在する。
Direct2Dは名前が示すようにDirectXコンポーネントの一部で、ハードウェア(GPU)を利用して二次元描画を高速に行うサービスである。DirectSoundなどDirectXのほかのコンポーネントがゲーム向けであるのに対して、Direct2DはOSや一般アプリが利用することを想定されている点が特徴である。IE9が利用して大幅に高速化されたというのも、このDirect2Dである。
Direct2Dについては下記のリンクが詳しい。
- Direct2D (Windows)
- Direct2D による描画
- Direct2D ? Wikipedia
- 川西 裕幸のブログ : Direct2D
- ASCII.jp:Windowsの画面表示を変えるDirect2DとDirectWrite
- Direct2D 入門
- ホイール欲しい ハンドル欲しい ≫ Direct2D と Direct3D10.1 の下位互換
DirectX関係はC++になるわバカでかいSDKをダウンロードしないといけないわ環境が汚れるわで敬遠しているんだけど、仕方ない、さっそく DirectX SDK (February 2010) を落としてインストール。サンプルのDirect2DHelloWorldを改造して、タイマーでアニメーションするようにした。
タイマーを設定して、
::SetTimer(hwnd, 0, 1000/100, NULL);
ウインドウプロシージャで受けて、
case WM_TIMER:
::InvalidateRect(hwnd, NULL, FALSE);
break;
描画ルーチンでQPCの値基準で横方向に移動させるようにした。
m_pRenderTarget->DrawText(
sc_helloWorld,
ARRAYSIZE(sc_helloWorld) - 1,
m_pTextFormat,
D2D1::RectF((float)fmod(800 * (f+i/10.0f), 1600) - 800, (i-25)*40.0f, renderTargetSize.width, renderTargetSize.height),
m_pBlackBrush
);
DWM環境下で実行させてみるとびっくり、これなら滑らかに動いている!?
……と思ったけど、なんかおかしい。前よりは滑らかだけど、やっぱりカクカクしている。現代のコンピュータはこの程度の負荷では間違いなく処理落ちしない。なにか、意図しない出来事が中で起こっているのに違いない。
試しに60fpsになるようにタイマーのウェイトを調整してみたら、さらにコマ落ちが発生した。さらに、半透明ウインドウにしてみたらさらにコマ落ちがひどくなった。アルファブレンドでのレイヤードウインドウは面倒なのでサボった。
ここまでのまとめ
ここまでの実験結果から分かることは、
- Windows 7では処理落ちしなくてもコマ落ちする。DWM環境に限らず、GDI環境でも落ちる。 レイヤードウインドウでなくてもダメ。
- タイマーの割り込みはXP/Vistaと同じ程度には良い精度で受け取ることができる。しかし、タイマーが周期的に描画していても、画面上では周期的に更新されない場合がある。
- Direct2Dを用いて超高速描画を行うと状況は改善する。
……ふむ、要は「アプリは描画したつもりなのに画面に反映されていない」ということらしい。となれば、やっぱりDWMが余計なことしてくれている可能性が高い。「余計なこと」の第一候補はアンチティアリングだろう。画面描画のために同期をとっているのだから、一般的に考えて何も起きないわけがない。