First Look at DX8 Graphics

Home | Up | Search | X-Zone News | Services | Book Support | Links | Feedback | Smalltalk MT | The Scrapyard | FAQ | Technical Articles

 

Moving to DirectX 8:
A First Look at DirectX 8 Graphics

Written by Robert Dunlop
Microsoft DirectX MVP


Table of Contents

Introduction - A New Graphics Architecture
2D Operations under DirectX Graphics
Exclusive Use of Flexible Vertex Formats
Addition of Index Buffers
Drawing Primitives: Setting Streams and Shaders
Support of Non-Buffered Primitives
Changes in State Names
New D3DX Library Naming Conventions
Added 1/6/2001

 Introduction - A New Graphics Architecture

Whether porting an existing application written for DX 7, or writing a new application based on prior knowledge of DX 7, the new DirectX SDK may seem a bit daunting at first.  Having ported several programs over to DirectX 8, I will attempt to relate some of my experiences here, and provide some insight into how to make the changeover as smoothly as possible.

One of the most profound changes, overall, is the integration of DirectDraw with Direct3D.  All access to the graphics device is now performed through the Direct3D device, rather than having a separate DirectDraw layer for 2D operations.  All graphics functionality is wrapped under a single API, known as DirectX Graphics.

2D Operations under DirectX Graphics

DirectX graphics is specialized for 3D operations, and as such may appear to lack the previous support of 2D operations.  However, it does provide several means to perform 2D operations. DX Graphics is structured to make use of 3D hardware acceleration, and 2D operations are thus generally performed using 3D primitives.

Here is a look at the methods available for performing 2D operations under DirectX Graphics:

bullet

For simple blit operations, images can be loaded onto IDirect3DSurface8 surfaces, and blitted using the IDirect3DDevice8::CopyRects() method.  This method supports performing multiple rectangular blits in one call, but provides no support for alpha, stretching, color keys, clipping, or format conversion.  The back buffer surface can be attained for use as a target by calling the IDirect3DDevice8::GetBackBuffer() method.

bullet

A surface, or portion thereof, may be locked for memory access using the IDirect3DSurface8::LockRect() method.  The back buffer may be attained by calling the IDirect3DDevice8::GetBackBuffer() method.  Note that for the back buffer to be locked, the swapping chain must have been created with the D3DPRESENTFLAG_LOCKABLE_BACKBUFFER flag set.

bullet

The ID3DXSprite can be used to draw 2D sprites with 2D or 3D transformation applied.

bullet

Simple pre-transformed primitives can be used to apply a texture to the render target using the DrawPrimitive methods.

bullet

Point sprites may be used to render a texture to a square region of the render surface.

Exclusive Use of Flexible Vertex Formats

In DirectX 7, support for Flexible Vertex Formats was provided, while still supplying standard vertex types for ready use.  Under DirectX 8, however, all operations are based on application specified vertex formats using the FVF flags.

Using Flexible Vertex Formats allows you to tailor the structure of your vertices to include only those elements that your application needs, thus reducing the memory footprint of vertex buffers as well as decreasing the bandwidth necessary to process them.

However, when getting started with DX 8, or porting applications from DX 7 that  use the pre-defined formats, having templates that match the previous formats is a handy starting point.  You can find a set of definitions in Definitions for Standard DX7 Vertex Types, which you can use as they are or as sample templates to modify to your needs.

Addition of Index Buffers

Just as vertex buffers are used to avoid unnecessary copying of vertices at render time, indexes are now also packaged into lockable buffers, known as index buffers.  Support is now also provided for 32 bit index values, if supported by the device, allowing the 64K vertex limit to be broken.  To check for support, check the MaxVertexIndex member of D3DCAPS8.

For more information on creating index buffers, see the documentation for the IDirect3DDevice8::CreateIndexBuffer() method.

Drawing Primitives: Setting Streams and Shaders

Unlike previous versions of the DrawPrimitive methods, in DX 8 the vertex format and source buffers for vertices and indices are set prior to rendering.  The following functions are used:
bullet

IDirect3DDevice8::SetVertexShader() - Defines how vertices will be processed by the pipeline.  This function accepts either a combination of the FVF flags defining the vertex format, or a handle to a programmable vertex shader.

bullet

IDirect3DDevice8::SetIndices() - Sets a pointer to the index buffer to be used by calls to the DrawIndexedPrimitive() method.  Accepts an index buffer pointer, and a value that will be added to all index values.

bullet

IDirect3DDevice8::SetStreamSource() - Set a pointer to incoming vertex data.  This function takes a stream number, a vertex buffer pointer, and a stride value specifying the number of bytes between vertices.  If not using a programmable vertex shader, all data is read from stream 0.  Multiple streams may be used with programmable vertex shaders, allowing different components of a vertex 

For example, the following DX 7 call:

lpDevice->DrawIndexedPrimitiveVB(D3DPT_TRIANGLELIST,vbuf,0,128,lpIndex,630,0);

would, in DX 8, look something like this:

lpDevice->SetIndices(indBuf,0);
lpDevice->SetStreamSource(0,vbuf,sizeof(D3DVERTEX);
lpDevice->SetVertexShader(D3DFVF_D3DVERTEX);
lpDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,128,0,210);

Note that the number of triangles to be rendered, rather than the number of indices used (210 vs. 630), is passed to the rendering function.

Support of Non-Buffered Primitives

The DrawPrimitive methods in DX 8 are designed to utilize vertex buffers.  To render primitives stored in arrays of vertices, the DrawPrimitiveUP() and DrawIndexedPrimitiveUP() methods are provided to support this legacy method.  There are two major differences from the previous DrawPrimitive functions that should be noted:
bullet

The vertex format must be defined first, by a call to SetVertexShader(), using a combination of FVF flags.

bullet

These functions require a primitive count, defining the number of triangles to render.  This is in place of a vertex count in DrawPrimitiveUP(), and in place of an index count in DrawIndexedPrimitiveUP().

Changes in State Names

The naming conventions for state enumerations has changed from Direct3D 7 to DirectGraphics 8, shortening the state names :

bulletThose starting with D3DRENDERSTATE_ now start with D3DRS_.
bulletThose starting with D3DTRANSFORMSTATE_ now start with D3DTS_.
bulletRather than having separate texture filtering flags for minimization and magnification, prefixed with D3DTFN_ and D3DTFG_, a single enumerated type is used, starting with the prefix D3DTEXF_.

New D3DX Library Naming Conventions

Separate D3DX library files are now provided for release and debug builds.  Release builds must link to D3DX8.LIB, and debug builds must link to D3DX8D.LIB.  You can set up linking of the D3D8 and D3DX linkage in your source code, using a conditional linkage like this :

#pragma comment(lib,"d3d8.lib")
#ifdef _DEBUG
    #pragma comment(lib, "d3dx8d.lib")
#else
    #pragma comment(lib, "d3dx8.lib")
#endif

Note that you must also use the next D3DX8.H header file, failure to do so will result in the DX 7 headers being used.  Unlike most headers in the SDK, these are version dependant.

This site, created by DirectX MVP Robert Dunlop and aided by the work of other volunteers, provides a free on-line resource for DirectX programmers.

Special thanks to WWW.MVPS.ORG, for providing a permanent home for this site.

Visitors Since 1/1/2000: Hit Counter
Last updated: 07/26/05.