设备


Microsoft® Direct3D®设备是Direct3D的渲染部件,它封装并储存渲染状态。此外,Direct3D设备还执行变换和光照操作以及把图像光栅化到表面上。

从结构上讲,Direct3D设备包含一个变换模块、一个光照模块和一个光栅化模块,如下图所示。

当前Direct3D支持两种主要类型的Direct3D设备:一种是硬件抽象层(HAL)设备,HAL设备具有硬件光栅化加速,并可用软硬件顶点处理进行着色操作;另一种是参考设备。

可以认为这些设备是两个单独的驱动程序。软件和参考设备由软件驱动程序表示,而HAL设备由硬件驱动程序表示。这些设备最通常的用法是使用HAL设备发行应用程序,而用参考设备做特性测试。这些参考设备由第三方提供用于模拟特定的设备——例如,尚未发布的还在开发中的硬件。

应用程序创建的设备必须与应用程序所运行的图形硬件的能力相对应。Direct3D以两种方式提供对渲染的支持,一种是访问安装在计算机中的三维硬件,另一种是用软件模拟三维硬件的能力。因此,对于硬件访问和软件模拟,Direct3D都提供了相应的设备。

硬件加速设备提供了比软件设备好得多的性能。HAL设备类型在所有支持Direct3D的图形适配器上都可用。在大多数情况下,应用程序将具有硬件加速的计算机作为目标平台,并依赖软件模拟以适应低端计算机。

除了参考设备,软件设备并不总是支持与硬件设备相同的特性。应用程序应该总是查询设备能力以确定该设备支持的特性。

因为Microsoft DirectX® 9.0提供的软件设备和参考设备的行为与HAL设备完全相同,所以针对HAL设备开发的应用程序代码也可以用在软件设备或参考设备上而无需修改。注意,虽然Direct3D提供的软件设备或参考设备的行为与HAL设备完全相同,但是设备的能力肯定会不同,某特定的软件设备可能仅实现一个较小的能力集。

行为

Direct3D允许应用程序指定设备的行为和设备的类型。IDirect3D9::CreateDevice方法允许用一个或多个行为标志的组合控制Direct3D设备的整体行为。这些标志指定让Direct3D运行库维护哪些以及不维护哪些行为,同时设备类型指定使用哪个驱动程序。虽然有些设备行为标志的组合是无效的,但是在各种不同类型的设备上还是有可能会用到所有的设备行为标志的。例如,在一个用D3DCREATE_PUREDEVICE标志创建的设备上指定D3DDEVTYPE_SW是有效的。


设备类型


HAL设备

最主要的设备类型是硬件抽象层(HAL)设备,它支持硬件光栅化加速和软硬件顶点处理。如果应用程序在一台安装了支持Microsoft® Direct3D®的显示适配器的计算机上运行,那么应用程序应该用它进行三维操作。Direct3D HAL设备用硬件实现变换、光照及光栅化模块的部分或全部。

应用程序不直接访问三维加速卡,它们调用Direct3D的函数和方法,而Direct3D通过HAL访问硬件。如果应用程序在支持HAL的计算机上运行,那么通过使用HAL设备它将获得最佳的性能。

要在C++程序中创建一个HAL设备,应该调用IDirect3D9::CreateDevice方法,并将D3DDEVTYPE_HAL常数作为设备类型传入。

注意   硬件设备不能渲染到8位渲染目标表面。

参考设备

Direct3D支持另一种称为参考设备或参考光栅化器的设备类型。与软件设备不同的是,参考光栅化器支持所有Direct3D特性。因为这些特性的实现是为了精确而不是为了速度,并且是用软件实现,所以结果不会很快。虽然参考光栅化器尽可能地使用了特殊的CPU指令,但它不是为正式零售的应用程序准备的。应该仅把参考光栅化器用于特性测试或演示用途。

要在C++程序中创建一个参考设备,应该调用IDirect3D9::CreateDevice方法,并将D3DDEVTYPE_REF常数作为设备类型传入。


创建设备


注意   Microsoft® Direct3D®对象创建的所有渲染设备共享相同的物理资源。虽然应用程序可以从单个Direct3D对象创建多个渲染设备,但因为它们共享相同的硬件,所以会导致严重的性能下降。

要在C++应用程序中创建一个Direct3D设备,应用程序必须先创建一个Direct3D对象,如Direct3D对象中所述。

首先,初始化用于创建Direct3D设备的D3DPRESENT_PARAMETERS结构的值。以下示例代码指定了一个窗口应用程序,其后缓存(back buffer)只有在垂直回扫(VSYNC)时才翻转到前缓存(front buffer)。

LPDIRECT3DDEVICE9 pDevice = NULL;

 

D3DPRESENT_PARAMETERS d3dpp;

 

ZeroMemory( &d3dpp, sizeof(d3dpp) );

d3dpp.Windowed   = TRUE;

d3dpp.SwapEffect = D3DSWAPEFFECT_COPY;

下一步,创建Direct3D设备。以下IDirect3D9::CreateDevice调用指定了默认的适配器,硬件抽象层(HAL)设备,以及软件顶点处理。

if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,

                                  D3DCREATE_SOFTWARE_VERTEXPROCESSING,

                                      &d3dpp, &d3dDevice ) ) )

    return E_FAIL;

注意,对创建、释放或重置设备的调用应该仅发生在焦点窗口的窗口处理函数所在的线程中。

创建设备后,应该设置它的状态。


选择设备


要检测硬件支持的Microsoft® Direct3D®设备类型,应用程序可以对硬件进行查询。本节包含了枚举显示适配器和选择Direct3D设备所涉及到的主要任务的信息。

要选择适当的Direct3D设备,应用程序必须执行一系列任务。注意以下步骤是全屏应用程序使用的。大多数情况下,窗口应用程序可以跳过其中的多数步骤。

  1. 首先,应用程序必须枚举系统中的显示适配器。一个适配器是一块物理硬件。注意图形卡可能包含一个以上适配器,如双头显示的情况。与多显示器无关的应用程序可以忽略这一步,并在第2步时将D3DADAPTER_DEFAULT参数传给IDirect3D9::EnumAdapterModes方法。
  2. 对每一个适配器,应用程序通过调用IDirect3D9::EnumAdapterModes枚举该适配器支持的显示模式。
  3. 如果需要,应用程序可以通过调用IDirect3D9::CheckDeviceType检查每一个被枚举的显示模式是否具备硬件加速,如以下示例代码所示。注意这仅是IDirect3D9::CheckDeviceType的用法之一,更多细节请参阅检测硬件支持

D3DPRESENT_PARAMETERS Params;

// 初始化D3DPRESENT_PARAMETERS成员的值。

 

Params.BackBufferFormat = D3DFMT_X1R5G5B5;

 

if(FAILED(m_pD3D->CheckDeviceType(Device.m_uAdapter,

                                  Device.m_DevType,

                                  Params.BackBufferFormat, Params.BackBufferFormat,

                                  FALSE)))

        return E_FAIL;

  1. 应用程序通过调用IDirect3D9::GetDeviceCaps方法检查此适配器上的设备对希望使用的功能的支持度。此方法会过滤掉不支持所需的功能的设备。对经由IDirect3D9::CheckDeviceType验证过所有显示模式的设备,可以保证IDirect3D9::GetDeviceCaps返回的设备能力是不变的。
  2. 若设备支持被枚举的显示模式,则设备总是可以渲染到这些格式的表面。若应用程序需要渲染到不同格式的表面,则可以调用IDirect3D9::CheckDeviceFormat。若设备可以渲染到这种格式的表面,则可以保证所有IDirect3D9::GetDeviceCaps返回的能力都是可用的。
  3. 最后,应用程序可以使用IDirect3D9::CheckDeviceMultiSampleType方法检测设备是否支持多重取样技术,如全屏抗锯齿。

完成以上步骤后,应用程序应该有一个可用的显示模式列表。最后一步是验证有足够的设备可存取内存可供使用,以容纳所需数量的缓存和抗锯齿所需的内存。这个测试是必需的,因为不同显示模式和多重取样的组合所消耗的内存不通过验证是无法预测的。此外,有些显示适配器的体系结构可能无法保证设备可存取内存的数量保持不变。这意味着当切换到全屏模式时,应用程序应该有能力报告视频内存(此后简称为显存)用尽的错误。一般来说,应用程序应该在提供给用户的模式列表中移除全屏模式,或者应该通过减少后缓存的数量或使用不太复杂的多重取样技术以试图消耗较少的内存。

窗口应用程序执行一系列类似的操作。

  1. 检测被窗口的客户区覆盖的桌面矩形。
  2. 枚举适配器,找到覆盖该客户区的相应适配器。如果客户区被一个以上适配器拥有,那么应用程序可以选择单独处理每个适配器,或仅处理单个适配器,并由Direct3D在presentation时把像素从一个设备上传送到另一设备上。应用程序也可以忽略以上两步并使用D3DADAPTER_DEFAULT。注意当窗口被放置在第二个显示器上时,运行速度可能会比较慢。
  3. 应用程序应该调用IDirect3D9::CheckDeviceType检测在桌面模式下设备是否支持渲染到指定格式的后缓存。IDirect3D9::GetAdapterDisplayMode可用于检测桌面显示格式,如以下示例代码所示。

