Creating a Direct3D Renderer
After going through the basics in our previous tutorials, we can jump into Direct3D programming
by learning how to create and initialize what we need to setup before we can draw anything.
Some History…
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.
Adding the Renderer to the GameEngineCore
The next step of our project is to add the D3D8Renderer to the GameEngineCore object in our codebase. As
you’ll notice in the attached code, we are initializing everything during the GameEngineCore’s
startup phase, and cleaning itself up during the shutdown phase.
m_pRenderer = new D3D8Renderer();
m_pRenderer->setHWND( m_hWnd );
if(!m_pRenderer->loadDevice( iWidth, iHeight, m_bWindowed ))
{
FileLogger::getSingletonPtr()
->logFatal( "GameEngineCore",
"Failed to initialize application objects." );
unloadGameEngine();
return false;
}
In the main loop area of GameEngineCore we are
also using the clearDevice and flipDevice methods of the D3D8Renderer object to prepare our device for what
we want to draw, and then once we’ve finished drawing, use the flip to make the scene “viewable” for our game.
// Render a frame during idle time (no messages are waiting)
if( m_bActive )
{
m_pMainApp->onUpdateApp();
if(m_pRenderer->clearDevice())
{
m_pMainApp->onRenderApp();
m_pRenderer->flipDevice();
}
}
Enjoy the code, and make sure to play with it somewhat to figure the basics out!











This is default description text on Padangan Themes, of course you can change this text via you profile administration.