mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
Debugger: add game.speed.get/set for fast-forward and a speed percentage
Both mechanisms already existed and just weren't reachable from the WebSocket API: PSP_CoreParameter().fastForward for unlimited, and an FPSLimit mode plus a target frame rate for everything else. FrameTimingLimit() in sceDisplay.cpp is where they all resolve to a single number. The one thing worth being careful about is which knob to drive. Reusing CUSTOM1/CUSTOM2 - the user's own alternative speeds - would have meant writing g_Config.iFpsLimit1, which is persisted per game, so a debugger session would permanently overwrite whatever speed the user had configured. Same class of mistake as a debug setting leaking into the saved config. So this gets its own FPSLimit::DEBUGGER mode and a debuggerFpsLimit field on CoreParameter, which isn't persisted and is value-initialized on every boot. Two things fall out for free: the analog-speed handler already backs off for any mode it doesn't own (EmuScreen.cpp), and a debugger can't leave a game slowed down after a restart. Percentages are relative to 60 FPS, the same convention GameSettingsScreen uses when presenting the alternative speeds, so "200%" means one thing across the app. Unlimited is fastForward rather than percent 0, so there's a single way to express it. The response reports limitFps straight from FrameTimingLimit(), exposed for the purpose. That's the number the frame timing actually consumes, so a client never has to reconstruct the interaction between fast-forward, this override and the user's own hotkeys - which is exactly the sort of thing that goes stale. Requests fail rather than being quietly ignored when something else owns the speed: achievements hardcore mode, or netplay without the "allow speed control while connected" option. Being ignored with a successful response is the worst outcome for an automation client. Verified against a running headless instance: percent 200 with fast-forward off resolves to limitFps 120, fast-forward takes it to 0 while remembering the 200 underneath, an explicit null clears it, and out-of-range or empty requests are rejected. The throttle *behaviour* is not verified end to end - a debug, software-rendered headless build runs this game at about 1% of real time, so it never reaches any of these targets and the limit can't be observed. That path is shared with the existing CUSTOM1/CUSTOM2 speeds and unchanged. sceNet.h is forward-declared rather than included: it reaches windows.h through proAdhoc.h, which redefines the OPTIONAL macro that collides with DebuggerParamType::OPTIONAL - the hazard this file's header comment already warns about. pspautotests 314/314, UnitTest 55/55. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
This commit is contained in:
1 parent
a0fbf32469
commit
44b832f755
6 files changed
+184
-1
No files matched your search
@@ -132,6 +132,44 @@ way for periodic GPU stats. `client.config.set` in the same file carries
|
||||
per-connection settings that aren't about broadcasts - currently just
|
||||
`acknowledgeDeferred`, described under "Message protocol" above.
|
||||
|
||||
### Emulation speed
|
||||
|
||||
`game.speed.set` drives two independent things:
|
||||
|
||||
```json
|
||||
-> { "event": "game.speed.set", "fastForward": true } // unlimited
|
||||
-> { "event": "game.speed.set", "percent": 200 } // double speed
|
||||
-> { "event": "game.speed.set", "percent": 25 } // quarter speed
|
||||
-> { "event": "game.speed.set", "percent": null } // drop the override
|
||||
<- { "event": "game.speed.set", "fastForward": false, "percent": 200, "limitFps": 120 }
|
||||
```
|
||||
|
||||
Percentages are relative to 60 FPS, matching how the in-app settings present the
|
||||
alternative speeds - so `200` means the same thing in both places. `percent`
|
||||
must be at least 1; use `fastForward` for unlimited rather than `0`, so there's
|
||||
only one way to say it. `fastForward` wins while it is on, and `percent` is
|
||||
remembered underneath it.
|
||||
|
||||
`limitFps` in the response is the frame rate throttling is actually aiming for
|
||||
once everything - fast-forward, this override, the user's own alternative-speed
|
||||
hotkeys - has been taken into account, with `0` meaning unlimited. It comes
|
||||
straight from the function the frame timing itself consumes, so prefer reading
|
||||
it over inferring the result from the other two fields.
|
||||
|
||||
This is a **separate channel** from the user's own alternative speeds. It
|
||||
deliberately never touches `g_Config`, whose speed settings are persisted per
|
||||
game, so a debugger session can't permanently change what the user configured.
|
||||
Clearing the override also only stands down from a limit set through this event,
|
||||
never from one the user set themselves. It resets on every game boot.
|
||||
|
||||
The request **fails** rather than being silently ignored when something else
|
||||
owns the speed: RetroAchievements hardcore mode, or being connected to a network
|
||||
game without "allow speed control while connected".
|
||||
|
||||
Headless sets fast-forward at startup and never turns it off on its own, so
|
||||
`game.speed.set` works there too - turning fast-forward off makes headless
|
||||
throttle to real time, which is occasionally useful for a wall-clock repro.
|
||||
|
||||
### Breakpoint hits
|
||||
|
||||
`cpu.breakpoint.hit` fires every time a breakpoint's condition passes and it has
|
||||
@@ -206,7 +244,7 @@ file - this is just an index.
|
||||
|
||||
| Category | Events | File |
|
||||
|---|---|---|
|
||||
| Game/version | `game.reset`, `game.status`, `version` | `GameSubscriber.cpp` |
|
||||
| Game/version | `game.reset`, `game.status`, `game.speed.get/set` (emulation speed - unlimited fast-forward, or a percentage of 60 FPS; see below), `version` | `GameSubscriber.cpp` |
|
||||
| CPU core | `cpu.stepping`, `cpu.resume`, `cpu.status` (reports `ticks` plus `us`, emulated microseconds, and `clockHz` - use `us` to line up with wall-clock timings, since games change the clock frequency and the ticks-per-second ratio isn't fixed), `cpu.getAllRegs`, `cpu.getReg`, `cpu.setReg`, `cpu.evaluate` | `CPUCoreSubscriber.cpp` |
|
||||
| Stepping | `cpu.stepInto`, `cpu.stepOver`, `cpu.stepOut`, `cpu.runUntil`, `cpu.runUntilTime` (run until a point in emulated time - `us` absolute or `relativeUs` from now - and break there; this is how to get a scripted repro reproducibly "N seconds into the game" instead of polling `cpu.status` in a loop), `cpu.nextHLE` | `SteppingSubscriber.cpp` |
|
||||
| Breakpoints | `cpu.breakpoint.add/update/remove/list`, `memory.breakpoint.add/update/remove/list`, `cpu.regBreakpoint.add/update/remove/list` (break when a register is written to, by any instruction anywhere - currently GPRs only; interpreter-only, no effect under a JIT backend) | `BreakpointSubscriber.cpp` |
|
||||
|
||||
Reference in new issue
Block a user