D3DPRESENT_PARAMETERS Params;

// 初始化D3DPRESENT_PARAMETERS成员的值。

 

// 使用当前的显示模式。

D3DDISPLAYMODE mode;

 

if(FAILED(m_pD3D->GetAdapterDisplayMode(Device.m_uAdapter , &mode)))

        return E_FAIL;

 

Params.BackBufferFormat = mode.Format;

 

if(FAILED(m_pD3D->CheckDeviceType(Device.m_uAdapter, Device.m_DevType,

Params.BackBufferFormat, Params.BackBufferFormat, FALSE)))

       return E_FAIL;


丢失的设备


一个Microsoft® Direct3D®可以处于操作状态或丢失状态。操作状态是设备的正常状态,设备按预期运行并present所有渲染结果。当事件发生时,如全屏应用程序失去键盘输入焦点,设备就转变到丢失状态,这会导致渲染无法进行。丢失状态表现为所有渲染操作的悄然失败,这意味着即使渲染操作失败所有的渲染方法仍可以返回成功码。在这种情况下,IDirect3DDevice9::Present返回错误码D3DERR_DEVICELOST。

Direct3D有意没有对可能导致设备丢失的所有情况进行详细说明。一些典型的例子包括窗口失去焦点,例如用户按下了ALT+TAB或弹出了一个系统对话框。设备也会因为电源管理事件而丢失,或者另一个应用程序进行全屏操作。另外,任何对IDirect3DDevice9::Reset调用的失败会把设备置为丢失状态。

注意   可以保证所有继承自IUnknown的方法在设备丢失后仍能正常工作。设备丢失后,每个函数一般有三种可能:

对丢失的设备作出响应

设备在被重置后,应该重新创建资源(包括显存资源)。如果设备丢失了,那么应用程序应该查询设备状态,看是否可以将之恢复回操作状态。如果不行,那么就等到设备可以被恢复为止。

如果设备可以被恢复,那么应用程序应该销毁所有显存资源和交换链,并准备恢复。然后,应用程序调用IDirect3DDevice9::Reset方法。Reset方法是当设备丢失时唯一有效的方法,并且是应用程序可用来把设备从丢失状态恢复到操作状态的唯一方法。除非应用程序释放所有在D3DPOOL_DEFAULT中分配的资源,包括用IDirect3DDevice9::CreateRenderTargetIDirect3DDevice9::CreateDepthSstencilSurface方法创建的资源,否则Reset将会失败。

Direct3D中大部分被频繁调用的方法不返回任何关于设备是否已丢失的信息。应用程序可以继续调用渲染方法,如IDirect3DDevice9::DrawPrimitive,而不会收到设备丢失的通知。在Direct3D内部,这些操作被抛弃,直到设备被重置为操作状态为止。

通过查询IDirect3DDevice9::TestCooperativeLevel方法的返回值,应用程序可以决定在遇到设备丢失时如何处理。

锁定操作

为了确保在设备丢失后锁定操作仍会成功,Direct3D在内部做了很多工作,但是,它并不保证在锁定操作时显存资源中的数据是正确的,Direct3D只保证不返回错误代码。这使得在编写应用程序时,不必关心在锁定操作时设备丢失与否。

资源

资源会消耗显存。因为丢失的设备与适配器拥有的显存的连接被切断,所以当设备丢失时不可能保证显存的分配。因此,在这种情况下,所有用于创建资源的方法都被实现为成功返回D3D_OK,而实际上只分配假的系统内存。因为在调整设备的大小之前任何显存资源必须被销毁,所有不会存在显存被过度分配的问题。这些假的表面使锁定和复制操作看起来运行正常,直到应用程序调用IDirect3DDevice9::Present并发现设备已经丢失。

在可以把设备从丢失状态重置为操作状态前,所有显存必须被释放。这意味着应用程序应该释放所有用IDirect3DDevice9::CreateAdditionalSwapChain创建的交换链和所有放在D3DPOOL_DEFAULT内存类型中的资源。应用程序无需释放在D3DPOOL_MANAGED或D3DPOOL_SYSTEMMEM内存类型中的资源。其余状态数据在改变到操作状态时会被自动销毁(译注:如后缓存)。

Direct3D鼓励在开发应用程序时用同一部分代码对设备丢失做出响应。这部分代码和启动应用程序时用于初始化设备的那部分代码即使不完全相同,也应该差不多。

取回的数据

Direct3D允许应用程序通过IDirect3DDevice9::ValidateDevice对照单次渲染用硬件验证纹理和渲染状态。这种方法一般在应用程序初始化时调用,如果设备已经丢失,那么该方法将返回D3DERR_DEVICELOST。

Direct3D也允许应用程序把生成的(译注:即渲染得到的)或以前写入到显存中的图像从显存资源中复制到非易失性的(nonvolatile)内存资源中。因为这类传输中的源图像可能在任何时候丢失,所以当设备丢失时Direct3D允许这类操作失败。

关于异步查询,如果使用了FLUSH标志,那么IDirect3DQuery9::GetData将返回D3DERR_DEVICELOST,这是为了告诉应用程序IDirect3DQuery9::GetData方法根本不会返回S_OK,。

当设备丢失时,因为不存在主表面,所以复制操作IDirect3DDevice9::GetFrontBufferData会失败,返回值为D3DERR_DEVICELOST。当设备丢失时,IDirect3DDevice9::CreateAdditionalSwapChain也会因为无法创建后缓存而失败,并返回D3DERR_DEVICELOST。注意除了IDirect3DDevice9::PresentIDirect3DDevice9::TestCooperativeLevelIDirect3DDevice9::Reset方法之外, D3DERR_DEVICELOST返回值只可能出现在以上两种情况中。

可编程着色器

Microsoft DirectX® 9.0中,Vertex Shader 1_1Pixel Shader 1_XReset后不需要被重置,它们会被记住。在DirectX的前一个版本中,设备丢失后需要重新创建着色器。


检测硬件支持


Microsoft® Direct3D®为检测硬件支持提供了以下函数。

用于验证某个表面格式是否可被用作纹理,某个表面格式是否可同时被用作纹理和渲染目标,或某个表面格式是否可被用作深度/模板缓存。另外,这个方法可用于验证对深度缓存格式的支持和对深度/模板缓存格式的支持。

用于验证设备执行硬件加速的能力,设备创建presentation交换链的能力,或设备渲染到当前显示格式的能力。

用于验证是否某个深度/模板缓存格式与某个渲染目标格式相兼容。注意在调用此方法前,应用程序应该同时对深度/模板格式和渲染目标格式调用IDirect3D9::CheckDeviceFormat


处理顶点数据


IDirect3DDevice9接口同时支持软件和硬件顶点处理。一般来说,设备的软件和硬件顶点处理能力是不完全一样的。硬件能力是可变的,取决于显示适配器和驱动程序,而软件能力则是固定的。

下列标志控制硬件抽象层(HAL)和参考设备的顶点处理状态。

当调用IDirect3D9::CreateDevice时,要指定以上顶点处理状态标志中的一个。混合模式标志允许设备同时执行软件和硬件顶点处理。在任意时刻,只能给某个设备设置一个顶点处理标志。注意当创建一个纯设备(pure device,D3DCREATE_PUREDEVICE)时需要设置D3DCREATE_HARDWARE_VERTEXPROCESSING标志。

为了避免在一个设备上的双重顶点处理能力,只有硬件顶点处理能力可以在运行的时候查询。软件顶点处理能力是固定的,不能在运行的时候查询。

要确定设备的硬件顶点处理能力,可以参考D3DCAPS9结构的VertexProcessingCaps成员。软件顶点处理支持以下能力。

另外,下表列出了在软件顶点处理模式下设备的D3DCAPS9结构成员的值。

成员

软件顶点处理能力

MaxActiveLights

没有限制

MaxUserClipPlanes

6

MaxVertexBlendMatrices

4

MaxStreams

16

MaxVertexIndex

0xFFFFFFFF

软件顶点处理提供了一个可以保证的顶点处理能力集,包括不限数量的光源和对可编程顶点着色器的完全支持。在使用HAL设备时,可在任何时间在软件和硬件顶点处理间切换。HAL设备是唯一同时支持软件和硬件顶点处理的设备类型,但唯一的要求是用于软件顶点处理的顶点缓存必须分配在系统内存中。

注意   硬件顶点处理和软件顶点处理的性能是相当的,因此在一个设备类型中同时提供顶点处理的硬件加速和软件仿真是个不错的想法。而光栅化器则不同,主处理器比特定的图形硬件慢得多,因此在一个设备类型中不会同时提供硬件和软件仿真。软件顶点处理是在一个设备类型中Direct3D运行库和硬件(驱动程序)功能重复的唯一例子,因此所有其余的设备能力代表了由驱动程序提供的潜在可变的功能。


设备支持的图元类型


Microsoft® Direct3D®设备可以创建并管理以下类型的图元。

可以在C++应用程序中用IDirect3DDevice9接口提供的任何渲染方法渲染各种类型的图元。


点表


点表是一个顶点的集合,被渲染为孤立的点。应用程序可以将它们用于星空,或多边形表面的虚线。

下图描绘了一个渲染得到的点表。

应用程序可以将材质和纹理应用于点表。材质和纹理的颜色只影响绘制得到的那些点,不会影响各点之间的任何其它地方。

以下示例代码显示了如何为这个点表创建顶点。

struct CUSTOMVERTEX

{

    float x,y,z;

};

 

CUSTOMVERTEX Vertices[] =

{

    {-5.0, -5.0, 0.0},

    { 0.0,  5.0, 0.0},

    { 5.0, -5.0, 0.0},

    {10.0,  5.0, 0.0},

    {15.0, -5.0, 0.0},

    {20.0,  5.0, 0.0}

};

以下示例代码显示了如何使用IDirect3DDevice9::DrawPrimitive渲染这个点表。

//

// 这里假定d3dDevice是指向IDirect3DDevice9接口的有效指针

//

d3dDevice->DrawPrimitive( D3DPT_POINTLIST, 0, 6 );


线表


线表是一个孤立线段的集合。线表对诸如给三维场景加入雨雪或大雨这样的任务很有用。

下图描绘了一个渲染得到的线表。

可以将材质和纹理用于线表。材质和纹理的颜色只会影响绘制得到的那些线段,而不会影响各线段之间的任何其它地方。

以下示例代码显示了如何为这个线表创建顶点。

struct CUSTOMVERTEX

{

    float x,y,z;

};

 

CUSTOMVERTEX Vertices[] =

{

    {-5.0, -5.0, 0.0},

    { 0.0,  5.0, 0.0},

    { 5.0, -5.0, 0.0},

    {10.0,  5.0, 0.0},

    {15.0, -5.0, 0.0},

    {20.0,  5.0, 0.0}

};

以下示例代码显示了如何使用IDirect3DDevice9::DrawPrimitive渲染这个线表。

//

// 这里假定d3dDevice是指向IDirect3DDevice9接口的有效指针

//

d3dDevice->DrawPrimitive( D3DPT_LINELIST, 0, 3 );


线带


线带是由相互连接的线段组成的图元。应用程序可以将线段带用于创建不封闭的多边形,封闭多边形是最后一个顶点与第一个顶点通过一条线段相连的多边形。如果应用程序使用基于线段带的多边形,那么不能保证顶点是共面的。

下图描绘了一个渲染得到的线段带。

以下示例代码显示了如何为这个线段带创建顶点。

struct CUSTOMVERTEX

{

    float x,y,z;

};

 

CUSTOMVERTEX Vertices[] =

{

    {-5.0, -5.0, 0.0},

    { 0.0,  5.0, 0.0},

    { 5.0, -5.0, 0.0},

    {10.0,  5.0, 0.0},

    {15.0, -5.0, 0.0},

    {20.0,  5.0, 0.0}

};

以下示例代码显示了如何使用IDirect3DDevice9::DrawPrimitive渲染这个线段带。

//

// 这里假定d3dDevice是指向IDirect3DDevice9接口的有效指针

//

d3dDevice->DrawPrimitive( D3DPT_LINESTRIP, 0, 5 );


三角形表


三角形表是一个孤立三角形的集合。它们可能也可能不彼此相邻。三角形表必须至少有三个顶点,全部顶点的数量必须是三的倍数。

可以将三角形表用于创建由不相连的部分组成的物体。例如,在三维游戏中创建一个能量场的一种方法就是指定一个很大的三角形表,它由许多小且不相连的三角形组成的。然后将看起来发光的材质和纹理应用于三角形表。墙上的每个三角形看起来都发光。穿过三角形之间的缝隙,可以看到墙后面的一部分,这就和玩家看一个能量场时所应该看到的一样。

三角形表在创建具有锋利边缘并使用高洛德着色算法进行着色的物体时同样有用。请参阅面和顶点法向量

下图描绘了一个渲染得到的三角形表。

以下示例代码显示了如何为这个三角形表创建顶点。

struct CUSTOMVERTEX

{

    float x,y,z;

};

 

CUSTOMVERTEX Vertices[] =

{

    {-5.0, -5.0, 0.0},

    { 0.0,  5.0, 0.0},

    { 5.0, -5.0, 0.0},

    {10.0,  5.0, 0.0},

    {15.0, -5.0, 0.0},

    {20.0,  5.0, 0.0}

 

};

以下示例代码显示了如何使用IDirect3DDevice9::DrawPrimitive渲染这个三角形表。

//

// 这里假定d3dDevice是指向IDirect3DDevice9接口的有效指针