Skip to content

perf(d3d): enable PUREDEVICE and remove unused mixed vertex processing#2438

Closed
githubawn wants to merge 1 commit intoTheSuperHackers:mainfrom
githubawn:performance/d3d-driver-overhead
Closed

perf(d3d): enable PUREDEVICE and remove unused mixed vertex processing#2438
githubawn wants to merge 1 commit intoTheSuperHackers:mainfrom
githubawn:performance/d3d-driver-overhead

Conversation

@githubawn
Copy link

This PR optimizes the D3D8 renderer by enabling Pure Device and switching from Mixed Vertex Processing to a hardware-only path. These changes reduce CPU overhead and improve frame stability, particularly in translation layers.

@githubawn githubawn changed the title performance(d3d): enable PUREDEVICE and remove unused mixed vertex processing perf(d3d): enable PUREDEVICE and remove unused mixed vertex processing Mar 11, 2026
@greptile-apps
Copy link

greptile-apps bot commented Mar 11, 2026

Greptile Summary

This PR optimizes the D3D8 renderer for both Generals and Zero Hour by enabling D3DCREATE_PUREDEVICE (skipping per-draw-call state validation) and replacing mixed vertex processing with hardware-only vertex processing. To support pure-device mode — which prohibits reading device state via GetRenderState/GetTexture etc. — all such direct reads are replaced with calls into DX8Wrapper's internal render-state cache, and the cache is now pre-seeded with correct D3D defaults for D3DRS_COLORWRITEENABLE and D3DRS_CULLMODE on invalidation.

Key changes:

  • dx8wrapper.cpp (Generals + GeneralsMD): Switches Vertex_Processing_Behavior from D3DCREATE_MIXED_VERTEXPROCESSING to D3DCREATE_HARDWARE_VERTEXPROCESSING; enables D3DCREATE_PUREDEVICE when D3DDEVCAPS_PUREDEVICE is advertised. Seeds cache defaults for the two render states read-back in downstream code.
  • W3DShaderManager.cpp: Replaces every direct _Get_D3D_Device8()->SetTexture() and ->SetPixelShader() call with the caching wrappers DX8Wrapper::Set_DX8_Texture / DX8Wrapper::Set_Pixel_Shader; also removes an unused hr variable.
  • W3DScene.cpp + W3DVolumetricShadow.cpp (both Generals/GeneralsMD): Replaces _Get_D3D_Device8()->GetRenderState(D3DRS_COLORWRITEENABLE, &old) with the cache-read DX8Wrapper::Get_DX8_Render_State(D3DRS_COLORWRITEENABLE).
  • W3DWater.cpp: Replaces GetRenderState/SetRenderState for D3DRS_CULLMODE with the cached equivalents, but leaves several direct ->SetPixelShader() and ->SetTexture() calls in the water shader paths unaddressed — see inline comment for details.

Confidence Score: 3/5

  • Core PUREDEVICE enablement is correct, but W3DWater.cpp retains direct SetPixelShader/SetTexture calls that bypass the wrapper cache and can leave the device in the wrong shader state.
  • The dx8wrapper.cpp changes are well-implemented: hardware-only vertex processing, PUREDEVICE gated on caps, and correct D3D default seeds. The fixes in W3DShaderManager, W3DScene, and W3DVolumetricShadow are complete and mechanical. However, W3DWater.cpp still contains numerous direct _Get_D3D_Device8()->SetPixelShader() and ->SetTexture() calls (at least 8 call sites) that the PR does not touch. With PUREDEVICE active, these bypass calls can desynchronise the Pixel_Shader cache from the device's actual state, causing subsequent cached Set_Pixel_Shader() calls to incorrectly elide their D3D submission and leave the wrong shader bound. This was the exact condition the original KJM comment was guarding against before enabling pure-device mode.
  • Core/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWater.cpp requires the remaining direct SetPixelShader/SetTexture calls to be migrated to the DX8Wrapper cached wrappers.

Important Files Changed

Filename Overview
GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp Core PUREDEVICE enablement: switches vertex processing from MIXED to HARDWARE, enables D3DCREATE_PUREDEVICE when caps allow, and seeds the render state cache with correct D3D defaults for D3DRS_COLORWRITEENABLE and D3DRS_CULLMODE. Minor ternary continuation indentation inconsistency.
Generals/Code/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp Same PUREDEVICE enablement as the GeneralsMD counterpart, with the same ternary continuation indentation issue.
Core/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWater.cpp Migrates D3DRS_CULLMODE save/restore to the cached wrapper API, but leaves numerous direct _Get_D3D_Device8()->SetPixelShader() and SetTexture() calls unchanged. These bypass the DX8Wrapper cache and create state-coherency hazards now that PUREDEVICE is active and GetPixelShader/GetTexture can no longer be used to resync.
Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DShaderManager.cpp Comprehensive replacement of all direct _Get_D3D_Device8()->SetTexture() and SetPixelShader() calls with their DX8Wrapper cached equivalents. Also removes an unused hr variable. Changes are mechanical and correct.
Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DScene.cpp Replaces direct GetRenderState(D3DRS_COLORWRITEENABLE) call with Get_DX8_Render_State(), correctly using the seeded cache value instead of querying the pure device.
GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DScene.cpp Same D3DRS_COLORWRITEENABLE save fix as Generals counterpart.
Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/Shadow/W3DVolumetricShadow.cpp Replaces direct GetRenderState(D3DRS_COLORWRITEENABLE) with the cached wrapper, consistent with PUREDEVICE requirements.
GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Shadow/W3DVolumetricShadow.cpp Same D3DRS_COLORWRITEENABLE fix as Generals counterpart.

