Windows 7 におけるULWの挙動 (1)

ULW (UpdateLayeredWindow)を利用したソフトを開発しています。そのソフトではイメージを毎フレーム書き換えてアニメーションするんだけど、Windows Vista 32bit から Windows 7 64bit に乗り換えたときにソフトウェアの挙動が変わった。

どう変わったかというと、

  • フレームレートが極端に落ちた。
  • なめらかにアニメーションしなくなった。
  • ティアリングしなくなった(アンチティアリング)。

というふうになった。要するに、アニメーションは遅く・醜くなり、代わりに意図しない現象が起こるようになった。単なるフレームレート落ちであれば「7は遅いな」となるだけだが、今回は遅い上に安定しないのが謎だった。

アンチティアリングとは、ディスプレイの更新周期とプログラムの更新周期を同期させて、ディスプレイのアニメーションなどを綺麗に見せる技術のこと。これは、マルチメディア系のアプリケーションでは昔から使われてきた技術(昔はVsync: Vertical Synchronizeとか垂直同期などと呼んだ)だけど、エクセルなどの一般アプリでは「どうでもいい」ところで、Windowsも通常はティアリングしまくっていた。(アンチティアリングすると一般に性能が(極端に)落ちるので、必要なければしないほうがよい。)

ところが、Windows 7ではアプリ全体が自動的にアンチティアリングされているように見えた。これは、地味だが大きな変更だというのは分かった。

Windows VistaとDWM

問題が起こったのは7に乗り換えたときだが、しかしOS的にはVista ?> 7 より XP ?> Vista のほうが影響の大きな変更が入ったはずだった(自作アプリはなんともなかったけど)。その変更とはDWM (Desktop Window Manager)の導入である。

DWMは、Windows Vistaから導入されたウインドウ描画領域を管理するシステムプログラムである。DWMが動作している環境では、アプリケーションが行う描画はすべてDWM管理下の描画領域にリダイレクトされ、実際のディスプレイへの表示はDWMが行う。

DWMについては下記のリンクが詳しい。

Windows Vista/7で描画周りの挙動が変わったのなら、DWM周りの影響に違いない。

単純なGDI描画でのタイミングずれ

DWMが原因なら、DWMを切ればいい。Vista/7では、Aeroではない画面表示モードにするとDWMによる画面管理がオフになる。

そうした上で、単純にタイマーでアニメーションするプログラムで検証した。このプログラムはそもそもレイヤードウインドウでもなく、クライアント領域を持つ普通のアプリである。擬似コードだが要は次のようなもの。

void onTimerElapsed() {

myObj.x = k * QueryPerformanceCounter() / FREQ;

myObj.y = CONSTANT_Y;

}

よくある (dX * ++counter) 的なことをしないのは、タイマーはずれるので、絶対時刻で位置決めしないといけないから。QueryPerformanceCounter() はプロセッサが持つクロックレベル分解能の経過時間を得る関数。

ところが、これを行っても相変わらず綺麗にアニメーションできない。ということは、DWMだけが原因ではない??

タイマー動作の確認

DWMに原因がないとしたら、タイマーしか思い当たる節がない。というわけで、タイマーが本当に意図通り動いているか、次のようなプログラムを用いて確認することにした。

  • timeBeginPeriod(1)でタイマー周期精度を上げる
  • タイマーを10msで割り込ませる
  • タイマーハンドラ内でQPC (QueryPerformanceCounter) で周期を確認

確認すると、まあだいたい意図した振る舞いをしているようだ。1ms程度の揺らぎはあるものの、1msの変化が目に見えるズレとして顕在化することはない。

 

DWMでもなく、タイマーでもない。原因になる得るものがなく、困ってしまった。