動画で見る Windows 7

Professional Developers Conference 2008の2日目、WindowsおよびWindows Live担当上席副社長スティーブン・シノフスキー氏の基調講演によって、Windows Vistaの後継となるWindows 7の概要が明らかになった。

PDC 2008 の基調講演の様子は動画で公開されています.

以下,Windows 7 デモ部分の抜粋.
http://msstudios.vo.llnwd.net/o21/presspass/zune/PDC_Day2_clip2_Zune.wmv
全画面でみるなら,以下の高解像度版がおすすめ.

また,以下に文字起こししたものが公開されてます.英語の聞き取りに不安がある方はどうぞ.

DirectDrawウィンドウモードでのティアリング回避 (1)

DWMの無いXPでも垂直同期を待てばティアリングを防ぐことは出来ます。

ただしウィンドウモードで垂直同期を待てるようになったのはつい最近で、DirectX9.0になってからです。

あたりから始まって色々コメントを書いた成り行き上,ちょいとばかり昔の資料を漁って書いてみます.と言っても私もそんなに詳しくないので,嘘を書いちゃった場合はひげねこさんの突っ込みに期待.

まずは基礎編から.

DirectDraw ウィンドウモードでのティアリング回避アルゴリズム

実際のところ DirectDraw ウィンドウモードでのティアリング回避アルゴリズムは,DirectDraw 初期から変わっていないはずです.
アルゴリズムのコンセプトは単純で,デスクトップへの BilBlt が走査線通過中に行われないようにタイミング調整を行うというもの.走査線のタイミングに合わせるのは DirectDraw 初期の頃から存在する GetScanLine / WaitForVerticalBlank が使えます.即時的な BitBlt には,いわゆる NOWAIT 系のフラグが利用できて,ドライバ内でブロックされてタイミングが狂う可能性を減らすことができます.

ドライバモデルについて

Windows 2000Windows XPWindows Server 2003 は同じドライバモデルを利用しています.このドライバモデルは,DirectDraw のファンクションを基本とし,建て増し的にコマンドバッファ方式の DrawPrimitive 機能を追加した構造をしています.

さて,Windows 2000 から Windows XPWindows Server 2003 までの間に DirectX 6 から DirectX 9までがリリースされましたが,実はこの間にドライバモデルは変わっていません.一般には DirectX 8 で DirectDraw と Direct3D が統合されたと思われていますが,あれは単に API 層でラップしただけです.実際には Direct3D のランタイムライブラリがラッパー化していき,内部でせっせと DirectDraw 相当のドライバファンクションを使い分けるようになっただけでした.
例として,先ほど登場した GetScanLine を見てみましょう.Windows 2000 のドライバモデルで,ドライバは以下のファンクションを提供します.

Windows 2000 から Windows Server 2003 までの間で,この仕組みは基本的に変わりません.一方でアプリケーションからは,走査線位置の取得方法が複数あるように見えます.

しかしこれらの API,同一のドライバファンクションの異なるラッパーに過ぎません.

(続く)

ゲームプレイ中のスクリーンセーバやディスプレイ休止を回避する方法

主にゲームパッドのみでプレイできる PC ゲームでは,ゲームプレイ中にスクリーンセーバが起動したりディスプレイの電源が切れたりして「むがー!」ということがあります.

これはプログラム側の対応である程度防止できますので,その辺のメモでも.
もっとも保守的で無難な方法は,ウィンドウメッセージのトラップです.WM_SYSCOMMAND メッセージのうち SC_MONITORPOWER と SC_SCREENSAVE を DefWindowProc に渡さないようにします.

Once a screen saver is chosen, Windows monitors keystrokes and mouse movements and then starts the screen saver after a period of inactivity. However, Windows does not start the screen saver if any of the following conditions exist:

  • The active application is not a Windows-based application.
  • A computer-based training (CBT) window is present.
  • The active application receives the WM_SYSCOMMAND message with the wParam parameter set to the SC_SCREENSAVE value, but it does not pass the message to the DefWindowProc function.

Windows Vista and later: If password protection is enabled by policy, the screen saver is started regardless of what an application does with the SC_SCREENSAVE notification.

