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.16k stars 83 forks source link

Dd7to9 zbuffer support with Dungeon Keeper 2 #65

Closed Trass3r closed 1 year ago

Trass3r commented 4 years ago

Tested on Dungeon Keeper 2:

``` Starting DxWrapper v1.0.6370.21 Disabling High DPI Scaling... Loaded library: user32.dll Loaded library: shcore.dll Loading 'ddraw.dll'... Hooking ddraw.dll APIs... Enabling ddraw wrapper Enabling d3d9 wrapper Loading 'd3d9.dll'... DxWrapper loaded! DxWrapperSettings Called! dd_DirectDrawCreate dd_DirectDrawCreateEx Redirecting 'DirectDrawCreate' IID_IDirectDraw to --> 'Direct3DCreate9' Creating device m_IDirectDrawX::m_IDirectDrawX(009BE9B8) converting device from v1 to v9 d9_Direct3DCreate9 Redirecting 'Direct3DCreate9' ... Creating device m_IDirect3D9Ex::m_IDirect3D9Ex(009EA910) m_IDirectDrawX::QueryInterface (009BE9B8) m_IDirectDrawX::AddRef (009BE9B8) m_IDirectDrawX::Release (009BE9B8) m_IDirectDrawX::SetCooperativeLevel (009BE9B8) m_IDirectDrawX::CreateSurface2 (009BE9B8) Creating device m_IDirectDrawSurfaceX::m_IDirectDrawSurfaceX(00A560D8) converting device from v4 to v9 m_IDirectDrawX::QueryInterface (009BE9B8) Creating device m_IDirect3DX::m_IDirect3DX(00A3F988) converting device from v3 to v9 m_IDirectDrawX::CreateSurface2 (009BE9B8) Creating device m_IDirectDrawSurfaceX::m_IDirectDrawSurfaceX(00A56278) converting device from v4 to v9 m_IDirectDrawSurfaceX::Blt (00A560D8) m_IDirectDrawSurfaceX::ColorFill (00A560D8) m_IDirect3D9Ex::GetDeviceCaps (009EA910) m_IDirectDrawX::CreateD3D9Device wnd: WND(00061326,_BullfrogLibScreen,{0,0,1920,1080}) D3d9 Device params: {1920,1080,0,1,0,0,1,null,1,1,75,0x0,0,0x80000000} flags: 68 m_IDirect3D9Ex::CreateDevice (009EA910) UpdatePresentParameter Setting WndProc: WND(00061326,_BullfrogLibScreen,{0,0,1920,1080}) Creating device m_IDirect3DDevice9Ex::InitDirect3DDevice(009BFF68) m_IDirectDrawSurfaceX::CreateD3d9Surface D3d9 Surface size: 1920x1080 Format: 22 m_IDirect3DDevice9Ex::CreateTexture (009BFF68) Creating device m_IDirect3DTexture9::m_IDirect3DTexture9(00A92248) m_IDirect3DDevice9Ex::CreateTexture Succeeded m_IDirect3DDevice9Ex::CreateTexture (009BFF68) Creating device m_IDirect3DTexture9::m_IDirect3DTexture9(00A92748) m_IDirect3DDevice9Ex::CreateTexture Succeeded m_IDirect3DDevice9Ex::SetVertexShader (009BFF68) m_IDirect3DDevice9Ex::SetFVF (009BFF68) m_IDirect3DDevice9Ex::CreateVertexBuffer (009BFF68) Creating device m_IDirect3DVertexBuffer9::m_IDirect3DVertexBuffer9(00A92648) m_IDirect3DDevice9Ex::SetStreamSource (009BFF68) m_IDirect3DDevice9Ex::SetRenderState (009BFF68) m_IDirect3DDevice9Ex::SetSamplerState (009BFF68) m_IDirect3DVertexBuffer9::Lock (00A92648) m_IDirectDrawSurfaceX::CreateD3d9Surface D3d9 Vertex size: 1920x1080 pad: 0x0 m_IDirect3DVertexBuffer9::Unlock (00A92648) m_IDirect3DTexture9::LockRect (00A92248) m_IDirect3DTexture9::UnlockRect (00A92248) m_IDirectDrawSurfaceX::PresentSurface Skipping scene! m_IDirect3DDevice9Ex::UpdateTexture (009BFF68) m_IDirect3DTexture9::GetType (00A92248) m_IDirect3DTexture9::GetType (00A92748) m_IDirect3DDevice9Ex::SetTexture (009BFF68) m_IDirect3DTexture9::GetType (00A92748) m_IDirectDrawX::EndScene (009BE9B8) m_IDirectDrawX::BeginScene (009BE9B8) m_IDirect3DDevice9Ex::BeginScene (009BFF68) m_IDirect3DDevice9Ex::DrawPrimitive (009BFF68) m_IDirect3DDevice9Ex::EndScene (009BFF68) m_IDirect3DDevice9Ex::Present (009BFF68) m_IDirectDrawX::BeginScene (009BE9B8) m_IDirect3DDevice9Ex::BeginScene (009BFF68) m_IDirectDrawX::CreateSurface2 (009BE9B8) Creating device m_IDirectDrawSurfaceX::m_IDirectDrawSurfaceX(0A8230C8) converting device from v4 to v9 m_IDirectDrawX::CreateSurface2 (009BE9B8) Creating device m_IDirectDrawSurfaceX::m_IDirectDrawSurfaceX(0B562C00) converting device from v4 to v9 m_IDirectDrawSurfaceX::GetSurfaceDesc2 (0B562C00) m_IDirectDrawSurfaceX::AddRef (0B562C00) m_IDirectDrawX::CreateSurface2 (009BE9B8) m_IDirectDrawX::CreateSurface2 Error: non-supported ddsCaps! 8192 0 0 Creating device m_IDirectDrawSurfaceX::m_IDirectDrawSurfaceX(0B562DA0) converting device from v4 to v9 m_IDirectDrawX::CreateSurface2 (009BE9B8) m_IDirectDrawX::CreateSurface2 Error: zbuffer not Implemented. m_IDirectDrawX::CreateSurface2 (009BE9B8) m_IDirectDrawX::CreateSurface2 Error: zbuffer not Implemented. m_IDirectDrawX::CreateSurface2 (009BE9B8) m_IDirectDrawX::CreateSurface2 Error: zbuffer not Implemented. m_IDirectDrawSurfaceX::AddAttachedSurface (0B562DA0) Quiting DxWrapper Unloading libraries... Reseting screen resolution Reseting font smoothing DxWrapper terminated! ```
elishacloud commented 4 years ago

