Fix invisible cursor for games using D3D9 software cursors#132
Fix invisible cursor for games using D3D9 software cursors#132Night1099 wants to merge 4 commits intoNVIDIAGameWorks:mainfrom
Conversation
Games that use software cursors (D3D9 sprites) never call SetCursorProperties, leaving m_hCursor as nullptr. When the game calls IDirect3DDevice9::ShowCursor(TRUE), the previous code passed nullptr to ::SetCursor, actively hiding the OS cursor. Only call ::SetCursor(m_hCursor) when a hardware cursor has been configured. This allows the OS cursor to remain visible for games whose software cursor sprite is not rendered by the path tracer.
Games that draw their cursor as a D3D9 sprite call SetCursor(NULL) to hide the OS cursor. Under Remix the path tracer drops the sprite, leaving no visible cursor at all. Add a Detours hook on SetCursor in the bridge client (alongside the existing SetCursorPos/GetCursorPos hooks). When the game's D3D9 ShowCursor state is TRUE (menu/UI mode) and SetCursor is called with NULL, substitute the default arrow cursor so the OS cursor stays visible. During gameplay (ShowCursor FALSE) the NULL is passed through so mouse-look works normally. Controlled by client.forceSoftwareCursorVisibility (default: true).
When the game provides a cursor bitmap via SetCursorProperties, build an HCURSOR on the bridge client side from the surface pixel data. The SetCursor hook uses this cursor instead of the generic arrow, preserving the game's intended cursor appearance under Remix.
|
While this WAR may be useful, I wonder why it's necessary. Doesnt DXVK already handle this for us? If not, it should, and would potentially be more robust than adding in another hook (generally we want to support the minimal number of hooks as possible). |
|
Well upstream dxvk does handle it, the bug is introduced by the bridge when DXVK's D3D9Cursor::ShowCursor calls ::SetCursor(), it happens in the same process as the game, so it "wins" over whatever the game does. The server-side fix in d3d9_cursor.cpp is the DXVK-level fix — it prevents ShowCursor(TRUE) from nulling the cursor when no hardware cursor exists. However, games using software cursors also call ::SetCursor(NULL) directly (bypassing D3D9), and because of the bridge architecture those calls happen in the client process where DXVK's server-side code can't intercept them. The SetCursor hook in di_hook.cpp covers that path. The existing di_hook infrastructure already hooks other cursor functions (GetCursorPos, SetCursorPos), so the SetCursor hook follows the same pattern. |
Summary:
Games that draw their cursor as a D3D9 sprite and call
SetCursor(NULL)to hide the OS cursorend up with no visible cursor under Remix — the path tracer drops the sprite, and the OS cursor
is actively hidden.
Changes:
d3d9_cursor.cpp):ShowCursor(TRUE)no longer calls::SetCursor(nullptr)whenm_hCursoris NULL (game never calledSetCursorProperties), allowing the OS cursor to remain visibledi_hook.cpp): HooksSetCursoralongside existingSetCursorPos/GetCursorPoshooks. When
SetCursor(NULL)is called while the game's D3D9ShowCursorstate is TRUE (menu/UI),substitutes a fallback cursor. During gameplay (
ShowCursor FALSE), NULL passes through for mouse-lookd3d9_device.cpp): When the game callsSetCursorPropertieswith a cursor bitmap,creates an HCURSOR on the client side so the hook uses the game's actual cursor icon instead of a
generic arrow
Configuration:
client.forceSoftwareCursorVisibility(default:true) — set tofalseto disableTested:
SetCursorProperties(hardware cursor) unaffectedclient.forceSoftwareCursorVisibility = falsedisables the behavior