Getting Started With DirectX8.1
This tutorial is an introduction to getting a bare-bones DirectX8.1 application configured and running. While we could get started on our own class framework to take care of the lower-level Windows initialization for us, there really isn’t much point if the goal is to just learn the beginning stages of using DirectX.
EmptyProject == Hello World
For our basic “Hello World” type of tutorial using the DirectX common framework, we’re going to create an empty shell of an application which will:
- Create a new class derived from the sample framework
- Launch the app and handle our registration with the Windows OS
- Query our video hardware to create a default Direct3D device
- Display a solid blue background to the monitor
MyApp
To begin with, we’ll create a new class which will be derived from the
main driver class of the DirectX common framework, CD3DApplication. If you
open the accompanying source code, you’ll find the declaration for MyApp.
class MyApp : public CD3DApplication
{
protected:
protected:
HRESULT ConfirmDevice( D3DCAPS8*, DWORD, D3DFORMAT );
HRESULT OneTimeSceneInit();
HRESULT InitDeviceObjects();
HRESULT RestoreDeviceObjects();
HRESULT InvalidateDeviceObjects();
HRESULT DeleteDeviceObjects();
HRESULT FinalCleanup();
HRESULT Render();
HRESULT FrameMove();
public:
MyApp();
~MyApp();
};
That’s all you have to do to get yourself up and running!
Winmain - Launching the application
Every Windows application from games to business applications need to register
themselves properly with the Windows operating system, in order to fit properly
into the Windows event model of operations. The first step of this process
is to always define a WinMain entry point into your application.
MyApp g_d3dApp;
/**
* Name: WinMain()
* Desc: Entry point to the program. Initializes everything, and goes into a
* message-processing loop. Idle time is used to render the scene.
*/
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
{
if( FAILED( g_d3dApp.Create( hInst ) ) )
return 0;
return g_d3dApp.Run();
}
Within the Create method that is a member function of the CD3DApplication
class, is the next step to proper Windows registration; filling out a
WNDCLASS structure and creating the main window.
// Register the windows class
WNDCLASS wndClass = { 0, WndProc, 0, 0, hInstance,
LoadIcon( hInstance, MAKEINTRESOURCE(IDI_MAIN_ICON) ),
LoadCursor( NULL, IDC_ARROW ),
(HBRUSH)GetStockObject(WHITE_BRUSH),
NULL, _T("D3D Window") };
RegisterClass( &wndClass );
// Set the window's initial style
m_dwWindowStyle = WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_THICKFRAME|
WS_MINIMIZEBOX|WS_VISIBLE;
// Set the window's initial width
RECT rc;
SetRect( &rc, 0, 0, m_dwCreationWidth, m_dwCreationHeight );
AdjustWindowRect( &rc, m_dwWindowStyle, TRUE );
// Create the render window
m_hWnd = CreateWindow( _T("D3D Window"), m_strWindowTitle, m_dwWindowStyle,
CW_USEDEFAULT, CW_USEDEFAULT,
(rc.right-rc.left), (rc.bottom-rc.top), 0L,
LoadMenu( hInstance, MAKEINTRESOURCE(IDR_MENU) ),
hInstance, 0L );
Querying the Video Hardware
The whole point of the architecture behind DirectX is to shield you (the programmer) from having to deal
with lower-level details such as setting proper memory bit flags, or even for writing your rendering details
into a specific memory address range (from the SVGA days), which slightly differed depending on video card
vendor. (Have you hugged an SVGA assembly programmer today?)
Direct3D abstracts this to a level where you simply pass your rendering commands to the
Direct3DDevice (eg.
video card) and the Direct3DDevice will do the rest. The video hardware manufacturer/vendor has the onus and
responsibility to follow the Direct3D specification, and tune their performance accordingly.
For our tutorial, the general method of creating a Direct3D device is:
- Initialize the main IDirect3D8 interface
- Use the IDirect3D8 interface to enumerate and query what is available on the machine
- Within this enumeration process, fill out some data structures with the device information we find
- Fill out a D3DPresentParameters structure with the device information we want
- Use the IDirect3D8 interface to create the IDirect3DDevice8 interface (our Direct3DDevice)
Enumerating Direct3D Devices
The attached project files for this tutorial demonstrate the enumeration process
more effectively than a simple overview here. If you can, keep the project open
while running through this basic description to understand what’s going on.
The first thing we do is query the Direct3D interface for the number of video
card adapters available to us. We store the information we get from each adapter.
Then we move to going through each adapter and enumerating the display modes
possible for each one.
// Loop through all the adapters on the system (usually, there's just one
// unless more than one graphics card is present).
for( UINT iAdapter = 0; iAdapter < m_pd3d->GetAdapterCount(); iAdapter++ )
{
// Fill in adapter info
D3DAdapterInfo* pAdapter = &m_d3dAdapters[m_dwNumAdapters];
m_pd3d->GetAdapterIdentifier( iAdapter,
D3DENUM_NO_WHQL_LEVEL,
&pAdapter->d3dAdapterIdentifier );
m_pd3d->GetAdapterDisplayMode( iAdapter, &pAdapter->d3ddmDesktop );
pAdapter->dwNumDevices = 0;
pAdapter->dwCurrentDevice = 0;
// Enumerate all display modes on this adapter
D3DDISPLAYMODE modes[100];
D3DFORMAT formats[20];
DWORD dwNumFormats = 0;
DWORD dwNumModes = 0;
DWORD dwNumAdapterModes = m_pd3d->GetAdapterModeCount( iAdapter );
// Add the adapter's current desktop format to the list of formats
formats[dwNumFormats++] = pAdapter->d3ddmDesktop.Format;
for( UINT iMode = 0; iMode < dwNumAdapterModes; iMode++ )
{
// Get the display mode attributes
D3DDISPLAYMODE DisplayMode;
m_pd3d->EnumAdapterModes( iAdapter, iMode, &DisplayMode );
For example, it will tell us that the adapter supports a 640×480x16 windowed and fullscreen display.
Next, we do a little bit of housekeeping on the data gathered so far, as the structure
is also filled with the various monitor refresh rates that the display mode supports. It’s probably
the safest to simply use the default refresh rate, otherwise we could zap the machine’s display.
// Filter out low-resolution modes if( DisplayMode.Width < 640 || DisplayMode.Height < 400 ) continue; // Check if the mode already exists (to filter out refresh rates) DWORD m; for( m=0L; m
After this, we go through our display mode information again, and test each one to see if it’s
various display properties are valid for our use. Once the display mode passes this litmus
test, it is stored for our convenience.
// Add all enumerated display modes with confirmed formats to the
// device's list of valid modes
DWORD m;
for( m=0L; m < dwNumModes; m++ )
{
for( f=0; f < dwNumFormats; f++ )
{
if( modes[m].Format == formats[f] )
{
if( bFormatConfirmed[f] == TRUE )
{
// Add this mode to the device's list of valid modes
pDevice->modes[pDevice->dwNumModes].Width = modes[m].Width;
pDevice->modes[pDevice->dwNumModes].Height = modes[m].Height;
pDevice->modes[pDevice->dwNumModes].Format = modes[m].Format;
pDevice->modes[pDevice->dwNumModes].dwBehavior = dwBehavior[f];
pDevice->modes[pDevice->dwNumModes].DepthStencilFormat = fmtDepthStencil[f];
pDevice->dwNumModes++;
if( pDevice->DeviceType == D3DDEVTYPE_HAL )
bHALIsSampleCompatible = TRUE;
}
}
}
}
There isn’t really much more than that. The sample code chooses a default display mode for our use, but
you can leave this out if you want to force the user to select their video settings.
Creating the Device
Finally we get down to business on creating the device. Again, it’s probably easier to have the code open
while reading this short overview of what’s involved.
In the current version of the sample code, if we’re specifying that we want a windowed device, then we
set the display parameters to match the width and height of the device. If we want a fullscreen device,
then we simply use the default parameters that we chose in the device enumeration process.
After filling out some other details, we make the official request to our Direct3D interface to create
our Direct3DDevice using the supplied parameters.
Rendering our Background
After all that process is undertaken for us by the underlying DirectX common framework,
We have access to the IDirect3DDevice8 interface via the class member
variable m_pd3dDevice. Direct3D attempts to work using a system
of what’s known as “double buffering” to handle graphics presentation. Essentially
the video hardware has access to 2 buffers; one to handle the drawing of the graphics data
in the front buffer (aka your video monitor), and another one to use as a canvas
to create the next frame that needs to be displayed. This is also
known as a buffer swap chain, in which the current front surface is flipped with
the contents in the “back” surface, thus creating the illusion of smooth
animation.This is handled in Direct3D, by first using Clear to clean
the contents of the back buffer. Next we signal to the video hardware that
we’re going to start drawing objects with the use of the BeginScene
function. Once we finish sending our object data, we signal to the hardware
that we’re finished with the use of the EndScene method. This method
is also responsible for signaling to our video device that we’re ready to initiate
the buffer chain swap.
HRESULT MyApp::Render()
{
// Clear the viewport
m_pd3dDevice->Clear( 0L, NULL, D3DCLEAR_ZBUFFER | D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0L );
// Begin the scene
if( SUCCEEDED( m_pd3dDevice->BeginScene() ) )
{
// End the scene.
m_pd3dDevice->EndScene();
}
return S_OK;
}
That’s it for this lesson. We covered some of the basics behind the DirectX common framework, and was able to get our first “Hello World” application up and running!
Feedback? Comments?
Either comment to this posting, or use our contact form to get in touch with us.





Leave a Reply