未来の標準は現在のブラウザ実装を制約するか

  • qnighy qnighy DirectXって…標準の意味わかってんのかこいつら 2009/11/19

Internet Explorer 6 以前の DirectX Filter みたいなのと誤解しているのならアレですが,(まだドラフト段階の) 標準と (現在初期実装段階にある製品の) 実装技術が切り離せるかという切り口で見ると結構奥深い話かもしれません.今後たとえば WebGL が標準として意味を持つのであれば,OpenGL をブラウザの実装技術の中核にしておいた方が都合が良いという議論はありえると思います.
WebGL の場合,それが OpenGL コマンドをそのまま流用したような命令形態であることがポイントとなります.仮にブラウザのレンダリングエンジンを Direct3D / Direct2D ベースで行っていた場合,WebGL サポートのためには 1)部分的に OpenGL を使用する 2) Direct3DWebGL の処理をエミュレートする,といった対応が必要となります.いずれの場合も,最初から OpenGL ベースでブラウザを実装するよりは複雑性が増すこととなるでしょう.

WebGLは、3Dグラフィックスを活用したWebアプリの実現を目指し制定された、OpenGL準拠のAPIセット。OpenGL関連であることからうかがえるように、各種グラフィックス技術の標準化を進める団体Khronosグループが仕様の策定を進めている。今回、Safariなど多数のWebブラウザに採用されているHTMLレンダリングエンジン「WebKit」が、WebGL実装を含む開発者向けテスト版の配布を開始したことを受け、どのように表示されるかなどエンドユーザからの目線でテストを実施した。

WebGL最大の特徴は、その3D描画をプラグインなしに実現できることだろう。これまでもWebブラウザ上に3Dグラフィックスを表示する試みは存在し、現在もGoogleが「O3D」の開発を進めるなど動きはあるが、Web標準ではない。プラグインの力を借りず、次世代のWeb標準たるHTML5でサポートされることが、WebGLが注目を集める大きな理由だ。

HTML5 の一部として WebGL がサポートされるかのように海上氏は書かれていますが,果たしてこれが本当かどうかはよく分かりません.少なくとも 『HTML 5 - W3C Working Draft 25 August 2009』にそれらしい記述はみつけられませんでした.とはいえ,今後 HTML5 の延長線上に WebGL が "標準" の地位を手に入れる可能性はあります.このシナリオは Microsoft にとってあまり好ましくないものかもしれません.そして,未来の標準「勧告」に合わせようとすると,現在の技術選定の幅が狭められかねないという構図はちとアレです.
もちろん,WebGLDirect3D/2D が絶対に共存不可能ということはなく,実際現場レベルではこんな話もあります.以下は,WebKit (WebCore) が現在実装中の CSS 3D Transforms Module Level 3 (W3C Working Draft 20 March 2009) (これもまだ Working Draft) を,Windows でどう行うかに関するものです.

現在コミットされているパッチを見る限り,WebKit は,CSS 3D Transforms に Direct3D を使う方向で動いているようです.この流れに対し Brent Fulgham 氏は将来的に WebGL 実装が必要となったときの懸念を示しましたが,Apple の Chris Marrin 氏は「Both OpenGL and Direct3D can handle multiple contexts without any trouble.」と述べ,(WebGL のために必要ならば) OpenGLDirect3D の同時に使用することも念頭に置いているようでした.とまあ外野としては「それが世界の選択か」とつぶやく前にとりあえずソースと関係者の議論を読むってのがいいのかもしれません.
ちなみに,WebKit での DirectWrite サポートも,既にトラック用のバグは登録されています.

同様に Mozilla でも DirectWrite に関する議論は開始されています.流れを見るに Mozilla の方が先に何か出てくるかもしれません.

追記

なんかこんな記事が出ていたのを発見.

追記2

Firefox の関係者この発言も興味深い.WebGL が,Direct3D のような他の Native 3D API でも実装できることを確認したいとのこと.

