这是indexloc提供的服务,不要输入任何密码
Skip to content

Conversation

@amurzeau
Copy link
Contributor

@amurzeau amurzeau commented Aug 8, 2022

When the game is paused (with F3), the GUI become less and less responsive to mouse and keyboard:

simplescreenrecorder-2022-08-08_22.25.24.mp4

The patch keep the polling of input event active even when the game is paused, by not checking single_step.
The resulting behavior is a fluid GUI even when the game is paused:

simplescreenrecorder-2022-08-08_22.27.09.mp4

@k4zmu2a k4zmu2a merged commit 367f453 into k4zmu2a:master Aug 9, 2022
@k4zmu2a
Copy link
Owner

k4zmu2a commented Aug 9, 2022

Looks good to me, merged.

Slow polling while paused was a feature from the original game.
It worked fine on Windows, not sure why it would skip inputs like that on Linux.

@amurzeau
Copy link
Contributor Author

amurzeau commented Aug 9, 2022

I've checked what was causing this "laggy" behavior and the history of the related code.

This seems to be related to the number of events generated by the system.

In that function (before this PR):

int winmain::ProcessWindowMessages()
{
	static auto idleWait = 0;
	SDL_Event event;
	if (has_focus && !single_step)
	{
		idleWait = static_cast<int>(TargetFrameTime.count());
		while (SDL_PollEvent(&event))
		{
			if (!event_handler(&event))
				return 0;
		}

		return 1;
	}

	// Progressively wait longer when transitioning to idle
	idleWait = std::min(idleWait + static_cast<int>(TargetFrameTime.count()), 500);
	if (SDL_WaitEventTimeout(&event, idleWait))
	{
		idleWait = static_cast<int>(TargetFrameTime.count());
		return event_handler(&event);
	}
	return 1;
}

When paused (with F3), the SDL_PollEvent is never called. Events get processed only by SDL_WaitEventTimeout.
As ProcessWindowMessages is called only once per update (so at most 120 times per second the default UPS value), only 120 events maximum are processed per second.
SDL seems to queue all events and not loose them, which cause this:

  • When moving the mouse, there are a bunch of event (type == SDL_MOUSEMOTION) generated. This cause the SDL event queue to fill up a bit with many SDL_MOUSEMOTION to process and SpaceCadetPinball takes time to process them all at a rate of 60 events per second.

I use a high DPI mouse which might be the cause of the high number of generated mouse move event.

I've also checked and found that the old behavior (before this PR) is from the older code that was using GetMessage/PeekMessage,
They were doing this because the code was doing this (pseudocode):

  • When not paused:
while(1)
{
    while (PeekMessageA(&Msg, nullptr, 0, 0, 1u))
    { ... }
    debugStuff?()
    displayFrame()
    sleep(time before next frame)
}
  • When paused:
while(1)
{
    GetMessageA(&Msg, hwnd_frame, 0, 0);
    debugStuff?()
}

Which means that when the game was paused, the only thing it was doing is looping on GetMessage without any Sleep() which explain why it was not "lagging" (at least not in wine on linux).

So I guess SDL_PollEvent could be called without condition and the SDL_WaitEventTimeout call removed to simplify the function, I don't think that would cause any problem as the sleep is now always done (even when paused) preventing the while(1) loop to eat 100% of the cpu.

This would become this:

int winmain::ProcessWindowMessages()
{
	SDL_Event event;
	while (SDL_PollEvent(&event))
	{
		if (!event_handler(&event))
			return 0;
	}

	return 1;
}

Are you interested with a PR for this ?

While I'm here I've a question about the variable single_step, I found that it is toggled when pausing the game, and when pressing F10 with cheat_mode enabled. Maybe this variable can be renamed to game_paused ?

@amurzeau
Copy link
Contributor Author

amurzeau commented Aug 9, 2022

I have found that the sleep() is only called when the window has focus. So when !has_focus, we must not only do SDL_PollEvent else we end up in a busy loop doing SDL_PollEvent and eating 100% of the CPU.

So the ProcessWindowMessages function is good at it is now and my proposition to simplify it must not be implemented.

@k4zmu2a
Copy link
Owner

k4zmu2a commented Aug 10, 2022

About event stuff:
I was under the impression that SDL_WaitEventTimeout works like this:
On empty queue, SDL_WaitEventTimeout is supposed to wait for the first event to arrive, then return. Or timeout waiting for next event.
It works this way on Windows, it returns first event, idleWait gets reduced, user does not notice any input lag.

As ProcessWindowMessages is called only once per update (so at most 120 times per second the default UPS value), only 120 events maximum are processed per second.

The way I see it:
SDL_WaitEventTimeout is supposed to wait for the first event, then return immediately (without losing any events).
The rate of ProcessWindowMessages call does not matter here (at idleWait=500 it is ~2 per second).
The game spends most of its time in SDL_WaitEventTimeout.
Sorta proof: suppose there was SDL_WaitEvent instead, would that logically change anything?

About original code:
You can see that my SDL code tries to mimic event polling of the original game.
The question here is this: which SDL event poll method, analog of GetMessageA, such that does not lose input events on Linux, could be used here?
I am not sure if method like that exists.

About sleep and events:
As you noticed, the game is throttled by event polling when idle.
This is by design, how the original does it, no need to change it.

About single_step:
I think it was named after F10/F1 combo, for single stepping the game.
User mode pause was attached to it later.
My intention is to preserve the original name.

To sum it up:
You don’t have to worry about this topic, the state of event polling after this PR is acceptable.

@k4zmu2a
Copy link
Owner

k4zmu2a commented Aug 10, 2022

I got curious, so I tried it with this PR rolled back on my Linux VMs.
And it worked correctly with WSLG and Debian XFCE on VMWare.
While paused and focused, much like on Windows, no input events were dropped.
I as user could not notice any input lag. Frame rate scaled as it should with event rate.

So, not all Linux setups have this problem.
I don’t have high speed mouse to test with, but I kind of doubt it is the cause.
WSLG uses host mouse, VMware uses mouse to pen tablet VM thing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants