Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[dxgi] Delay qualifying foreground loss as occlusion #4297

Merged
merged 1 commit into from
Sep 27, 2024

Conversation

gofman
Copy link
Contributor

@gofman gofman commented Sep 27, 2024

That fixes a regression with Yakuza 0 introduced by commit ed9ffa6 (after which commit the game hangs with black screen on start).

There are actually two manifestations of essentially the same problem.

  1. The initial hang on start happens after the game sets fullscreen mode through dxgi and then calls GetFullscreenState which kicks off fullscreen state and retuns no fullscreen status. This is unexpected, game seems to refuse to do anything after this. What happens here is that the game didn't make the window visible yet before SetFullscreenState, it is only becoming visible in the course of configuring fullscreen mode (here on Windows WS_VISIBLE attributed is still not set while window is fullscreen and visible, but this is unrelated and unfixable in dxvk in isolation). The process of window gaining focus has some delay. Window manager ultimately controls the focus, so Wine doesn't make a window foreground for WINAPI until WM sends corresponding X events. That doesn't happen immediately and the delay may be quite big until the window is fully mapped, displayed and only then we get a focus event and mark the window as foreground. So GetFullscreenState called right after SetFullscreenState sees that the window is not foreground and kicks off fullscreen mode. If solving just initial fullscreen setting (by checking that our window ever had focus at all before concuding that it is occluded) fixes the hang on start, but then at any alt tab the same happens: after kicking off fullscreen the game seems very unhappy and refuses to do any presentation or window management.
  2. How the game is supposed to work, is that upon receiving WM_KILLFOCUS it sets windowed mode itself and minimizes the window. WM_KILLFOCUS is apparently supposed to arrive before GetFullscreenState() will kick off fullscreen mode. I think here plays the role that using GetForegroundWindow for detecting "occlusion" status is a workaround, some delays for focusing events may also affect this sequence. I doubt it is possible to fully correctly solve without having Windows-like WSI and window management not intergating host's native WM.

The added delay to react on foreground loss should hopefully solve the issues with transition process in p. 2. A separate check for yet-nevet-foreground window is still needed for p. 1., ~100ms may be by far not enough for that process to settle at list in this case when the window is getting mapped during fullscreening.

@gofman
Copy link
Contributor Author

gofman commented Sep 27, 2024

From the failed build log it seems to me the failure is not related to the patch:

ERROR: failed to authorize: failed to fetch oauth token: unexpected status from GET request to https://auth.docker.io/token?scope=repository%3Alibrary%2Farchlinux%3Apull&service=registry.docker.io: 401 Unauthorized

@WinterSnowfall
Copy link
Contributor

From the failed build log it seems to me the failure is not related to the patch

GitHub Actions are just like that sometimes: https://github.com/doitsujin/dxvk/actions/runs/11039230566/job/30664242387 ... if you force-push a nop it will usually go through just fine.

@doitsujin
Copy link
Owner

doitsujin commented Sep 27, 2024

Overall idea alright to me, but is the check really something we want to do in the WSI backend rather than the DXGI swap chain itself? I feel like doing this in the swap chain would give us better control on when to reset the timer and also make it more obvious what's going on.

We have high_resolution_clock (in util_time.h) as a portable way to measure time if that's a concern, use high_resolution_clock::time_point to store time stamps and then the comparison can be something like if (high_resolution_clock::now() - m_timestamp > std::chrono::milliseconds(100)). This maps to QPC on Windows and to std::high_resolution_clock is on Linux builds.

@gofman
Copy link
Contributor Author

gofman commented Sep 27, 2024

The reason I placed it into WSI is that logically that part is supposed to tell whether this is occluded or not (taking into account Win32 state, potentially in the future performing some Win specific interaction). I think this timeout logically belongs there. Here the timer is reset on start of a certain Win32 operation, backend should be aware of anything happening to window config. I'd suggest to keep the Win / Wine specifc 'isOccluded' logic in the backend while the logic related to swapchain config may be in the swapchain part.

But if you feel strong to do that another way no problem for me moving it to the swapchain part of course.

@gofman
Copy link
Contributor Author

gofman commented Sep 27, 2024

(in other words, if sdl2 backend would decide to implement isOcclusion that delay logic on top of that in swapchain code would likely make no sense for it)

@doitsujin doitsujin merged commit a172cab into doitsujin:master Sep 27, 2024
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants