Creating DirectDraw and Direct3D Objects
The CreateD3DApp function in D3dmain.cpp is responsible for initializing the DirectDraw and Direct3D objects that are required before rendering begins. Important local functions called by CreateD3DApp include D3DAppCreateFromHWND, D3DAppGetRenderState, OverrideDefaults, D3DAppSetRenderState, ReleaseView, and InitView. Functions whose names begin with D3DApp are part of the D3DApp series of helper functions.
The same command-line options passed to the WinMain function are also passed to CreateD3DApp. Valid options are -systemmemory and -emulation. The -systemmemory option is used purely for debugging. The -emulation option prevents the application from using DirectDraw or Direct3D hardware.
CreateD3DApp calls the D3DAppAddTexture function to generate a series of textures. Then the D3DAppAddTexture function creates a source texture surface and object in system memory. Then it creates a second, initially empty, texture surface in video memory if hardware is present. The source texture is loaded into the destination texture surface and then discarded. This two-stage process allows a device to compress or reformat a texture map as it enters video memory. The code uses the IDirectDrawSurface::QueryInterface method to retrieve an interface to an IDirect3DTexture interface and the IDirect3DTexture::Load method to load the textures. The IDirect3DTexture::GetHandle method is used to create a list of texture handles.
After creating a list of textures, CreateD3DApp creates the DirectDraw and Direct3D objects required to start rendering. The code uses the D3DAppCreateFromHWND helper function. D3DAppCreateFromHWND uses functions that are implemented in the D3dapp.c, D3dcalls.c, Texture.c, and Ddcalls.c source files.
First, D3DAppCreateFromHWND uses the DirectDrawEnumerate and DirectDrawCreate functions to create and initialize the DirectDraw object, sets the values of global variables, and enumerates the display modes by calling the IDirectDraw2::EnumDisplayModes method.
D3DAppCreateFromHWND then creates the Direct3D object and enumerates the Direct3D device drivers. To create the Direct3D object, it calls the IDirectDraw::QueryInterface method, passing the IID_IDirect3D interface identifier. It uses IDirect3D::EnumDevices to enumerate the device drivers.
Calling IDirect3D::EnumDevices is not the easiest or even the best way to find Direct3D device drivers, however. Instead of setting up an enumeration routine, most Immediate-Mode applications use the IDirect3D::FindDevice method. This method allows you to simply specify the capabilities of the device you prefer—the system examines the available drivers and returns the globally unique identifier (GUID) for the first matching device. The system always searches the hardware first, so if both a hardware and a software device meet the required capabilities, the system returns the GUID for the hardware device.
After choosing a device driver and display mode (full-screen versus windowed), D3DAppCreateFromHWND creates front and back buffers for the chosen display mode. The code performs different actions based on whether the application is running in a window or the full screen, and whether video memory or system memory is being employed. If the application is running in a window, the code calls the IDirectDraw::CreateClipper method to create a clipper object and then calls the IDirectDrawClipper::SetHWnd method to attach it to the window and the IDirectDrawSurface::SetClipper method to attach it to the front buffer.
Then, D3DAppCreateFromHWND checks whether the front buffer is palettized. If it is, the code initializes the palette. First it creates the palette by calling the IDirectDraw::CreatePalette method, then it uses the IDirectDrawSurface::SetPalette method to set this as the palette for the front and back surfaces.
At this point the code calls the IDirectDraw::CreateSurface method to create a z-buffer, the IDirectDrawSurface::AddAttachedSurface method to attach the z-buffer to the back buffer, and the IDirectDrawSurface::GetSurfaceDesc method to determine whether the z-buffer is in video memory.
Then the code creates an IDirect3DDevice interface and uses it to enumerate the texture formats. The sample calls the IDirectDrawSurface::QueryInterface method to create the interface and the IDirect3DDevice::EnumTextureFormats method to enumerate the texture formats. When the textures have been enumerated, the code uses the same series of calls it employed in CreateD3DApp to load the textures and create a list of texture handles.
After using the driver's bit-depth and total video memory to filter the appropriate display modes, the code sets up the device-creation callback function, which is described in Setting Up the Device-Creation Callback Function.
When the device-creation callback function has been set up, D3DAppCreateFromHWND sets the application's render state, which is described in Setting the Immediate-Mode Render State.
When the D3DAppCreateFromHWND function has created the required Direct3D objects and set up the render state, it is almost finished. The function calls a local function to set the dirty rectangles for the front and back buffers to the entire client area, sets flags indicating that the application is initialized and that rendering can proceed, and returns TRUE.
The last part of the D3DAppCreateFromHWND function is its error-handling section. Whenever a call fails, the error-handling code jumps to the exit_with_error label. This section calls the callback function that destroys the device, resets the display mode and cooperative level if the application was running in full-screen mode, releases all the Direct3D and DirectDraw objects that were created, and returns FALSE.