这一章我们首先将描述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面被创建。