Part Overview
Chapter 4: Direct3D Initialization Overview
4.3 Initializing DirectSD
The following subsections show how to initialize Direct3D. Our process of initializing Direct3D can be broken down into the following steps:
1. Acquire a pointer to an IDirect3D9 interface. Recall that this interface is used for finding information about the physical hardware devices on a system and for creating the IDirect3DDevice9 interface, which is our C++ object that represents the physical hardware device we use for displaying 3D graphics.
2. Verify hardware support for using the current display mode format as a display format and back buffer format in windowed mode, and for using the format D3DFMT_X8R8G8B8 as a display format and back buffer format in full-screen mode.
3. Check the device capabilities (D3DCAPS9) to see if the primary display adapter (primary graphics card) supports hardware vertex processing and a pure device. We will need to know if it can when we create the IDirect3DDevice9 interface.
4. Initialize an instance of the D3DPRESENT_PARAMETERS structure. This structure consists of a number of data members that allow us to specify the characteristics of the IDirect3DDevice9 interface we are going to create.
5. Create the IDirect3DDevice9 object based on an initialized D3DPRESENT_PARAMETERS structure. As said, the IDirect3DDevice9 object is our C++ object that represents the physical hardware device we use for displaying 3D graphics.
Note Remember that for the demo programs in this book, we will make it easy by selecting the primary adapter, and using the current display mode for windowed mode and the commonly supported format D3DFMT_x8R8G8B8 for full-screen mode, thereby bypassing the chore of enumerating adapters and display modes.
4.3.1 Acquiring an IDirect3D9 Interface
Initialization of Direct3D begins by acquiring a pointer to an IDirect3D9 interface. This is easily done using a special Direct3D function, as the following lines of code show:
IDirect3D9* md3dObject;
md3dObject = Direct3DCreate9(D3D_SDK_VERSION);
The single parameter to Direct3DOeate9 should always be D3D_SDK_VERSION, which guarantees that the application is built against the correct header files. If this function fails, it will return a null pointer.
4.3.2 Verifying HAL Support
After we have created the IDirect3D9 object, we should verify that the pixel format combination that we are using for the display and back buffer are supported by the hardware. To do this, we use the IDirect3D9::CheckDeviceType method:
HRESULT IDirect3D9::CheckDeviceType(
The first parameter identifies the display adapter for which we are testing support; the second parameter identifies the device type (usually D3DDEVTYPE_HAL since we want to test for hardware acceleration support); the third and fourth parameters specify the display and back buffer formats, respectively, of which we are testing if the display adapter supports hardware acceleration; and finally, the fifth parameter specifies whether we will be using these formats in windowed mode — specify true if windowed or false if full screen. The reason for this last parameter is that a certain display and back buffer format configuration may work in windowed mode but not full-screen mode, or conversely. This function will return an error code if the formats are not supported by the specified adapter in the specified settings.
Note For windowed applications, the back buffer format need not match the display format. However, you should call
IDirect3D9::CheckDeviceFormatConversion to see if the hardware supports a conversion between the two formats you want to use.
In our sample framework, for windowed mode rendering, we are going to use the current display mode pixel format; this can be obtained by making a call to IDirect3D9::SetAdapterDisplayMode, which returns (via the second parameter) the current display mode of the specified adapter (first parameter); then the pixel format of the current display mode is just given by mode.Format . For full-screen mode, we are going to use the commonly supported format
D3DFMT_X8R8G8B8 as both the display format and back buffer format. The following code snippet illustrates our HAL verification code:
D3DDISPLAYMODE mode;
md3dObject - > GetAdapterDisplayMode (D3DADAPTER_DEFAULT, &mode);
HR(md3dObject - > CheckDeviceType (D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, mode.Format, mode.Format, true));
HR(md3dObject - > CheckDeviceType (D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, D3DFMT_X8R8G8B8, false));
Note HR is a macro that handles the return codes returned by the Direct3D functions; it is discussed in §4.7.
4.3.3 Checking for Hardware Vertex Processing
When we create an IDirect3DDevice9 object to represent the primary display adapter, we must specify the type of vertex processing to use with it. We want to use hardware vertex processing if we can (and also a pure device), but because not all cards support hardware vertex processing and pure devices, we must first check whether the card supports it. (We also only take a pure device if using hardware vertex processing.)
To do this, we must first initialize a D3DCAPS9 instance based on the capabilities of the primary display adapter. We use the following method:
HRESULT IDirect3D9::GetDeviceCaps(UINT Adapter, D3DDEVTYPE DeviceType, D3DCAPS9 *pCaps);
The first parameter specifies the physical display adapter for which we are going to get the capabilities; the second parameter specifies the device type to use (e.g.
hardware device (D3DDEVTYPE_HAl) or reference device (D3DDEVTYPE_REF) ); and the last parameter returns the initialized capabilities structure.
Then, we can check the capabilities as we did in §4.2.9. The following code snippet illustrates:
D3DCAPS9 caps;
HR(md3dObject - >GetDeviceCaps(D3DADAPTER_DEFAULT, mDevType, &caps));
DWORD devBehaviorFlags = 0;
if( caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT ) devBehaviorFlags |= mRequestedVP;
else
devBehaviorFlags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING;
// If pure device and HW T&L supported if( caps.DevCaps & D3DDEVCAPS_PUREDEVICE &&
devBehaviorFlags & D3DCREATE_HARDWARE_VERTEXPROCESSING) devBehaviorFlags |= D3DCREATE_PUREDEVICE;
Note that mRequestedVP is a variable that stores the requested vertex processing mode (it will be specified at application initialization time), which is either hardware, mixed, or software vertex processing. Thus, if hardware vertex processing is available, we can always satisfy the requested vertex processing mode (hardware, mixed, or software). If hardware vertex processing is not supported, then we cannot use a hardware or mixed vertex processing mode, and we default to software vertex processing.
The identifiers D3DCREATE_HARDWARE_VERTEXPROCESSING, D3DCREATE_SOFTWARE_VERTEXPROCESSING, and D3DCREATE_PUREDEVICE are predefined
"creation flag" values that denote hardware vertex processing, software vertex processing, and pure device, respectively. A combination (combined using bitwise OR) of these creation flags is specified when we create the IDirect3DDevice9 object. Therefore, we save the creation flags we are going to use in the variable devBehaviorFlags for later use. (The creation flag for mixed vertex processing is D3DCREATE_MIXED_VERTEXPROCESSING. )
4.3.4 D3DPRESENT_PARAMETERS
The next step in the initialization process is to fill out an instance of the D3DPRESENT_PARAMETERS structure. This structure is used to specify some of the characteristics of the IDirect3DDevice9 object we are going to create, and is defined as:
typedef struct _D3DPRESENT_PARAMETERS_ {
Note In the following data member descriptions for the D3DPRESENT_PARAMETERS structure we only cover the flags and options we feel are most important to a beginner at this point. For a description of further flags, options, and configurations, refer to the SDK documentation.
BackBufferWidth: Width of the back buffer surface in pixels. For windowed mode, we can specify 0 and it will use the client area width.
BackBufferHeight: Height of the back buffer surface in pixels. For windowed mode, we can specify 0 and it will use the client area height.
BackBufferFormat: Pixel format of the back buffer (e.g., 32-bit pixel format: D3DFMT X8R8G8B8) . For windowed mode, you can also specify the format D3DFMT_UNKNOWN for this member, which means that Direct3D should use the current display mode's pixel format.
BackBufferCount: The number of back buffers to use. Usually we specify 1 to indicate we want only one back buffer.
MultiSampleType: The type of multisampling to use with the back buffer and also with the depth -stencil buffer (provided
EnableAutoDepthStenci1 is set to true); see §4.2.6 for further details on multisampling. Note that to use multisampling, both the render target (back buffer) and depth -stencil buffer must use the same multisampling type.
MultiSampleQuality: The quality level of multisampling; see §4.2.6.
SwapEffect: A member of the D3DSWAPEFFECT enumerated type that specifies how the buffers in the flipping chain will be swapped. Specifying D3DSWAPEFFECT_DISCARD is the most efficient; see the SDK documentation for other swap methods. Also note that multisampling can only be used with D3DSWAPEFFECT_DISCARD .
hDeviceWindow: The window handle associated with the device. Specify the application window into which you want to draw.
Windowed: Specify true to run in windowed mode or false for full-screen mode.
EnableAutoDepthstenci1: Set to true to have Direct3D create and maintain the depth/stencil buffer automatically; set to false otherwise. Note that if you do not specify true, your application will either not have a depth buffer corresponding to the back buffer, or you will have to attach a depth buffer manually; see IDirect3DDevice9::CreateDepthStenci1Surface and IDirect3DDevice9::SetDepthStenci1Surface in the SDK documentation.
AutoDepthStenci1Format: The format of the depth/stencil buffer (e.g., 24-bit depth with 8 bits reserved for the stencil buffer: D3DFMT_D24S8) . Flags: Some additional characteristics. Specify zero (no flags) or a member of the D3DPRESENTFLAS set. See the documentation for a complete list of valid flags. Two common flags are:
D3DPRESENTFLAG_LOCKABLE_BACKBUFFER: Specifies that the back buffer can be locked. Note that using a lockable back buffer can degrade performance.
D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL: Specifies that the depth/stencil buffer contents will be discarded (invalid) after the next back buffer is presented. This can improve performance.
FullScreen_RefreshRatelnHz: Refresh rate. Use the default refresh rate by specifying D3DPRESENT_RATE_DEFAULT .
PresentationInterval: A member of the D3DPRESENT set. See the documentation for a complete list of valid intervals. Two common intervals are:
D3DPRESENT_INTERVAL_IMMEDIATE: Presents immediately.
D3DPRESENT_INTERVAL_DEFAULT: Direct3D will choose the present rate. Usually this is equal to the refresh rate.
Note The back buffer dimensions should always match the client area dimensions for a crisp image. If they do not match, Direct3D will do a rectangle stretch to make it fit, but this stretch degrades the visual quality.
An example of filling this structure out is:
D3DPRESENT_PARAMETERS d3dpp;
d3dpp.BackBufferWidth = 800;
d3dpp.BackBufferHeight = 600;
d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
d3dpp.EnableAutoDepthStencil = true;
d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
d3dpp.Flags = 0;
d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
d3dpp.Presentationlnterval = D3DPRESENT_INTERVAL_IMMEDIATE;
4.3.5 Creating the IDirect3DDevice9 Interface
With the D3DPRESENT_PARAMETERS filled out, we can finally create the IDirectSDDevice9 object with the following method:
HRESULT IDirect3D9::CreateDevice(
Adapter: Specifies the physical display adapter we want the created IDirect3DDevice9 object to represent.
DeviceType: Specifies the device type to use (e.g., hardware device [D3DDEVTYPE_HAL] or reference device [D3DDEVTYPE_REF] ).
hFocusWindow: Handle to the window the device will be associated with. This is typically the window into which the device will draw, and for our purposes it will be the same handle we specified for the data member d3dpp.hDeviceWindow of the D3DPRESENT_PARAMETERS structure.
BehaviorFlags: Here we specify the creation flags we checked for in §4.3.3; that is, we specify either:
D3DCREATE_HARDWARE_VERTEXPROCESSING
D3DCREATE_HARDWARE_VERTEXPROCESSINS|D3DCREATE_PUREDEVICE,D3DCREATESOFTWARE_VERTEXPROCESSING,or D3DCREATE_MIXED_VERTEXPROCESSING.
There are other additional flags; see D3DCREATE in the SDK documentation for details.
pPresentationParameters: Specify an initialized D3DPRESENT_PARAMETERS instance that defines some of the characteristics of the device to create.
ppReturnedDevicelnterface: Returns the created device.
Note If hardware vertex processing is supported, you can actually use D3DCREATE_MIXED_VERTEXPROCESSING , which allows you to switch between hardware vertex processing and software vertex processing at run time. To switch, you use the following method:
HRESULT SetSoftwareVertexProcessing(BOOL bSoftware);
Note Only one IDirect3DDevice9 object should be created per application. Consequently, since there is only one IDirect3DDevice9 object, it is typical to provide access to this object at the global scope.