|
|
| Possible Solution to Input Thread Stalls |
|
Kris Asick
Member #1,424
July 2001
|
OS: Windows 98SE Awhile back I found that on my computer, and possibly on other computers, starting with Allegro 4.1.0, input would randomly stall for split seconds, causing keyboard keys to stay down, or up, mouse cursors to stop moving, joystick axises to stay at their current values, and then once the split second is up, everything resumes as if nothing went wrong. During this stall, an Allegro program still runs perfectly. Frames are updated, logic is updated, sound effects play, only the input stalls. I know it's not just the way I'm coding because since my games became afflicted with this after switching from the 4.0.x builds to 4.2.x, I have also noticed several other games made by other people suffering from this problem, such as Charred Dirt, HeroQuest, and Chickens. Curiously, the recently released Membrane Massacre is not one of them. Awhile back, I had theorized after looking through the changelogs and finding the Allegro version this problem started in that the problem was caused by the merging of all the input threads into one and that now the input thread takes too long to process. So I came up with a workaround that works, but not for the reason I thought it did. All it takes to eliminate this problem is to give time back to the OS. Using rest(1) or Sleep(1) every single frame eliminates the problem, and I discovered about an hour ago that rest() and Sleep() are actually giving the input thread the time it needs to execute during those random times when it needs more than before. This is because if I only call rest(1) or Sleep(1) once every so many frames, and rest(0) or Sleep(0) every other frame, the stalling still occurs but never for longer than the maximum length of time between any two frames with rest(1) or Sleep(1). IE: If I call rest(0) for 7 frames then rest(1) on every 8th frame, no input stall will last more than 8 frames. To see this in action, I changed some code in one of my games so that rest(0) was called every frame, except frames where the mouse variables are the exact same as the last frame. In this case, rest(1) is called instead. This code eliminated the input stalling with the mouse, even when the mouse was in constant motion. (But burning CPU time when input is at idle is hardly an ideal solution.) However, these workarounds don't work simply because the input thread is taking too long to execute, but mostly because the input thread doesn't have a high enough priority! Originally, the keyboard, mouse and joystick handlers in Allegro ran three separate threads, one of which had a priority of 8 (normal), two of which had a priority of 9 (above normal). To put this in perspective, the timer thread has a priority of 15 (time critical) and most of DirectX has a priority of 24 or higher. (As it is set as a real-time process.) The new, merged thread, since 4.1.0, still only has a priority of 9! This means that roughly a third of the CPU time originally passed to three input threads is now being used to handle all three inputs and it's just not enough. Especially when other more important threads from other background processes want some of that. The solution I think would be to either separate the input threads again, or simply make the input thread much higher in priority. (12 or 13 would seem about right. That would put it over most other threads but would still give the timer routines their fair share.) Unfortunately, I have no multi-threaded programming experience and haven't a clue how to test this myself. If anyone who knows how to do this is willing to give this a try I'm willing to test it and see if it solves the problem. --- Kris Asick (Gemini) --- Kris Asick (Gemini) |
|
Evert
Member #794
November 2000
|
Quote: This means that roughly a third of the CPU time originally passed to three input threads is now being used to handle all three inputs and it's just not enough. Ok, that makes sense! Quote: The solution I think would be to either separate the input threads again, or simply make the input thread much higher in priority. I would much prefer the latter. Quote: Unfortunately, I have no multi-threaded programming experience and haven't a clue how to test this myself. Can't you just increase the priority of the thread and recompile Allegro to see if it does what you want? |
|
Kris Asick
Member #1,424
July 2001
|
Quote: Can't you just increase the priority of the thread and recompile Allegro to see if it does what you want? I don't know how and can't find the line of code that sets the priority. EDIT: Granted, I'm sure I could try changing it myself if I knew what to look for. --- Kris Asick (Gemini) --- Kris Asick (Gemini) |
|
Evert
Member #794
November 2000
|
It seems not to be set at all for the input thread. It looks like the input thread is created at or around line 146 of allegro/src/win/winput.c, while the thread priority should be set with SetThreadPriority (eg, SetThreadPriority(input_thread, THREAD_PRIORITY_ABOVE_NORMAL), check MSDN for what options can go there). EDIT: I just grepped for "thread" and "priority" in allegro/src/win. |
|
Kris Asick
Member #1,424
July 2001
|
Quote: It seems not to be set at all for the input thread. You're right! I found out that the window created by Allegro has a higher priority than the input thread! (The input thread is 8, the window thread is 9.) For some reason, my request to change the thread priority for the input thread was ignored when I added the line and compiled Allegro with that change... which is odd since I'm not doing it any differently than anywhere else in the Allegro source where SetThreadPriority() is called. shrugs I'll keep trying.
EDIT: I was right the first time. It's not honoring my priority setting requests for some bizarre reason... EDIT: That line of code you found Evert, the input thread isn't being created there. In fact, it looks as though input is being handled by the dedicated window created by Allegro now, instead of by a separate thread. (IE: That line only gets processed if you attach Allegro to your own window and not have Allegro create it for you.) I'm going to see what happens if I force Allegro to create an input thread even if the dedicated window is active. EDIT: SOLVED!! (But not without side effects...) Here is a particular section of winput.c:
What this code is doing is only creating an input thread if the user opts to attach Allegro to their own window, instead of having Allegro create one for them. (Apparently, the merge of the input threads was not only to merge them into one, but to eliminate the multi-threading nature of it entirely.) To solve this problem, I changed the code as such to force Allegro to always create the input thread:
However, even though this solves the problem, it may have side effects I'm not aware of since it's ignoring the state of need_thread, which would be set elsewhere. There is also one side effect I am aware of: The thread isn't properly shut down when Allegro does since Allegro wasn't trying to create it, causing a page fault when Allegro is exited. Still, it works! Clearing the side effects should be as simple as going through and changing any code that depends on the state of the input thread to assume that the thread is always present. ...should I do that or is that better left to the devs? EDIT: I've tested as much as I can by supplementing the Allegro DLL created with that change into my programs and other people's programs and I can't find anything wrong with the new code... short of the invalid page fault when Allegro exits. I can't figure out why the invalid page fault is happening at all. It's definitely related to the closing of the input thread, but I don't understand why... ---------------------------------------------------------------------------- EDIT: Ok, after hours of testing, here's the situation as best as I can figure:
--- Kris Asick (Gemini) --- Kris Asick (Gemini) |
|
Evert
Member #794
November 2000
|
Quote: ...should I do that or is that better left to the devs?
It's better left to someone who uses Windows. I guess that means you, sorry. |
|
Kris Asick
Member #1,424
July 2001
|
In creating my own input library I ran into the same stuttering problem that's apparent in Allegro. Since my input system is about twenty times simpler than the Allegro one I was able to trace the cause of the input stuttering down... and it isn't anything like what I thought it was... In my testing program for my input system, I decided to check to see if my I/O thread was being stalled or if the input simply wasn't going through. So what I did was I created a counter in my input thread that would continually count up and then my program would display this counter on-screen so I could monitor it whenever a stutter occurred. Now, my first thought was that if the stuttering occurs, the number will stop, then continue. And that is what appeared to be happening at first... until I realized something peculiar was happening with the number. It's like this: Every loop of my I/O thread, the counter increases by 1. There would be roughly 100 increases a second while the I/O was idle and about 200 increases a second while pressing keys or moving the mouse. But when a stall occurred, the loop would increase by several thousand in a split second! So the stuttering has nothing to do with the thread not working and everything to do with the thread eating a giant load of CPU time at random moments! (Which doesn't make sense. My I/O thread calls Sleep(0) every iteration.) And I presume this is also what's happening in Allegro. Adding Sleep(1) into the I/O loop solves the problem, but on certain systems that's going to cause the input to update at a poor rate due to the granularity of the Sleep() statement. So the solution is to find some way to detect when the I/O thread starts executing more than once per CPU timeslice and to call Sleep(1) in that moment to prevent the stutter from happening. That's what I'm going to try to do with my input system and that's what needs to happen in Allegro in order to prevent these stutters. --- Kris Asick (Gemini) --- Kris Asick (Gemini) |
|
BAF
Member #2,981
December 2002
|
I've never noticed any input stuttering. Is it an issue with your Windows or drivers or something? |
|
Kris Asick
Member #1,424
July 2001
|
I doubt it, since it only happens with Allegro-based programs on my system. (And as I've said above, it's not limited to my own programs, and as I also said above, this problem didn't start until Allegro 4.1.0. Until then, there was no stuttering.) Granted, I'm on Windows 98 SE, and am using a computer far more powerful than Windows 98 was ever expected to handle. (2 Ghz, 512 MB RAM, GeForce FX5200 video card...) Anyways, with my input engine I've solved the problem, but my solution relies on the way I've built it and on the way I access the I/O data retrieved, but the solution was essentially as I said: Find a way to detect when the I/O thread hogs the CPU time and call Sleep(1) to force the thread to give up its time so that it doesn't get processed a few thousand times in rapid succession. I suppose there might be some combination of hardware and software drivers I have that's making this happen, but if it can happen to me it could happen to anyone, and as I work with shareware, compatability is of paramount importance to me. EDIT: A side note I should mention: When building the I/O thread for my input system I discovered that changing its thread priority was a really bad idea. If it's higher than the Allegro window thread, the framerate dies. If it's lower, the input drifts as it only gets updated a few times a second. --- Kris Asick (Gemini) --- Kris Asick (Gemini) |
|
BAF
Member #2,981
December 2002
|
Offtopic, but why are you running Windows 98 on that box? You could be running 2k or XP, which is tons more stable. |
|
Kris Asick
Member #1,424
July 2001
|
And in the process eliminate the ability to run half of my software collection. My plan is to skip 2K/XP and go straight to Vista once I have the finances to invest into an entirely new system. --- Kris Asick (Gemini) --- Kris Asick (Gemini) |
|
BAF
Member #2,981
December 2002
|
Quote: And in the process eliminate the ability to run half of my software collection. What do you have that won't run on 2k or XP? DOS games and apps will run fine in DOSBOX. |
|
Kris Asick
Member #1,424
July 2001
|
Quote: What do you have that won't run on 2k or XP? DOS games and apps will run fine in DOSBOX.
I'd rather wait to have a second computer above the 120Mhz mark all the same. --- Kris Asick (Gemini) --- Kris Asick (Gemini) |
|
BAF
Member #2,981
December 2002
|
Oh yeah, I forgot that dosbox won't run well on XP at 120 MHz. |
|
Kris Asick
Member #1,424
July 2001
|
I don't think XP runs at 120 MHz! --- Kris Asick (Gemini) --- Kris Asick (Gemini) |
|
BAF
Member #2,981
December 2002
|
Oh, you're concerned about having a backup computer? What does it matter if your backup computer runs XP, it's not like you can just throw your XP hdd in your spare computer and have it work, unless the hardware is like identical. [edit] |
|
Matthew Leverton
Supreme Loser
January 1999
|
Let's keep it on topic here. |
|
|