zbuffer is used for Direct3D. Some games add the zbuffer flag but don't use it. However, I looked into Dungeon Keeper 2 in the past and it does require the Direct3D APIs. I plan to support it in the future, but am working to fix the remaining issue with DirectDraw (2D) APIs first. I do plan to get this game working with Dd7to9 but just have not had time yet.

Trass3r commented 4 years ago

Yep it does use Direct3D 3 for rendering. Though just as a rasterizer. All vertices are pre-transformed.

mirh commented 4 years ago

There's a good list of games with zbuffer problems here.

Trass3r commented 3 years ago

@elishacloud Actually this already works when patching it just a bit:

```diff diff --git a/ddraw/IDirectDrawX.cpp b/ddraw/IDirectDrawX.cpp index 51e8de5..061f9df 100644 --- a/ddraw/IDirectDrawX.cpp +++ b/ddraw/IDirectDrawX.cpp @@ -423,15 +423,18 @@ HRESULT m_IDirectDrawX::CreateSurface2(LPDDSURFACEDESC2 lpDDSurfaceDesc2, LPDIRE // Check for MipMap if ((lpDDSurfaceDesc2->dwFlags & DDSD_MIPMAPCOUNT) || (lpDDSurfaceDesc2->ddsCaps.dwCaps & DDSCAPS_MIPMAP)) { - LOG_LIMIT(100, __FUNCTION__ << " Error: MipMap not Implemented."); - return DDERR_NOMIPMAPHW; + if (lpDDSurfaceDesc2->dwMipMapCount > 1) + { + LOG_LIMIT(100, __FUNCTION__ << " Error: MipMap not Implemented."); + return DDERR_NOMIPMAPHW; + } } // Check for zbuffer if (((lpDDSurfaceDesc2->dwFlags & DDSD_PIXELFORMAT) && (lpDDSurfaceDesc2->ddpfPixelFormat.dwFlags & DDPF_ZBUFFER)) || (lpDDSurfaceDesc2->ddsCaps.dwCaps & DDSCAPS_ZBUFFER)) { - LOG_LIMIT(100, __FUNCTION__ << " Error: zbuffer not Implemented."); - return DDERR_NOZBUFFERHW; + LOG_LIMIT(100, __FUNCTION__ << " Warning: zbuffer not Implemented."); +// return DDERR_NOZBUFFERHW; } // Check for alpha ```

Edit: fixed in ea12a2232

Trass3r commented 3 years ago

With the latest version it doesn't work. First of all D3DFMT_D24X8 seems to mean dwZBitMask = 0xFFFFFF (ref). When patching dxwrapper as follows it shows the loading screen but crashes when it should switch to the game.

diff --git a/ddraw/IDirectDrawTypes.cpp b/ddraw/IDirectDrawTypes.cpp
index b49b171..805c70c 100644
--- a/ddraw/IDirectDrawTypes.cpp
+++ b/ddraw/IDirectDrawTypes.cpp
@@ -605,9 +605,13 @@ D3DFORMAT GetDisplayFormat(DDPIXELFORMAT ddpfPixelFormat)
        {
            return D3DFMT_D16;
        }
