- Status
- Offline
- Joined
- Mar 3, 2026
- Messages
- 447
- Reaction score
- 7
Trying to build a custom DirectX 9 library and hitting a wall with anything that isn't a basic line? It's a classic roadblock when moving from standalone D3D9 apps to internal hooks in games like Counter-Strike: Source or Left 4 Dead 2. If ID3DXLines work but your vertex buffers are ghosting, you're likely fighting the game's internal state machine.
The Problematic Implementation
The logic below follows a standard flow: creating a vertex buffer, locking it to copy data, and attempting a D3DPT_TRIANGLESTRIP draw. While this works in a vacuum, it often fails inside an EndScene hook because the game engine has already configured the device states for its own frame rendering.
Technical Breakdown & Troubleshooting
If CreateVertexBuffer isn't failing but nothing shows up on screen, check these common D3D9 hook pitfalls:
Has anyone else dealt with specific render state conflicts in the Source Engine's D3D9 implementation?
The Problematic Implementation
The logic below follows a standard flow: creating a vertex buffer, locking it to copy data, and attempting a D3DPT_TRIANGLESTRIP draw. While this works in a vacuum, it often fails inside an EndScene hook because the game engine has already configured the device states for its own frame rendering.
Code:
if (!Valid()) { return; }
if (!d3ddev) { return; }
LPDIRECT3DVERTEXBUFFER9 vBuffer;
_VTX OurVertices[] =
{
{ 0, 0, 0, 1.0f, D3DCOLOR_ARGB(255,255,0,0)},
{ 640, 0, 0, 1.0f, D3DCOLOR_ARGB(255,0,255,0) },
{ 0, 480, 0, 1.0f, D3DCOLOR_ARGB(255,0,0,255) },
{ 640, 480, 0, 1.0f, D3DCOLOR_ARGB(255,127,127,127) }
};
d3ddev->CreateVertexBuffer(4 * sizeof(_VTX), 0, CUSTOMFVF, D3DPOOL_MANAGED, &vBuffer, NULL);
VOID* pVoid;
vBuffer->Lock(0, 0, (void**)&pVoid, 0);
memcpy(pVoid, OurVertices, sizeof(OurVertices));
vBuffer->Unlock();
d3ddev->SetFVF(CUSTOMFVF);
d3ddev->SetStreamSource(0, vBuffer, 0, sizeof(_VTX));
d3ddev->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
vBuffer->Release();
Technical Breakdown & Troubleshooting
If CreateVertexBuffer isn't failing but nothing shows up on screen, check these common D3D9 hook pitfalls:
- State Soup: Source Engine games mess with the RenderState heavily. You need to disable lighting (D3DRS_LIGHTING), culling (D3DRS_CULLMODE), and potentially the Z-buffer (D3DRS_ZENABLE) before your draw call. Better yet, use a IDirect3DStateBlock9 to capture and restore the device state.
- FVF Mismatch: For screen-space coordinates (2D), your vertex structure must include the RHW (Reciprocal Homogeneous W) component. Ensure your CUSTOMFVF is defined with D3DFVF_XYZRHW.
- Texture States: The game might have a texture bound to Stage 0. If you aren't using a texture for your rectangle, you might need to call SetTexture(0, NULL) to prevent the device from trying to sample garbage data.
- Vertex Buffer Overhead: Creating and releasing a vertex buffer every single frame inside a hook is a massive performance killer. Allocate a static buffer once or use DrawPrimitiveUP for simple dynamic geometry.
When hooking EndScene, your code runs before the final buffer swap.
Since it runs in a standard D3D9 app, the logic is sound, but the environment is hostile.
Code:
Sudevice = pDevice;
sudsy::Render(); // Calls the draw logic above
return oEndScene(pDevice);
Has anyone else dealt with specific render state conflicts in the Source Engine's D3D9 implementation?