winraw: keyboard and mouse are read by the poll by default

Reading the raw input queue in bulk from the poll has been behind
RETROARCH_RAWINPUT_POLL=1. It is the default now, and
RETROARCH_RAWINPUT_POLL=0 turns it off, should something need telling
apart from it.

Tried on Windows with threaded video, Vulkan, a 1000 Hz mouse and a
keyboard, a minute of play: 9714 reports read in 7679 reads - one read
an iteration, 2241 of them with reports in - none taken by the pump as
a message, no key event dropped. The old path's two calls a report
would have been 19428. With nothing happening it is one read a frame
that finds nothing: 604 frames in the menu, 604 reads.

What that buys beside the calls: the state the core reads is what the
devices had sent at the moment of the poll, not what the video
thread's last pump left; key events reach the menu and the core's
keyboard callback from the main thread rather than the video thread;
and the window is the main thread's, not the video driver's.

That run also settles what Wine could not: Windows gives raw input to
a window whose thread does not have the focus. The sink registration
stays as RETROARCH_RAWINPUT_POLL=2 for a system where that turns out
otherwise.

Not tried on Windows: video not threaded, where the window, the pump
and the poll are one thread's, and winraw_joypad's reports can come
out of the same bulk read. samples/input/winraw_poll now runs that
arrangement under Wine - window made by the poll, the pump leaving
the reports, the poll reading them in bulk - and the test's switch-off
case is =0.

The line of counts the driver logs when it is freed is a debug line
now.
This commit is contained in:
LibretroAdmin authored and libretroadmin committed 2026-10-03 06:41:01 +02:00
1 parent c1efc6ee18
commit 1df2715776
3 files changed
+91 -29

No files matched your search

