Direct3D 11 - Initialization


Direct3D 11 初始化步骤

  • 使用D3D11CreateDevice 函数,创建 ID3D11Device 和 ID3D11DeviceContext

ID3D11Device 和 ID3D11DeviceContext 接口是 Direct3D 最重要的接口,认为是图形硬件的软件控制器(software controller),也就是说可以通过这两个接口可以控制图形硬件完成制定工作(比如,申请GPU内存资源,清除后台缓冲区,绑定资源到管线的各个阶段,绘制几何图元),更准确地说,

1. ID3D11Device 接口用于 check feature support,allocate resource
2. ID3D11DeviceContext 接口用于 set render states,bind resource to the graphics pipeline,issue rendering commands

device 和 context 由下面的函数创建:

HRESULT D3D11CreateDevice(
  _In_opt_ IDXGIAdapter *pAdapter,
  D3D_DRIVER_TYPE DriverType,
  HMODULE Software,
  UINT Flags,
  _In_opt_ const D3D_FEATURE_LEVEL *pFeatureLevels,
  UINT FeatureLevels,
  UINT SDKVersion,
  _Out_opt_ ID3D11Device **ppDevice,
  _Out_opt_ D3D_FEATURE_LEVEL *pFeatureLevel,
  _Out_opt_ ID3D11DeviceContext **ppImmediateContext
);

参数含义,详见MSDN SDK文档

创建示例如下:

// Create the device and device context.

UINT createDeviceFlags = 0;

#if defined(DEBUG) || defined(_DEBUG) 
     createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

D3D_FEATURE_LEVEL featureLevel;
HRESULT hr = D3D11CreateDevice(
        0,                 // default adapter
        md3dDriverType,
        0,                 // no software device
        createDeviceFlags,
        0, 0,              // default feature level array
        D3D11_SDK_VERSION,
        &md3dDevice,
        &featureLevel,
        &md3dImmediateContext);

if( FAILED(hr) )
{
    MessageBox(0, L"D3D11CreateDevice Failed.", 0, 0);
    return false;
}

if( featureLevel != D3D_FEATURE_LEVEL_11_0 )
{
    MessageBox(0, L"Direct3D Feature Level 11 unsupported.", 0, 0);
    return false;
}
  • 检查 4X MSAA Quality Support

因为Direct3D 11 默认要支持4X多重采样,所以多重采样质量总大于0。

// Check 4X MSAA quality support for our back buffer format.
// All Direct3D 11 capable devices support 4X MSAA for all render
// target formats, so we only need to check quality support.

HR(md3dDevice->CheckMultisampleQualityLevels(
    DXGI_FORMAT_R8G8B8A8_UNORM, 4, &m4xMsaaQuality));
assert( m4xMsaaQuality > 0 );
  • 描述 Swap Chain

首先我们需要填充一个 DXGI_SWAP_CHAIN_DESC 结构体,用来描述所要创建的 swap chain 的属性,DXGI_SWAP_CHAIN_DESC 定义如下:

typedef struct DXGI_SWAP_CHAIN_DESC {
  DXGI_MODE_DESC  BufferDesc;
  DXGI_SAMPLE_DESC SampleDesc;
  DXGI_USAGE      BufferUsage;
  UINT            BufferCount;
  HWND            OutputWindow;
  BOOL            Windowed;
  DXGI_SWAP_EFFECT SwapEffect;
  UINT            Flags;
} DXGI_SWAP_CHAIN_DESC;

// DXGI_MODE_DESC Definition
typedef struct DXGI_MODE_DESC {
  UINT                    Width;
  UINT                    Height;
  DXGI_RATIONAL            RefreshRate;
  DXGI_FORMAT              Format;
  DXGI_MODE_SCANLINE_ORDER ScanlineOrdering;
  DXGI_MODE_SCALING        Scaling;
} DXGI_MODE_DESC;

定义含义,详见MSDN SDK文档:DXGI_SWAP_CHAIN_DESC, DXGI_MODE_DESC

示例代码:

// Fill out a DXGI_SWAP_CHAIN_DESC to describe our swap chain.

DXGI_SWAP_CHAIN_DESC sd;
sd.BufferDesc.Width  = mClientWidth;
sd.BufferDesc.Height = mClientHeight;
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
sd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;

// Use 4X MSAA?
if( mEnable4xMsaa )
{
    sd.SampleDesc.Count   = 4;
    sd.SampleDesc.Quality = m4xMsaaQuality-1;
}
// No MSAA
else
{
    sd.SampleDesc.Count   = 1;
    sd.SampleDesc.Quality = 0;
}

sd.BufferUsage  = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.BufferCount  = 1;
sd.OutputWindow = mhMainWnd;
sd.Windowed     = true;
sd.SwapEffect   = DXGI_SWAP_EFFECT_DISCARD;
sd.Flags        = 0;
  • 创建 Swap Chain
// To correctly create the swap chain, we must use the IDXGIFactory that was
// used to create the device.  If we tried to use a different IDXGIFactory instance
// (by calling CreateDXGIFactory), we get an error: "IDXGIFactory::CreateSwapChain:
// This function is being called with a device from a different IDXGIFactory."