We still have some ways to go, as there are issues in shader security and portability, not to mention figuring out what to do on platforms where OpenGL is not available. (The latter is an interesting problem; we're trying to ensure that the API can be implementable on top of a non-GL native 3D API, such as Direct3D, so that might be one option.) But progress is being quickly made.

DirectX Graphics フルスクリーンモードと窓使いの憂鬱: 解決編

サークルで作成しているゲームについて「フルスクリーン環境で実行した後にゲームを終了すると一般保護違反が発生する」という症状が報告されて,色々調べてみたところ原因は『窓使いの憂鬱』にありました.どおりでこちらの環境で再現しなかったわけです.実際『窓使いの憂鬱 Ver.3.30』を常駐させることで問題を再現できることを確認しました.

多くの場合こういう現象は「相性問題」という便利な言葉で真実に蓋をされてしまいがちですが…たまには「解」でもご覧あれ.

すんません,これ「相性問題」でした.今更ながらにコールスタックを眺め直していたら,こちらのゲーム側に Win32 ウィンドウのリークがありそうな気がしてきて実際ソースを読み直したらまさにその通りだったという……
窓使いの憂鬱』を一方的に原因扱いしてしまって申し訳ありません.また,以前質問を受けたときに気付けていれば id:applet_at_h さんにこれほどお時間を取っていただくこともなかったわけで,本当にご迷惑をおかけしました.お詫び申し上げます.



原因が分かったので問題の詳細を定義し直しておきます.
のどか - チケット #19267: DirectX全画面アプリ終了時に、アプリケーションエラーを引き起こす。』は,実際には Direct3D のフルスクリーンモードは直接は関係が無く,以下のような問題と言い換えることができます.

WinMain 終了時に DestroyWindow されていないウィンドウが残っていて,かつそのウィンドウに新着送信メッセージ (いわゆる SendMessage 系で送信されるメッセージ) がキューイングされている状態でアプリケーションが終了した場合に,『窓使いの憂鬱』系列のフック DLL が常駐していると終了ルーチンが不安定になる.

もっとも,そもそもアプリケーションがウィンドウをリークさせてしまっているので,もとより安定とは言い難い終了なわけですが.
『窓使いの憂鬱』の有無で挙動が変わるのは,『窓使いの憂鬱』のフック DLL が DllMain で SendMessageTimeout を使用しているためです.SMTO_BLOCK を使用せずに呼び出された SendMessageTimeout は,送信先ウィンドウからの返信を待つ間,キューイングされている新着送信メッセージをせっせとディスパッチしていきます.この結果,単にリークしているだけだったウィンドウにメッセージが配信されるように変化します.
『窓使いの憂鬱』が常駐することで発生するようになったこのメッセージディスパッチは,WinMain 終了後,しかも DllMain という非常に扱いの難しい状態から行われるため,呼び出されたウィンドウプロシージャで問題が起きる可能性は非常に高くなります.WinMain 終了後ということで,多くのリソースは解放されてしまっているでしょうし,DllMain 内ということで LoaderLock の問題もあります.結果として,リソースリーク (とはいえすぐに OS に回収される) をともなうサイレントな終了処理が,クラッシュが発生しユーザーが気付きやすい終了処理へと変化する,というのがことのあらましでした.
以下に問題を再現させるミニマムコードを掲載します.「のどか 4.10」と Windows XP SP3 および Windows 7 x64 版にて,WinMain 終了後にウィンドウプロシージャが呼び出されることを確認しました.

#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <tchar.h>

// Aux Library は以下からダウンロードできる.
// http://go.microsoft.com/FWLink/?LinkId=85311
// あるいは,Windows 7 SDK にも Aux Library は同梱されており,こちらを使用しても良い.
// http://go.microsoft.com/fwlink/?LinkID=150217
#include <Aux_ulib.h>

// Visual C++ 2005 でビルドする場合は以下の HotFix を適用する
// http://support.microsoft.com/kb/949009
#pragma comment (lib, "Aux_ulib.lib")

DWORD WINAPI threadProc(void* arg) {
  HWND window_handle = reinterpret_cast<HWND>(arg);
  ::SendNotifyMessage(window_handle, WM_NULL, 0, 0);
  return 0;
}

bool g_unexpected = false;

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  bool debug_break = false;

  if (g_unexpected) {
    // WinMain 終了後のコールバックは通常予想していない
    // -> クラッシュする可能性が高い
    ::OutputDebugString(_T("Unexpected callback was detected!\n"));
    debug_break = true;
  }

  BOOL has_loader_lock = FALSE;
  // http://msdn.microsoft.com/en-us/library/bb432187.aspx
  ::AuxUlibIsDLLSynchronizationHeld(&has_loader_lock);
  if (has_loader_lock) {
    // DllMain からのコールバックは通常想定していない
    // -> クラッシュする可能性が高い
    ::OutputDebugString(_T("LoaderLock was detected!\n"));
    debug_break = true;
  }

  if (debug_break) {
    // 問題が存在する場合ここで break
    DebugBreak();
  }

  switch (uMsg) {
    case WM_DESTROY:
      ::PostQuitMessage(0);
      break;
  }
  return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}


int PASCAL _tWinMain(HINSTANCE hinst, HINSTANCE, LPTSTR, int)
{
  const TCHAR* windowClassName = _T("SendMessageTest");
  const TCHAR* windowName = _T("Test Window");

  if (!::AuxUlibInitialize()) {
    return 1;
  }

  WNDCLASS wc      = {0};
  wc.lpfnWndProc   = WndProc;
  wc.hInstance     = hinst;
  wc.lpszClassName = windowClassName;
  if (!::RegisterClass(&wc)) {
    return 1;
  }

  // このウィンドウをリークさせる
  const HWND window_handle = ::CreateWindowEx(0, windowClassName, windowName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);

  // リークするウィンドウに別スレッドから SendMessage 系列でメッセージを送っておく
  DWORD thread_id;
  HANDLE threadHandle = ::CreateThread(NULL, 0, threadProc, window_handle, 0, &thread_id);
  ::WaitForSingleObject(threadHandle, INFINITE);
  ::CloseHandle(threadHandle);

  // これ以降 WndProc が呼ばれなければ,ウィンドウのリークは表面化しない
  g_unexpected = true;

  // 以下のコードで問題を使用することで
  // 『窓使いの憂鬱』が常駐しない環境でも問題をエミュレーションできる
#if 0
  DWORD result = 0;
  // SMTO_NORMAL フラグが指定されているため,このスレッドのウィンドウに対して
  // 送られてきていた送信メッセージキューの配信も発生する
  ::SendMessageTimeout(::GetDesktopWindow(), WM_NULL, 0, 0, SMTO_ABORTIFHUNG | SMTO_NORMAL, 5000, &result);
#endif

  return 0;
}

