Quake 3 weird bugs with Reshade

  • SolivaN
  • Topic Author
More
10 months 6 days ago - 10 months 6 days ago #1 by SolivaN Quake 3 weird bugs with Reshade was created by SolivaN
Hi there crosire, hope your fine men.
Well, like i said in previous post, i want to explain what happens when you use Reshade with Quake3.

1) strange 2D overlap effect while load a map
where happen: in SINGLEPLAYER or MULTIPLAYER menus
explanation: when we load a map, a strange overlap 2D layers appears
technical: may be a 2D framebuffer conflict with Reshade hook?



2) lost some gametype options
where happen: in MULTIPLAYER menu
explanation: when you tray to change GAME TYPE option, only Team Deathmatch or Capture the Flag options are available. Free for All and Tournament options are lost
technical: the same before, seems quake3 manage 2D layers for menus and options where conflict with Reshade hook?




3) infinite map loading bug
where happens: only happens in MULTIPLAYER menu, SINGLEPLAYER work fine
explanation1: select any map from MULTIPLAYER and hit FIGHT button. Quake3 will stuck on an infinite loop. You need to press ESC to stop the loop
explanation2: is possible to load a map manually, from console typing /map q3dm1 or whatever map you want
technical: again, i think quake3 can't manage correctly these 2D layers because of Reshade hook, making quake3 can't release these 2D screens?




4) cinematic playback bug
where happens: when you win a SINGLEPLAYER TIER and in CINEMATICS menu
explanation 1: when you win a SINGLEPLAYER TIER, quake3 will simply skip the cinematic
explanation 2: when you simply try to play a CINEMATIC from the menu, you can hear the video. you need to press ESC key to see the video
technical: all this is around the 2D elements of the game. Reshade seems hook some 2D screen framebuffer elements, causing quake3 have this strange behavior


hope you can help me and find this stranges problems
vanilla quake3 looks spectacular with Reshade
well, see ya and thnks for all your work!
Last edit: 10 months 6 days ago by SolivaN.

Please Log in or Create an account to join the conversation.

  • SolivaN
  • Topic Author
More
9 months 2 weeks ago #2 by SolivaN Replied by SolivaN on topic Quake 3 weird bugs with Reshade
halp? 😥

Please Log in or Create an account to join the conversation.

  • SolivaN
  • Topic Author
More
1 week 1 day ago #3 by SolivaN Replied by SolivaN on topic Quake 3 weird bugs with Reshade
Hi crosire,I finally found a very strong clue about the Quake 3 problems I reported earlier in this thread.Originally I thought some of the problems could be related to the OpenGL/framebuffer hooks because several of the symptoms looked graphical: menu layers behaving strangely, missing gametype choices, infinite loading from the Multiplayer menu, cinematics not displaying correctly, etc.However, I have now found that all of those problems, together with the mouse problems, disappear when ReShade's input hooks are disabled.I tested with:
set "RESHADE_DISABLE_INPUT_HOOK=1"
start "" "H:\Juegos\Quakes\Quake III Arena\inject32.exe" quake3.exe
With this enabled:
  • phantom/repeated mouse clicks disappear completely;
  • mouse sensitivity becomes perfectly smooth again;
  • the strange sudden sensitivity/acceleration-like jumps disappear;
  • the menu/loading/UI glitches I reported earlier disappear too.
