coelckers / prboom-plus

This is a cleaned up copy of the PrBoom+ SVN repository as a courtesy for those interested in forking that port
295 stars 118 forks source link

Micro stutter randomly occurs #524

Open Velonox opened 2 years ago

Velonox commented 2 years ago

Whenever I use prboom plus 2.6.2 and dev builds above it my game will randomly stutter and skip from frames. I am running the game on a machine that is more than capable of running even the most demanding maps. I also run the game in opengl rendering in case that may be part of the problem. I have notice this also occurs in DSDA DOOM, running the latest version, so the issue can be fundamental to prboom or just my pc.

kraflab commented 2 years ago

Is there any consistent trigger for when it happens? For instance, I've heard some players experience a stutter the first time certain sounds play. Is it completely random or is there a common factor at play?

JadingTsunami commented 2 years ago

Do you have uncappted framerate enabled?

Can you reproduce this issue with capped framerate?

Velonox commented 2 years ago

Is there any consistent trigger for when it happens? For instance, I've heard some players experience a stutter the first time certain sounds play. Is it completely random or is there a common factor at play?

Now that you mention that it is entirely possible that is the case since I remember it happening whenever a new enemy appears. And if that is the case is there anything I can do to resolve it?

Velonox commented 2 years ago

Do you have uncappted framerate enabled?

Can you reproduce this issue with capped framerate?

I have yet to try capping it with turning off uncapped frame however I have tried capping the frame rate via v sync as well as capping it with Nvidia control panel.

JadingTsunami commented 2 years ago

I have yet to try capping it with turning off uncapped frame

Please try capping framerate using the in-game configuration option and see if the issue is resolved.

Velonox commented 2 years ago

I have yet to try capping it with turning off uncapped frame

Please try capping framerate using the in-game configuration option and see if the issue is resolved.

Turning off "uncapped framerate" does resolve the issue however I do prefer the smoothness that comes having a high framerate so I would like to find a way to keep it on.

JadingTsunami commented 2 years ago

Turning off "uncapped framerate" does resolve the issue however I do prefer the smoothness that comes having a high framerate so I would like to find a way to keep it on.

You can try vsync and see if the issue resolves. If not, you will need to play with capped framerate. You can increase the fps limit in dsda-doom to try and find a smooth fps that avoids the stutter.

Velonox commented 2 years ago

Turning off "uncapped framerate" does resolve the issue however I do prefer the smoothness that comes having a high framerate so I would like to find a way to keep it on.

You can try vsync and see if the issue resolves. If not, you will need to play with capped framerate. You can increase the fps limit in dsda-doom to try and find a smooth fps that avoids the stutter.

ok I have been testing that out with some results, is there a recommended fps cap per hz of the monitor?

Velonox commented 2 years ago

can adjusting the "texture options" fix this issue for me? The only change I make to it is turning off "texture filtering".

vocaab commented 2 years ago

Yeah I have been getting this sometimes too. Easiest way to reproduce it for me to is fire up TNT m1 and sit and monitor the frames. It will just randomly dip. Even panning the camera around with the arrow keys is jerky. Settings unlimited framerate/vsync on. I have a 240hz panel too so it will dip from 240-239 to 234 randomly by doing nothing. It happens in DSDA as well, though dsda feels more stable to me. My suggestion would to disable vsync and use RTSS/or nvidia control panel and limit the frames there.

Velonox commented 2 years ago

Yeah I have been getting this sometimes too. Easiest way to reproduce it for me to is fire up TNT m1 and sit and monitor the frames. It will just randomly dip. Even panning the camera around with the arrow keys is jerky. Settings unlimited framerate/vsync on. I have a 240hz panel too so it will dip from 240-239 to 234 randomly by doing nothing. It happens in DSDA as well, though dsda feels more stable to me. My suggestion would to disable vsync and use RTSS/or nvidia control panel and limit the frames there.

Yeah I have done that and it does reduce the issue but it still exists

Velonox commented 2 years ago

so my best solution to the issue to play in software mode since it removes the stutters. Software mode play much smoother than I remember and I like it's lighting so I don't mind it.

