這一章我們首先將描述direct3D的通用的架構,並且討論Direct3D與windows GDI的聯繫,然後再引入Direct3D的一些抽象概念:devices, swap chains , surfaces和resources。 接下來,我們將描述IDirect3D接口,這個接口揭露了系統裏面的圖形devices。
當一個圖形應用程序初始化的時候,它首先probe系統裏面的圖形設備,然後再選擇一個合適的設備來滿足應用程序的display需求。這個過程稱作“device enumeration”。IDirect3D使枚舉設備過程非常容易,能夠讓我們檢查出現在系統裏面的設備是否符合我們的需要。
每一個monitor都是由一個adapter驅動的,它能夠支持很多種顯示模式和刷新率。每個adapterd都擁有運行在windowed 和exclusive模式下的的硬件和軟件設備對象。應用程序列舉出每個adapter上的可用的設備,檢查他們的capability和支持的顯示模式,找到一個合適的設備。當這個設備找到之後,應用程序需要檢查這個設備是否支持render target 格式,resource格式以及multisampling。
Direct3D能夠枚舉默認adapter上的所有設備或者所有adapters上的所有設備。 多個monitor的系統開始很通用,應用程序能充分利用多個monitor的附加的顯示空間。
Direct3D Architecture
Direct3D是windows系統裏面與GDI相同層級的組件。Direct3D 設備驅動直接與圖形硬件打交到,並且可以直接與顯卡驅動程序通信。Direct3D的抽象概念包括devices, swap chains和resources。 設備封裝了硬件渲染操作方法,設備屬性控制渲染行爲並且提供渲染信息,設備方法用於執行渲染。設備至少包括一個swap chain和多個用於渲染的資源。資源存放在設備硬件裏或者其附近,用於提供圖形渲染所需要的特定數據。Direct3D提供的資源包括場景幾何體(頂點和索引)以及外觀數據(圖片,紋理和volumes)。
surface也是一種資源,它包含一個矩形集合的像素數據,如color, alpha, depth/stencil以及紋理信息。一個swap chain 包含了一個和多個back buffer surface。設備的render target也是back buffer surface,並且還可能攜帶一個depth/stencil surface。
所有的back buffer都是合理的render target,但是並不是所有render target都是back buffer。我們也可以讓一個紋理surface作爲render target,來實現動態的渲染效果。
爲了獲得設備對象,Direct3D提供了設備枚舉和創建的方法。所有的其他對象就可以由設備來創建。應用程序首先獲取runtime的接口,選擇和創建一個設備。接着使用設備創建其他的資源對象。
Windowed and Exclusive mode
Direct3D 設備兩種不同的操作模式:windowed 和exclusive。在windowed下,圖形渲染在桌面窗口的客戶端區域進行。Direct3D 將跟GDI一起工作,使用::stretchBlt方法在windows的客戶端區域present一個back buffer。在Exclusive模式下,Direct3D直接調用顯卡驅動,而並不通過GDI。當一個exclusive模式的應用程序運行的時候,其他應用程序都不可以再訪問顯卡了。
Devices Type
每個adapter支持幾種類型的設備。Direct3D支持的三種類型:包括HAL 設備,reference設備以及可插拔的軟件設備。D3DDEVTYPE定義了設備類型。
typedef enum _D3DDEVTYPE
{
D3DDEVTYPE_HAL = 1,
D3DDEVTYPE_NULLREF = 4,
D3DDEVTYPE_REF = 2,
D3DDEVTYPE_SW = 3
} D3DDEVTYPE;
HAL(hardware abstraction layer)設備使用了圖形渲染的硬件加速,所以是速度最快的設備類型。reference設備只有在SDK的安裝版本里面纔有,它包括了對整個圖形的pipeline進行軟件實現。這個設備雖然速度很慢,但是它易於圖形應用程序的調試。null referece 設備將什麼都不做,所有的渲染只是一個黑屏。當系統沒有裝SDK,但是應用程序請求一個reference設備的時候,它就返回一個null reference。可插拔的軟件設備通過RegisterDevice設備方法提供,Direct3D 9.0c還沒有可插拔的軟件設備。
Resources
每種資源都有Type,Pool, Format和Usage屬性。這些屬性都是在資源創建的時候一次性指定。Type屬性指定了資源的類型,定義在D3DRESOURCETYPE枚舉類型裏面。
typedef enum _D3DRESOURCETYPE
{
D3DRTYPE_SURFACE = 1,
D3DRTYPE_VOLUME = 2,
D3DRTYPE_TEXTURE = 3,
D3DRTYPE_VOLUMETEXTURE = 4,
D3DRTYPE_CUBETEXTURE = 5,
D3DRTYPE_VERTEXBUFFER= 6,
D3DRTYPE_INDEXBUFFER = 7
}
Pool屬性描述了Direct3D怎麼管理它,定義在D3DPOOL枚舉類型裏面。資源默認是在設備內存裏面的。managed 池裏面資源是在系統內存裏面,當需要使用他的時候它將copy到設備內存裏面。系統內存池的資源只存在於系統內存裏面。scratch池裏面的資源只存在與系統內存裏面,並且它不受設備格式限制。當一個設備lost的時候,它的默認池裏面的資源都會lost。
typedef enum _D3DPOOL
{
D3DPOOL_DEFAULT = 0,
D3DPOOL_MANAGED = 1,
D3DPOOL_SYSTEMMEM=2,
D3DPOOL_SCRATCH = 3
}D3DPOOL;
格式屬性描述了資源在內存裏面的layout,它定義在D3DFORMAT枚舉類型裏面。所有的資源都有一種格式,但是大部分格式枚舉都是像素數據的格式。
typedef enum _D3DFORMAT
{
D3DFMT_UNKNOWN = 0,
D3DFMT_INDEX16 = 101,
D3DFMT_INDEX32 = 102,
D3DFMT_VERTEXDATA = 100,
D3DFMT_A4L4 = 52,
D3DFMT_A8 = 28,
D3DFMT_L8 = 50,
D3DFMT_P8=41,
D3DFMT_R3G3B2 = 27,
D3DFMT_A1R5G5B5 = 25
D3DFMT_A4R4G4B4= 26,
D3DFMT_A8L8 = 51,
D3DFMT_A8P8 = 40,
D3DFMT_A8R3G3B2 = 29,
D3DFMT_L16 = 81,
D3DFMT_L6V5U5= 61,
D3DFMT_R16F = 111,
D3DFMT_R5G6B5 = 23,
D3DFMT_V8U8= 60,
D3DFMT_X1R5G5B5 = 24,
D3DFMT_X4R4G4B4 = 30,
D3DFMT_R8G8B8 = 20,
D3DFMT_A2R10G10B10= 35,
D3DFMT_A2W10V10U10= 31,
D3DFMT_A2W10V10U10 = 67,
D3DFMT_A8B8G8R8 = 32,
D3DFMT_A8R8G8B8 = 21,
D3DFMT_CxV8U8 = 117,
D3DFMT_G16R16 = 34,
D3DFMT_Q8W8V8U8 = 63,
D3DFMT_R32F = 114,
D3DFMT_V16U16 = 64,
D3DFMT_W11V11U10 = 65,
D3DFMT_X8L8V8U8 = 62,
D3DFMT_X8B8G8R8 = 33,
D3DFMT_X8R8G8B8=22,
D3DFMT_A16B16G16R16= 36,
D3DFMT_A16B16G16R16F = 113,
D3DFMT_G32R32F = 115,
D3DFMT_Q16W16V16U16 = 110,
D3DFMT_A32B32G32R32F = 116,
D3DFMT_DXT1 = MAKEFOURCC('D','X','T','1');
D3DFMT_DXT2 = MAKEFOURCC('D','X','T','2');
D3DFMT_DXT3 = MAKEFOURCC('D','X','T','3');
D3DFMT_DXT4 = MAKEFOURCC('D','X','T','4');
D3DFMT_DXT5 = MAKEFOURCC('D','X','T','5');
D3DFMT_G8R8_G8B8 = MAKEFOURCC('G','R','G','B');
D3DFMT_R8G8_B8G8 = MAKEFOURCC('R','G','B','G');
D3DFMT_UYVY = MAKEFOURCC('U','Y','V','Y');
D3DFMT_YUY2 =MAKEFOURCC('Y','U','Y','2');
D3DFMT_MULT2_ARGB8 = MAKEFOURCC('M','E','T','1');
D3DFMT_D15S1 = 73,
D3DFMT_D16 = 80,
D3DFMT_D16_LOCKABLE = 70,
D3DFMT_D32F_LOCKABLE = 82,
D3DFMT_D24S8 = 75,
D3DFMT_D24FS8 = 83,
D3DFMT_D24X4S4 = 79,
D3DFMT_D24X8 = 77,
D3DFMT_D32 = 71
}D3DFORMAT;
An,Ln,Bn,Pn,Rn,Gn和Bn都是無符號的,Un,Vn,Wn,Qn是有符號的。Dn和Sn設備指定的depth/stencil surface數據。
MAKEFOURCC宏用來生成一個四字符碼。附加的vendor指定的格式可以通過MAKEFOURCC定義。DXTn格式是壓縮紋理格式。D3DCOLOR的像素格式是D3DFMT_A8R8G8B8,然而PALETTEENTRY和COLORREF 卻讓R和G互換。
Usage 包括D3DUSAGE_AUTOGENMIPMAP,D3DUSAGE_DEPTHSTENCIL,D3DUSAGE_DMAP,D3DUSAGE_DONOTCLIP,D3DUSAGE_DYNAMIC,D3DUSAGE_NPATCHES,D3DUSAGE_POINTS,D3DUSAGE_RENDERTARGET,D3DUSAGE_RTPATCHES,D3DUSAGE_SOFTWAREPROCESSING,D3DUSAGE_WRITEONLY。
IDirect3D9
可以通過Direct3D的全局核心函數來獲取IDirect3D9接口。
#define D3DSDK_VERSION 31
IDirect3D9 * WINAPI ::Direct3DCreate9(UINT sdk_version);
版本參數必須是D3D_SDK_VERSION。當direct3D頭文件改變時,這個數字將增加。如果傳了錯誤的版本號碼,這個函數將失敗,並且返回NULL。
IDirect3D提供了一個圖形硬件模型。在這個模型裏,每個monitor都連接一個adapter(它被一個無符號整數所標識)。一個Apdater並不完全與一張顯卡等同,近些年,有些顯卡上可以支持兩個apdater,稱作"dual head" display,IDirect3D9認爲他們是不同的apdaters。
接口提供了統一的方式檢查連接到系統的所有的適配器,並且選擇最適合的一個來完成渲染任務。
方法和屬性 | 描述 |
GetAdapterCount | 系統裏面的adpater的數量 |
GetAdapterDisplayMode | 一個adapter的當前video display mode |
GetAdapterIdentifier | adapter的標識符 |
GetAdapterModeCount | apdater可支持的video display mode 的數目 |
GetAdapterMonitor | 一個adapter的HMONITOR |
GetDeviceCaps | 設備通常的capabilities |
CheckDepthStencilMatch | 檢查設備使用的depth/stencil surface與render target和adpater顯示格式是否匹配 |
CheckDeviceFormat | 檢查設備支持的資源類型和格式 |
CheckDeviceFormatConversion | |
CheckDeviceMultiSampleType | 檢查設備是否支持multisample |
CheckDeviceType | |
EnumAdapterModes | 在一個adpater上創建一個設備對象 |
RegisterSoftwareDevice | 註冊一個direct3D的軟件設備 |
選擇一個設備
一般情況下,應用程序列舉所有的設備,然後選擇一個最合適的設備。首先,調用GetAdapterCount 來獲取系統中的adapter的數目,每個adapter有多種顯示模式。每個顯示模式包含screen dimension,refresh rate 和像素格式,Direct3D使用結構體D3DDISPLAYMODE定義。
typedef struct _D3DDISPLAYMODE
{
UINT Width;
UINT Height;
UINT RefreshRate;
D3DFORMAT Format;
}D3DDISPLAYMODE;
Format 要麼是RGB格式,要麼是XRGB格式。 back buffer surface格式必須與顯示模式格式兼容。 使用CheckDeviceFormat 發現兼容的格式。一般情況下,back buffer格式與顯示格式有着相同的像素深度和顏色layout。一個XRGB顯示格式能與同樣深度的ARGB back buffer一起使用。GetAdapterModeCount返回apdator支持的顯示模式的數目。EnumAdapterModes返回顯示模式信息。adapter當前使用的顯示模式可以通過GetAdapterDisplayMode取得。 使用一些支持的顯示格式,我們可以查詢支持的設備類型。CheckDeviceType方法告訴我們顯示格式和back buffer格式對一個指定類型的設備是否合理。找到了合適的設備,我們就可以通過GetDeviceCaps來檢查設備的渲染功能。接下來,我們可以使用CheckDeviceFormat更新所有的資源(back buffer surfaces, depth/stencil surfaces,texture surfaces和volume texture 格式)。再接下來,如果應用程序需要做深度可視性檢測,它應該使用CheckDepthStencilMatch發現一個深度buffer。
最後,需要使用multisampling應用程序需要通過CheckDeviceMultisampling來檢測。
注意:CheckDeviceType 用於檢測Adapter允許的顏色Format與與它的某個設備支持的backbuffer顏色格式是否兼容。CheckDeviceFormat用於檢測是否某個adapter的設備支持某種資源格式。
Determining Available Resource Memory
當已經找到了一個能滿足應用程序需要的設備後,這個時候,我們要看看設備是否能提供足夠的內存資源滿足應用程序的需要。如果應用程序已經有了指定的內存需求,判斷備是否能夠滿足這個需求的唯一方式是初始化設備,並且嘗試創建必需的資源。
應用程序可用的內存與當前使用的diplay mode 有關。如果設備申請的video mode與桌面顯示的video mode不一致時,創建一個exclusive模式的設備可能引起不一致顯示模式的變化。爲了避免在應用程序啓動時這種失常的效果,應用程序最好能在安裝的時候執行內存測試,然後保存這個結果。這個操作是安全的,因爲在某種特殊的顯示模式可用內存量不會改變,除非硬件被更換。
Device Capabilities
圖形設備一般都有很多architectures ,harware capabilities和performance range。應用程序probe硬件的capablities,然後決定合適的渲染strategy。應用程序可以在某個指定的adapter上調用GetDeviceCaps來獲取它的capabilities。設備capabilities一般使用D3DCAPS9定義的。
Identifying a Particular Device
在理想情況下,設備的支持情況和capabilities檢測以後,我們可能就不使用incapable的設備。 但是,現實環境去不這樣。GetAdapterIdentifier允許應用程序識別某個品牌的adapter。GetAdapterIdentifier返回一個D3DADAPTER_IDENTIFIER9的結構體。
typedef struct _D3DADAPTER_IDENTIFIER9
{
char Driver[MAX_DEVICE_IDENTIFIER_STRING];
char Description[MAX_DEVICE_IDENTIFIER_STRING];
char DeviceName[32];
#ifdef _WIN32
LARGE_INTEGER DriverVersion;
#else
DWORD DriverVersionLowPart;
DWORD DriverVersionHighPart;
#endif
DWORD VendorId;
DWORD DeviceId;
DWORD SubSysId;
DWORD Revision;
GUID DeviceIdentifier;
DWORD WHQLLevel;
HMONITOR hMonitor;
} D3DADAPTER_IDENTIFIER9;
Driver和Description用於圖形界面對設備的選擇。DriverVersion 指示了Direct3D的版本號碼。VendorId, DeviceId,SubSysId,Revision 用來區分不同的硬件芯片。WHQLLEVEL就是這個驅動的WHQL(Windows hardware quality Laboratory) 信息。這個值如果是0表示它沒有過這個認證,如果是1表示過了,但是沒有日期信息。決定WHQL LEVEL是一個很費時的操作,通常一般都是避免做這個操作。將GetAdapterIdentifier的flag參數爲0,就可以避免這個操作。
Creating the Device
通過IDirect3D9::CreateDevice(UINT adapter, D3DDEVTYPE device_kind,HWND focus_window, DWORD behavior_flags, D3DPRESENT_PARAMETERS* presentation,IDirect3DDevice8 **result); focus_window 參數表示它將是設備的focus。 對於一個工作在exclusive mode 設備來說,窗口必須是一個top-level窗口。 應用程序不應該在響應WM_CREATE的消息處理函數裏創建設備。 當設備創建後並且在exclusive模式下,Direct3D 應用程序將接收focus 窗口的消息,Direct3D 將subclass這個focus的窗口。MFC 應用程序取得focus窗口句柄是通過函數::AfxGetSafeWindow。
behavior_flags參數指定了創建設備的全局行爲,這些flag包括D3DCREATE_ADAPTERGROUP_DEVICE,D3DCREATE_DISABLE_DRIVER_MANAGEMENT,D3DCREATE_DISABLE_DRIVER_MANAGEMENT_EX, D3DCREATE_FPU_PRESERVE, D3DCREATE_HARDWARE_VERTEXPROCESSING, D3DCREATE_MIXED_VERTEXPROCESSING, D3DCREATE_MULTITHREADED, D3DCREATE_NOWINDOWCHANGES, D3DCREATE_PUREDEVICE,D3DCREATE_SOFTWARE_VERTEXPROCESSING。
有些顯卡可以在單張卡上提供多個視頻output。D3DCREATE_ADAPTERGROUP_DEVICE 允許應用程序通過單個設備接口驅動兩個video output,允許資源在兩個output上共享。D3DCREATE_DISABLE_DRIVER_MANAGEMENT和D3DCREATE_DISABLE_DRIVER_MANAGEMENT_EX disable設備資源管理,強迫所有的資源管理髮生在運行時。使用ex 形式當創建資源內存不足的時候會返回錯誤,non-ex形式將不會返回錯誤。
Direct3D使用單精度浮點計算。如果一個應用程序需要FPU更高的精度,有兩種選擇。或者應用程序保證FPU運行在單精度模式下,或者應用程序在執行浮點運算之前預先保存應用程序的FPU精度,當返回時再恢復FPU。
pure device flag 讓是被使用最少的內部狀態跟蹤,可以提高應用程序的performance。
Presentation參數描述了設備顯示在monitor上的渲染參數。presentation的每個成員描述了設備的presentation的行爲,所以在這個函數返回的時候有可能會修改裏面的值,所以presentation必須是可寫的。
typedef struct _D3DPRESENT_PARAMETERS_
{
UINT BackBufferWidth;
UINT BackBufferHeight;
UINT BackBufferFormat;
UINT BackBufferCount;
D3DMULTISAMPLE_TYPE MultiSampleType;
DWORD MultiSampleQuality;
D3DSWAPEFFECT SwapEffect;
HWND hDeviceWindow;
BOOL Windowed;
BOOL EnableAutoDepthStencil;
D3DFORMAT AutoDepthStencilFormat;
DWORD Flags;
UINT FullSCreen_RefreshRateInHz;
UINT PresentationInterval;
}D3DPRESENT_PARAMETERS;
Flags包含D3DPRESENTFLAG_DEVICECLIP, D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL, D3DPRESENTFLAG_VIDEO。
D3DPRESENTFLAG_DEVICECLIP 限制了window模式下客戶端區域present操作的結構,它在WindowXP和Windows 2000下支持。D3DPRESENTFLG_VIDEO flag暗示了back buffer包含有video。D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL 在調用present後discard depth/stencil surface裏面的內容。這樣就使depth/stencil surface是一個可寫的surface。如果depth/stencil surface的格式是D3DFMT_D16_LOCKABLE 或者 D3DFMT_D32_LOCKABLE設置這個標記將會返回一個錯誤。
在window模式下,hDeviceWindow指定了渲染窗口的句柄。如果hDeviceWindow爲null,則focus窗口將是被渲染的窗口。FullScreen_RefreshRateInHz必須是0。
在exclusive模式下,hDeviceWindow指定了應用程序使用的top-level窗口。如果系統裏面有多個設備,只有一個設備能使用hDeviceWindow的focus_window。BackBufferWidth, BackBufferHeight, BackBufferFormat必須等於這個適配器的D3DDISPLAYMODE的相關成員。 FullScreen_PresentationInterval指定了presentation rate和屏幕refresh rate期望的關係。FullScreen_RefreshRateInHz是一個很裏的refresh rate 或者是D3DPRESENT_RATE_DEFUALT默認值。D3DPRESENT_RATE_DEFUALT 指示exclusive 模式下runtime選擇一個合適的refresh rate, window模式下使用當前的refresh rate。
Multiple Monitors
對於多個monitor的系統來說,虛擬桌面由一個包含了所有參與到windows桌面的adapters的有邊界的矩形組成。其他沒有參與進來的adapters也可以附着在這個系統上面。桌面上所有的adapters至少共享一個像素邊界。
應用程序可能想要某個monitor的全屏的顯示,GetAdapterMonitor返回一個Adapter的HMonitor句柄。一旦你有這個句柄,你就可以決定虛擬桌面的哪部分被這個moniter佔用。
Adapter Group Devices
多個adapter能從單張卡里面提供多個不同的video output。當使用D3DCREATE_ADAPTERGROUP_DEVICE創建設備時,需要提供一組D3DPRESENT_PARAMETERS。這個數組的數目不能少於D3DCAPS的NumberOfAdaptersInGroup成員。只有一個D3DPRESENT_PARAMETERS 能使用focus窗口作爲它的設備窗口,剩下的必須使用他們自己的top-level窗口作爲設備窗口。無論創建多少個swap chain,只有一個depth/stencil面被創建。