crosire / d3d8to9

A D3D8 pseudo-driver which converts API calls and bytecode shaders to equivalent D3D9 ones.
BSD 2-Clause "Simplified" License
875 stars 77 forks source link

Fix vsync, oPos register and RenderState #108

Closed elishacloud closed 4 years ago

elishacloud commented 4 years ago

This pull request fixes #49 This pull request fixes #63 This pull request fixes #86 This pull request fixes #103 This pull also fixes AquaNox, which never worked before with d3d8to9.

There are three main updates in this pull:

  1. Fixes present parameters to match the documentation.

This will properly set the vsync based on whether the game is usin windowed or fullscreen. This also removes the code that forced D3DMULTISAMPLE_NONE, as this broke some AA functions.

  1. Fixes oPos register swizzles in VertexShader

AquaNox uses almost 100 VirtexShaders. Several of them would set only on the x and y components of the pPos register, which is not allowed in DirectX9. See sample below.

Before fix (see error):

> Dumping translated shader assembly:

    vs_1_1
    dcl_position v0
    dcl_blendweight v1
    dcl_blendindices v2
    dcl_normal v3
    dcl_psize v4
    mov r0, c0 /* initialize register r0 */
    mov oT1, c0 /* initialize output register oT1 */
    mov oT0, c0 /* initialize output register oT0 */
    mul r0.xy, v3, c23
    add oPos.xy, r0, c22      //  <--  Error is here
    mov oT0.xy, v3
    mul r0.xy, v3, c25
    add oT1.xy, r0, c24

// approximately 5 instruction slots used

> Failed to reassemble shader:

d:\games\aquanox\memory(17,1): error X5350: Vertex shader must minimally write all four components (xyzw) of oPos output register.  Missing components(*): x/0 y/1 *z/2 *w/3

After fix:

> Dumping translated shader assembly:

    vs_1_1
    dcl_position v0
    dcl_blendweight v1
    dcl_blendindices v2
    dcl_normal v3
    dcl_psize v4
    mov r0, c0 /* initialize register r0 */
    mov oT1, c0 /* initialize output register oT1 */
    mov oT0, c0 /* initialize output register oT0 */
    mul r0.xy, v3, c23
    add oPos, r0, c22 /* removed oPos swizzles */
    mov oT0.xy, v3
    mul r0.xy, v3, c25
    add oT1.xy, r0, c24

// approximately 5 instruction slots used
  1. Changes how SetRenderState/GetRenderState works with the following States: D3DRS_LINEPATTERN, D3DRS_EDGEANTIALIAS and D3DRS_PATCHSEGMENTS

These states were removed in DirectX9. For D3DRS_LINEPATTERN and D3DRS_PATCHSEGMENTS this changes from returning D3DERR_INVALIDCALL to returning D3D_OK. It also changes D3DRS_EDGEANTIALIAS to use D3DRS_ANTIALIASEDLINEENABLE instead.

These changes are required for AquaNox because AquaNox would monitor the return call and fail out if anything other than D3D_OK is returned.

crosire commented 4 years ago

Awesome work!

CookiePLMonster commented 1 month ago

This makes GTA III and Vice City not use vsync when running in windowed mode (forced by another mod), even though games should request vsync. Is this intended?

EDIT: I should have checked the code before asking: // Windowed mode should always present immediately

That doesn't match my experience though, PRESENT_ONE always worked fine in windowed. Where is this change coming from?

EDIT2: OK I see that it's just DX8's behaviour... That is not optimal to be fair, but I can understand the reasoning. It makes using windowed mode in GTA quite annoying though.

elishacloud commented 1 month ago

@CookiePLMonster, this may be the behavior of Windows when using Direct3D9. Native Direct3D8 it uses "Composed: Copy with GPU GDI", but with d3d8to9 it uses "Hardware Composed: Independent Flip". These modes handle vsync (and GDI) differently. I found out if you call Direct3D9SetSwapEffectUpgradeShim(0) it will switch the Direct3D9 mode back.

See this thread for more details.

CookiePLMonster commented 1 month ago

I found out if you call Direct3D9SetSwapEffectUpgradeShim(0) it will switch the Direct3D9 mode back.

Would that be worth doing for d3d8to9 then, or is forced-no-vsync the behaviour you desire in this scenario?

elishacloud commented 1 month ago

I found out if you call Direct3D9SetSwapEffectUpgradeShim(0) it will switch the Direct3D9 mode back.

Would that be worth doing for d3d8to9 then, or is forced-no-vsync the behaviour you desire in this scenario?

See comments here to answer that question.