デバイスの列挙は、Microsoft® Direct3D®保持モードの基本的な概念である。デフォルトデバイスは、デバイスを列挙せずに作成できる。 デバイスを列挙すると、システムにあるデバイスを識別し、もっとも適切なデバイスを選択して、より高度な機能が使用できる。
この項では、サンプルRMEnumのコードのウォークスルーを行う。RMEnumはシステムのハードウェアデバイスおよびソフトウェアデバイスを列挙し、最適なデバイスを選択して、デバイスを作成する。さらに、列挙したデバイスをアプリケーションの [ファイル] メニューに追加し、ユーザがメニューからデバイスを選択できるようにする。実際のDirect3D保持モードアプリケーションとは異なり、RMEnumは3Dオブジェクトの表示とレンダリングは行わない。このサンプルは、説明を簡潔にするために、列挙だけを扱っている。
この項は、ユーザがCまたはC++のプログラミング経験があること、および、メッセージループ、ウィンドウの作成、メニューの使用など、Windowsプログラミングの基本概念に精通していることを前提にしている。
この項は、次の項目に分かれている。
RMEnumはDDraw.libとD3DRM.libをリンクする。RMEnumは、Error.cのMsgとD3DRMErrorToString ヘルパ関数を使用して、エラー発生時に詳細情報を表示する。デフォルトでは、Error.cはMicrosoft® DirectX® Software Development Kit (SDK)のSamples\Miscディレクトリに入っている。ヘルパ関数を使用する代わりに、Microsoft® Windows®の標準MessageBox関数を使用するだけでもよいが、わかりやすく詳細なエラー報告は得られない。サンプルは、RMEnum.rcが提供するメニューなどのリソースもリンクしている。
グローバルユニーク識別子(GUID)を適切に初期化するために、Direct3D アプリケーションは、 コードの先頭で次の定義を行う必要がある。
#define INITGUID
サンプルのRMEnum.cpp ファイルは次のヘッダファイルをインクルードする。
#include <windows.h> /* 標準Windowsヘッダファイル */ #include <direct.h> /* DirectDrawの定義 */ #include <d3drmwin.h> /* Direct3D保持モードの定義 */ #include "RMEnum.h" /* RMEnum.rcで使用する定数の定義 */ #include "rmerror.h" /* rmerror.cの、エラー報告関数のプロトタイプ */
このサンプルでは、いくつかの定数とグローバル変数を定義して、情報を整理する。
RMEnum.cppでは、コードを簡略化するために、列挙するドライバの数を制限しているが、1回ドライバを列挙して使用可能なドライバ数を調べた後、もう1度列挙してドライバの機能を調べることもできる。実際のアプリケーションでは、このサンプルのようにサイズを固定した配列ではなく、ドライバ情報の動的なリストを管理することもある。
#define MAX_DRIVERS 5 /* 検出するDirect3Dのドライバ数を 恣意的に制限する。 */
アプリケーションで定義されているRELEASEマクロは、オブジェクトがNULL以外の場合にだけオブジェクトを解放し、解放後にオブジェクトをNULLに初期化する。すでに解放したオブジェクトを解放すると、ページフォールトなどの予期せぬ結果が発生するが、この手順によって、そうした問題が回避できる。
/* オブジェクトを解放するマクロ。 */ #define RELEASE(x) if (x != NULL) {x->Release(); x = NULL;}
myglobs構造体は、アプリケーションの現在の状態に加えて、デバイスとドライバの情報を収集する。Direct3D保持モードおよびDirectDrawClipperオブジェクトもグローバルである(DirectDrawClipperオブジェクトは、描画を指定した領域に制限する)。
/* * グローバル変数 */ LPDIRECT3DRM lpD3DRM = NULL; /* Direct3DRMオブジェクト */ LPDIRECTDRAWCLIPPER lpDDClipper = NULL; /* DirectDrawClipperオブジェクト */ struct _myglobs { /* Direct3DRMデバイス */ LPDIRECT3DRMDEVICE dev; /* 使用可能なDirect3D (D3D)ドライバのGUID */ GUID DriverGUID[MAX_DRIVERS]; /* 使用可能なD3Dドライバの名前 */ char DriverName[MAX_DRIVERS][50]; /* 使用可能なD3Dドライバ数を示すカウンタ */ int NumDrivers; /* 現在使用中のD3Dドライバの数。enumDeviceFuncで、 * 指定したドライバに設定する。 */ int CurrDriver; /* プログラムは終了中である。*/ BOOL bQuit; /* すべてのD3Dオブジェクトは初期化されている。 */ BOOL bInitialized; /* 現在のディスプレイモードのビット深度。 */ DWORD BPP; } myglobs;
RMEnum.cppには、以下の関数が含まれている。
CreateObjectsは、グローバル変数を初期化するマスタ関数であり、EnumDevicesを使用してデバイスを列挙し、オブジェクトを作成する。InitAppはCreateObjectsを使用する。
BOOL CreateObjects(HWND win);
GetCurrentBitDepthは、現在のビット深度をCreateObjectsに返す。
static DWORD GetCurrentBitDepth(HWND);
EnumDevicesは、デバイスの列挙に使用するDirect3Dオブジェクトを取得し、直接モードのIDirect3D2::EnumDevices関数を使用して、パラメータとしてenumDeviceFuncコールバック関数を渡し、列挙したデバイスを [ファイル] メニューに入れる。
static BOOL EnumDevices(HWND);
enumDeviceFuncコールバック関数は、使用可能なドライバを循環し、最適なドライバを選択する。
static HRESULT WINAPI enumDeviceFunc(LPGUID, LPSTR, LPSTR, LPD3DDEVICEDESC, LPD3DDEVICEDESC, LPVOID);
RMEnumは、ユーザのメニュー選択に応じて、ChangeDriverを使用して他の使用可能なドライバに切り替える。
void ChangeDriver(HWND, WPARAM);
CleanUpAndQuitは、アプリケーションを終了する前にオブジェクトを解放する。
static void CleanUpAndQuit(void);
その他の関数 (InitApp、WinMain、AppAbout、WindowProc) は、標準 Windows関数を基に、サンプルRMEnum固有のコードを少し含んでいる。
static HWND InitApp(HINSTANCE, int); int APIENTRY WinMain (HINSTANCE, HINSTANCE, LPSTR, int); LRESULT CALLBACK AppAbout(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM);
以上の関数の詳細については、この項の次の項目を参照すること。
CreateObjectsは、サンプルRMEnumの核となるコードである。グローバル変数を初期化し、現在のビット深度を取得した後、EnumDevicesを使用して実際にデバイスを列挙する。enumDeviceFuncコールバック関数は、後でビット深度を使用して、現在のビット深度をサポートするドライバを決定する。
CreateObjectsは、デバイスを列挙した後、デバイスの作成に必要な手順を実行し、アプリケーションのメインウィンドウに関連付ける。以下の手順に従う。
次に、CreateObjects関数のソースコードを示す。
/* * グローバル変数を初期化し、デバイスを列挙して、オブジェクトを作成する。 */ BOOL CreateObjects(HWND win) { HRESULT rval; /* 戻り値 */ RECT rc; /* メインウィンドウの矩形の境界 */ /* * グローバル変数の初期化。 */ memset(&myglobs, 0, sizeof(myglobs)); /* * ディスプレイの現在のビット深度を記録する。 */ myglobs.BPP = GetCurrentBitDepth(win); /* * D3Dドライバを列挙し、1つを選択する。 */ if (!EnumDevices(win)) return FALSE; /* * Direct3DRMオブジェクトとウィンドウオブジェクトを作成する。 */ rval = Direct3DRMCreate(&lpD3DRM); if (rval != D3DRM_OK) { Msg("Failed to create Direct3DRM.\n%s", D3DRMErrorToString(rval)); return FALSE; } /* * DirectDrawClipperオブジェクトを作成し、ウィンドウに関連付ける。 */ rval = DirectDrawCreateClipper(0, &lpDDClipper, NULL); if (rval != DD_OK) { Msg("Failed to create DirectDrawClipper object"); return FALSE; } rval = lpDDClipper->SetHWnd(0, win); if (rval != DD_OK) { Msg("Failed to set the window handle for the DirectDrawClipper"); return FALSE; } /* * 選択したD3DドライバでD3DRMデバイスを作成する。GUIDを * NULLにセットすると、列挙なしにデフォルトデバイスを作成できる。 */ GetClientRect(win, &rc); rval = lpD3DRM->CreateDeviceFromClipper(lpDDClipper, &myglobs.DriverGUID[myglobs.CurrDriver], rc.right, rc.bottom, &myglobs.dev); if (rval) { Msg("Failed to create the D3DRM device.\n%s", D3DRMErrorToString(rval)); return FALSE; } /* * グローバル変数は初期化された。 */ myglobs.bInitialized = TRUE; return TRUE; }
GetCurrentBitDepth関数は、現在のデバイスのビット深度性能を決定して、DirectDraw ビット深度フラグの形式で情報を返す。ビット深度は、デバイスのカラー設定 (8ビット、16ビット、または24ビットカラー) を表わす。
/* * 現在のディスプレイのビット深度をBPP (bits per pixel) で表わす * DirectDrawビット深度フラグを取得する。 */ static DWORD GetCurrentBitDepth(HWND win) { HDC hdc; int BPP; /* * 現在のディスプレイのBPPを記録する。 */ hdc = GetDC(win); BPP = GetDeviceCaps(hdc, BITSPIXEL); ReleaseDC(win, hdc); /* * BPP値をDirectDrawビット深度にマッピングする。 */ switch(BPP) { case 1: return DDBD_1; case 2: return DDBD_2; case 4: return DDBD_4; case 8: return DDBD_8; case 16: return DDBD_16; case 24: return DDBD_24; case 32: return DDBD_32; default: return 0; } }
RMEnumのEnumDevices関数は、以下の手順に従って、デバイスを列挙する。手順には、サンプルの変数とコードの一部も示す。
LPDIRECTDRAW lpDD; HRESULT rval; rval = DirectDrawCreate(NULL, &lpDD, NULL);
LPDIRECT3D lpD3D; rval = lpDD->QueryInterface(IID_IDirect3D, (void**) &lpD3D);
rval = lpD3D->EnumDevices(enumDeviceFunc, &myglobs.CurrDriver);
このコールバック関数は、使用可能なデバイスを循環し、ある基準に合うドライバを選択する。
ARMEnumのEnumDevices関数はドライバを列挙した後、DirectDrawとDirect3Dのデバイスを解放し、アプリケーションの [ファイル] メニューにドライバを追加する。
lpD3D->Release(); lpDD->Release();
次に、RMEnumのEnumDevices関数の全ソースコードを示す。
/* * EnumDevices * 使用可能なDirect3Dドライバを列挙し、[ファイル] メニューに追加し、 * 使用するドライバを1つ選択する。 */ static BOOL EnumDevices(HWND win) { LPDIRECTDRAW lpDD; LPDIRECT3D lpD3D; HRESULT rval; HMENU hmenu; int i; /* * DirectDrawオブジェクトを作成し、ドライバの列挙に使用する * Direct3Dインターフェイスを問い合わせる。 */ rval = DirectDrawCreate(NULL, &lpDD, NULL); if (rval != DD_OK) { Msg("Creation of DirectDraw HEL failed.\n%s", D3DRMErrorToString(rval)); return FALSE; } rval = lpDD->QueryInterface(IID_IDirect3D, (void**) &lpD3D); if (rval != DD_OK) { Msg("Creation of Direct3D interface failed.\n%s", D3DRMErrorToString(rval)); lpDD->Release(); return FALSE; } /* * ドライバを列挙する。アプリケーション定義データとして、 * 任意の値である-1を渡す。enumDeviceFuncコールバック関数は、 * -1に対応して、特殊な初期化コードを実行する。 */ myglobs.CurrDriver = -1; rval = lpD3D->EnumDevices(enumDeviceFunc, &myglobs.CurrDriver); if (rval != DD_OK) { Msg("Enumeration of drivers failed.\n%s", D3DRMErrorToString(rval)); return FALSE; } /* * 有効なドライバが少なくとも1つあることを保証する。 */ if (myglobs.NumDrivers == 0) { Msg("Could not find a D3D driver that is compatible with this \ display depth"); return FALSE; } lpD3D->Release(); lpDD->Release(); /* * ドライバ名を [ファイル] メニューに追加する。 */ hmenu = GetSubMenu(GetMenu(win), 0); for (i = 0; i < myglobs.NumDrivers; i++) { InsertMenu(hmenu, 2 + i, MF_BYPOSITION | MF_STRING, MENU_FIRST_DRIVER + i, myglobs.DriverName[i]); } return TRUE; }
Direct3D 直接モードは、システムにインストールされたDirect3Dデバイスを列挙するコールバック関数のプロトタイプ定義として、D3DENUMDEVICESCALLBACKを定義している。RMEnumはenumDeviceFuncを列挙コールバック関数として提供する。Direct3D直接モードは、システムにインストールされたデバイスごとにenumDeviceFuncを呼び出し、デバイス情報をenumDeviceFuncに渡す。D3D直接モードが使用可能なドライバをループする間、enumDeviceFuncは、現在のディスプレイのビット深度でレンダリング可能なドライバに関する情報をグローバル変数に格納する。enumDeviceFuncは、ソフトウェアエミュレーションドライバよりも、ハードウェアドライバを優先的に選択する(ハードウェアドライバは一般的にレンダリングが高速なためである)。また、3次元照明の効果を上げるために、モノクロ照明よりもカラー照明を優先的に選択する。このサンプルでは、RMEnumが実際にはレンダリングを行わないため、この選択の効果は現れない。しかし、実際のアプリケーションでは、こうした選択によって、3Dグラフィックスの表示状態に大きな影響が出る。
enumDeviceFuncのパラメータには、デバイスのグローバルユニーク識別子 (GUID)、デバイスのテキスト記述、およびデバイス名がある。その他に、Direct3D 直接モードのD3DDEVICEDESC構造体が2つ含まれる。最初の構造体 (lpHWDesc) には、Direct3D デバイスのハードウェア能力、2つめの構造体 (lpHELDesc) には、デバイスのハードウェアエミュレーションレイヤ (HEL) のソフトウェアエミュレーション能力がそれぞれ格納される。最後のパラメータは、Direct3D 直接モードがこのコールバック関数を最初に呼び出すときに、アプリケーション定義データとして渡される。
次に、RMEnumのenumDeviceFuncコールバック関数の全ソースコードを示す。
/* * enumDeviceFunc * 使用可能なD3Dドライバの名前とGUIDを記録するコールバック関数。 * システムのドライバを選択する。ソフトウェアドライバよりも * ハードウェアドライバ、また、モノクロ照明よりもカラー照明を * 優先的に選択する。myglobs.CurrDriverに、選択したドライバを * 設定する。 */ static HRESULT WINAPI enumDeviceFunc(LPGUID lpGuid, LPSTR lpDeviceDescription, LPSTR lpDeviceName, LPD3DDEVICEDESC lpHWDesc, LPD3DDEVICEDESC lpHELDesc, LPVOID lpContext) { static BOOL hardware = FALSE; /* 現在の開始ドライバはソフトウェアである。*/ static BOOL mono = FALSE; /* 現在の開始ドライバはカラー照明である。*/ LPD3DDEVICEDESC lpDesc; /* 現在のドライバの記述。*/ /* * このアプリケーションが定義するデータ。-1であれば、 * 最初に列挙されたドライバを現在のドライバにすることを示す。 */ int *lpStartDriver = (int *)lpContext; /* * どのデバイス記述を調べるか決定する。ドライバはハードウェア (HW) か * ソフトウェアである。ソフトウェアドライバは、HEL (ハードウェア * エミュレーションレイヤ) でサポートされる。 */ lpDesc = lpHWDesc->dcmColorModel ? lpHWDesc : lpHELDesc; /* * このドライバが、現在のディスプレイのビット深度ではレンダリングできない * 場合、D3DENUMRET_OKを返して、このドライバをスキップする。 * 3D3は次のドライバに対して enumDeviceFuncを再び呼び出すので、 * 列挙は自動的に継続される。 */ if (!(lpDesc->dwDeviceRenderBitDepth & myglobs.BPP)) return D3DENUMRET_OK; /* * このドライバのGUIDと名前を記録する。 */ memcpy(&myglobs.DriverGUID[myglobs.NumDrivers], lpGuid, sizeof(GUID)); lstrcpy(&myglobs.DriverName[myglobs.NumDrivers][0], lpDeviceName); /* * ソフトウェアよりもハードウェアを、モノクロ照明よりもカラー照明を * 選択する。 */ if (*lpStartDriver == (int)-1) { /* * これが最初の有効なドライバである。そこで、これがハードウェア * ドライバであるかどうか、モノクロ照明に限定されているかどうかを * 記録する。 */ myglobs.CurrDriver = myglobs.NumDrivers; hardware = lpDesc == lpHWDesc ? TRUE : FALSE; mono = lpDesc->dcmColorModel & D3DCOLOR_MONO ? TRUE : FALSE; } else if (lpDesc == lpHWDesc && !hardware) { /* * このドライバがハードウェアドライバであり、開始ドライバが * ハードウェアドライバでなければ、このドライバを新しいドライバにして、 * ハードウェアかつモノクロ性能であることを記録する。D3Dが * enumDeviceFuncを次に呼び出すとき、そのドライバは、 * この新しい開始ドライバと比較される。 */ myglobs.CurrDriver = myglobs.NumDrivers; hardware = lpDesc == lpHWDesc ? TRUE : FALSE; mono = lpDesc->dcmColorModel & D3DCOLOR_MONO ? TRUE : FALSE; } else if ((lpDesc == lpHWDesc && hardware ) || (lpDesc == lpHELDesc && !hardware)) { if (lpDesc->dcmColorModel == D3DCOLOR_RGB && mono) { /* * このドライバと開始ドライバが同じタイプ * (両方ともハードウェアか、両方ともソフトウェア) で、このドライバは * カラー照明だが開始ドライバはカラー照明でなければ、このドライバを * 新しいドライバに設定して、性能を記録する。 * D3DがenumDeviceFuncを次に呼び出すとき、そのドライバは、 * この新しい開始ドライバと比較される。 */ myglobs.CurrDriver = myglobs.NumDrivers; hardware = lpDesc == lpHWDesc ? TRUE : FALSE; mono = lpDesc->dcmColorModel & D3DCOLOR_MONO ? TRUE : FALSE; } } /* * 次のドライバに備えて、現在のドライバ番号を * 増やす。 */ myglobs.NumDrivers++; /* ドライバの最大数に達したら、列挙を停止する。 */ if (myglobs.NumDrivers == MAX_DRIVERS) return (D3DENUMRET_CANCEL); /* * ドライバの列挙を継続する。D3Dは次のドライバに対して、 * この情報を使ってenumDeviceFuncを呼び出す。 */ return (D3DENUMRET_OK); }
ChangeDriverは、サンプルRMEnumにとって必須の関数ではないが、ユーザがドライバを選択する柔軟性を付加する。ChangeDriverは、メニュー選択に応じて現在のドライバを変更する。そして、RMEnumのCreateObjects関数がデバイスを最初に作成したように、Direct3D保持モードオブジェクトのCreateDeviceFromClipperメソッドを使用してデバイスを再作成する。
次に、ChangeDriverのソースコードを示す。
/* * 現在のデバイスを解放して、新しいデバイスを作成する。 */ void ChangeDriver(HWND win, WPARAM wparam) { HRESULT rval; RECT rc; /* * 前のドライバ選択をLastDriverとして保存する。 */ int LastDriver = myglobs.CurrDriver; /* * 選択内容が変更されたため、グローバル変数は正確でなくなる。 */ myglobs.bInitialized = FALSE; /* * 現在のデバイスを解放する。 */ RELEASE(myglobs.dev); /* * 現在のドライバをメニューから選択されたドライバに設定する。 */ myglobs.CurrDriver = LOWORD(wparam)-MENU_FIRST_DRIVER; /* * ウィンドウの境界矩形を取得する。グローバルオブジェクトDirectDrawClipper * を使用して、そのウィンドウの新しい現在のドライバを作成する。 */ GetClientRect(win, &rc); rval = lpD3DRM->CreateDeviceFromClipper(lpDDClipper, &myglobs.DriverGUID[myglobs.CurrDriver], rc.right, rc.bottom, &myglobs.dev); /* * エラーが発生したら、前のドライバを再作成する。 */ if (rval) { myglobs.CurrDriver = LastDriver; rval = lpD3DRM->CreateDeviceFromClipper(lpDDClipper, &myglobs.DriverGUID[myglobs.CurrDriver], rc.right, rc.bottom, &myglobs.dev); if (rval) { Msg("Failed to create the D3DRM device.\n%s", D3DRMErrorToString(rval)); CleanUpAndQuit(); } else { Msg("There was not enough video memory available to use the \ 3D accelerated hardware device.\nRestoring old software \ device."); myglobs.bInitialized = TRUE; } } else { /* * グローバル変数は、再び適切に初期化された。 */ myglobs.bInitialized = TRUE; } }
ごく短いCleanUpAndQuit関数は、グローバルオブジェクトを解放して、quitフラグをTRUEに設定し、アプリケーションの終了準備が整ったことを示す。
void CleanUpAndQuit(void) { myglobs.bInitialized = FALSE; RELEASE(myglobs.dev); RELEASE(lpD3DRM); RELEASE(lpDDClipper); myglobs.bQuit = TRUE; }
RMEnum.cppの、その他の関数 (InitApp、WinMain、AppAbout、WindowProc) は、本来は標準Windows関数で、サンプルRMEnumに固有の短いコードを含んでいる。詳細については、RMEnumのサンプルコードを参照すること。
ここではデバイスを列挙する方法を学習した。Direct3D保持モードのサンプルコードが、前よりも簡単に理解できるようになったであろう。ほとんどのサンプルは、RMMainが提供するヘルパコードを使用してデバイスを列挙する。次のステップとしては、EggやGlobeなどのサンプルを見て、シーンの裏でデバイスの列挙を行っていることを念頭におきながら、レンダリングする方法を調べるとよい。
保持モードサンプルのすべての概要については、「サンプル」を参照すること。
トップに戻る
© 1999 Microsoft and/or its suppliers. All rights reserved. Terms of Use.