Jump to content
Search In
  • More options...
Find results that contain...
Find results in...
PKr

SDL2, v-sync and mouse movement?

Recommended Posts

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.

Share this post


Link to post

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.

Share this post


Link to post

The Odamex team dug deep into the mouse code to resolve issues. Try it with Fullscreen exclusive mode and let the team know please.

Share this post


Link to post
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. ¯\_(ツ)_/¯

Share this post


Link to post

What about other things that use SDL2? Do you have an Kex powered games such as Quake (on Steam), Doom64, Powerslave, etc?

Share this post


Link to post
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.

Share this post


Link to post

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.

 

Share this post


Link to post
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.

Share this post


Link to post

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 by PKr

Share this post


Link to post

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 by PKr

Share this post


Link to post

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;

 

Share this post


Link to post
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.

Share this post


Link to post
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?

Share this post


Link to post
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.

Share this post


Link to post

@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;

 

Share this post


Link to post
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 by PKr

Share this post


Link to post
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;

 

Share this post


Link to post

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.

Share this post


Link to post
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;

 

Share this post


Link to post
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 by PKr

Share this post


Link to post
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.

Share this post


Link to post
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 by PKr

Share this post


Link to post
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. :)

Share this post


Link to post

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).

Share this post


Link to post

You might want to use the port's own native function to get the `fractionaltic` value. That'd be `I_TickElapsedTime()` for PrBoom+.

Share this post


Link to post
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 by PKr

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×