🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

RawInput buffered read problem

Started by
7 comments, last by Shaarigan 4 years, 6 months ago

I wanted to design my input handling to not rely on windows messages, so I decided to use raw input API and read in the buffered manner via GetRawInputBuffer() function (as opposed to the standard manner with GetRawInputData() that is used with windows messages). I read the whole buffer of raw input events at the beginning of every frame in the game loop and update my controller/mouse/keyboard states.

The issue is that I keep losing messages every once in a while. The problem shows when I hold a controller button, then some frames I don't receive the controller button input and the gameplay breaks because of expecting a held down button (animation cancels for example).

My game loop is put in the windows message loop like this:

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

		game.Run();

	}
}

I can fix it in two ways right now, but ideally I would like to avoid both:

  1. Comment out the PeekMessage, TranslateMessage, DispatchMessage calls. This results in the native controls on the window becoming unresponsive. The game will work as expected.
  2. Handle the WM_INPUT message. Instead of using buffered reads, do the standard read of the windows messages in the message handler.

I followed the Microsoft docs to implement the buffered and standard read methods: https://docs.microsoft.com/en-us/windows/win32/inputdev/using-raw-input

Is there a way to avoid my input handler to rely on windows messages and only use the buffered reads, while still having a working message pump going for other tasks?


Advertisement

As far as I know, there isn't any way to live without any message from the OS. The reason is simple, you have to be informed when something is happening in the background and most of the time this is bound to certain context.

I for example avoid using the standard input like key-press or mouse events that come from the OS and instead use the RawIput API available in the HID driver. The you will be informed with a windows message that input has arrived but you don't have to capture any other message and instead request the backbuffer from the HID devices registered to your window.

There has been a tutorial in some forum but unfortunately it is not in english. I worked along this tutorial, made some improvements and came up with my input handling module for our game engine.

All you have to do is to register a window to receive raw input messages. To achieve this, you have first to qurey the devices available (attached) to the PC. You can grab information about the device before, for example what kind of input device it is (Keyboard, Mouse, Gamepad), what name the device has, which vendor but also the axis and button information etc.

Once you have the device ID, you can initialize it for use in your application. You'll then receive WM_INPUT messages for all devices initialized and attached to your app. They are packed into the raw buffer data and you have to extract them for example if it is a gamepad. You'll need the template for this placed by the device's vendor in it's hardware but not necessarily for standard Keyboard/ Mouse devices.

As I'm currently in rework of our engine, I sadly don't have any code examples on GitHub so far.

However, HID is reliable and plays on the same level as DirectInput e.g. XInput2 so it is the fastest you can get. What I do in our engine is obtaining the input into a buffer in memory and place an event in our event queue. The systems can then handle the input via multiple interfaces, event driven ( the buffer is iterated and more events are fired) or via Poll (a qurey into the buffer if there is certain input state set) but however a system handles the input, it is fast and has latence of not even a millisecond in my test cases. This is enougth even for games like Diablo 3 to work properly.

Another pro of using HID is that you always have information of which device the input was from. If youw ant to support local multiplayer, you can just use the HID handle to identify which player has recently made what input.

If you also want to support UPnP, this is a different approach you can't solve with the generic HID interface. There you have to catch messages not in your generic game loop but in the window class itself

EDIT

I forgot to mention that you don't need to install the Windows Driver Kit to handle HID. Anything is already in kernel32.dll and accessible via GetProcAddress

Thanks, I already have the RawInput system implemented, my only grief is that now I have to either:

  • Disable message processing
  • Or parse the WM_INPUT messages as raw input, so I need to expose rawinput interface to windows message handler

Ideally, I would only want to read rawinput in a buffered manner once per frame. This nearly works, but I sometimes lose messages (but in these cases now I get them in the message loop via standard raw input read).

The reason I want to avoid having to parse windows messages, because I am developing an engine, and want a single interface point that the user needs to call, this is game.Run(); and everything else handled automatically within (such as reading raw input buffer).

turanszkij said:
The reason I want to avoid having to parse windows messages, because I am developing an engine, and want a single interface point that the user needs to call

This may or may not be bad design for some reasons, especially the Window Handling. How do you support Window Configuration like FullScreen, Windowed, Border or Borderless mode? How is the user able to react on Window resizing or a layout change for example when the user decides to move the Game from one Desktop to another?

All I can tell you is that you have to work with OS Messages, this is how the OS is designed to work. You can't for example handle UPnP (a Controller/ Gamepad was attached) without notifications from the OS and I'm 99% sure that this is the fact also for raw input. If you have a game loop (I suppose you have and hide it behind the game.Run() function), why not implement the WM_INPUT message by default and feed your input system with the data?

As I described above, you anyways have to maintain a buffer for your input states and where the data is produced shouldn't matter as long as the user is able to obtain them in order and in-time. Or else my question, why hide something behind a game.Run() function if you are inconsitent in what you hide behind?

This is for sure a case of all-or-nothing.

Engines like Urho3D offer a macro for this btw. and so did I too in ours'. My macro takes 3 functions, the main function as same as a prepare and cleanup one called automatically by the code

#ifdef WINDOWS
#include <Windows/WindowsPlatform.h>