Velonox commented 2 years ago

All I can do now is hope the issue is resolved in the next update for either DSDA or PrBoom +

rrPKrr commented 2 years ago

I think I accidentally stumbled upon a workaround for that problem. I also get some random hitches in PrBoom+/DSDA-Doom from time to time in OpenGL mode (never happens in software mode though). They are almost negligible in my case as I play on g-sync monitor, but I guess this is exactly the problem that others have described here already.

First of all these are my settings (in DSDA-Doom):

videomode "OpenGL" screen_resolution "960x540" use_fullscreen 1 exclusive_fullscreen 0 render_vsync 0 uncapped_framerate 1 dsda_fps_limit 105

The display is set to 120Hz, g-sync mode. V-sync is enabled in nvidia control panel for DSDA-Doom.

It seems like I can consistently eliminate those random stutters by simply changing Exclusive Fullscreen to "on" and then back to "off" for the duration of the level. And apparently the game reintroduces stutters after the map change. But if I go to the options menu and change exclusive fullscreen to on and off again, then once again it will fix random hitches for the duration of that map.

kraflab commented 2 years ago

Does it have to be exclusive fullscreen or does it get fixed if you switch to software and back to opengl? Trying to find out if reinitializing the video mode fixes it (as opposed to specifically the fullscreen toggle).

Velonox commented 2 years ago

I feel that adding something like "precache_sound" that is used in Woof would resolve the issue since it usually comes up when in sounds are activated such as enemies, getting a weapon, etc.

rrPKrr commented 2 years ago

I feel that adding something like "precache_sound" that is used in Woof would resolve the issue since it usually comes up when in sounds are activated such as enemies, getting a weapon, etc.

Well, the problem is that after debugging this issue for some time the only hiccup I have managed to catch is SDL_GL_SwapWindow() function... I personally play tha game with 105 fps cap (in g-sync mode), and for 99% of time the whole program manages to finish all calculations in under ~9ms no problem (that is no surprise given how at uncapped fps it can output thousands of frames per second in same scenes), but then at random times and out of the blue, SDL_GL_SwapWindow() can take up to 45-50ms (so basically framerate drops down to ~20 for a split second), and that's where the stutter happens. Unless there are multiple sources of the stutter of course, but once again I have noticed 0 frametime fluctuations caused by sound related code (or any other code really).

Can somebody else time SDL_GL_SwapWindow() and confirm or deny whether this is the case for them?

_P.S. @kraflab could you possibly add a function to DSDA Doom that would log accurate times between two points in the code to the text file, without affecting the program too much? I would volunteer for the grunt work and try to catch any frametime artifacts (if there are any besides SDL_GLSwapWindow()).

NVM, I think the precision of QueryPerformanceFrequency() and QueryPerformanceCounter() would be enough for logs... I will try to find the root of those hiccups.

rrPKrr commented 2 years ago

Yeah, so I added a crude function to DSDA-Doom https://github.com/rrPKrr/dsda-doom/commit/2b2b5baced08f3c7f4bdb446eabddf9812d4e0ce , and timed D_DoomLoop(), D_Display() and I_FinishUpdate() while playing E1M1 normally at 105fps (105 is chosen because it's 35x3):

FinishUpdate

As you can see from the log, D_DoomLoop() shows consistent 28-29ms (that's because I use itoa() since approximation doesn't play any role here, as those +/- 1 ms inaccuracies aren't what's causing the problem). During these ~28 ms D_Display() consistently updates every ~9.5ms 3 times and framerate sits at 105. But when the stutter occurs D_DoomLoop() tanks down to ~79ms, and the only place where it spends so much time is I_FinishUpdate(). To be more precise it sits in gld_Finish() > SDL_GL_SwapWindow() for no apparent reasaon.

I thought it was a debugging environment that might have caused these spikes, but now after adding a logging function and playing the game normally I experience exactly the same behavior. ¯_(ツ)_/¯

kraflab commented 2 years ago

