Just wanted to check: what are the messages that cause the problem, exactly? If it’s only WM_MOUSEMOVE and WM_NCMOUSEMOVE, then it’s important to note that they do not, in fact, become real messages[1] until you GetMessage or PeekMessage:

> When the hardware mouse reports an interrupt, indicating that the physical mouse has moved, Windows determines which thread should receive the mouse move message and sets a flag on that thread’s input queue that says, “The mouse moved, in case anybody cares.” (Other stuff happens, too, which we will ignore here for now. In particular, if a mouse button event arrives, a lot of bookkeeping happens to preserve the virtual input state.)

> When that thread calls a message retrieval function like GetMessage, and the “The mouse moved” flag is set, Windows inspects the mouse position and does the work that is commonly considered to be part of mouse movement: Determining the window that should receive the message, changing the cursor, and determining what type of message to generate (usually WM_MOUSEMOVE or perhaps WM_NCMOUSEMOVE).

The book version of Raymond Chen’s blog[2] elaborates on the subject of messages arriving in an empty forest:

> One of [the] special messages is the WM_MOUSEMOVE message. As you saw earlier, the message is not added as an input message when the mouse moves; instead, a “the mouse moved” flag is set. When the window manager goes looking for an input message and the “the mouse moved” flag is set, it clears the flag and generates a WM_MOUSEMOVE message on the fly, adding it to the list of input messages (or coalescing it with an existing WM_MOUSEMOVE message).

> Other special messages that fall into this “generated on the fly” category are the WM_PAINT, WM_TIMER and WM_QUIT messages. The first two messages are generated even later in the message search process, only after no applicable input message list was found, and only if the message filter indicates that that type of message is being requested. (The WM_QUIT message is even shier than the paint and timer messages, because it emerges only after the posted message list is empty. On the other hand, the WM_QUIT message is also bolder, in that it ignores the message filter.)

Keeping that in mind, using PeekMessage() and thus materializing the pending WM_MOUSEMOVE should be done very carefully (though with PM_REMOVE it shouldn’t be a problem). In fact, it seems like unless you care about WM_TIMER, WM_PAINT, or WM_QUIT, the right thing to do upon seeing a WM_MOUSEMOVE (but not a WM_NCMOUSEMOVE) would be to behave as though the message queue is currently empty and proceed to do whatever other processing you need to perform for the current frame.

Still, I don’t get why this should be a problem. If you receive a WM_MOUSEMOVE and your only action is to discard it, surely it doesn’t take 125 μs to do that? And if it doesn’t, you shouldn’t see more WM_MOUSEMOVEs that can only get generated at 8 kHz at most.

Suggested experiment because I don’t have a fancy mouse: if you insert

if (msg.hwnd == <root window> && msg.message == WM_MOUSEMOVE) continue;

before the TranslateMessage call, will the stutter disappear? I know that above I proposed break instead of continue, but continue could be less disruptive to other special messages—if it works.

[1]: https://devblogs.microsoft.com/oldnewthing/20031001-00/?p=42...

[2] https://openlibrary.org/works/OL9256722W