送信メッセージ (PostMessage と SendMessage の違い) に関しては,『メッセージ管理 - EternalWindows』および『Windowsプログラミングの極意 歴史から学ぶ実践的Windowsプログラミング!』の第 15 章を参照してください.



さて,問題の解決方法ですが,まずアプリケーション側がきちんとウィンドウを破棄していれば問題は発生しません.この点,特に本当にご迷惑をおかけしました.
窓使いの憂鬱』側で取ることができる対応としては,DllMain 内ということを考慮して,不用意なメッセージディスパッチを極力避けるという方法が考えられます.とりあえず 3 つほど挙げてみます.下に行くほどうまくいきそうな感じで.

SendMessageTimeout + SMTO_BLOCK

/nodoka/trunk/nodoka/dll/hook.cpp (revision 105) では SendMessageTimeout が SMTO_NORMAL 付きで使用されていますが,これを SMTO_BLOCK に置き換えることで,SendMessageTimeout 内部からの予期せぬディスパッチは避けることができます.ただし,この変更による副作用も考えられますので,これで解決といえるかはまだ分かりません.

SendNotifyMessage

SendMessageTimeout を SendNotifyMessage に置き換えられるかもしれません.SendNotifyMessage は WM_COPYDATA と併用できませんが,/nodoka/trunk/nodoka/nodoka/hook.h (revision 105) を見る限り,送信しているデータは以下の 3 つだけのようです.これだけなら WM_USER + X の X,lParam, wParam で表現できそうです.

  • Notify::Type m_type
  • DWORD m_debugParam;
  • DWORD m_threadId;

SendNotifyMessage であれば,ノンブロッキングに処理できるため,レスポンスも良くなりそうです.

OpenThread

窓使いの憂鬱』/『のどか』本体の方のコード次第によっては,そもそも DLL_PROCESS_DETACH / DLL_THREAD_DETACH で Win32 メッセージを使った通信を行う必要をなくすことができそうな気もします.
ざっと見たところ,フック先プロセスで終了したスレッドの ID を,『窓使いの憂鬱』/『のどか』のサーバ側で使用しているようです.

窓使いの憂鬱」で、実施していることは、Hookしているアプリが終了して、DLLデタッチした際、そのDLLのGetCurrentThreadId()値を、EXEに通知し、m_detachedThreadIds list を更新するというもの。それを、そのまま「のどか」も「Yamy」も踏襲している。

この目的のために DLL_PROCESS_DETACH / DLL_THREAD_DETACH のたびにフック DLL からサーバに SendMessageTimeout で通知していたのがオリジナルのオリジナルの『窓使いの憂鬱』で,それが派生版にも引き継がれているとのこと.
ここですが,もし Windows 2000 以前を非サポートにしてよいのであれば,OpenThread を使ってサーバからスレッドハンドルを開き,そのシグナル状態を監視することでも可能かもしれません.DLL_PROCESS_DETACH / DLL_THREAD_DETACH はプロセスの異常終了等のため呼ばれないケースも存在しますし,外部からの監視で済むならそれに超したことは無いような気がします.
罪滅ぼしもかねてご参考までにということで.



参考文献.

GDI アクセラレーションについて整理する

WDK 公開から約1ヶ月遅れになってしまったが,公開されたドキュメントから,Windows 7 で復活する GDI アクセラレーションがどんなものか,かなりのところまで推測することができる.ドキュメントはすでにオンラインで誰でも読むことが可能になっている.

筆者も軽く目を通してみたが,WDDM 1.1 で新設される GDI アクセラレーションは,XPDM までで行われていた GDI アクセラレーションとだいぶ内容が異なりそうだ.その意味では,これを GDI アクセラレーションの「復活」と呼ぶのは語弊があるかもしれない.
何より印象的だったのが,GDI アクセラレーションと称してドライバが受け取るコマンドが以下の 7 種類 (うち,将来の拡張用のエスケープコマンドが 1 種類) しか存在しなかったことだ.これは XPDM 時代の GDI ドライバファンクションの数に比べて圧倒的に少ない.

The DXGK_RENDERKM_OPERATION enumeration indicates the type of GDI hardware-accelerated rendering operation to perform when the DxgkDdiRenderKm function is called.

