Files
ppsspp/Core/CoreParameter.h
T
Henrik RydgårdandClaude Opus 5 44b832f755 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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
2026-08-18 13:42:13 +02:00

100 lines
2.9 KiB
C++

// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#pragma once
#include <string>
#include "Common/File/Path.h"
#include "Core/Compatibility.h"
#include "Core/Loaders.h"
enum GPUCore : int {
GPUCORE_GLES = 0,
GPUCORE_SOFTWARE = 1,
GPUCORE_DIRECTX11 = 3,
GPUCORE_VULKAN = 4,
};
enum class FPSLimit {
NORMAL = 0,
CUSTOM1 = 1,
CUSTOM2 = 2,
ANALOG = 3,
// Set over the WebSocket debugger (game.speed.set). Deliberately its own mode instead of
// reusing CUSTOM1/2: those read their rate from g_Config, which is persisted per game, so a
// debugger session would permanently overwrite the user's configured alternative speed.
DEBUGGER = 4,
};
class FileLoader;
class GraphicsContext;
namespace Draw {
class DrawContext;
}
enum class CPUCore;
// PSP_CoreParameter()
struct CoreParameter {
CPUCore cpuCore;
GPUCore gpuCore;
GraphicsContext *graphicsContext = nullptr; // TODO: Find a better place.
bool enableSound = true; // there aren't multiple sound cores.
Path fileToStart;
Path mountIso; // If non-empty, and fileToStart is an ELF or PBP, will mount this ISO in the background to umd1:.
Path mountRoot; // If non-empty, and fileToStart is an ELF or PBP, mount this as host0: / umd0:.
std::string errorString;
bool loadGameConfigs = true;
bool startBreak = false;
std::string *collectDebugOutput = nullptr;
bool headLess = false; // Try to avoid messageboxes etc
// Internal PSP rendering resolution and scale factor.
int renderScaleFactor = 1;
int renderWidth = 0;
int renderHeight = 0;
// Actual output resolution in pixels.
int pixelWidth = 0;
int pixelHeight = 0;
// Can be modified at runtime. Do not belong here.
bool fastForward = false;
FPSLimit fpsLimit = FPSLimit::NORMAL;
int analogFpsLimit = 0;
// Target FPS for FPSLimit::DEBUGGER. Lives here rather than in g_Config on purpose - see the
// enum. Resets with the rest of this struct on every boot, so a debugger can't leave a game
// permanently slowed down.
int debuggerFpsLimit = 0;
bool updateRecent = true;
// Freeze-frame. For nvidia perfhud profiling. Developers only.
bool freezeNext = false;
bool frozen = false;
FileLoader *mountIsoLoader = nullptr;
IdentifiedFileType fileType = IdentifiedFileType::UNKNOWN;
Compatibility compat;
};