DirectX11 Tutorial 1: Getting started

Setting up Visual Studio and the DX11 SDK
In this tutorials I will assume that you use Microsoft Visual Studio C++ Express, since it's free anyway. You're going to need to activate it, but you'll get your free code if you register using a MS live account.
Microsoft Visual Studio is a Integrated Development Environment(IDE), which means that it's a text editor for your code that has integrated tools to compile, link and debug the applications you make in it. It also takes care of resources such as menus and icons. All you do is write code, and press play to compile and execute your code, fairly simple.
Download it here and install it.

Also, to write anything with DirectX11, you'll need it's SDK (Software Development Kit)
When I tried to install the SDK, the installer failed with the error code S1023. If you get the same problem, here is how to fix it.

Making a win32 application
Now that you have everything installed lets make a new project, by simply clicking File->new->Project.
Under Visual C++, chose Win32, and Win32 Project.
You'll get to chose a name, if you want you can call it DX11Tutorial1, but we will continue to work with the same project, so give it a name you think fit. A wizard should pop up, and if you press next, make sure that empty project is NOT checked. Press finish and lots of different kinds of files should be generated.
I'll assume that you know what header and source files are, but nevertheless, DX11Tutorial1.cpp is our main source file. We'll do most of what we'll do in this file. If you are new to C++, this page has lots of tutorials.

You can go ahead and compile and execute, by pressing the green play button. You should get something like this(if you're having problems you can find my source at the end of the tutorial):
So, now we have a running application, but where do we poke to change it? The source files contain different defined variables, values and functions. and when the program executes, it will start at the main function defined on line 20 in DXTutorial1:
int APIENTRY _tWinMain(some parameters)
So if we want to do something, other than just defining stuff, we need to add code in this function.

At the moment this function calls a few other functions to initialize a window, and then it enters a loop that checks what's up, and if it has to do something. The program will continue to run inside this loop until something within the loop tells it to quit(For example receiving an IDM_EXIT message, generated if you close it by pressing the upper right cross, or exit in the menu).

Other than the main function there are the following functions:

ATOM MyRegisterClass(HINSTANCE hInstance)
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)

Called during initialization, and then two CALLBACK functions:


LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);


These functions define what we want to do when we get messages in the main loop, and if we haven't defined what we want to do with the message we send it over to DefWindowProc(), which does what any default windows application would do. if we don't do this, the application will lose all basic capabilities of a window, and won't even appear.
There is one CALLBACK function for the main window, and one for the about dialog.

Also, you can edit the menu and the dialog text in DX11Tutorial1.rc (or <project name>.rc), in VS Express we can't edit resources in the IDE, so just open it in you favorite editor(such as notepad).

Notes on compiling
Next to the "play button" there is a text field that says Debug, this compiles the application in a fail safe way. If we want, we can change this configuration to e.g. Release. The Release configuration optimized the code at compilation, and we'll get a slightly faster executable. We want to use this configuration when we distribute our games to friends etc.

Adding some DX11
DX11 is contained in the libraries d3d11.lib and d3dx11.lib, and the information we need to add to use these libraries is contained in the header files d3d11.h and d3dx11.h, respectively.
For this tutorial we'll settle with just including these libraries and headers, trying to reach some function in it, and hope that it compiles and links.

First, we add these lines in the beginning of our file, I added them from line 6.


#include <d3d11.h>
#include <d3dx11.h>


The second line should probably get lined red, this is because it's not a standard header included in the VStudio include directory. So we have to make VStudio looks in the DX11 SDK directory as well.
To do this, we right-click on our project (upper left, in the solution explorer, probably called DXTutorial1), and chose properties. In VC++ Directories, we edit Include Directories and Library Directories and add these lines: (in the properties editor, note the configuration setting to the top left, change this to all, to avoid getting very unexpected errors when we finally want to compile a release .exe)
C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Include to Include
C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Lib\x86 to Library.

Now the IDE can find d3dx11.h as well, and our application compiles again.

Lets call a DX11 function, but to do this we need to prepare some things for it, so we'll add the following block of code before our main loop, around line 50. We'll talk about, move, change, and add to this code in the next tutorial.

D3D_FEATURE_LEVEL g_featureLevel = D3D_FEATURE_LEVEL_11_0;

ID3D11Device* g_pd3dDevice = NULL;
ID3D11DeviceContext* g_pImmediateContext = NULL;
IDXGISwapChain* g_pSwapChain = NULL;


D3D_FEATURE_LEVEL featureLevels[] =
{
    D3D_FEATURE_LEVEL_11_0,
    D3D_FEATURE_LEVEL_10_1,
    D3D_FEATURE_LEVEL_10_0,
};


DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory( &sd, sizeof( sd ) );
sd.BufferCount = 1;
sd.BufferDesc.Width = 1024;
sd.BufferDesc.Height = 768;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.OutputWindow = hWnd;
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.Windowed = TRUE;


UINT numFeatureLevels = ARRAYSIZE( featureLevels );
HRESULT hr = S_OK;
hr = D3D11CreateDeviceAndSwapChain( NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, featureLevels, numFeatureLevels, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &g_featureLevel, &g_pImmediateContext );



if( g_pImmediateContext ) g_pImmediateContext->ClearState();
if( g_pSwapChain ) g_pSwapChain->Release();
if( g_pImmediateContext ) g_pImmediateContext->Release();
if( g_pd3dDevice ) g_pd3dDevice->Release();




Also, we need to move the first line from the InitInstance() function,
HWND hWnd;
to the beginning of the file, say line 15, to make it global.

Now, our application should compile, BUT, we get an error, looking something like this:
error LNK2001: unresolved external symbol _D3D11CreateDeviceAndSwapChain@48

This is because even if the IDE knows where it should look for library files, we haven't actually told it to look for any. So we go back to project properties, don't forget to change the configuration to all, and in Linker- >Input, we edit Additional Dependencies to add these lines:


d3d11.lib
d3dx11.lib

Now we can compile, and we get exactly the same window as before! Except that it has successfully asked for and gotten a DX11 device and some other things we need to use it, only to throw it away!
In the next tutorial, we will make more use of the device we create. :)

The source you should have ended up with can be downloaded from here. Don't forget to correct the include/lib paths in the project properties if you have a different location for your SDK. (mine is in C:\Program files x86\)

Debugging using the IDE
When using an IDE we have some very useful tools for debugging. As an example, click in the grey area to the left of the code to add a "breakpoint", while debugging (running the program from the IDE), the program will stop as soon as it is about to execute this line of code. We can add a breakpoint at the line we call the D3D11CreateDeviceAndSwapChain()function, and we can check the values of our local variables in locals below the text editor. If we add another break point at the next line and press continue (F5), we can see how our variables where given addresses to the DX11 device and so on. If we press "Step into"(F11), we can execute one line at a time. This function is mainly used to follow how variables are added, and how we pass through the code, if something we didn't expect(a bug) happens at some time. By doing this we can potentially see what the problem is and fix it, hence debugging.




2 comments:

  1. Very nice tutorial, I enjoyed the read very much!
    Waiting for the next one!

    ReplyDelete
  2. Thank you, I'm sorry to say that the project is dead at the moment. I might take it up again some day but there is a large chance it will be using only OpenGL when/if that happens. This is because of the increasing integration of directX into just becoming a part of other windows libraries, and newer DirectX versions requiring a new version of windows.

    ReplyDelete