typedef enum _DXGK_RENDERKM_OPERATION {
  DXGK_GDIOP_BITBLT         = 1,
  DXGK_GDIOP_COLORFILL      = 2,
  DXGK_GDIOP_ALPHABLEND     = 3,
  DXGK_GDIOP_STRETCHBLT     = 4,
  DXGK_GDIOP_ESCAPE         = 5,
  DXGK_GDIOP_TRANSPARENTBLT = 6,
  DXGK_GDIOP_CLEARTYPEBLEND = 7,
} DXGK_RENDERKM_OPERATION;

定義されている OpCode は,いわゆる BitBlt (矩形転送) 系に絞られている.線分や円の描画に関しては影も形もない.
それでは線分や円の描画をどうするかだが,筆者はサーフェスロックによるソフトウェア描画ではないかと予想している.例えばこちらの D3DKMDT_GDISURFACETYPE 列挙体 が興味深い.

The D3DKMDT_GDISURFACETYPE enumeration indicates the type of lockable surface that is used by the Desktop Windows Manager (DWM) for redirection.

typedef enum _D3DKMDT_GDISURFACETYPE {
  D3DKMDT_GDISURFACE_INVALID            = 0,
  D3DKMDT_GDISURFACE_TEXTURE            = 1,
  D3DKMDT_GDISURFACE_STAGING_CPUVISIBLE = 2,
  D3DKMDT_GDISURFACE_STAGING            = 3,
  D3DKMDT_GDISURFACE_LOOKUPTABLE        = 4,
  D3DKMDT_GDISURFACE_EXISTINGSYSMEM     = 5,
} D3DKMDT_GDISURFACETYPE;

考えてみれば,サーフェスロック方式の採用は合理的だ.
既報のように,Windows 7 DWM でのウィンドウイメージの保持は DXGI サーフェイスに一元化される.このサーフェスが,GDI 描画時に一時的にロックされ,CPU から直接読み書き可能なメモリ空間にマッピングされるとしよう.いったん通常のメモリ空間にマッピングされてしまえば,あとは従来のソフトウェア描画と同じである.実際,メインメモリ上に描画するソフトウェアレンダラは Windows Vista で実装済みだ.違いがあるとすれば,Vista DWM のころは必要だったソフトウェアでのピクセルフォーマット変換が,WDDM 1.1 では不要なことか.WDDM 1.1 は GDI 互換のピクセルフォーマットのサポートを義務付けたので,ピクセルフォーマット変換は最終合成段階にハードウェアで行えばよい.
以上が,WDK のドキュメントをざっと眺めてみての筆者の予想である.
自分でベンチマークを書いてテストできる人は,これらのことに注意して調査してみると何か収穫があるのではなかろうか.矩形処理以外の GDI 描画処理のスループットを計測してみて,もしそこに塗りつぶし面積に比例するような傾向が見られれば興味深い.それはソフトウェアレンダリングで説明が可能な振る舞いだ.

Windows7 の GDI 系ネイティブ API

実装詳細を占うという意味では,Windows SDK for Windows 7 and .NET Framework 3.5 SP1: BETA に付属する ntgdi.h も興味深い.前回リリースされた Windows SDK *1 に含まれる ntgdi.h との差分をとったところ,以下の API の追加が確認できた.

  • NtGdiGetFontFileData
  • NtGdiGetFontFileInfo
  • NtGdiCreateBitmapFromDxSurface
  • NtGdiBeginGdiRendering
  • NtGdiEndGdiRendering
  • NtGdiSfmGetNotificationTokens
  • NtGdiSfmRegisterLogicalSurfaceForSignaling
  • NtGdiDwmGetHighColorMode
  • NtGdiDwmSetHighColorMode
  • NtGdiDwmCaptureScreen
  • NtGdiDdCreateFullscreenSprite
  • NtGdiDdNotifyFullscreenSpriteUpdate
  • NtGdiDdDestroyFullscreenSprite
  • NtGdiDdQueryVisRgnUniqueness
  • NtGdiHLSurfGetInformation
  • NtGdiHLSurfSetInformation

これらの API はいわゆるNT ネイティブ API に相当し,通常のアプリケーションからの使用が意図されたものではない.が,先ほどの WDK のドキュメントと併せて,Windows 7 のグラフィックスシステムの実装を考えるヒントになるだろう.
NtGdiCreateBitmapFromDxSurface などは,いかにも DWM が使いそうな響きである.

*1:Windows SDK for Windows Server 2008 and .NET Framework 3.5 (6001.18000.367)

Windows 7 のグラフィックスの変更点を整理する

(2009年2月9日追記)
GDI アクセラレーションについて整理する - NyaRuRu の日記』にて,公開された WDK のドキュメントを元に追加の考察を行っています.


基本的にはプレビュー版のWindows 7においても、日本語を利用することは可能だった。しかし、1つだけ大きな問題がある。それはAero Glassと日本語表示が必ずしも共存しないことだ。

