Before you can use the magic that is DirectX to command your graphics card to do amazing things, you must first initialize DirectX. This process consists of several sub-steps. All of these steps are in the moderate category. For detail description I will try to link in the Microsoft documentation. Let’s take a look anyway.
Also in order for this to work, you must have at least an intermediate knowledge of C++. DirectX is still an intense API, and while there are many game engines like Unity 3D, Unreal 3D, etc., at their core lies a similar API, such as DirectX, OpenGL or Vulkan for handling the graphics hardware. Conquer this, and someone needs you.
The DirectX API utilizes the use of COM classes to refer to complex references to hardware devices and internal mechanisms. These are declared through the use of pointers. In this example, you will start by making use (declare) of the following critical pointers:
IDXGISwapChain*
ID3D11Device*
ID3D11DeviceContext*
ID3D11RenderTargetView*
Microsoft code pattern usually employs the use of a description that get passed into a function. This is common. At first, this can be difficult to grasp and intimidating, but we will try to keep it simple. The structure is a Something_DESC that inputs into your function to give it robustness and to tell it how to work. You’ll get it. The DESC stands for Description.
Let’s talk about some basic concepts. If you have this already, scroll down to the final initialization part. BUT! If you don’t know, you will not want to skip this part for your life!
*Remember DirectX is like water. If you memorize it like blocks of ice, you will be missing the encased goodies, like a dollar bill right in the middle of a block. If you realize it is water, you could simply reach in and graze it… nanoseconds before Bruce Lee snatches it from you. Ok, ok, you get it.
The Breakdown
Device ( ID3D11Device*
)– This is a pointer to the graphics device. If not specifically defined, will point to the default graphics device. Description.
Swap Chain ( IDXGISwapChain*
) – The swap chain is a basic machine for the displaying of a forward image while rendering an image onto the back buffer. Description.
Render Target View ( ID3D11RenderTargetView*
)– The render target view is a pointer to the view. Description.
Device Context ( ID3D11DeviceContext*
)– The device context houses the key functions that will allow you to interact with the graphics device when rendering to the screen. Description.
Viewport ( D3D11_VIEWPORT
) – the viewport is a definition housing the window size and other properties. Description.
The first thing that needs to be done is to create the device and swap chain. This is done with the D3D11CreateDeviceAndSwapChain()
function. (Description) But we can’t just call it. This function has the following signature (Sinister laughter):
HRESULT WINAPI D3D11CreateDeviceAndSwapChain( __in_opt IDXGIAdapter* pAdapter, D3D_DRIVER_TYPE DriverType, HMODULE Software, UINT Flags, __in_ecount_opt( FeatureLevels ) CONST D3D_FEATURE_LEVEL* pFeatureLevels, UINT FeatureLevels, UINT SDKVersion, __in_opt CONST DXGI_SWAP_CHAIN_DESC* pSwapChainDesc, __out_opt IDXGISwapChain** ppSwapChain, __out_opt ID3D11Device** ppDevice, __out_opt D3D_FEATURE_LEVEL* pFeatureLevel, __out_opt ID3D11DeviceContext** ppImmediateContext );Relax. Until you get to that level, we are only going to be using the simplest way to get your imagination running wild as you are that it is possible for you to do this. Ok. First, we have to describe the swap chain before we create it. This is done with the
DXGI_SWAP_CHAIN_DESC
type,(Description) which is a struct with the following signature:
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 is a description
that will be inserted into the D3D11CreateDeviceAndSwapChain()
function.(Description)
Without going into the details, Let’s fill out this structure.
DXGI_SWAP_CHAIN_DESC SwapChainDesc; ZeroMemory(&SwapChainDesc, sizeof(DXGI_SWAP_CHAIN_DESC)); SwapChainDesc.BufferCount = 1; SwapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; SwapChainDesc.BufferDesc.Width = ns_Width; SwapChainDesc.BufferDesc.Height = ns_Height; SwapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; SwapChainDesc.OutputWindow = hWnd; SwapChainDesc.BufferDesc.RefreshRate.Numerator = 60; SwapChainDesc.BufferDesc.RefreshRate.Denominator = 1; SwapChainDesc.SampleDesc.Count = 1; SwapChainDesc.SampleDesc.Quality = 0; SwapChainDesc.Windowed = TRUE; SwapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
Ok, now we’ll explain this (I swear this is only hard looking because you don’t understand it right now. Once you get it, you’ll hate me and love for explaining this this way.) Here’s the breakdown:
The Breakdown
DXGI_SWAP_CHAIN_DESC SwapChainDesc;
ZeroMemory(&SwapChainDesc, sizeof(DXGI_SWAP_CHAIN_DESC));
Above, we defined and declared this DXGI_SWAP_CHAIN_DESC
type (Description) and called it SwapChainDesc
. Captain Obvious Strikes AGAIN! Then we zero out the structure of any miscellaneous values by calling ZeroMemory()
with a pointer to the instantiated SwapChainDesc
and the size of its type, DXGI_SWAP_CHAIN_DESC
.
SwapChainDesc.BufferCount
= 1;
This is how many buffers are in the swap chain. For now, this will be one.
SwapChainDesc.BufferDesc.Format
= DXGI_FORMAT_R8G8B8A8_UNORM;
This line determines the buffer storage type. The value above means that you have one 8-bit Red channel, one 8-bit blue channel, one 8-bit green channel, and a 8-bit alpha channel in Unsigned normal format. Again, for now trust that.
SwapChainDesc.BufferDesc.Width = ns_Width; SwapChainDesc.BufferDesc.Height = ns_Height;
This one’s easy! The height and width of the buffer.
SwapChainDesc.BufferUsage
= DXGI_USAGE_RENDER_TARGET_OUTPUT;
This line determines the output of the swap chain. Here, the Render target is selected.
SwapChainDesc.OutputWindow
= hWnd;
OutputWindow
determines where to draw the output to, such as a panel in a window, or just the entire window. As you learn more about Windows programming, you will find this useful. The input is a HWND
type which is short for Window Handle, which is essentially a pointer to a Window. If you are looking at this tutorial, hopeful you have created a window already. If you have, you will want to pass your HWND
here, otherwise your window will show up. But DirectX will be drawing to the abyss. If you haven’t, that tutorial’s a-coming.
SwapChainDesc.BufferDesc.RefreshRate.Numerator = 60; SwapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
RefreshRate.Numerator
is your monitors refresh rate.
SwapChainDesc.SampleDesc.Count = 1; SwapChainDesc.SampleDesc.Quality = 0;
The SampleDesc
stores the quality settings, remember, the high the quality the slower the frame render.
SwapChainDesc.Windowed =
TRUE;
Well, Windowed mode, or fullscreen mode.
SwapChainDesc.Flags =
DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
Flags are special properties that we could assign to the swap chain. Here, we are specifying that we could resize the render target at any time.
Whew! If you are feeling
lucky, you can reference these as the default settings for the DXGI_SWAP_CHAIN_DESC
Alright let’s get this swap chain going!
D3D11CreateDeviceAndSwapChain( NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, D3D11_CREATE_DEVICE_DEBUG, NULL, NULL, D3D11_SDK_VERSION, &SwapChainDesc, &Swapchain, &Device, NULL, &DeviceContext );
*Trollface* Having fun? Ok, this function actually makes things easier (I did say that you’ll hate me. If you really, really hate me, curse me out in the comments below. Keep it like, PG-13 curses though) Let’s check out the breakdown.
The Breakdown
- The first parameter is a pointer to the
IDXGIAdapter
, luckily, leaving thisNULL
points to the default adapter. - The second parameter is the driver type. For now,
let’s keep it to
D3D_DRIVER_TYPE_HARDWARE
for the purposes of this tutorial. - The third parameter can be left
NULL
. (You would only be interested in this parameter if you set the second value toD3D_DRIVER_TYPE_SOFTWARE
) - The ahem, fourth parameter for
FLAGS
. We’ll leave it atD3D11_CREATE_DEVICE_DEBUG
. - The fifth parameter is a pointer to our feature
level. We’ll set this to
NULL
. - The sixth parameter is also
NULL
, referencing feature level. - The seventh parameter is our SDK version, we’ll call
it
D3D11_SDK_VERSION
. - A HA! Here we can add a pointer to our
DXGI_SWAP_CHAIN_DESC
. - A pointer to our
IDXGISwapChain* Swapchain
. - Pointer to our
ID3D11Device* Device
. - The fifth parameter is a pointer to our feature
level. We’ll set this to
NULL
. - Pointer to our
ID3D11DeviceContext* DeviceContext
Whew! We got that out of the way. This function does exactly what it proposes. After running this function, the IDXGISwapChain* Swapchain
and ID3D11Device* Device
pointer are now pointing to somewhere in memory that actually contain relevant data! (The function initializes the values)
Let’s build our drawing canvas, or render target view, starting by filling out a description.
D3D11_RENDER_TARGET_VIEW_DESC ViewDESC;
ZeroMemory(&ViewDESC, sizeof(D3D11_RENDER_TARGET_VIEW_DESC));
ViewDESC.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
ViewDESC.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
ViewDESC.Texture2D.MipSlice = 0;
Swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&Texture2d);
Device->CreateRenderTargetView(Texture2d, NULL, &RenderTargetView);
Texture2d->Release();
D3D11_RENDER_TARGET_VIEW_DESC ViewDESC;
ZeroMemory(&ViewDESC, sizeof(D3D11_RENDER_TARGET_VIEW_DESC));
The Breakdown
We start to create and zero out a D3D11_RENDER_TARGET_VIEW_DESC
structure.
ViewDESC.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
The render target here will be a 2D texture. (It’s a 2D screen. Call me up if you get creative.) We are going to draw onto this texture soon.
ViewDESC.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
The value above means that you are ultimately going to pass in a data array with little segments of one 8-bit Red channel, one 8-bit blue channel, one 8-bit green channel, and an 8-bit alpha channel in Unsigned Normal format.
ViewDESC.Texture2D.MipSlice = 0;
Swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&Texture2d);
hr = Device->CreateRenderTargetView(Texture2d, NULL, &RenderTargetView);
Texture2d->Release();
Here, we bind the texture to the swap chain and call the device to create the render target view.
Of course calling Release()
afterward. This is important, because if Release()
is not called, the allocated memory will stay in memory. Image you had a jar of money and every time you filled it up, the jar got smaller. (I don’t know, the money left a residue, eww.)
Then, the render target is created by passing in the texture and a pointer to the render target.
Don’t end up with a small jar. Clean up after yourself. Last but not least, we have the view port:
D3D11_VIEWPORT Viewport;
ZeroMemory(&Viewport, sizeof(D3D11_VIEWPORT));
Viewport.Height = 800;
Viewport.Width = 600;
Viewport.MaxDepth = 1.0f;
Viewport.MinDepth = 0.0f;
Viewport.TopLeftX = 0;
Viewport.TopLeftY = 0;
DeviceContext->RSSetViewports(1, &Viewport);
The Breakdown
D3D11_VIEWPORT Viewport;
ZeroMemory(&Viewport, sizeof(D3D11_VIEWPORT));
Above, our traditional zeroing of the structure before use. We won’t forget the viewport description. Let’s leave these settings [below] like this.
Viewport.Height = 800;
Viewport.Width = 600;
Viewport.MaxDepth = 1.0f;
Viewport.MinDepth = 0.0f;
Viewport.TopLeftX = 0;
Viewport.TopLeftY = 0;
…And set our viewport below:
DeviceContext->RSSetViewports(1, &Viewport);
That’s it! The completed initialization is below.
IDXGISwapChain* Swapchain;
ID3D11Device* Device;
ID3D11DeviceContext* DeviceContext;
ID3D11Texture2D *Texture2d;
DXGI_SWAP_CHAIN_DESC SwapChainDesc;
ZeroMemory(&SwapChainDesc, sizeof(DXGI_SWAP_CHAIN_DESC));
SwapChainDesc.BufferCount = 1;
SwapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
SwapChainDesc.BufferDesc.Width = 800;
SwapChainDesc.BufferDesc.Height = 600;
SwapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
SwapChainDesc.OutputWindow = hWnd;
SwapChainDesc.BufferDesc.RefreshRate.Numerator = 60; SwapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
SwapChainDesc.SampleDesc.Count = 1;
SwapChainDesc.SampleDesc.Quality = 0;
SwapChainDesc.Windowed = TRUE;
SwapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
D3D11CreateDeviceAndSwapChain(
NULL,
D3D_DRIVER_TYPE_HARDWARE,
NULL,
D3D11_CREATE_DEVICE_DEBUG,
NULL,
NULL,
D3D11_SDK_VERSION,
&SwapChainDesc,
&Swapchain,
&Device,
NULL,
&DeviceContext);
D3D11_RENDER_TARGET_VIEW_DESC ViewDESC;
ZeroMemory(&ViewDESC, sizeof(D3D11_RENDER_TARGET_VIEW_DESC));
ViewDESC.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
ViewDESC.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
ViewDESC.Texture2D.MipSlice = 0;
Swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&Texture2d);
Device->CreateRenderTargetView(Texture2d, NULL, &RenderTargetView);
Texture2d->Release();
D3D11_VIEWPORT Viewport;
ZeroMemory(&Viewport, sizeof(D3D11_VIEWPORT));
Viewport.Height = ns_Height;
Viewport.Width = ns_Width;
Viewport.MaxDepth = 1.0f;
Viewport.MinDepth = 0.0f;
Viewport.TopLeftX = 0;
Viewport.TopLeftY = 0;
DeviceContext->RSSetViewports(1, &Viewport);
In the next tutorial, we’ll discover how to draw the screen.
Have a question about the tutorials? Post it below! The last thing I want if for you to be staring for hours before realizing that I’m not making any sense!
Leave A Comment