pspKeys and pspAxis were written from the input thread and the render
thread without a lock. Entering VR camera adjust "released" held keys by
sending PSP button codes as keycodes, which released nothing; use
ControlMapper::ReleaseAll instead.
Input devices are now matched by ID. InputDevice objects are replaced after
device changes, so reference comparison registered the same device again.
Removing a device now releases what it was holding.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
VR_DestroyRenderer() freed projections without nulling it, and
VR_GetView() indexed it with no initialized check - a latent
use-after-free if VR_GetView were ever called between a destroy and
the next VR_InitRenderer. Not currently reachable (VR_DestroyRenderer
is only called from inside VR_InitRenderer, synchronously followed by
reallocation), but cheap to close off.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L4QAoxV2KY7ek4PcZw3WvY
INVR_Vibrate()'s channel selection was `i & chan` (bitwise AND) inside
a loop over i in [0,2), instead of just using chan directly as the
index. For chan=0 (left controller, per the only call site iterating
j in [0,2)), i & 0 is always 0, so the "if (channel)" check was never
true and vibration_channel_duration/intensity were never set - the
left controller silently never vibrated. chan=1 (right) happened to
work by coincidence (i=1 gives 1 & 1 == 1). chan is now used directly
as the array index, with no loop needed.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L4QAoxV2KY7ek4PcZw3WvY
This adds a per-game graphics option, `DisplayRefreshRate`, to override the display refresh rate.
It defaults to 60 Hz, and is located in Dev tools.
Games using variable timesteps benefit from higher refresh rates.
Closes#19319