図5はおなじみの日本語エディタ、秀丸Windows 7のプレビュー版で起動したところだ。見れば分かるように、メニューの表示がおかしい。「その他」のメニューの途中から日本語表示が普通なのは、まだカーソルがこのエリアまで至っていないことを意味している。カーソルを下に持って行くと、それに連れてメニュー上部のように日本語表示がおかしくなっていく。なお、この画面はBlue Badgeを適用した後のものだが、この現象はBlue Badgeの適用如何を問わない。こうした日本語表示の乱れは、メニューだけでなく、たとえばアプリケーションをインストールする際のライセンス条項等のダイアローグ、あるいはIMEでの変換中でも見られる。

さすがにこれでは、日本語が使えなくもない、と言っても使う気にはなれない。が、これを修正する手段が存在する。それはAero Glassを無効にし、Aero Basicにすることだ。図6は、図5のシステムのままAero Basicに切り替えたものだが、日本語の表示は完全に復旧している。通知領域にATOKのアイコンが見えるが、もちろんATOKの動作(この例はATOK2007だが)もほぼ問題ない。

ただ、Aero Basicにしてしまうと、起動中のアプリケーションのサムネイルが表示されなくなる(図7)など、Windows 7の魔法(新UI)が少なからず解けてしまう。これはこれで微妙な感じだが、現状ではやむを得ない。

どうして日本語の表示がうまくいかないのか。ハッキリと言えることは、フォントそのものはシステム中に存在するし、そのフォントを正しく読み出すこともできている、ということだ。そうでなくては、Aero Basicでも日本語が表示できなくなる。おそらく問題があるのはフォントのレンダリングとリフレッシュだろう。

Windows 7 の Pre-Beta はまだ触っていませんが,日本語表示が乱れるケースがあるというのは初めて聞く話でした.確かに面白い話ではあります.が,この後に続く考察はちょっと危ういように感じられました.記事中には GDI のハードウェアアクセラレーションに関しても混乱も見られます.
情報を整理する意味で,以下に私が知る範囲での Windows 7 のグラフィックスの変更点についてここでまとめておきます.

Windows Vista までのドライバモデル

まずはおさらいから.
Windows Vista では,新しいグラフィックスドライバモデルが導入されました.このドライバモデルは,WDDM (Windows Display Driver Model) と呼ばれています.と同時に,Windows XP 以前のグラフィックスドライバモデルは XPDM *1 と呼ばれるようになり,WDDM と XPDM の間の区別が行われるようになりました.
Windows Vista / Windows Server 2008 世代のカーネル (6.0 系) は,実際には WDDM と XPDM どちらとも組み合わせて使うことができます (排他利用).ただし,Windows Vista ロゴと絡めた Microsoft のブランディング戦略から,ほとんどのベンダは事実上 WDDM の使用を強制されています.一方で,サーバ製品での XPDM の延命は続いており,後述するように Windows Server 2008 R2 世代においても XPDM は選択肢のひとつとして残るようです.
WDDM は以下のようなコンポーネント間の依存関係をもたらします.

  • Direct3D 9Ex や Direct3D 10 のプログラミングモデルは WDDM に強く依存しています.これらの世代の Direct3D の機能であるプロセス間共有サーフェスや,デバイスロストの排除等は,ドライバモデルの変更によって初めて実現されています.これは,Direct3D 10 等を Windows XP にバックポート出来なくなった原因でもあります.
  • Windows Vista の新しいウィンドウマネージャである DWM (Desktop Window Manager) は,Direct3D 9Ex の機能を前提に設計されています.特にプロセス間共有サーフェスとデバイスロストの排除は,DWM の設計を容易にし,また DWM の安定性と高速化に大きく寄与しています.
  • 一方で,WPF (Windows Presentation Foundation) が要求するコンポーネントは,.NET Framework 3.5 SP1 の段階でも,高々 Direct3D 9 Shader Model 2.0 までとなっています.このことは,WPFプログラマに公開している機能が Shader Model 2.0 までに限定されている原因となっています.ただし,WPF の描画エンジン自体は Direct3D 9Ex をサポートしており,Direct3D 9Ex の新機能が GPU を使ったテキストレンダリングとして活用されています.また,D3DImage 使用時にも WDDM の恩恵を受けられます(Direct3D9 および WPF の相互運用性のパフォーマンスに関する考慮事項).

さて,GDI のハードウェアアクセラレーションについても整理しておきましょう.XPDM までのグラフィックスドライバは,Win32 GDI API とほぼ一対一に対応するドライバファンクションを公開していました (『Windows Vista での GDI/GDI+ 描画 - NyaRuRuの日記』).これらのドライバファンクションは,必須なもの,条件付きで必須ななもの,オプショナルなものの 3 種類に大別されており,グラフィックスチップのベンダは必要なドライバファンクションを実装することで GDI 命令を受け取ることができました.
ドライバによって実装されないファンクションは,OS によってエミュレートされます.このエミュレーションは,他のドライバファンクションを組み合わせて実現したり,いったんシステムメモリに読み出して CPU でエミュレートしたりと様々です.XPDM の時代から,必ずしも全ての GDI API がハードウェア処理されていなかったことに注意してください.特に,アルファチャネルを利用した合成処理はほとんどのケースでソフトウェア処理されていたと考えられます.
この状況は Windows Vista でさらに加速します.Windows Vista で導入された WDDM (WDDM v1) には,GDI のドライバファンクションが存在しませんでした.つまり,アプリケーションからの GDI API 呼び出しがドライバに渡されるルートそのものが無くなったことになります.これが,「Windows Vista で GDI アクセラレーションが廃止された」と言われる理由です.
なお,従来の DirectDraw オーバーレイをサポートするためのファンクションは,WDDM でも残されています (CreateOverlay).ただし フルスクリーンモードの Direct3D アプリケーションと DirectDraw 由来のオーバーレイの間に互換性がないのは変わらず,デスクトップコンポジションとオーバーレイは両立しません.WDDM v1.1 では,新しい DDI の追加という形で DWM と共存可能なオーバーレイを導入するようです.
また XPDM で動作する Windows Vista は,従来通りドライバの公開する GDI ファンクションを利用します.

