Advertisement

SDL Mouse Movement High CPU Usage

Started by July 18, 2017 01:12 AM
8 comments, last by Kylotan 7 years, 6 months ago

Hi, I am writing a Windows multithreaded SDL 1.2 application, and am trying to fix a performance bug by process of elimination (graphics thread only wakes up once every 5 seconds for now). The bug is that when I move the mouse in circles, everything is fine for the first 50 seconds (smooth mouse movement and low CPU usage), and then the CPU usage spikes and the mouse starts to lag and skip rather than move smoothly. I am taking mouse input in the input thread as follows:

while (SDL_WaitEvent(g_pEventData))
{

...

SDL_Delay(10);//this was the recommendation i saw when researching perf issues

}

 

Windows Performance Analyzer tells me theres a large amount of CPU usage taken by KernelBase.dll!SleepEx which I THINK is called internally from SDL_WaitEvent in the loop above. Does anyone have an idea why the CPU usage suddenly spikes and performance lags after 50 seconds although I'm making the same circular mouse movements the whole time?

 

Thanks.

 

 

Capture.thumb.JPG.cf3a0fdfdb07895e9270a980ec507927.JPG

Graph.JPG

SDL_Delay is a sleep.

 

Additionally, without seeing the rest of your loop, it's hard to see what else you're doing that could be wrong. For instance, you should process all outstanding events before moving on to your game update. Alternatively, spin up another thread to handle input.

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

Advertisement

Thanks for the response, Washu. That loop _IS_ its own thread and runs until it gets sdl_quit. The loop predicate (SDL_WaitEvent) blocks, and if it internally calls sleep, that explains the time spent in KernelBase.dll!SleepEx. However, it doesn't explain why the copy is busy in the graph. My understanding is that Windows schedules other threads when Sleep() is called.

As I said in my original post, to isolate the bug, I made all the other threads (such as the graphics thread) wake up every 5 seconds, so I can be sure it's not the one consuming CPU. There's no updates happening in that loop--only input being read and saved to a global variable for the graphics thread to process.

After further research, I found this question here:
https://stackoverflow.com/questions/6095760/cpu-usage-doesnt-drop-during-sleepex
...where Lyubomyu says he has seen this issue before. Are SDL applications subject to the restrictions listed in MSDN's Sleep documentation https://msdn.microsoft.com/en-us/library/windows/desktop/ms686298(v=vs.85).aspx that they should not be calling Sleep()? The above SDL_WaitEvent()/SDL_Delay() loop is called from my "int main(int argc, char* argv[])" function, which makes it look like a console application. However, my application is linked with /SUBSYSTEM:WINDOWS and I think SDL requires some tricks where it might be a Windows application even though the main signature looks like that of a console application (see emartel's answer https://stackoverflow.com/questions/13555249/hiding-the-console-window-from-sdl).

Another thing I noticed is that when the CPU usage is high and I let go of the mouse and let the application idle for some time, CPU usage drops substantially and for some time, further mouse movement don't cause high CPU, but after prolonged movement, it causes a CPU usage spike again.

You didnt show enough code, but you might be flooding the event queue and only check for 1 event every 10ms.

 

That's exactly what it currently does, which is a bad idea. There should be no Sleep call in there at all. The SDL_WaitEvent call already sleeps when there's nothing to do.

Take that Sleep out, and see what the situation is.

Advertisement

Ok, heres the code after following your directions:

    while (SDL_WaitEvent(g_pEventData))
    {
        //printf("sdl_refresh(): SDL_PollEvent loop. g_pEventData->type=%d eventCount=%u\n", g_pEventData->type, eventCount);
        eventCount--;
        if(g_pEventData->type == SDL_QUIT)
        {
            g_pEventData = NULL;
            /*
             *    SDL system shutdown request. Send to event channel.
             */
            SDL_QuitSubSystem(SDL_INIT_VIDEO);
            return FALSE;
        }
        //nonblocking notification to outside world of event goes here regardless of event type.
        if (ERROR_SUCCESS != dwRetVal)
        {
            LOG("sdl_refresh: Notify failed with error(%d).\n",
                dwRetVal);
        }
        switch (g_pEventData->type)
        {
        case SDL_VIDEOEXPOSE:
            sdl_update();
            break;

        case SDL_VIDEORESIZE:
            boRetval = sdl_scale(g_pEventData->resize.w, g_pEventData->resize.h);
            if (!boRetval)
            {
                return FALSE;
            }
            break;

        case SDL_MOUSEBUTTONDOWN:
        case SDL_MOUSEBUTTONUP:
            /*
             * Sleep is to allow event handling to sync.
             * Without this sleep the Mouse scroll does not work.
             * This sleep gives that extra 5 milliseconds to Source
             * to process the Mouse Scroll event, before it is overwritten
             * by next event, which happens quite fast in case of
             * scroll.
             */
            Sleep(5);
            break;
        }
        //SDL_Delay(10);
    }

Even with the change, I still see the performance issue. Now that I think about it, the behavior matches what wintertime and Kylotan said of a queue getting full. When I move the mouse in circles, I'm generating a lot of MOUSE_MOVEMENT events, and after a few seconds of doing that I see the lagging perf issue, which behaves like a buffer getting full and using high CPU to process all the events. After I stop moving the mouse, it seems like the queue might be getting drained, and the perf goes back to normal again. But we should also keep in mind that I'm using SDL_WaitEvent rather than SDL_PollEvent, and that this loop is on the main thread (called from main).

Heres where I got the recommendation to call SDL_Delay():
http://www.sdltutorials.com/huh-my-pong-clone-uses-100-percent-cpu-and-is-still-slow
https://forums.libsdl.org/viewtopic.php?p=11173

 

I see more sleeps in there, that probably shouldn't be there. If you have an issue where you're not properly handling mouse up and down, introducing a sleep is not the way to fix it.

 

Additionally, looking at the SDL documentation, SDL_WaitEvent should be called on the thread that initialized the graphics subsystem and not some other thread.

 

Regarding mouse move events... the chances of you being able to move your mouse fast enough that any decent plain old event handling loop cannot handle them is highly unlikely.

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

15 hours ago, neilthereildeil said:

            /*
             * Sleep is to allow event handling to sync.
             * Without this sleep the Mouse scroll does not work.
             * This sleep gives that extra 5 milliseconds to Source
             * to process the Mouse Scroll event, before it is overwritten
             * by next event, which happens quite fast in case of
             * scroll.
             */

No no no no no no no...

You should not be 'overwriting' events! There should be no 'g_pEventData' which is shared and continually overwritten. Each event pulled from SDL_WaitEvent should be written into a local variable, and handled immediately if possible. If that is not possible, perhaps because you're expecting to use the event in a different thread, you must copy the event and pass it to the thread that needs it.

If you're trying to make multithreading work by throwing Sleeps in so that some other thread has enough time to work with shared data before you overwrite it, you're doing it completely wrong. You can't create a stable system that way.

Take out the Sleep, take out the SDL_Delay. Handle these events immediately, properly. Your lag will go away.

(Also, where is the mouse movement code? You keep talking about that being the cause of the problem but you're not even handling those events in the code you're posting.)

This topic is closed to new replies.

Advertisement