elishacloud / dxwrapper

Fixes compatibility issues with older games running on Windows 10/11 by wrapping DirectX dlls. Also allows loading custom libraries with the file extension .asi into game processes.
zlib License
1.17k stars 83 forks source link

D3D9 reading internal fields of resources? #27

Closed CookiePLMonster closed 5 years ago

CookiePLMonster commented 5 years ago

Notice this is not about dxwrapper at all, more about a weird discovery I just had with a D3D9 wrapper (which might affect yours too).

Take a look at this crash:

image

And this code it crashed at:

image

Why has this never been a problem? Looks like D3D9 reads an internal field of this D3D9 vertex buffer here!

CookiePLMonster commented 5 years ago

Of course, passing a "real" interface to SetStreamSource instead of a wrapper helped - but is it me just being dumb and forgetting something or something wrappers didn't account for so far?

EDIT: Looks like your wrapper is aware of that quirk... are there any other methods which behave like that?

elishacloud commented 5 years ago

I am not exactly sure which quirk you are asking about. I have found several different issues with wrapping DirectX APIs. I was hoping to write up a wiki to discuss each of them in more detail, but never got around to it.

The crash happens at that location, but the issue could have been a ways up the stack. The problem with wrappers is that if you don't wrap everything correctly then it is possible for the process to get the actual d3d9 interface rather than your wrapper interface. This can cause all kinds of issues. In one game I saw a case where the real interface was exposed at the beginning of the game but it did not crash until you left the game or started a new match, which could be hours later.

Actually in d3d9 I believe you have a chance of crashing, depending on what the game does, if you don't wrap every interface. I believe every interface in d3d9 has at least one function where you need to swap the address with the wrapper address.

For the VertexBuffer there is function called GetDevice. For Textures there is a function called GetSurfaceLevel. Etc...

I am not sure if this is useful or not. Let me know if you need any specific data. You are always welcome to use my wrappers. I spent a good amount of time trying to make sure they can fully wrap all interfaces. It is strictly a wrapper with no additional code in it.

CookiePLMonster commented 5 years ago

What I haven't noticed at the moment of creating this issue is what in any function passing resources to the device (like SetStreamSource) you are passing underlying resources instead of the wrapper itself. On the second thought it makes sense, since D3D has a right to internally assume those resources are "final" and thus refer to them directly and not via virtual methods.

Example: https://github.com/elishacloud/dxwrapper/blob/master/d3d9/IDirect3DDevice9.cpp#L576

elishacloud commented 5 years ago

Yes, you need to pass the real resource to D3D and the wrapper interface to the game process. If you check out my DirectX-Wrappers project and look at the d3d9 wrapper you can see which functions I pass either the real or the wrapper interface.

If the function just has a return ProxyInterface->(function_name) then you don't need to do anything to that function. Example: https://github.com/elishacloud/DirectX-Wrappers/blob/master/d3d9/IDirect3D9Ex.cpp#L57

For other functions there will be additional code to either do one of these three things:

  1. Lookup the wrapper address.
  2. Create a new wrapper interface.
  3. Lookup the resource address.

I should also mention that when I lookup the wrapper address if it does not exist then I create a new interface.

CookiePLMonster commented 5 years ago

Cool! So it was purely an oversight on my part and not some thing everyone forgot about... good to know!