Command and Vertex Buffer Allocation

There are three types of buffers used in Direct3D:

  • Implicit vertex buffers, which are created for internal use only; that is, applications are unaware of them. One implicit vertex buffer is always created after context creation and Direct3D stores vertex data in them.

  • Explicit vertex buffers, which are created only in response to an application request. Direct3D then stores vertex data in explicit vertex buffers.

  • Command buffers, which are created for internal use only; that is, applications are unaware of command buffers. Direct3D stores command data in command buffers.

Implicit vertex buffers are special vertex buffers used internally by Direct3D for batching. They are created during device initialization and can be multibuffered. They are always read/write so they should not be put in video memory (for Microsoft DirectX 6.0 and the later versions). This type of buffer is marked by the absence of both the DDSCAPS2_VERTEXBUFFER and DDSCAPS2_COMMANDBUFFER flags.

Explicit vertex buffers are created and controlled by the application. These cannot be multibuffered and cannot be put into local or nonlocal video memory unless the DDSCAPS_WRITEONLY flag is set. Explicit vertex buffers are marked with DDSCAPS_VERTEXBUFFER.

Command buffers are used by Direct3D to batch commands. They can be multibuffered and are used for all APIs except for TLVERTEX or unclipped execute-buffer API calls. This type of buffer is marked by the flag DDSCAPS2_COMMANDBUFFER. They are always write-only, though no explicit flag is set and they never contain invalid instructions.

By default, the Direct3D runtime allocates all of these buffers. Implicit vertex buffers and command buffers are accessed through the surfaces with which they are associated. All buffers are passed to the driver's D3dDrawPrimitives2 callback.

Driver-Allocated Vertex and Command Buffers

A Direct3D driver optionally performs the allocation of vertex and command buffers by supplying callback functions. To supply these callback functions, the Direct3D driver fills out a DD_D3DBUFCALLBACKS structure and points the lpD3DBufCallbacks member of the DD_HALINFO structure to it. DD_HALINFO is returned by DrvGetDirectDrawInfo in response to the initialization of the DirectDraw component of the driver. The callbacks reported in the DD_D3DBUFCALLBACKS structure are:

These functions are called in the same way as the DdXxxSurface callbacks (such as DdCanCreateSurface) and only when the DDSCAPS_EXECUTEBUFFER flag is set. The buffer creation flags are DDSCAPS_WRITEONLY, DDSCAPS2_VERTEXBUFFER, and DDSCAPS2_COMMANDBUFFER.

Drivers determine the type of buffer being requested by checking the ddsCaps member of the DD_SURFACE_LOCAL structure passed to the CanCreateExecuteBuffer and CreateExecuteBuffer callback for the following flags:

  • DDSCAPS_VERTEXBUFFER indicates that the driver should allocate an explicit vertex buffer.

  • DDSCAPS_COMMANDBUFFER indicates that the driver should allocate a command buffer.

  • If neither flag is set, the driver should allocate an implicit vertex buffer.

The driver internally allocates vertex and command buffers and cycles through these buffers. Direct3D fills a given pair while the hardware asynchronously renders from the other queued buffers. This is very useful with direct memory access (DMA).

Buffers in a multibuffering set can be in different memory types, that is, in system or video memory. When the driver is called to create the first buffer, it creates the set immediately and returns the first buffer in the set to Direct3D. The driver uses flags to specify the type of memory that it used to allocate each buffer in the set. The driver should return a new buffer in system memory for each call to D3dDrawPrimitives2 if the D3DHALDP2_SWAPVERTEXBUFFER or D3DHALDP2_SWAPCOMMANDBUFFER flag is set. If the returned buffer is in video memory, the corresponding D3DHALDP2_VIDMEMVERTEXBUF or D3DHALDP2_VIDMEMCOMMANDBUF flag should be set.

Occasionally, Direct3D requests the minimum size for the next buffer. If the size is too large, the driver should allocate the buffer in system memory (a backing surface). If the size is too small, the driver is permitted to provide a larger buffer. The driver should keep track of how many buffers and what memory types they are and clean up everything on exit.