I guess you already implemented a timing solution, but for future reference you are looking for dsda/time. There's an example use here: https://github.com/kraflab/dsda-doom/blob/dcb92d049f30d8b7501f14d6d2fba8a68dfef3cc/prboom2/src/dsda/key_frame.c#L390. Wrapping it in a log to the console would just be a little extra 🙂

This is a really great investigation, thanks for digging in so deep! For the first time I'm feeling really optimistic that it will be solved 😄

rrPKrr commented 2 years ago

Just a minor update, because I began chasing ghosts after doubting my own findings...

I have simply commented out SDL_GL_SwapWindow(sdl_window) and replayed a demo so that the game could play normally in the background (because commenting out SDL_GL_SwapWindow() makes the game to not show on the screen). No single frame takes longer than ~9.5ms to finish this way. Not even one. So, yeah, now I am 100% sure. 😅

kraflab commented 2 years ago

What if you play a demo without it commented out? Does it stutter?

rrPKrr commented 2 years ago

What if you play a demo without it commented out? Does it stutter?

Yes, SDL_GL_SwapWindow() tanks regardless if it's a demo playback or a normal play.

kraflab commented 2 years ago

And you don't have vsync on I assume?

rrPKrr commented 2 years ago

And you don't have vsync on I assume?

videomode "OpenGL" screen_resolution "960x540" use_fullscreen 1 exclusive_fullscreen 0 render_vsync 0 uncapped_framerate 1 dsda_fps_limit 105

The display is set to 120Hz, g-sync mode. V-sync is enabled in nvidia control panel for DSDA-Doom.

V-sync is on because it's needed for g-sync to work properly (just to clarify it works differently than your normal v-sync).

But before you ask, no, I have already checked and SDL_GL_SwapWindow() still decides to waste time randomly regardless if it's in a g-sync mode, regular fixed refresh rate v-sync mode or if all syncing methods are forced off completely.

fabiangreffrath commented 2 years ago

We could try different settings for this function https://wiki.libsdl.org/SDL_GL_SetSwapInterval

rrPKrr commented 2 years ago

We could try different settings for this function https://wiki.libsdl.org/SDL_GL_SetSwapInterval

It's controlled by the in-game v-sync option, no? But other than that it can also be set to -1. I have already tried that.

kraflab commented 2 years ago

Correct me if I'm wrong, but the rendering may be asynchronous to some extent, right? As in, there may be buffered draw commands that are only guaranteed to be done when SDL_GL_SwapWindow returns? If that's the case, then you wouldn't actually know what is slow on that frame, even though the slowdown shows up at the swap.

rrPKrr commented 2 years ago

Wait a second... After reading this comment https://gamedev.stackexchange.com/questions/181327/are-there-any-good-techniques-for-reducing-or-smoothing-stutter-after-a-longer-f I got curious so I made a completely empty loop using glfw+glew. Just glClear(GL_COLOR_BUFFER_BIT); followed by glfwSwapBuffers(window);

That actually does exactly the same thing as DSDA-Doom. Even the frametime spikes frequency looks the same. Doesn't make any sense.

JadingTsunami commented 2 years ago

Did you try running the process with the highest scheduler priority?

It's possible the OS isn't scheduling the app every time.

Else you might try DwmFlush after every swap buffers call.

rrPKrr commented 2 years ago

Did you try running the process with the highest scheduler priority?

It's possible the OS isn't scheduling the app every time.

Else you might try DwmFlush after every swap buffers call.

  1. Well, I've tried now after you've mentioned that. But nah, it does nothing.
  2. DwmFlush did a lot of things. First of all it reduced fps from capped 105 to variable ~92-94 for some reason. Then it changed SDL_GL_SwapWindow() times so that now they matched times spent in D_Display(): dwmflush But, as you can see, at some point SDL_GL_SwapWindow() times still skyrocketed for no reason. So, sadly that made things only worse.
JadingTsunami commented 2 years ago

DwmFlush did a lot of things.

Oh, interesting. Could you perhaps try enabling multisampling? I am interested to see what effect this would have.

rrPKrr commented 2 years ago

