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

Comments are closed.