Getting started with DirectX10 – Part 2: Initialising our Direct3D Device
We are not going to write all our code into one class. The goal is to build a mini game-engine. So, we create a class named DX10Engine, using the default wizard settings.
We add the dx includes in the stdafx file:
#include <d3d10.h>
#include <d3dx10.h>
Don’t forget to cleanbuild you application, after changing stdafx.h !
We create a class DX10Engine, using the default wizard settings.
In the include file:
We change the constructor, because we need the window pointer.
DX10Engine(HWND ptr);
We add the methods:
HRESULT Init(void);
void CleanupDX10Device();
void Render(void);
We add the class member Variables:
HWND m_hWindow;
D3D10_DRIVER_TYPE m_driverType;
ID3D10Device* m_pd3d10Device;
IDXGISwapChain* m_pSwapChain;
ID3D10RenderTargetView* m_pRenderTargetView;
ID3D10Texture2D* m_pDepthStencil;
ID3D10DepthStencilView* m_pDepthStencilView;
The source file:
#include "StdAfx.h"
#include "DX10Engine.h"
In the constructor, we initialise our member variables.
DX10Engine::DX10Engine(HWND ptr):m_hWindow(ptr),
m_driverType(D3D10_DRIVER_TYPE_NULL),
m_pd3d10Device(NULL),
m_pSwapChain(NULL),
m_pRenderTargetView(NULL),
m_pDepthStencil(NULL),
m_pDepthStencilView(NULL)
{
}
DX10Engine::~DX10Engine(void)
{
CleanupDX10Device();
}
HRESULT DX10Engine::InitDX10Device(){
HRESULT hr = S_OK;
We calculate the window size.
RECT rc;
GetClientRect( m_hWindow, &rc );
UINT width = rc.right - rc.left;
UINT height = rc.bottom - rc.top;
UINT createDeviceFlags = 0;
We use an array to define what kind of driver types we will support. Some devices are implemented in hardware (your video card) and some are implemented in software (the Direct3D reference rasterizer of software renderer). The order is important, we first try to get a hardware device!
D3D10_DRIVER_TYPE driverTypes[] =
{
D3D10_DRIVER_TYPE_HARDWARE,
D3D10_DRIVER_TYPE_REFERENCE,
};
We calculate the length of the array
UINT numDriverTypes = sizeof(driverTypes) / sizeof(driverTypes[0]);
We create a device, the device creates “a swap chain”. Usually games use double buffering. One buffer is displayed on the monitor (frontbuffer), while the other is invisible and used to draw in. When drawing operations are finished, the backbufferpointer and the frontbuffer pointers are swapped. This is also called flipping. To tell the device what kind of swapchain we want, we fill in a struct, and then use the struct as a parameter. Using a struct is done to reduce the amount of needed parameters.
DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory( &sd, sizeof(sd) );
sd.BufferCount = 1;
sd.BufferDesc.Width = width;
sd.BufferDesc.Height = height;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.OutputWindow = m_hWindow;
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.Windowed = TRUE;
for( UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++ )
{
m_driverType = driverTypes[driverTypeIndex];
hr = D3D10CreateDeviceAndSwapChain( NULL, m_driverType, NULL, createDeviceFlags,
D3D10_SDK_VERSION, &sd, &m_pSwapChain, &m_pd3d10Device );
if( SUCCEEDED( hr ) )
break;//succeeded !!
}
if( FAILED(hr) )
return hr;
Now we have the device and the SwapChain.
We create a render target view.
ID3D10Texture2D *pBackBuffer;
We ask the swapchain to return us one of the backbuffers.
hr = m_pSwapChain->GetBuffer( 0, __uuidof( ID3D10Texture2D ), (LPVOID*)&pBackBuffer );
if( FAILED(hr) )
return hr;
We use the buffer to create a RenderTargetView, rendering done is offsceen, on this backbuffer
hr = m_pd3d10Device->CreateRenderTargetView( pBackBuffer, NULL, &m_pRenderTargetView );
pBackBuffer->Release();
if( FAILED(hr) )
return hr;
A depth-stencil buffer, which is created as a texture resource, can contain both depth data and stencil data. The depth data is used to determine which pixels lie closest to the camera, and the stencil data is used to mask which pixels can be updated. Ultimately, both the depth and stencil values data are used by the output-merger stage to determine if a pixel should be drawn or not. Without this buffer, objects are simply drawn ontop of each other. This buffer makes intersections look right.
First, fill in a struct with a descriptor of what we want, the 3Ddevice creates a texture buffer using the struct
D3D10_TEXTURE2D_DESC descDepth;
descDepth.Width = width;
descDepth.Height = height;
descDepth.MipLevels = 1;
descDepth.ArraySize = 1;
descDepth.Format = DXGI_FORMAT_D32_FLOAT;
descDepth.SampleDesc.Count = 1;
descDepth.SampleDesc.Quality = 0;
descDepth.Usage = D3D10_USAGE_DEFAULT;
descDepth.BindFlags = D3D10_BIND_DEPTH_STENCIL;
descDepth.CPUAccessFlags = 0;
descDepth.MiscFlags = 0;
hr = m_pd3d10Device->CreateTexture2D( &descDepth, NULL, &m_pDepthStencil );
if( FAILED(hr) )
return hr;
Now we fill in a view descriptor struct, and pass it together with the m_pDepthStencilbuffer to the 3DDevice, which creates a DepthstencilView
// Create the depth-stencil state, wat staat voor elkaar ?
D3D10_DEPTH_STENCIL_VIEW_DESC descDSV;
descDSV.Format = descDepth.Format;
descDSV.ViewDimension = D3D10_DSV_DIMENSION_TEXTURE2D;
descDSV.Texture2D.MipSlice = 0;
hr = m_pd3d10Device->CreateDepthStencilView( m_pDepthStencil, &descDSV, &m_pDepthStencilView );
if( FAILED(hr) )
return hr;
Now we have a rendertargetview and a depthstencilview, now they are merged to the “output-merger state”.
m_pd3d10Device->OMSetRenderTargets( 1, &m_pRenderTargetView, m_pDepthStencilView );//correct intersections
//m_pd3d10Device->OMSetRenderTargets( 1, &m_pRenderTargetView, 0 );//on top of each other
// Setup the viewport, and bind it to the rasteriser stage. Here we use only one viewport
D3D10_VIEWPORT vp;
vp.Width = width;
vp.Height = height;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = 0;
vp.TopLeftY = 0;
m_pd3d10Device->RSSetViewports( 1, &vp );
return S_OK;
}
void DX10Engine::Render(void)
{
We start by clearing the Target View. We are using double buffering,so none of this is visible.
float ClearColor[4] = { 0.0f, 0.125f, 0.3f, 1.0f }; //red,green,blue,alpha
m_pd3d10Device->ClearRenderTargetView( m_pRenderTargetView, ClearColor );
We also clear the depth buffer to its max value of 1.0f
m_pd3d10Device->ClearDepthStencilView( m_pDepthStencilView, D3D10_CLEAR_DEPTH, 1.0f, 0 );
Here we want to do some drawing later…
Finaly, the result is presented to the visible screen. Use 1,0 to sync with vertical blanking.
m_pSwapChain->Present( 0, 0 );
}
We have to release the used components
DX10Engine::~DX10Engine(void)
{
CleanupDX10Device();
}
void DX10Engine::CleanupDX10Device()
{
//
if( m_pd3d10Device ) m_pd3d10Device->ClearState();
if( m_pRenderTargetView ) m_pRenderTargetView->Release();
if( m_pDepthStencil ) m_pDepthStencil->Release();
if( m_pDepthStencilView ) m_pDepthStencilView->Release();
if( m_pSwapChain ) m_pSwapChain->Release();
}
In DX10Tutorial.h, we add:
DX10Engine *m_pDX10Engine;
#include DX10Engine.h
We move HWND hWnd; from the InitInstance method to the include file, we make it a member pointer. We need it to initialise the DX10 device.
You can download the complete source here.

2 Comments
Jump to comment form | comments rss [?] | trackback uri [?]