Step 5: Setting the Immediate-Mode Render State

The D3DAppISetRenderState function in the D3dcalls.c source file creates and executes an execute buffer that sets the render state and light state for the current viewport. The D3DAppCreateFromHWND function calls D3DAppISetRenderState from D3dapp.c; generally, the sample code calls D3DAppISetRenderState whenever the render state needs to be set or reset. This section reproduces the D3DAppISetRenderState function (except for some error-checking code).

After setting some local variables, including the D3DEXECUTEBUFFERDESC and D3DEXECUTEDATA structures, D3DAppISetRenderState calls the IDirect3DDevice::CreateExecuteBuffer method to create an execute buffer. When the execute buffer has been created, the code calls the IDirect3DExecuteBuffer::Lock method to lock it so that it can be filled.

BOOL D3DAppISetRenderState()

{

D3DEXECUTEBUFFERDESC debDesc;

D3DEXECUTEDATA d3dExData;

LPDIRECT3DEXECUTEBUFFER lpD3DExCmdBuf = NULL;

LPVOID lpBuffer, lpInsStart;

size_t size;

// Create an execute buffer of the required size and lock it

// so that it can be filled.

size = 0;

size += sizeof(D3DINSTRUCTION) * 3;

size += sizeof(D3DSTATE) * 17;

memset(&debDesc, 0, sizeof(D3DEXECUTEBUFFERDESC));

debDesc.dwSize = sizeof(D3DEXECUTEBUFFERDESC);

debDesc.dwFlags = D3DDEB_BUFSIZE;

debDesc.dwBufferSize = size;

LastError = d3dappi.lpD3DDevice->lpVtbl->CreateExecuteBuffer(

d3dappi.lpD3DDevice, &debDesc, &lpD3DExCmdBuf, NULL);

LastError = lpD3DExCmdBuf->lpVtbl->Lock(lpD3DExCmdBuf, &debDesc);

memset(debDesc.lpData, 0, size);

lpInsStart = debDesc.lpData;

lpBuffer = lpInsStart;

The d3dappi.lpD3DDevice parameter in the call to IDirect3DDevice::CreateExecuteBuffer is a pointer to a Direct3DDevice object. The lpData member of the debDesc variable (a D3DEXECUTEBUFFERDESC structure) is a pointer to the actual data in the execute buffer.

Now the D3DAppISetRenderState function sets the render states. It uses the OP_STATE_DATA macro to help do some of this work. This macro, in turn, uses the PUTD3DINSTRUCTION macro. These macros are defined as follows in the D3dmacs.h header file in this SDK:

#define PUTD3DINSTRUCTION(op, sz, cnt, ptr) \

((LPD3DINSTRUCTION) ptr)->bOpcode = op; \

((LPD3DINSTRUCTION) ptr)->bSize = sz; \

((LPD3DINSTRUCTION) ptr)->wCount = cnt; \

ptr = (void *)(((LPD3DINSTRUCTION) ptr) + 1)

#define OP_STATE_RENDER(cnt, ptr) \

PUTD3DINSTRUCTION(D3DOP_STATERENDER, sizeof(D3DSTATE), cnt, ptr)

Notice that the PUTD3DINSTRUCTION macro is little more than an initializer for the D3DINSTRUCTION structure. D3DOP_STATERENDER, the first parameter of the call to PUTD3DINSTRUCTION in the OP_STATE_RENDER macro, is one of the opcodes in the D3DOPCODE enumerated type; the second parameter is the size of the D3DSTATE structure.

The STATE_DATA macro, which is also defined in D3dmacs.h, handles the render states. To work with the render states, it uses pointers to members in the D3DSTATE structure—in particular, a pointer to the D3DRENDERSTATETYPE enumerated type.

#define STATE_DATA(type, arg, ptr) \

((LPD3DSTATE) ptr)->drstRenderStateType = (D3DRENDERSTATETYPE)type; \

((LPD3DSTATE) ptr)->dwArg[0] = arg; \

ptr = (void *)(((LPD3DSTATE) ptr) + 1)

The following code fragment from D3DAppISetRenderState uses the OP_STATE_RENDER and STATE_DATA macros to set 14 render states. The d3dapprs structure is the D3DAppRenderState structure, which is defined in the D3dapp.h header file.

OP_STATE_RENDER(14, lpBuffer);

STATE_DATA(D3DRENDERSTATE_SHADEMODE, d3dapprs.ShadeMode, lpBuffer);

STATE_DATA(D3DRENDERSTATE_TEXTUREPERSPECTIVE,

d3dapprs.bPerspCorrect, lpBuffer);

STATE_DATA(D3DRENDERSTATE_ZENABLE, d3dapprs.bZBufferOn &&

d3dappi.ThisDriver.bDoesZBuffer, lpBuffer);

STATE_DATA(D3DRENDERSTATE_ZWRITEENABLE, d3dapprs.bZBufferOn,

lpBuffer);

STATE_DATA(D3DRENDERSTATE_ZFUNC, D3DCMP_LESSEQUAL, lpBuffer);

STATE_DATA(D3DRENDERSTATE_TEXTUREMAG, d3dapprs.TextureFilter,

lpBuffer);

STATE_DATA(D3DRENDERSTATE_TEXTUREMIN, d3dapprs.TextureFilter,

lpBuffer);

STATE_DATA(D3DRENDERSTATE_TEXTUREMAPBLEND, d3dapprs.TextureBlend,

lpBuffer);

STATE_DATA(D3DRENDERSTATE_FILLMODE, d3dapprs.FillMode, lpBuffer);

STATE_DATA(D3DRENDERSTATE_DITHERENABLE, d3dapprs.bDithering,

lpBuffer);

STATE_DATA(D3DRENDERSTATE_SPECULARENABLE, d3dapprs.bSpecular,

lpBuffer);

STATE_DATA(D3DRENDERSTATE_ANTIALIAS, d3dapprs.bAntialiasing,

lpBuffer);

STATE_DATA(D3DRENDERSTATE_FOGENABLE, d3dapprs.bFogEnabled,

lpBuffer);

STATE_DATA(D3DRENDERSTATE_FOGCOLOR, d3dapprs.FogColor, lpBuffer);

Now the OP_STATE_RENDER and STATE_DATA macros set three light states. The OP_EXIT macro simply uses the PUTD3DINSTRUCTION macro to call the D3DOP_EXIT opcode from the D3DOPCODE enumerated type.

OP_STATE_LIGHT(3, lpBuffer);

STATE_DATA(D3DLIGHTSTATE_FOGMODE, d3dapprs.bFogEnabled ?

d3dapprs.FogMode : D3DFOG_NONE, lpBuffer);

STATE_DATA(D3DLIGHTSTATE_FOGSTART,

*(unsigned long*)&d3dapprs.FogStart, lpBuffer);

STATE_DATA(D3DLIGHTSTATE_FOGEND, *(unsigned long*)&d3dapprs.FogEnd,

lpBuffer);

OP_EXIT(lpBuffer);

Now that the render states have been set, D3DAppISetRenderState unlocks the execute buffer by calling the IDirect3DExecuteBuffer::Unlock method. It sets the execute data by calling the IDirect3DExecuteBuffer::SetExecuteData method. Finally, it begins the scene, executes the execute buffer, and ends the scene again by calling the IDirect3DDevice::BeginScene, IDirect3DDevice::Execute, and IDirect3DDevice::EndScene methods.

LastError = lpD3DExCmdBuf->lpVtbl->Unlock(lpD3DExCmdBuf);

memset(&d3dExData, 0, sizeof(D3DEXECUTEDATA));

d3dExData.dwSize = sizeof(D3DEXECUTEDATA);

d3dExData.dwInstructionOffset = (ULONG) 0;

d3dExData.dwInstructionLength = (ULONG) ((char*)lpBuffer -

(char*)lpInsStart);

lpD3DExCmdBuf->lpVtbl->SetExecuteData(lpD3DExCmdBuf, &d3dExData);

LastError =

d3dappi.lpD3DDevice->lpVtbl->BeginScene(d3dappi.lpD3DDevice);

LastError =

d3dappi.lpD3DDevice->lpVtbl->Execute(d3dappi.lpD3DDevice,

lpD3DExCmdBuf, d3dappi.lpD3DViewport);

LastError = d3dappi.lpD3DDevice->lpVtbl->EndScene(d3dappi.lpD3DDevice);

The D3DAppISetRenderState has finished its work with the execute buffer, so it calls the IDirect3DExecuteBuffer::Release method to release it, and it returns.

lpD3DExCmdBuf->lpVtbl->Release(lpD3DExCmdBuf);

return TRUE;

}