crosire / d3d8to9

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

Crash in Max Payne #43

Closed elishacloud closed 6 years ago

elishacloud commented 6 years ago

I wanted to file this issue with d3d8to9 since I have not had time to figure out the issue and hoping someone help out or give me a suggestion.

The issue is that d3d8to9 will cause Max Payne to crash. I have figure out why it is crashing and how to fix it but the fix causes issues with Haegemonia: Legions of Iron and Haegemonia: The Solon Heritage.

What is happening is that when Max Payne loads up it calls the ValidateDevice() function which returns an error. At this point Max Payne simply exits with an error. If the ValidateDevice() function is hard coded to always return D3D_OK then Max Payne runs fine. However this is obviously not a proper fix and besides that it causes display glitches in the two aforementioned Haegemonia games.

The second change I found that will allow Max Payne to run correctly is to call SetFVF() from the CreateDevice() function (see here where this was removed a while back). When SetFVF() is called here than the ValidateDevice() function correctly returns D3D_OK (even without hard coding it). However this change also causes the same display glitches in the two Haegemonia games.

Whatever I do to fix Max Payne seems to cause an issue in Haegemonia. I cannot seem to get them both to work at the same time. However I know it is possible to get both working because I tested with enbConverter and it works with both games.

Here is the error I am getting from Max Payne:

max-payne-error

Here is what the display glitch in Haegemonia looks like (notice the distortion at the bottom):

haegemonia-display-issue

Here is what it is supposed to look like:

haegemonia-good

elishacloud commented 6 years ago

I think I figured out what the issue is here. From what I can tell SetFVF() needs to be called from the CreateDevice() function (see here). This solves the issue with ValidateDevice() and Max Payne. The ValidateDevice() never works in d3d9 until you first call SetFVF().

The issue with Haegemonia: Legions of Iron has to do with SetRenderState() and D3DRS_SOFTWAREVERTEXPROCESSING. It seems that switching between software and hardware processing does not work the same in d3d9 as it did in d3d8. On this page Microsoft documented that when you call D3DRS_SOFTWAREVERTEXPROCESSING from d3d8 it will "will reset the current stream, indices, and vertex shader to their default values of NULL or 0". This does not happen with d3d9 and it seems there is no easy way to replicate this functionality with d3d9.

Modifying the code to return D3DERR_INVALIDCALL when the application calls SetRenderState() with the D3DRS_SOFTWAREVERTEXPROCESSING flag resolves the issue with Haegemonia: Legions of Iron.

elishacloud commented 6 years ago

Quick update. It seems that some games (specifically Star Wars Starfighter) does not run correctly if you return D3DERR_INVALIDCALL here. But returning D3D_OK seems to work for all games I tested with.

crosire commented 6 years ago

Sounds good. We don't really need software vertex processing on modern hardware anyway, so disabling it shouldn't cause issues.

elishacloud commented 6 years ago

Closing issue. Fix checked in.