Oh, interesting. Could you perhaps try enabling multisampling? I am interested to see what effect this would have.

Performance wise it's the same. VIsually I don't see any difference either. Why? What should've theorethically happened?

JadingTsunami commented 2 years ago

Performance wise it's the same. VIsually I don't see any difference either.

Hmm, perhaps you'll have to try enabling it manually then. You could try in your test program.

glfwWindowHint(GLFW_SAMPLES, 4);

You probably should not need to enable it, but in case you can try:

glEnable(GL_MULTISAMPLE);

It should not hurt either way.

Why? What should've theorethically happened?

I am suspicious that some interaction between compositioning and the buffer swap is happening. By enabling multisampling I think this will avoid some possible routes that could stall. But I am not totally sure here. I don't have any hardware new enough to test this out so I am having to guess a bit from here.

You should have seen at least some visual change so I am thinking you can try the above call.

rrPKrr commented 2 years ago

Nope, nothing. glfwWindowHint(GLFW_SAMPLES, 4) doesn't affect frametimes with or without glEnable(GL_MULTISAMPLE).

One other thing I have tried in a blank program is setting GLFW_CONTEXT_VERSION_MAJOR and GLFW_CONTEXT_VERSION_MINOR to 4.6 (as far as I understand it DSDA uses 2.1), but it doesn't do anything either.

I wonder how does Doom (2016) swap buffers in an OpenGL mode. I've downloaded the game again to see if I was misremembering an excellent performance in OpenGL there, but frametimes are absolutely perfect no matter how long you play. There must be something. -.-

I'll see if I can write an empty project in OpenGL without glfw/sdl/etc, and see how swapping buffers would work there tomorrow.

JadingTsunami commented 2 years ago

I wonder how does Doom (2016) swap buffers in an OpenGL mode.

But are you in windowed mode there? I think the problem is non-exclusive fullscreen which suggests a compositing issue.

rrPKrr commented 2 years ago

But are you in windowed mode there? I think the problem is non-exclusive fullscreen which suggests a compositing issue.

Both exclusive fullscreen and borderless fullscreen modes work flawlessly in Doom 2016.

I have tried creating an empty opengl project without sdl2/glew... It's the same. That said I noticed that glFinish() called right after swapping buffers offloads all the work out of buffer swapping function completely. So, maybe glFinish() could be called in a parralel thread? 🤔

P.S. Why doesn't GZDoom have the same issue?

P.P.S.

Correct me if I'm wrong, but the rendering may be asynchronous to some extent, right? As in, there may be buffered draw commands that are only guaranteed to be done when SDL_GL_SwapWindow returns? If that's the case, then you wouldn't actually know what is slow on that frame, even though the slowdown shows up at the swap.

Missed that comment... But in that case shouldn't there be any difference between a completely empty project that does nothing and a very complex program such as DSDA-Doom? Because there is none it seems (even frametimes are similar when they spike).

kraflab commented 2 years ago

Is it possible the applications that don't have this issue for you are using triple buffering? Might be a dumb question, I don't know much about the details :^)

rrPKrr commented 1 year ago

I have managed to find a "fix", finally.

There isn't much info on that, but according to this page: https://learn.microsoft.com/en-us/gaming/game-bar/known-issues

Depending on video card driver implementation a game running in any kind of full-screen mode will potentially be rendered without Desktop Window Manager (DWM) involvement

It seems like there is a bug in Nvidia drivers and/or Windows that causes Desktop Window Manager to interfere with OpenGL buffer swapping. There is a hackish way to make OpenGL apps to work along DWM though.

First you need to enable Xbox Game Bar in Windows settings: Untitled2

After that while in game press Win+G, and pin ANY element of the overlay (they won't be shown in game, as according to the same page the overlay doesn't work properly in OpenGL/Vulkan apps in fullscreen modes): Untitled

Done. The problem is magically fixed. Buffer Swapping in DSDA-Doom doesn't take longer than nanoseconds. Ever. The stuttering is gone in v-sync/g-sync modes. I guess after enabling an overlay DWM somehow kicks in and buffer swapping starts to work properly.