DirectX 9 Tutorials – #01 – Initialization

Before we can initialize DirectX, we need to create a window first. There’re several libraries and frameworks out there, like MFC, wxWidgets, GTK and so on. In order to keep things simple, i’ll use the Windows APIs, so that everyone will be able to compile the sources without the need to download external libraries. Also, since we don’t need to create a complex graphical user interface, the resulting WinAPI code is really short; a more powerful library is not needed at all.

As you can see from the source code, the first thing i do is to initialize the logger object; a graphical application can grow a lot thus becoming really complex and hard to debug. My suggestion is to always log every error, so that it will be easier to track down bugs when we need to.

Let’s talk about the main window; as you can see, we need to declare and fill a WNDCLASS/WNDCLASSEX structure. It defines the basic informations of the window we are going to create. The class name is just a string that will be used to identify our window class, and it is needed by CreateWindow/CreateWindowEx. Once filled, the window class structure needs to be registered using RegisterClass/RegisterClassEx (note that you need to match the extended version of the structure with the extended version of the RegisterClass function).

Once registered, the window class can be used through a call to either CreateWindow or CreateWindowEx. The parameters the functions needs are very simple; from left to right, it will asks for: extended window style, window class name (the one we specified inside the window class structure), window name, window style, position, size, parent window (since this is a top-level window, we don’t have any parent), menu handle (that can be created or loaded from the resources using LoadMenu), the module instance handle, and a user-defined parameter that will be sent to the window procedure (the callback function that handles the events) through the WM_CREATE event.

Speaking of that callback, its prototype is:

LRESULT CALLBACK lrWindowProcedure(
HWND hwndWindow, UINT uiMessage,
WPARAM wpParam, LPARAM lpParam
);

This function (specified inside the window class structure) is automatically called by Windows whenever a user (or another program) interacts with our interface. Each event has a well-known code that is defined as constant inside the platform SDK with the “WM_” prefix, and it’s passed using the second parameter of the function (UINT uiMessage). The hwndWindow parameter is the handle of the window that received the event (we can handle different windows with the same window procedure). The WPARAM and LPARAM values are event-specific, and their contents changes according to what the documentation says (http://www.msdn.com/). For example, the forementioned WM_CREATE message will receive the user-define parameter specified in the last argument of CreateWindow/CreateWindowEx through the LPARAM value of the event.

The AdjustWindowRect function is used to get the window size needed to obtain a particular client area. The client area is the part of the window that is inside the borders. Since DirectX will use only that area, we need to adjust the size of the window according to the DEFAULT_WINDOW_WIDTH/DEFAULT_WINDOW_HEIGHT parameters.

Now that the window is up and running, we just need to add the so-called message loop, that will receive and dispatch the messages to our window procedure; it can be done with the following functions: GetMessage, PeekMessage. The differences between the two functions are that the first is blocking, while the second is not. Since we also need to render, we either have to use PeekMessage in the same thread of the render code, or use GetMessage from another thread. In the samples, i decided to create the window from another thread, so that the entry point will be more simple to read.

In order to initialize DirectX, we first need to obtain its interface object. We do this calling Direct3DCreate9; it requests for the SDK version (just use the D3D_SDK_VERSION value). This is the way a COM interface works; it is very handy, because it lets the developers update the libraries without the risks of breaking old applications by changing/adding functions and features to newer versions.

The first examples will not go full screen, so we need to get the default adapter’s active display mode. Basically, a display mode is just the current configuration (that we assume to be fully-compatible with your hardware) for the specified adapter: resolution, refresh rate, pixel format (that defines how a pixel is built). If we were to go full screen, we would have to enumerate the supported display modes, and the sample source codes would became more complicated; to keep things simple, this will be explained in another tutorial.

The next step is to fill the D3D_PRESENTATION_PARAMETERS structure; it asks for a backbuffer format (we will use the one that the current display mode structure holds), a backbuffer size (the size of the client area of the window), the number of back buffers to use (we will use 3 buffers), and the window handle. For the first examples, i’ll not use neither multisampling nor a stencil buffer; concerning the depth buffer (also known as Z-Buffer), we will ask DirectX to manage it automatically, with a simple 16 bit format. More about the Z-Buffer in the next tutorials, when i’ll introduce you the third dimension.

The Direct3D device can now be created calling CreateDevice:

CreateDevice(
D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
NULL, D3DCREATE_HARDWARE_VERTEXPROCESSING,
&g_dppPresentParameters, &g_p_dv9DirectX9Device
);

Here, the default adapter is used to create an HAL device. Basically, we’re asking DirectX to rely on the hardware for rendering and vertex processing, with the configuration we specified inside the D3DPRESENT_PARAMETERS structure.

The program is ready to render! Well, in fact, the only thing it can render is the clear color, because we don’t have any geometry to draw yet. Here’s the typical game loop:

while(running)
{
	handle window events
	handle the user input
	update the objects (sound, movement, networking and so on)
	clear the scene
	begin the scene
	draw the scene
	end the scene
	present the scene
}

Our one is a lot simpler, since we don’t have any sound, input, geometry to draw, or connections to take care of. Everything it does is clear the scene with the predefined color, and begin/end the scene. For simplicity, i’ve defined some functions that gets called from the window procedure: vOnSize, vOnClose, vOnActivate. Those functions will take care of cleanup, resize and activation/deactivation of the window. In the first case, there’s not much to do; we just need to release the DirectX interface object and the Direct3D device. The vOnActive function sets a global flag that tells us if our window is active (we don’t need to render the scene if the user is working on another window). The last function, vOnSize, updates the D3DPRESENT_PARAMETERS structure with the new client area size, and then resets the device. It also sets the global g_blMainWindowIsMinimized flag that tells us if the window is minimized or not.

The last thing to take care of is the lost device status. This is what the DirectX documentation says about lost devices:


The lost state is characterized by the silent failure of all rendering operations, which means that the rendering methods can return success codes even though the rendering operations fail. In this situation, the error code D3DERR_DEVICELOST is returned by IDirect3DDevice9::Present.

By design, the full set of scenarios that can cause a device to become lost is not specified. Some typical examples include loss of focus, such as when the user presses ALT+TAB or when a system dialog is initialized. Devices can also be lost due to a power management event, or when another application assumes full-screen operation. In addition, any failure from IDirect3DDevice9::Reset puts the device into a lost state
….
A lost device must re-create resources (including video memory resources) after it has been reset. If a device is lost, the application queries the device to see if it can be restored to the operational state. If not, the application waits until the device can be restored.

Handling this event is rather simple; according to the documentation, when the Present() method returns a lost device status, we need to periodically call TestCooperativeLevel to see if the device can be restored (D3DERR_DEVICENOTRESET status). If it can be reset, we just need to release the device objects and call the Reset() method and check for the returned value. Once reset, if everything went good, we need to reload the resources the program was using (in fact, this behavior depends on the memoy pool used to create the resource; more on that in the next tutorial); then, we can restore the normal program flow, as nothing happened.
Before you take a look at the source code for this tutorial, i want to show you how it is organized:

commonheaders.hpp

  • Common include files

libraries.hpp

  • Libraries to be linked to the project

icons.hpp, icons.rc

  • Defines the default icon as well as its resource id.

Logger.cpp, Logger.hpp

  • The logger class that is used to log errors, warnings and information strings to file.

main.cpp

  • Entry point

Speaking of main.cpp, these are the main blocks:

_tWinMain

  • DirectX 9 initialization
  • Game loop
  • GUI thread creation.

dwCreateWindow

  • This is the thread that creates the main window and implements the message loop.

lrWindowProcedure

  • The callback that gets called by Windows when someone/something interacts with our window.

vOnClose, vOnActivate

  • Cleanup
  • Window states update

vOnSize

  • Resets the device with the new client area size whenever the dimensions of the window changes

Source code: http://www.insanedevelopers.net/downloads/Sources/DirectX9Tutorials/01 – Initialization.zip

DirectX 9 Tutorials – #00 – Introduction

For this tutorials, we’ll need to download the DirectX SDK and a compiler. Since most of the header files are full of Microsoft-specific stuff, the only compiler that will work out-of-the-box is Visual C++. Starting from the 2005 version, Microsoft began to release freeware editions of their development products. In this tutorial, we’ll download and install Visual C++ 2008 Express Edition, but you can use any version you like.

The Express Editions can be obtained from http://www.microsoft.com/express/. Downloading the installer should take little time, but you will have to stay online while it is running, because it needs to download the components from the website. During the wizard, you can select what you want to install; the optional products (Microsoft SQL Server Express Edition and Silverlight) are not required to follow the samples, so you can skip them to save disk space and bandwidth.

Go take a coffee, it will take some time to download and install everything; eventually, it will ask you to restart the computer.

Visual C++ 2008 Express Edition - Setup

Visual C++ 2008 Express Edition - Setup

The setup will also install a basic platform SDK (now called Windows SDK); it should be enough for us, but if you want you can download the latest version from the Microsoft website. Here is a sample “hello world” application you may copy and paste inside Visual C++ to see if everything is working as expected.

#include <windows.h>
#include <tchar.h>

INT WINAPI _tWinMain(HINSTANCE hiProgram, HINSTANCE, LPTSTR tstrCmdLine, INT iCmdShow)
{
	MessageBox(NULL, _T("Hello world!"), _T("Greetings!"), MB_OK);
	return 0;
}

Now we just need the DirectX SDK; it can be downloaded from http://msdn.microsoft.com/en-us/directx/default.aspx. This time the setup application is pretty heavy (should be around 500 MB), but it can be installed without an internet connection. I suggest you to at least install the runtime libraries, the debug symbols, the headers/libraries (of course) and the samples (they may come in handy). The documentation can also be accessed online from the Microsoft website.

The setup program should automatically set the additional library and header directories of the compiler. To be sure the configuration is right, we must open the Tools/Options menu from Visual C++ and check for the VC++ Directories page under the Project and Solutions voice. The following is the configuration the installer set for me on my copy of Visual Studio 2008 Standard Edition.

Visual Studio 2008 Standard Edition - Include directories

Visual Studio 2008 Standard Edition - Include directories

Visual Studio 2008 Standard Edition - Library files (x86)

Visual Studio 2008 Standard Edition - Library files (x86)

Visual Studio 2008 Standard Edition - Library files (x64)

Visual Studio 2008 Standard Edition - Library files (x64)

I noticed that if you’re using Visual C++ 2008 Express Edition the setup will also add an executable directory to the configuration; this didn’t happen for the normal edition of Visual Studio.

Visual C++ 2008 Express Edition - Additional executable directories

Visual C++ 2008 Express Edition - Additional executable directories

Your computer should now be ready to compile the samples, you can then skip to the next tutorials.

Creating a console from a windows subsystem program

Sometimes, printing messages and dumping variables can be very handy for debugging purposes, but most of today’s programs do not need and in fact do not open a console window.

The following is a way a console can be opened from a windows subsystem application.

// Open the console window
AllocConsole();

// Assign the stdin/stdout/stderr streams to the newly created console
_tfreopen_s(&fpStdIn, _T("CONIN$"), _T("r"), stdin);
_tfreopen_s(&fpStdOut, _T("CONOUT$"), _T("w"), stdout);
_tfreopen_s(&fpStdErr, _T("CONOUT$"), _T("w"), stderr);

Once opened and initialized, the console can be used just like you do in a normal text-based application. Note that you need to explicitly release both the console and the streams you opened.

// Release the console
FreeConsole();

// Release the streams
fclose(stdin);
fclose(stdout);
fclose(stderr);

It’s a good idea to add this code inside an #ifdef statement, in order to remove this code (as well as the printf functions) from the release version.

Source code: http://www.InsaneDevelopers.net/downloads/Sources/CreatingAConsole.zip

Handling CTRL+C in your console application

First of all, to make sure we can receive this event, the ENABLE_PROCESSED_INPUT console mode must be enabled using SetConsoleMode; without this, the CTRL+C signal would be sent as a keystroke inside the input buffer.

SetConsoleMode(hdlStdInput, ENABLE_ECHO_INPUT | ENABLE_INSERT_MODE | ENABLE_LINE_INPUT | ENABLE_MOUSE_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_QUICK_EDIT_MODE);

We can now register our HandlerRoutine function using SetConsoleCtrlHandler

BOOL WINAPI blCtrlCHandler(DWORD dwCtrlType) { ... }
...
SetConsoleCtrlHandler(blCtrlCHandler, TRUE);

The handler function can be also handy for cleanup code, since it does not receives only the CTRL+C signal, but other events as well. Checking the dwCtrlType variable allows you to detect CTRL+BREAK and system events like logoff, shutdown, and the program’s termination through the close button.

If you don’t need the CTRL+C signal, and you want to mask it, it is possible to register a null function pointer that will prevent the default handler from handling the signal.

SetConsoleCtrlHandler(NULL, TRUE);

Source code: http://www.insanedevelopers.net/downloads/Sources/CTRL-CHandling.zip

Application monitoring

I’ve been asked if there’s a way to programmatically determine if an application crashed in order to restart it; the answer is, of course, yes. To accomplish this task, you have to create the process with a launcher that acts like a debugger; your application only needs to check the exit code of the process whenever it receives an EXIT_PROCESS_DEBUG_EVENT event (you can also check for unrecoverable exceptions).

Here’s the pseudo-code:

DEBUG_EVENT deDebugEvent;
...
WaitForDebugEvent(..);
...
// Check the event type
switch(deDebugEvent.dwDebugEventCode)
{
	...
	case EXIT_PROCESS_DEBUG_EVENT:
	{
		// We assume that if the program exited with a nonzero code, it crashed
		if (deDebugEvent.u.ExitProcess.dwExitCode)
		{
			// The program crashed
			DebugActiveProcessStop(...);
			CreateProcess(...);
			...
		}

		// Clean exit
		ExitProcess(0);
	}
	...
}

Source code: http://www.insanedevelopers.net/downloads/Sources/ApplicationMonitor.zip