Windows 7 でのドライバモデル

Windows 7 / Windows Server 2008 R2 (カーネル 6.1 系) では,WDDM のバージョンが上がり,WDDM v1.1 となります.Windows Vista 世代の WDDMWDDM v1 と呼ばれるようになります.
OS としての Windows 7 / Windows Server 2008 R2 は,WDDM v1.1, WDDM v1, XPDM の 3 種類に対応します.ただし Windows 7 をインストールして出荷される PC は,Vista 同様ロゴプログラムによって WDDM v1.1 が強く推進されていくことでしょう.一方で,Server Logo では XPDM は依然として許される方向にあるようです.

Phasing Out XPDM

  • XPDM will not get a Win7 client logo
  • Win7 UMPCs can ship with WDDM v1
  • XPDM allowed for Win7 Server logo
    • XPDM allowed to install on Win7
    • Signed drivers required for X64
    • Signed for Windows Server 2008 or Vista
    • Unsigned allowed only for x86

WDDM v1.1 の新機能はいくつかありますが,ここで重要なのは

  • DXGI フォーマットでの BGRA フォーマットサポートの義務化したこと
  • GDI ハードウェアアクセラレーションをサポート可能にしたこと

のふたつです.後者は,以前公開されていた Guidelines for Graphics in Windows 7 ではオプショナルな要求に読めますが,ロゴプログラムと絡めて恐らく事実上サポートが必須という方向になるでしょう.なお,上述のように Ultra Mobile PC (UMPCs) は依然として WDDM v1 とともに出荷が認められていることに注意してください.
さて,それでは何故 Windows 7 で GDI ハードウェアアクセラレーションが復活したのかを考えてみることにしましょう.

Windows Vista 世代の DWM の仕組み

  1. GDI アプリケーションでは,トップレベルウィンドウのクライアント領域サイズ × 4 bytes の共有メモリが,フレームバッファとして各アプリケーションのメモリ空間に確保される.(『DWM による描画の現場を押さえる - NyaRuRuの日記』)
  2. ウィンドウへの任意の GDI 描画は,ソフトウェアによって先ほどのフレームバッファに描き込まれる (GDI Redirection).フォーマット変換も同時に行われる.(歴史的に DDB への処理が BGRA フォーマットを仮定していた一方で,Direct3D では ARGB フォーマットが主流なため,どこかでフォーマット変換が必要になる)
  3. Direct3D アプリケーションでは,Direct3D Runtime によって必要なサイズの隠しサーフェスが作られ,バックバッファから (フロントバッファへ) の Present は,この隠しサーフェスにリダイレクトされる (Direct3D Redirection).これは Direct3D Runtime によって自動的に行われる.
  4. 共有メモリを通じて,各アプリケーションのクライアント領域イメージが dwm.exe のメモリ空間にマップされる (ARGB フォーマット)
  5. DWM は各アプリケーションのクライアント領域イメージから Direct3D サーフェスを作成する
  6. DWM はプロセス間共有サーフェスを利用して Direct3D アプリケーションの出力先に当たる隠しサーフェスを取得する.
  7. DWM はウィンドウの位置関係に基づいてデスクトップを描画する.(Flip を用いた画面更新を行い,ティアリングを回避する)

Windows Vista 世代の DWM の仕組みでは,GDI アプリケーションについて,システムメモリとドライバリソース (Direct3D サーフェス) に全く同じ画像が二重に保持されていることになります.

Windows 7 世代の (WDDM v1.1 フル機能サポート時の) DWM の仕組み (予想)

  1. GDI アプリケーションでは,トップレベルウィンドウのクライアント領域サイズと等しい BGRA フォーマットの Direct3D サーフェスが作成される.
  2. 何らかの形で GDI によるクライアント領域描画と BGRA サーフェスを結びつけ,それをドライバで処理させる.(例えば Direct3D サーフェスへの GetDC を利用?)

つまり,ドライバで管理されるサーフェスでありながら,GDI でも読み描きでき,かつ BGRA フォーマットの Direct3D Surface (DXGI Surface) としても使えるデュアルインターフェイスなものを可能にするのが WDDM v1.1 なんじゃないかと適当に予想しています.(Windows 7 に対応した WDK が入手できたら,もう少し状況がはっきりするでしょう → 公開されたので調べてみた『GDI アクセラレーションについて整理する - NyaRuRu の日記』)
このサーフェスはドライバによって管理されているため,GDI 描画はドライバによって処理される必要があります.(これはつまり,ドライバが処理できさえすれば良いわけで,必ずしも専用チップによる最適化は必要ないのかも知れません)
WDDM v1.1 によって (ロゴ目当ての場合に) 要求される GDI 処理には,次のようなものがあります.

WDDM v1.1 Requirement

GDI Hardware Acceleration Interfaces
  • Support for common 2D operations:
    • Drawing operations:
      • BitBlt, ColorFill, AlphaBlend etc
    • Cleartype font support
  • Support for common ROPs
  • Support for Linear Heaps
  • Texture size of 8K X 8K

こうして,Direct3D サーフェスとシステムメモリの二重確保は不要になり,保持するデータはドライバリソースのみで良くなるというのが Windows 7 の目指す世界のようです (もちろんドライバがデータを保持しておくための領域は必要で,これには専用の VRAM または UMA によるシステムメモリが消費されます).
WinHEC 2008 初日のキーノートスピーチで出てくる「ダブルバッファは取り除かれた」という言葉は,このことを意味しているものと考えられます.

JON DEVAAN: Right, that's great. And so we have a little over 70 windows on both systems, yet preserve the whole user experience on Windows 7, and it's basically because we're letting the video card do its job in managing the memory for those windows. We don't have to manage it double in system memory.

MIKE ANGIULO: That's right. We had some double buffering that was going on, and all of that has been eliminated. So even though we're using less memory we actually have a faster graphics system going on Win 7. So let me show you on this machine, I'm going to close down all these Windows, which is actually pretty easy now with the new Windows 7 taskbar, you can close entire groups at a time.

余談ですが,このキーノートを伝えている山田祥平氏の記事にも誤りがあります.

ステージでは引き続き、スクリプトによって大量のウィンドウを開く拷問テストのデモが行なわれた。Vistaと7で多くのウィンドウを開いた様子が比較され、クラッシュしてしまうVistaに対して、7はメモリの使い方の改良などでビクともしない堅牢性を実現している。

このテスト,公開されているキーノート動画 の 15:42 あたりから始まりますが,動画を見る限り確かにウィンドウを大量に開くことで Vista のデスクトップコンポジションはオフになっています.しかし,どのアプリケーションもクラッシュしているようには見えません.
また,デスクトップコンポジションの停止は『Windows Vista Rules for Enabling Windows Aero With Guidelines for Troubleshooting - NyaRuRuの日記』で紹介したとおりの挙動です.Vista で実装された安全装置はきちんと機能していたことになります.

各論 1 : なぜ氏の Windows 7 環境で日本語表示に問題が発生したか?

ひとつの仮説として,元麻布氏が使っていた環境が WDDM v1.1 で,かつドライバが GDI アクセラレーションに対応していて,しかもその実装に問題があった可能性があります.
上に見てきたように,WDDM v1.1 環境では GDI 命令がドライバによって処理されるケースがあります.Microsoft 提供の DIB エンジンによって常に同じ描画結果が得られていた WDDM v1 とは異なり,再び GDI 描画の結果が環境によって異なりうる時代に戻ったわけです.
もし日本語表示の問題が,ドライバの GDI 実装にまつわるものであった場合,「GDI/GDI+に対する直接的なハードウェアアクセラレーションもなくなる」と伝えてしまった記事としては二重の意味で皮肉な結果と言えるかも知れません.つまり,GDI アクセラレーションが復活したからこその不具合というわけです.

各論 2 : DirectWrite に関して

さて日本語表示の問題に話を戻すと、このWindows 7で追加されるDirectWriteが、まだ開発途上であることが原因なのではないか、という気がする。DirectWriteでは、従来のClearType技術に加え、Y軸方向のアンチエイリアスがサポートされ、フォント表示がさらに改善される見込みだ。しかし、同時にこれはフォントレンダラが変更されるということであり、このBuild 6801のDirectWriteはマルチバイト言語への対応が十分ではないのだろう。図10で示したように、DirectWriteはDirectX/DWMの上に成り立っており、Aero Glassを無効にすることで、フォントレンダラの新しい機能も無効になるのではないかと思う。

GPU を使ったフォントレンダリングについては『GPU を利用したテキストレンダリング - NyaRuRuの日記]』で紹介したように WinHEC 2004 の頃から成果が示されていて,実際 WPF では Y 方向アンチエイリアスを既に実装済みです.DirectWrite はこれらの技術の延長線上にあると考えられます.
もっとも,MUI が不安というのは確かにありますけどね (This is not yet my take on DirectWrite - Sorting it all Out).
個人的には,そもそも Windows 7 RTM 段階で DirectWrite が既存の GDI アプリケーションの高速化に使われるという話自体が怪しいんじゃないかと思っています.
あと DirectWrite は DWM に直接の依存関係を持っていません.

各論 3 : そもそも Windows 7 でなにが高速化するの?

