We don't mess with cursor state when focus is lost, only when the device is reset or the application re-creates it. So I'm not sure what CEF is doing here, but the behavior has been tested to work in like a gazillion games with frequent transitions from SW to HW cursors.
P.S.: Try reverting only these lines, I guess:
// Prevents the win32 cursor from being overwritten with nullptr
// in situations when a hardware cursor is set, but not shown.
if (!m_visible && !bShow)
return m_visible;
and let me know if that fixes it.
Confirmed. Reverting these lines fixes the issue in GTA:SA/SAMP with CEF overlays. It seems that during the transition, the application sends redundant hide commands that DXVK now handles differently, breaking the sync with the internal game logic
Well, from what I can see, at least from our (dxvk) point of view, everything is working as expected:
The D3D9 cursor never changes state nor is it supposed to. The previous behavior we had in place was the bug, hence the new behavior isn't a regression, it's a fix.
I see your point regarding internal consistency. From DXVK's perspective, the state indeed hasn't changed.
The problem is that we are running in a mixed environment (Wine/Proton) where DXVK doesn't own the window exclusively. External processes like the CEF overlay manipulate the underlying OS cursor state directly, bypassing D3D9.
This creates a desync:
DXVK believes m_visible = false (correct per internal logic).
The actual OS/Window Manager state is Cursor = Visible/Unconfined (changed by the overlay).
When the game regains focus, it calls ShowCursor(FALSE) attempting to re-assert control. The previous behavior allowed this call to pass through to the underlying system, effectively forcing a re-sync and fixing the cursor. The new behavior blocks it as "redundant", leaving the user with a broken cursor state that the game can no longer fix.
Since removing this "redundant" update breaks recovery from external interference (which is common with overlays), could we consider keeping the old behavior, or maybe putting this optimization behind a config flag?
The previous behavior allowed this call to pass through to the underlying system, effectively forcing a re-sync and fixing the cursor. The new behavior blocks it as "redundant", leaving the user with a broken cursor state that the game can no longer fix.
The current behavior was introduced as a fix for a D3D8 game, which mixed win32 cursor calls and D3D9 cursor calls, it wasn't put there haphazardly. As for a config option to control cursor behavior which should be managed properly by whatever overlay it is you are using? No, that won't happen.
Hmm, I get that reverting is not an option due to the D3D8 fixes. But maintaining a local patch for my game of choice is not an option for me
However, could we improve the check instead of a hard return?
Currently, DXVK assumes its internal m_visible state matches the OS state.
What if we query the actual Win32 cursor state before deciding to bail out?
Something like this:
if (!m_visible && !bShow) {
// Only optimize out if the OS agrees the cursor is actually hidden
if (IsOSCursorReallyHidden())
return m_visible;
}
This would keep your optimization for well-behaved apps but allow a fix when the states get desynced by external factors
It's not an optimization, it's how it actually is supposed to behave. D3D9 isn't supposed to be aware of the external state of the cursor, only the internal state. If anything external messes with the state behind its back, it also has the responsibility to put it back to how it was when it's done.
So what should we do? Everything works for Windows users, everything works on the old version, but what should users of the new versions of dxvk do?
I would advise you to raise a bug with whatever overlay is causing the problem. Clearly D3D9/dxvk is not causing any issues here, it is the victim of cursor state subterfuge 😉.
I'll try to get Arizona devs involved. I hope they don't send me away because it's not Windows, but I'm almost certain they will do so
Ticket on Arizona RP forum: https://forum.arizona-rp.com/threads/10562639/
I fully understand the rationale behind the guard: preventing redundant calls when the state is consistent.
However, in multi-process scenarios (like games with overlays), the Win32 cursor state can be modified externally, creating a desync where m_visible is false, but ::GetCursor() returns a valid handle (the overlay's cursor).
The current guard strictly relies on internal state:
if (!m_visible && !bShow) return m_visible;
This prevents the application from recovering from such desyncs because DXVK assumes "I hid it, so it's hidden," ignoring that an overlay might have shown it again.
Could we refine the guard to query the actual Win32 state before bailing out?
if (!m_visible && !bShow && ::GetCursor() == nullptr)
return m_visible;
This change:
I verified this build locally, and the cursor behavior is ok in my game
I fully understand the rationale behind the guard: preventing redundant calls when the state is consistent.
I don't believe you do. As I have said before, it is meant to explicitly fix situations in which games rely on an inconsistent state because they repeatedly (on every frame) hide the D3D9 cursor, but expect a system cursor to remain visible. These are albeit rare, but we have seen them in the wild.
I verified this build locally, and the cursor behavior is ok in my game
That is nice and well, and feel free to keep using it then, but your game isn't the only application in the universe, and the issue you are seeing is caused by the overlay, not by dxvk, so why are you trying to fix an external bug in our code? That is what you should ask yourself.
Nothing extracted yet.
Please describe your issue as accurately as possible. Include screenshots or videos when relevant.
Cursor gets stuck when open phone(CEF) -> open any textdraw -> close textdraw
https://github.com/user-attachments/assets/ffa79ba3-574a-4cba-851f-96ba869140a1
I figured out this bug is happening because of 66ac77a79b0cf03fa021dd8080de138ed7a140c6
The issue is likely caused by the new m_hCursor == nullptr check in d3d9_cursor.cpp, which returns early and prevents the cursor state from being restored when the focus returns to the game from the Chromium process
Software information
Arizona RP(GTA SA:MP): lastest
System information
Apitrace file(s)
Recording an apitrace is difficult due to the Arizona Launcher's integrity checks and multi-process nature, but the regression is 100% reproducible by toggling the mentioned commit.
Log files
Arizona Games Launcher_dxgi.log
Arizona Games Launcher_d3d11.log
gta_sa_d3d9.log
gta_sa_dxgi.log
cef_process_dxgi.log