• Advertisement
Sign in to follow this  

WM_INPUT polling rate

This topic is 2079 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm playing around with WM_INPUT, but am getting an issue where I can't drag or resize my window on my desktop computer (it's fine in a VM or on my laptop). It looks as if my app gets flooded with WM_INPUT messages for a while after I move my mouse, and then stops, and during that flood I can't drag/resize and I get the Windows loading spinner icon. Setting the polling rate of my Logitech G5 in SetPoint to 1000Hz makes it worse; setting it to 125Hz isn't too bad but it's still there. Note: I tried my mouse on my laptop too, but since I don't have SetPoint installed on it, it doesn't exhibit this behaviour.

Should I be handling WM_INPUT separately to the rest of my app? I've got some basic code which shows this problem when you have a high-frequency mouse:


#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <stdio.h>
#include "console.h"

HWND hWnd;
HDC hDC;
HGLRC hRC;

// Set up pixel format for graphics initialization
void SetupPixelFormat()
{
PIXELFORMATDESCRIPTOR pfd, *ppfd;
int pixelformat;
pfd = &pfd;
ppfd->nSize = sizeof(PIXELFORMATDESCRIPTOR);
ppfd->nVersion = 1;
ppfd->dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
ppfd->dwLayerMask = PFD_MAIN_PLANE;
ppfd->iPixelType = PFD_TYPE_COLORINDEX;
ppfd->cColorBits = 16;
ppfd->cDepthBits = 16;
ppfd->cAccumBits = 0;
ppfd->cStencilBits = 0;
pixelformat = ChoosePixelFormat(hDC, ppfd);
SetPixelFormat(hDC, pixelformat, ppfd);
}

// Initialize OpenGL graphics
void InitGraphics()
{
hDC = GetDC(hWnd);
SetupPixelFormat();
hRC = wglCreateContext(hDC);
wglMakeCurrent(hDC, hRC);
glClearColor(0, 0, 0, 0.5);
glClearDepth(1.0);
glEnable(GL_DEPTH_TEST);
}

// Resize graphics to fit window
void ResizeGraphics()
{
// Get new window size
RECT rect;
int width, height;
GLfloat aspect;
GetClientRect(hWnd, &rect);
width = rect.right;
height = rect.bottom;
aspect = (GLfloat)width / height;
// Adjust graphics to window size
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0, aspect, 1.0, 100.0);
glMatrixMode(GL_MODELVIEW);
}

// Draw frame
void DrawGraphics()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Set location in front of camera
glLoadIdentity();
glTranslated(0, 0, -10);
// Draw a square
glBegin(GL_QUADS);
glColor3d(1, 0, 0);
glVertex3d(-2, 2, 0);
glVertex3d(2, 2, 0);
glVertex3d(2, -2, 0);
glVertex3d(-2, -2, 0);
glEnd();
// Show the new scene
SwapBuffers(hDC);
}

// Handle window events and messages
LONG WINAPI MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_MOUSEMOVE:
//printf("\nMouse Move message received\n");
return 0;
break;

case WM_INPUT:
// printf("\nInput message received %d", rand());
return 0;
break;

case WM_SIZE:
ResizeGraphics();
break;

case WM_CLOSE:
DestroyWindow(hWnd);
break;

case WM_DESTROY:
PostQuitMessage(0);
break;

// Default event handler
default:
return DefWindowProc (hWnd, uMsg, wParam, lParam);
break;
}

return 1;
}

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
SetStdOutToNewConsole();
const LPCWSTR appname = TEXT("OpenGL Sample");
WNDCLASS wndclass;
MSG msg;

// Define the window class
wndclass.style = 0;
wndclass.lpfnWndProc = (WNDPROC)MainWndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(hInstance, appname);
wndclass.hCursor = LoadCursor(NULL,IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wndclass.lpszMenuName = appname;
wndclass.lpszClassName = appname;

// Register the window class
if (!RegisterClass(&wndclass)) return FALSE;

//Calculate x and y coords of where window should be
int dwWidth = GetSystemMetrics(SM_CXSCREEN);
int dwHeight = GetSystemMetrics(SM_CYSCREEN);
dwWidth /= 2;
dwWidth -= (800/2);
dwHeight /= 2;
dwHeight -= (600/2);
// Create the window
hWnd = CreateWindow(
appname,
appname,
WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
dwWidth,
dwHeight,
800,
600,
NULL,
NULL,
hInstance,
NULL);

if (!hWnd) return FALSE;

RAWINPUTDEVICE Rid[1];
Rid[0].usUsagePage = 0x01;
Rid[0].usUsage = 0x02;
Rid[0].dwFlags = RIDEV_INPUTSINK;
Rid[0].hwndTarget = hWnd;
RegisterRawInputDevices(Rid, 1, sizeof(Rid[0]));

// Initialize OpenGL
InitGraphics();

// Display the window
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);

// Event loop
while (1)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE) == TRUE)
{
if (!GetMessage(&msg, NULL, 0, 0)) return TRUE;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
DrawGraphics();
}
wglDeleteContext(hRC);
ReleaseDC(hWnd, hDC);
}

Share this post


Link to post
Share on other sites
Advertisement
printf() is a pretty heavy-duty thing to do in an input message handler. If you replace that call with something simpler, like incrementing a counter, does the problem continue?

Share this post


Link to post
Share on other sites
Yep, tried commenting the printf() statements out and still get the same behaviour.

Share this post


Link to post
Share on other sites
You should also comment out the printf on your WM_MOUSEMOVE handler, as that is likely getting called too.

Check the documentation for WM_INPUT (and any other message you handle) as well - the normal convention is to return 0 from your WndProc if you handle them yourself (a quick check of WM_INPUT confirms this - "If an application processes this message, it should return zero"), but you're returning 1. Beware though as there are some messages that require you to return non-zero, so be certain that you assume nothing here.

Share this post


Link to post
Share on other sites
Made those changes (commented both printf statements, and returned 0 after both WM_MOUSEMOVE and WM_INPUT) but the problem persists. Just to note, when printing statements, on the laptop when moving the mouse I'd generally get 2-3 WM_INPUT messages then a WM_MOUSEMOVE. On my desktop I get a huge amount of WM_INPUTS with a rare WM_MOUSEMOVE inbetween.

Share this post


Link to post
Share on other sites
Could just be that your drivers are doing something evil; you mentioned the polling rate affects this. Does using that mouse on other computers replicate the problem?

Share this post


Link to post
Share on other sites
Figured it out, it's because I was using an if statement with PeekMessage rather than a while loop so only one message was getting processed per game loop (I should have put this in the Game Programming section, and should have also read up on the Win32 message system, sorry!) I guess the polling rate on the other computers was set too low even though I used the same mouse.

This post helped:

http://www.gamedev.n...vs-peekmessage/

I have another question: I see there being 2 ways of capturing input - either you can call a function every game loop iteration which checks the current keyboard/mouse button/mouse pointer inputs, or you can use the message system, receive messages independently to your main game loop, then process what you've captured every game loop iteration. What is the preferred way of doing this? Bear in mind I'll be using raw mouse input for accuracy, so would it be better to go with the latter, buffer the raw data and then during each game loop iteration just combine all the data in the buffer into a single set of values that can be used in my game?

Share this post


Link to post
Share on other sites
I tend to prefer buffered messages over polling, mainly because it makes it harder to "miss" inputs that occur between ticks when you're busy doing other things.

Share this post


Link to post
Share on other sites
The way I understand it is you can:

- Use WM_INPUT to receive every single keyboard/mouse event individually, where you'd get the data for each event using GetRawInputData, update your own key array/mouse pointer structs every time you get that message, then when it comes to your game loop use the latest data, OR

- You poll it manually every game loop using GetRawInputBuffer, which gives you a buffer of inputs, which you'd process in one go, update your own key array/mouse pointer structs and use in that game loop.

So either way there shouldn't be any missed inputs, in fact the second method sounds nicer to me as there would be less function calls and messages being passed to WndProc, but I may be missing something so am wondering which method is generally used, and is "nicer" :)

Share this post


Link to post
Share on other sites
The preferred way is as suggested by ApochPiQ. You must, and I repeat absolutely must use events to handle input. Have you ever played a game that forgot to fire even though you clearly hit the fire button? Only via polling can this happen. And it is extremely irritating to any human when it happens.
Listening to events is the only way to ensure this never happens.
And only by listening to events on a second thread is this guaranteed to work as promised. Smooth mouse movements require this, and there is some work involved in making it happen correctly, but your players will appreciate it.
In either case, even if you do not use a second thread for game logic and rendering, you still must absolutely listen to events and base your input functionality off those timings.

My frame took 2 seconds to draw. Did the player input last for 2 seconds or did it last for only 0.025 seconds? He or she gently tapped the Up direction, and certainly not for 2 seconds. This is a fairly important question to answer, and makes a huge world of a difference to the player.

Polling is absolutely unacceptable in the world of game development. But simple event-handling is not either. The solution you seek requires a lot more complexity.
Every input event should have a timestamp and some kind of input manager (one for mouse, one for touch, one for keyboard) should be able to present this data in the way the player originally intended it. With proper spacing between events etc.


L. Spiro

Share this post


Link to post
Share on other sites

The preferred way is as suggested by ApochPiQ. You must, and I repeat absolutely must use events to handle input. Have you ever played a game that forgot to fire even though you clearly hit the fire button? Only via polling can this happen. And it is extremely irritating to any human when it happens.
Listening to events is the only way to ensure this never happens.
And only by listening to events on a second thread is this guaranteed to work as promised. Smooth mouse movements require this, and there is some work involved in making it happen correctly, but your players will appreciate it.


This is interesting. Thus far I've always let the input reading(via polling or events) be done in the same thread as the game loop. But I see the issues with this and have encountered many of them in the past, though I've never considered multithreading the input as an option.

Now, if we disregard polling, should it be done by running your message loop in a separate thread or the way DirectInput does it, with a hidden window that only handles input messages? My guess is the first option, but I may have overlooked something, like a third option, hence the question.

Share this post


Link to post
Share on other sites
When I said polling, I mentioned using GetRawInputBuffer so that even if I were only checking for inputs during a render loop, I'd still get all inputs since the last as I'd be getting an array of events. However, I was going to post another question, and what you're saying about a proper event manager with timestamps pretty much answered it.

I was considering the case where between frames, the user taps forward, then taps left, then releases. I'm guessing I should treat all of these events individually - when the user taps forward, check if they collide with anything e.t.c, then again when they tap left e.t.c., rather than combining these movements and testing at a certain increment. The timestep would be used to correctly space tests and game logic out relative to the input events, so I see the benefit in message handling now over polling, even if I were to get a whole array of buffered inputs (don't think they're timestamped). I've never separated game logic, rendering, and event handling before, as I've only developed homebrew games for consoles where 60fps was pretty much guaranteed, so do you have any good resources or tutorials to help me get thinking about the right way to do it?

P.S. I took a look at your blog, and funnily enough I came across it a few days ago on Google and have been meaning to read that "Fix your timestep" article you linked to.

Share this post


Link to post
Share on other sites

Now, if we disregard polling, should it be done by running your message loop in a separate thread or the way DirectInput does it, with a hidden window that only handles input messages? My guess is the first option, but I may have overlooked something, like a third option, hence the question.

Windows requires that the message loop be the same thread that created the window, so the option I would suggest is that the main thread handles input, and #2 be game/rendering, or #2 be game and #3 be rendering. Of course making the render thread its own thread is an entirely different topic and if you don’t need that kind of performance then don’t try it.


The timestep would be used to correctly space tests and game logic out relative to the input events, so I see the benefit in message handling now over polling, even if I were to get a whole array of buffered inputs (don't think they're timestamped). I've never separated game logic, rendering, and event handling before, as I've only developed homebrew games for consoles where 60fps was pretty much guaranteed, so do you have any good resources or tutorials to help me get thinking about the right way to do it?

Unfortunately I do not, but this is something I might consider for an upcoming tutorial.
Without being able to give you any further resources for studying up on this, I can just suggest that you think about it for a while on your own before implementing it. In everything I do there is always this thought process beforehand where I try to visualize what needs to be addressed and what implementation will address it fully without creating side-effects in any other subsystems. It seems as though you have understood what needs to be addressed so just ponder for a while how to properly implement it.

I also try to look past my current needs and think about what the future might hold and also consider all kinds of variants that have been noticed in games I have played and try to think of an implementation that could handle all of those variants. And don’t feel rushed on this. Most things take me days, some complicated things take me months.


L. Spiro

Share this post


Link to post
Share on other sites
Sorry for bringing up a somewhat old thread...

Wouldn't it be appropriate to use the windows message queue on the same thread as your game logic/rendering, and just utilize the MSG::time parameter to determine when to apply user input to the game simulation?

Share this post


Link to post
Share on other sites

Sorry for bringing up a somewhat old thread...

Wouldn't it be appropriate to use the windows message queue on the same thread as your game logic/rendering, and just utilize the MSG::time parameter to determine when to apply user input to the game simulation?

UI messages need to be handled on the thread that created the Window. This is a requirement of windows.

The actual issue with the code has to do with the message loop, which insists on drawing each and every time through. Thus only a single message is ever processed per iteration. The correct message loop syntax should only operate during the IDLE time, all windows messages have been processed. This is typically done with a loop that looks something like:
while(true) {
if(PeekMessage) {
Do stuff with the windows message
} else {
Do your game processing stuff here
}
}
Edited by Washu

Share this post


Link to post
Share on other sites
I'm aware the win32 message pump needs to be handled on the same thread as the window. I'm curious why to separate input and game logic/rendering to two threads. Why can't we do this to process all messages instead of just one?

while(true) {
while(PeekMessage) {
Do stuff with the windows message
}
Do your game processing stuff here
}


All messages are processed before stepping forward the game sim. During your gamelogic update, more messages will be queue'd up by Windows.

The messages from PeekMessage can be put into a queue and stamped with MSG::time parameter. Then those messages can be used to determine when to apply inputs to your fixed time step game logic.

Are there any problems with this approach?

EDIT: I suppose using that 'while loop' could indefinitely keep you from processing and rendering the scene. i.e. Constant mouse movement. Therefore... two threads are needed to give gamelogic updates/renders and input handling a chance to run. Edited by web383

Share this post


Link to post
Share on other sites

I'm aware the win32 message pump needs to be handled on the same thread as the window. I'm curious why to separate input and game logic/rendering to two threads. Why can't we do this to process all messages instead of just one?

while(true) {
while(PeekMessage) {
Do stuff with the windows message
}
Do your game processing stuff here
}


All messages are processed before stepping forward the game sim. During your gamelogic update, more messages will be queue'd up by Windows.

Check again. My loop processes all messages before rendering.

The difference between the two is actually just one simple thing: Exit condition handling. In the while/if version you can simple call "break" to exit the message loop. In the double loop version you have to do more work, specifically you need a boolean variable to determine when to end the message processing/break out of the game loop:

bool run = true;
while(run) {
while(PeekMessage) {
if quit message:
run = false;
}
if(run) {
Do game stuff
}
}



The messages from PeekMessage can be put into a queue and stamped with MSG::time parameter. Then those messages can be used to determine when to apply inputs to your fixed time step game logic.

Are there any problems with this approach?
[/quote]
Not really, but its kind of silly. Just use rawinput and handle it in thread. It's very very very minor where you pickup the input. Using another thread just for input is stupid and wasteful.

EDIT: I suppose using that 'while loop' could indefinitely keep you from processing and rendering the scene. i.e. Constant mouse movement. Therefore... two threads are needed to give gamelogic updates/renders and input handling a chance to run.
[/quote]
No. How do you think games worked before the "magical threading" craze.

Share this post


Link to post
Share on other sites
Check again. My loop processes all messages before rendering.[/quote]
Sorry, misread that code. You are correct.

I guess I'm mostly interested in knowing why input should be handled on a separate thread. I'm working on a deterministic game simulation with fixed time increments, and I want my input to be applied at the correct times in the simulation. I'm planning on using raw input via WM_INPUT msg, instead of WM_KEYDOWN, WM_KEYUP, etc, etc.

L. Spiro says the proper way is to thread the input. You say it's wasteful. I suppose I can continue implementing and make a decision for myself. Thanks for all your help :)

Share this post


Link to post
Share on other sites
The only reason to thread message loop (and by extension, input) is to avoid the blocking nature of the message handling. Ie DefWindowProc is a, potentially, blocking call (for example, dragging a window makes it block until you stop dragging it). If main thread stopping for a long (several hundred frames for example) is not a problem - then there is no reason i am aware of to bother threading it.

One needs (or might want) to record the input with a low level keyboard proc anyway (to allow using all the keys for game purposes: for example the "Win" key etc) and it, iirc, has sufficiently precise message timecodes (not relevant for me atm, so, not actually using thous).

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement