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

[Knight Rider 2] abnormal program termination #53

Closed ThirteenAG closed 1 year ago

ThirteenAG commented 5 years ago

Log consists of only 1 line:

Loading dinput8.dll

elishacloud commented 5 years ago

Yeah, I need to add more logging. I made this pretty quick and tested it only with a few games. I suspect what is happening is that a structure is getting overrun. The size of some of the structures are larger in DirectInput8 than they are in older versions. I will look it into this. Thanks!

elishacloud commented 5 years ago

@ThirteenAG, I pulled the release because I found some serious issue that I missed in my testing. There is still an issue with the ref count, but I think it should work on Knight Rider 2.

Try this build and let me know what the results are: dinput.zip

ThirteenAG commented 5 years ago

It works now. By the way, do you know anything about why this game runs with window border even in fullscreen mode? Changing window styles sometimes produces interesting results: image So it should be possible to hide the border, but I feel like the root of the problem is not window styles related.

elishacloud commented 5 years ago

It works now.

Ok, great. Glad it worked.

By the way, do you know anything about why this game runs with window border even in fullscreen mode?

I took a quick look at the game and it appears that the game is calling the correct SetCooperativeLevel with the DDSCL_FULLSCREEN flag set, so it is telling ddraw to go to fullscreen.

However Windows 10 (and Windows 8) seem to be picky about this. If the window style is not correct than it will not go into fullscreen mode for some reason.

In my wrapper if I add the code sample below than the game seems to go into fullscreen fine and I did not see any ill side effects. Here is a copy of the wrapper: ddraw.zip