これは結構謎です.
WinHEC 2008 のメッセージを見る限り,GDI ハードウェアアクセラレーションは,一貫して「メモリ負荷削減のため」と言われており,「高速化のため」という表現は見つけられませんでした.GDI ハードウェアアクセラレーションの復活が,Windows XP から Vista で大幅に低下した GDI ベンチマークを元に戻すためと考えるのは,現時点ではかなり慎重になった方がよいように思います.
もちろん,サーフェスに対する GetDC がハードウェア処理されるのはありがたいと言えばありがたい話で,Direct3D と GDI の相互運用性の向上というのも恐らくこの辺りのことを言っているのではないかと想像しています.BGRA サーフェスのサポートは,ある意味で DDB の進化形かもしれません.
また,Direct2D / DirectWrite に関しても,既存アプリケーションの高速化に使われるかどうかはまだはっきりとした情報を掴んでいません.個人的には,Windows 7 RTM の段階での Direct2D / DirectWrite は単なる顔見せじゃないかと予想しているのですが,どうなんでしょうね?

修正履歴

  • 2008年11月26日
    • 初版
  • 2008年11月27日
    • DWM 環境下での Direct3D Redirection の説明を修正 (実際にはバックバッファが共有されるのではなく,その出力先の隠しサーフェスが共有される)
    • Greg Schechter の記事を参考資料に追加
  • 2009年2月9日

*1:実際には XPDM は Windows 2000 で導入されたドライバモデルなので,XPDM という呼び名は多少政治的な趣もある

Guidelines for Graphics in Windows 7

WinHEC 2008 にあわせて,Guidelines for Graphics in Windows 7 というドキュメントが公開されていたので読んでみました.

このガイドラインは,ハードウェアメーカーや,PC を組み立てて販売するベンダー向けのものです.
ざっと目を通した範囲でほとんどの更新は WDDM 1.1 と関係しているようでした.Windows 7 の成功は,いかにトラブルなく WDDM 1.1 を立ち上げられるかにかかっていそうです.
いくつか重要そうなポイントをメモ.括弧内は心の中のつぶやき.

  • Multi-GPU Systems について
    • Hybrid graphics systems (VAIO type Z や MacBook Pro のような,性質の異なる GPU を切り替えて使う構成) は「強く非推奨」とする.
      • "Windows 7 does not offer native support for hybrid graphics systems. We strongly discourage system manufacturers from shipping such systems, which can be unstable and provide a poor user experience."
      • どうしてもそういう製品を出荷したかったら,"High Fidelity Graphics in Windows 7: A Guide for OEMs and IHVs." を見てね,とのこと.
  • DWM 環境下でも使用可能な,Video Overlay DDI が導入される.
  • GDI Hardware Acceleration を使用可能にする.
    • サポート自体は必須ではない模様.
      • A driver must report the support for GDI acceleration only if the cache-coherent GPU aperture segment exists and there is no significant performance penalty when you access the memory by CPU.
      • (『NVIDIA Graphics Supercharge Windows 7』の中で,「NVidia ドライバは GDI Hardware Acceleration をサポートするぜ」とかゆーてる)
      • (あとはロゴプログラムとの兼ね合いか)
    • GDI Hardware Acceleration サポート時のみ,Aero 使用時のメモリ使用量削減が可能
      • (アプリケーションごとのオフスクリーンイメージを,システムメモリ上ではなく DDB に相当する VRAM 上に保持してメインメモリを節約? 具体的には,BGRA Direct3D サーフェスに対する GetDC がハードウェア処理されるようになる感じのものを予想してるんだけどどうだろ?)
    • GDI Hardware Acceleration サポート時のみ,新しい Explorer のリストビューアニメーションが可能 (これはどうだろ?)
    • 以下の API は,ドライバ内で (結果的に) 高速化される必要がある.(GDI サポート自体は must ではないので,この辺言い回しが微妙な)
      • BitBlt
      • ColorFill
      • StretchBlt
      • AlphaBlend
      • Cleartype font support
      • TransparentBlt
    • BGRA Color Format (Direct3D では ARGB が一般的だけど,GDI は BGRA の並び順) サポートは必須.

Windows 7 Developer Guide とグラフィックスの改良点

The Windows 7 Blog for Developers より Windows 7 Developer Guide 公開のお知らせ.

With Windows 7 unveiled during PDC Day 2 keynote, we also released Windows 7 Developer Guide. This is relatively a short guide outlining some of Windows 7’s main features as well highlighting the solid foundations Windows 7 is build upon.

個人的に興味のあった Windows 7 のグラフィックスについてもある程度載っていた.以下まとめ.括弧内は心のつぶやき.(2008年11月1日更新) 色々詳しい情報が出てきたのでいったんまとめを簡素化.あとで再追記.

  • Direct2D (D2D)
  • DirectWrite
  • Windows Imaging Component (WIC) の改良
  • Direct3D 11
  • Direct3D 10 の改良
  • Windows Advanced Rasterization Platform
  • DirectX/GDI 相互運用

Direct2D 開発者 blog

WinHEC 2008 Conference Presentation