遅延の話とdecltype


スクリーンショット撮って気付いた。Aero無効にしてねえ。
黄昏の日記から飛べる記事はこれだが…。
http://www.ouma.jp/ootake/delay-win7vista-j.html
AeroはDwmEnableComposition()で、キューイング*1フレーム数はDirect3D9Ex::SetMaximumFrameLatency()で0にしろ、ということらしいが、Direct3D9ExってVista専用じゃないか…?今XPと両用でDirect3D9使ってるんだから無理ゲー。しかしググるともうひとつ、IDXGIDevice1::SetMaximumFrameLatency()というのが出てきた。DXGIってなんじゃらほいと思ったが、MSDNによると、

DirectX グラフィックス インフラストラクチャー (DXGI) は、グラフィックスの一部が他と比べてゆっくりと変化することを認識することが可能です。DXGI の主な目標は、DirectX グラフィックスのランタイムに依存する必要のない、低レベル タスクの管理を行うことであり、今後のグラフィックス コンポーネントに共通のフレームワークを提供します。そのテクノロジを利用した最初のコンポーネントDirect3D 10 です。
以前のバージョンの Direct3D では、ハードウェア デバイスの列挙、出力へのレンダリングされたフレームの表示、ガンマ コントロール、フルスクリーン切り替えの管理などの低レベル タスクが、3D ランタイムに含まれていました。今回のバージョンでは、これらのタスクは DXGI に実装されています。
DXGI は、カーネル モード ドライバーおよびシステム ハードウェアと通信を行うことを目的としています。

と書いてある。まあ最初の1文は翻訳がやっちゃった系なので元の英文をなんとなく想像することにして、ハードウェア(要はビデオカード)をいじくる低レベルレイヤーをDirect3Dから分離して、Direct3DはDXGIを使ってビデオカードとやりとりするようになりました。だからDirect3Dは9だの10だの11だのでいろいろ変わるけど、DXGIはそんなに変わらないよ、って文章なんでしょう、1行目は。
DirectX10の解説ページを見たら、ID3D10***インターフェースの他に、IDXGI***インターフェースも使っていたので、Vista以降+DirectX10以降で書く時代になったら、IDXGIDevice1を取得してSetMaximumFrameLatency()してやればいいと思う。多分だ!!!


というわけでSetMaximumFrameLatency()はビデオカードのコントロールパネルでやってもらうことにして、Aeroの無効化はやっておこう。全然面倒ではないし。最近は東方もやってるし…。

HRESULT WINAPI DwmEnableComposition(
    UINT uCompositionAction
);

Parameters
uCompositionAction
DWM_EC_ENABLECOMPOSITION to enable DWM composition; DWM_EC_DISABLECOMPOSITION to disable composition.

サポート Vista以降
ヘッダ dwmapi.h
インポートライブラリ dwmapi.lib
実体DLL dwmapi.dll

まあXP以降対応にしようとなると、リンカにdwmapi.libを渡してしまうとexeのインポートテーブルにdwmapi.dllが追加され、XPだとそんなものないのでexeが起動できなくなってしまいますね。で、案の定みなさんLoadLibrary()+GetProcAddress()をしているようです。XPだとLoadLibrary()で"dwmapi.dll"が見つからずロードできないから何もしなくていいや、って感じみたいです。
ここで…、GetProcAddress()の返り値を適切な関数ポインタ型にキャストしないといけないのですが…。
問題:さっきのDwmEnableComposition関数へのポインタが代入できるポインタ変数を定義しなさい。

// UINTが引数で返り値がHRESULTで呼び出し規約がWINAPI
// (__stdcall)の関数へのポインタpf
HRESULT (WINAPI *pf)(UINT);
// typedefするなら
typedef HRESULT (WINAPI *FuncType)(UINT);
// pを(Cスタイル)キャストするなら
(HRESULT (WINAPI *)(UINT))p

関数ポインタの書き方は基本的にイミフで、まあそりゃあ一応規則はある(funcnameをカッコで括って中でポインタの*を前につけて(*pfunc)に変えるとだいたいできるよ)から慣れれば耐えるものの、__stdcallなどの変な拡張指定が入ってくるとグーグル先生に頼る羽目になる。
しかし、ここでC++0xで追加されたdecltypeをdwmapi.hに宣言されているDwmEnableComposition関数に使うと…!

decltype(DwmEnableComposition) *pf;
// 変数宣言にもキャストにも使うならtypedef安定だろう…
// これならreinterpret_castしても読めそう
typedef decltype(DwmEnableComposition) *FuncType;
FuncType pf = (FuncType)::GetProcAddress(...);

decltypeはカッコの中の式を評価した場合の型を表す新機能です。decltype(DwmEnableComposition)で関数の型HRESULT WINAPI (UINT)が得られるので、それへのポインタとするだけです。これでHRESULT (WINAPI *)(UINT)がすごく分かりやすく得られます。ヘッダをインクルードするだけでOK!
というのがググったら出てきましたとさ。めでたしめでたし。
decltype、VCで使えるのかと思ったが書いたら色が変わった。普通に使えた。C++0xに完全に対応してるわけでは全くないし、むしろ何が使えるのか把握しないと…。

*1:先入れ先出し待ち行列、キューに入れること。デフォルトで3人待たせるから3F遅延。