From 26bf40c49783972cda1b143d6f4c2fc1209d422c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 30 Mar 2023 10:47:28 +0200 Subject: [PATCH 01/25] ControlMapper: Change the callbacks to be more suitable for the upcoming refactor. (#17209) * ControlMapper: Change the callbacks to be more suitable for the upcoming refactor. * SetAllButtons: Separate bits to set and bits to clear. * Oops, missed committing some files somehow --- Core/ControlMapper.cpp | 83 ++++--------- Core/ControlMapper.h | 11 +- Core/HLE/sceCtrl.cpp | 7 ++ Core/HLE/sceCtrl.h | 2 + UI/ControlMappingScreen.cpp | 4 +- UI/EmuScreen.cpp | 238 +++++++++++++++++++----------------- UI/EmuScreen.h | 3 +- UI/JoystickHistoryView.cpp | 5 +- 8 files changed, 171 insertions(+), 182 deletions(-) diff --git a/Core/ControlMapper.cpp b/Core/ControlMapper.cpp index ac1f931016ad..ad66020ff0e1 100644 --- a/Core/ControlMapper.cpp +++ b/Core/ControlMapper.cpp @@ -41,9 +41,13 @@ void ConvertAnalogStick(float &x, float &y) { y = Clamp(y / norm * mappedNorm, -1.0f, 1.0f); } -void ControlMapper::SetCallbacks(std::function onVKeyDown, std::function onVKeyUp, std::function setPSPButtonState, std::function setPSPAnalog) { - onVKeyDown_ = onVKeyDown; - onVKeyUp_ = onVKeyUp; +void ControlMapper::SetCallbacks( + std::function onVKey, + std::function setAllPSPButtonStates, + std::function setPSPButtonState, + std::function setPSPAnalog) { + onVKey_ = onVKey; + setAllPSPButtonStates_ = setAllPSPButtonStates; setPSPButtonState_ = setPSPButtonState; setPSPAnalog_ = setPSPAnalog; } @@ -200,11 +204,11 @@ void ControlMapper::SetPSPKey(int deviceId, int pspKeyCode, int flags) { int vk = pspKeyCode - VIRTKEY_FIRST; if (flags & KEY_DOWN) { virtKeys_[vk] = true; - onVKeyDown(deviceId, pspKeyCode); + onVKey(deviceId, pspKeyCode, true); } if (flags & KEY_UP) { virtKeys_[vk] = false; - onVKeyUp(deviceId, pspKeyCode); + onVKey(deviceId, pspKeyCode, false); } } else { int rotations = 0; @@ -232,7 +236,7 @@ void ControlMapper::SetPSPKey(int deviceId, int pspKeyCode, int flags) { } } -void ControlMapper::onVKeyDown(int deviceId, int vkey) { +void ControlMapper::onVKey(int deviceId, int vkey, bool down) { switch (vkey) { case VIRTKEY_AXIS_X_MIN: case VIRTKEY_AXIS_X_MAX: @@ -260,62 +264,27 @@ void ControlMapper::onVKeyDown(int deviceId, int vkey) { break; case VIRTKEY_ANALOG_ROTATE_CW: - autoRotatingAnalogCW_ = true; - autoRotatingAnalogCCW_ = false; - break; - case VIRTKEY_ANALOG_ROTATE_CCW: - autoRotatingAnalogCW_ = false; - autoRotatingAnalogCCW_ = true; - break; - - default: - if (onVKeyDown_) - onVKeyDown_(vkey); - break; - } -} - -void ControlMapper::onVKeyUp(int deviceId, int vkey) { - switch (vkey) { - - case VIRTKEY_AXIS_X_MIN: - case VIRTKEY_AXIS_X_MAX: - SetVKeyAnalog(deviceId, 'X', CTRL_STICK_LEFT, VIRTKEY_AXIS_X_MIN, VIRTKEY_AXIS_X_MAX); - break; - case VIRTKEY_AXIS_Y_MIN: - case VIRTKEY_AXIS_Y_MAX: - SetVKeyAnalog(deviceId, 'Y', CTRL_STICK_LEFT, VIRTKEY_AXIS_Y_MIN, VIRTKEY_AXIS_Y_MAX); - break; - - case VIRTKEY_AXIS_RIGHT_X_MIN: - case VIRTKEY_AXIS_RIGHT_X_MAX: - SetVKeyAnalog(deviceId, 'X', CTRL_STICK_RIGHT, VIRTKEY_AXIS_RIGHT_X_MIN, VIRTKEY_AXIS_RIGHT_X_MAX); - break; - case VIRTKEY_AXIS_RIGHT_Y_MIN: - case VIRTKEY_AXIS_RIGHT_Y_MAX: - SetVKeyAnalog(deviceId, 'Y', CTRL_STICK_RIGHT, VIRTKEY_AXIS_RIGHT_Y_MIN, VIRTKEY_AXIS_RIGHT_Y_MAX); - break; - - case VIRTKEY_ANALOG_LIGHTLY: - SetVKeyAnalog(deviceId, 'X', CTRL_STICK_LEFT, VIRTKEY_AXIS_X_MIN, VIRTKEY_AXIS_X_MAX, false); - SetVKeyAnalog(deviceId, 'Y', CTRL_STICK_LEFT, VIRTKEY_AXIS_Y_MIN, VIRTKEY_AXIS_Y_MAX, false); - SetVKeyAnalog(deviceId, 'X', CTRL_STICK_RIGHT, VIRTKEY_AXIS_RIGHT_X_MIN, VIRTKEY_AXIS_RIGHT_X_MAX, false); - SetVKeyAnalog(deviceId, 'Y', CTRL_STICK_RIGHT, VIRTKEY_AXIS_RIGHT_Y_MIN, VIRTKEY_AXIS_RIGHT_Y_MAX, false); - break; - - case VIRTKEY_ANALOG_ROTATE_CW: - autoRotatingAnalogCW_ = false; - setPSPAnalog_(0, 0.0f, 0.0f); + if (down) { + autoRotatingAnalogCW_ = true; + autoRotatingAnalogCCW_ = false; + } else { + autoRotatingAnalogCW_ = false; + setPSPAnalog_(0, 0.0f, 0.0f); + } break; - case VIRTKEY_ANALOG_ROTATE_CCW: - autoRotatingAnalogCCW_ = false; - setPSPAnalog_(0, 0.0f, 0.0f); + if (down) { + autoRotatingAnalogCW_ = false; + autoRotatingAnalogCCW_ = true; + } else { + autoRotatingAnalogCCW_ = false; + setPSPAnalog_(0, 0.0f, 0.0f); + } break; default: - if (onVKeyUp_) - onVKeyUp_(vkey); + if (onVKey_) + onVKey_(vkey, down); break; } } diff --git a/Core/ControlMapper.h b/Core/ControlMapper.h index 3a9158a3c3e5..74783f0a7e5a 100644 --- a/Core/ControlMapper.h +++ b/Core/ControlMapper.h @@ -21,8 +21,8 @@ class ControlMapper { // Required callbacks void SetCallbacks( - std::function onVKeyDown, - std::function onVKeyUp, + std::function onVKey, + std::function setAllPSPButtonStates_, std::function setPSPButtonState, std::function setPSPAnalog); @@ -41,8 +41,7 @@ class ControlMapper { void SetPSPAxis(int deviceId, char axis, float value, int stick); void ProcessAnalogSpeed(const AxisInput &axis, bool opposite); - void onVKeyDown(int deviceId, int vkey); - void onVKeyUp(int deviceId, int vkey); + void onVKey(int deviceId, int vkey, bool down); // To track mappable virtual keys. We can have as many as we want. bool virtKeys_[VIRTKEY_COUNT]{}; @@ -59,9 +58,9 @@ class ControlMapper { bool autoRotatingAnalogCCW_ = false; // Callbacks + std::function onVKey_; + std::function setAllPSPButtonStates_; std::function setPSPButtonState_; - std::function onVKeyDown_; - std::function onVKeyUp_; std::function setPSPAnalog_; std::function setRawAnalog_; }; diff --git a/Core/HLE/sceCtrl.cpp b/Core/HLE/sceCtrl.cpp index 898ba6e37ba4..6e8e160da5ad 100644 --- a/Core/HLE/sceCtrl.cpp +++ b/Core/HLE/sceCtrl.cpp @@ -204,6 +204,13 @@ void __CtrlButtonUp(u32 buttonBit) ctrlCurrent.buttons &= ~buttonBit; } +void __CtrlSetAllButtons(u32 bitsToSet, u32 bitsToClear) +{ + std::lock_guard guard(ctrlMutex); + ctrlCurrent.buttons &= ~(bitsToClear & CTRL_MASK_USER); + ctrlCurrent.buttons |= (bitsToSet & CTRL_MASK_USER); +} + void __CtrlSetAnalogXY(int stick, float x, float y) { u8 scaledX = clamp_u8((int)ceilf(x * 127.5f + 127.5f)); diff --git a/Core/HLE/sceCtrl.h b/Core/HLE/sceCtrl.h index 6a77cd0b9d3d..8307247e1730 100644 --- a/Core/HLE/sceCtrl.h +++ b/Core/HLE/sceCtrl.h @@ -70,6 +70,8 @@ void __CtrlShutdown(); void __CtrlButtonDown(u32 buttonBit); // Call this whenever a button is released. Similar to __CtrlButtonDown(). void __CtrlButtonUp(u32 buttonBit); +// To be used by the new mapping code. +void __CtrlSetAllButtons(u32 bitsToSet, u32 bitsToClear); // Call this to set the position of an analog stick, ideally when it changes. // X and Y values should be from -1 to 1, inclusive, in a square (no need to force to a circle.) diff --git a/UI/ControlMappingScreen.cpp b/UI/ControlMappingScreen.cpp index 9be1e43b9850..1c4ee31635bd 100644 --- a/UI/ControlMappingScreen.cpp +++ b/UI/ControlMappingScreen.cpp @@ -449,8 +449,8 @@ void KeyMappingNewMouseKeyDialog::axis(const AxisInput &axis) { AnalogSetupScreen::AnalogSetupScreen(const Path &gamePath) : UIDialogScreenWithGameBackground(gamePath) { mapper_.SetCallbacks( - [](int vkey) {}, - [](int vkey) {}, + [](int vkey, bool down) {}, + [&](uint32_t bitsToSet, uint32_t bitsToClear) {}, [&](int button, bool down) {}, [&](int stick, float x, float y) { analogX_[stick] = x; diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index aed79db1581d..cf659e9f2e14 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -176,13 +176,15 @@ EmuScreen::EmuScreen(const Path &filename) lastNumFlips = gpuStats.numFlips; startDumping = false; controlMapper_.SetCallbacks( - std::bind(&EmuScreen::onVKeyDown, this, _1), - std::bind(&EmuScreen::onVKeyUp, this, _1), - [](int pspKey, bool down) { + std::bind(&EmuScreen::onVKey, this, _1, _2), + [](uint32_t bitsToSet, uint32_t bitsToClear) { + __CtrlSetAllButtons(bitsToSet, bitsToClear); + }, + [](int pspButton, bool down) { if (down) { - __CtrlButtonDown(pspKey); + __CtrlButtonDown(pspButton); } else { - __CtrlButtonUp(pspKey); + __CtrlButtonUp(pspButton); } }, &SetPSPAnalog); @@ -562,190 +564,200 @@ void EmuScreen::touch(const TouchInput &touch) { } } -void EmuScreen::onVKeyDown(int virtualKeyCode) { +void EmuScreen::onVKey(int virtualKeyCode, bool down) { auto sc = GetI18NCategory("Screen"); switch (virtualKeyCode) { case VIRTKEY_FASTFORWARD: - if (coreState == CORE_STEPPING) { - Core_EnableStepping(false); + if (down) { + if (coreState == CORE_STEPPING) { + Core_EnableStepping(false); + } + PSP_CoreParameter().fastForward = true; + } else { + PSP_CoreParameter().fastForward = false; } - PSP_CoreParameter().fastForward = true; break; case VIRTKEY_SPEED_TOGGLE: - // Cycle through enabled speeds. - if (PSP_CoreParameter().fpsLimit == FPSLimit::NORMAL && g_Config.iFpsLimit1 >= 0) { - PSP_CoreParameter().fpsLimit = FPSLimit::CUSTOM1; - osm.Show(sc->T("fixed", "Speed: alternate"), 1.0); - } else if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1 && g_Config.iFpsLimit2 >= 0) { - PSP_CoreParameter().fpsLimit = FPSLimit::CUSTOM2; - osm.Show(sc->T("SpeedCustom2", "Speed: alternate 2"), 1.0); - } else if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1 || PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM2) { - PSP_CoreParameter().fpsLimit = FPSLimit::NORMAL; - osm.Show(sc->T("standard", "Speed: standard"), 1.0); + if (down) { + // Cycle through enabled speeds. + if (PSP_CoreParameter().fpsLimit == FPSLimit::NORMAL && g_Config.iFpsLimit1 >= 0) { + PSP_CoreParameter().fpsLimit = FPSLimit::CUSTOM1; + osm.Show(sc->T("fixed", "Speed: alternate"), 1.0); + } else if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1 && g_Config.iFpsLimit2 >= 0) { + PSP_CoreParameter().fpsLimit = FPSLimit::CUSTOM2; + osm.Show(sc->T("SpeedCustom2", "Speed: alternate 2"), 1.0); + } else if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1 || PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM2) { + PSP_CoreParameter().fpsLimit = FPSLimit::NORMAL; + osm.Show(sc->T("standard", "Speed: standard"), 1.0); + } } break; case VIRTKEY_SPEED_CUSTOM1: - if (PSP_CoreParameter().fpsLimit == FPSLimit::NORMAL) { - PSP_CoreParameter().fpsLimit = FPSLimit::CUSTOM1; - osm.Show(sc->T("fixed", "Speed: alternate"), 1.0); + if (down) { + if (PSP_CoreParameter().fpsLimit == FPSLimit::NORMAL) { + PSP_CoreParameter().fpsLimit = FPSLimit::CUSTOM1; + osm.Show(sc->T("fixed", "Speed: alternate"), 1.0); + } + } else { + if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1) { + PSP_CoreParameter().fpsLimit = FPSLimit::NORMAL; + osm.Show(sc->T("standard", "Speed: standard"), 1.0); + } } break; case VIRTKEY_SPEED_CUSTOM2: - if (PSP_CoreParameter().fpsLimit == FPSLimit::NORMAL) { - PSP_CoreParameter().fpsLimit = FPSLimit::CUSTOM2; - osm.Show(sc->T("SpeedCustom2", "Speed: alternate 2"), 1.0); + if (down) { + if (PSP_CoreParameter().fpsLimit == FPSLimit::NORMAL) { + PSP_CoreParameter().fpsLimit = FPSLimit::CUSTOM2; + osm.Show(sc->T("SpeedCustom2", "Speed: alternate 2"), 1.0); + } + } else { + if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM2) { + PSP_CoreParameter().fpsLimit = FPSLimit::NORMAL; + osm.Show(sc->T("standard", "Speed: standard"), 1.0); + } } break; case VIRTKEY_PAUSE: - pauseTrigger_ = true; + if (down) { + pauseTrigger_ = true; + } break; case VIRTKEY_FRAME_ADVANCE: - // If game is running, pause emulation immediately. Otherwise, advance a single frame. - if (Core_IsStepping()) - { - frameStep_ = true; - Core_EnableStepping(false); - } - else if (!frameStep_) - { - Core_EnableStepping(true, "ui.frameAdvance", 0); + if (down) { + // If game is running, pause emulation immediately. Otherwise, advance a single frame. + if (Core_IsStepping()) { + frameStep_ = true; + Core_EnableStepping(false); + } else if (!frameStep_) { + Core_EnableStepping(true, "ui.frameAdvance", 0); + } } break; case VIRTKEY_OPENCHAT: - if (g_Config.bEnableNetworkChat) { + if (down && g_Config.bEnableNetworkChat) { UI::EventParams e{}; OnChatMenu.Trigger(e); } break; case VIRTKEY_AXIS_SWAP: - KeyMap::SwapAxis(); + if (down) { + KeyMap::SwapAxis(); + } break; case VIRTKEY_DEVMENU: - { - UI::EventParams e{}; - OnDevMenu.Trigger(e); + if (down) { + UI::EventParams e{}; + OnDevMenu.Trigger(e); + } break; - } #ifndef MOBILE_DEVICE case VIRTKEY_RECORD: - { - if (g_Config.bDumpFrames == g_Config.bDumpAudio) { - g_Config.bDumpFrames = !g_Config.bDumpFrames; - g_Config.bDumpAudio = !g_Config.bDumpAudio; - } else { - // This hotkey should always toggle both audio and video together. - // So let's make sure that's the only outcome even if video OR audio was already being dumped. - if (g_Config.bDumpFrames) { - AVIDump::Stop(); - AVIDump::Start(PSP_CoreParameter().renderWidth, PSP_CoreParameter().renderHeight); - g_Config.bDumpAudio = true; + if (down) { + if (g_Config.bDumpFrames == g_Config.bDumpAudio) { + g_Config.bDumpFrames = !g_Config.bDumpFrames; + g_Config.bDumpAudio = !g_Config.bDumpAudio; } else { - WAVDump::Reset(); - g_Config.bDumpFrames = true; + // This hotkey should always toggle both audio and video together. + // So let's make sure that's the only outcome even if video OR audio was already being dumped. + if (g_Config.bDumpFrames) { + AVIDump::Stop(); + AVIDump::Start(PSP_CoreParameter().renderWidth, PSP_CoreParameter().renderHeight); + g_Config.bDumpAudio = true; + } else { + WAVDump::Reset(); + g_Config.bDumpFrames = true; + } } } break; - } #endif case VIRTKEY_REWIND: - if (SaveState::CanRewind()) { - SaveState::Rewind(&AfterSaveStateAction); - } else { - osm.Show(sc->T("norewind", "No rewind save states available"), 2.0); + if (down) { + if (SaveState::CanRewind()) { + SaveState::Rewind(&AfterSaveStateAction); + } else { + osm.Show(sc->T("norewind", "No rewind save states available"), 2.0); + } } break; case VIRTKEY_SAVE_STATE: - SaveState::SaveSlot(gamePath_, g_Config.iCurrentStateSlot, &AfterSaveStateAction); + if (down) + SaveState::SaveSlot(gamePath_, g_Config.iCurrentStateSlot, &AfterSaveStateAction); break; case VIRTKEY_LOAD_STATE: - SaveState::LoadSlot(gamePath_, g_Config.iCurrentStateSlot, &AfterSaveStateAction); + if (down) + SaveState::LoadSlot(gamePath_, g_Config.iCurrentStateSlot, &AfterSaveStateAction); break; case VIRTKEY_NEXT_SLOT: - SaveState::NextSlot(); - NativeMessageReceived("savestate_displayslot", ""); + if (down) { + SaveState::NextSlot(); + NativeMessageReceived("savestate_displayslot", ""); + } break; case VIRTKEY_TOGGLE_FULLSCREEN: - System_ToggleFullscreenState(""); + if (down) + System_ToggleFullscreenState(""); break; case VIRTKEY_SCREENSHOT: - g_TakeScreenshot = true; + if (down) + g_TakeScreenshot = true; break; case VIRTKEY_TEXTURE_DUMP: - g_Config.bSaveNewTextures = !g_Config.bSaveNewTextures; - if (g_Config.bSaveNewTextures) { - osm.Show(sc->T("saveNewTextures_true", "Textures will now be saved to your storage"), 2.0); - NativeMessageReceived("gpu_configChanged", ""); - } else { - osm.Show(sc->T("saveNewTextures_false", "Texture saving was disabled"), 2.0); + if (down) { + g_Config.bSaveNewTextures = !g_Config.bSaveNewTextures; + if (g_Config.bSaveNewTextures) { + osm.Show(sc->T("saveNewTextures_true", "Textures will now be saved to your storage"), 2.0); + NativeMessageReceived("gpu_configChanged", ""); + } else { + osm.Show(sc->T("saveNewTextures_false", "Texture saving was disabled"), 2.0); + } } break; case VIRTKEY_TEXTURE_REPLACE: - g_Config.bReplaceTextures = !g_Config.bReplaceTextures; - if (g_Config.bReplaceTextures) - osm.Show(sc->T("replaceTextures_true", "Texture replacement enabled"), 2.0); - else - osm.Show(sc->T("replaceTextures_false", "Textures no longer are being replaced"), 2.0); - NativeMessageReceived("gpu_configChanged", ""); + if (down) { + g_Config.bReplaceTextures = !g_Config.bReplaceTextures; + if (g_Config.bReplaceTextures) + osm.Show(sc->T("replaceTextures_true", "Texture replacement enabled"), 2.0); + else + osm.Show(sc->T("replaceTextures_false", "Textures no longer are being replaced"), 2.0); + NativeMessageReceived("gpu_configChanged", ""); + } break; case VIRTKEY_RAPID_FIRE: - __CtrlSetRapidFire(true); + __CtrlSetRapidFire(down); break; case VIRTKEY_MUTE_TOGGLE: - g_Config.bEnableSound = !g_Config.bEnableSound; + if (down) + g_Config.bEnableSound = !g_Config.bEnableSound; break; case VIRTKEY_SCREEN_ROTATION_VERTICAL: - g_Config.iInternalScreenRotation = ROTATION_LOCKED_VERTICAL; + if (down) + g_Config.iInternalScreenRotation = ROTATION_LOCKED_VERTICAL; break; case VIRTKEY_SCREEN_ROTATION_VERTICAL180: - g_Config.iInternalScreenRotation = ROTATION_LOCKED_VERTICAL180; + if (down) + g_Config.iInternalScreenRotation = ROTATION_LOCKED_VERTICAL180; break; case VIRTKEY_SCREEN_ROTATION_HORIZONTAL: - g_Config.iInternalScreenRotation = ROTATION_LOCKED_HORIZONTAL; + if (down) + g_Config.iInternalScreenRotation = ROTATION_LOCKED_HORIZONTAL; break; case VIRTKEY_SCREEN_ROTATION_HORIZONTAL180: - g_Config.iInternalScreenRotation = ROTATION_LOCKED_HORIZONTAL180; - break; - } -} - -void EmuScreen::onVKeyUp(int virtualKeyCode) { - auto sc = GetI18NCategory("Screen"); - - switch (virtualKeyCode) { - case VIRTKEY_FASTFORWARD: - PSP_CoreParameter().fastForward = false; - break; - - case VIRTKEY_SPEED_CUSTOM1: - if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM1) { - PSP_CoreParameter().fpsLimit = FPSLimit::NORMAL; - osm.Show(sc->T("standard", "Speed: standard"), 1.0); - } - break; - case VIRTKEY_SPEED_CUSTOM2: - if (PSP_CoreParameter().fpsLimit == FPSLimit::CUSTOM2) { - PSP_CoreParameter().fpsLimit = FPSLimit::NORMAL; - osm.Show(sc->T("standard", "Speed: standard"), 1.0); - } - break; - - case VIRTKEY_RAPID_FIRE: - __CtrlSetRapidFire(false); - break; - - default: + if (down) + g_Config.iInternalScreenRotation = ROTATION_LOCKED_HORIZONTAL180; break; } } diff --git a/UI/EmuScreen.h b/UI/EmuScreen.h index c3e34369fa0f..ebb7800b4c7e 100644 --- a/UI/EmuScreen.h +++ b/UI/EmuScreen.h @@ -68,8 +68,7 @@ class EmuScreen : public UIScreen { bool hasVisibleUI(); void renderUI(); - void onVKeyDown(int virtualKeyCode); - void onVKeyUp(int virtualKeyCode); + void onVKey(int virtualKeyCode, bool down); void autoLoad(); void checkPowerDown(); diff --git a/UI/JoystickHistoryView.cpp b/UI/JoystickHistoryView.cpp index 38dbcb54a580..3fdd895ac504 100644 --- a/UI/JoystickHistoryView.cpp +++ b/UI/JoystickHistoryView.cpp @@ -1,10 +1,11 @@ #include + #include "UI/JoystickHistoryView.h" + #include "Common/UI/Context.h" #include "Common/UI/UI.h" -// From ControlMapper.h -void ConvertAnalogStick(float &x, float &y); +#include "Core/ControlMapper.h" void JoystickHistoryView::Draw(UIContext &dc) { const AtlasImage *image = dc.Draw()->GetAtlas()->getImage(ImageID("I_CROSS")); From 2814668cf5c919b2f3efe2fee1ed018efeecdd29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 31 Mar 2023 10:08:12 +0200 Subject: [PATCH 02/25] Show a MIPS stack trace on crash screen (#17211) * Print simple stack traces to log on crashes. * Display stack traces on crash screen * Show the in-function offset in the printed callstacks. * Libretro buildfix attempt --- Core/ControlMapper.cpp | 1 - Core/Core.cpp | 13 +++++++++++++ Core/Core.h | 1 + Core/HLE/sceKernel.cpp | 3 +-- Core/MIPS/MIPSStackWalk.cpp | 4 +++- Core/MIPS/MIPSStackWalk.h | 2 +- Core/MemFault.cpp | 39 +++++++++++++++++++++++++++++++++++-- Core/MemFault.h | 9 +++++++++ UI/EmuScreen.cpp | 10 +++++++++- libretro/Makefile.common | 1 + 10 files changed, 75 insertions(+), 8 deletions(-) diff --git a/Core/ControlMapper.cpp b/Core/ControlMapper.cpp index ad66020ff0e1..3cef1079f123 100644 --- a/Core/ControlMapper.cpp +++ b/Core/ControlMapper.cpp @@ -151,7 +151,6 @@ void ControlMapper::Update() { } } - inline bool IsAnalogStickKey(int key) { switch (key) { case VIRTKEY_AXIS_X_MIN: diff --git a/Core/Core.cpp b/Core/Core.cpp index 2b157e4e8344..a1cf09cadcaf 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -37,6 +37,7 @@ #include "Core/MemMap.h" #include "Core/SaveState.h" #include "Core/System.h" +#include "Core/MemFault.h" #include "Core/Debugger/Breakpoints.h" #include "Core/HW/Display.h" #include "Core/MIPS/MIPS.h" @@ -443,6 +444,11 @@ void Core_MemoryException(u32 address, u32 accessSize, u32 pc, MemoryExceptionTy } if (!g_Config.bIgnoreBadMemAccess) { + // Try to fetch a call stack, to start with. + std::vector stackFrames = WalkCurrentStack(-1); + std::string stackTrace = FormatStackTrace(stackFrames); + WARN_LOG(MEMMAP, "\n%s", stackTrace.c_str()); + ExceptionInfo &e = g_exceptionInfo; e = {}; e.type = ExceptionType::MEMORY; @@ -450,6 +456,7 @@ void Core_MemoryException(u32 address, u32 accessSize, u32 pc, MemoryExceptionTy e.memory_type = type; e.address = address; e.accessSize = accessSize; + e.stackTrace = stackTrace; e.pc = pc; Core_EnableStepping(true, "memory.exception", address); } @@ -465,12 +472,18 @@ void Core_MemoryExceptionInfo(u32 address, u32 pc, u32 accessSize, MemoryExcepti } if (!g_Config.bIgnoreBadMemAccess || forceReport) { + // Try to fetch a call stack, to start with. + std::vector stackFrames = WalkCurrentStack(-1); + std::string stackTrace = FormatStackTrace(stackFrames); + WARN_LOG(MEMMAP, "\n%s", stackTrace.c_str()); + ExceptionInfo &e = g_exceptionInfo; e = {}; e.type = ExceptionType::MEMORY; e.info = additionalInfo; e.memory_type = type; e.address = address; + e.stackTrace = stackTrace; e.pc = pc; Core_EnableStepping(true, "memory.exception", address); } diff --git a/Core/Core.h b/Core/Core.h index 333dbd1b4a8c..5be45aac6421 100644 --- a/Core/Core.h +++ b/Core/Core.h @@ -122,6 +122,7 @@ enum class ExceptionType { struct ExceptionInfo { ExceptionType type; std::string info; + std::string stackTrace; // if available. // Memory exception info MemoryExceptionType memory_type; diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index f0e0456995de..01bc74979b18 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -306,8 +306,7 @@ bool __KernelIsRunning() { } std::string __KernelStateSummary() { - std::string threadSummary = __KernelThreadingSummary(); - return StringFromFormat("%s", threadSummary.c_str()); + return __KernelThreadingSummary(); } diff --git a/Core/MIPS/MIPSStackWalk.cpp b/Core/MIPS/MIPSStackWalk.cpp index 40e926700877..8c6305f4f878 100644 --- a/Core/MIPS/MIPSStackWalk.cpp +++ b/Core/MIPS/MIPSStackWalk.cpp @@ -185,4 +185,6 @@ namespace MIPSStackWalk { return frames; } -}; \ No newline at end of file + + +}; diff --git a/Core/MIPS/MIPSStackWalk.h b/Core/MIPS/MIPSStackWalk.h index f380b5881fda..a924c9dddc2a 100644 --- a/Core/MIPS/MIPSStackWalk.h +++ b/Core/MIPS/MIPSStackWalk.h @@ -33,4 +33,4 @@ namespace MIPSStackWalk { }; std::vector Walk(u32 pc, u32 ra, u32 sp, u32 threadEntry, u32 threadStackTop); -}; \ No newline at end of file +}; diff --git a/Core/MemFault.cpp b/Core/MemFault.cpp index 5cedc51738b6..fe413d6a2660 100644 --- a/Core/MemFault.cpp +++ b/Core/MemFault.cpp @@ -20,7 +20,9 @@ #include #include #include +#include +#include "Common/StringUtils.h" #include "Common/MachineContext.h" #if PPSSPP_ARCH(AMD64) || PPSSPP_ARCH(X86) @@ -38,6 +40,12 @@ #include "Core/MemFault.h" #include "Core/MemMap.h" #include "Core/MIPS/JitCommon/JitCommon.h" +#include "Core/Debugger/SymbolMap.h" + +// Stack walking stuff +#include "Core/MIPS/MIPSStackWalk.h" +#include "Core/MIPS/MIPSDebugInterface.h" +#include "Core/HLE/sceKernelThread.h" namespace Memory { @@ -132,8 +140,7 @@ bool HandleFault(uintptr_t hostAddress, void *ctx) { } } - - // OK, a guest executable did a bad access. Take care of it. + // OK, a guest executable did a bad access. Let's handle it. uint32_t guestAddress = invalidHostAddress ? 0xFFFFFFFFUL : (uint32_t)(hostAddress - baseAddress); @@ -317,3 +324,31 @@ bool HandleFault(uintptr_t hostAddress, void *ctx) { #endif } // namespace Memory + +std::vector WalkCurrentStack(int threadID) { + DebugInterface *cpuDebug = currentDebugMIPS; + + auto threads = GetThreadsInfo(); + uint32_t entry = cpuDebug->GetPC(); + uint32_t stackTop = 0; + for (const DebugThreadInfo &th : threads) { + if ((threadID == -1 && th.isCurrent) || th.id == threadID) { + entry = th.entrypoint; + stackTop = th.initialStack; + break; + } + } + + uint32_t ra = cpuDebug->GetRegValue(0, MIPS_REG_RA); + uint32_t sp = cpuDebug->GetRegValue(0, MIPS_REG_SP); + return MIPSStackWalk::Walk(cpuDebug->GetPC(), ra, sp, entry, stackTop); +} + +std::string FormatStackTrace(const std::vector &frames) { + std::stringstream str; + for (const auto &frame : frames) { + std::string desc = g_symbolMap->GetDescription(frame.entry); + str << StringFromFormat("%s (%08x+%03x, pc: %08x sp: %08x)\n", desc.c_str(), frame.entry, frame.pc - frame.entry, frame.pc, frame.sp); + } + return str.str(); +} diff --git a/Core/MemFault.h b/Core/MemFault.h index 4de4cbdf6b82..3e825d2e53f4 100644 --- a/Core/MemFault.h +++ b/Core/MemFault.h @@ -2,6 +2,8 @@ #include +#include "Core/MIPS/MIPSStackWalk.h" + namespace Memory { void MemFault_Init(); @@ -19,3 +21,10 @@ void MemFault_IgnoreLastCrash(); bool HandleFault(uintptr_t hostAddress, void *context); } + +// Stack walk utility function, walks from the current state. Useful in the debugger and crash report screens etc. +// Doesn't exactly belong here maybe, but can think of worse locations. +// threadID can be -1 for current. +std::vector WalkCurrentStack(int threadID); + +std::string FormatStackTrace(const std::vector &frames); diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index cf659e9f2e14..af665bdd6cb2 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -1201,7 +1201,7 @@ static void DrawCrashDump(UIContext *ctx, const Path &gamePath) { ctx->PushScissor(Bounds(x, y, columnWidth, height)); - INFO_LOG(SYSTEM, "DrawCrashDump (%d %d %d %d)", x, y, columnWidth, height); + // INFO_LOG(SYSTEM, "DrawCrashDump (%d %d %d %d)", x, y, columnWidth, height); snprintf(statbuf, sizeof(statbuf), R"(%s %s (%s) @@ -1262,6 +1262,14 @@ Invalid / Unknown (%d) ctx->Draw()->DrawTextShadow(ubuntu24, kernelState.c_str(), x, y, 0xFFFFFFFF); + y += 40; + + ctx->Draw()->SetFontScale(.5f, .5f); + + ctx->Draw()->DrawTextShadow(ubuntu24, info.stackTrace.c_str(), x, y, 0xFFFFFFFF); + + ctx->Draw()->SetFontScale(.7f, .7f); + ctx->PopScissor(); // Draw some additional stuff to the right. diff --git a/libretro/Makefile.common b/libretro/Makefile.common index 39e68cc1d257..325571d3cfe8 100644 --- a/libretro/Makefile.common +++ b/libretro/Makefile.common @@ -650,6 +650,7 @@ SOURCES_CXX += \ $(COREDIR)/MIPS/MIPSInt.cpp \ $(COREDIR)/MIPS/MIPSIntVFPU.cpp \ $(COREDIR)/MIPS/MIPSTables.cpp \ + $(COREDIR)/MIPS/MIPSStackWalk.cpp \ $(COREDIR)/MIPS/MIPSVFPUUtils.cpp \ $(COREDIR)/MemFault.cpp \ $(COREDIR)/MemMap.cpp \ From 38f4cc4cc97baf1e7c8abe1ee0ba8bb21471c863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 29 Mar 2023 10:21:49 +0200 Subject: [PATCH 03/25] Make reverse mapping lookup work for the simple PSP buttons. --- Common/Input/InputState.h | 1 - Common/VR/PPSSPPVR.cpp | 3 + Core/ControlMapper.cpp | 135 ++++++++++++++------ Core/ControlMapper.h | 16 +++ Core/Debugger/WebSocket/InputSubscriber.cpp | 3 + Core/HLE/sceCtrl.h | 3 +- Core/KeyMap.cpp | 21 ++- Core/TiltEventProcessor.cpp | 2 + Qt/QtMain.cpp | 1 - SDL/SDLJoystick.cpp | 1 - android/jni/app-android.cpp | 1 - ios/ViewController.mm | 6 - 12 files changed, 129 insertions(+), 64 deletions(-) diff --git a/Common/Input/InputState.h b/Common/Input/InputState.h index 5de44996ede6..1c7181572d4c 100644 --- a/Common/Input/InputState.h +++ b/Common/Input/InputState.h @@ -183,7 +183,6 @@ struct AxisInput { int deviceId; int axisId; // Android axis Ids are the canonical ones. float value; - int flags; }; // Is there a nicer place for this stuff? It's here to avoid dozens of linking errors in UnitTest.. diff --git a/Common/VR/PPSSPPVR.cpp b/Common/VR/PPSSPPVR.cpp index 9e00e75aa495..30df54352c54 100644 --- a/Common/VR/PPSSPPVR.cpp +++ b/Common/VR/PPSSPPVR.cpp @@ -9,6 +9,9 @@ #include "Common/VR/VRMath.h" #include "Common/VR/VRRenderer.h" +#include "Common/Input/InputState.h" +#include "Common/Input/KeyCodes.h" + #include "Common/GPU/Vulkan/VulkanContext.h" #include "Common/Math/lin/matrix4x4.h" diff --git a/Core/ControlMapper.cpp b/Core/ControlMapper.cpp index 3cef1079f123..d1cb5d223c03 100644 --- a/Core/ControlMapper.cpp +++ b/Core/ControlMapper.cpp @@ -20,6 +20,11 @@ static float MapAxisValue(float v) { return sign * Clamp(invDeadzone + (abs(v) - deadzone) / (1.0f - deadzone) * (sensitivity - invDeadzone), 0.0f, 1.0f); } +// TODO: Possibly make these configurable? +float GetDeviceAxisThreshold(int device) { + return device == DEVICE_ID_MOUSE ? AXIS_BIND_THRESHOLD_MOUSE : AXIS_BIND_THRESHOLD; +} + void ConvertAnalogStick(float &x, float &y) { const bool isCircular = g_Config.bAnalogIsCircular; @@ -97,21 +102,89 @@ void ControlMapper::SetPSPAxis(int device, char axis, float value, int stick) { } } -bool ControlMapper::Key(const KeyInput &key, bool *pauseTrigger) { - std::vector pspKeys; - KeyMap::InputMappingToPspButton(InputMapping(key.deviceId, key.keyCode), &pspKeys); +static int RotatePSPKeyCode(int x) { + switch (x) { + case CTRL_UP: return CTRL_RIGHT; + case CTRL_RIGHT: return CTRL_DOWN; + case CTRL_DOWN: return CTRL_LEFT; + case CTRL_LEFT: return CTRL_UP; + default: + return x; + } +} + +bool ControlMapper::UpdatePSPState() { + // Instead of taking an input key and finding what it outputs, we loop through the OUTPUTS and + // see if the input that corresponds to it has a value. That way we can easily implement all sorts + // of crazy input combos if needed. + + // For the PSP's button inputs, we just go through and put the flags together. + uint32_t buttonMask = 0; + + int rotations = 0; + switch (g_Config.iInternalScreenRotation) { + case ROTATION_LOCKED_HORIZONTAL180: rotations = 2; break; + case ROTATION_LOCKED_VERTICAL: rotations = 1; break; + case ROTATION_LOCKED_VERTICAL180: rotations = 3; break; + } + + for (int i = 0; i < 32; i++) { + uint32_t mask = 1 << i; + if (!(mask & CTRL_MASK_USER)) { + // Not a mappable button bit + continue; + } + + uint32_t mapping = mask; + for (int i = 0; i < rotations; i++) { + mapping = RotatePSPKeyCode(mapping); + } + + std::vector inputMappings; + // This is really "MappingsFromPspButtons". + if (!KeyMap::InputMappingsFromPspButton(mapping, &inputMappings, false)) + continue; + + for (int j = 0; j < inputMappings.size(); j++) { + auto iter = curInput_.find(inputMappings[j]) ; + if (iter != curInput_.end() && iter->second > 0.0f) { + buttonMask |= mask; + } + } + } + + setAllPSPButtonStates_(buttonMask); - if (pspKeys.size() && (key.flags & KEY_IS_REPEAT)) { + // OK, handle all the virtual keys next. For these we need to do deltas here and send events. + + // Now let's look at the four axes. + for (int i = 0; i < 4; i++) { + + } + + // TODO: Here we need to diff pspState with prevPspState_ to generate + // any new PSP key events. Though for the actual PSP buttons themselves (not the sticks), + // we could just or them together and set them all at once. + return true; +} + +bool ControlMapper::Key(const KeyInput &key, bool *pauseTrigger) { + if (key.flags & KEY_IS_REPEAT) { // Claim that we handled this. Prevents volume key repeats from popping up the volume control on Android. return true; } - for (size_t i = 0; i < pspKeys.size(); i++) { - SetPSPKey(key.deviceId, pspKeys[i], key.flags); + InputMapping mapping(key.deviceId, key.keyCode); + + if (key.flags & KEY_DOWN) { + curInput_[mapping] = 1.0f; + } else if (key.flags & KEY_UP) { + curInput_.erase(mapping); } + std::vector pspKeys; + KeyMap::InputMappingToPspButton(InputMapping(key.deviceId, key.keyCode), &pspKeys); DEBUG_LOG(SYSTEM, "Key: %d DeviceId: %d", key.keyCode, key.deviceId); - if (!pspKeys.size() || key.deviceId == DEVICE_ID_DEFAULT) { if ((key.flags & KEY_DOWN) && key.keyCode == NKCODE_BACK) { *pauseTrigger = true; @@ -119,18 +192,22 @@ bool ControlMapper::Key(const KeyInput &key, bool *pauseTrigger) { } } - return pspKeys.size() > 0; + return UpdatePSPState(); } void ControlMapper::Axis(const AxisInput &axis) { if (axis.value > 0) { - ProcessAxis(axis, 1); + InputMapping mapping(axis.deviceId, axis.axisId, 1); + curInput_[mapping] = axis.value; } else if (axis.value < 0) { - ProcessAxis(axis, -1); - } else if (axis.value == 0) { + InputMapping mapping(axis.deviceId, axis.axisId, -1); + curInput_[mapping] = axis.value; + } else if (axis.value == 0.0f) { // Threshold? // Both directions! Prevents sticking for digital input devices that are axises (like HAT) - ProcessAxis(axis, 1); - ProcessAxis(axis, -1); + InputMapping mappingPositive(axis.deviceId, axis.axisId, -1); + InputMapping mappingNegative(axis.deviceId, axis.axisId, -1); + curInput_[mappingPositive] = 0.0f; + curInput_[mappingNegative] = 0.0f; } } @@ -167,17 +244,6 @@ inline bool IsAnalogStickKey(int key) { } } -static int RotatePSPKeyCode(int x) { - switch (x) { - case CTRL_UP: return CTRL_RIGHT; - case CTRL_RIGHT: return CTRL_DOWN; - case CTRL_DOWN: return CTRL_LEFT; - case CTRL_LEFT: return CTRL_UP; - default: - return x; - } -} - void ControlMapper::SetVKeyAnalog(int deviceId, char axis, int stick, int virtualKeyMin, int virtualKeyMax, bool setZero) { // The down events can repeat, so just trust the virtKeys_ array. bool minDown = virtKeys_[virtualKeyMin - VIRTKEY_FIRST]; @@ -199,6 +265,7 @@ void ControlMapper::PSPKey(int deviceId, int pspKeyCode, int flags) { } void ControlMapper::SetPSPKey(int deviceId, int pspKeyCode, int flags) { + /* if (pspKeyCode >= VIRTKEY_FIRST) { int vk = pspKeyCode - VIRTKEY_FIRST; if (flags & KEY_DOWN) { @@ -210,29 +277,13 @@ void ControlMapper::SetPSPKey(int deviceId, int pspKeyCode, int flags) { onVKey(deviceId, pspKeyCode, false); } } else { - int rotations = 0; - switch (g_Config.iInternalScreenRotation) { - case ROTATION_LOCKED_HORIZONTAL180: - rotations = 2; - break; - case ROTATION_LOCKED_VERTICAL: - rotations = 1; - break; - case ROTATION_LOCKED_VERTICAL180: - rotations = 3; - break; - } - - for (int i = 0; i < rotations; i++) { - pspKeyCode = RotatePSPKeyCode(pspKeyCode); - } - // INFO_LOG(SYSTEM, "pspKey %d %d", pspKeyCode, flags); if (flags & KEY_DOWN) setPSPButtonState_(pspKeyCode, true); if (flags & KEY_UP) setPSPButtonState_(pspKeyCode, false); } + */ } void ControlMapper::onVKey(int deviceId, int vkey, bool down) { @@ -343,7 +394,7 @@ void ControlMapper::ProcessAxis(const AxisInput &axis, int direction) { } int axisState = 0; - float threshold = axis.deviceId == DEVICE_ID_MOUSE ? AXIS_BIND_THRESHOLD_MOUSE : AXIS_BIND_THRESHOLD; + float threshold = GetDeviceAxisThreshold(axis.deviceId); if (direction == 1 && axis.value >= threshold) { axisState = 1; } else if (direction == -1 && axis.value <= -threshold) { diff --git a/Core/ControlMapper.h b/Core/ControlMapper.h index 74783f0a7e5a..27b6cd22656f 100644 --- a/Core/ControlMapper.h +++ b/Core/ControlMapper.h @@ -4,6 +4,7 @@ #include "Core/KeyMap.h" #include +#include // Utilities for mapping input events to PSP inputs and virtual keys. // Main use is of course from EmuScreen.cpp, but also useful from control settings etc. @@ -11,6 +12,17 @@ // At some point I want to refactor this from using callbacks to simply providing lists of events. // Still it won't be able to be completely stateless due to the 2-D processing of analog sticks. +// Unified representation. Might spread across the code base later. +struct ControlInputKey { + int direction; // 0 if key, 1 or -1 if axis. + int deviceId; + int controlId; // key or axis. + + bool operator < (const ControlInputKey &other) const { + return memcmp(this, &other, sizeof(*this)) < 0; + } +}; + class ControlMapper { public: void Update(); @@ -34,6 +46,8 @@ class ControlMapper { void SetRawCallback(std::function setRawAnalog); private: + bool UpdatePSPState(); + void ProcessAxis(const AxisInput &axis, int direction); void SetVKeyAnalog(int deviceId, char axis, int stick, int virtualKeyMin, int virtualKeyMax, bool setZero = true); @@ -57,6 +71,8 @@ class ControlMapper { bool autoRotatingAnalogCW_ = false; bool autoRotatingAnalogCCW_ = false; + std::map curInput_; + // Callbacks std::function onVKey_; std::function setAllPSPButtonStates_; diff --git a/Core/Debugger/WebSocket/InputSubscriber.cpp b/Core/Debugger/WebSocket/InputSubscriber.cpp index fd0f478e76f1..49db4758ca26 100644 --- a/Core/Debugger/WebSocket/InputSubscriber.cpp +++ b/Core/Debugger/WebSocket/InputSubscriber.cpp @@ -192,6 +192,7 @@ void WebSocketInputState::ButtonsPress(DebuggerRequest &req) { } press.button = info->second; + // TODO: Route into the control mapper's PSPKey function instead. __CtrlButtonDown(press.button); pressTickets_.push_back(press); } @@ -205,6 +206,7 @@ void WebSocketInputState::Broadcast(net::WebSocketServer *ws) { for (PressInfo &press : pressTickets_) { press.duration--; if (press.duration == -1) { + // TODO: Route into the control mapper's PSPKey function instead. __CtrlButtonUp(press.button); ws->Send(press.Event()); } @@ -254,6 +256,7 @@ void WebSocketInputState::AnalogSend(DebuggerRequest &req) { if (!AnalogValue(req, &x, "x") || !AnalogValue(req, &y, "y")) return; + // TODO: Route into the control mapper's PSPKey function or similar instead. __CtrlSetAnalogXY(stick == "left" ? CTRL_STICK_LEFT : CTRL_STICK_RIGHT, x, y); req.Respond(); diff --git a/Core/HLE/sceCtrl.h b/Core/HLE/sceCtrl.h index 8307247e1730..c163f2174442 100644 --- a/Core/HLE/sceCtrl.h +++ b/Core/HLE/sceCtrl.h @@ -70,7 +70,8 @@ void __CtrlShutdown(); void __CtrlButtonDown(u32 buttonBit); // Call this whenever a button is released. Similar to __CtrlButtonDown(). void __CtrlButtonUp(u32 buttonBit); -// To be used by the new mapping code. +// Sets the full state. Used by the new control mapper. The above two functions +// should go away over time. void __CtrlSetAllButtons(u32 bitsToSet, u32 bitsToClear); // Call this to set the position of an analog stick, ideally when it changes. diff --git a/Core/KeyMap.cpp b/Core/KeyMap.cpp index 21cc22554e81..f303ab3978f9 100644 --- a/Core/KeyMap.cpp +++ b/Core/KeyMap.cpp @@ -43,7 +43,7 @@ std::set g_seenPads; std::map g_padNames; std::set g_seenDeviceIds; -bool g_swapped_keys = false; +bool g_swapDpadWithLStick = false; // TODO: This is such a mess... void UpdateNativeMenuKeys() { @@ -472,8 +472,8 @@ std::vector GetMappableKeys() { return temp; } -int CheckAxisSwap(int btn) { - if (g_swapped_keys) { +inline int CheckAxisSwap(int btn) { + if (g_swapDpadWithLStick) { switch (btn) { case CTRL_UP: btn = VIRTKEY_AXIS_Y_MAX; break; case VIRTKEY_AXIS_Y_MAX: btn = CTRL_UP; break; @@ -488,19 +488,18 @@ int CheckAxisSwap(int btn) { return btn; } -static bool FindKeyMapping(const InputMapping &mapping, std::vector *pspButtons) { +bool InputMappingToPspButton(const InputMapping &mapping, std::vector *pspButtons) { + bool found = false; for (auto iter = g_controllerMap.begin(); iter != g_controllerMap.end(); ++iter) { for (auto iter2 = iter->second.begin(); iter2 != iter->second.end(); ++iter2) { if (*iter2 == mapping) { - pspButtons->push_back(CheckAxisSwap(iter->first)); + if (pspButtons) + pspButtons->push_back(CheckAxisSwap(iter->first)); + found = true; } } } - return pspButtons->size() > 0; -} - -bool InputMappingToPspButton(const InputMapping &mapping, std::vector *pspButtons) { - return FindKeyMapping(mapping, pspButtons); + return found; } bool InputMappingsFromPspButton(int btn, std::vector *mappings, bool ignoreMouse) { @@ -774,7 +773,7 @@ std::string PadName(int deviceId) { // Swap direction buttons and left analog axis void SwapAxis() { - g_swapped_keys = !g_swapped_keys; + g_swapDpadWithLStick = !g_swapDpadWithLStick; } bool HasChanged(int &prevGeneration) { diff --git a/Core/TiltEventProcessor.cpp b/Core/TiltEventProcessor.cpp index 5d142955a836..716fee429ecc 100644 --- a/Core/TiltEventProcessor.cpp +++ b/Core/TiltEventProcessor.cpp @@ -129,6 +129,8 @@ inline float clamp(float f) { return f; } +// TODO: Instead of __Ctrl, route data into the ControlMapper. + void GenerateAnalogStickEvent(float tiltX, float tiltY) { __CtrlSetAnalogXY(CTRL_STICK_LEFT, clamp(tiltX), clamp(tiltY)); } diff --git a/Qt/QtMain.cpp b/Qt/QtMain.cpp index 411f26e1fdb0..17edeefb741f 100644 --- a/Qt/QtMain.cpp +++ b/Qt/QtMain.cpp @@ -725,7 +725,6 @@ void MainUI::updateAccelerometer() { if (reading) { AxisInput axis; axis.deviceId = DEVICE_ID_ACCELEROMETER; - axis.flags = 0; axis.axisId = JOYSTICK_AXIS_ACCELEROMETER_X; axis.value = reading->x(); diff --git a/SDL/SDLJoystick.cpp b/SDL/SDLJoystick.cpp index 59906f4a3e75..2f6b47171d0e 100644 --- a/SDL/SDLJoystick.cpp +++ b/SDL/SDLJoystick.cpp @@ -187,7 +187,6 @@ void SDLJoystick::ProcessInput(SDL_Event &event){ if (axis.value > 1.0f) axis.value = 1.0f; if (axis.value < -1.0f) axis.value = -1.0f; axis.deviceId = DEVICE_ID_PAD_0 + getDeviceIndex(event.caxis.which); - axis.flags = 0; NativeAxis(axis); break; case SDL_CONTROLLERDEVICEREMOVED: diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 8dfdf662b9d2..78e7a100bc88 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -1221,7 +1221,6 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_accelerometer(JNIEnv *, AxisInput axis; axis.deviceId = DEVICE_ID_ACCELEROMETER; - axis.flags = 0; axis.axisId = JOYSTICK_AXIS_ACCELEROMETER_X; axis.value = x; diff --git a/ios/ViewController.mm b/ios/ViewController.mm index 4535b39a8095..359eda075b40 100644 --- a/ios/ViewController.mm +++ b/ios/ViewController.mm @@ -460,7 +460,6 @@ - (void)buttonDown:(iCadeState)button break; } axis.deviceId = DEVICE_ID_PAD_0; - axis.flags = 0; NativeAxis(axis); } else { KeyInput key; @@ -530,7 +529,6 @@ - (void)buttonUp:(iCadeState)button break; } axis.deviceId = DEVICE_ID_PAD_0; - axis.flags = 0; NativeAxis(axis); } else { KeyInput key; @@ -687,7 +685,6 @@ - (void)setupController:(GCController *)controller extendedProfile.leftThumbstick.xAxis.valueChangedHandler = ^(GCControllerAxisInput *axis, float value) { AxisInput axisInput; axisInput.deviceId = DEVICE_ID_PAD_0; - axisInput.flags = 0; axisInput.axisId = JOYSTICK_AXIS_X; axisInput.value = value; NativeAxis(axisInput); @@ -696,7 +693,6 @@ - (void)setupController:(GCController *)controller extendedProfile.leftThumbstick.yAxis.valueChangedHandler = ^(GCControllerAxisInput *axis, float value) { AxisInput axisInput; axisInput.deviceId = DEVICE_ID_PAD_0; - axisInput.flags = 0; axisInput.axisId = JOYSTICK_AXIS_Y; axisInput.value = -value; NativeAxis(axisInput); @@ -706,7 +702,6 @@ - (void)setupController:(GCController *)controller extendedProfile.rightThumbstick.xAxis.valueChangedHandler = ^(GCControllerAxisInput *axis, float value) { AxisInput axisInput; axisInput.deviceId = DEVICE_ID_PAD_0; - axisInput.flags = 0; axisInput.axisId = JOYSTICK_AXIS_Z; axisInput.value = value; NativeAxis(axisInput); @@ -715,7 +710,6 @@ - (void)setupController:(GCController *)controller extendedProfile.rightThumbstick.yAxis.valueChangedHandler = ^(GCControllerAxisInput *axis, float value) { AxisInput axisInput; axisInput.deviceId = DEVICE_ID_PAD_0; - axisInput.flags = 0; axisInput.axisId = JOYSTICK_AXIS_RZ; axisInput.value = -value; NativeAxis(axisInput); From 8dabcaea7d043bea9ee07d894e959105ea987bf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 29 Mar 2023 12:30:55 +0200 Subject: [PATCH 04/25] Enable virtual key mappings in UpdatePSPState --- Core/ControlMapper.cpp | 44 +++++++++++++++++++++++++++++++----------- Core/ControlMapper.h | 2 +- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/Core/ControlMapper.cpp b/Core/ControlMapper.cpp index d1cb5d223c03..13476e49730f 100644 --- a/Core/ControlMapper.cpp +++ b/Core/ControlMapper.cpp @@ -118,9 +118,6 @@ bool ControlMapper::UpdatePSPState() { // see if the input that corresponds to it has a value. That way we can easily implement all sorts // of crazy input combos if needed. - // For the PSP's button inputs, we just go through and put the flags together. - uint32_t buttonMask = 0; - int rotations = 0; switch (g_Config.iInternalScreenRotation) { case ROTATION_LOCKED_HORIZONTAL180: rotations = 2; break; @@ -128,6 +125,8 @@ bool ControlMapper::UpdatePSPState() { case ROTATION_LOCKED_VERTICAL180: rotations = 3; break; } + // For the PSP's button inputs, we just go through and put the flags together. + uint32_t buttonMask = 0; for (int i = 0; i < 32; i++) { uint32_t mask = 1 << i; if (!(mask & CTRL_MASK_USER)) { @@ -141,13 +140,13 @@ bool ControlMapper::UpdatePSPState() { } std::vector inputMappings; - // This is really "MappingsFromPspButtons". if (!KeyMap::InputMappingsFromPspButton(mapping, &inputMappings, false)) continue; + // If a mapping could consist of a combo, we could trivially check it here. for (int j = 0; j < inputMappings.size(); j++) { auto iter = curInput_.find(inputMappings[j]) ; - if (iter != curInput_.end() && iter->second > 0.0f) { + if (iter != curInput_.end() && iter->second > GetDeviceAxisThreshold(iter->first.deviceId)) { buttonMask |= mask; } } @@ -156,12 +155,34 @@ bool ControlMapper::UpdatePSPState() { setAllPSPButtonStates_(buttonMask); // OK, handle all the virtual keys next. For these we need to do deltas here and send events. + for (int i = 0; i < VIRTKEY_COUNT; i++) { + int vkId = i + VIRTKEY_FIRST; + std::vector inputMappings; + if (!KeyMap::InputMappingsFromPspButton(vkId, &inputMappings, false)) + continue; + bool value = false; - // Now let's look at the four axes. - for (int i = 0; i < 4; i++) { + // If a mapping could consist of a combo, we could trivially check it here. + for (int j = 0; j < inputMappings.size(); j++) { + auto iter = curInput_.find(inputMappings[j]); + if (iter != curInput_.end() && iter->second > GetDeviceAxisThreshold(iter->first.deviceId)) { + value = true; + } + } + if (!lastVirtKeys_[i] && value) { + onVKeyDown_(vkId); + } else if (lastVirtKeys_[i] && !value) { + onVKeyUp_(vkId); + } + lastVirtKeys_[i] = value; } + // Now let's look at the four axes. + // for (int i = 0; i < 4; i++) { + + // } + // TODO: Here we need to diff pspState with prevPspState_ to generate // any new PSP key events. Though for the actual PSP buttons themselves (not the sticks), // we could just or them together and set them all at once. @@ -182,10 +203,9 @@ bool ControlMapper::Key(const KeyInput &key, bool *pauseTrigger) { curInput_.erase(mapping); } - std::vector pspKeys; - KeyMap::InputMappingToPspButton(InputMapping(key.deviceId, key.keyCode), &pspKeys); + bool mappingFound = KeyMap::InputMappingToPspButton(InputMapping(key.deviceId, key.keyCode), nullptr); DEBUG_LOG(SYSTEM, "Key: %d DeviceId: %d", key.keyCode, key.deviceId); - if (!pspKeys.size() || key.deviceId == DEVICE_ID_DEFAULT) { + if (!mappingFound || key.deviceId == DEVICE_ID_DEFAULT) { if ((key.flags & KEY_DOWN) && key.keyCode == NKCODE_BACK) { *pauseTrigger = true; return true; @@ -245,6 +265,7 @@ inline bool IsAnalogStickKey(int key) { } void ControlMapper::SetVKeyAnalog(int deviceId, char axis, int stick, int virtualKeyMin, int virtualKeyMax, bool setZero) { + /* // The down events can repeat, so just trust the virtKeys_ array. bool minDown = virtKeys_[virtualKeyMin - VIRTKEY_FIRST]; bool maxDown = virtKeys_[virtualKeyMax - VIRTKEY_FIRST]; @@ -258,6 +279,7 @@ void ControlMapper::SetVKeyAnalog(int deviceId, char axis, int stick, int virtua if (setZero || minDown || maxDown) { SetPSPAxis(deviceId, axis, value, stick); } + */ } void ControlMapper::PSPKey(int deviceId, int pspKeyCode, int flags) { @@ -345,7 +367,7 @@ void ControlMapper::ProcessAxis(const AxisInput &axis, int direction) { return; } - const float scale = virtKeys_[VIRTKEY_ANALOG_LIGHTLY - VIRTKEY_FIRST] ? g_Config.fAnalogLimiterDeadzone : 1.0f; + const float scale = lastVirtKeys_[VIRTKEY_ANALOG_LIGHTLY - VIRTKEY_FIRST] ? g_Config.fAnalogLimiterDeadzone : 1.0f; std::vector results; KeyMap::InputMappingToPspButton(InputMapping(axis.deviceId, axis.axisId, direction), &results); diff --git a/Core/ControlMapper.h b/Core/ControlMapper.h index 27b6cd22656f..a84b2858738d 100644 --- a/Core/ControlMapper.h +++ b/Core/ControlMapper.h @@ -58,7 +58,7 @@ class ControlMapper { void onVKey(int deviceId, int vkey, bool down); // To track mappable virtual keys. We can have as many as we want. - bool virtKeys_[VIRTKEY_COUNT]{}; + bool lastVirtKeys_[VIRTKEY_COUNT]{}; // De-noise mapped axis updates int axisState_[JOYSTICK_AXIS_MAX]{}; From 526b4f782de8226bc72389b79067b93ab0b41f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 29 Mar 2023 13:50:57 +0200 Subject: [PATCH 05/25] Fixes, add callback for analog virtual keys Some fixes --- Core/ControlMapper.cpp | 306 +++++++++++++----------------------- Core/ControlMapper.h | 29 +--- Core/KeyMap.cpp | 49 ++++++ Core/KeyMap.h | 4 +- UI/ControlMappingScreen.cpp | 1 + UI/EmuScreen.cpp | 1 + 6 files changed, 172 insertions(+), 218 deletions(-) diff --git a/Core/ControlMapper.cpp b/Core/ControlMapper.cpp index 13476e49730f..354005a157d1 100644 --- a/Core/ControlMapper.cpp +++ b/Core/ControlMapper.cpp @@ -11,6 +11,27 @@ #include "Core/CoreParameter.h" #include "Core/System.h" +// TODO: Possibly make these configurable? +static float GetDeviceAxisThreshold(int device) { + return device == DEVICE_ID_MOUSE ? AXIS_BIND_THRESHOLD_MOUSE : AXIS_BIND_THRESHOLD; +} + +static int GetOppositeVKey(int vkey) { + switch (vkey) { + case VIRTKEY_AXIS_X_MIN: return VIRTKEY_AXIS_X_MAX; break; + case VIRTKEY_AXIS_X_MAX: return VIRTKEY_AXIS_X_MIN; break; + case VIRTKEY_AXIS_Y_MIN: return VIRTKEY_AXIS_Y_MAX; break; + case VIRTKEY_AXIS_Y_MAX: return VIRTKEY_AXIS_Y_MIN; break; + case VIRTKEY_AXIS_RIGHT_X_MIN: return VIRTKEY_AXIS_RIGHT_X_MAX; break; + case VIRTKEY_AXIS_RIGHT_X_MAX: return VIRTKEY_AXIS_RIGHT_X_MIN; break; + case VIRTKEY_AXIS_RIGHT_Y_MIN: return VIRTKEY_AXIS_RIGHT_Y_MAX; break; + case VIRTKEY_AXIS_RIGHT_Y_MAX: return VIRTKEY_AXIS_RIGHT_Y_MIN; break; + default: + return 0; + } +} + +// This is applied on the circular radius, not directly on the axes. static float MapAxisValue(float v) { const float deadzone = g_Config.fAnalogDeadzone; const float invDeadzone = g_Config.fAnalogInverseDeadzone; @@ -20,11 +41,6 @@ static float MapAxisValue(float v) { return sign * Clamp(invDeadzone + (abs(v) - deadzone) / (1.0f - deadzone) * (sensitivity - invDeadzone), 0.0f, 1.0f); } -// TODO: Possibly make these configurable? -float GetDeviceAxisThreshold(int device) { - return device == DEVICE_ID_MOUSE ? AXIS_BIND_THRESHOLD_MOUSE : AXIS_BIND_THRESHOLD; -} - void ConvertAnalogStick(float &x, float &y) { const bool isCircular = g_Config.bAnalogIsCircular; @@ -48,10 +64,12 @@ void ConvertAnalogStick(float &x, float &y) { void ControlMapper::SetCallbacks( std::function onVKey, + std::function onVKeyAnalog, std::function setAllPSPButtonStates, std::function setPSPButtonState, std::function setPSPAnalog) { onVKey_ = onVKey; + onVKeyAnalog_ = onVKey; setAllPSPButtonStates_ = setAllPSPButtonStates; setPSPButtonState_ = setPSPButtonState; setPSPAnalog_ = setPSPAnalog; @@ -61,7 +79,7 @@ void ControlMapper::SetRawCallback(std::function setRaw setRawAnalog_ = setRawAnalog; } -void ControlMapper::SetPSPAxis(int device, char axis, float value, int stick) { +void ControlMapper::SetPSPAxis(int device, int stick, char axis, float value) { int axisId = axis == 'X' ? 0 : 1; float position[2]; @@ -113,7 +131,7 @@ static int RotatePSPKeyCode(int x) { } } -bool ControlMapper::UpdatePSPState() { +bool ControlMapper::UpdatePSPState(const InputMapping &changedMapping) { // Instead of taking an input key and finding what it outputs, we loop through the OUTPUTS and // see if the input that corresponds to it has a value. That way we can easily implement all sorts // of crazy input combos if needed. @@ -127,6 +145,7 @@ bool ControlMapper::UpdatePSPState() { // For the PSP's button inputs, we just go through and put the flags together. uint32_t buttonMask = 0; + uint32_t changedButtonMask = 0; for (int i = 0; i < 32; i++) { uint32_t mask = 1 << i; if (!(mask & CTRL_MASK_USER)) { @@ -134,25 +153,31 @@ bool ControlMapper::UpdatePSPState() { continue; } - uint32_t mapping = mask; + uint32_t mappingBit = mask; for (int i = 0; i < rotations; i++) { - mapping = RotatePSPKeyCode(mapping); + mappingBit = RotatePSPKeyCode(mappingBit); } std::vector inputMappings; - if (!KeyMap::InputMappingsFromPspButton(mapping, &inputMappings, false)) + if (!KeyMap::InputMappingsFromPspButton(mappingBit, &inputMappings, false)) continue; // If a mapping could consist of a combo, we could trivially check it here. - for (int j = 0; j < inputMappings.size(); j++) { - auto iter = curInput_.find(inputMappings[j]) ; + for (auto &mapping : inputMappings) { + // Check if the changed mapping was involved in this PSP key. + if (changedMapping == mapping) { + changedButtonMask |= mask; + } + + auto iter = curInput_.find(mapping); if (iter != curInput_.end() && iter->second > GetDeviceAxisThreshold(iter->first.deviceId)) { buttonMask |= mask; } } } - setAllPSPButtonStates_(buttonMask); + // We only request changing the buttons where the mapped input was involved. + setAllPSPButtonStates_(buttonMask & changedButtonMask, (~buttonMask) & changedButtonMask); // OK, handle all the virtual keys next. For these we need to do deltas here and send events. for (int i = 0; i < VIRTKEY_COUNT; i++) { @@ -160,32 +185,50 @@ bool ControlMapper::UpdatePSPState() { std::vector inputMappings; if (!KeyMap::InputMappingsFromPspButton(vkId, &inputMappings, false)) continue; - bool value = false; + float value = 0.0f; // If a mapping could consist of a combo, we could trivially check it here. - for (int j = 0; j < inputMappings.size(); j++) { - auto iter = curInput_.find(inputMappings[j]); - if (iter != curInput_.end() && iter->second > GetDeviceAxisThreshold(iter->first.deviceId)) { - value = true; + // Save the first device ID so we can pass it into onVKeyDown, which in turn needs it for the analog + // mapping which gets a little hacky. + float threshold = 1.0f; + bool touchedByMapping = false; + for (auto &mapping : inputMappings) { + if (mapping == changedMapping) { + touchedByMapping = true; + } + + auto iter = curInput_.find(mapping); + if (iter != curInput_.end()) { + if (mapping.IsAxis()) { + threshold = GetDeviceAxisThreshold(iter->first.deviceId); + value += iter->second; + } else { + value += iter->second; + } } } - if (!lastVirtKeys_[i] && value) { - onVKeyDown_(vkId); - } else if (lastVirtKeys_[i] && !value) { - onVKeyUp_(vkId); + if (!touchedByMapping) { + continue; } - lastVirtKeys_[i] = value; - } - // Now let's look at the four axes. - // for (int i = 0; i < 4; i++) { + value = clamp_value(value, 0.0f, 1.0f); - // } + // Derive bools from the floats using the device's threshold. + bool bPrevValue = virtKeys_[i] >= threshold; + bool bValue = value >= threshold; + if (virtKeys_[i] != value) { + // INFO_LOG(G3D, "vkeyanalog %s : %f", KeyMap::GetVirtKeyName(vkId), value); + onVKeyAnalog(changedMapping.deviceId, vkId, value); + } + virtKeys_[i] = value; + if (!bPrevValue && bValue) { + onVKey(vkId, true); + } else if (bPrevValue && !bValue) { + onVKey(vkId, false); + } + } - // TODO: Here we need to diff pspState with prevPspState_ to generate - // any new PSP key events. Though for the actual PSP buttons themselves (not the sticks), - // we could just or them together and set them all at once. return true; } @@ -200,10 +243,10 @@ bool ControlMapper::Key(const KeyInput &key, bool *pauseTrigger) { if (key.flags & KEY_DOWN) { curInput_[mapping] = 1.0f; } else if (key.flags & KEY_UP) { - curInput_.erase(mapping); + curInput_[mapping] = 0.0f; } - bool mappingFound = KeyMap::InputMappingToPspButton(InputMapping(key.deviceId, key.keyCode), nullptr); + bool mappingFound = KeyMap::InputMappingToPspButton(mapping, nullptr); DEBUG_LOG(SYSTEM, "Key: %d DeviceId: %d", key.keyCode, key.deviceId); if (!mappingFound || key.deviceId == DEVICE_ID_DEFAULT) { if ((key.flags & KEY_DOWN) && key.keyCode == NKCODE_BACK) { @@ -212,22 +255,26 @@ bool ControlMapper::Key(const KeyInput &key, bool *pauseTrigger) { } } - return UpdatePSPState(); + return UpdatePSPState(mapping); } void ControlMapper::Axis(const AxisInput &axis) { if (axis.value > 0) { InputMapping mapping(axis.deviceId, axis.axisId, 1); curInput_[mapping] = axis.value; + UpdatePSPState(mapping); } else if (axis.value < 0) { InputMapping mapping(axis.deviceId, axis.axisId, -1); - curInput_[mapping] = axis.value; + curInput_[mapping] = -axis.value; + UpdatePSPState(mapping); } else if (axis.value == 0.0f) { // Threshold? // Both directions! Prevents sticking for digital input devices that are axises (like HAT) - InputMapping mappingPositive(axis.deviceId, axis.axisId, -1); + InputMapping mappingPositive(axis.deviceId, axis.axisId, 1); InputMapping mappingNegative(axis.deviceId, axis.axisId, -1); curInput_[mappingPositive] = 0.0f; curInput_[mappingNegative] = 0.0f; + UpdatePSPState(mappingPositive); + UpdatePSPState(mappingNegative); } } @@ -248,55 +295,16 @@ void ControlMapper::Update() { } } -inline bool IsAnalogStickKey(int key) { - switch (key) { - case VIRTKEY_AXIS_X_MIN: - case VIRTKEY_AXIS_X_MAX: - case VIRTKEY_AXIS_Y_MIN: - case VIRTKEY_AXIS_Y_MAX: - case VIRTKEY_AXIS_RIGHT_X_MIN: - case VIRTKEY_AXIS_RIGHT_X_MAX: - case VIRTKEY_AXIS_RIGHT_Y_MIN: - case VIRTKEY_AXIS_RIGHT_Y_MAX: - return true; - default: - return false; - } -} - -void ControlMapper::SetVKeyAnalog(int deviceId, char axis, int stick, int virtualKeyMin, int virtualKeyMax, bool setZero) { - /* - // The down events can repeat, so just trust the virtKeys_ array. - bool minDown = virtKeys_[virtualKeyMin - VIRTKEY_FIRST]; - bool maxDown = virtKeys_[virtualKeyMax - VIRTKEY_FIRST]; - - const float scale = virtKeys_[VIRTKEY_ANALOG_LIGHTLY - VIRTKEY_FIRST] ? g_Config.fAnalogLimiterDeadzone : 1.0f; - float value = 0.0f; - if (minDown) - value -= scale; - if (maxDown) - value += scale; - if (setZero || minDown || maxDown) { - SetPSPAxis(deviceId, axis, value, stick); - } - */ -} - void ControlMapper::PSPKey(int deviceId, int pspKeyCode, int flags) { - SetPSPKey(deviceId, pspKeyCode, flags); -} - -void ControlMapper::SetPSPKey(int deviceId, int pspKeyCode, int flags) { - /* if (pspKeyCode >= VIRTKEY_FIRST) { int vk = pspKeyCode - VIRTKEY_FIRST; if (flags & KEY_DOWN) { - virtKeys_[vk] = true; - onVKey(deviceId, pspKeyCode, true); + virtKeys_[vk] = 1.0f; + onVKey(pspKeyCode, true); } if (flags & KEY_UP) { - virtKeys_[vk] = false; - onVKey(deviceId, pspKeyCode, false); + virtKeys_[vk] = 0.0f; + onVKey(pspKeyCode, false); } } else { // INFO_LOG(SYSTEM, "pspKey %d %d", pspKeyCode, flags); @@ -305,36 +313,35 @@ void ControlMapper::SetPSPKey(int deviceId, int pspKeyCode, int flags) { if (flags & KEY_UP) setPSPButtonState_(pspKeyCode, false); } - */ } -void ControlMapper::onVKey(int deviceId, int vkey, bool down) { +void ControlMapper::onVKeyAnalog(int deviceId, int vkey, float value) { + // Unfortunately, for digital->analog inputs to work sanely, we need to sum up + // with the opposite value too. + int stick = 0; + int axis = 'X'; + int opposite = GetOppositeVKey(vkey); + float sign = 1.0f; switch (vkey) { - case VIRTKEY_AXIS_X_MIN: - case VIRTKEY_AXIS_X_MAX: - SetVKeyAnalog(deviceId, 'X', CTRL_STICK_LEFT, VIRTKEY_AXIS_X_MIN, VIRTKEY_AXIS_X_MAX); - break; - case VIRTKEY_AXIS_Y_MIN: - case VIRTKEY_AXIS_Y_MAX: - SetVKeyAnalog(deviceId, 'Y', CTRL_STICK_LEFT, VIRTKEY_AXIS_Y_MIN, VIRTKEY_AXIS_Y_MAX); - break; - - case VIRTKEY_AXIS_RIGHT_X_MIN: - case VIRTKEY_AXIS_RIGHT_X_MAX: - SetVKeyAnalog(deviceId, 'X', CTRL_STICK_RIGHT, VIRTKEY_AXIS_RIGHT_X_MIN, VIRTKEY_AXIS_RIGHT_X_MAX); - break; - case VIRTKEY_AXIS_RIGHT_Y_MIN: - case VIRTKEY_AXIS_RIGHT_Y_MAX: - SetVKeyAnalog(deviceId, 'Y', CTRL_STICK_RIGHT, VIRTKEY_AXIS_RIGHT_Y_MIN, VIRTKEY_AXIS_RIGHT_Y_MAX); - break; - - case VIRTKEY_ANALOG_LIGHTLY: - SetVKeyAnalog(deviceId, 'X', CTRL_STICK_LEFT, VIRTKEY_AXIS_X_MIN, VIRTKEY_AXIS_X_MAX, false); - SetVKeyAnalog(deviceId, 'Y', CTRL_STICK_LEFT, VIRTKEY_AXIS_Y_MIN, VIRTKEY_AXIS_Y_MAX, false); - SetVKeyAnalog(deviceId, 'X', CTRL_STICK_RIGHT, VIRTKEY_AXIS_RIGHT_X_MIN, VIRTKEY_AXIS_RIGHT_X_MAX, false); - SetVKeyAnalog(deviceId, 'Y', CTRL_STICK_RIGHT, VIRTKEY_AXIS_RIGHT_Y_MIN, VIRTKEY_AXIS_RIGHT_Y_MAX, false); - break; + case VIRTKEY_AXIS_X_MIN: sign = -1.0f; break; + case VIRTKEY_AXIS_X_MAX: break; + case VIRTKEY_AXIS_Y_MIN: axis = 'Y'; sign = -1.0f; break; + case VIRTKEY_AXIS_Y_MAX: axis = 'Y'; break; + case VIRTKEY_AXIS_RIGHT_X_MIN: stick = CTRL_STICK_RIGHT; sign = -1.0f; break; + case VIRTKEY_AXIS_RIGHT_X_MAX: stick = CTRL_STICK_RIGHT; break; + case VIRTKEY_AXIS_RIGHT_Y_MIN: stick = CTRL_STICK_RIGHT; axis = 'Y'; sign = -1.0f; break; + case VIRTKEY_AXIS_RIGHT_Y_MAX: stick = CTRL_STICK_RIGHT; axis = 'Y'; break; + default: + if (onVKeyAnalog_) + onVKeyAnalog_(vkey, value); + return; + } + value -= virtKeys_[opposite - VIRTKEY_FIRST]; + SetPSPAxis(deviceId, stick, axis, sign * value); +} +void ControlMapper::onVKey(int vkey, bool down) { + switch (vkey) { case VIRTKEY_ANALOG_ROTATE_CW: if (down) { autoRotatingAnalogCW_ = true; @@ -353,7 +360,6 @@ void ControlMapper::onVKey(int deviceId, int vkey, bool down) { setPSPAnalog_(0, 0.0f, 0.0f); } break; - default: if (onVKey_) onVKey_(vkey, down); @@ -361,96 +367,6 @@ void ControlMapper::onVKey(int deviceId, int vkey, bool down) { } } -void ControlMapper::ProcessAxis(const AxisInput &axis, int direction) { - // Sanity check - if (axis.axisId < 0 || axis.axisId >= JOYSTICK_AXIS_MAX) { - return; - } - - const float scale = lastVirtKeys_[VIRTKEY_ANALOG_LIGHTLY - VIRTKEY_FIRST] ? g_Config.fAnalogLimiterDeadzone : 1.0f; - - std::vector results; - KeyMap::InputMappingToPspButton(InputMapping(axis.deviceId, axis.axisId, direction), &results); - - for (int result : results) { - float value = fabs(axis.value) * scale; - switch (result) { - case VIRTKEY_AXIS_X_MIN: - SetPSPAxis(axis.deviceId, 'X', -value, CTRL_STICK_LEFT); - break; - case VIRTKEY_AXIS_X_MAX: - SetPSPAxis(axis.deviceId, 'X', value, CTRL_STICK_LEFT); - break; - case VIRTKEY_AXIS_Y_MIN: - SetPSPAxis(axis.deviceId, 'Y', -value, CTRL_STICK_LEFT); - break; - case VIRTKEY_AXIS_Y_MAX: - SetPSPAxis(axis.deviceId, 'Y', value, CTRL_STICK_LEFT); - break; - - case VIRTKEY_AXIS_RIGHT_X_MIN: - SetPSPAxis(axis.deviceId, 'X', -value, CTRL_STICK_RIGHT); - break; - case VIRTKEY_AXIS_RIGHT_X_MAX: - SetPSPAxis(axis.deviceId, 'X', value, CTRL_STICK_RIGHT); - break; - case VIRTKEY_AXIS_RIGHT_Y_MIN: - SetPSPAxis(axis.deviceId, 'Y', -value, CTRL_STICK_RIGHT); - break; - case VIRTKEY_AXIS_RIGHT_Y_MAX: - SetPSPAxis(axis.deviceId, 'Y', value, CTRL_STICK_RIGHT); - break; - - case VIRTKEY_SPEED_ANALOG: - ProcessAnalogSpeed(axis, false); - break; - } - } - - std::vector resultsOpposite; - KeyMap::InputMappingToPspButton(InputMapping(axis.deviceId, axis.axisId, -direction), &resultsOpposite); - - for (int result : resultsOpposite) { - if (result == VIRTKEY_SPEED_ANALOG) - ProcessAnalogSpeed(axis, true); - } - - int axisState = 0; - float threshold = GetDeviceAxisThreshold(axis.deviceId); - if (direction == 1 && axis.value >= threshold) { - axisState = 1; - } else if (direction == -1 && axis.value <= -threshold) { - axisState = -1; - } else { - axisState = 0; - } - - if (axisState != axisState_[axis.axisId]) { - axisState_[axis.axisId] = axisState; - if (axisState != 0) { - for (size_t i = 0; i < results.size(); i++) { - if (!IsAnalogStickKey(results[i])) - SetPSPKey(axis.deviceId, results[i], KEY_DOWN); - } - // Also unpress the other direction (unless both directions press the same key.) - for (size_t i = 0; i < resultsOpposite.size(); i++) { - if (!IsAnalogStickKey(resultsOpposite[i]) && std::find(results.begin(), results.end(), resultsOpposite[i]) == results.end()) - SetPSPKey(axis.deviceId, resultsOpposite[i], KEY_UP); - } - } else if (axisState == 0) { - // Release both directions, trying to deal with some erratic controllers that can cause it to stick. - for (size_t i = 0; i < results.size(); i++) { - if (!IsAnalogStickKey(results[i])) - SetPSPKey(axis.deviceId, results[i], KEY_UP); - } - for (size_t i = 0; i < resultsOpposite.size(); i++) { - if (!IsAnalogStickKey(resultsOpposite[i])) - SetPSPKey(axis.deviceId, resultsOpposite[i], KEY_UP); - } - } - } -} - void ControlMapper::ProcessAnalogSpeed(const AxisInput &axis, bool opposite) { static constexpr float DEADZONE_THRESHOLD = 0.15f; static constexpr float DEADZONE_SCALE = 1.0f / (1.0f - DEADZONE_THRESHOLD); diff --git a/Core/ControlMapper.h b/Core/ControlMapper.h index a84b2858738d..cbcab1ce0bc2 100644 --- a/Core/ControlMapper.h +++ b/Core/ControlMapper.h @@ -8,21 +8,6 @@ // Utilities for mapping input events to PSP inputs and virtual keys. // Main use is of course from EmuScreen.cpp, but also useful from control settings etc. - -// At some point I want to refactor this from using callbacks to simply providing lists of events. -// Still it won't be able to be completely stateless due to the 2-D processing of analog sticks. - -// Unified representation. Might spread across the code base later. -struct ControlInputKey { - int direction; // 0 if key, 1 or -1 if axis. - int deviceId; - int controlId; // key or axis. - - bool operator < (const ControlInputKey &other) const { - return memcmp(this, &other, sizeof(*this)) < 0; - } -}; - class ControlMapper { public: void Update(); @@ -34,6 +19,7 @@ class ControlMapper { // Required callbacks void SetCallbacks( std::function onVKey, + std::function onVKeyAnalog, std::function setAllPSPButtonStates_, std::function setPSPButtonState, std::function setPSPAnalog); @@ -46,19 +32,17 @@ class ControlMapper { void SetRawCallback(std::function setRawAnalog); private: - bool UpdatePSPState(); + bool UpdatePSPState(const InputMapping &changedMapping); - void ProcessAxis(const AxisInput &axis, int direction); - void SetVKeyAnalog(int deviceId, char axis, int stick, int virtualKeyMin, int virtualKeyMax, bool setZero = true); + void SetPSPAxis(int deviceId, int stick, char axis, float value); - void SetPSPKey(int deviceId, int pspKeyCode, int flags); - void SetPSPAxis(int deviceId, char axis, float value, int stick); void ProcessAnalogSpeed(const AxisInput &axis, bool opposite); - void onVKey(int deviceId, int vkey, bool down); + void onVKey(int vkey, bool down); + void onVKeyAnalog(int deviceId, int vkey, float value); // To track mappable virtual keys. We can have as many as we want. - bool lastVirtKeys_[VIRTKEY_COUNT]{}; + float virtKeys_[VIRTKEY_COUNT]{}; // De-noise mapped axis updates int axisState_[JOYSTICK_AXIS_MAX]{}; @@ -75,6 +59,7 @@ class ControlMapper { // Callbacks std::function onVKey_; + std::function onVKeyAnalog_; std::function setAllPSPButtonStates_; std::function setPSPButtonState_; std::function setPSPAnalog_; diff --git a/Core/KeyMap.cpp b/Core/KeyMap.cpp index f303ab3978f9..a7742296bcf5 100644 --- a/Core/KeyMap.cpp +++ b/Core/KeyMap.cpp @@ -784,4 +784,53 @@ bool HasChanged(int &prevGeneration) { return false; } +static const char *g_vKeyNames[] = { + "AXIS_X_MIN", + "AXIS_Y_MIN", + "AXIS_X_MAX", + "AXIS_Y_MAX", + "RAPID_FIRE", + "FASTFORWARD", + "PAUSE", + "SPEED_TOGGLE", + "AXIS_RIGHT_X_MIN", + "AXIS_RIGHT_Y_MIN", + "AXIS_RIGHT_X_MAX", + "AXIS_RIGHT_Y_MAX", + "REWIND", + "SAVE_STATE", + "LOAD_STATE", + "NEXT_SLOT", + "TOGGLE_FULLSCREEN", + "ANALOG_LIGHTLY", + "AXIS_SWAP", + "DEVMENU", + "FRAME_ADVANCE", + "RECORD", + "SPEED_CUSTOM1", + "SPEED_CUSTOM2", + "TEXTURE_DUMP", + "TEXTURE_REPLACE", + "SCREENSHOT", + "MUTE_TOGGLE", + "OPENCHAT", + "ANALOG_ROTATE_CW", + "ANALOG_ROTATE_CCW", + "SCREEN_ROTATION_VERTICAL", + "SCREEN_ROTATION_VERTICAL180", + "SCREEN_ROTATION_HORIZONTAL", + "SCREEN_ROTATION_HORIZONTAL180", + "SPEED_ANALOG", + "VR_CAMERA_ADJUST", + "VR_CAMERA_RESET", +}; + +const char *GetVirtKeyName(int vkey) { + int index = vkey - VIRTKEY_FIRST; + if (index < 0 || index >= ARRAY_SIZE(g_vKeyNames)) { + return "N/A"; + } + return g_vKeyNames[index]; +} + } // KeyMap diff --git a/Core/KeyMap.h b/Core/KeyMap.h index 3d41e86c29ef..113117173b5b 100644 --- a/Core/KeyMap.h +++ b/Core/KeyMap.h @@ -29,6 +29,7 @@ #define KEYMAP_ERROR_KEY_ALREADY_USED -1 #define KEYMAP_ERROR_UNKNOWN_KEY 0 +// Don't change any of these - it'll break backwards compatibility with configs. enum { VIRTKEY_FIRST = 0x40000001, VIRTKEY_AXIS_X_MIN = 0x40000001, @@ -118,7 +119,8 @@ namespace KeyMap { std::string GetKeyOrAxisName(const InputMapping &mapping); std::string GetAxisName(int axisId); std::string GetPspButtonName(int btn); - const char* GetPspButtonNameCharPointer(int btn); + const char *GetVirtKeyName(int vkey); + const char *GetPspButtonNameCharPointer(int btn); std::vector GetMappableKeys(); diff --git a/UI/ControlMappingScreen.cpp b/UI/ControlMappingScreen.cpp index 1c4ee31635bd..8aad793e0d08 100644 --- a/UI/ControlMappingScreen.cpp +++ b/UI/ControlMappingScreen.cpp @@ -450,6 +450,7 @@ void KeyMappingNewMouseKeyDialog::axis(const AxisInput &axis) { AnalogSetupScreen::AnalogSetupScreen(const Path &gamePath) : UIDialogScreenWithGameBackground(gamePath) { mapper_.SetCallbacks( [](int vkey, bool down) {}, + [](int vkey, float analogValue) {}, [&](uint32_t bitsToSet, uint32_t bitsToClear) {}, [&](int button, bool down) {}, [&](int stick, float x, float y) { diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index af665bdd6cb2..74bc85fc33d6 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -177,6 +177,7 @@ EmuScreen::EmuScreen(const Path &filename) startDumping = false; controlMapper_.SetCallbacks( std::bind(&EmuScreen::onVKey, this, _1, _2), + [](int vkey, float analogValue) {}, [](uint32_t bitsToSet, uint32_t bitsToClear) { __CtrlSetAllButtons(bitsToSet, bitsToClear); }, From 7dfa587fe232bea694f67c97f96a4eb6754f5949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 30 Mar 2023 14:42:11 +0200 Subject: [PATCH 06/25] Remove in/out function arguments --- Core/ControlMapper.cpp | 20 +++++++++++--------- Core/ControlMapper.h | 2 +- UI/JoystickHistoryView.cpp | 8 ++++---- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/Core/ControlMapper.cpp b/Core/ControlMapper.cpp index 354005a157d1..2847feb204c3 100644 --- a/Core/ControlMapper.cpp +++ b/Core/ControlMapper.cpp @@ -41,13 +41,15 @@ static float MapAxisValue(float v) { return sign * Clamp(invDeadzone + (abs(v) - deadzone) / (1.0f - deadzone) * (sensitivity - invDeadzone), 0.0f, 1.0f); } -void ConvertAnalogStick(float &x, float &y) { +void ConvertAnalogStick(float x, float y, float *outX, float *outY) { const bool isCircular = g_Config.bAnalogIsCircular; float norm = std::max(fabsf(x), fabsf(y)); - - if (norm == 0.0f) + if (norm == 0.0f) { + *outX = x; + *outY = y; return; + } if (isCircular) { float newNorm = sqrtf(x * x + y * y); @@ -58,8 +60,8 @@ void ConvertAnalogStick(float &x, float &y) { } float mappedNorm = MapAxisValue(norm); - x = Clamp(x / norm * mappedNorm, -1.0f, 1.0f); - y = Clamp(y / norm * mappedNorm, -1.0f, 1.0f); + *outX = Clamp(x / norm * mappedNorm, -1.0f, 1.0f); + *outY = Clamp(y / norm * mappedNorm, -1.0f, 1.0f); } void ControlMapper::SetCallbacks( @@ -102,7 +104,7 @@ void ControlMapper::SetPSPAxis(int device, int stick, char axis, float value) { bool ignore = false; if (inDeadZone && lastNonDeadzoneDeviceID_[stick] != device) { // Ignore this event! See issue #15465 - ignore = true; + ignore = true; } if (!inDeadZone) { @@ -114,9 +116,9 @@ void ControlMapper::SetPSPAxis(int device, int stick, char axis, float value) { float x = history_[stick][0]; float y = history_[stick][1]; - - ConvertAnalogStick(x, y); - setPSPAnalog_(stick, x, y); + float convertedX, convertedY; + ConvertAnalogStick(x, y, &convertedX, &convertedY); + setPSPAnalog_(stick, convertedX, convertedY); } } diff --git a/Core/ControlMapper.h b/Core/ControlMapper.h index cbcab1ce0bc2..17b6fb5bfa32 100644 --- a/Core/ControlMapper.h +++ b/Core/ControlMapper.h @@ -66,4 +66,4 @@ class ControlMapper { std::function setRawAnalog_; }; -void ConvertAnalogStick(float &x, float &y); +void ConvertAnalogStick(float x, float y, float *outX, float *outY); diff --git a/UI/JoystickHistoryView.cpp b/UI/JoystickHistoryView.cpp index 3fdd895ac504..3aa1828f9c6b 100644 --- a/UI/JoystickHistoryView.cpp +++ b/UI/JoystickHistoryView.cpp @@ -34,8 +34,8 @@ void JoystickHistoryView::Draw(UIContext &dc) { float by = (iy + 1) * dx; if (type_ == StickHistoryViewType::OUTPUT) { - ConvertAnalogStick(ax, ay); - ConvertAnalogStick(bx, by); + ConvertAnalogStick(ax, ay, &ax, &ay); + ConvertAnalogStick(bx, by, &bx, &by); } ax = ax * minRadius + bounds_.centerX(); @@ -58,8 +58,8 @@ void JoystickHistoryView::Draw(UIContext &dc) { float by = fy; if (type_ == StickHistoryViewType::OUTPUT) { - ConvertAnalogStick(ax, ay); - ConvertAnalogStick(bx, by); + ConvertAnalogStick(ax, ay, &ax, &ay); + ConvertAnalogStick(bx, by, &bx, &by); } ax = ax * minRadius + bounds_.centerX(); From ca300a4dfd5d7ed43d28418fa6cab18c24b27def Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 30 Mar 2023 15:03:41 +0200 Subject: [PATCH 07/25] More callback fixes --- Core/ControlMapper.cpp | 21 +++++++++------------ Core/ControlMapper.h | 3 --- UI/EmuScreen.cpp | 6 +++++- UI/EmuScreen.h | 1 + 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/Core/ControlMapper.cpp b/Core/ControlMapper.cpp index 2847feb204c3..b1e4189b952b 100644 --- a/Core/ControlMapper.cpp +++ b/Core/ControlMapper.cpp @@ -71,7 +71,7 @@ void ControlMapper::SetCallbacks( std::function setPSPButtonState, std::function setPSPAnalog) { onVKey_ = onVKey; - onVKeyAnalog_ = onVKey; + onVKeyAnalog_ = onVKeyAnalog; setAllPSPButtonStates_ = setAllPSPButtonStates; setPSPButtonState_ = setPSPButtonState; setPSPAnalog_ = setPSPAnalog; @@ -113,12 +113,9 @@ void ControlMapper::SetPSPAxis(int device, int stick, char axis, float value) { if (!ignore) { history_[stick][axisId] = value; - - float x = history_[stick][0]; - float y = history_[stick][1]; - float convertedX, convertedY; - ConvertAnalogStick(x, y, &convertedX, &convertedY); - setPSPAnalog_(stick, convertedX, convertedY); + float x, y; + ConvertAnalogStick(history_[stick][0], history_[stick][1], &x, &y); + setPSPAnalog_(stick, x, y); } } @@ -187,13 +184,13 @@ bool ControlMapper::UpdatePSPState(const InputMapping &changedMapping) { std::vector inputMappings; if (!KeyMap::InputMappingsFromPspButton(vkId, &inputMappings, false)) continue; - float value = 0.0f; // If a mapping could consist of a combo, we could trivially check it here. // Save the first device ID so we can pass it into onVKeyDown, which in turn needs it for the analog // mapping which gets a little hacky. float threshold = 1.0f; bool touchedByMapping = false; + float value = 0.0f; for (auto &mapping : inputMappings) { if (mapping == changedMapping) { touchedByMapping = true; @@ -203,10 +200,8 @@ bool ControlMapper::UpdatePSPState(const InputMapping &changedMapping) { if (iter != curInput_.end()) { if (mapping.IsAxis()) { threshold = GetDeviceAxisThreshold(iter->first.deviceId); - value += iter->second; - } else { - value += iter->second; } + value += iter->second; } } @@ -222,11 +217,13 @@ bool ControlMapper::UpdatePSPState(const InputMapping &changedMapping) { if (virtKeys_[i] != value) { // INFO_LOG(G3D, "vkeyanalog %s : %f", KeyMap::GetVirtKeyName(vkId), value); onVKeyAnalog(changedMapping.deviceId, vkId, value); + virtKeys_[i] = value; } - virtKeys_[i] = value; if (!bPrevValue && bValue) { + // INFO_LOG(G3D, "vkeyon %s", KeyMap::GetVirtKeyName(vkId)); onVKey(vkId, true); } else if (bPrevValue && !bValue) { + // INFO_LOG(G3D, "vkeyoff %s", KeyMap::GetVirtKeyName(vkId)); onVKey(vkId, false); } } diff --git a/Core/ControlMapper.h b/Core/ControlMapper.h index 17b6fb5bfa32..300f9bb81e64 100644 --- a/Core/ControlMapper.h +++ b/Core/ControlMapper.h @@ -44,9 +44,6 @@ class ControlMapper { // To track mappable virtual keys. We can have as many as we want. float virtKeys_[VIRTKEY_COUNT]{}; - // De-noise mapped axis updates - int axisState_[JOYSTICK_AXIS_MAX]{}; - int lastNonDeadzoneDeviceID_[2]{}; float history_[2][2]{}; diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 74bc85fc33d6..3781180c0549 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -177,7 +177,7 @@ EmuScreen::EmuScreen(const Path &filename) startDumping = false; controlMapper_.SetCallbacks( std::bind(&EmuScreen::onVKey, this, _1, _2), - [](int vkey, float analogValue) {}, + std::bind(&EmuScreen::onVKeyAnalog, this, _1, _2), [](uint32_t bitsToSet, uint32_t bitsToClear) { __CtrlSetAllButtons(bitsToSet, bitsToClear); }, @@ -763,6 +763,10 @@ void EmuScreen::onVKey(int virtualKeyCode, bool down) { } } +void EmuScreen::onVKeyAnalog(int virtualKeyCode, float value) { + +} + bool EmuScreen::key(const KeyInput &key) { Core_NotifyActivity(); diff --git a/UI/EmuScreen.h b/UI/EmuScreen.h index ebb7800b4c7e..a7ee1c23afec 100644 --- a/UI/EmuScreen.h +++ b/UI/EmuScreen.h @@ -69,6 +69,7 @@ class EmuScreen : public UIScreen { void renderUI(); void onVKey(int virtualKeyCode, bool down); + void onVKeyAnalog(int virtualKeyCode, float value); void autoLoad(); void checkPowerDown(); From fa0fb6eee6cc0d99602a070d3a3519047d5e13ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 30 Mar 2023 15:11:34 +0200 Subject: [PATCH 08/25] Rework and simplify VIRTKEY_SPEED_ANALOG --- Core/Config.cpp | 1 - Core/Config.h | 1 - Core/ConfigValues.h | 6 ----- Core/ControlMapper.cpp | 59 ------------------------------------------ Core/ControlMapper.h | 2 -- UI/EmuScreen.cpp | 26 +++++++++++++++++++ 6 files changed, 26 insertions(+), 69 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index b93e04886a15..1debc4692036 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -849,7 +849,6 @@ static const ConfigSetting graphicsSettings[] = { ConfigSetting("FrameRate", &g_Config.iFpsLimit1, 0, true, true), ConfigSetting("FrameRate2", &g_Config.iFpsLimit2, -1, true, true), ConfigSetting("AnalogFrameRate", &g_Config.iAnalogFpsLimit, 240, true, true), - ConfigSetting("AnalogFrameRateMode", &g_Config.iAnalogFpsMode, 0, true, true), ConfigSetting("UnthrottlingMode", &g_Config.iFastForwardMode, &DefaultFastForwardMode, &FastForwardModeToString, &FastForwardModeFromString, true, true), #if defined(USING_WIN_UI) ConfigSetting("RestartRequired", &g_Config.bRestartRequired, false, false), diff --git a/Core/Config.h b/Core/Config.h index a022e8105da7..18d991a48a30 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -220,7 +220,6 @@ struct Config { int iFpsLimit1; int iFpsLimit2; int iAnalogFpsLimit; - int iAnalogFpsMode; // 0 = auto, 1 = single direction, 2 = mapped to opposite int iMaxRecent; int iCurrentStateSlot; int iRewindSnapshotInterval; diff --git a/Core/ConfigValues.h b/Core/ConfigValues.h index 6945dfacf0ac..96690ecc5d46 100644 --- a/Core/ConfigValues.h +++ b/Core/ConfigValues.h @@ -132,12 +132,6 @@ enum class BackgroundAnimation { MOVING_BACKGROUND = 4, }; -enum class AnalogFpsMode { - AUTO = 0, - MAPPED_DIRECTION = 1, - MAPPED_DIR_TO_OPPOSITE_DIR = 2, -}; - // for Config.iShowStatusFlags enum class ShowStatusFlags { FPS_COUNTER = 1 << 1, diff --git a/Core/ControlMapper.cpp b/Core/ControlMapper.cpp index b1e4189b952b..4f4a2064c946 100644 --- a/Core/ControlMapper.cpp +++ b/Core/ControlMapper.cpp @@ -365,62 +365,3 @@ void ControlMapper::onVKey(int vkey, bool down) { break; } } - -void ControlMapper::ProcessAnalogSpeed(const AxisInput &axis, bool opposite) { - static constexpr float DEADZONE_THRESHOLD = 0.15f; - static constexpr float DEADZONE_SCALE = 1.0f / (1.0f - DEADZONE_THRESHOLD); - - FPSLimit &limitMode = PSP_CoreParameter().fpsLimit; - // If we're using an alternate speed already, let that win. - if (limitMode != FPSLimit::NORMAL && limitMode != FPSLimit::ANALOG) - return; - // Don't even try if the limit is invalid. - if (g_Config.iAnalogFpsLimit <= 0) - return; - - AnalogFpsMode mode = (AnalogFpsMode)g_Config.iAnalogFpsMode; - float value = axis.value; - if (mode == AnalogFpsMode::AUTO) { - // TODO: Consider the pad name for better auto? KeyMap::PadName(axis.deviceId); - switch (axis.axisId) { - case JOYSTICK_AXIS_X: - case JOYSTICK_AXIS_Y: - case JOYSTICK_AXIS_Z: - case JOYSTICK_AXIS_RX: - case JOYSTICK_AXIS_RY: - case JOYSTICK_AXIS_RZ: - // These, at least on directinput, can be used for triggers that go from mapped to opposite. - mode = AnalogFpsMode::MAPPED_DIR_TO_OPPOSITE_DIR; - break; - - default: - // Other axises probably don't go from negative to positive. - mode = AnalogFpsMode::MAPPED_DIRECTION; - break; - } - } - - // Okay, now let's map it as appropriate. - if (mode == AnalogFpsMode::MAPPED_DIRECTION) { - value = fabsf(value); - // Clamp to 0 in this case if we're processing the opposite direction. - if (opposite) - value = 0.0f; - } else if (mode == AnalogFpsMode::MAPPED_DIR_TO_OPPOSITE_DIR) { - value = fabsf(value); - if (opposite) - value = -value; - value = 0.5f - value * 0.5f; - } - - // Apply a small deadzone (against the resting position.) - value = std::max(0.0f, (value - DEADZONE_THRESHOLD) * DEADZONE_SCALE); - - // If target is above 60, value is how much to speed up over 60. Otherwise, it's how much slower. - // So normalize the target. - int target = g_Config.iAnalogFpsLimit - 60; - PSP_CoreParameter().analogFpsLimit = 60 + (int)(target * value); - - // If we've reset back to normal, turn it off. - limitMode = PSP_CoreParameter().analogFpsLimit == 60 ? FPSLimit::NORMAL : FPSLimit::ANALOG; -} diff --git a/Core/ControlMapper.h b/Core/ControlMapper.h index 300f9bb81e64..87decc1a273c 100644 --- a/Core/ControlMapper.h +++ b/Core/ControlMapper.h @@ -36,8 +36,6 @@ class ControlMapper { void SetPSPAxis(int deviceId, int stick, char axis, float value); - void ProcessAnalogSpeed(const AxisInput &axis, bool opposite); - void onVKey(int vkey, bool down); void onVKeyAnalog(int deviceId, int vkey, float value); diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 3781180c0549..5868ae079ab5 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -764,7 +764,33 @@ void EmuScreen::onVKey(int virtualKeyCode, bool down) { } void EmuScreen::onVKeyAnalog(int virtualKeyCode, float value) { + if (virtualKeyCode != VIRTKEY_SPEED_ANALOG) { + return; + } + + // We only handle VIRTKEY_SPEED_ANALOG here. + + static constexpr float DEADZONE_THRESHOLD = 0.15f; + static constexpr float DEADZONE_SCALE = 1.0f / (1.0f - DEADZONE_THRESHOLD); + + FPSLimit &limitMode = PSP_CoreParameter().fpsLimit; + // If we're using an alternate speed already, let that win. + if (limitMode != FPSLimit::NORMAL && limitMode != FPSLimit::ANALOG) + return; + // Don't even try if the limit is invalid. + if (g_Config.iAnalogFpsLimit <= 0) + return; + + // Apply a small deadzone (against the resting position.) + value = std::max(0.0f, (value - DEADZONE_THRESHOLD) * DEADZONE_SCALE); + + // If target is above 60, value is how much to speed up over 60. Otherwise, it's how much slower. + // So normalize the target. + int target = g_Config.iAnalogFpsLimit - 60; + PSP_CoreParameter().analogFpsLimit = 60 + (int)(target * value); + // If we've reset back to normal, turn it off. + limitMode = PSP_CoreParameter().analogFpsLimit == 60 ? FPSLimit::NORMAL : FPSLimit::ANALOG; } bool EmuScreen::key(const KeyInput &key) { From 88e89653b13bd590f58e57a82d07b398134a4afc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 30 Mar 2023 15:15:33 +0200 Subject: [PATCH 09/25] Bring back zeroing of the opposite direction. --- Core/ControlMapper.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Core/ControlMapper.cpp b/Core/ControlMapper.cpp index 4f4a2064c946..1c6c4f925abf 100644 --- a/Core/ControlMapper.cpp +++ b/Core/ControlMapper.cpp @@ -11,7 +11,7 @@ #include "Core/CoreParameter.h" #include "Core/System.h" -// TODO: Possibly make these configurable? +// TODO: Possibly make these thresholds configurable? static float GetDeviceAxisThreshold(int device) { return device == DEVICE_ID_MOUSE ? AXIS_BIND_THRESHOLD_MOUSE : AXIS_BIND_THRESHOLD; } @@ -190,6 +190,7 @@ bool ControlMapper::UpdatePSPState(const InputMapping &changedMapping) { // mapping which gets a little hacky. float threshold = 1.0f; bool touchedByMapping = false; + bool zeroOpposite = false; float value = 0.0f; for (auto &mapping : inputMappings) { if (mapping == changedMapping) { @@ -200,6 +201,7 @@ bool ControlMapper::UpdatePSPState(const InputMapping &changedMapping) { if (iter != curInput_.end()) { if (mapping.IsAxis()) { threshold = GetDeviceAxisThreshold(iter->first.deviceId); + zeroOpposite = true; } value += iter->second; } @@ -219,6 +221,14 @@ bool ControlMapper::UpdatePSPState(const InputMapping &changedMapping) { onVKeyAnalog(changedMapping.deviceId, vkId, value); virtKeys_[i] = value; } + if (zeroOpposite) { + // For analog stick events, always zero the "opposite" when we get a valid value. + // Otherwise, lingering small values can create strange offsets when summing up later. + int opposite = GetOppositeVKey(vkId); + if (opposite) { + virtKeys_[i] = 0.0f; + } + } if (!bPrevValue && bValue) { // INFO_LOG(G3D, "vkeyon %s", KeyMap::GetVirtKeyName(vkId)); onVKey(vkId, true); From ea60ff2235a8fd69d8e57e6abd0dc09c4c098c14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 30 Mar 2023 15:28:08 +0200 Subject: [PATCH 10/25] Add missing locking to control mapper --- Core/ControlMapper.cpp | 6 ++++++ Core/ControlMapper.h | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/Core/ControlMapper.cpp b/Core/ControlMapper.cpp index 1c6c4f925abf..e39006dbf59c 100644 --- a/Core/ControlMapper.cpp +++ b/Core/ControlMapper.cpp @@ -130,6 +130,7 @@ static int RotatePSPKeyCode(int x) { } } +// Can only be called from Key or Axis. bool ControlMapper::UpdatePSPState(const InputMapping &changedMapping) { // Instead of taking an input key and finding what it outputs, we loop through the OUTPUTS and // see if the input that corresponds to it has a value. That way we can easily implement all sorts @@ -247,6 +248,8 @@ bool ControlMapper::Key(const KeyInput &key, bool *pauseTrigger) { return true; } + std::lock_guard guard(mutex_); + InputMapping mapping(key.deviceId, key.keyCode); if (key.flags & KEY_DOWN) { @@ -255,6 +258,7 @@ bool ControlMapper::Key(const KeyInput &key, bool *pauseTrigger) { curInput_[mapping] = 0.0f; } + // TODO: See if this can be simplified further somehow. bool mappingFound = KeyMap::InputMappingToPspButton(mapping, nullptr); DEBUG_LOG(SYSTEM, "Key: %d DeviceId: %d", key.keyCode, key.deviceId); if (!mappingFound || key.deviceId == DEVICE_ID_DEFAULT) { @@ -268,6 +272,7 @@ bool ControlMapper::Key(const KeyInput &key, bool *pauseTrigger) { } void ControlMapper::Axis(const AxisInput &axis) { + std::lock_guard guard(mutex_); if (axis.value > 0) { InputMapping mapping(axis.deviceId, axis.axisId, 1); curInput_[mapping] = axis.value; @@ -305,6 +310,7 @@ void ControlMapper::Update() { } void ControlMapper::PSPKey(int deviceId, int pspKeyCode, int flags) { + std::lock_guard guard(mutex_); if (pspKeyCode >= VIRTKEY_FIRST) { int vk = pspKeyCode - VIRTKEY_FIRST; if (flags & KEY_DOWN) { diff --git a/Core/ControlMapper.h b/Core/ControlMapper.h index 87decc1a273c..9d116d5517fe 100644 --- a/Core/ControlMapper.h +++ b/Core/ControlMapper.h @@ -5,6 +5,7 @@ #include #include +#include // Utilities for mapping input events to PSP inputs and virtual keys. // Main use is of course from EmuScreen.cpp, but also useful from control settings etc. @@ -13,6 +14,7 @@ class ControlMapper { void Update(); // Inputs to the table-based mapping + // These functions are free-threaded. bool Key(const KeyInput &key, bool *pauseTrigger); void Axis(const AxisInput &axis); @@ -50,6 +52,9 @@ class ControlMapper { bool autoRotatingAnalogCW_ = false; bool autoRotatingAnalogCCW_ = false; + // Protects basically all the state. + std::mutex mutex_; + std::map curInput_; // Callbacks From 778d9ac5ca7e5d5a3c019ab2b2fffc13ad81f628 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 30 Mar 2023 21:25:49 +0200 Subject: [PATCH 11/25] Handle mapping signed axis to our only unsigned input, VIRTKEY_SPEED_ANALOG. --- Common/Input/InputState.h | 7 +++++ Core/ControlMapper.cpp | 56 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/Common/Input/InputState.h b/Common/Input/InputState.h index 1c7181572d4c..180119109704 100644 --- a/Common/Input/InputState.h +++ b/Common/Input/InputState.h @@ -117,6 +117,13 @@ class InputMapping { return TranslateKeyCodeToAxis(keyCode, direction); } + InputMapping FlipDirection() const { + _dbg_assert_(IsAxis()); + InputMapping other = *this; + other.keyCode ^= 1; + return other; + } + // If you want to use std::find and match ANY, you need to perform an explicit search for that. bool operator < (const InputMapping &other) const { if (deviceId < other.deviceId) return true; diff --git a/Core/ControlMapper.cpp b/Core/ControlMapper.cpp index e39006dbf59c..a61604e09cb8 100644 --- a/Core/ControlMapper.cpp +++ b/Core/ControlMapper.cpp @@ -31,6 +31,24 @@ static int GetOppositeVKey(int vkey) { } } +static bool IsUnsignedMapping(int vkey) { + return vkey == VIRTKEY_SPEED_ANALOG; +} + +static bool IsSignedAxis(int axis) { + switch (axis) { + case JOYSTICK_AXIS_X: + case JOYSTICK_AXIS_Y: + case JOYSTICK_AXIS_Z: + case JOYSTICK_AXIS_RX: + case JOYSTICK_AXIS_RY: + case JOYSTICK_AXIS_RZ: + return true; + default: + return false; + } +} + // This is applied on the circular radius, not directly on the axes. static float MapAxisValue(float v) { const float deadzone = g_Config.fAnalogDeadzone; @@ -202,9 +220,36 @@ bool ControlMapper::UpdatePSPState(const InputMapping &changedMapping) { if (iter != curInput_.end()) { if (mapping.IsAxis()) { threshold = GetDeviceAxisThreshold(iter->first.deviceId); - zeroOpposite = true; + if (IsUnsignedMapping(vkId)) { + // If a signed axis is mapped to an unsigned mapping, + // convert it. This happens when mapping DirectInput triggers to analog speed, + // for example. + int direction; + if (IsSignedAxis(mapping.Axis(&direction))) { + // The value has been split up into two curInput values, so we need to go fetch the other + // and put them back together again. Kind of awkward, but at least makes the regular case simple... + InputMapping other = mapping.FlipDirection(); + if (other == changedMapping) { + touchedByMapping = true; + } + float valueOther = curInput_[other]; + float signedValue = iter->second - valueOther; + float ranged = (signedValue + 1.0f) * 0.5f; + if (direction == -1) { + ranged = 1.0f - ranged; + } + // NOTICE_LOG(SYSTEM, "rawValue: %f other: %f signed: %f ranged: %f", iter->second, valueOther, signedValue, ranged); + value += ranged; + } else { + value += iter->second; + } + } else { + zeroOpposite = true; + value += iter->second; + } + } else { + value += iter->second; } - value += iter->second; } } @@ -215,13 +260,16 @@ bool ControlMapper::UpdatePSPState(const InputMapping &changedMapping) { value = clamp_value(value, 0.0f, 1.0f); // Derive bools from the floats using the device's threshold. + // NOTE: This must be before the equality check below. bool bPrevValue = virtKeys_[i] >= threshold; bool bValue = value >= threshold; + if (virtKeys_[i] != value) { // INFO_LOG(G3D, "vkeyanalog %s : %f", KeyMap::GetVirtKeyName(vkId), value); onVKeyAnalog(changedMapping.deviceId, vkId, value); virtKeys_[i] = value; } + if (zeroOpposite) { // For analog stick events, always zero the "opposite" when we get a valid value. // Otherwise, lingering small values can create strange offsets when summing up later. @@ -351,7 +399,9 @@ void ControlMapper::onVKeyAnalog(int deviceId, int vkey, float value) { onVKeyAnalog_(vkey, value); return; } - value -= virtKeys_[opposite - VIRTKEY_FIRST]; + if (opposite != 0) { + value -= virtKeys_[opposite - VIRTKEY_FIRST]; + } SetPSPAxis(deviceId, stick, axis, sign * value); } From 025ec248e46de6c3ab867c8763fc0b0194d7e5bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 31 Mar 2023 11:11:46 +0200 Subject: [PATCH 12/25] Don't need two SetCallback functions. --- Core/ControlMapper.cpp | 6 ++---- Core/ControlMapper.h | 12 ++++++------ UI/ControlMappingScreen.cpp | 14 +++++++------- UI/EmuScreen.cpp | 3 ++- 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/Core/ControlMapper.cpp b/Core/ControlMapper.cpp index a61604e09cb8..ee512bc98815 100644 --- a/Core/ControlMapper.cpp +++ b/Core/ControlMapper.cpp @@ -87,15 +87,13 @@ void ControlMapper::SetCallbacks( std::function onVKeyAnalog, std::function setAllPSPButtonStates, std::function setPSPButtonState, - std::function setPSPAnalog) { + std::function setPSPAnalog, + std::function setRawAnalog) { onVKey_ = onVKey; onVKeyAnalog_ = onVKeyAnalog; setAllPSPButtonStates_ = setAllPSPButtonStates; setPSPButtonState_ = setPSPButtonState; setPSPAnalog_ = setPSPAnalog; -} - -void ControlMapper::SetRawCallback(std::function setRawAnalog) { setRawAnalog_ = setRawAnalog; } diff --git a/Core/ControlMapper.h b/Core/ControlMapper.h index 9d116d5517fe..878e2eade912 100644 --- a/Core/ControlMapper.h +++ b/Core/ControlMapper.h @@ -18,21 +18,21 @@ class ControlMapper { bool Key(const KeyInput &key, bool *pauseTrigger); void Axis(const AxisInput &axis); - // Required callbacks + // Required callbacks. + // TODO: These are so many now that a virtual interface might be more appropriate.. void SetCallbacks( std::function onVKey, std::function onVKeyAnalog, std::function setAllPSPButtonStates_, std::function setPSPButtonState, - std::function setPSPAnalog); + std::function setPSPAnalog, + std::function setRawAnalog); // Inject raw PSP key input directly, such as from touch screen controls. - // Combined with the mapped input. + // Combined with the mapped input. Unlike __Ctrl APIs, this supports + // virtual key codes, though not analog mappings. void PSPKey(int deviceId, int pspKeyCode, int flags); - // Optional callback, only used in config - void SetRawCallback(std::function setRawAnalog); - private: bool UpdatePSPState(const InputMapping &changedMapping); diff --git a/UI/ControlMappingScreen.cpp b/UI/ControlMappingScreen.cpp index 8aad793e0d08..badf788125ca 100644 --- a/UI/ControlMappingScreen.cpp +++ b/UI/ControlMappingScreen.cpp @@ -454,13 +454,13 @@ AnalogSetupScreen::AnalogSetupScreen(const Path &gamePath) : UIDialogScreenWithG [&](uint32_t bitsToSet, uint32_t bitsToClear) {}, [&](int button, bool down) {}, [&](int stick, float x, float y) { - analogX_[stick] = x; - analogY_[stick] = y; - }); - mapper_.SetRawCallback([&](int stick, float x, float y) { - rawX_[stick] = x; - rawY_[stick] = y; - }); + analogX_[stick] = x; + analogY_[stick] = y; + }, + [&](int stick, float x, float y) { + rawX_[stick] = x; + rawY_[stick] = y; + }); } void AnalogSetupScreen::update() { diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 5868ae079ab5..d7c1102c0425 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -188,7 +188,8 @@ EmuScreen::EmuScreen(const Path &filename) __CtrlButtonUp(pspButton); } }, - &SetPSPAnalog); + &SetPSPAnalog, + nullptr); // Make sure we don't leave it at powerdown after the last game. // TODO: This really should be handled elsewhere if it isn't. From 48993f4f4bfeae016046f91e9b786707d90b7b35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 31 Mar 2023 20:12:48 +0200 Subject: [PATCH 13/25] Control: Add debug display, do assorted fixes. --- Common/Input/InputState.cpp | 10 ++++++ Common/Input/InputState.h | 2 ++ Core/Config.h | 2 ++ Core/ControlMapper.cpp | 64 ++++++++++++++++++++++++------------- Core/ControlMapper.h | 3 ++ Core/KeyMap.cpp | 3 +- UI/DevScreens.cpp | 14 ++++---- UI/DevScreens.h | 1 - UI/EmuScreen.cpp | 47 ++++++++++++++++++++------- 9 files changed, 103 insertions(+), 43 deletions(-) diff --git a/Common/Input/InputState.cpp b/Common/Input/InputState.cpp index 67c2b6415a42..143f95e5fdbf 100644 --- a/Common/Input/InputState.cpp +++ b/Common/Input/InputState.cpp @@ -76,3 +76,13 @@ int GetAnalogYDirection(int deviceId) { return configured->second; return 0; } + +void InputMapping::FormatDebug(char *buffer, size_t bufSize) const { + if (IsAxis()) { + int direction; + int axis = Axis(&direction); + snprintf(buffer, bufSize, "Device: %d Axis: %d (%d)", deviceId, axis, direction); + } else { + snprintf(buffer, bufSize, "Device: %d Key: %d", deviceId, keyCode); + } +} diff --git a/Common/Input/InputState.h b/Common/Input/InputState.h index 180119109704..b1e30acefa7d 100644 --- a/Common/Input/InputState.h +++ b/Common/Input/InputState.h @@ -136,6 +136,8 @@ class InputMapping { if (keyCode != other.keyCode) return false; return true; } + + void FormatDebug(char *buffer, size_t bufSize) const; }; enum { diff --git a/Core/Config.h b/Core/Config.h index 18d991a48a30..17c5197a9712 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -276,9 +276,11 @@ struct Config { std::string sThemeName; + // These aren't saved, just for instant debugging. bool bLogFrameDrops; bool bShowDebugStats; bool bShowAudioDebug; + bool bShowControlDebug; bool bShowGpuProfile; // Analog stick tilting diff --git a/Core/ControlMapper.cpp b/Core/ControlMapper.cpp index ee512bc98815..5877a2ba63f3 100644 --- a/Core/ControlMapper.cpp +++ b/Core/ControlMapper.cpp @@ -1,7 +1,9 @@ #include +#include #include "Common/Math/math_util.h" #include "Common/TimeUtil.h" +#include "Common/StringUtils.h" #include "Common/Log.h" #include "Core/HLE/sceCtrl.h" @@ -31,6 +33,11 @@ static int GetOppositeVKey(int vkey) { } } +static bool IsAxisVKey(int vkey) { + // Little hacky but works, of course. + return GetOppositeVKey(vkey) != 0; +} + static bool IsUnsignedMapping(int vkey) { return vkey == VIRTKEY_SPEED_ANALOG; } @@ -131,6 +138,8 @@ void ControlMapper::SetPSPAxis(int device, int stick, char axis, float value) { history_[stick][axisId] = value; float x, y; ConvertAnalogStick(history_[stick][0], history_[stick][1], &x, &y); + converted_[stick][0] = x; + converted_[stick][1] = y; setPSPAnalog_(stick, x, y); } } @@ -207,7 +216,6 @@ bool ControlMapper::UpdatePSPState(const InputMapping &changedMapping) { // mapping which gets a little hacky. float threshold = 1.0f; bool touchedByMapping = false; - bool zeroOpposite = false; float value = 0.0f; for (auto &mapping : inputMappings) { if (mapping == changedMapping) { @@ -242,7 +250,6 @@ bool ControlMapper::UpdatePSPState(const InputMapping &changedMapping) { value += iter->second; } } else { - zeroOpposite = true; value += iter->second; } } else { @@ -268,14 +275,6 @@ bool ControlMapper::UpdatePSPState(const InputMapping &changedMapping) { virtKeys_[i] = value; } - if (zeroOpposite) { - // For analog stick events, always zero the "opposite" when we get a valid value. - // Otherwise, lingering small values can create strange offsets when summing up later. - int opposite = GetOppositeVKey(vkId); - if (opposite) { - virtKeys_[i] = 0.0f; - } - } if (!bPrevValue && bValue) { // INFO_LOG(G3D, "vkeyon %s", KeyMap::GetVirtKeyName(vkId)); onVKey(vkId, true); @@ -319,22 +318,20 @@ bool ControlMapper::Key(const KeyInput &key, bool *pauseTrigger) { void ControlMapper::Axis(const AxisInput &axis) { std::lock_guard guard(mutex_); - if (axis.value > 0) { + if (axis.value >= 0.0f) { InputMapping mapping(axis.deviceId, axis.axisId, 1); + InputMapping opposite(axis.deviceId, axis.axisId, -1); curInput_[mapping] = axis.value; + curInput_[opposite] = 0.0f; UpdatePSPState(mapping); - } else if (axis.value < 0) { + UpdatePSPState(opposite); + } else if (axis.value < 0.0f) { InputMapping mapping(axis.deviceId, axis.axisId, -1); + InputMapping opposite(axis.deviceId, axis.axisId, 1); curInput_[mapping] = -axis.value; + curInput_[opposite] = 0.0f; UpdatePSPState(mapping); - } else if (axis.value == 0.0f) { // Threshold? - // Both directions! Prevents sticking for digital input devices that are axises (like HAT) - InputMapping mappingPositive(axis.deviceId, axis.axisId, 1); - InputMapping mappingNegative(axis.deviceId, axis.axisId, -1); - curInput_[mappingPositive] = 0.0f; - curInput_[mappingNegative] = 0.0f; - UpdatePSPState(mappingPositive); - UpdatePSPState(mappingNegative); + UpdatePSPState(opposite); } } @@ -381,7 +378,7 @@ void ControlMapper::onVKeyAnalog(int deviceId, int vkey, float value) { // with the opposite value too. int stick = 0; int axis = 'X'; - int opposite = GetOppositeVKey(vkey); + int oppositeVKey = GetOppositeVKey(vkey); float sign = 1.0f; switch (vkey) { case VIRTKEY_AXIS_X_MIN: sign = -1.0f; break; @@ -397,8 +394,12 @@ void ControlMapper::onVKeyAnalog(int deviceId, int vkey, float value) { onVKeyAnalog_(vkey, value); return; } - if (opposite != 0) { - value -= virtKeys_[opposite - VIRTKEY_FIRST]; + if (oppositeVKey != 0) { + float oppVal = virtKeys_[oppositeVKey - VIRTKEY_FIRST]; + if (oppVal != 0.0f) { + value -= oppVal; + // NOTICE_LOG(SCECTRL, "Reducing %f by %f (from %08x : %s)", value, oppVal, oppositeVKey, KeyMap::GetPspButtonName(oppositeVKey).c_str()); + } } SetPSPAxis(deviceId, stick, axis, sign * value); } @@ -429,3 +430,20 @@ void ControlMapper::onVKey(int vkey, bool down) { break; } } + +void ControlMapper::GetDebugString(char *buffer, size_t bufSize) const { + std::stringstream str; + for (auto iter : curInput_) { + char temp[256]; + iter.first.FormatDebug(temp, sizeof(temp)); + str << temp << ": " << iter.second << std::endl; + } + for (int i = 0; i < ARRAY_SIZE(virtKeys_); i++) { + int vkId = VIRTKEY_FIRST + i; + if ((vkId >= VIRTKEY_AXIS_X_MIN && vkId <= VIRTKEY_AXIS_Y_MAX) || vkId == VIRTKEY_ANALOG_LIGHTLY) { + str << KeyMap::GetPspButtonName(vkId) << ": " << virtKeys_[i] << std::endl; + } + } + str << "Lstick: " << converted_[0][0] << ", " << converted_[0][1] << std::endl; + truncate_cpy(buffer, bufSize, str.str().c_str()); +} diff --git a/Core/ControlMapper.h b/Core/ControlMapper.h index 878e2eade912..f0fae812c929 100644 --- a/Core/ControlMapper.h +++ b/Core/ControlMapper.h @@ -33,6 +33,8 @@ class ControlMapper { // virtual key codes, though not analog mappings. void PSPKey(int deviceId, int pspKeyCode, int flags); + void GetDebugString(char *buffer, size_t bufSize) const; + private: bool UpdatePSPState(const InputMapping &changedMapping); @@ -47,6 +49,7 @@ class ControlMapper { int lastNonDeadzoneDeviceID_[2]{}; float history_[2][2]{}; + float converted_[2][2]{}; // for debug display // Mappable auto-rotation. Useful for keyboard/dpad->analog in a few games. bool autoRotatingAnalogCW_ = false; diff --git a/Core/KeyMap.cpp b/Core/KeyMap.cpp index a7742296bcf5..52f907ed8e8d 100644 --- a/Core/KeyMap.cpp +++ b/Core/KeyMap.cpp @@ -424,9 +424,10 @@ const KeyMap_IntStrPair psp_button_names[] = { }; static std::string FindName(int key, const KeyMap_IntStrPair list[], size_t size) { - for (size_t i = 0; i < size; i++) + for (size_t i = 0; i < size; i++) { if (list[i].key == key) return list[i].name; + } return StringFromFormat("%02x?", key); } diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index 170cb994148b..16c562187cd4 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -107,7 +107,14 @@ void DevMenuScreen::CreatePopupContents(UI::ViewGroup *parent) { items->Add(new Choice(dev->T("Toggle Freeze")))->OnClick.Handle(this, &DevMenuScreen::OnFreezeFrame); items->Add(new Choice(dev->T("Dump next frame to log")))->OnClick.Handle(this, &DevMenuScreen::OnDumpFrame); - items->Add(new Choice(dev->T("Toggle Audio Debug")))->OnClick.Handle(this, &DevMenuScreen::OnToggleAudioDebug); + items->Add(new Choice(dev->T("Toggle Audio Debug")))->OnClick.Add([](UI::EventParams &) { + g_Config.bShowAudioDebug = !g_Config.bShowAudioDebug; + return UI::EVENT_DONE; + }); + items->Add(new Choice(dev->T("Toggle Control Debug")))->OnClick.Add([](UI::EventParams &) { + g_Config.bShowControlDebug = !g_Config.bShowControlDebug; + return UI::EVENT_DONE; + }); #ifdef USE_PROFILER items->Add(new CheckBox(&g_Config.bShowFrameProfiler, dev->T("Frame Profiler"), "")); #endif @@ -123,11 +130,6 @@ void DevMenuScreen::CreatePopupContents(UI::ViewGroup *parent) { } } -UI::EventReturn DevMenuScreen::OnToggleAudioDebug(UI::EventParams &e) { - g_Config.bShowAudioDebug = !g_Config.bShowAudioDebug; - return UI::EVENT_DONE; -} - UI::EventReturn DevMenuScreen::OnResetLimitedLogging(UI::EventParams &e) { Reporting::ResetCounts(); return UI::EVENT_DONE; diff --git a/UI/DevScreens.h b/UI/DevScreens.h index 0a871b56fcc6..a582d7951af0 100644 --- a/UI/DevScreens.h +++ b/UI/DevScreens.h @@ -45,7 +45,6 @@ class DevMenuScreen : public PopupScreen { UI::EventReturn OnFreezeFrame(UI::EventParams &e); UI::EventReturn OnDumpFrame(UI::EventParams &e); UI::EventReturn OnDeveloperTools(UI::EventParams &e); - UI::EventReturn OnToggleAudioDebug(UI::EventParams &e); UI::EventReturn OnResetLimitedLogging(UI::EventParams &e); private: diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index d7c1102c0425..8707215c47e6 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -1337,6 +1337,23 @@ static void DrawAudioDebugStats(UIContext *ctx, const Bounds &bounds) { ctx->RebindTexture(); } +static void DrawControlDebug(UIContext *ctx, const ControlMapper &mapper, const Bounds &bounds) { + FontID ubuntu24("UBUNTU24"); + + char statbuf[4096] = { 0 }; + mapper.GetDebugString(statbuf, sizeof(statbuf)); + // System_AudioGetDebugStats(statbuf, sizeof(statbuf)); + + ctx->Flush(); + ctx->BindFontTexture(); + ctx->Draw()->SetFontScale(0.5f, 0.5f); + ctx->Draw()->DrawTextRect(ubuntu24, statbuf, bounds.x + 11, bounds.y + 31, bounds.w - 20, bounds.h - 30, 0xc0000000, FLAG_DYNAMIC_ASCII); + ctx->Draw()->DrawTextRect(ubuntu24, statbuf, bounds.x + 10, bounds.y + 30, bounds.w - 20, bounds.h - 30, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); + ctx->Draw()->SetFontScale(1.0f, 1.0f); + ctx->Flush(); + ctx->RebindTexture(); +} + static void DrawFPS(UIContext *ctx, const Bounds &bounds) { FontID ubuntu24("UBUNTU24"); float vps, fps, actual_fps; @@ -1549,7 +1566,7 @@ bool EmuScreen::hasVisibleUI() { if (g_Config.bEnableCardboardVR || g_Config.bEnableNetworkChat) return true; // Debug UI. - if (g_Config.bShowDebugStats || g_Config.bShowDeveloperMenu || g_Config.bShowAudioDebug || g_Config.bShowFrameProfiler) + if (g_Config.bShowDebugStats || g_Config.bShowDeveloperMenu || g_Config.bShowAudioDebug || g_Config.bShowFrameProfiler || g_Config.bShowControlDebug) return true; // Exception information. @@ -1583,20 +1600,26 @@ void EmuScreen::renderUI() { root_->Draw(*ctx); } - if (g_Config.bShowDebugStats && !invalid_) { - DrawDebugStats(ctx, ctx->GetLayoutBounds()); - } + if (!invalid_) { + if (g_Config.bShowDebugStats) { + DrawDebugStats(ctx, ctx->GetLayoutBounds()); + } - if (g_Config.bShowAudioDebug && !invalid_) { - DrawAudioDebugStats(ctx, ctx->GetLayoutBounds()); - } + if (g_Config.bShowAudioDebug) { + DrawAudioDebugStats(ctx, ctx->GetLayoutBounds()); + } - if (g_Config.iShowStatusFlags && !invalid_) { - DrawFPS(ctx, ctx->GetLayoutBounds()); - } + if (g_Config.iShowStatusFlags) { + DrawFPS(ctx, ctx->GetLayoutBounds()); + } - if (g_Config.bDrawFrameGraph && !invalid_) { - DrawFrameTimes(ctx, ctx->GetLayoutBounds()); + if (g_Config.bDrawFrameGraph) { + DrawFrameTimes(ctx, ctx->GetLayoutBounds()); + } + + if (g_Config.bShowControlDebug) { + DrawControlDebug(ctx, controlMapper_, ctx->GetLayoutBounds()); + } } #if !PPSSPP_PLATFORM(UWP) && !PPSSPP_PLATFORM(SWITCH) From 9804a905c88e51b286143f901e981cb88a5f6f42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 31 Mar 2023 20:27:30 +0200 Subject: [PATCH 14/25] More tweaks (work around the old problem where lingering analog values biased the digital input) --- Core/ControlMapper.cpp | 25 ++++++++++++++++++++++++- UI/EmuScreen.cpp | 4 +++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/Core/ControlMapper.cpp b/Core/ControlMapper.cpp index 5877a2ba63f3..cd144c51783b 100644 --- a/Core/ControlMapper.cpp +++ b/Core/ControlMapper.cpp @@ -155,6 +155,15 @@ static int RotatePSPKeyCode(int x) { } } +// Used to decay analog values when clashing with digital ones. +static float ReduceMagnitude(float value) { + value *= 0.75f; + if ((value > 0.0f && value < 0.05f) || (value < 0.0f && value > -0.05f)) { + value = 0.0f; + } + return value; +} + // Can only be called from Key or Axis. bool ControlMapper::UpdatePSPState(const InputMapping &changedMapping) { // Instead of taking an input key and finding what it outputs, we loop through the OUTPUTS and @@ -262,6 +271,20 @@ bool ControlMapper::UpdatePSPState(const InputMapping &changedMapping) { continue; } + // Small values from analog inputs like gamepad sticks can linger around, which is bad here because we sum + // up before applying deadzone etc. This means that it can be impossible to reach the min/max values with digital input! + // So if non-analog events clash with analog ones mapped to the same input, decay the analog input, + // which will quickly get things back to normal, while if it's intentional to use both at the same time for some reason, + // that still works, though a bit weaker. We could also zero here, but you never know who relies on such strange tricks.. + // Note: This is an old problem, it didn't appear with the refactoring. + if (!changedMapping.IsAxis()) { + for (auto &mapping : inputMappings) { + if (mapping.IsAxis()) { + curInput_[mapping] = ReduceMagnitude(curInput_[mapping]); + } + } + } + value = clamp_value(value, 0.0f, 1.0f); // Derive bools from the floats using the device's threshold. @@ -440,7 +463,7 @@ void ControlMapper::GetDebugString(char *buffer, size_t bufSize) const { } for (int i = 0; i < ARRAY_SIZE(virtKeys_); i++) { int vkId = VIRTKEY_FIRST + i; - if ((vkId >= VIRTKEY_AXIS_X_MIN && vkId <= VIRTKEY_AXIS_Y_MAX) || vkId == VIRTKEY_ANALOG_LIGHTLY) { + if ((vkId >= VIRTKEY_AXIS_X_MIN && vkId <= VIRTKEY_AXIS_Y_MAX) || vkId == VIRTKEY_ANALOG_LIGHTLY || vkId == VIRTKEY_SPEED_ANALOG) { str << KeyMap::GetPspButtonName(vkId) << ": " << virtKeys_[i] << std::endl; } } diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 8707215c47e6..96590a75e518 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -771,7 +771,9 @@ void EmuScreen::onVKeyAnalog(int virtualKeyCode, float value) { // We only handle VIRTKEY_SPEED_ANALOG here. - static constexpr float DEADZONE_THRESHOLD = 0.15f; + // Xbox controllers need a pretty big deadzone here to not leave behind small values + // on occasion when releasing the trigger. Still feels right. + static constexpr float DEADZONE_THRESHOLD = 0.20f; static constexpr float DEADZONE_SCALE = 1.0f / (1.0f - DEADZONE_THRESHOLD); FPSLimit &limitMode = PSP_CoreParameter().fpsLimit; From 2428051f17c98e359518a3a61009a0d149a51b6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 31 Mar 2023 20:35:22 +0200 Subject: [PATCH 15/25] buildfix --- Common/Input/InputState.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Common/Input/InputState.cpp b/Common/Input/InputState.cpp index 143f95e5fdbf..a788ed10a855 100644 --- a/Common/Input/InputState.cpp +++ b/Common/Input/InputState.cpp @@ -1,6 +1,8 @@ +#include +#include + #include "Common/Input/InputState.h" #include "Common/Input/KeyCodes.h" -#include const char *GetDeviceName(int deviceId) { switch (deviceId) { From 1f776b4330111b192afc41eb340ccf9a4feb8e7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 31 Mar 2023 20:59:45 +0200 Subject: [PATCH 16/25] Update pspautotests submodule --- pspautotests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pspautotests b/pspautotests index 46065027500c..2e02c4a7c075 160000 --- a/pspautotests +++ b/pspautotests @@ -1 +1 @@ -Subproject commit 46065027500cd781ce7c15c051c8b0c4751ad1fa +Subproject commit 2e02c4a7c075f1a7cf28ec1add000ecd7077cd09 From 5332850b009c2f70afd105ce25b55b7051274987 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 31 Mar 2023 21:24:21 +0200 Subject: [PATCH 17/25] More accurate check for LoadCLUT from framebuffer margins. Fixes #16819 --- GPU/Common/TextureCacheCommon.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 5ad9f98299be..6f9d235bc698 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -638,7 +638,7 @@ TexCacheEntry *TextureCacheCommon::SetTexture() { } if (hasClutGPU) { - WARN_LOG_REPORT_ONCE(clutUseRender, G3D, "Using texture with dynamic CLUT: texfmt=%d, clutfmt=%d", gstate.getTextureFormat(), gstate.getClutPaletteFormat()); + WARN_LOG_N_TIMES(clutUseRender, 5, G3D, "Using texture with dynamic CLUT: texfmt=%d, clutfmt=%d", gstate.getTextureFormat(), gstate.getClutPaletteFormat()); entry->status |= TexCacheEntry::STATUS_CLUT_GPU; } @@ -671,7 +671,7 @@ TexCacheEntry *TextureCacheCommon::SetTexture() { entry->maxLevel = maxLevel; entry->status &= ~TexCacheEntry::STATUS_BGRA; - // This would overestimate the size in many case so we underestimate instead + // This would overestimate the size in many cases so we underestimate instead // to avoid excessive clearing caused by cache invalidations. entry->sizeInRAM = (textureBitsPerPixel[texFormat] * bufw * h / 2) / 8; entry->bufw = bufw; @@ -1324,14 +1324,16 @@ void TextureCacheCommon::LoadClut(u32 clutAddr, u32 loadBytes) { if (fbMatchWidth == 512) { fbMatchWidth = 480; } - bool inMargin = ((offset / fb_bpp) % framebuffer->fb_stride) == fbMatchWidth; + int pixelOffsetX = ((offset / fb_bpp) % framebuffer->fb_stride); + bool inMargin = pixelOffsetX >= fbMatchWidth && (pixelOffsetX + (loadBytes / fb_bpp) <= framebuffer->fb_stride); // The offset check here means, in the context of the loop, that we'll pick // the framebuffer with the smallest offset. This is yet another framebuffer matching // loop with its own rules, eventually we'll probably want to do something // more systematic. if (matchRange && !inMargin && offset < (int)clutRenderOffset_) { - WARN_LOG_N_TIMES(clutfb, 5, G3D, "Detected LoadCLUT(%d bytes) from framebuffer %08x (%s), byte offset %d", loadBytes, fb_address, GeBufferFormatToString(framebuffer->fb_format), offset); + WARN_LOG_N_TIMES(clutfb, 5, G3D, "Detected LoadCLUT(%d bytes) from framebuffer %08x (%s), byte offset %d, pixel offset %d", + loadBytes, fb_address, GeBufferFormatToString(framebuffer->fb_format), offset, offset / fb_bpp); framebuffer->last_frame_clut = gpuStats.numFlips; // Also mark used so it's not decimated. framebuffer->last_frame_used = gpuStats.numFlips; @@ -2143,6 +2145,7 @@ void TextureCacheCommon::ApplyTexture() { gstate_c.SetTextureIs3D((entry->status & TexCacheEntry::STATUS_3D) != 0); gstate_c.SetTextureIsArray(false); gstate_c.SetTextureIsBGRA((entry->status & TexCacheEntry::STATUS_BGRA) != 0); + gstate_c.SetUseShaderDepal(ShaderDepalMode::OFF); } } From 839bc1fc8753e5df9bcca35d1411b735e79568da Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 31 Mar 2023 23:51:04 -0700 Subject: [PATCH 18/25] headless: Fix screenshot failure. Needs to output to the collected output for it to actually show as a failure. --- headless/HeadlessHost.cpp | 12 +++++++++--- headless/HeadlessHost.h | 2 ++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/headless/HeadlessHost.cpp b/headless/HeadlessHost.cpp index 7a88ca5bb378..b68f4ce0948e 100644 --- a/headless/HeadlessHost.cpp +++ b/headless/HeadlessHost.cpp @@ -42,14 +42,20 @@ void HeadlessHost::SendDebugScreenshot(const u8 *pixbuf, u32 w, u32 h) { ScreenshotComparer comparer(pixels, FRAME_STRIDE, FRAME_WIDTH, FRAME_HEIGHT); double errors = comparer.Compare(comparisonScreenshot_); if (errors < 0) - SendDebugOutput(comparer.GetError() + "\n"); + SendAndCollectOutput(comparer.GetError() + "\n"); if (errors > maxScreenshotError_) - SendDebugOutput(StringFromFormat("Screenshot MSE: %f\n", errors)); + SendAndCollectOutput(StringFromFormat("Screenshot MSE: %f\n", errors)); if (errors > maxScreenshotError_ && writeFailureScreenshot_) { if (comparer.SaveActualBitmap(Path("__testfailure.bmp"))) - SendDebugOutput("Actual output written to: __testfailure.bmp\n"); + SendAndCollectOutput("Actual output written to: __testfailure.bmp\n"); comparer.SaveVisualComparisonPNG(Path("__testcompare.png")); } } + +void HeadlessHost::SendAndCollectOutput(const std::string &output) { + SendDebugOutput(output); + if (PSP_CoreParameter().collectDebugOutput) + *PSP_CoreParameter().collectDebugOutput += output; +} diff --git a/headless/HeadlessHost.h b/headless/HeadlessHost.h index a40c8ff19180..9f61bf08b309 100644 --- a/headless/HeadlessHost.h +++ b/headless/HeadlessHost.h @@ -62,6 +62,8 @@ class HeadlessHost { virtual void SwapBuffers() {} protected: + void SendAndCollectOutput(const std::string &output); + Path comparisonScreenshot_; double maxScreenshotError_ = 0.0; std::string debugOutputBuffer_; From 2c5b0999e88d24010df5a5073e6dd2e374b89670 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 31 Mar 2023 23:52:23 -0700 Subject: [PATCH 19/25] softgpu: Make debug-only optim more consistent. Of course it doesn't matter when optimizations are enabled in any compiler that can build PPSSPP... --- GPU/Software/RasterizerRectangle.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/GPU/Software/RasterizerRectangle.cpp b/GPU/Software/RasterizerRectangle.cpp index a7e5c43eacf6..ebc9563cd41b 100644 --- a/GPU/Software/RasterizerRectangle.cpp +++ b/GPU/Software/RasterizerRectangle.cpp @@ -101,7 +101,6 @@ template static inline void DrawSinglePixel(u16 *pixel, const u32 color_in) { u32 new_color; // Because of this check, we only support src.a / 1-src.a blending. - // We take advantage of short circuiting by checking the constant (template) value first. if (!alphaBlend || (color_in >> 24) == 255) { new_color = color_in & 0xFFFFFF; } else { @@ -142,7 +141,7 @@ template static inline void DrawSinglePixel32(u32 *pixel, const u32 color_in) { u32 new_color; // Because of this check, we only support src.a / 1-src.a blending. - if ((color_in >> 24) == 255 || !alphaBlend) { + if (!alphaBlend || (color_in >> 24) == 255) { new_color = color_in & 0xFFFFFF; } else { const u32 old_color = *pixel; From a88b8a14f66691a321a441b4080d34ca0287b172 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 31 Mar 2023 23:53:37 -0700 Subject: [PATCH 20/25] softgpu: Fix over-optimization of alpha test. When alpha blend is off, was previously skipping the alpha test if only it was enabled. See #17213. --- GPU/Software/RasterizerRectangle.cpp | 37 ++++++++++++++++------------ 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/GPU/Software/RasterizerRectangle.cpp b/GPU/Software/RasterizerRectangle.cpp index ebc9563cd41b..db14c3306e61 100644 --- a/GPU/Software/RasterizerRectangle.cpp +++ b/GPU/Software/RasterizerRectangle.cpp @@ -230,7 +230,7 @@ static inline Vec4IntResult SOFTRAST_CALL ModulateRGBA(Vec4IntArg prim_in, Vec4I return ToVec4IntResult(out); } -template +template static void DrawSpriteTex(const DrawingCoords &pos0, const DrawingCoords &pos1, int s_start, int t_start, int ds, int dt, u32 color0, const RasterizerState &state, Sampler::FetchFunc fetchFunc) { const u8 *texptr = state.texptr[0]; uint16_t texbufw = state.texbufw[0]; @@ -244,7 +244,7 @@ static void DrawSpriteTex(const DrawingCoords &pos0, const DrawingCoords &pos1, for (int x = pos0.x; x < pos1.x; x++) { Vec4 tex_color = fetchFunc(s, t, texptr, texbufw, 0, state.samplerID); if (isWhite) { - if (!alphaBlend || tex_color.a() != 0) { + if (!alphaTestZero || tex_color.a() != 0) { u32 tex_color32 = tex_color.ToRGBA(); if (fmt == GE_FORMAT_8888) DrawSinglePixel32(pixel32, tex_color32); @@ -254,7 +254,7 @@ static void DrawSpriteTex(const DrawingCoords &pos0, const DrawingCoords &pos1, } else { Vec4 prim_color = c0; prim_color = Vec4(ModulateRGBA(ToVec4IntArg(prim_color), ToVec4IntArg(tex_color), state.samplerID)); - if (!alphaBlend || prim_color.a() > 0) { + if (!alphaTestZero || prim_color.a() > 0) { if (fmt == GE_FORMAT_8888) DrawSinglePixel32(pixel32, prim_color.ToRGBA()); else @@ -271,20 +271,20 @@ static void DrawSpriteTex(const DrawingCoords &pos0, const DrawingCoords &pos1, } } -template +template static void DrawSpriteTex(const DrawingCoords &pos0, const DrawingCoords &pos1, int s_start, int t_start, int ds, int dt, u32 color0, const RasterizerState &state, Sampler::FetchFunc fetchFunc) { switch (state.pixelID.FBFormat()) { case GE_FORMAT_565: - DrawSpriteTex(pos0, pos1, s_start, t_start, ds, dt, color0, state, fetchFunc); + DrawSpriteTex(pos0, pos1, s_start, t_start, ds, dt, color0, state, fetchFunc); break; case GE_FORMAT_5551: - DrawSpriteTex(pos0, pos1, s_start, t_start, ds, dt, color0, state, fetchFunc); + DrawSpriteTex(pos0, pos1, s_start, t_start, ds, dt, color0, state, fetchFunc); break; case GE_FORMAT_4444: - DrawSpriteTex(pos0, pos1, s_start, t_start, ds, dt, color0, state, fetchFunc); + DrawSpriteTex(pos0, pos1, s_start, t_start, ds, dt, color0, state, fetchFunc); break; case GE_FORMAT_8888: - DrawSpriteTex(pos0, pos1, s_start, t_start, ds, dt, color0, state, fetchFunc); + DrawSpriteTex(pos0, pos1, s_start, t_start, ds, dt, color0, state, fetchFunc); break; default: // Invalid, don't draw anything... @@ -292,6 +292,17 @@ static void DrawSpriteTex(const DrawingCoords &pos0, const DrawingCoords &pos1, } } +template +static inline void DrawSpriteTex(const DrawingCoords &pos0, const DrawingCoords &pos1, int s_start, int t_start, int ds, int dt, u32 color0, const RasterizerState &state, Sampler::FetchFunc fetchFunc) { + // Standard alpha blending implies skipping alpha zero. + if (state.pixelID.alphaBlend) + DrawSpriteTex(pos0, pos1, s_start, t_start, ds, dt, color0, state, fetchFunc); + else if (state.pixelID.AlphaTestFunc() != GE_COMP_ALWAYS) + DrawSpriteTex(pos0, pos1, s_start, t_start, ds, dt, color0, state, fetchFunc); + else + DrawSpriteTex(pos0, pos1, s_start, t_start, ds, dt, color0, state, fetchFunc); +} + template static void DrawSpriteNoTex(const DrawingCoords &pos0, const DrawingCoords &pos1, u32 color0, const RasterizerState &state) { if (alphaBlend && Vec4::FromRGBA(color0).a() == 0) @@ -392,15 +403,9 @@ void DrawSprite(const VertexData &v0, const VertexData &v1, const BinCoords &ran if (UseDrawSinglePixel(pixelID) && (samplerID.TexFunc() == GE_TEXFUNC_MODULATE || samplerID.TexFunc() == GE_TEXFUNC_REPLACE) && samplerID.useTextureAlpha) { if (isWhite || samplerID.TexFunc() == GE_TEXFUNC_REPLACE) { - if (pixelID.alphaBlend) - DrawSpriteTex(pos0, pos1, s_start, t_start, ds, dt, v1.color0, state, fetchFunc); - else - DrawSpriteTex(pos0, pos1, s_start, t_start, ds, dt, v1.color0, state, fetchFunc); + DrawSpriteTex(pos0, pos1, s_start, t_start, ds, dt, v1.color0, state, fetchFunc); } else { - if (pixelID.alphaBlend) - DrawSpriteTex(pos0, pos1, s_start, t_start, ds, dt, v1.color0, state, fetchFunc); - else - DrawSpriteTex(pos0, pos1, s_start, t_start, ds, dt, v1.color0, state, fetchFunc); + DrawSpriteTex(pos0, pos1, s_start, t_start, ds, dt, v1.color0, state, fetchFunc); } } else { float dsf = ds * (1.0f / (float)(1 << state.samplerID.width0Shift)); From 04d3d3111c5698902b8b25e02824fa35e2d7fba7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 1 Apr 2023 08:55:45 +0200 Subject: [PATCH 21/25] Clean up __Ctrl button functions. --- Core/Debugger/WebSocket/InputSubscriber.cpp | 11 +++-------- Core/HLE/sceCtrl.cpp | 17 +---------------- Core/HLE/sceCtrl.h | 11 ++--------- Core/TiltEventProcessor.cpp | 17 ++++++++--------- UI/EmuScreen.cpp | 6 +++--- UI/GamepadEmu.cpp | 15 +++++++-------- pspautotests | 2 +- 7 files changed, 25 insertions(+), 54 deletions(-) diff --git a/Core/Debugger/WebSocket/InputSubscriber.cpp b/Core/Debugger/WebSocket/InputSubscriber.cpp index 49db4758ca26..3b2f5c5733bf 100644 --- a/Core/Debugger/WebSocket/InputSubscriber.cpp +++ b/Core/Debugger/WebSocket/InputSubscriber.cpp @@ -155,12 +155,7 @@ void WebSocketInputState::ButtonsSend(DebuggerRequest &req) { } } - if (downFlags) { - __CtrlButtonDown(downFlags); - } - if (upFlags) { - __CtrlButtonUp(upFlags); - } + __CtrlUpdateButtons(downFlags, upFlags); req.Respond(); } @@ -193,7 +188,7 @@ void WebSocketInputState::ButtonsPress(DebuggerRequest &req) { press.button = info->second; // TODO: Route into the control mapper's PSPKey function instead. - __CtrlButtonDown(press.button); + __CtrlUpdateButtons(press.button, 0); pressTickets_.push_back(press); } @@ -207,7 +202,7 @@ void WebSocketInputState::Broadcast(net::WebSocketServer *ws) { press.duration--; if (press.duration == -1) { // TODO: Route into the control mapper's PSPKey function instead. - __CtrlButtonUp(press.button); + __CtrlUpdateButtons(0, press.button); ws->Send(press.Event()); } } diff --git a/Core/HLE/sceCtrl.cpp b/Core/HLE/sceCtrl.cpp index 6e8e160da5ad..e2d60a723c09 100644 --- a/Core/HLE/sceCtrl.cpp +++ b/Core/HLE/sceCtrl.cpp @@ -189,22 +189,7 @@ u32 __CtrlReadLatch() return ret; } -// Functions so that the rest of the emulator can control what the sceCtrl interface should return -// to the game: - -void __CtrlButtonDown(u32 buttonBit) -{ - std::lock_guard guard(ctrlMutex); - ctrlCurrent.buttons |= buttonBit; -} - -void __CtrlButtonUp(u32 buttonBit) -{ - std::lock_guard guard(ctrlMutex); - ctrlCurrent.buttons &= ~buttonBit; -} - -void __CtrlSetAllButtons(u32 bitsToSet, u32 bitsToClear) +void __CtrlUpdateButtons(u32 bitsToSet, u32 bitsToClear) { std::lock_guard guard(ctrlMutex); ctrlCurrent.buttons &= ~(bitsToClear & CTRL_MASK_USER); diff --git a/Core/HLE/sceCtrl.h b/Core/HLE/sceCtrl.h index c163f2174442..02feaceb433c 100644 --- a/Core/HLE/sceCtrl.h +++ b/Core/HLE/sceCtrl.h @@ -64,15 +64,8 @@ void __CtrlInit(); void __CtrlDoState(PointerWrap &p); void __CtrlShutdown(); -// Call this whenever a button is pressed, using the above CTRL_ constants. -// Multiple buttons may be sent in one call OR'd together. -// Resending a currently pressed button is fine but not required. -void __CtrlButtonDown(u32 buttonBit); -// Call this whenever a button is released. Similar to __CtrlButtonDown(). -void __CtrlButtonUp(u32 buttonBit); -// Sets the full state. Used by the new control mapper. The above two functions -// should go away over time. -void __CtrlSetAllButtons(u32 bitsToSet, u32 bitsToClear); +// Clears and sets selected buttons. NOTE: Clearing happens first. +void __CtrlUpdateButtons(u32 bitsToSet, u32 bitsToClear); // Call this to set the position of an analog stick, ideally when it changes. // X and Y values should be from -1 to 1, inclusive, in a square (no need to force to a circle.) diff --git a/Core/TiltEventProcessor.cpp b/Core/TiltEventProcessor.cpp index 716fee429ecc..f505f23397bd 100644 --- a/Core/TiltEventProcessor.cpp +++ b/Core/TiltEventProcessor.cpp @@ -139,12 +139,12 @@ void GenerateDPadEvent(int digitalX, int digitalY) { static const int dir[4] = { CTRL_RIGHT, CTRL_DOWN, CTRL_LEFT, CTRL_UP }; if (digitalX == 0) { - __CtrlButtonUp(tiltButtonsDown & (CTRL_RIGHT | CTRL_LEFT)); + __CtrlUpdateButtons(tiltButtonsDown & (CTRL_RIGHT | CTRL_LEFT), 0); tiltButtonsDown &= ~(CTRL_LEFT | CTRL_RIGHT); } if (digitalY == 0) { - __CtrlButtonUp(tiltButtonsDown & (CTRL_UP | CTRL_DOWN)); + __CtrlUpdateButtons(0, tiltButtonsDown & (CTRL_UP | CTRL_DOWN)); tiltButtonsDown &= ~(CTRL_UP | CTRL_DOWN); } @@ -159,7 +159,7 @@ void GenerateDPadEvent(int digitalX, int digitalY) { if (digitalY == 1) ctrlMask |= CTRL_UP; ctrlMask &= ~__CtrlPeekButtons(); - __CtrlButtonDown(ctrlMask); + __CtrlUpdateButtons(ctrlMask, 0); tiltButtonsDown |= ctrlMask; } @@ -167,12 +167,12 @@ void GenerateActionButtonEvent(int digitalX, int digitalY) { static const int buttons[4] = { CTRL_CIRCLE, CTRL_CROSS, CTRL_SQUARE, CTRL_TRIANGLE }; if (digitalX == 0) { - __CtrlButtonUp(tiltButtonsDown & (CTRL_SQUARE | CTRL_CIRCLE)); + __CtrlUpdateButtons(0, tiltButtonsDown & (CTRL_SQUARE | CTRL_CIRCLE)); tiltButtonsDown &= ~(CTRL_SQUARE | CTRL_CIRCLE); } if (digitalY == 0) { - __CtrlButtonUp(tiltButtonsDown & (CTRL_TRIANGLE | CTRL_CROSS)); + __CtrlUpdateButtons(0, tiltButtonsDown & (CTRL_TRIANGLE | CTRL_CROSS)); tiltButtonsDown &= ~(CTRL_TRIANGLE | CTRL_CROSS); } @@ -187,7 +187,7 @@ void GenerateActionButtonEvent(int digitalX, int digitalY) { if (digitalY == 1) ctrlMask |= CTRL_TRIANGLE; ctrlMask &= ~__CtrlPeekButtons(); - __CtrlButtonDown(ctrlMask); + __CtrlUpdateButtons(ctrlMask, 0); tiltButtonsDown |= ctrlMask; } @@ -208,14 +208,13 @@ void GenerateTriggerButtonEvent(int digitalX, int digitalY) { } downButtons &= ~__CtrlPeekButtons(); - __CtrlButtonUp(tiltButtonsDown & upButtons); - __CtrlButtonDown(downButtons); + __CtrlUpdateButtons(downButtons, tiltButtonsDown & upButtons); tiltButtonsDown = (tiltButtonsDown & ~upButtons) | downButtons; } void ResetTiltEvents() { // Reset the buttons we have marked pressed. - __CtrlButtonUp(tiltButtonsDown); + __CtrlUpdateButtons(0, tiltButtonsDown); tiltButtonsDown = 0; __CtrlSetAnalogXY(CTRL_STICK_LEFT, 0.0f, 0.0f); } diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 96590a75e518..b3da268c81df 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -179,13 +179,13 @@ EmuScreen::EmuScreen(const Path &filename) std::bind(&EmuScreen::onVKey, this, _1, _2), std::bind(&EmuScreen::onVKeyAnalog, this, _1, _2), [](uint32_t bitsToSet, uint32_t bitsToClear) { - __CtrlSetAllButtons(bitsToSet, bitsToClear); + __CtrlUpdateButtons(bitsToSet, bitsToClear); }, [](int pspButton, bool down) { if (down) { - __CtrlButtonDown(pspButton); + __CtrlUpdateButtons(pspButton, 0); } else { - __CtrlButtonUp(pspButton); + __CtrlUpdateButtons(0, pspButton); } }, &SetPSPAnalog, diff --git a/UI/GamepadEmu.cpp b/UI/GamepadEmu.cpp index 4e6e45e561ee..b26e00400b46 100644 --- a/UI/GamepadEmu.cpp +++ b/UI/GamepadEmu.cpp @@ -182,9 +182,9 @@ bool PSPButton::Touch(const TouchInput &input) { if (g_Config.bHapticFeedback) { System_Vibrate(HAPTIC_VIRTUAL_KEY); } - __CtrlButtonDown(pspButtonBit_); + __CtrlUpdateButtons(pspButtonBit_, 0); } else if (lastDown && !down) { - __CtrlButtonUp(pspButtonBit_); + __CtrlUpdateButtons(0, pspButtonBit_); } return retval; } @@ -361,10 +361,10 @@ void PSPDpad::ProcessTouch(float x, float y, bool down) { if (g_Config.bHapticFeedback) { System_Vibrate(HAPTIC_VIRTUAL_KEY); } - __CtrlButtonDown(dir[i]); + __CtrlUpdateButtons(dir[i], 0); } if (released & dir[i]) { - __CtrlButtonUp(dir[i]); + __CtrlUpdateButtons(0, dir[i]); } } } @@ -652,10 +652,9 @@ void PSPCustomStick::ProcessTouch(float x, float y, bool down) { posY_ = 0.0f; } - if (release != 0) - __CtrlButtonUp(release); - if (press != 0) - __CtrlButtonDown(press); + if (release || press) { + __CtrlUpdateButtons(press, release); + } } void InitPadLayout(float xres, float yres, float globalScale) { diff --git a/pspautotests b/pspautotests index 46065027500c..2e02c4a7c075 160000 --- a/pspautotests +++ b/pspautotests @@ -1 +1 @@ -Subproject commit 46065027500cd781ce7c15c051c8b0c4751ad1fa +Subproject commit 2e02c4a7c075f1a7cf28ec1add000ecd7077cd09 From 04321284c0063061dd76cf4d30ac5fc6e1002425 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 1 Apr 2023 08:57:42 +0200 Subject: [PATCH 22/25] Remove redundant callback --- Core/ControlMapper.cpp | 12 +++++------- Core/ControlMapper.h | 6 ++---- UI/ControlMappingScreen.cpp | 1 - UI/EmuScreen.cpp | 7 ------- 4 files changed, 7 insertions(+), 19 deletions(-) diff --git a/Core/ControlMapper.cpp b/Core/ControlMapper.cpp index cd144c51783b..a97fac935813 100644 --- a/Core/ControlMapper.cpp +++ b/Core/ControlMapper.cpp @@ -92,14 +92,12 @@ void ConvertAnalogStick(float x, float y, float *outX, float *outY) { void ControlMapper::SetCallbacks( std::function onVKey, std::function onVKeyAnalog, - std::function setAllPSPButtonStates, - std::function setPSPButtonState, + std::function updatePSPButtons, std::function setPSPAnalog, std::function setRawAnalog) { onVKey_ = onVKey; onVKeyAnalog_ = onVKeyAnalog; - setAllPSPButtonStates_ = setAllPSPButtonStates; - setPSPButtonState_ = setPSPButtonState; + updatePSPButtons_ = updatePSPButtons; setPSPAnalog_ = setPSPAnalog; setRawAnalog_ = setRawAnalog; } @@ -211,7 +209,7 @@ bool ControlMapper::UpdatePSPState(const InputMapping &changedMapping) { } // We only request changing the buttons where the mapped input was involved. - setAllPSPButtonStates_(buttonMask & changedButtonMask, (~buttonMask) & changedButtonMask); + updatePSPButtons_(buttonMask & changedButtonMask, (~buttonMask) & changedButtonMask); // OK, handle all the virtual keys next. For these we need to do deltas here and send events. for (int i = 0; i < VIRTKEY_COUNT; i++) { @@ -390,9 +388,9 @@ void ControlMapper::PSPKey(int deviceId, int pspKeyCode, int flags) { } else { // INFO_LOG(SYSTEM, "pspKey %d %d", pspKeyCode, flags); if (flags & KEY_DOWN) - setPSPButtonState_(pspKeyCode, true); + updatePSPButtons_(pspKeyCode, 0); if (flags & KEY_UP) - setPSPButtonState_(pspKeyCode, false); + updatePSPButtons_(0, pspKeyCode); } } diff --git a/Core/ControlMapper.h b/Core/ControlMapper.h index f0fae812c929..b0e3c604e16f 100644 --- a/Core/ControlMapper.h +++ b/Core/ControlMapper.h @@ -23,8 +23,7 @@ class ControlMapper { void SetCallbacks( std::function onVKey, std::function onVKeyAnalog, - std::function setAllPSPButtonStates_, - std::function setPSPButtonState, + std::function updatePSPButtons, std::function setPSPAnalog, std::function setRawAnalog); @@ -63,8 +62,7 @@ class ControlMapper { // Callbacks std::function onVKey_; std::function onVKeyAnalog_; - std::function setAllPSPButtonStates_; - std::function setPSPButtonState_; + std::function updatePSPButtons_; std::function setPSPAnalog_; std::function setRawAnalog_; }; diff --git a/UI/ControlMappingScreen.cpp b/UI/ControlMappingScreen.cpp index badf788125ca..7d267c2b6018 100644 --- a/UI/ControlMappingScreen.cpp +++ b/UI/ControlMappingScreen.cpp @@ -452,7 +452,6 @@ AnalogSetupScreen::AnalogSetupScreen(const Path &gamePath) : UIDialogScreenWithG [](int vkey, bool down) {}, [](int vkey, float analogValue) {}, [&](uint32_t bitsToSet, uint32_t bitsToClear) {}, - [&](int button, bool down) {}, [&](int stick, float x, float y) { analogX_[stick] = x; analogY_[stick] = y; diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index b3da268c81df..3f3ac85ef220 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -181,13 +181,6 @@ EmuScreen::EmuScreen(const Path &filename) [](uint32_t bitsToSet, uint32_t bitsToClear) { __CtrlUpdateButtons(bitsToSet, bitsToClear); }, - [](int pspButton, bool down) { - if (down) { - __CtrlUpdateButtons(pspButton, 0); - } else { - __CtrlUpdateButtons(0, pspButton); - } - }, &SetPSPAnalog, nullptr); From 0b574613b95479d5937076547fafac8332242c2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 1 Apr 2023 09:01:27 +0200 Subject: [PATCH 23/25] Address assorted feedback --- Core/ControlMapper.cpp | 10 +++++----- Core/Debugger/WebSocket/InputSubscriber.cpp | 2 -- UI/EmuScreen.cpp | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/Core/ControlMapper.cpp b/Core/ControlMapper.cpp index a97fac935813..56be689b2450 100644 --- a/Core/ControlMapper.cpp +++ b/Core/ControlMapper.cpp @@ -125,7 +125,7 @@ void ControlMapper::SetPSPAxis(int device, int stick, char axis, float value) { bool ignore = false; if (inDeadZone && lastNonDeadzoneDeviceID_[stick] != device) { // Ignore this event! See issue #15465 - ignore = true; + ignore = true; } if (!inDeadZone) { @@ -325,10 +325,10 @@ bool ControlMapper::Key(const KeyInput &key, bool *pauseTrigger) { } // TODO: See if this can be simplified further somehow. - bool mappingFound = KeyMap::InputMappingToPspButton(mapping, nullptr); - DEBUG_LOG(SYSTEM, "Key: %d DeviceId: %d", key.keyCode, key.deviceId); - if (!mappingFound || key.deviceId == DEVICE_ID_DEFAULT) { - if ((key.flags & KEY_DOWN) && key.keyCode == NKCODE_BACK) { + if ((key.flags & KEY_DOWN) && key.keyCode == NKCODE_BACK) { + bool mappingFound = KeyMap::InputMappingToPspButton(mapping, nullptr); + DEBUG_LOG(SYSTEM, "Key: %d DeviceId: %d", key.keyCode, key.deviceId); + if (!mappingFound || key.deviceId == DEVICE_ID_DEFAULT) { *pauseTrigger = true; return true; } diff --git a/Core/Debugger/WebSocket/InputSubscriber.cpp b/Core/Debugger/WebSocket/InputSubscriber.cpp index 3b2f5c5733bf..1ef9260686ee 100644 --- a/Core/Debugger/WebSocket/InputSubscriber.cpp +++ b/Core/Debugger/WebSocket/InputSubscriber.cpp @@ -187,7 +187,6 @@ void WebSocketInputState::ButtonsPress(DebuggerRequest &req) { } press.button = info->second; - // TODO: Route into the control mapper's PSPKey function instead. __CtrlUpdateButtons(press.button, 0); pressTickets_.push_back(press); } @@ -201,7 +200,6 @@ void WebSocketInputState::Broadcast(net::WebSocketServer *ws) { for (PressInfo &press : pressTickets_) { press.duration--; if (press.duration == -1) { - // TODO: Route into the control mapper's PSPKey function instead. __CtrlUpdateButtons(0, press.button); ws->Send(press.Event()); } diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 3f3ac85ef220..22bf7bf76827 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -1319,6 +1319,7 @@ Invalid / Unknown (%d) static void DrawAudioDebugStats(UIContext *ctx, const Bounds &bounds) { FontID ubuntu24("UBUNTU24"); + char statbuf[4096] = { 0 }; System_AudioGetDebugStats(statbuf, sizeof(statbuf)); @@ -1337,7 +1338,6 @@ static void DrawControlDebug(UIContext *ctx, const ControlMapper &mapper, const char statbuf[4096] = { 0 }; mapper.GetDebugString(statbuf, sizeof(statbuf)); - // System_AudioGetDebugStats(statbuf, sizeof(statbuf)); ctx->Flush(); ctx->BindFontTexture(); From f3012f6914fd6e9574efda7d44a1c1035ec8724c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 1 Apr 2023 09:07:29 +0200 Subject: [PATCH 24/25] Break out the signed->unsigned axis mapping --- Core/ControlMapper.cpp | 56 ++++++++++++++++++++++-------------------- Core/ControlMapper.h | 1 + 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/Core/ControlMapper.cpp b/Core/ControlMapper.cpp index 56be689b2450..60b071d88002 100644 --- a/Core/ControlMapper.cpp +++ b/Core/ControlMapper.cpp @@ -162,6 +162,35 @@ static float ReduceMagnitude(float value) { return value; } +float ControlMapper::MapAxisValue(float value, int vkId, const InputMapping &mapping, const InputMapping &changedMapping, bool *oppositeTouched) { + if (IsUnsignedMapping(vkId)) { + // If a signed axis is mapped to an unsigned mapping, + // convert it. This happens when mapping DirectInput triggers to analog speed, + // for example. + int direction; + if (IsSignedAxis(mapping.Axis(&direction))) { + // The value has been split up into two curInput values, so we need to go fetch the other + // and put them back together again. Kind of awkward, but at least makes the regular case simple... + InputMapping other = mapping.FlipDirection(); + if (other == changedMapping) { + *oppositeTouched = true; + } + float valueOther = curInput_[other]; + float signedValue = value - valueOther; + float ranged = (signedValue + 1.0f) * 0.5f; + if (direction == -1) { + ranged = 1.0f - ranged; + } + // NOTICE_LOG(SYSTEM, "rawValue: %f other: %f signed: %f ranged: %f", iter->second, valueOther, signedValue, ranged); + return ranged; + } else { + return value; + } + } else { + return value; + } +} + // Can only be called from Key or Axis. bool ControlMapper::UpdatePSPState(const InputMapping &changedMapping) { // Instead of taking an input key and finding what it outputs, we loop through the OUTPUTS and @@ -233,32 +262,7 @@ bool ControlMapper::UpdatePSPState(const InputMapping &changedMapping) { if (iter != curInput_.end()) { if (mapping.IsAxis()) { threshold = GetDeviceAxisThreshold(iter->first.deviceId); - if (IsUnsignedMapping(vkId)) { - // If a signed axis is mapped to an unsigned mapping, - // convert it. This happens when mapping DirectInput triggers to analog speed, - // for example. - int direction; - if (IsSignedAxis(mapping.Axis(&direction))) { - // The value has been split up into two curInput values, so we need to go fetch the other - // and put them back together again. Kind of awkward, but at least makes the regular case simple... - InputMapping other = mapping.FlipDirection(); - if (other == changedMapping) { - touchedByMapping = true; - } - float valueOther = curInput_[other]; - float signedValue = iter->second - valueOther; - float ranged = (signedValue + 1.0f) * 0.5f; - if (direction == -1) { - ranged = 1.0f - ranged; - } - // NOTICE_LOG(SYSTEM, "rawValue: %f other: %f signed: %f ranged: %f", iter->second, valueOther, signedValue, ranged); - value += ranged; - } else { - value += iter->second; - } - } else { - value += iter->second; - } + value += MapAxisValue(iter->second, vkId, mapping, changedMapping, &touchedByMapping); } else { value += iter->second; } diff --git a/Core/ControlMapper.h b/Core/ControlMapper.h index b0e3c604e16f..9fa2fc8e50a5 100644 --- a/Core/ControlMapper.h +++ b/Core/ControlMapper.h @@ -36,6 +36,7 @@ class ControlMapper { private: bool UpdatePSPState(const InputMapping &changedMapping); + float MapAxisValue(float value, int vkId, const InputMapping &mapping, const InputMapping &changedMapping, bool *oppositeTouched); void SetPSPAxis(int deviceId, int stick, char axis, float value); From 335df0fc71a42feb7db261a1abf8f4f93ebfe561 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 1 Apr 2023 09:08:41 +0200 Subject: [PATCH 25/25] libretro buildfix --- libretro/libretro.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index 2038e43bd53c..ce7a13b09142 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -1523,11 +1523,11 @@ static void retro_input(void) if (pressed) { - __CtrlButtonDown(map[i].sceCtrl); + __CtrlUpdateButtons(map[i].sceCtrl, 0); } else { - __CtrlButtonUp(map[i].sceCtrl); + __CtrlUpdateButtons(0, map[i].sceCtrl); } }