narzoul / DDrawCompat

DirectDraw and Direct3D 1-7 compatibility, performance and visual enhancements for Windows Vista, 7, 8, 10 and 11
BSD Zero Clause License
878 stars 67 forks source link

Existing Patcher which Hooks into RegisterClassA fails because of DDrawCompatPresentationWindow/DDrawCompatMessageWindow #328

Open H-K-47 opened 2 weeks ago

H-K-47 commented 2 weeks ago

I have a DirectX7 Game that mixes ddraw with GDI.

The game uses a custom launcher which works fine, in conjunction with DDrawCompat in fact it works so well it fixes most performance issues through the GDI/GetDC / Surface handling which even dgvodoo2 could not fix till today.

I have 2 issues:

  1. The first is - that Version 0.5.2 does not work anymore for me. While 0.5.1 0.5 0.4 work fine. The only difference i see is the SurfacePatches parameter. The problem is after the game start a BMP is shown, after some loading in the background 3D graphics in conjunction with GDI Text is usually shown. But in 0.5.2 the BMP stays there, i can here sound in the background from the usual UI elements so the game seems to be working its just not showing it. Input also works fine, since i can blindly select an item and the expected things happens.

  2. DDrawCompat does some funky stuff with the window/message handler function. The customer launcher i have hooks into the original game windows proc and catches event for key presses. However this does no longer work since the original games window class/window is never registered. Example: Before:

Hook_RegisterClassA lpszClassName _thegamewindowclass

After:

Hook_RegisterClassA lpszClassName DDrawCompatPresentationWindow Hook_RegisterClassA lpszClassName DDrawCompatMessageWindow

I tried to hook into either DDrawCompatPresentationWindow or DDrawCompatMessageWindow but then the loading does not continue.

How can i hook into the RegisterClassA function? The original game window still gets created with CreateWindowExA but that doesnt help me, seems like there are 2 more windows that no do the job for the original games window/message handler.

Thank you for this wonderful library!

narzoul commented 2 weeks ago

The first is - that Version 0.5.2 does not work anymore for me. While 0.5.1 0.5 0.4 work fine. The only difference i see is the SurfacePatches parameter. The problem is after the game start a BMP is shown, after some loading in the background 3D graphics in conjunction with GDI Text is usually shown. But in 0.5.2 the BMP stays there, i can here sound in the background from the usual UI elements so the game seems to be working its just not showing it. Input also works fine, since i can blindly select an item and the expected things happens.

It's hard to say since you haven't given any means for me to reproduce the issue, but assuming this unnamed game runs in windowed mode, it's probably fixable by this: https://github.com/narzoul/DDrawCompat/issues/298#issuecomment-2094362559

DDrawCompat does some funky stuff with the window/message handler function. The customer launcher i have hooks into the original game windows proc and catches event for key presses. However this does no longer work since the original games window class/window is never registered. Example: Before:

Hook_RegisterClassA lpszClassName _thegamewindowclass

After:

Hook_RegisterClassA lpszClassName DDrawCompatPresentationWindow Hook_RegisterClassA lpszClassName DDrawCompatMessageWindow

DDrawCompat also hooks into RegisterClassA, and redirects it to RegisterClassExA. I don't know how you hook it currently, maybe you just need to hook RegisterClassExA too.

H-K-47 commented 1 week ago

Thank you for your reply

  1. I can confirm that the patch you provided in the other issue works for me. Yes my custom launcher provides already windowed functionality and the game runs in windowed mode.

  2. For the second issue, I now tried to hook RegisterClassExA instead of just RegisterClassA and indeed I see the original game window/msg here but something else is going on I haven't figured out because I cant reach the window hwd or the msg function correctly.

Curiously, what is the reason for mapping it to the Ex class in Icon.cpp?

ATOM WINAPI registerClassA(const WNDCLASSA* lpWndClass)
    {
        LOG_FUNC("RegisterClassA", lpWndClass);
        return LOG_RESULT(registerClass(lpWndClass, CALL_ORIG_FUNC(RegisterClassExA)));
    }

    ATOM WINAPI registerClassW(const WNDCLASSW* lpWndClass)
    {
        LOG_FUNC("RegisterClassW", lpWndClass);
        return LOG_RESULT(registerClass(lpWndClass, CALL_ORIG_FUNC(RegisterClassExW)));
    }

Wouldn't it be better to keep the original behaviour for other patchers/launchers. I guess you have a stronger reason for changing ClassA to ClassExA and ClassW to ClassExW?

  1. I actually don't require all the custom DDrawCompatMessageWindow and DDrawCompatMessageWindow windows/handles. Is this something that is required outside of making a fullscreen game windowed? Otherwise could there be an option to not add these wrapper windows around the game?

  2. I looked into the GDI code, how did you achieve such a remarkable speedup (win7 being the last that did GDI correctly in hardware) - less BLT? Because I still see callings to the original function CALL_ORIG_FUNC(ExtTextOutW) ....

The game writes a lot of HUD/UI elements using TextOut etc from GDI and that's what slowing it down, without a HUD the game runs perfectly. Before attempting this - would it be feasible that I just try to port out the GDI text out enhancements somehow? Or is this all required along with the additional window/msg handler you are creating?

Again thanks for this super useful lib!

narzoul commented 1 week ago

Curiously, what is the reason for mapping it to the Ex class in Icon.cpp?

Because the class small icon can only be overridden in the Ex version. It was needed to fix some corrupt icon rendering issue, I don't remember which one exactly.

Wouldn't it be better to keep the original behaviour for other patchers/launchers.

It's simply impossible to keep compatibility with every 3rd party hook out there, which is why the readme explicitly mentions that using DDrawCompat in combination with other hooks is not supported.

I actually don't require all the custom DDrawCompatMessageWindow and DDrawCompatMessageWindow windows/handles. Is this something that is required outside of making a fullscreen game windowed?

What do you mean? DDrawCompat doesn't even have a function to make fullscreen games windowed. The message window is used for the GUI thread and its overlays. The presentation windows are for providing proper presentation for windowed games/apps that mix GDI and DirectDraw.

I looked into the GDI code, how did you achieve such a remarkable speedup (win7 being the last that did GDI correctly in hardware) - less BLT? Because I still see callings to the original function CALL_ORIG_FUNC(ExtTextOutW) ....

I don't know without seeing the exact case, but it has nothing to do with the ExtTextOut wrapper, which is just used to fix some issues with the GDI interworking in DDrawCompat. My best guess is your game uses IDirectDrawSurface::GetDC to obtain a DC to draw on multiple times per frame. Since each such use requires locking the surface for CPU access, the performance overhead could be significant. DDrawCompat caches a CPU side copy of video memory surfaces, so repeated locks have no extra overhead unless the GPU also writes to the surface in between. This is implemented in the D3dDdi::Resource class.