PeekMessage() takes 1-2 ms to retrieve WM_MOUSEMOVE messages?
Asked Answered
S

1

8

I've looked at many posts here regarding PeekMessage and WM_MOUSEMOVE, but so far I have not found anything that relates to performance.

Long story short; I'm doing PC game development and yesterday I noticed that I could bring our application from a stable 500+ FPS (in a menu) to single digits FPS (as low as 6), only by moving the mouse around fast.

After a few hours of digging and profiling, I discovered that the source was PeekMessage(). Not DispatchMessage() for that message, just the peek. I measured calling that function alone clocking in at 1-2 ms very frequently (multiple times per frame). If you combine that with fast mouse movement, pumping the messagequeue could cost me upwards of 1000-2000ms per frame rendered. Sounds ridiculous, but I actually downloaded a super basic DirectX sample from MS and tested out the same thing there, with the same results.

The whole sample is too big to paste here, but if you'd like to try, download the sample at: http://code.msdn.microsoft.com/Direct3D-Tutorial-Win32-829979ef

and make the following changes;

typedef unsigned __int64    QWORD;      // 64-bit unsigned.
DOUBLE GSecondsPerCycle;
void appInitTiming(void)
{
    LARGE_INTEGER Frequency;
    QueryPerformanceFrequency(&Frequency);
    GSecondsPerCycle = 1.0 / Frequency.QuadPart;
}

QWORD appCycles()
{
    LARGE_INTEGER Cycles;
    QueryPerformanceCounter(&Cycles);
    return Cycles.QuadPart;
}

int WINAPI wWinMain( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow )
{
    UNREFERENCED_PARAMETER( hPrevInstance );
    UNREFERENCED_PARAMETER( lpCmdLine );

    if( FAILED( InitWindow( hInstance, nCmdShow ) ) )
        return 0;

    if( FAILED( InitDevice() ) )
    {
        CleanupDevice();
        return 0;
    }
    appInitTiming();

    // Main message loop
    MSG msg = {0};
    while( WM_QUIT != msg.message )
    {
        QWORD StartCycles = appCycles();
        BOOL hasMsg = PeekMessage( &msg, nullptr, 0, 0, PM_REMOVE );
        const QWORD DeltaCycles = appCycles() - StartCycles;
        double deltaMS = DeltaCycles * GSecondsPerCycle * 1000.0;
        if (deltaMS > 1/* || msg.message == 512*/)
        {
            std::ostringstream os;          
            os << "SlowPeekMsg ID: " << msg.message << ", time: " << deltaMS << "ms" << std::endl;
            std::string buffer(os.str());
            OutputDebugStringA(buffer.c_str());
        }

        if(hasMsg)
        {
            TranslateMessage( &msg );
            DispatchMessage( &msg );
        }
        else
        {
            Render();
        }
    }

    CleanupDevice();

    return ( int )msg.wParam;
}
Straticulate answered 3/10, 2014 at 19:19 Comment(6)
Games typically don't uses message queues. They have a game loop with a frequent pulse and use async input.Fit
That's not entirely true. A windows game needs to pump the message queue. Maybe not for input, but every game definitely needs to handle messages. There is a reason the directx samples are set up this way.Straticulate
I suppose once you have a window then you have to have a queue. It would be quite nice if we could avoid that, but perhaps it ain't possible.Fit
This is an environmental problem, highly likely to be a windows hook that's spying on the input and a bit slow at processing it. That happens, you made it a problem by putting the Render() call in the else {} branch. Don't do that.Kiely
Not sure what you are suggesting. What should I do then? Again, this is a directx sample from microsoft that I modified, our game handles the message loop completely separately from any rendering, and the problem is the exact same either way. FWIW I took out the else block and called render() either way, and nothing changes. What can I do about (or detect) a windows hook spying on input?Straticulate
I have't seen this particular issue so I don't have an answer, but consider putting your message pump into a separate thread and inserting the events into a queue that is processed from your main thread, and/or try raw mouse input events.Pyridoxine
G
-1

Give this a go:

UNREFERENCED_PARAMETER( hPrevInstance );
UNREFERENCED_PARAMETER( lpCmdLine );

if( FAILED( InitWindow( hInstance, nCmdShow ) ) )
    return 0;

if( FAILED( InitDevice() ) )
{
    CleanupDevice();
    return 0;
}
appInitTiming();

// Main message loop
MSG msg = {0};
while( WM_QUIT != msg.message )
{
    QWORD StartCycles = appCycles();
    while(PeekMessage( &msg, nullptr, 0, 0, PM_REMOVE ))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    const QWORD DeltaCycles = appCycles() - StartCycles;

    double deltaMS = DeltaCycles * GSecondsPerCycle * 1000.0;
    if (deltaMS > 1/* || msg.message == 512*/)
    {
        std::ostringstream os;          
        os << "SlowPeekMsg ID: " << msg.message << ", time: " << deltaMS << "ms" << std::endl;
        std::string buffer(os.str());
        OutputDebugStringA(buffer.c_str());
    }

    Render();
}

CleanupDevice();

return ( int )msg.wParam;

It might just be something wack like a hook like a commenter mentioned above.

Gillum answered 23/8, 2015 at 20:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.