Something about programming

Direct3D 11.1 initialization

Next tutorial: HLSL Shader Compiling in DirectX 11

With every new version of DirectX the creation of first application, which uses 3d graphics, is becoming more harder. In this tutorial we'll go through all steps of Direct3D initialization. But first, we need to understand what is Direct3D 11.

Direct3D

Direct3D is used for rendering 3d graphics. Rendering is creation of 3d scene and drawing it on the screen.

Direct3D 11.1 (the latest verison) consists of next parts: Direct3D Graphics, DXGI, HLSL, DDS. DDS (it's a graphics format) is not used anymore, HLSL (shaders) we don't need yet, but we need to understand first two parts.

Direct3D Graphics

This is the main Direct3D part. There are all main interfaces of Direct3d, which is used for 3d rendering. Here are interfaces and resources. Here you can configure the graphics pipeline. These interfaces starts with ID3D11 prefix, for example: ID3D11Device, ID3D11DeviceContext.

This part is just the buildup on the next part - DXGI.

DXGI

DXGI - DirectX Graphics Infrastructure. DXGI is a low-level commands. All Direct3D stuff communicate with video adapter through DXGI. Just some parts of DXGI coder can use directly, and others are covered by high-level interfaces. These interfaces starts with the IDXGI prefix. DXGI has important interface - IDXGISwapChain. This interface allows to show rendered image in the window.

Important interfaces of Direct3D 11.1

D3D11 device - virtual representation of video adapter in our program. D3D11 devices represented by the interface ID3D11Device. D3D11 devices can create different resources.

Device Context - interface ID3D11DeviceContext. Device context is responsible for rendering. This interface has commands for creation 3d scene.

Swap Chain - interface IDXGISwapChain. Cointains buffers in which 3d scene is rendered. We need better understanding of swap chain.

IDXGISwapChain

When swap chain is created it is binded to the window (HWND) and d3d11 device (ID3D11Device). Every swap chain consists of one and more buffers. Buffer in that case is just a rectangle picture. Size of buffers should be the same as the size of client area of the window. Every swap chain has one front buffer and some back buffers - zero and more.

Several times per second rendering of 3d scene is happening. Created 2d image copied in one of back buffers of swap chain. When back buffer is filled it is changed with front buffer - it is presented. Back buffer\'s content becomes visible in the application window. The main goal of swap chain is to present render of 3d scene to the screen.

Now we can start initialization of Direct3D application.

Direct3D 11 initialization

1. Firstly we must create d3d11 device and bind to it device context and swap chain.
2. Creation of render-target view. Render-target view is a picture where 3d scene is rendered.

After that we can start graphics output.

ID3D11Device creation

In DirectX application all actions are made by interfaces. Except one. First interface is created through function. Main interface in Direct3D 11 - ID3D11Device. To get this interface you can call one of these functions: D3D11CreateDevice, D3D11CreateDeviceAndSwapChain. First function creates only device. Second - d3d11 device and the swap chain. We'll call second function - that's simpler. Let's look at the prototype:

D3D11CreateDeviceAndSwapChain function

HRESULT D3D11CreateDeviceAndSwapChain( _In_ IDXGIAdapter *pAdapter, _In_ D3D_DRIVER_TYPE DriverType, _In_ HMODULE Software, _In_ UINT Flags, _In_ const D3D_FEATURE_LEVEL *pFeatureLevels, _In_ UINT FeatureLevels, _In_ UINT SDKVersion, _In_ const DXGI_SWAP_CHAIN_DESC *pSwapChainDesc, _Out_ IDXGISwapChain **ppSwapChain, _Out_ ID3D11Device **ppDevice, _Out_ D3D_FEATURE_LEVEL *pFeatureLevel, _Out_ ID3D11DeviceContext **ppImmediateContext );

This function gets 12 arguments. This function creates three interfaces and one variable. Let's look at arguments more closely:

1. IDXGIAdapter *pAdapter - pointer to video adapter. Video adapter by default will be used then this argument is NULL. This argument has sense when you have not one video adapter plugged to monitors. In all programs we will set NULL.

2. D3D_DRIVER_TYPE DriverType - driver type. One of values of enumeration D3D_DRIVER_TYPE. We will use D3D_DRIVER_TYPE_HARDWARE - all commands are happening in hardware. Other values make sense if you video adapter doesn't support DirectX 11.

3. HMODULE Software - handle dll library, which is uses as software rasterizer. You should point this argument when previous argument is D3D_DRIVER_TYPE_SOFTWARE. We will set it to NULL.

4. UINT Flags - combination of flag from the enumeration D3D11_CREATE_DEVICE_FLAG. The main flag - D3D11_CREATE_DEVICE_DEBUG. This value starts debug layer - additional conditions of resource binding and shaders and also error descriptions. Direct3D 11 in runtime consists of two layers: core layer and debug layer. Core layer - is a Direct3D API. When you use debug layer you can get additional information. We will learn debug layer in separate tutorial, and before it we will use only core layer. This argument - 0.

5. const D3D_FEATURE_LEVEL *pFeatureLevels - feature level. By now the highest feature level - D3D_FEATURE_LEVEL_11_1. We will use this level. This argument takes pointer to the array. In this array different feature levels are enumerated - from the highest to the lowest. If video adapter doesn't support highest feature level, DirectX will try create d3d11 device with next feature level in the array. If this argument is NULL, then next array will be used:

{ D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, D3D_FEATURE_LEVEL_9_1, };

This array doesn't have D3D_FEATURE_LEVEL_11_1.

6. UINT FeatureLevels - number of elements in the array from previous argument. If you will pass one element (that's I do in attached example), then in previous argument you can pass pointer to variable D3D_FEATURE_LEVEL, not to array.

7. UINT SDKVersion - version of software development kit. For Direct3D 11 you need to pass D3D11_SDK_VERSION.

8. const DXGI_SWAP_CHAIN_DESC *pSwapChainDesc - pointer to structure that describes swap chain.

9. IDXGISwapChain **ppSwapChain - pointer to interface of swap chain.

10. ID3D11Device **ppDevice - pointer to interface of D3D11 device.

11. D3D_FEATURE_LEVEL *pFeatureLevel - in this variable will be stored feature level of video adapter. In all tutorials I will assume that this level is D3D_FEATURE_LEVEL_11_1. This value depends from 5th argument.

12. ID3D11DeviceContext **ppImmediateContext - pointer to d3d11 device context.

After calling D3D11CreateDeviceAndSwapChain we will get variable with feature level and three interfaces: IDXGISwapChain, ID3D11Device, ID3D11DeviceContext. 8th argument - pointer to the structure DXGI_SWAP_CHAIN_DESC. This structure we must fill before calling function D3D11CreateDeviceAndSwapChain. Let\'s see the description of this structure:

DXGI_SWAP_CHAIN_DESC structure

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;

This structure says which properties swap chain should have.

1. DXGI_MODE_DESC BufferDesc - structure which describes buffers: BufferDesc.Width - width, BufferDesc.Height - height, BufferDesc.Format - buffer pixel format, BufferDesc.ScanlineOrdering - rasterization method - not important now, BufferDesc.Scaling - scaling method - not important now, BufferDesc.RefreshRate - refresh rate in hertz, is set by two fields: BufferDesc.RefreshRate.Numerator - numerator and BufferDesc.RefreshRate.Denominator - denominator.

2. DXGI_SAMPLE_DESC SampleDesc - multisampling parameters. This structure consists of two fields: Count and Quality. By default for quality - 0, and for count - 1.

3. DXGI_USAGE BufferUsage - this field set how to use buffer, and also access of central processor to this buffer. We will use buffer for rendering of 3d scene to the window, so we'll use value DXGI_USAGE_RENDER_TARGET_OUTPUT.

4. UINT BufferCount - number of buffers.

5. HWND OutputWindow - window where will be output.

6. BOOL Windowed - this field sets windowed mode.

7. DXGI_SWAP_EFFECT SwapEffect - this field sets what will happen with buffer's content after Present command. Value by default DXGI_SWAP_EFFECT_DISCARD. We'll learn more about this field in next tutorials.

8. UINT Flags - set of flags which is described behavior of swap chain. We will set 0.

Creation of D3D11 device and swap chain

Let's see at the code:

ID3D11Device* dev; // d3d11 device ID3D11DeviceContext* devContext; // device context IDXGISwapChain* sc; // swap chain DXGI_SWAP_CHAIN_DESC sсd; ZeroMemory(&sсd, sizeof(sсd)); sсd.BufferCount = 2; sсd.BufferDesc.Width = 500; sсd.BufferDesc.Height = 500; sсd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; sсd.BufferDesc.RefreshRate.Numerator = 60; sсd.BufferDesc.RefreshRate.Denominator = 1; sсd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; sсd.OutputWindow = hWnd; sсd.SampleDesc.Count = 1; sсd.SampleDesc.Quality = 0; sсd.Windowed = TRUE; D3D_FEATURE_LEVEL FeatureLevels = D3D_FEATURE_LEVEL_11_1; UINT numLevels = 1; D3D_FEATURE_LEVEL FeatureLevel; D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, &FeatureLevels, numLevels, D3D11_SDK_VERSION, &sсd, &sc, &dev, &FeatureLevel, &devContext);

Here we are filling DXGI_SWAP_CHAIN_DESC description and calling D3D11CreateDeviceAndSwapChain function.

Creation of render-target view

Now we need to understand responsibility of every interface and why we need render-target-view. D3D11 device creates different resources. Swap chain presents content of back buffer to the screen. Device context does all rendering. Device context "draws" graphics to the buffer, and swap chain shows this graphics in the window. But there is one problem. Device context can't work with swap chain's buffers directly. For this task there is render-target view. Device context for rendering use interface ID3D11RenderTargetView. So, we need to bind this interface to the back buffer of swap chain. Firstly we get the address of back buffer:

HRESULT IDXGISwapChain::GetBuffer( [in] UINT Buffer, [in] REFIID riid, [in, out] void **ppSurface );

UINT Buffer - buffer index. If we set for swap chain DXGI_SWAP_EFFECT_DISCARD, then this argument is always 0.

REFIID riid - here we are setting type of interface that we want to get. We need to get ID3D11Texture2D (2d picture) interface. Type of this argument is COM stuff. We'll learn COM in separate tutorial. By then we set this argument: __uuidof(ID3D11Texture2D).

void **ppSurface - pointer to interface (in our case - ID3D11Texture2D).

After calling GetBuffer method we have interface ID3D11Texture2D, which have back buffer of the swap chain. Now, using this interface we can create render-target view:

HRESULT ID3D11Device::CreateRenderTargetView( [in] ID3D11Resource *pResource, [in] const D3D11_RENDER_TARGET_VIEW_DESC *pDesc, [out] ID3D11RenderTargetView **ppRTView );

ID3D11Resource *pResource - we pass here interface that we have got in the previous method..

const D3D11_RENDER_TARGET_VIEW_DESC *pDesc - description render-target view. Just pass NULL.

ID3D11RenderTargetView **ppRTView - address of the ID3D11RenderTargetView. This interface will be used for rendering by d3d11 device context.

Let's see at the code of creation of render-target view:

ID3D11RenderTargetView* view; ID3D11Texture2D* backBuffer; sc->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&backBuffer); dev->CreateRenderTargetView(backBuffer, NULL, &view);

That's it. Now we can draw graphics.

Back buffer presenting

For back buffer presenting (when content of back buffer shown in the window) you just need to call Present method:

sc->Present(0, 0);

First argument - synchronization. Set it to zero.
Second argument - flags. Zero too.

If you launch application, you'll see just the blank white window. We didn't make any drawing operations. Let's just change background color:

const float greyColor[4] = { 0.8f, 0.8f, 0.8f, 1.0f }; devContext->ClearRenderTargetView( view, greyColor);

For device context we are calling ClearRenderTargetView method. First argument - render-target view, which should be cleared, second - color by which should filled scene. Color is set by array of 4 floats (red, green, blue, alpha). Add this code before calling Present and you'll see such window:

Conclusion

We've learned initialization of Direct3D application. By now you must understand logic of initialization. I hope I've made clear explanation of this process.

Comments:

No comments yet