PKr Posted February 14, 2022 Does mouse movement work properly in any SDL2 source port when v-sync is turned on on Windows 10+ for anyone? Initially I have posted about the issue here on page 68: On 1/29/2022 at 3:20 AM, PKr said: Hi bradharding! I have an issue with Doom Retro. It lags whenever I use mouse to look around. Everything works silky smooth when using arrow keys to turn around. Doom Retro also works perfectly fine with an xinput controller. But as soon as I try to play with the mouse, the game stutters terribly/lags (it looks like frameskipping, but it's not). At first I thought I had some framerate/v-sync related issues. I have tried different gpu settings and vid_cvars, but it didn't improve the issue. After playing a few minutes with an xinput controller however I noticed that the problem was gone completely (sadly I don't play with controllers, so using a controller instead of kb+m is not a fix for me 😢). Then I tried arrow keys to turn around, and once again Doom Retro worked perfectly fine. I also use GZDoom, and Crispy Doom and mouse works correctly for me in both source ports. I have tried changing mouse polling rate to lower values (125hz from 1000hz) but it didn't improve anything. Could you please look into that problem? ...but here is the deal. It actually does the same thing in every SDL2 source I use. Even in Crispy Doom (it's just that v-sync was turned off, so I thought the stuttering was absent there). Just to reiterate: 1. If v-sync is turned off and fps is uncapped (500-700fps), camera movement is perfect when using no matter what - mouse, arrow keys, controller. 2. If v-sync is turned on and fps is at max. refresh rate, camera movement is perfect when using arrow keys and controller, but is extremely choppy when using mouse. 3. If v-sync is turned on and fps is below max. refresh rate, camera movement is choppy no matter the input method. Now this is usually a normal behavior, except I have a g-sync monitor, so that shouldn't be the case. So, basically, I can't use v-sync whatsoever if I want to use mouse to turn around. That applies to at least Crispy Doom, Doom Retro, Eternity Engine and PrBoom+ (in case of PrBoom+ it is really choppy no matter what unless OpenGl is used). Not sure about Chocolate Doom since fps is so low, that I can't tell whether it's just low fps or stuttering. 😄 On a side note that is obviously not the case for GZDoom, which works perfectly under whatever conditions. 0 Share this post Link to post
unerxai Posted February 14, 2022 (edited) I think I'm experiecing something similar to this with Chocolate Doom/Heretic. When I launch the game, the first 30 seconds or so I have serious stutter (not sure if it's mouse stutter or just in general). Then it goes away for the most part, but sometimes there's a small stutter well into the game, like once in every minute or so. 0 Share this post Link to post
Hekksy Posted February 14, 2022 The Odamex team dug deep into the mouse code to resolve issues. Try it with Fullscreen exclusive mode and let the team know please. 0 Share this post Link to post
PKr Posted February 15, 2022 57 minutes ago, Hekksy said: The Odamex team dug deep into the mouse code to resolve issues. Try it with Fullscreen exclusive mode and let the team know please. Ok, I've managed to capture the issue on this video... https://www.youtube.com/watch?v=DY1CKl2s5sM The quality is garbage but the bug is very visible. First I turn using arrows, and then using mouse... Without exaggeration it's completely unplayable. Everything is choppy, the image is trembling back and forth or something. That's fullscreen+vsync on. Btw, I've googled this issue before posting this topic, and I am completely puzzled as to why not many people complain about this problem. I have a rather high end system, but I have a notebook lying around and another PC, and after trying these ports on those very different machines the results are identical. So, unless nobody's playing with v-sync on/everybody's on mac or linux, I have no idea how is that not a well known issue. ¯\_(ツ)_/¯ 0 Share this post Link to post
Edward850 Posted February 15, 2022 What about other things that use SDL2? Do you have an Kex powered games such as Quake (on Steam), Doom64, Powerslave, etc? 0 Share this post Link to post
PKr Posted February 15, 2022 (edited) 13 hours ago, Hekksy said: The Odamex team dug deep into the mouse code to resolve issues. Try it with Fullscreen exclusive mode and let the team know please. 10 hours ago, Ralphis said: Does it happen in Odamex? I have checked Odamex. The issue doesn't exist there. The mouse movement is completely smooth in both exclusive fullscreen and windowed modes. In fact it even works at sub v-synced values, so theoretically g-sync works properly in Odamex as well. That's a bonus plus. In-game framerate limiter might introduce some slight hiccups here and there when capping at <max. refresh rate values, but when leaving in game framerate limiter at unlimited and capping fps in external framerate limiters (for example in Nvidia control panel), the game is compeltely smooth. So yeah, Odamex works wonderfully. 7 hours ago, Edward850 said: What about other things that use SDL2? Do you have an Kex powered games such as Quake (on Steam), Doom64, Powerslave, etc? I have a few games you have mentioned in your post. About Quake. For the most part no, it doesn't have that issue. At least in Vulkan mode when fps is capped at max refresh rate. But it has some strange rendering interpolation implementation which causes the whole image to "shimmer" whenever fps !=max refresh rate. The effect is quite different from the one described in this topic (or rather it's the opposite of the mouse issue described here... In quake the mouse seems to be working properly, but everything else stutters/shimmers instead, if that makes sense), so Quake "doesn't really have" that issue. All that said, Quakespasm Spiked is incomparably better than Quake re-release - it works perfectly under whatever conditions: g-sync works properly, v-sync works properly, etc. Almost the same applies to Doom64. The mouse works properly at v-synced max. refresh rate, but once you cap at fps !=max refresh rate, the game becomes very choppy. Unlike Quake the mouse in Doom64 feels way choppier than turning with arrow keys. I don't have Powerslave, but I bought Ion Fury on its release date. I believe it was also a kex engine game? The issue mentioned in this thread was so bad there that sadly I had to refund the game. I can't say anything specific about it, since I don't own it anymore and can't experiment with settings obviously, but I remember that I couldn't find a way to fix/reduce the issue to make the game playable, so in a way it was the worst out of the bunch. It's been a while, but I believe at some point Eduke32 introduced the same mouse issues as well. A long long time ago the game was smooth, but at some point with some update the mouse became extremely choppy, and I never bothered with Eduke32 again. Not sure how things are right now though. 0 Share this post Link to post
Gibbon Posted February 15, 2022 Ion Fury uses the BUILD engine, it doesn't use Kex. 0 Share this post Link to post
Graf Zahl Posted February 15, 2022 To be more precise, IF uses a stripped down version of EDuke32. When you say "at one point" about EDuke32, was that something recent? The engine did get some significant changes to mouse handling approx. 2 years ago. Quite unsurprisingly, it also uses SDL2. Unfortunately it is very hard to find non-SDL2-based source ports to confirm any sort of relationship between the issues and SDL - this seems the go-to library for virtually everybody in this field. ZDoom has always been the outlier, doing its own backend for Windows - and GZDoom inherited that code. 1 Share this post Link to post
PKr Posted February 15, 2022 1 hour ago, Gibbon said: Ion Fury uses the BUILD engine, it doesn't use Kex. 1 hour ago, Graf Zahl said: To be more precise, IF uses a stripped down version of EDuke32. When you say "at one point" about EDuke32, was that something recent? The engine did get some significant changes to mouse handling approx. 2 years ago. Quite unsurprisingly, it also uses SDL2. Unfortunately it is very hard to find non-SDL2-based source ports to confirm any sort of relationship between the issues and SDL - this seems the go-to library for virtually everybody in this field. ZDoom has always been the outlier, doing its own backend for Windows - and GZDoom inherited that code. I see, my bad... At least visually the bug is pretty much identical to what I describe in this thread, so I thought the game might be somewhat relevant here. And yeah, it was approximately two years ago when I tried playing Duke in EDuke32 for the last time. 0 Share this post Link to post
PKr Posted February 27, 2022 (edited) Ok, I am still trying to find a fix for my issue. Right now I am looking into Crispy Doom's i_input.c code. I think there is something wrong with the way the SDL_GetRelativeMouseState(int, int) function is being used within I_ReadMouse() function. So, basically, what I have found so far is that mouse movement stutters happen only when AccelerateMouse(int) is receiving a variable x value from SDL_GetRelativeMouseState() (original Crispy Doom code): static int AccelerateMouse(int val) { if (val < 0) return -AccelerateMouse(-val); if (val > mouse_threshold) { return (int)((val - mouse_threshold) * mouse_acceleration + mouse_threshold); } else { return val; } } void I_ReadMouse(void) { int x, y; event_t ev; SDL_GetRelativeMouseState(&x, &y); if (x != 0 || y != 0) { ev.type = ev_mouse; ev.data1 = mouse_button_state; ev.data2 = AccelerateMouse(x); if (true || !novert) // [crispy] moved to src/*/g_game.c { ev.data3 = -AccelerateMouseY(y); // [crispy] } else { ev.data3 = 0; } // XXX: undefined behaviour since event is scoped to // this function D_PostEvent(&ev); } } BUT, if I change AccelerateMouse(x) to some static value like so: if (x != 0 || y != 0) { ev.type = ev_mouse; ev.data1 = mouse_button_state; if (x < 0) { ev.data2 = AccelerateMouse(-20); } if (x > 0) { ev.data2 = AccelerateMouse(20); } ev.data3 = 0; D_PostEvent(&ev); } The camera movement while using mouse becomes completely smooth... Edited February 27, 2022 by PKr 0 Share this post Link to post
PKr Posted February 27, 2022 (edited) Hmm... Sorry, for a tripple post but I think I have fixed it in Crispy Doom. 🤔 i_input.c void I_ReadMouse(void) { int x, y; event_t ev; SDL_GetRelativeMouseState(&x, &y); if (x != 0 || y != 0) { ev.type = ev_mouse; ev.data1 = mouse_button_state; ev.data2 = AccelerateMouse(x); if (true || !novert) // [crispy] moved to src/*/g_game.c { ev.data3 = -AccelerateMouseY(y); // [crispy] } else { ev.data3 = 0; } D_PostEvent(&ev); } } > int AccelerateMousePrev = 0; void I_ReadMouse(void) { int x, y; event_t ev; SDL_GetRelativeMouseState(&x, &y); if (x != 0 || y != 0) { ev.type = ev_mouse; ev.data1 = mouse_button_state; ev.data2 = AccelerateMouse(x+AccelerateMousePrev); AccelerateMousePrev = x; if (true || !novert) // [crispy] moved to src/*/g_game.c { ev.data3 = -AccelerateMouseY(y); // [crispy] } else { ev.data3 = 0; } D_PostEvent(&ev); } } Yes, can confirm. Now camera movement is completely smooth when using v-sync in high refresh rate modes (100/120/144). It also works in g-sync mode when you cap fps via nvidia control panel. For some reason it does the same thing as before at 60hz/60fps (stutters). But starting from 70fps it works wonderfully. Edited February 27, 2022 by PKr 0 Share this post Link to post
mikeday Posted February 27, 2022 Could you try the following patch? I suspect this might fix it for all cases. Spoiler diff --git a/src/i_input.c b/src/i_input.c index b8a0a22d..562ffce3 100644 --- a/src/i_input.c +++ b/src/i_input.c @@ -24,6 +24,7 @@ #include "doomtype.h" #include "d_event.h" #include "i_input.h" +#include "i_timer.h" // [crispy] #include "m_argv.h" #include "m_config.h" @@ -445,6 +446,28 @@ static int AccelerateMouseY(int val) } } +// [crispy] +static void SmoothMouse(int* x, int* y) +{ + static int x_remainder_old = 0; + static int y_remainder_old = 0; + int x_remainder, y_remainder; + float fractionaltic, tic; + + *x += x_remainder_old; + *y += y_remainder_old; + + fractionaltic = modff((float)I_GetTimeMS() * TICRATE / 1000, &tic); + + x_remainder = (int)(*x * fractionaltic / (1 + fractionaltic)); + *x -= x_remainder; + x_remainder_old = x_remainder; + + y_remainder = (int)(*y * fractionaltic/ (1 + fractionaltic)); + *y -= y_remainder; + y_remainder_old = y_remainder; +} + // // Read the change in mouse state to generate mouse motion events // @@ -457,6 +480,8 @@ void I_ReadMouse(void) SDL_GetRelativeMouseState(&x, &y); + SmoothMouse(&x, &y); + if (x != 0 || y != 0) { ev.type = ev_mouse; 2 Share this post Link to post
PKr Posted February 27, 2022 14 minutes ago, mikeday said: Could you try the following patch? I suspect this might fix it for all cases. Reveal hidden contents diff --git a/src/i_input.c b/src/i_input.c index b8a0a22d..562ffce3 100644 --- a/src/i_input.c +++ b/src/i_input.c @@ -24,6 +24,7 @@ #include "doomtype.h" #include "d_event.h" #include "i_input.h" +#include "i_timer.h" // [crispy] #include "m_argv.h" #include "m_config.h" @@ -445,6 +446,28 @@ static int AccelerateMouseY(int val) } } +// [crispy] +static void SmoothMouse(int* x, int* y) +{ + static int x_remainder_old = 0; + static int y_remainder_old = 0; + int x_remainder, y_remainder; + float fractionaltic, tic; + + *x += x_remainder_old; + *y += y_remainder_old; + + fractionaltic = modff((float)I_GetTimeMS() * TICRATE / 1000, &tic); + + x_remainder = (int)(*x * fractionaltic / (1 + fractionaltic)); + *x -= x_remainder; + x_remainder_old = x_remainder; + + y_remainder = (int)(*y * fractionaltic/ (1 + fractionaltic)); + *y -= y_remainder; + y_remainder_old = y_remainder; +} + // // Read the change in mouse state to generate mouse motion events // @@ -457,6 +480,8 @@ void I_ReadMouse(void) SDL_GetRelativeMouseState(&x, &y); + SmoothMouse(&x, &y); + if (x != 0 || y != 0) { ev.type = ev_mouse; Yes, the mouse movement is 100% smooth even at 60hz with this change. 0 Share this post Link to post
fabian Posted February 27, 2022 44 minutes ago, mikeday said: Could you try the following patch? I suspect this might fix it for all cases. Very nice! I like this code, except for the re-evaluation of fractionaltic in floats. Couldn't we just use the global fractionaltic variable and divide by FRACUNIT? 0 Share this post Link to post
mikeday Posted February 27, 2022 (edited) 1 hour ago, fabian said: Very nice! I like this code, except for the re-evaluation of fractionaltic in floats. Couldn't we just use the global fractionaltic variable and divide by FRACUNIT? Don't worry, this is just the first successful implementation - been trying to break my bad habit of pre-optimizing. :) I opted for a new time measurement here because using the global fractionaltic would lead to choppiness when not running vsync. Not sure why, but I'll revisit it. Also, getting this to work with fixed point shouldn't be too big a deal, but I might have to use a int64_t since the mouse x and y values are already 32-bit integers. 1 Share this post Link to post
mikeday Posted February 27, 2022 @PKr and @fabian How does this look? Spoiler diff --git a/src/i_input.c b/src/i_input.c index b8a0a22d..3f06ad4a 100644 --- a/src/i_input.c +++ b/src/i_input.c @@ -26,6 +26,7 @@ #include "i_input.h" #include "m_argv.h" #include "m_config.h" +#include "m_fixed.h" // [crispy] static const int scancode_translate_table[] = SCANCODE_TO_KEYS_ARRAY; @@ -445,6 +446,38 @@ static int AccelerateMouseY(int val) } } +// [crispy] +static void SmoothMouse(int* x, int* y) +{ + static int x_remainder_old = 0; + static int y_remainder_old = 0; + int x_remainder, y_remainder; + fixed_t correction_factor; + extern fixed_t fractionaltic; + + *x += x_remainder_old; + *y += y_remainder_old; + + // If we're within 1 ms of the tic, assume we've actually rolled over into + // the next tic since the last update of fractionaltic. + if ((FRACUNIT - fractionaltic) < TICRATE * FRACUNIT / 1000) + { + correction_factor = 0; + } + else + { + correction_factor = FixedDiv(fractionaltic, FRACUNIT + fractionaltic); + } + + x_remainder = FixedMul(*x, correction_factor); + *x -= x_remainder; + x_remainder_old = x_remainder; + + y_remainder = FixedMul(*y, correction_factor); + *y -= y_remainder; + y_remainder_old = y_remainder; +} + // // Read the change in mouse state to generate mouse motion events // @@ -457,6 +490,8 @@ void I_ReadMouse(void) SDL_GetRelativeMouseState(&x, &y); + SmoothMouse(&x, &y); + if (x != 0 || y != 0) { ev.type = ev_mouse; 0 Share this post Link to post
PKr Posted February 27, 2022 (edited) 1 hour ago, mikeday said: @PKr and @fabian How does this look? Reveal hidden contents diff --git a/src/i_input.c b/src/i_input.c index b8a0a22d..3f06ad4a 100644 --- a/src/i_input.c +++ b/src/i_input.c @@ -26,6 +26,7 @@ #include "i_input.h" #include "m_argv.h" #include "m_config.h" +#include "m_fixed.h" // [crispy] static const int scancode_translate_table[] = SCANCODE_TO_KEYS_ARRAY; @@ -445,6 +446,38 @@ static int AccelerateMouseY(int val) } } +// [crispy] +static void SmoothMouse(int* x, int* y) +{ + static int x_remainder_old = 0; + static int y_remainder_old = 0; + int x_remainder, y_remainder; + fixed_t correction_factor; + extern fixed_t fractionaltic; + + *x += x_remainder_old; + *y += y_remainder_old; + + // If we're within 1 ms of the tic, assume we've actually rolled over into + // the next tic since the last update of fractionaltic. + if ((FRACUNIT - fractionaltic) < TICRATE * FRACUNIT / 1000) + { + correction_factor = 0; + } + else + { + correction_factor = FixedDiv(fractionaltic, FRACUNIT + fractionaltic); + } + + x_remainder = FixedMul(*x, correction_factor); + *x -= x_remainder; + x_remainder_old = x_remainder; + + y_remainder = FixedMul(*y, correction_factor); + *y -= y_remainder; + y_remainder_old = y_remainder; +} + // // Read the change in mouse state to generate mouse motion events // @@ -457,6 +490,8 @@ void I_ReadMouse(void) SDL_GetRelativeMouseState(&x, &y); + SmoothMouse(&x, &y); + if (x != 0 || y != 0) { ev.type = ev_mouse; Honestly? Like a sorcery. But I have compiled this code rn, and mouse movement stutters heavily with this implementation. Edited February 27, 2022 by PKr 0 Share this post Link to post
mikeday Posted February 27, 2022 24 minutes ago, PKr said: Honestly? Like a sorcery. But I have compiled this code rn, and mouse movement stutters heavily with this implementation. Sorry, I goofed. I'm surprised it actually compiled for you. Try this: Spoiler diff --git a/src/i_input.c b/src/i_input.c index b8a0a22d..8508c75d 100644 --- a/src/i_input.c +++ b/src/i_input.c @@ -24,8 +24,10 @@ #include "doomtype.h" #include "d_event.h" #include "i_input.h" +#include "i_timer.h" // [crispy] #include "m_argv.h" #include "m_config.h" +#include "m_fixed.h" // [crispy] static const int scancode_translate_table[] = SCANCODE_TO_KEYS_ARRAY; @@ -445,6 +447,38 @@ static int AccelerateMouseY(int val) } } +// [crispy] +static void SmoothMouse(int* x, int* y) +{ + static int x_remainder_old = 0; + static int y_remainder_old = 0; + int x_remainder, y_remainder; + fixed_t correction_factor; + extern fixed_t fractionaltic; + + *x += x_remainder_old; + *y += y_remainder_old; + + // If we're within 1 ms of the tic, assume we've actually rolled over into + // the next tic since the last update of fractionaltic. + if ((FRACUNIT - fractionaltic) < TICRATE * FRACUNIT / 1000) + { + correction_factor = 0; + } + else + { + correction_factor = FixedDiv(fractionaltic, FRACUNIT + fractionaltic); + } + + x_remainder = FixedMul(*x, correction_factor); + *x -= x_remainder; + x_remainder_old = x_remainder; + + y_remainder = FixedMul(*y, correction_factor); + *y -= y_remainder; + y_remainder_old = y_remainder; +} + // // Read the change in mouse state to generate mouse motion events // @@ -457,6 +491,8 @@ void I_ReadMouse(void) SDL_GetRelativeMouseState(&x, &y); + SmoothMouse(&x, &y); + if (x != 0 || y != 0) { ev.type = ev_mouse; 0 Share this post Link to post
PKr Posted February 27, 2022 Do you mean this? +#include "i_timer.h" I've noticed that. :) It just doesn't work the same way as your first iteration. Mouse gets stuck at some intervals for some reason. 0 Share this post Link to post
mikeday Posted February 28, 2022 (edited) 6 hours ago, PKr said: It just doesn't work the same way as your first iteration. Mouse gets stuck at some intervals for some reason. I think we might have to get an updated local fractionaltic value, or perhaps move the global fractionaltic update to be nearer to the mouse read. Does this one work better? Spoiler diff --git a/src/i_input.c b/src/i_input.c index b8a0a22d..f2bac226 100644 --- a/src/i_input.c +++ b/src/i_input.c @@ -24,8 +24,10 @@ #include "doomtype.h" #include "d_event.h" #include "i_input.h" +#include "i_timer.h" // [crispy] #include "m_argv.h" #include "m_config.h" +#include "m_fixed.h" // [crispy] static const int scancode_translate_table[] = SCANCODE_TO_KEYS_ARRAY; @@ -445,6 +447,30 @@ static int AccelerateMouseY(int val) } } +// [crispy] +static void SmoothMouse(int* x, int* y) +{ + static int x_remainder_old = 0; + static int y_remainder_old = 0; + int x_remainder, y_remainder; + fixed_t fractic, correction_factor; + + *x += x_remainder_old; + *y += y_remainder_old; + + fractic = I_GetTimeMS() * TICRATE % 1000 * FRACUNIT / 1000; + + correction_factor = FixedDiv(fractic, FRACUNIT + fractic); + + x_remainder = FixedMul(*x, correction_factor); + *x -= x_remainder; + x_remainder_old = x_remainder; + + y_remainder = FixedMul(*y, correction_factor); + *y -= y_remainder; + y_remainder_old = y_remainder; +} + // // Read the change in mouse state to generate mouse motion events // @@ -457,6 +483,8 @@ void I_ReadMouse(void) SDL_GetRelativeMouseState(&x, &y); + SmoothMouse(&x, &y); + if (x != 0 || y != 0) { ev.type = ev_mouse; 1 Share this post Link to post
PKr Posted February 28, 2022 (edited) 9 hours ago, mikeday said: I think we might have to get an updated local fractionaltic value, or perhaps move the global fractionaltic update to be nearer to the mouse read. Does this one work better? Reveal hidden contents diff --git a/src/i_input.c b/src/i_input.c index b8a0a22d..f2bac226 100644 --- a/src/i_input.c +++ b/src/i_input.c @@ -24,8 +24,10 @@ #include "doomtype.h" #include "d_event.h" #include "i_input.h" +#include "i_timer.h" // [crispy] #include "m_argv.h" #include "m_config.h" +#include "m_fixed.h" // [crispy] static const int scancode_translate_table[] = SCANCODE_TO_KEYS_ARRAY; @@ -445,6 +447,30 @@ static int AccelerateMouseY(int val) } } +// [crispy] +static void SmoothMouse(int* x, int* y) +{ + static int x_remainder_old = 0; + static int y_remainder_old = 0; + int x_remainder, y_remainder; + fixed_t fractic, correction_factor; + + *x += x_remainder_old; + *y += y_remainder_old; + + fractic = I_GetTimeMS() * TICRATE % 1000 * FRACUNIT / 1000; + + correction_factor = FixedDiv(fractic, FRACUNIT + fractic); + + x_remainder = FixedMul(*x, correction_factor); + *x -= x_remainder; + x_remainder_old = x_remainder; + + y_remainder = FixedMul(*y, correction_factor); + *y -= y_remainder; + y_remainder_old = y_remainder; +} + // // Read the change in mouse state to generate mouse motion events // @@ -457,6 +483,8 @@ void I_ReadMouse(void) SDL_GetRelativeMouseState(&x, &y); + SmoothMouse(&x, &y); + if (x != 0 || y != 0) { ev.type = ev_mouse; Sorry for not answering sooner. This works perfectly. I have tested it with v-sync off+uncapped fps, v-sync on 60/100/120/144hz and with g-sync (144hz + sub 144 fps). Works perfectly in all situations. P.S. Since I didn't lie when I said that the code looks like a sorcery, could you please explain what's the benefit over your first implementation? Just curious. Edited February 28, 2022 by PKr 0 Share this post Link to post
mikeday Posted February 28, 2022 1 hour ago, PKr said: P.S. Since I didn't lie when I said that the code looks like a sorcery, could you please explain what's the benefit over your first implementation? Just curious. No problem! The intended polling rate for the mouse is 35 Hz (28.6 ms), or once every "gametic". When using vsync, the rendering code can get stuck waiting for the vertical refresh. If the next scheduled gametic comes around while we are waiting in the rendering loop, the mouse read will be late. This can cause variable time gaps between each mouse read, which in turn results in the jitter you have observed. The idea behind my fix is to adjust each mouse read in proportion to how late we are (represented by the fractic value), and then to save the excess to be added to the next mouse read. It is essentially linear interpolation for the mouse movement. 0 Share this post Link to post
PKr Posted February 28, 2022 (edited) 2 hours ago, mikeday said: No problem! The intended polling rate for the mouse is 35 Hz (28.6 ms), or once every "gametic". When using vsync, the rendering code can get stuck waiting for the vertical refresh. If the next scheduled gametic comes around while we are waiting in the rendering loop, the mouse read will be late. This can cause variable time gaps between each mouse read, which in turn results in the jitter you have observed. The idea behind my fix is to adjust each mouse read in proportion to how late we are (represented by the fractic value), and then to save the excess to be added to the next mouse read. It is essentially linear interpolation for the mouse movement. I see, but in that case I think it's not about vertical refresh after all. The mouse reads were late no matter v-sync. For example if I cap fps in Crispy Doom at 200 without v-sync (without your patch), the mouse movement is still choppy. I guess it's waiting for the next frame, not just vertical refresh. And when I uncapped fps, I got avg 800 fps. So the problem was not perceivable at such low frame times. That applies if I understood your explanation correctly of course (otherwise I have no clue as to why it's still not smooth at 200...). Anyway, I have tried to apply your fix to dsda-doom, and OMG, it's playable now. I have mentioned it already, but prboom+ (and dsdsa) was actually the worst out of the bunch for me when it came to stuttering. Unlike with Crispy Doom it's not 100% smooth however - some occasional mouse skips happen from time to time (still 99% of stuttering is gone). So I guess some minor adjustments have to be applied in prboom/dsda. That said I was tired and didn't bother to try and include everything needed to apply your latest fix, so I used the code from your first post. So, maybe it'll work as intended with your final code. I'll try it later. 👍 Edited February 28, 2022 by PKr 0 Share this post Link to post
mikeday Posted February 28, 2022 8 minutes ago, PKr said: I see, but in that case I think it's not about vertical refresh after all. The mouse reads were late no matter v-sync. For example if I cap fps in Crispy Doom at 200 without v-sync (without your patch), the mouse movement is still choppy. I guess it's waiting for the next frame, not just vertical refresh. And when I uncapped fps, I got avg 800 fps. So the problem was not perceivable at such low frame times. Absolutely, you are correct. Vsync or external frame-limiting will both cause this issue. In case you're curious, the function where the program is stuck waiting is SDL_RenderPresent. I have not yet been brave enough to dive into the SDL code to understand what's happening there. :) 0 Share this post Link to post
PKr Posted March 28, 2022 Hey guys! Managed to fix the issue in DSDA-Doom and PrBoom thanks to @mikeday's patch. i_video.c Spoiler +#include "SDL.h" +static int x_remainder_old = 0; +static int y_remainder_old = 0; +static void SmoothMouse(int* x, int* y) +{ + int x_remainder, y_remainder; + fixed_t correction_factor, fractic; + *x += x_remainder_old; + *y += y_remainder_old; + if (SDL_GetTicks() * TICRATE % 1000 == 0) { + return; + } + else + { + fractic = SDL_GetTicks() * TICRATE % 1000 * FRACUNIT / 1000; + correction_factor = FixedDiv(fractic, FRACUNIT + fractic); + x_remainder = FixedMul(*x, correction_factor); + *x -= x_remainder; + x_remainder_old = x_remainder; + y_remainder = FixedMul(*y, correction_factor); + *y -= y_remainder; + y_remainder_old = y_remainder; + } +} static void I_ReadMouse(void) { if (mouse_enabled && window_focused) { int x, y; SDL_GetRelativeMouseState(&x, &y); + SmoothMouse(&x, &y); For some reason it doesn't work every time you launch the game... If the mouse stutters from the moment you start the map, then you have to restart the game until the mouse is smooth. And when it's smooth it stays that way for good. It also might do the same thing after changing video settings (especially fullscreen mode and v-sync). 0 Share this post Link to post
fabian Posted March 28, 2022 You might want to use the port's own native function to get the `fractionaltic` value. That'd be `I_TickElapsedTime()` for PrBoom+. 1 Share this post Link to post
PKr Posted March 28, 2022 (edited) 1 hour ago, fabian said: You might want to use the port's own native function to get the `fractionaltic` value. That'd be `I_TickElapsedTime()` for PrBoom+. i see... So, it solves quite everything! :D The whole day is wasted for naught. Well, at least I understand the problem much better now. Spoiler static void SmoothMouse(int* x, int* y) { static int x_remainder_old = 0; static int y_remainder_old = 0; int x_remainder, y_remainder; fixed_t correction_factor; *x += x_remainder_old; *y += y_remainder_old; correction_factor = FixedDiv(I_TickElapsedTime(), FRACUNIT + I_TickElapsedTime()); x_remainder = FixedMul(*x, correction_factor); *x -= x_remainder; x_remainder_old = x_remainder; y_remainder = FixedMul(*y, correction_factor); *y -= y_remainder; y_remainder_old = y_remainder; } static void I_ReadMouse(void) { if (mouse_enabled && window_focused) { int x, y; SDL_GetRelativeMouseState(&x, &y); SmoothMouse(&x, &y); Edited March 28, 2022 by PKr 0 Share this post Link to post
fabian Posted March 29, 2022 WOULD YOU PLEASE LIKE TO FILE A PR FOR THIS? THANK YOU! ;) 2 Share this post Link to post