case WM_SYSCOMMAND:
    switch( ( wParam & 0xFFF0 ) )
    {
        case SC_MONITORPOWER:
        case SC_SCREENSAVE:
            return 0; // DefWindowProc は呼ばない
            break;
        
        ...以下略

これだけでかなりのケースは対応できます.以下各論.

ゲームパッドの操作でディスプレイ休止は阻止できることもある

これは手元の Windows Vista SP1 (x86) + Xbox 360 パッド (純正ドライバ) で確認したケースですが,Xbox 360 パッドの操作中はディスプレイの休止は発生せず,またディスプレイ休止状態に Xbox 360 パッドを操作するとディスプレイ休止状態から回復することを確認しています.
ただややこしいことに,スクリーンセーバには効果がないようでした.

SetThreadExecutionState API ではスクリーンセーバを阻止中断できない

This function does not stop the screen saver from executing either.

(2009年10月12日追記)

  • 一度実行してしまったスクリーンセーバを SetThreadExecutionState で中断することはできない.
  • スクリーンセーバ起動防止に使うためには,SetThreadExecutionState(ES_DISPLAY_REQUIRED) のように ES_DISPLAY_REQUIRED 単独で指定する必要がある.ES_DISPLAY_REQUIRED | ES_CONTINUOUS 等は効果がない.

参考:

WM_SYSCOMMAND の wParam の扱い

本来 0xfff0 と & を取るのが正しい……はず.

In WM_SYSCOMMAND messages, the four low-order bits of the wParam parameter are used internally by the system. To obtain the correct result when testing the value of wParam, an application must combine the value 0xFFF0 with the wParam value by using the bitwise AND operator.

でも KB とか見ると結構適当.

2. あるアプリケーションが起動中にスクリーンセーバを起動させない方法

スクリーンセーバの機動を止めるには WM_SYSCOMMAND の wParam == SC_SCREENSAVE の時 return 1 を返してください。

case WM_SYSCOMMAND:
    if ( wParam == SC_SCREENSAVE )
    {
        return 1;
    }
    return (DefWindowProc(hWnd, message, wParam, lParam));

何も考えずに wParam == SC_SCREENSAVE とかやってますけどいいんすかねこれ.みんなコピペ大好きですよ?

WM_SYSCOMMAND トラップ時の返値は 0 にすべきか 1 にすべきか

2. あるアプリケーションが起動中にスクリーンセーバを起動させない方法

スクリーンセーバの機動を止めるには WM_SYSCOMMAND の wParam == SC_SCREENSAVE の時 return 1 を返してください。

case WM_SYSCOMMAND:
    if ( wParam == SC_SCREENSAVE )
    {
        return 1;
    }
    return (DefWindowProc(hWnd, message, wParam, lParam));

ここだけ読むと 1 を返すことが重要なように読めますが……

Return Value

An application should return zero if it processes this message.

Once a screen saver is chosen, Windows monitors keystrokes and mouse movements and then starts the screen saver after a period of inactivity. However, Windows does not start the screen saver if any of the following conditions exist:

  • The active application is not a Windows-based application.
  • A computer-based training (CBT) window is present.
  • The active application receives the WM_SYSCOMMAND message with the wParam parameter set to the SC_SCREENSAVE value, but it does not pass the message to the DefWindowProc function.

戻り値としては 0 の方が適切だし,スクリーンセーバ避けには DefWindowProc を呼ばないことの方が重要にも読めます.一方で,昔の Windows だと違った系の可能性も.

Windows Vista 以降,スクリーンセーバのパスワードロックが有効だと,メッセージトラップでスクリーンセーバを阻止できない

Windows Vista and later: If password protection is enabled by policy, the screen saver is started regardless of what an application does with the SC_SCREENSAVE notification.


Windows Vista 以降,ここにチェックが入っていると,SC_SCREENSAVE トラップを行ってもスクリーンセーバは起動します.
これを気にするのであれば,ダミーのキーイベントかマウスイベントを発生させることを検討した方が良いのかもしれず.

イベント案内: 同人ゲームの潮流①「同人ゲームの過去、現在、未来」(DigraJ公開講座08年09月期)

2008年9月26日(金) 18:00〜20:00,東京大学本郷キャンパス 工学部新2号館9階 92B教室にて.
折角なので行ってみます.残席あと 30 ぐらい.

D3DX のベクトル型の罠

久しぶりにコードの出てくる Direct3D 話.
次のコードはコンパイル可能か?

#include <d3dx9.h>
#include <iostream>

int main()
{
    D3DXVECTOR2 vec2(0.0f, 1.0f);
    D3DXVECTOR3 vec3 = vec2;
    std::cout << vec3.z;

    return 0;
}









答え.可能.
まず D3DXVECTOR2 を見てみる.

//--------------------------
// 2D Vector
//--------------------------
typedef struct D3DXVECTOR2
{
#ifdef __cplusplus
public:
    D3DXVECTOR2() {};
    D3DXVECTOR2( CONST FLOAT * );
    D3DXVECTOR2( CONST D3DXFLOAT16 * );
    D3DXVECTOR2( FLOAT x, FLOAT y );

    // casting
    operator FLOAT* ();
    operator CONST FLOAT* () const;
    (以下略)

次に D3DXVECTOR3 を見てみる.

//--------------------------
// 3D Vector
//--------------------------
#ifdef __cplusplus
typedef struct D3DXVECTOR3 : public D3DVECTOR
{
public:
    D3DXVECTOR3() {};
    D3DXVECTOR3( CONST FLOAT * );
    D3DXVECTOR3( CONST D3DVECTOR& );
    D3DXVECTOR3( CONST D3DXFLOAT16 * );
    D3DXVECTOR3( FLOAT x, FLOAT y, FLOAT z );

    // casting
    operator FLOAT* ();
    operator CONST FLOAT* () const;
    (以下略)

つまり,

    D3DXVECTOR3 vec3 = vec2;

    D3DXVECTOR3 vec3(static_cast<float*>(vec2));

と解釈される.
そして,D3DXVECTOR3 のコンストラクタは,大方の予想通り,読むべきではないところまで float だと思って読み進める.
嫌すぎる.
嫌すぎるので,私は D3DX で始まる構造体の CONST FLOAT* のみを受け取るコンストラクタ explicit を付け加えている.個人の環境でのみだけど.
そしてもしかなうことなら,DirectX SDK の今後のリリースでこれらのコンストラクタが明示的コンストラクタに修正されることを希望する.その困難さは承知の上で.
あちこちのプロジェクトでビルドが通らなくなりそうだけど,そのコンパイルエラーの何割かは,きっと本当にエラーが相応しい.

明示的なコンストラクタ

1変数を受け取るコンストラクタにはexplicitキーワードを使用してください。

Windows 7 と GDI/GDI+ の GPU アクセラレーションの可能性

PDC 08 アジェンダに対する各地の反応

いやまあ私もその辺の話は全然知らないのですが.
ちなみに DWM や WPF といった技術・製品は DirectX チームの管轄ではないので,MVP Summit 等ではなかなか情報が集まりにくかったりします.まあ落ち着いて考えてみればそんなものでしょうけど.おすすめの情報源については後ほど.

GDI/GDI+ の GPU アクセラレーションは可能か?

んでまあ GDI/GDI+ が Direct3D で有効にアクセラレーション出来るかどうかですが,個人的には「やれば出来るかもしれないけど,それ意味あんの?」という印象です.つまり実装が難しい云々という話ではなくて,そもそも有効な場面があまり思い付かない,という根本的なところが気になります.
現在のメジャーな Web ブラウザや Flash (10 より前) / Silverlight の描画方式を思い出しましょう.近年のブラウザや Flash/Silverlight は,多くのアプリケーションシナリオをこなすのに十分なグラフィックス性能を得たように思うのですが,それが実は CPU 描画であるという点について皆様いかがお考えでしょうか?
Web ブラウザや Flash は,Windows Vista 登場以前から,クライアント領域全体のイメージをソフトウェアで描画していました.描画処理は CPU - メインメモリ間で完結しており,当然ながらグラフィックス性能は CPU クロックに比例します.VRAM 転送が発生するのは,クライアント領域の内容が完成し,実際に画面に表示する最後の部分だけです.
GPU 描画という響きに漠然と期待を持っている人は,『Flash Player 10 の GPU サポート機能について - akihiro kamijo』という記事がおすすめです*1
GPU による GDI/GDI+ のアクセラレーションは,可能だとしても効果があるかよく分からない.そう認識しておいた方が,現物を見たときに変ながっかり感を受けずに済むかもしれません.仮に GPU による GDI/GDI+ の描画支援が導入されたとしても,従来から完全ソフトウェア描画を行っていたソフトウェア (しかしユーザにその見分けはつかないでしょう) には何の影響もありませんし,GDI/GDI+ アプリケーションなら必ず高速化すると約束できるほど世の中甘くもないでしょう.私はそういう認識です.
とはいえ,PDC 2008 で紹介すると言っているので,Microsoft は何らかの活用法は見つけたのでしょう.個人的には,割と枝葉末節なチューニングなんじゃないかなぁと予想しています.例えば ClearType 描画のフォントキャッシュ生成に GPU を活用するとか,投機的なレンダリング (CPU と GPU で同時に描画を行って,描画結果の読み出しが発生したら GPU 描画をキャンセル) とか.もちろん,私が見落としている何かすごい応用方法があるのかもしれません,
(追記: 以下の動画再生支援の可能性についてはコメント欄も参照のこと)
ちなみに,GPU の活用法として真っ先に考慮・評価されるべきは,動画のデコードでしょう.こればっかりは,サーバサイドだけに任せてしまうわけには行きません.通信路のデータ流量を減らす必要があるため,圧縮は不可避であり,クライアントサイドで働く何かが必要です.GPU による動画再生支援はだいぶ一般的になってきたかと思いますが,普及率・効率ともにまだまだ伸びしろはあると思っています.みんなムービー好きですしね.

おすすめの情報源

次世代 Windows のアーキテクチャに関する情報は,WinHEC で集めるのが一番です.と思ったら,今年の WinHEC は PDC 2008 の翌月なんですね.となれば PDC で情報を解禁して,本当にその情報を必要としている開発者向けには WinHEC 2008 で説明,という流れなのかもしれません.

あとは,DWM のときのように Greg Schechter 氏の blog もチェックという感じでしょうか.PDC から WinHEC にかけて何か動きがあるかもしれませんね.

*1:『[http://cpplover.blogspot.com/2008/05/flash-player-10gpu.html:title=Flash Player 10のGPUアクセラレーションについての誤解 - 本の虫]』で知りました.感謝.

Microsoft が提供する C/C++ ヘッダファイルにおける事前条件・事後条件の充実っぷりは異常

ふむ.
むしろ Microsoft の開発現場で,一番 DbC が流行っているのが Visual C++ 関連だと思うのですが.Header Annotations とか SAL AnnotationsC/C++ コード障害の検出と修正 等.
最近の Visual C++ のインクルードファイルや Windows SDKs 以下のインクルードファイルを見ていても,事前条件・事後条件の充実っぷりは異常.その規模は既に COM 全盛期の IDL を凌ぎます.
あれは手で書いているんでしょうかねぇ? 何か DSL から C/C++ のヘッダファイルを自動生成していてもおかしくない気もしますが,はて.

整数範囲注釈

そういえば Visual C++ 2008 SP1 のコード検証は,Header Annotations の整数範囲注釈を活用していないように見えるのですが,誰かその辺の事情をご存じだったりしませんかね?
整数範囲注釈は,例えばこんな感じで引数の事前条件・事後条件を記述するものです.他にも,フィールドや関数戻り値の範囲に注釈を記述することも可能です.

#include <specstrings.h>
#include <iostream>

// 事前条件: i は 64 以上 127 以下
void foo( __in_range(64, 127) int i )
{
    std::cout << i;
}
// 事前条件: i は 50未満
void bar( __in_range(<, 50) int i )
{
    std::cout << i;
}
// 事後条件: iptr は NULL ではなく,*iptr は 11
void buz( __notnull __deref_out_range(==, 11) int* iptr )
{
    *iptr = 10;
}

詳しい文法については specstrings.h にコメントで書かれています.
整数範囲注釈は,Windows SDK 6.0 以降でちらほら使われ始め,最近であれば Direct3D10 のヘッダファイルでも使用されています.例として ID3D10Device::GSSetConstantBuffers の宣言.

virtual void STDMETHODCALLTYPE GSSetConstantBuffers( 
    /*  */ 
    __in_range( 0, D3D10_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT - 1 )  UINT StartSlot,
    /*  */ 
    __in_range( 0, D3D10_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT - StartSlot )  UINT NumBuffers,
    /*  */ 
    __in_ecount(NumBuffers)  ID3D10Buffer *const *ppConstantBuffers) = 0;

ただ,手元の環境 (Visual Studio Team Suite 2008 SP1) では,整数範囲制約への違反が静的コード検証で指摘されたことが一度もないんですよな.いまのところ文字通り「注釈」だけなのかなと.__notnull とかは,静的コード分析で活用されているのを見たことがあるんですけどねぇ.

「Win32 API といえばシステムハンガリアン」という印象で止まっていませんか?

Win32 API といえばシステムハンガリアン,というのは比較的有名かと思いますが,その印象で時間が止まっている人は,もう一度最近のヘッダファイルを読み直してみることをおすすめします.今では,C/C++ の型から区別が付かない数多くの情報がヘッダファイルに記述されるようになっており,ヘッダファイルは MSDN Library を補う第二のドキュメントになりつつあります.
時代も徐々に変わるのですよ.
将来的には,ドキュメント自動生成や,IntelliSense との統合なんかも期待ですな.