mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-08 08:10:30 -10:00
winraw: the pump leaves raw input for the poll's bulk read
A session on Windows with RETROARCH_RAWINPUT_POLL=1 and the mouse in use: 4578 reports, of which 3180 were read in bulk, in 951 reads, and 1398 came as messages; 4805 more reads found nothing. So the mouse works read this way, and the count of calls is this: the pump took 1398 reports out as messages, each with a GetRawInputData() of its own and a bulk read behind it, and the poll made a read every iteration. That is 1398 messages, 1398 single reads and 5756 bulk reads - 8552 calls where the old path, two a report, would have made 9156. A few per cent, not the saving a bulk read is for. The pump got to the reports first because it runs every iteration and takes whatever is in the queue. On the thread whose raw input the poll reads, it now asks for the messages below WM_INPUT and the messages above it and leaves the reports where they are; the two ranges take turns so that neither waits on the other. This is how Microsoft's own sample of a buffered read keeps its pump off the reports. The poll's read is then the only one: for that session, the 4358 the poll made against 9156. Every runloop path that pumps also polls - the core's poll, the menu's, and the explicit ones before the pause, idle and unfocused waits - so nothing is left in the queue for longer than an iteration, and the wait on the queue (win32_display_server_idle_wait()) is not cut short by reports nobody takes. A pump that is not the frontend's - the loop Windows runs for a menu or while a window is dragged - still dispatches reports as messages, and the callback still takes them, one from its message and the rest in bulk. Threads whose raw input is not read by the poll pump as before: the video thread always, and every thread with the switch off. samples/input/winraw_poll: after a pump with the two ranges, an ordinary message has been dispatched, six injected reports are still in the queue with none taken, and the next poll reads the six in one read.
This commit is contained in:
1 parent
34d92a3c15
commit
b2968a37ed
4 files changed
+126
-7
No files matched your search
@@ -512,8 +512,11 @@ jobs:
|
||||
# events in order with the modifier held at the time; six
|
||||
# reports the pump meets first are one from its message and
|
||||
# five in one bulk read, in order; with nothing waiting a
|
||||
# poll makes one read and a pump none; free takes the window
|
||||
# down, twenty restarts each do the same.
|
||||
# poll makes one read and a pump none; a pump that asks for
|
||||
# the messages below WM_INPUT and above it, as the
|
||||
# frontend's does on that thread, dispatches the ordinary
|
||||
# ones and leaves the reports for one bulk read; free takes
|
||||
# the window down, twenty restarts each do the same.
|
||||
trap 'wineserver -k >/dev/null 2>&1 || true' EXIT
|
||||
make clean all
|
||||
WINEDEBUG=-all timeout 300 make check
|
||||
|
||||
@@ -743,9 +743,19 @@ static void winraw_update_mouse_state(winraw_input_t *wr,
|
||||
* What it costs when nothing is happening is the poll's one read a
|
||||
* frame, which then finds nothing: knowing what the devices have sent
|
||||
* at the moment of the poll means asking at the moment of the poll.
|
||||
* That is the only read made unasked. The thread's pump is left as it
|
||||
* was; if it comes across a report as a message, the callback takes
|
||||
* that one and reads the rest in bulk (below).
|
||||
* That is the only read made unasked.
|
||||
*
|
||||
* The thread's pump leaves raw input in the queue for that read
|
||||
* (winraw_poll_owns_thread()): it asks for the messages below WM_INPUT
|
||||
* and the ones above it, which is how Microsoft's own sample of a
|
||||
* buffered read keeps its pump off the reports. Before it did, a
|
||||
* session with the mouse in use had the pump take 1398 of 4578
|
||||
* reports out one message at a time, each with a read of its own and
|
||||
* a bulk read behind it, and the calls saved over the old path came
|
||||
* to a few per cent. Some other pump can still come across a report
|
||||
* as a message - a modal loop Windows runs for a menu or a window
|
||||
* being dragged - and then the callback takes that one and reads the
|
||||
* rest in bulk (below).
|
||||
*
|
||||
* And the window is no longer the video driver's thread's, which has
|
||||
* to be true before the input driver can stop being restarted with
|
||||
@@ -990,6 +1000,15 @@ static void winraw_drain(winraw_input_t *wr)
|
||||
}
|
||||
}
|
||||
|
||||
/* For a thread's pump (ui_application_win32_process_events()): true
|
||||
* on the thread whose raw input the poll reads in bulk. The pump then
|
||||
* leaves raw input where it is - in the queue, for the poll - instead
|
||||
* of taking it out one message at a time. */
|
||||
bool winraw_poll_owns_thread(void)
|
||||
{
|
||||
return winraw_drain_tid && GetCurrentThreadId() == winraw_drain_tid;
|
||||
}
|
||||
|
||||
/* Input that came while the application was in the background is not
|
||||
* taken - unless the driver registered as a sink, where everything
|
||||
* that is not foreground input comes marked that way and the main
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
* message carries and reads the rest in bulk, in order, and their
|
||||
* key events still wait for the poll;
|
||||
* - with nothing waiting, a poll makes one read and a pump makes none;
|
||||
* - a pump that asks for the messages below WM_INPUT and above it, as
|
||||
* the frontend's does on this thread, leaves the reports in the
|
||||
* queue, and the next poll reads them all in one;
|
||||
* - a controller's report in a bulk read is passed to the controller
|
||||
* driver;
|
||||
* - which modifier keys are held is read from the key table, and only
|
||||
@@ -197,6 +200,29 @@ static void key(WORD scan, bool down)
|
||||
SendInput(1, &in, sizeof(in));
|
||||
}
|
||||
|
||||
/* ui_application_win32_process_events() on a thread whose raw input
|
||||
* the poll reads: everything but WM_INPUT. */
|
||||
static void pump_leaving_raw_input(void)
|
||||
{
|
||||
MSG msg;
|
||||
for (;;)
|
||||
{
|
||||
bool any = false;
|
||||
if (PeekMessageA(&msg, 0, 0, WM_INPUT - 1, PM_REMOVE))
|
||||
{
|
||||
DispatchMessageA(&msg);
|
||||
any = true;
|
||||
}
|
||||
if (PeekMessageA(&msg, 0, WM_INPUT + 1, 0xFFFFFFFF, PM_REMOVE))
|
||||
{
|
||||
DispatchMessageA(&msg);
|
||||
any = true;
|
||||
}
|
||||
if (!any)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned wm_input_waiting(void)
|
||||
{
|
||||
MSG msg;
|
||||
@@ -371,6 +397,43 @@ int main(void)
|
||||
(wr->drain_empty - empty0) + (wr->drain_reads - reads0));
|
||||
printf(" ok nothing waiting: a poll makes one read, a pump makes none\n");
|
||||
}
|
||||
|
||||
/* ---- the frontend's pump on this thread ----------------- */
|
||||
{
|
||||
unsigned long reads0;
|
||||
CHECK(winraw_poll_owns_thread(), "the polling thread is not known as the one that is polled");
|
||||
wr->drained = wr->by_message = 0;
|
||||
reads0 = wr->drain_reads;
|
||||
key(0x10, true);
|
||||
key(0x10, false);
|
||||
key(0x11, true);
|
||||
key(0x11, false);
|
||||
key(0x12, true);
|
||||
key(0x12, false);
|
||||
/* a message that is not raw input, to see the pump still
|
||||
* dispatches those */
|
||||
PostMessageA(raw_window, WM_APP, 0, 0);
|
||||
Sleep(150);
|
||||
pump_leaving_raw_input();
|
||||
{
|
||||
MSG probe;
|
||||
CHECK(!PeekMessageA(&probe, raw_window, WM_APP, WM_APP, PM_NOREMOVE),
|
||||
"the pump left an ordinary message behind");
|
||||
}
|
||||
CHECK(wr->by_message == 0 && wr->drained == 0 && !wr->kb_keys[0x12],
|
||||
"the pump took raw input: %lu as messages, %lu in bulk",
|
||||
wr->by_message, wr->drained);
|
||||
winraw_poll(wr);
|
||||
CHECK(wr->drained == 6 && wr->by_message == 0
|
||||
&& wr->drain_reads - reads0 == 1,
|
||||
"after that pump the poll read %lu reports in %lu reads; wanted 6 in 1",
|
||||
wr->drained, wr->drain_reads - reads0);
|
||||
CHECK(seen_n == 6 && seen[0].code == 0x10 && seen[2].code == 0x11
|
||||
&& seen[4].code == 0x12 && !seen[5].down,
|
||||
"the six did not come out of the poll in order (%u events)", seen_n);
|
||||
seen_n = 0;
|
||||
printf(" ok the frontend's pump on this thread: ordinary messages dispatched, six reports left for one bulk read\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
printf(" note an injected key did not arrive as raw input here: the bulk read is not checked\n");
|
||||
|
||||
+36
-2
@@ -131,12 +131,46 @@ static void ui_application_win32_dispatch(MSG *msg)
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(HAVE_WINRAWINPUT) && !defined(_XBOX) && _WIN32_WINNT >= 0x0501 && !defined(__WINRT__)
|
||||
extern bool winraw_poll_owns_thread(void);
|
||||
#define WIN32_RAW_INPUT_IS_POLLED() winraw_poll_owns_thread()
|
||||
#else
|
||||
#define WIN32_RAW_INPUT_IS_POLLED() false
|
||||
#endif
|
||||
|
||||
static void ui_application_win32_process_events(void)
|
||||
{
|
||||
MSG msg;
|
||||
|
||||
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
|
||||
ui_application_win32_dispatch(&msg);
|
||||
if (WIN32_RAW_INPUT_IS_POLLED())
|
||||
{
|
||||
/* The raw input driver reads this thread's reports in bulk when
|
||||
* it polls (winraw_input.c, "Read by the poll"). Taking them out
|
||||
* here, one WM_INPUT at a time, is the work that read exists to
|
||||
* save, so this asks for everything below WM_INPUT and
|
||||
* everything above it and leaves the reports in the queue. The
|
||||
* two ranges take turns, so that neither waits for the other to
|
||||
* run dry. */
|
||||
for (;;)
|
||||
{
|
||||
bool any = false;
|
||||
if (PeekMessage(&msg, 0, 0, WM_INPUT - 1, PM_REMOVE))
|
||||
{
|
||||
ui_application_win32_dispatch(&msg);
|
||||
any = true;
|
||||
}
|
||||
if (PeekMessage(&msg, 0, WM_INPUT + 1, 0xFFFFFFFF, PM_REMOVE))
|
||||
{
|
||||
ui_application_win32_dispatch(&msg);
|
||||
any = true;
|
||||
}
|
||||
if (!any)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
|
||||
ui_application_win32_dispatch(&msg);
|
||||
|
||||
/* The video thread pumps through here too (win32_check_window);
|
||||
* only the main thread may run the parked commands. */
|
||||
|
||||
Reference in new issue
Block a user