HRESULT m_IDirectDrawX::SetCooperativeLevel(HWND hWnd, DWORD dwFlags)
{
    if (IsWindow(hWnd) && ((dwFlags & DDSCL_FULLSCREEN) || (dwFlags & DDSCL_EXCLUSIVE)))
    {
        // Remove window border
        SetWindowLong(hWnd, GWL_STYLE, GetWindowLong(hWnd, GWL_STYLE) & ~WS_CAPTION);
        SetWindowPos(hWnd, nullptr, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
    }

    return ProxyInterface->SetCooperativeLevel(hWnd, dwFlags);
}
elishacloud commented 5 years ago

So it should be possible to hide the border, but I feel like the root of the problem is not window styles related.

There may be another root cause, but I am not aware of it. This seems to be an issue with Windows 8.0 and above. The general solution that is given is to enable the ForceSimpleWindow compatibility option. See: here, here, and here for some examples.

Actually you only need to remove the WS_CAPTION window style from the window, which I think is the same thing that ForceSimpleWindow does. In programming terms, a "simple window" is a window without a border, see here for an example.

Note: I have seen the same issue with Direct3D8. All versions of DirectX older than 9.0 have this issue. But it seems that Direct3D9 and newer does not have this issue.

ThirteenAG commented 5 years ago

Actually you only need to remove the WS_CAPTION window style from the window

I hooked CreateWindowExA and AdjustWindowRect, doesn't seem to work. But this worked for both windowed and fullscreen:

HWND WINAPI CreateWindowExAHook(DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName, DWORD dwStyle, int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam)
{
    auto hWnd = CreateWindowExA(dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam);
    SetWindowLong(hWnd, GWL_STYLE, dwStyle & ~WS_OVERLAPPEDWINDOW);
    SetWindowPos(hWnd, nullptr, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
    return hWnd;
}

BOOL WINAPI AdjustWindowRectHook(LPRECT lpRect, DWORD dwStyle, BOOL bMenu)
{
    return AdjustWindowRect(lpRect, dwStyle & ~WS_OVERLAPPEDWINDOW, bMenu);
}
elishacloud commented 5 years ago

Interesting. I wonder if there is a difference between setting the window style at creation time verse setting it when the game is requesting for a fullscreen in DirectX.

Is there some reason that you don't just change the creation values rather than calling SetWindowLong?

Wouldn't this be more efficent?

HWND WINAPI CreateWindowExAHook(DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName, DWORD dwStyle, int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam)
{
    return CreateWindowExA(dwExStyle, lpClassName, lpWindowName, dwStyle & ~WS_OVERLAPPEDWINDOW, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam);
}

BOOL WINAPI AdjustWindowRectHook(LPRECT lpRect, DWORD dwStyle, BOOL bMenu)
{
    return AdjustWindowRect(lpRect, dwStyle & ~WS_OVERLAPPEDWINDOW, bMenu);
}
ThirteenAG commented 5 years ago

My thought was exactly, but for some reason it doesn't work. My current code: vs what you suggested:

Also without hooking AdjustWindowRectHook white border appears: (at the bottom)

elishacloud commented 5 years ago

Can you check out the ddraw wrapper below and let me know if this works for you or not? This wrapper only removes the WS_CAPTION window style. I am just wondering if this solution only works on my computer.

Here is the wrapper: ddraw.zip

ThirteenAG commented 5 years ago

It works for me, yes.

ThirteenAG commented 5 years ago

@elishacloud I have another unrelated question, do you know anything about amstream.dll? I couldn't find anything to rescale these mpg videos for widescreen fix and I don't want to leave them as is. There's a function at 6C28E0, it's definitely related to video player, but all calls are going into amstream.dll, where I assume the video just gets stretched to the window area. Any idea how to rescale it?

elishacloud commented 5 years ago

I have not used amstream.dll before and I did not have much time to look into it, but it looks like it is part of DirectShow.

As far as I can tell it requires a call to DllGetClassObject to load it, which usually creates an IClassFactory interface to load it.

ThirteenAG commented 5 years ago

@elishacloud I was also checking out KR1 game, ddraw wrapper seem to crash that game. Do you want me to open another issue for it?

elishacloud commented 5 years ago

No need to open another issue. I will take a look at it when I get a chance. Can you confirm the issue is with ddraw wrapper and not the dinputto8 wrapper?

ThirteenAG commented 5 years ago

Yes, ddraw crashes.

UCyborg commented 5 years ago

Microsoft's idea was to implement fullscreen mode with advantages of both exclusive fullscreen mode and borderless windowed mode. This works fine in Direct3D 9; pressing Alt + Prt Scr while in fullscreen mode creates the screenshot of the game in the clipboard and if the game allows free movement of the mouse pointer outside of the screen, it's possible to invoke Aero Peek and see the desktop on which the fullscreen game is running, assuming the main taskbar is on another monitor and Aero Peek is enabled. Neither of these work in classic fullscreen. Also the game still has full control of vertical synchronization; DWM never forces its own on top. Game Bar can only be used in windowed/borderless windowed/this new fullscreen mode, so that's probably another reason for implemented changes. Notifications can also be displayed on top of the game when it's running in this new fullscreen mode, which isn't possible in classic fullscreen mode. Oddly, D3D11 stuff still seems to run in classic fullscreen mode.

This new fullscreen mode (also called maximized windowed mode or fullscreen optimizations by Microsoft guys) is not completely implemented for D3D8 and older, yet it's still the default. That's why window borders can be seen in fullscreen and gamma can't be changed. DWM is still performing composition in a similar manner as it happens in windowed mode. Some games change their window style in a way so the borders don't appear under any circumstances, but messing with window styles isn't needed for Direct3D games; picking what gives the desired appearance in windowed mode at window creation time is sufficient. Changing window styles doesn't accomplish what we want in this case on Windows 8.x/10, even if it appears that it does with the naked eye.

There are ways to revert to classic fullscreen. For D3D8, read this thread and this issue. For DirectDraw, DXPrimaryEmulation shim with -DisableMaxWindowedMode parameter can be applied using Compatibility Administrator, which is part of Windows 10 ADK (select Application Compatibility Tools on the feature selection screen). It's similar for Windows 8.x; download the appropriate ADK and make sure to select the right component on feature selection screen, just its name is a bit different. There should be instructions on how to use it somewhere online, but I don't recall where I found them last time. DxWnd is also capable of enabling these options, need to enable expert mode from its menu and then enable Disable Maximized Windowed Mode checkbox in Compat. tab in the application profile settings. Some DirectDraw wrappers also offer this option.

Issue with D3D8 is specific to Windows 10. Newer builds however fix visible borders and inability to change gamma. It appears to behave like borderless windowed mode that some games offer.

DirectDraw issue applies to Windows 8/8.1/10. There are some OS specific quirks. Both Windows 8 and 8.1 have performance problems unless maximized windowed mode is disabled. Windows 10 fixes performance problems in the new mode, but inability to change gamma and visible window borders remain as well as VSync forced by DWM remain. There's also AllowMaximizedWindowGamma shim available in Compatibility Administrator which only restores ability to tweak gamma while in the maximized windowed mode. On Windows 8.1, there's a problem with disabling the new mode when using multiple monitors (extended mode); all screens blink, see this issue. On Windows 10 with certain graphics drivers, disabling it may cause game to freeze when trying to switch away from the game or quitting it; just opening CTRL + ALT + DEL screen helps it go forward. There are probably multiple factors that contribute to occurrence of the issue, but I've only ever seen it with NVIDIA's drivers. Interestingly, enabling Fast Sync can also prevent the issue from occurring.

elishacloud commented 5 years ago

@ThirteenAG, I have been looking into the crash on Knight Rider. It does seem to be an issue with my wrapper, but have not figure out the issue yet. I'll let you know when I get a fix for this.

@UCyborg, thanks for the great detail! It is true that if I set DisableMaxWindowedMode = 1 then Knight Rider is able to run in fullscreen without a border. Note: using dxwrapper you can set all the AppCompat features, see here. This means I could have fixed the border issue without any code changes.

Changing window styles doesn't accomplish what we want in this case on Windows 8.x/10, even if it appears that it does with the naked eye.

The problem is hardcoded setting in system d3d8.dll that activates so called maximized windowed mode, which causes the game to lose exclusive access to the screen and so games lose the ability to set gamma, makes them crash when attempting to use anti-aliasing and it leaves desktop compositing active, degrading performance.

Are you sure these issues are the same on ddraw? Or, is it possible this was fixed in the latest version of Windows 10? I have not seen any negative issues using the following code to change the window style:

HRESULT m_IDirectDrawX::SetCooperativeLevel(HWND hWnd, DWORD dwFlags)
{
    HRESULT hr = ProxyInterface->SetCooperativeLevel(hWnd, dwFlags);
    if (SUCCEEDED(hr) && (dwFlags & DDSCL_FULLSCREEN) && IsWindow(hWnd))
    {
        LONG lStyle = GetWindowLong(hWnd, GWL_STYLE);
        if (lStyle & WS_CAPTION)
        {
            SetWindowLong(hWnd, GWL_STYLE, lStyle & ~WS_CAPTION);
            SetWindowPos(hWnd, NULL, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_DEFERERASE | SWP_NOSENDCHANGING | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOREDRAW | SWP_FRAMECHANGED);
        }
    }

    return hr;
}

I also ran a few tests and I was able to set gamma on ddraw whether or not DisableMaxWindowedMode was enabled. Also I don't see any issues with anti-aliasing.

My understanding is that setting DisableMaxWindowedMode causes other side effects, like breaking the game mode in Windows 10. Unless there is an issue I prefer my fix since I cannot see any negative side effects and it is a generic fix that works on all ddraw games.

UCyborg commented 5 years ago

@elishacloud With D3D8, inability to change gamma and visible borders were fixed at some point, The issue with anti-aliasing also doesn't occur anymore. I remember back then when I tried enabling anti-aliasing in Mafia, it crashed in one of NVIDIA's driver DLLs. Disabling maximized windowed mode by changing the variable in d3d8.dll fixed it. Today, without changing the variable, games still practically run in borderless windowed mode. You can notice input latency when moving the mouse, which isn't the case with modified variable (assuming the game in question keeps VSync off). This particular issue still applies to both DirectDraw and Direct3D 8.

It's strange; you said you've seen problem with visible borders with D3D8, which shouldn't be a problem anymore on any Windows 10 build released this year.

I have not seen any negative issues using the following code to change the window style:

The code doesn't cause issues, it's just that on Windows 7 and older, when DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN is passed to SetCooperativeLevel method, you always got real exclusive fullscreen mode instead of fake fullscreen mode, as you've already noticed. Window borders aren't shown regardless of set window style and Desktop Window Manager doesn't interfere (no extra input latency). This changed with Windows 8.

I also ran a few tests and I was able to set gamma on ddraw whether or not DisableMaxWindowedMode was enabled.

Are you saying that IDirectDrawGammaControl::SetGammaRamp is effective on your end without DisableMaxWindowedMode tweak? On my end, it isn't. Currently on Windows 10 1803 (Build 17134). Not the latest, so could be the reason, though I doubt Microsoft still messes with DirectDraw.

My understanding is that setting DisableMaxWindowedMode causes other side effects, like breaking the game mode in Windows 10.

The Game Mode or Game bar? Game bar definitely needs this new fullscreen mode to be visible. Also not sure if it can be made to work in D3D11 games. On my end, it doesn't show in old games wrapped through dgVoodoo 2 and neither in Unigine Heaven 4.0 unless I run them in windowed mode. I don't have anything else installed ATM that uses D3D11.

Game bar keyboard shortcut (Win + G) works practically for almost any windowed application. I can press it while I'm using the web browser and it asks if this is the game.

elishacloud commented 5 years ago

@ThirteenAG, I believe I fixed the issue. The fix is checked in here: 3fd1fea.

Here is an updated wrapper, let me know if you have any other issues: ddraw.zip

I have not fixed it in my DirectX-Wrappers project, but I will do that later.

@UCyborg,

You can notice input latency when moving the mouse, which isn't the case with modified variable (assuming the game in question keeps VSync off).

Can you explain more about this issue?

The code doesn't cause issues, it's just that on Windows 7 and older, when DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN is passed to SetCooperativeLevel method, you always got real exclusive fullscreen mode instead of fake fullscreen mode, as you've already noticed.

Yes, I know this is not needed for Windows 7. But the primary goal of my dxwrapper project is to fix Windows 10 issues.

Are you saying that IDirectDrawGammaControl::SetGammaRamp is effective on your end without DisableMaxWindowedMode tweak?

Yes. I just tested and I am getting DD_OK return value when I call both IDirectDrawGammaControl::GetGammaRamp and IDirectDrawGammaControl::SetGammaRamp.

I am running:

Microsoft Windows [Version 10.0.17763.55]

Here is my code:

IDirectDrawGammaControl *gammaInterface = nullptr;
p_IDirectDrawSurface->QueryInterface(IID_IDirectDrawGammaControl, (void**)&gammaInterface);
if (gammaInterface)
{
    DDGAMMARAMP gmRamp;
    HRESULT hr = gammaInterface->GetGammaRamp(0, &gmRamp);
    if (SUCCEEDED(hr))
    {
        Logging::Log() << "GetGammaRamp succeded!";
    }
    hr = gammaInterface->SetGammaRamp(0, &gmRamp);
    if (SUCCEEDED(hr))
    {
        Logging::Log() << "SetGammaRamp succeded!";
    }
}

_Note: this code assumes that p_IDirectDrawSurface is a valid pointer to an IDirectDrawSurface class._

Below is the log I am getting:

17820 22:03:57.641 DxWrapper loaded!
17820 22:03:59.036 GetGammaRamp succeded!
17820 22:03:59.037 SetGammaRamp succeded!
17820 22:04:13.533 Quiting DxWrapper
UCyborg commented 5 years ago

You can notice input latency when moving the mouse, which isn't the case with modified variable (assuming the game in question keeps VSync off).

Can you explain more about this issue?

It's the same thing that happens when any game is run in windowed mode; Desktop Window Manager that's responsible for rendering the desktop is applying its own vertical synchronization, causing input lag.

Another side effect in DirectDraw without disabling maximized windowed mode is that game's rendering rate is always capped by monitor's refresh rate, which doesn't happen in normal windowed mode. It should only occur in normal fullscreen mode if DDFLIP_NOVSYNC is not specified in the call to Flip method of IDirectDrawSurface interface.

You can try 3DMark 99, which you can download from here. After installing it, also apply my patch, which solves error at startup and some other issues. Without DisableMaxWindowedMode, it'll only run at 60 FPS on 60 Hz monitor. Note that the unpatched 3DMark99 doesn't disable vertical synchronization so it only runs as fast as monitor's refresh rate allows, regardless of whether it runs on Windows 10 or on Windows 7 and earlier. Patched version should run as fast as the hardware allows, unless it's capped by Windows 8.x/10 maximized windowed mode.

Another thing worth mentioning, while you may actually want to run some game with vertical synchronization, it's still good to disable maximized windowed mode because when vertical synchronization is forced by Desktop Window Manager, it's impossible to minimize input lag by limiting the game's frame rate in a fashion described here. The key is the difference between vertical synchronization forced by Desktop Window Manager and the one that can be enabled when running in true fullscreen mode.

Anyway, DDrawCompat has maximized windowed mode disabled by default because that makes DirectDraw behave in a way that is expected, otherwise, behavior differs from the API specification.

Yes. I just tested and I am getting DD_OK return value when I call both IDirectDrawGammaControl::GetGammaRamp and IDirectDrawGammaControl::SetGammaRamp.

They return DD_OK here too, but nothing changes on the screen after the call to IDirectDrawGammaControl::SetGammaRamp unless maximized windowed mode is disabled.

ThirteenAG commented 5 years ago

Here is an updated wrapper, let me know if you have any other issues

Launches now, can confirm.

elishacloud commented 5 years ago

@ThirteenAG, the issue was that I was caching the addresses of the ddraw interfaces in a shared pool. In some cases Knight Rider would release the IDirectDraw class without releasing the IDirectSurface class. Then it would create a new IDirectDraw class at the exact same address as the IDirectSurface class was at. My wrapper would then use the cached address, meaning I would be wrapping the IDirectDraw class with an IDirectSurface wrapper. This caused a crash in one other game, that until now I was unable to fix.

@UCyborg, yes, I have seen the input lag you mention on a couple of ddraw games. I did not know it was related to DWM and vsync.

Another side effect in DirectDraw without disabling maximized windowed mode is that game's rendering rate is always capped by monitor's refresh rate, which doesn't happen in normal windowed mode.

Rendering more often than the monitors refresh rate typically means two things: 1. screen tearing, and 2 some frames are lost, since the screen cannot display them.

it's still good to disable maximized windowed mode because when vertical synchronization is forced by Desktop Window Manager, it's impossible to minimize input lag by limiting the game's frame rate

Are you sure about this? I created a quick function in my wrapper to auto skip frames based on timing and where the ScanLine was. After spending sometime tweaking it I can get the input to be basically as smooth as if vsync is off. Actually I am just skipping frames that would have not been seen anyways (see above).

They return DD_OK here too, but nothing changes on the screen after the call to IDirectDrawGammaControl::SetGammaRamp unless maximized windowed mode is disabled.

Oh, interesting. I did not check if it actually works. But it would be quite easy to convert these to Windows DWM APIs: SetDeviceGammaRamp. That is what WineD3D does.

ThirteenAG commented 5 years ago

@elishacloud Yeah, I want to find some time to test more, but here's another, well, unrelated thing. I bundled widescreen fix with dgvoodoo because I have a lot of flickering textures in these games and with 8x AA both games look great. I think dgvoodoo helps with flickering textures, didn't really test with wrapper yet. Problem is, dgvoodoo breaks some stuff like fading and cutscene borders, crashes in KR1 after loading, so if I were to replace it with something else, I would like to keep forced antialiasing option. Do you think it's something you can add in the wrapper?

elishacloud commented 5 years ago

dgVoodoo2 works by converting ddraw applications to Direct3D11. Unfortunately it is closed source. dgVoodoo1 is open, but won't help us here.

In my wrapper I am working toward converting from ddraw to Direct3D9. I have gone a long ways in the last 6 months or so. I have implemented about 70% of IDirectDraw and IDirectSurface interfaces. However I have not implemented any of the 3D interfaces.

The wrapper works in 3 modes:

  1. Wrapper-only mode with just a few fixes in it. In this mode it is basically just directly passes through the APIs. This is the mode I gave you because it should be close to 100% compatible.
  2. Conversion to DirectX7. In this mode I convert all the API's from v1-6 to v7. This has about 80% of the APIs implemented and will work for most games.
  3. Conversion to Direct3D9. Discussed above.

ddraw has pretty poor support for AA. The best bet for this is to convert the games to Direct3D9 and then use some tool like SweetFX, injectFXAA or ReShade. I already have this working with about 10% of my ddraw games.

Ultimately I would like to build a wrapper that could replace dgVoodoo, but I am still quite a ways off. What dgVoodoo does is pretty amazing.

What exactly are you looking for here?

ThirteenAG commented 5 years ago

I only included dgvoodoo for: windowed mode, flickering textures, flickering window itself, ability to force AA. But because it breaks things in game, probably better to include something more stable I thought.

elishacloud commented 5 years ago

Ok, let me see what I can do. Can you help me reproduce the flickering textures and the flickering window issues?

ThirteenAG commented 5 years ago

Okay, let's see. First, for windowed mode I tried to use wndmode.ini/dll, it worked but I had window flickering: Even using sharex menu made it flicker, so it's a no go. I think game's native windowed mode has the same problem. Fixed with dgvoodoo. Also I believe it's not a problem in KR2.

For some reason I can't reproduce flickering textures, with or without dgvoodoo. I had this sort of Z-fighting effect on many surfaces when I first launched KR2, but I don't see it anymore.

AA you could probably force with a driver, but I always prefer a portable approach. knight rider 2018-11-01 11-06-21-17 knight rider 2018-11-01 11-05-31-66

elishacloud commented 1 year ago

Closing this issue for now. If there is anymore to do here let me know.