-       if (ddpfPixelFormat.dwFlags == DDPF_ZBUFFER && ddpfPixelFormat.dwZBufferBitDepth == 32 && ddpfPixelFormat.dwZBitMask == 0xFFFFFF00)
+       if (ddpfPixelFormat.dwFlags == DDPF_ZBUFFER && ddpfPixelFormat.dwZBufferBitDepth == 32)
        {
-           return D3DFMT_D24X8;
+           if (ddpfPixelFormat.dwZBitMask == 0xFFFFFF)
+               return D3DFMT_D24X8;
+
+           if (ddpfPixelFormat.dwZBitMask == 0xFFFFFFFF)
+               return D3DFMT_D32;
        }
        if (ddpfPixelFormat.dwFlags == (DDPF_ZBUFFER | DDPF_STENCILBUFFER) && ddpfPixelFormat.dwZBufferBitDepth == 32 && ddpfPixelFormat.dwStencilBitDepth == 8 &&
            ddpfPixelFormat.dwZBitMask == 0xFFFFFF00 && ddpfPixelFormat.dwStencilBitMask == 0xFF)
@@ -724,7 +728,11 @@ void SetPixelDisplayFormat(D3DFORMAT Format, DDPIXELFORMAT &ddpfPixelFormat)
        break;
    case D3DFMT_D24X8:
        ddpfPixelFormat.dwFlags = DDPF_ZBUFFER;
-       ddpfPixelFormat.dwZBitMask = 0xFFFFFF00;
+       ddpfPixelFormat.dwZBitMask = 0xFFFFFF;
+       break;
+   case D3DFMT_D32:
+       ddpfPixelFormat.dwFlags = DDPF_ZBUFFER;
+       ddpfPixelFormat.dwZBitMask = 0xFFFFFFFF;
        break;
    case D3DFMT_D24S8:
        ddpfPixelFormat.dwFlags = DDPF_ZBUFFER | DDPF_STENCILBUFFER;
Trass3r commented 2 years ago

D3DFMT_D24X8 seems to mean dwZBitMask = 0xFFFFFF

Actually it can be both:

https://github.com/wine-mirror/wine/blob/f9f4ee1b5882067b1900a67201c376f0a1abe3ea/dlls/ddraw/utils.c#L514..L515

And 32bit zbuffer handling is missing in more places like https://github.com/elishacloud/dxwrapper/blob/6be80570acfcfd2b6f8e0aec1ed250dd96f5be5c/ddraw/IDirect3DX.cpp#L751

elishacloud commented 1 year ago

@Trass3r, thanks for your help here. I think the zBuffer issues should be addressed now.

Trass3r commented 1 year ago

I'll need to check again, I recently fixed something locally about it. It was falling back to 16bits zbuffer cause of some wrong mask iirc. But otherwise they seem to be fixed. The game still crashes later due to the Lock issue in the followup ticket though.

elishacloud commented 1 year ago

@Trass3r, thanks for all your comments here! I have cleaned up the code and added support for all the different types of zbuffers. This last check-in should be good. Let me know if you see anything.

Trass3r commented 1 year ago

Thanks! Looks good in general but formats with the depth at the end like D3DFMT_X8D24 don't seem to be used much nor supported. d3d9Object->CheckDeviceFormat fails for D3DFMT_X8D24 on my Intel driver while D3DFMT_D24X8 works. Wine always seems to use D3DFMT_D24X8 despite the confusingly different WINED3DFMT_X8D24: https://github.com/search?q=repo%3Awine-mirror%2Fwine+D3DFMT_X8D24&type=code

elishacloud commented 1 year ago

Looks good in general but formats with the depth at the end like D3DFMT_X8D24 don't seem to be used much nor supported.

Interesting. Ok, I cleaned up the code a bit more. You can see the new check-in here: a3e9f495aee71c8bcddde74849f17da18c074528

elishacloud commented 1 year ago

I guess what confused me is that the zbuffer seems backwards compared to all the other formats.

For example: For D3DFMT_R8G8B8 the dwRBitMask is 0xFF0000 because it is listed first. For D3DFMT_V8U8 the dwBumpDvBitMask is 0xFF00 because it is listed first. For D3DFMT_A8L8 the dwLuminanceAlphaBitMask is 0xFF00 because it is listed first.

Therefore, for D3DFMT_D24S8 the dwZBitMask should be 0xFFFFFF00 but it seems not to work that way. For some reason Wine shows it the other way.

I don't understand why the zbuffer is backwards. If you have any ideas why this is backwards I would be interested to hear it.

Trass3r commented 1 year ago

Yeah who knows. 3D stuff came later. And the z buffer is special anyway, normally not even directly accessible.

Trass3r commented 1 year ago

https://learn.microsoft.com/en-us/windows/win32/direct3d9/d3dformat#buffer-formats

indicate no particular bit ordering per pixel

elishacloud commented 1 year ago

I see. Thanks for the info and follow-up!