IDXGIDevice* dxgiDevice = 0;
HR(md3dDevice->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgiDevice));

IDXGIAdapter* dxgiAdapter = 0;
HR(dxgiDevice->GetParent(__uuidof(IDXGIAdapter), (void**)&dxgiAdapter));

IDXGIFactory* dxgiFactory = 0;
HR(dxgiAdapter->GetParent(__uuidof(IDXGIFactory), (void**)&dxgiFactory));

HR(dxgiFactory->CreateSwapChain(md3dDevice, &sd, &mSwapChain));

ReleaseCOM(dxgiDevice);
ReleaseCOM(dxgiAdapter);
ReleaseCOM(dxgiFactory);
  • 创建 Render Target View

因为我们不能将 resource 直接绑定到渲染管线的一个阶段,而只能将 resource view 绑定到管线的一个阶段。所以,为了将back buffer 绑定到 output merge stage,我们需要创建一个后台缓冲区的视图,如下,

ID3D11RenderTargetView* mRenderTargetView;
ID3D11Texture2D* backBuffer;
mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D),  // 0 表示后台缓冲区的索引地址,因为现在只有一块后台缓冲区,所以设置为0
     reinterpret_cast<void**>(&backBuffer));
md3dDevice->CreateRenderTargetView(backBuffer, 0, &mRenderTargetView);
ReleaseCOM(backBuffer);
  • 创建 深度/模板 缓冲区和视图

我们需要创建 深度/模板 缓冲区。深度缓冲区是一块保存深度数据的2D纹理。为了创建一块2D Texture,首先需要填充 D3D11_TEXTURE2D_DESC 结构体,然后调用 ID3D11Device::CreateTexture2D 方法。

typedef struct D3D11_TEXTURE2D_DESC {
  UINT            Width;
  UINT            Height;
  UINT            MipLevels;
  UINT            ArraySize;
  DXGI_FORMAT      Format;
  DXGI_SAMPLE_DESC SampleDesc;
  D3D11_USAGE      Usage;
  UINT            BindFlags;
  UINT            CPUAccessFlags;
  UINT            MiscFlags;
} D3D11_TEXTURE2D_DESC;

结构体每一个成员的含义见MSDN SDK文档

示例代码:

// Create the depth/stencil buffer and view.

D3D11_TEXTURE2D_DESC depthStencilDesc;

depthStencilDesc.Width     = mClientWidth;
depthStencilDesc.Height    = mClientHeight;
depthStencilDesc.MipLevels = 1;
depthStencilDesc.ArraySize = 1;
depthStencilDesc.Format    = DXGI_FORMAT_D24_UNORM_S8_UINT;

// Use 4X MSAA? --must match swap chain MSAA values.
if( mEnable4xMsaa )
{
    depthStencilDesc.SampleDesc.Count   = 4;
    depthStencilDesc.SampleDesc.Quality = m4xMsaaQuality-1;
}
// No MSAA
else
{
    depthStencilDesc.SampleDesc.Count   = 1;
    depthStencilDesc.SampleDesc.Quality = 0;
}

depthStencilDesc.Usage          = D3D11_USAGE_DEFAULT;
depthStencilDesc.BindFlags      = D3D11_BIND_DEPTH_STENCIL;
depthStencilDesc.CPUAccessFlags = 0;
depthStencilDesc.MiscFlags      = 0;

HR(md3dDevice->CreateTexture2D(&depthStencilDesc, 0, &mDepthStencilBuffer));

创建 depth/stencil buffer 之后,创建视图:

HR(md3dDevice->CreateDepthStencilView(
mDepthStencilBuffer,  // Resource we want to create a view to
0,                    //  D3D11_DEPTH_STENCIL_VIEW_DESC, 因为创建 depth/stencil buffer view,所以可以指定为0
&mDepthStencilView));  // Return depth/stencil view
  • 将渲染目标视图和深度/模板视图绑定到渲染管线的 Output Merge Stage
// Bind the render target view and depth/stencil view to the pipeline.

md3dImmediateContext->OMSetRenderTargets(1, &mRenderTargetView, mDepthStencilView);

第一个参数表示要绑定的render target 的个数,这里只绑定一个render target 。我们可以将这些视图绑定到多个渲染目标。第二个参数是将要绑定的渲染目标数组的第一个元素的指针。第三个参数是将要绑定到管线的 depth/stencil buffer view。注意,只能设置一个深度/模板视图。

  • 设置Viewport

通常情况,我们会把3D场景渲染到整个back buffer中,但是,有时候我们想只绘制back buffer的一个子矩形区域中,后台缓冲的子矩形区域叫作视口(Viewport)。设置适口需要先填充一个 D3D11_VIEWPORT 结构体,示例代码如下:

// Set the viewport transform.

mScreenViewport.TopLeftX = 0;
mScreenViewport.TopLeftY = 0;
mScreenViewport.Width    = static_cast<float>(mClientWidth);
mScreenViewport.Height   = static_cast<float>(mClientHeight);
mScreenViewport.MinDepth = 0.0f;
mScreenViewport.MaxDepth = 1.0f;

md3dImmediateContext->RSSetViewports(1, &mScreenViewport);

D3D11_VIEWPORT 结构体每一个成员的含义,见MSDN SDK文档