Sign in to follow this  
cozzie

Moving from dinput to RAW input (mouse)

Recommended Posts

Hi,

I've been working on getting rid of DirectInput for handling keyboard and mouse input.

On the keyboard side I'm all good, but my current RAW mouse implementation (1st attempt) has two strange symptons:

 

1 - terrible 'lag' in movement, like it's 'dragging along' after the mouse physically stopped moving

2 - interference with keyboard input

 

Here's the code I use.

Any input is really appreciated.

// setting up the mouse
bool CInputHandler::SetupMouseRaw(const HWND pHwnd)
{
	Rid[0].usUsagePage = HID_USAGE_PAGE_GENERIC; 
    Rid[0].usUsage = HID_USAGE_GENERIC_MOUSE; 
  
  Rid[0].dwFlags = RIDEV_CAPTUREMOUSE | RIDEV_INPUTSINK | RIDEV_NOLEGACY
    Rid[0].hwndTarget = pHwnd;
    RegisterRawInputDevices(Rid, 1, sizeof(Rid[0]));

	mInitialized = true;
	return true;
}

// updating the mouse
void CInputHandler::UpdateMouse(const int pPosX, const int pPosY)
{
	mMousePosX = pPosX;
	mMousePosY = pPosY;
	mMouseMoved = true;
}	

// windows messaging (WNDPROC)
		// MOUSE INPUT: RAW
		case WM_INPUT:
		{
	        UINT dwSize = 40;
		    static BYTE lpb[40];
    
			GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER));
    
			RAWINPUT* raw = (RAWINPUT*)lpb;
    
			if(raw->header.dwType == RIM_TYPEMOUSE)
			{
				_input.UpdateMouse(raw->data.mouse.lLastX, raw->data.mouse.lLastY);
			}
		}
		break;

// handling game input
			if(_input.MouseMoved())
			{
				_d3dcam.FreeLook(_input.GetMousePosX(), _input.GetMousePosY(), _player.GetLookSpeed()); // * _timer.GetDelta());
				_input.SetMouseMoved(false);
			}

Edited by cozzie

Share this post


Link to post
Share on other sites

1 - terrible 'lag' in movement, like it's 'dragging along' after the mouse physically stopped moving

This could happen when you re-draw & present everything after every message from your message loop, with VSYNC on. Because Present waits for VSYNC, it causes your whole message loop (including raw input messages) to be delayed every frame.

 

Fiddler's recommendation should fix this...

Edited by tonemgub

Share this post


Link to post
Share on other sites

Hi all. Thanks for the input.

 

- I've tried to use just the flag 'RIDEV_NOLEGACY',  but unfortunately no difference

- WM_MOUSEMOVE I tried, but didn't work out because I'm trying to come close to using DirectInput in exclusive mode (also with the cursor outside the render window)

- my main message loop is:

	PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
	while(msg.message != WM_QUIT)
	{
		if(PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))	
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}

I tried to change it to a 'while' instead for 'for'.

That solved most of the problems! Great.

 

Can someone explain what exactly happens there and what the cons are of doing it with a while loop?
(will this cause delay when there are no messages, since if waits?)

 

The only issue left if that there's some strange interference left with the keyboard input. Will look further into that (some testing with the flags etc.)

 

Add; flags don't change things, but I did notice that without V-sync mouse 'spreed' is much better then with V-sync. Do I need to make that speed dependent on V-sync, or are there better options?

 

On the left 'interference' it's really strange, I only get it with the 'VK_ESCAPE' key, all other input is OK. When I change back the the message loop to a 'if' instead of 'while' loop, the VK_ESCAPE reaction is back. Strange?

Edited by cozzie

Share this post


Link to post
Share on other sites

I personally never bother with Windows messages and use the API functions directly. Look up GetCursorPos() for mouse movement and GetAsyncKeyState() for keypresses. These will give you immediate values which you can process at your own will.

Share this post


Link to post
Share on other sites

 

I personally never bother with Windows messages and use the API functions directly. Look up GetCursorPos() for mouse movement and GetAsyncKeyState() for keypresses. These will give you immediate values which you can process at your own will.

 

GetAsyncKeyState() can miss key presses when the framerate is low or the key was pressed very quickly.

 

Usually that is not a problem in games since most of the time keys will be held down for some longer period. For example, when moving, you'll just want to know if key X is down or not, key-up is often not even relevant. You'd also have to have extremely low framerate to actually press-and-release in between frames.

For text input and such, it's a totally different scenario.

Share this post


Link to post
Share on other sites

Yet the OP has to open a topic on how to get it working correctly, where two simple function calls can also do the job. The main problem is that the Windows message pump was designed for event based processing, which is perpendicular to a frame-oriented approach such as used by games. This problem has passed these forums more than once.

 

But as always, to each their own. I'm just posing an alternative.

Share this post


Link to post
Share on other sites

Clear, so I have a few options;

- handle input through messages, using the 'while peekmessage' way, with the con that I need to make delta/ speed different with and without V-sync.

- handle input directly/ manual by get key states and mouse data, not sure how to do this though with RAW input without windows messages

 

I'm using direct3d windowed for debugging, and fullscreen for non-debugging.

 

@Jwezorek: what do you mean with dumping directinput is error prone?

 

My goal is actually to get the same functional result as with directinput, but without using the deprecated directinput API smile.png

With WM_MOUSEMOVE I'm only running into an issue in windowed mode, when the cursor goes outside the window.

 

EDIT: I solved the 'ESC' issue, wasn't posting the quit message in the windowproc but in my function handling the actual input.

Solved now. Only thing left is getting mouselook speed the same with and without V-sync.

 

Before I did:

_d3dcam.FreeLook(_input.GetMousePosX(), _input.GetMousePosY(), _player.GetLookSpeed());

I tried this, but no luck:

_d3dcam.FreeLook(_input.GetMousePosX(), _input.GetMousePosY(), _player.GetLookSpeed() * _timer.GetDelta());

The effect is 'slower' reaction with V-sync turned on.

I managed to get 'look speed' the same for with/ without V-sync, by moving this line to the message handler (windowproc), instead of my input handler function, which is run 'per frame', and not per message.

Edited by cozzie

Share this post


Link to post
Share on other sites

With WM_MOUSEMOVE I'm only running into an issue in windowed mode, when the cursor goes outside the window


You can continue to receive mouse messages when your cursor is outside the window by calling SetCapture(HWND) I believe. Using raw input just to avoid this issue is probably the wrong approach.

Share this post


Link to post
Share on other sites

The effect is 'slower' reaction with V-sync turned on.

I managed to get 'look speed' the same for with/ without V-sync, by moving this line to the message handler (windowproc), instead of my input handler function, which is run 'per frame', and not per message.

You should handle logic updates (such as camera position) at fixed update rate, not per message. Handling updates per message will result in the issue you encountered (different update rates with/without vsync, also different update rates depending on the mouse model, CPU, operating system, etc.)

 

If your application is sensitive to input latency, you should move either rendering or input handling to a separate thread. The latter is quite simple: create a message-only window on a background thread and use that to capture raw input (Hint: use GetMessage instead of PeekMessage on the background window to reduce its performance impact.)

Share this post


Link to post
Share on other sites

You could also try using GetMessageTime instead of current time to compute delta-time, to alleviate the problem - it is supposed to return the time when the message was added to the thread queue, so even if you do something that delays the message-queue (like VSYNC), you can still process the (possibly delayed) input message as though it happened at the time the input event was fired (or at least the closest you can get to it). Although I can't vouch for it yet, as I still have to try this myself.

 

..in addition to using PeekMessage in a loop, of course.

Edited by tonemgub

Share this post


Link to post
Share on other sites

With WM_MOUSEMOVE I'm only running into an issue in windowed mode, when the cursor goes outside the window


You can continue to receive mouse messages when your cursor is outside the window by calling SetCapture(HWND) I believe. Using raw input just to avoid this issue is probably the wrong approach.


Raw input is actually good if you're doing something like mouselook where you (usually) don't want the standard Windows cursor acceleration applied. Otherwise, if you're displaying an on-screen cursor (usually cause you're in a menu or you're making an RTS or something) you should use WM_MOUSEMOVE so your cursor behaves like the Windows cursor and allow the user to click outside - or at the very least give them the option to constrain the cursor to your window or not (i.e. edge scrolling in a RTS is a pain if the mouse can escape the window).

In cases where I've done this kind of thing before, my input system can handle both messages and raw input, and toggles between the two depending on whether I'm showing a cursor or not.

SetCapture will give you messages outside your window, but will not keep the cursor from leaving. It's intended for thinks like drag-and-drop where you need to know when the user releases the mouse button even when they leave your window.

You might be thinking of ClipCursor which restrains the cursor to a rectangular area without invoking the raw input API. Of course, you'll want to un-clip the cursor if you lose focus. Edited by SmkViper

Share this post


Link to post
Share on other sites


You can continue to receive mouse messages when your cursor is outside the window by calling SetCapture(HWND) I believe.

 

Hmm. According to the docs:
 

If the mouse cursor is over a window created by another thread, the system will direct mouse input to the specified window only if a mouse button is down.

Share this post


Link to post
Share on other sites

Thanks, I tried it and indeed didn't work. That is, strange symptons :)

 

I'm still struggling a bit on the choice of RAW input vs. MOUSEMOUSE/ windows messages.

Even though it's a FPS/ camera look, it seems that WM_MOUSEMOVE feels accurate enough.

But the main issue is the cursor going outside the window, I don't have that problem with RAW input.

Share this post


Link to post
Share on other sites

For FPS camera mouse movement and with an existing program I'd honestly just continue using DirectInput.

 

Now, before you all dogpile the -1s, despite Microsoft's advice to not use it, despite the fact that it's no longer updated, and despite the fact that it's just an emulaion layer over raw input these days, all of which are accepted caveats, there are several advantages to doing so:

  • No pointer ballistics: this is exactly what you want with this kind of camera.
  • The mouse is properly clipped to the window.
  • Showing/hiding the cursor is handled automatically for you.
  • You don't have to create another window and run raw input in a separate thread to get the smoothest movement.
  • It's better documented and with more examples available (just wait till you see the MSDN example for GetRawInputBuffer).
  • By the time you write all the extra code to handle the stuff that DirectInput automatically does for you, both raw input and Windows API messages no longer look as simple.

You've already got working code that does exactly what you want the way you want it, so theoretical rather than practical grounds is a bad case for replacing it.  DirectInput is

going to continue to exist for a long time: it's not going to suddenly stop working just because it's no longer being updated.  You can switch to RawInput for the keyboard but continue using DirectInput for the mouse if you wish; the two APIs will coexist peacefully.

Share this post


Link to post
Share on other sites


You don't have to create another window and run raw input in a separate thread to get the smoothest movement.

Isn't this what DirectInput also does?


By the time you write all the extra code to handle the stuff that DirectInput automatically does for you, both raw input and Windows API messages no longer look as simple.

Are you assuming we are lazy? :)

Share this post


Link to post
Share on other sites

For FPS camera mouse movement and with an existing program I'd honestly just continue using DirectInput.
 
Now, before you all dogpile the -1s, despite Microsoft's advice to not use it, despite the fact that it's no longer updated, and despite the fact that it's just an emulaion layer over raw input these days, all of which are accepted caveats, there are several advantages to doing so:

Basically... ignore the creator's documentation because they don't know what they're talking about and sacrifice performance by going through an unsupported additional layer of API?
 
  • No pointer ballistics: this is exactly what you want with this kind of camera.
Raw input doesn't have pointer ballistics. As you stated above, it's where DInput gets its data in the first place.
 

  • The mouse is properly clipped to the window.

ClipCursor is a single call that does this for you...
 

  • Showing/hiding the cursor is handled automatically for you.

Ok, so that's one point in favor... if you don't like writing about 2 lines of code to hide/show when you get activate events which you're handling anyway to pause/resume your game (you are doing that, right?).

Ironically, DirectInput also gets this wrong... Several bugs with cursor visibility and cursor restraint to the game window were fixed in my own personal projects (as well as professional ones) by switching to RawInput.
 

  • You don't have to create another window and run raw input in a separate thread to get the smoothest movement.

Not entirely sure where this is coming from - do you have a source?
 

  • It's better documented and with more examples available (just wait till you see the MSDN example for GetRawInputBuffer).

I can find a ton more documentation and examples for C++ programming then Python - does that mean I should use it for everything, even things that Python is better at? Raw input has been around long enough that it's a "solved" thing and you can easily find several complete examples for how to utilize it.
 

  • By the time you write all the extra code to handle the stuff that DirectInput automatically does for you, both raw input and Windows API messages no longer look as simple.

Again, I point to the fact that the creator tells you not to use something, you probably shouldn't be using it. If the complexity really bothers you you can easily find examples online to plug into your code, or probably even some wrapper APIs that are supported by third parties...
 

You've already got working code that does exactly what you want the way you want it, so theoretical rather than practical grounds is a bad case for replacing it.  DirectInput is
going to continue to exist for a long time: it's not going to suddenly stop working just because it's no longer being updated.  You can switch to RawInput for the keyboard but continue using DirectInput for the mouse if you wish; the two APIs will coexist peacefully.

DirectInput can disappear at any time. Granted, MS is far better then most at keeping ancient technology running because they understand the importance of backwards compatibility, but why risk it when a better solution exists that is supported? Edited by SmkViper

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this