So I now think at least some of the things that looked like graphics bugs were actually secondary effects of corrupted/incorrect input state in Quake 3's UI.Important detail: all these tests were done with my mouse at 500 Hz polling rate, not 1000/2000/8000 Hz.Quake 3 is using very old DirectInput 3The ReShade log shows:
Redirecting DirectInputCreateA(... dwVersion = 300 ...)
Redirecting IDirectInputA::CreateDevice(... GUID_SysMouse ...)
dwVersion = 300
here is
0x0300
, and the Quake 3 Win32 source confirms:
#define DIRECTINPUT_VERSION 0x0300
So vanilla Quake 3 is using the old DirectInput 3 API.The relevant Q3 code is in:
code/win32/win_input.c
code/win32/win_local.h
Quake 3's DirectInput buffering is also extremely old/smallQuake 3 defines:
#define DINPUT_BUFFERSIZE 16
and uses it directly as:
DIPROP_BUFFERSIZE = 16
More importantly,
IN_DIMouse()
drains buffered DirectInput events one object at a time:
for (;;) {
    dwElements = 1;

    hr = IDirectInputDevice_GetDeviceData(
        g_pMouse,
        sizeof(DIDEVICEOBJECTDATA),
        &od,
        &dwElements,
        0
    );
So every buffered DirectInput object requires another
GetDeviceData()
call.Buttons are handled as individual transitions:
case DIMOFS_BUTTON0:
    if (od.dwData & 0x80)
        Sys_QueEvent(... K_MOUSE1, qtrue ...);
    else
        Sys_QueEvent(... K_MOUSE1, qfalse ...);
After draining the buffered events, Q3 separately obtains relative movement with:
IDirectInputDevice_GetDeviceState(...)
mx = state.lX;
my = state.lY;
Therefore Q3 combines:
  1. a 16-element buffered event queue;
  2. one-event-per-call
    GetDeviceData()
    ;
  3. button DOWN/UP transitions from that buffer;
  4. relative mouse movement from
    GetDeviceState()
    .
This is a very fragile input path by modern standards.The interesting part on the ReShade sideLooking at current ReShade
source/windows/dinput.cpp
, when
CreateDevice
detects:
rguid == GUID_SysMouse
ReShade identifies it as
DIDEVTYPE_MOUSE
and installs hooks directly on:
IDirectInputDevice::GetDeviceState
IDirectInputDevice::GetDeviceData
So for vanilla Quake 3 the path effectively becomes:
Quake3 IN_DIMouse()
        |
        +-- GetDeviceData()
        |       |
        |       +-- ReShade hook
        |               |
        |               +-- original DirectInput
        |
        +-- GetDeviceState()
                |
                +-- ReShade hook
                        |
                        +-- original DirectInput
And because Quake 3 requests only one
DIDEVICEOBJECTDATA
per call, the ReShade
GetDeviceData
hook is crossed once for every buffered mouse object.Possible DI_BUFFEROVERFLOW interactionThere is another detail which may be important.Microsoft documents that
GetDeviceData()
can return:
DI_BUFFEROVERFLOW
when buffered input was lost.
DI_BUFFEROVERFLOW
is equal to
S_FALSE
, so it is still considered a successful HRESULT by
SUCCEEDED()
.Quake 3 only checks:
if (FAILED(hr)) {
    break;
}
Therefore:
DI_OK             -> accepted
DI_BUFFEROVERFLOW -> also accepted
Q3 does not explicitly detect that buffered input has been lost.This could be especially problematic for button events because DOWN and UP are independent buffered objects.For example, losing a button release could leave Quake 3's logical input state inconsistent and explain the phantom/repeated firing behavior.I cannot prove that buffer overflow is the exact internal cause yet, but it seems like a plausible mechanism considering:
  • Q3 has only a 16-object buffer;
  • it reads only one object per
    GetDeviceData()
    call;
  • ReShade hooks every one of those calls;
  • the symptoms completely disappear when ReShade's input hooking is bypassed.
It is also interesting that ReShade's own
GetDeviceData
wrapper already explicitly handles this case:
hr = DI_OK; /* Overwrite potential 'DI_BUFFEROVERFLOW' */
when input is being blocked.So this seems like a particularly interesting area to investigate.ReShade workaround proves the input hook is involvedThe most important result is this:
Normal ReShade input hooks:
    phantom mouse events
    unstable/sudden mouse sensitivity
    UI/menu/loading/cinematic problems

RESHADE_DISABLE_INPUT_HOOK=1:
    all of these problems disappear
The only disadvantage is that this workaround also disables the ReShade overlay input, because
RESHADE_DISABLE_INPUT_HOOK
currently disables all three together in
dll_main.cpp
:
reshade::hooks::register_module(L"user32.dll");
reshade::hooks::register_module(get_system_path() / L"dinput.dll");
reshade::hooks::register_module(get_system_path() / L"dinput8.dll");
So I cannot use the normal ReShade overlay while using the workaround.Possible ReShade-side fix / compatibility optionWould it be possible to separate the DirectInput hooks from the User32 input hooks?For example, something similar to:
RESHADE_DISABLE_DINPUT_HOOK=1
or an INI option such as:
HookDirectInput=0
which would disable only:
register_module(... dinput.dll);
register_module(... dinput8.dll);
while retaining:
register_module(L"user32.dll");
This would be a very useful compatibility fallback for old games using legacy DirectInput implementations like Quake 3.The obvious place seems to be the input hook registration block in:
source/dll_main.cpp
Currently everything is controlled by the single:
RESHADE_DISABLE_INPUT_HOOK
condition.A second possible place to investigate is:
source/windows/dinput.cpp
specifically the path:
DirectInputCreateA
    -> IDirectInputA::CreateDevice
        -> GUID_SysMouse
            -> hook GetDeviceState
            -> hook GetDeviceData
Since
DirectInputCreateA
already receives
dwVersion
, another possible compatibility safeguard could be to avoid installing the device-level DirectInput hooks for very old interfaces such as
0x0300
, or make that behavior optional.For example, conceptually:
if (dwVersion <= 0x0300 && legacy_dinput_passthrough)
{
    // Do not install GetDeviceState/GetDeviceData hooks
}
I think an opt-in switch would probably be safer than automatically changing behavior for every old DirectInput game.That would allow ReShade to leave the application's ancient DirectInput path completely untouched while still keeping the rest of ReShade operational.I am not suggesting that Quake 3's DirectInput implementation is good — it clearly is very old and fragile — but since vanilla
quake3.exe
cannot be changed by ReShade users, a selective DirectInput passthrough option in ReShade could be a useful compatibility safeguard.The key new finding is that disabling ReShade input hooks completely fixes the problem, even at only 500 Hz polling.Hopefully this gives you a much narrower place to investigate than my original report.

Please Log in or Create an account to join the conversation.

We use cookies
We use cookies on our website. Some of them are essential for the operation of the forum. You can decide for yourself whether you want to allow cookies or not. Please note that if you reject them, you may not be able to use all the functionalities of the site.