设备、交换链和设备环境如何创建
设备环境是什么?创建设备、交换链和设备环境通过什么方法调用?
设备和交换链能够通过Direct3D函数全部创建,这个函数就是D3D11CreateDeviceAndSwapChain。当然你也可以调用每一具体类型的D3D函数创建(如CreateSwapChain仅仅创建交换链)。这个函数最后一个参数也会返回创建的设备环境(渲染环境),Direct3D渲染环境是一种告诉设备怎样绘制的渲染环境,它包括渲染状态和其它的绘制信息。
D3D11CreateDeviceAndSwapChain函数的参数值如下:- [in]一个用于创建设备的视频适配器(即显卡)的指针。如果传进NULL,则D3D使用默认显卡。如果在机器上装有多个显卡,就可启用该参数。
- [in]我们希望创建的驱动设备类型(例如:硬件设备,WARP设备,软件设备或参考设备)。
- [in]实现软件渲染设备的动态库句柄。如果使用的驱动设备类型是软件设备,则该参数不能为NULL。
- [in]创建标志。D3D中的创建标志0用于我们的游戏发布,而标志D3D11_CREATE_DEVICE_DEBUG则允许我们创建可供调试的设备,在开发中这是比较有用的。
- [in]我们所希望创建的特性级别(也称特征级别),以我们的希望值排序进行安排。因为这个参数是一个指针,所以你可以传一个数组进来。
- [in]特性级别数组中的特性数量。(例如数组中有D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, D3D_FEATURE_LEVEL_9_1的话,特性数量就是数组大小3。)
- [in]SDK 版本号。因为我们只使用DirectX 11 SDK,所以书中都是D3D11_SDK_VERSION。
- [in]用于描述交换链的对象。(就是用于描述我们将创建的交换链的一个结构体,我们依赖这个结构体的内容来创建交换链)
- [out]该函数调用成功后保存我们创建好的设备。
- [out]该函数调用成功后保存我们成功创建的特征级别。
- [out]该函数调用成功后保存设备的渲染环境。
第二个参数,如何创建出条件允许下,我们最希望的设备类型?
我们可以采用循环,循环中依次遍历按我们希望的设备类型排序的数组,循环体中调用D3D11CreateDeviceAndSwapChain函数,如果调用失败则继续循环,调用成功则退出循环。
首先我们创建一个按我们希望的设备类型排序的数组:D3D_DRIVER_TYPE driverTypes[] = { D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE_WARP, D3D_DRIVER_TYPE_REFERENCE, D3D_DRIVER_TYPE_SOFTWARE };
接着再保存这个数组大小,以便我们用来做循环的条件,ARRAYSIZE是windows的api,可以得到数组的大小:
unsigned int totalDriverTypes = ARRAYSIZE( driverTypes );
每次调用D3D11CreateDeviceAndSwapChain函数时,第二个参数就依次填这个数组的元素值。
第四个参数,如何让编译器根据是否在debug调试状态,来传进不同的实参?
我们可以用一个宏判断编译器是否在debug调试状态。
首先我们创建一个用于保存状态标记的变量:unsigned int creationFlags = 0;
然后通过宏来让编译器是否在调试状态来决定creationFlags的值:
ifdef _DEBUG creationFlags |= D3D11_CREATE_DEVICE_DEBUG; //按位与或 #endif
特性级别也需要用一个循环遍历吗?
不需要,因为该函数已经提供一个可以传入数组的参数(第5个参数)和一个指定数组大小的参数(第6个参数),我们只需要填好这两个参数,该函数在内部就会我们依次遍历特性级别。
这和我们上面写的创建最希望的设备数组步骤一样。
首先,我们创建一个特性级别数组:D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0 }
然后保存数组大小:
unsigned int totalFeatureLevels = ARRAYSIZE( featureLevels );
需要描述一个交换链的什么内容?
一个交换链的描述用于我们想创建怎样的交换链,该描述交换链的结构体有如下成员:
- 缓存页数量(用于页面翻转的主/辅助缓存)。
- 缓存页的宽度和高度。
- 我们交换链中缓存的用法被设置为
DXGI_USGAE_RENDER_TARGET_OUTPUT,意味着该交换链能够用于输出,即它能够被渲染。 缓存格式。
有时你会被要求提供具体的DXGI格式。不同格式用于描述不同的事物,比如描述图像的布局,每一颜色使用的bits数,或者对于顶点缓存的顶点布局。最常见的是,DXGI格式用于描述交换链中的缓存布局。DXGI格式不是具体的某一类型的数据,只是描述它们是什么结构。
一个DXGI格式的例子是,DXGI_FORMAT_R8G8B8A8_UNORM用于描述数据对于RGBA的每一部分用8bits来存储。当定义顶点,使用DXGI_FORMAT_R32G32B32_FLOAT格式时,将对于三个部分都用32bits来存储。尽管格式可以是RGB,它只是描述数据的布局,而不管数据用于何处。
有时你可以看到对于每部分有相同的比特数但是后缀不同的格式。例如DXGI_FORMAT_R32G32B32A32_FLOAT 和DXGI_FORMAT_R32G32B32A32_UINT,这表示它们描述的对象的每一部分使用两种格式都使用相同数量的bit,但 是包含这些bit 的数据类型不同。这里考虑全部类型的格式。
对于没有声明类型的对象可以用无类型的格式。使用此种格式表明对象的每一部分使用的bits 数量而不关心所包含的数据类型,例如DXGI_FORMAT_R32G32B32A32_TYPELESS格式。公共格式列表见于表2.1 中。表2.1:Direct3D 公共格式
格式 描述
DXGI_FORMAT_R32G32B32A32_TYPELESS 组成无类型RGBA 分量的128 位格式
DXGI_FORMAT_R32G32B32A32_FLOAT 浮点类型RGBA 分量的128 位格式
DXGI_FORMAT_R32G32B32A32_UINT 无符号整型RGBA 分量的128 位格式
DXGI_FORMAT_R32G32B32A32_SINT 有符号整型RGBA 分量的128 位格式
DXGI_FORMAT_R8G8B8A8_TYPELESS 组成无类型RGBA 分量的32 位格式
DXGI_FORMAT_R8G8B8A8_UINT 无符号整型RGBA 分量的32 位格式
DXGI_FORMAT_R8G8B8A8_SINT 有符号整型RGBA 分量的32 位格式刷新率,用于决定刷新显示的频率(使用60/1表示60Hz的刷新频率,这也是交换链的交换频率)
- 窗口句柄(与CreateWindow函数创建的一样,指定我们要绘制的窗口)
- 用于是否应用窗口模式,true表示窗口模式,false当然表示全屏模式。
- 取样描述中的取样数量和质量。取样描述定义了D3D中多重取样性质,多重取样是一种用于像素间的取样和利用平均值渲染来创建在形状颜色间平滑过渡的技术。我们试图使用多重取样来减少所谓的锯齿边(即阶梯效果)。如果锯齿边很多,那游戏看起来就像“我的世界”那样,除非是游戏风格故意这样设定,锯齿边往往会使玩家感觉游戏画面不美观、不逼真。如下图:
下面我们就创建一个交换链描述对象:
RECT dimensions; GetClientRect( hwnd, &dimensions ); unsigned int width = dimensions.right - dimensions.left; unsigned int height = dimensions.bottom - dimensions.top; DXGI_SWAP_CHAIN_DESC swapChainDesc; ZeroMemory( &swapChainDesc, sizeof( swapChainDesc ) ); swapChainDesc.BufferCount = 1; // swapChainDesc.BufferDesc.Width = width; swapChainDesc.BufferDesc.Height = height; swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; swapChainDesc.BufferDesc.RefreshRate.Numerator = 60; swapChainDesc.BufferDesc.RefreshRate.Denominator = 1; swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapChainDesc.OutputWindow = hwnd; swapChainDesc.Windowed = true; swapChainDesc.SampleDesc.Count = 1; swapChainDesc.SampleDesc.Quality = 0;
最后,附上一个创建设备、交换链和设备环境的示例:
//创建设备和交换链
m_hInstance = hInstance;
m_hwnd = hwnd;
RECT dimensions;
GetClientRect( hwnd, &dimensions );
unsigned int width = dimensions.right - dimensions.left;
unsigned int height = dimensions.bottom - dimensions.top;
D3D_DRIVER_TYPE driverTypes[] =
{
D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE_WARP,
D3D_DRIVER_TYPE_REFERENCE, D3D_DRIVER_TYPE_SOFTWARE
};
unsigned int totalDriverTypes = ARRAYSIZE( driverTypes );
D3D_FEATURE_LEVEL featureLevels[] =
{
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0
};
unsigned int totalFeatureLevels = ARRAYSIZE( featureLevels );
DXGI_SWAP_CHAIN_DESC swapChainDesc;
ZeroMemory( &swapChainDesc, sizeof( swapChainDesc ) );
swapChainDesc.BufferCount = 1;
swapChainDesc.BufferDesc.Width = width;
swapChainDesc.BufferDesc.Height = height;
swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swapChainDesc.BufferDesc.RefreshRate.Numerator = 60;
swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.OutputWindow = hwnd;
swapChainDesc.Windowed = true;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
unsigned int creationFlags = 0;
#ifdef _DEBUG
creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
HRESULT result;
unsigned int driver = 0;
for( driver = 0; driver < totalDriverTypes; ++driver )
{
result = D3D11CreateDeviceAndSwapChain( 0, driverTypes[driver], 0, creationFlags,
featureLevels, totalFeatureLevels,
D3D11_SDK_VERSION, &swapChainDesc, &m_pSwapChain,
&m_pD3DDevice, &m_featureLevel, &m_pD3DContext );
if( SUCCEEDED( result ) )
{
m_driverType = driverTypes[driver];
break;
}
}
if( FAILED( result ) )
{
DXTRACE_MSG( L"Failed to create the Direct3D device!" );
return false;
}