Sequence Diagram

sequenceDiagram
    participant App
    participant DX8Wrapper
    participant D3DDevice

    Note over DX8Wrapper: Invalidate_Cached_Render_States()<br/>Seeds COLORWRITEENABLE=0xF<br/>Seeds CULLMODE=D3DCULL_CCW

    App->>DX8Wrapper: Create_Device()
    DX8Wrapper->>D3DDevice: CreateDevice(HARDWARE_VP | PUREDEVICE)
    D3DDevice-->>DX8Wrapper: IDirect3DDevice8*

    Note over App,D3DDevice: Rendering – PUREDEVICE path

    App->>DX8Wrapper: Get_DX8_Render_State(CULLMODE)
    DX8Wrapper-->>App: returns RenderStates[CULLMODE] (cache only)

    App->>DX8Wrapper: Set_DX8_Render_State(CULLMODE, NONE)
    DX8Wrapper->>D3DDevice: SetRenderState(CULLMODE, NONE)

    App->>DX8Wrapper: Set_DX8_Texture(stage, tex)
    DX8Wrapper->>D3DDevice: SetTexture(stage, tex)

    App->>DX8Wrapper: Set_Pixel_Shader(shader)
    DX8Wrapper->>D3DDevice: SetPixelShader(shader)

    App->>DX8Wrapper: Set_DX8_Render_State(CULLMODE, saved)
    DX8Wrapper->>D3DDevice: SetRenderState(CULLMODE, saved)

    Note over App,D3DDevice: ⚠️ W3DWater.cpp bypasses wrapper
    App->>D3DDevice: _Get_D3D_Device8()->SetPixelShader(0)
    Note over DX8Wrapper: Pixel_Shader cache now stale!
Loading

Comments Outside Diff (1)

  1. Core/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWater.cpp, line 2892 (link)

    Direct SetPixelShader calls stale the wrapper cache

    With D3DCREATE_PUREDEVICE now enabled by this PR, the DX8Wrapper's internal Pixel_Shader cache is the sole authoritative source for the current pixel shader state (since GetPixelShader on a pure device is prohibited and returns garbage). However, this line (and others below at lines 2904 and 3300, and the drawTrapezoidWater path at lines 265, 2431, 3022) call SetPixelShader directly on the device, bypassing the DX8Wrapper::Set_Pixel_Shader wrapper and its cache.

    This creates a concrete cache-coherency hazard:

    1. The wrapper has previously called Set_Pixel_Shader(X), so Pixel_Shader == X.
    2. This line directly sets the device to m_riverWaterPixelShader, then line 2904 sets it to 0 — both without updating the cache.
    3. At some later point, Set_Pixel_Shader(X) is called again; because the cache already records X, the wrapper elides the SetPixelShader(X) call to the device — but the device is actually at 0. The wrong shader remains active.

    The original code comment (// enable this when all 'get' dx calls are removed KJM) specifically gated the pure-device path on eliminating all state-read calls. Now that PUREDEVICE is live, these stale-write calls in W3DWater.cpp should be migrated to DX8Wrapper::Set_Pixel_Shader and DX8Wrapper::Set_DX8_Texture to keep the cache consistent.

Last reviewed commit: 1fbb3c0

@githubawn githubawn force-pushed the performance/d3d-driver-overhead branch 2 times, most recently from 5d1f618 to b75c140 Compare March 11, 2026 04:08
@githubawn githubawn force-pushed the performance/d3d-driver-overhead branch from b75c140 to 1fbb3c0 Compare March 11, 2026 04:49
@github-actions
Copy link

⚠️ Title/Commit Validation Failed

Invalid PR title:

  • perf(d3d): enable PUREDEVICE and remove unused mixed vertex processing

Invalid commit messages:

  • perf(d3d): enable PUREDEVICE and hardware vertex processing
    PR titles and commit messages must follow conventional commits format:
type: Description
type(scope): Description

Allowed types: bugfix, build, chore, ci, docs, fix, feat, perf, refactor, revert, style, test, tweak, unify

See CONTRIBUTING.md for details.

@githubawn githubawn marked this pull request as draft March 11, 2026 17:42
@githubawn
Copy link
Author

bigger and more changes than i expected, closing draft for now

@githubawn githubawn closed this Mar 11, 2026
@Mauller
Copy link

Mauller commented Mar 12, 2026

It might be easier to look at enabling Hardware vertex shading before trying a pure device mode, you have to be more careful when using a pure device in how you handle the hardware.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants