Index buffers, represented by the IDirect3DIndexBuffer9 interface, are memory buffers that contain index data. Index data, or indices, are integer offsets into vertex buffers and are used to render primitives using the IDirect3DDevice9::DrawIndexedPrimitive method.
A vertex buffer contains vertices; therefore, you can draw a vertex buffer either with or without indexed primitives. However, because an index buffer contains indices, you cannot use an index buffer without a corresponding vertex buffer. (As a side note, IDirect3DDevice9::DrawIndexedPrimitiveUP and IDirect3DDevice9::DrawPrimitiveUP are the only draw methods that draw without an index or a vertex buffer.)
An index buffer is described in terms of its capabilities, such as where it exists in memory, whether it supports reading and writing, and the type and number of indices it can contain. These traits are held in a D3DINDEXBUFFER_DESC structure.
Index buffer descriptions tell your application how an existing buffer was created. You provide an empty description structure for the system to fill with the capabilities of a previously created index buffer.
The performance of index processing operations depends heavily on where the index buffer exists in memory and what type of rendering device is being used. Applications control the memory allocation for index buffers when they are created. When the D3DPOOL_SYSTEMMEM memory flag is set, the index buffer is created in system memory. When the D3DPOOL_DEFAULT memory flag is used, the device driver determines where the memory for the index buffer is best allocated, often referred to as driver-optimal memory. Driver-optimal memory can be local video memory, non-local video memory, or system memory.
Setting the D3DUSAGE_SOFTWAREPROCESSING behavior flag when calling the IDirect3DDevice9::CreateIndexBuffer method specifies that the index buffer is to be used with software vertex processing. This flag is required in mixed mode vertex processing (D3DCREATE_MIXED_VERTEXPROCESSING) when software vertex processing is used.
The application can directly write indices to a index buffer allocated in driver-optimal memory. This technique prevents a redundant copy operation later. This technique does not work well if your application reads data back from an index buffer, because read operations done by the host from driver-optimal memory can be very slow. Therefore, if your application needs to read during processing or writes data to the buffer erratically, a system-memory index buffer is a better choice.
Note Always use D3DPOOL_DEFAULT, except when you don't want to use video memory or use large amounts of page-locked RAM when the driver is putting vertex or index buffers into AGP memory.
Create an index buffer object by calling the IDirect3DDevice9::CreateIndexBuffer method, which accepts six parameters.
The second parameter is a set of usage controls. Among other things, its value determines whether the vertices being referred to by the indices are capable of containing clipping information. To improve performance, specify D3DUSAGE_DONOTCLIP when clipping is not required.
The D3DUSAGE_SOFTWAREPROCESSING flag can be set when mixed-mode or software vertex processing (D3DCREATE_MIXED_VERTEXPROCESSING / D3DCREATE_SOFTWARE_VERTEXPROCESSING) is enabled for that device. D3DUSAGE_SOFTWAREPROCESSING must be set for buffers to be used with software vertex processing in mixed mode, but it should not be set for the best possible performance when using hardware index processing in mixed mode (D3DCREATE_HARDWARE_VERTEXPROCESSING). However, setting D3DUSAGE_SOFTWAREPROCESSING is the only option when a single buffer is used with both hardware and software vertex processing. D3DUSAGE_SOFTWAREPROCESSING is allowed for mixed and software devices.
It is possible to force vertex and index buffers into system memory by specifying D3DPOOL_SYSTEMMEM, even when the index processing is being done in hardware. This is a way to avoid overly large amounts of page-locked memory when a driver is putting these buffers into AGP memory.
The third parameter is either the D3DFMT_INDEX16 or D3DFMT_INDEX32 member of the D3DFORMAT enumerated type that specifies the size of each index.
The fourth parameter is a member of the D3DPOOL enumerated type that tells the system where in memory to place the new index buffer.
The final parameter that IDirect3DDevice9::CreateIndexBuffer accepts is the address of a variable that is filled with a pointer to the new IDirect3DIndexBuffer9 interface of the vertex buffer object, if the call succeeds.
The following C++ code example shows what creating an index buffer might look like in code.
/* * For the purposes of this example, the d3dDevice variable is the * address of an IDirect3DDevice9 interface exposed by a * Direct3DDevice object, g_IB is a variable of type * LPDIRECT3DINDEXBUFFER9. */ if( FAILED( d3dDevice->CreateIndexBuffer( 16384 *sizeof(WORD), D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &g_IB, NULL ) ) ) return E_FAIL;
Index buffer objects enable applications to directly access the memory allocated for index data. You can retrieve a pointer to index buffer memory by calling the IDirect3DIndexBuffer9::Lock method, and then accessing the memory as needed to fill the buffer with new index data or to read any data it contains. The Lock method accepts four parameters. The first, OffsetToLock, is the offset into the index data. The second parameter is the size, measured in bytes, of the index data. The third parameter accepted by the IDirect3DIndexBuffer9::Lock method, ppbData, is the address of a BYTE pointer filled with a pointer to the index data, if the call succeeds.
The last parameter, Flags, tells the system how the memory should be locked. You can use it to indicate how the application accesses the data in the buffer. Specify constants for the Flags parameter according to the way the index data will be accessed by your application. This allows the driver to lock the memory and provide the best performance given the requested access type. Use D3DLOCK_READONLY flag if your application will read only from the index buffer memory. Including this flag enables Direct3D to optimize its internal procedures to improve efficiency, given that access to the memory will be read-only.
After you fill or read the index data, call the IDirect3DIndexBuffer9::Unlock method, as shown in the following code example.
// This code example assumes the m_pIndexBuffer is a variable of type // LPDIRECT3DINDEXBUFFER9 and that g_Indices has been properly // initialized with indices. // To fill the index buffer, you must lock the buffer to gain // access to the indices. This mechanism is required because index // buffers may be in device memory. VOID* pIndices; if( FAILED( m_pIndexBuffer->Lock( 0, // Fill from start of the buffer sizeof(g_Indices), // Size of the data to load BYTE**)&pIndices, // Returned index data 0 ) ) ) // Send default flags to the lock { SAFE_RELEASE(m_pIndexBuffer); return E_FAIL; } memcpy( pIndices, g_Indices, sizeof(g_Indices) ); m_pIndexBuffer->Unlock();
Note
If you create an index buffer with the D3DUSAGE_WRITEONLY flag, do not use the D3DLOCK_READONLY locking flag. Use the D3DLOCK_READONLY flag if your application will read only from the index buffer memory. Including this flag enables Direct3D to optimize its internal procedures to improve efficiency, given that access to the memory will be read-only.
For information about using D3DLOCK_DISCARD or D3DLOCK_NOOVERWRITE for the Flags parameter of the IDirect3DIndexBuffer9::Lock method, see Performance Optimizations.
In C++, because you directly access the memory allocated for the index buffer, make sure your application properly accesses the allocated memory. Otherwise, you risk rendering that memory invalid. Use the stride of the index format your application uses to move from one index in the allocated buffer to another.
Retrieve information about an index buffer by calling the IDirect3DIndexBuffer9::GetDesc method. This method fills the members of the D3DINDEXBUFFER_DESC structure with information about the vertex buffer.