+6 -2
View File
@@ -500,7 +500,8 @@ jobs:
run: |
set -eu
# input/drivers/winraw_input.c with its keyboard and mouse
# read in bulk by the poll (RETROARCH_RAWINPUT_POLL). The real
# read in bulk by the poll (the default;
# RETROARCH_RAWINPUT_POLL=0 turns it off). The real
# driver under Wine on a virtual display, with the focused
# window made and pumped by a second thread as the video
# thread does, and the driver started from that thread. With
@@ -516,7 +517,10 @@ jobs:
# 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.
# the window down, twenty restarts each do the same; and
# with the window, the pump and the poll all on one thread,
# as without threaded video, the reports are read in bulk
# the same.
trap 'wineserver -k >/dev/null 2>&1 || true' EXIT
make clean all
WINEDEBUG=-all timeout 300 make check
+26 -18
View File
@@ -48,9 +48,10 @@ extern "C" {
/* Threading model
* ---------------
* (As it is by default. With RETROARCH_RAWINPUT_POLL the window is
* the polling thread's and the reports are read in bulk by the poll:
* see "Read by the poll" further down.)
* (This is how the driver was, and is with RETROARCH_RAWINPUT_POLL=0
* in the environment. By default the window is the polling thread's
* and the reports are read in bulk by the poll: see "Read by the
* poll" further down.)
*
* On every Windows video driver that can actually use this input
* driver, it is built by the video context driver's input_driver
@@ -716,12 +717,14 @@ static void winraw_update_mouse_state(winraw_input_t *wr,
/* Read by the poll
* ----------------
* With RETROARCH_RAWINPUT_POLL set in the environment, the keyboard
* and mouse window is made by the first winraw_poll(), on the thread
* that polls, and the reports waiting for it are read in bulk - one
* GetRawInputBuffer() for all of them - at the start of every poll.
* The keyboard and mouse window is made by the first winraw_poll(),
* on the thread that polls, and the reports waiting for it are read
* in bulk - one GetRawInputBuffer() for all of them - at the start of
* every poll. RETROARCH_RAWINPUT_POLL=0 in the environment turns this
* off and leaves the driver as the threading note at the top has it,
* should something need telling apart from it.
*
* As it is otherwise (the threading note at the top), the window is
* As it was (the threading note at the top), the window is
* made on whichever thread runs the video driver's init, and each
* report is a WM_INPUT message taken when that thread pumps: a
* PeekMessage() and a GetRawInputData() per report, once per video
@@ -787,13 +790,17 @@ static void winraw_update_mouse_state(winraw_input_t *wr,
* - the pump got to it before a poll did - is read from its message
* first, as it always was, and only then is the rest read in bulk.
*
* RETROARCH_RAWINPUT_POLL=2 also registers as a sink. Raw input goes
* to a window only while its application is in the foreground, and
* the expectation is that Windows judges that by process, so that a
* window on a thread that does not have the focus still gets it. If
* it judges by thread, nothing would arrive with =1 under threaded
* video; as a sink everything arrives, and the main window's focus
* (winraw_focus) decides whether it is taken. */
* Raw input goes to a window only while its application is in the
* foreground, and under threaded video this window's thread is not
* the one with the focus. Windows judges the foreground by process,
* not by thread: tried on Windows with threaded video, the reports
* arrive. RETROARCH_RAWINPUT_POLL=2 is kept for a system where they
* do not: it registers as a sink, so that everything arrives, and
* the main window's focus (winraw_focus) decides whether it is taken.
*
* Measured there, a minute of play with a 1000 Hz mouse in use: 9714
* reports in 7679 reads, one read an iteration, none taken by the
* pump. The old path's two calls a report would have been 19428. */
extern void winraw_joypad_take_hid(HANDLE device, const BYTE *data, DWORD size);
@@ -1101,12 +1108,13 @@ static bool winraw_poll_window_up(winraw_input_t *wr)
return false;
}
/* RETROARCH_RAWINPUT_POLL: 1, or 2 to register as a sink as well. */
/* On, unless RETROARCH_RAWINPUT_POLL=0. 2 registers as a sink as
* well. */
static bool winraw_poll_wanted(bool *sink)
{
const char *env = getenv("RETROARCH_RAWINPUT_POLL");
*sink = (env && env[0] == '2');
return env && (env[0] == '1' || env[0] == '2');
return !(env && env[0] == '0');
}
static void *winraw_init(const char *joypad_driver)
@@ -1756,7 +1764,7 @@ static void winraw_free(void *data)
PostMessageA(wr->window, WM_CLOSE, 0, 0);
}
winraw_drain_tid = 0;
RARCH_LOG("[WinRaw] Read by the poll: %lu reports in %lu bulk reads"
RARCH_DBG("[WinRaw] Read by the poll: %lu reports in %lu bulk reads"
" and %lu as messages; %lu reads found nothing waiting"
" (%lu key events dropped).\n",
wr->drained, wr->drain_reads, wr->by_message, wr->drain_empty,
+59 -9
View File
@@ -1,5 +1,5 @@
/* winraw: the keyboard and mouse read in bulk by the poll
* (RETROARCH_RAWINPUT_POLL).
/* winraw: the keyboard and mouse read in bulk by the poll (the
* default; RETROARCH_RAWINPUT_POLL=0 turns it off).
*
* The real driver, cross-built with mingw-w64 and run under Wine on a
* virtual display. The window that has the focus is made and pumped
@@ -31,7 +31,9 @@
* the lock keys' toggles from the published value;
* - background input is refused, unless registered as a sink with the
* main window focused;
* - free takes the window down, and twenty restarts each do the same.
* - free takes the window down, and twenty restarts each do the same;
* - with everything on one thread, as without threaded video, the
* same holds.
*
* What it cannot check is the thing only Windows can answer: whether
* Windows, like Wine, gives raw input to a window whose thread does
@@ -248,7 +250,7 @@ int main(void)
printf(" note no display for a window: injected input is not checked\n");
/* ---- the switch off: as it was -------------------------------- */
_putenv("RETROARCH_RAWINPUT_POLL=");
_putenv("RETROARCH_RAWINPUT_POLL=0");
wr = (winraw_input_t*)init_on_video_thread();
CHECK(wr != NULL, "init with the switch off");
if (!wr)
@@ -265,15 +267,16 @@ int main(void)
PostMessageA(raw_window, WM_CLOSE, 0, 0);
wr->window = NULL;
winraw_free(wr);
printf(" ok switch off: the window is made by init on init's thread, nothing read in bulk\n");
printf(" ok =0: the window is made by init on init's thread, nothing read in bulk\n");
/* ---- the switch on -------------------------------------------- */
_putenv("RETROARCH_RAWINPUT_POLL=1");
/* unset: on is the default */
_putenv("RETROARCH_RAWINPUT_POLL=");
wr = (winraw_input_t*)init_on_video_thread();
CHECK(wr != NULL, "init with the switch on");
CHECK(wr != NULL, "init with nothing set");
if (!wr)
return 1;
CHECK(wr->poll_drain && !wr->sink, "not reading in bulk with the switch on");
CHECK(wr->poll_drain && !wr->sink, "not reading in bulk by default");
CHECK(!wr->window, "init made the window");
winraw_poll(wr);
raw_window = wr->window;
@@ -283,7 +286,7 @@ int main(void)
"the window belongs to thread %lu; the poll's is %lu, init's %lu",
(unsigned long)GetWindowThreadProcessId(raw_window, NULL),
(unsigned long)main_tid, (unsigned long)vt_tid);
printf(" ok switch on: init makes no window; the first poll does, on the polling thread\n");
printf(" ok by default: init makes no window; the first poll does, on the polling thread\n");
/* ---- injected keys, read in bulk by one poll ------------------ */
if (vt_window)
@@ -526,6 +529,53 @@ int main(void)
SetEvent(vt_quit);
WaitForSingleObject(vt, 5000);
/* ---- video not threaded: everything on the one thread ---------- */
/* The focused window, the driver's start, the pump and the poll are
* all this thread's, as they are without threaded video. */
{
WNDCLASSA wc;
HWND top;
memset(&wc, 0, sizeof(wc));
wc.lpfnWndProc = plain_proc;
wc.hInstance = GetModuleHandleA(NULL);
wc.lpszClassName = "winraw-poll-test-same";
RegisterClassA(&wc);
top = CreateWindowExA(0, wc.lpszClassName, "t",
WS_OVERLAPPEDWINDOW | WS_VISIBLE, 10, 10, 200, 100,
NULL, NULL, wc.hInstance, NULL);
wr = (winraw_input_t*)winraw_init("null");
CHECK(wr != NULL, "init on the polling thread");
if (top && wr && raw_arrives)
{
SetForegroundWindow(top);
SetFocus(top);
pump_leaving_raw_input();
winraw_poll(wr);
CHECK(wr->window && GetWindowThreadProcessId(wr->window, NULL) == main_tid,
"the window is not this thread's");
seen_n = 0;
key(0x1E, true);
key(0x1E, false);
PostMessageA(top, WM_APP, 0, 0);
Sleep(150);
pump_leaving_raw_input();
CHECK(wr->by_message == 0 && wr->drained == 0,
"the pump took raw input on the one thread");
winraw_poll(wr);
CHECK(wr->drained == 2 && wr->by_message == 0 && seen_n == 2
&& seen[0].down && seen[0].code == 0x1E && !seen[1].down,
"on the one thread: %lu read in bulk, %lu as messages, %u events",
wr->drained, wr->by_message, seen_n);
seen_n = 0;
printf(" ok video not threaded: window, pump and poll on one thread, reports read in bulk\n");
}
if (wr)
winraw_free(wr);
if (top)
DestroyWindow(top);
}
if (failures)
{
printf("FAIL winraw_poll_test: %u\n", failures);