#define __app_entry(initialize, main, shutdown) \
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR cmdLine, int showCmd) \
{ \
    initialize(); \
	assert(&Drough::MainAllocator::Allocator()); \
	 \
	Drough::Array<const char*> args; \
	Drough::GetArguments(cmdLine, args); \
	 \
    int ret_code = main(args); \
	if(!args.Empty()) args.~Array(); \
	 \
	shutdown(); \
	 \
	return ret_code; \
}
#define __app_entry_no_args(initialize, main, shutdown) \
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR cmdLine, int showCmd) \
{ \
    initialize(); \
	assert(&Drough::MainAllocator::Allocator()); \
	 \
    int ret_code = main(); \
	shutdown(); \
	 \
	return ret_code; \
}
#elif defined(ANDROID)
#define __app_entry(prepare, main, cleanup) \
extern "C" int SDL_main(int argc, char** argv); \
int32 SDL_main(int argc, char** argv) \
{ \
    initialize(); \
	assert(&Drough::MainAllocator::Allocator()); \
	 \
    Drough::Array<const char*> args; \
	Drough::GetArguments(argc, argv, args); \
	if(!args.Empty()) args.~Array(); \
	 \
    int ret_code = main(args); \
	shutdown(); \
	 \
	return ret_code; \
}
#define __app_entry_no_args(prepare, main, cleanup) \
extern "C" int SDL_main(int argc, char** argv); \
int32 SDL_main(int argc, char** argv) \
{ \
    initialize(); \
	assert(&Drough::MainAllocator::Allocator()); \
	 \
    int ret_code = main(); \
	shutdown(); \
	 \
	return ret_code; \
}
#else
#define __app_entry(prepare, main, cleanup) \
int32 main(int argc, char** argv) \
{ \
    initialize(); \
	assert(&Drough::MainAllocator::Allocator()); \
	 \
    Drough::Array<const char*> args; \
	Drough::GetArguments(argc, argv, args); \
	if(!args.Empty()) args.~Array(); \
	 \
    int ret_code = main(args); \
	shutdown(); \
	 \
	return ret_code; \
}
#define __app_entry_no_args(prepare, main, cleanup) \
int32 main(int argc, char** argv) \
{ \
    initialize(); \
	assert(&Drough::MainAllocator::Allocator()); \
	 \
    int ret_code = main(args); \
	shutdown(); \
	 \
	return ret_code; \
}
#endif


And our message loop lookes like this. It is very simple and short (I don't use WM_DEVICECHANGED here but this usually belongs to UPnP)

bool MessageLoop(MSG& msg)
{
	if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
	{
		switch(msg.message)
		{
			default:
			{
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
			break;
			case WM_INPUT: 
				{
					void* device = Input::Process((void*)msg.lParam, *buffer);
					if(device == Input::GetKeyboard().Handle()) Input::Process(Input::GetKeyboard(), *buffer, Dispatcher::Handler<EventChannel::Default, void (ControlStats::Stat)>());
				}
				break;
			case WM_DEVICECHANGE: break;
			case WM_CLOSE:
			case WM_DESTROY:
				{
					FRM_LOG("Window closed");
					running = false; 
				}
				break;
		}
		return true;
	}
	else return false;
}


This can for example be hidden (and I already did so) behind an Engine::Start() or game.Run() function without problems

I do rely on windows messages for some specific things, like window resize/close/menu selection for sample apps (not as part of the engine). But because there are already input APIs that are not reliant on windows message handler, like XInput and DirectInput, I want the RawInput implementation to also follow this pattern of polling inputs. This will make the engine user worry about one less thing that needs to be handled in windows message handler.

As for reconnecting devices, I solved this without handling device changed message. I basically query for raw input device list every frame and if there is a change in the device list compared to the registered devices, I register/remove device handles from my active list.

From MSDN you have two chances to obtain input, either with the WM_INPUT message and a message pump or using GetRawInputBuffer and the result

"If pData is not NULL and the function is successful, the return value is the number of RAWINPUT structures written to pData.If an error occurs, the return value is (UINT)-1. Call GetLastError for the error code."

So this are your choices, no more no less. Btw, DirectInput and XInput2 are maintaining an extra thread that calls GetRawInputBuffer in a loop but I guess to this note on MSDN

"An application receives raw input from any HID whose top level collection (TLC) matches a TLC from the registration. When an application receives raw input, its message queue gets a WM_INPUT message and the queue status flag QS_RAWINPUT is set (QS_INPUT also includes this flag)"

that you can't do both! Either you query the message loop or you receive input using GetRawInputBuffer. If you do both, so discarding the message resets the QS_INPUT flag and you'll obtain no input at all

EDIT:

So in fact you are always handling with messages, regardless if you use them or don't. So instead of worrying about known APIs, you should design your engine to play well not be similar to something. And if you hide the message pump in the background as I describe above and feed your input system with the data, this is fine. If a user wants to handle more messages by his/her own, you could offer a callback before discarding anything

(Removed double post)

I am using GetRawInputBuffer() in the gameloop. This sometimes loses messages, as far as I understand it is because when WM_INPUT is received in Windows message loop, those events are no longer retrieved with GatRawInputBuffer().

I am using GetRawInputData() in the message loop to receive WM_INPUT messages and process them to not lose any messages.

But GetRawInputBuffer() still returns input messages in the gameloop, as I understand this is because after WM_INPUT was drained, the gameloop started and in the meantime some rawinput messages managed to arrive again. If I don't use GetRawInputBuffer(), they would be handled before the next frame when the message processing is invoked.

I'm not looking for how I can change my interface, but for an answer on how I can keep my current simplistic interface. But thanks for your insight @Shaarigan

TL;DR; As I wrote above, I read from the MSDN stuff that you can have either use WMINPUT message but TranslateMessage/DispatchMessage will consume it and remove the QSRAWINPUT flag from the OS queue where the raw input data is stored or you can have GetRawInputBuffer to return try getting stuff from the queue; but not both at the same time! This will result in what you already pointed out, loosing input messages

This topic is closed to new replies.

Advertisement