More command line improvements

This commit is contained in:
Henrik Rydgård committed 2026-07-27 18:37:28 +02:00
1 parent 3139d7ccfe
commit 5ef736f6a7
8 files changed
+52 -46

No files matched your search

+16
View File
@@ -65,6 +65,8 @@ void TimeInit() {
// The timer will be automatically deleted on process destruction. Don't need to CloseHandle.
Timer = CreateWaitableTimerExW(NULL, NULL, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS);
// TODO: We probably don't need this anymore if we are using the high res waitable timers?
#if !PPSSPP_PLATFORM(UWP)
TIMECAPS caps;
timeGetDevCaps(&caps, sizeof caps);
@@ -73,6 +75,12 @@ void TimeInit() {
#endif
}
void TimeShutdown() {
#if PPSSPP_PLATFORM(WINDOWS)
timeEndPeriod(1);
#endif
}
double time_now_d() {
LARGE_INTEGER time;
QueryPerformanceCounter(&time);
@@ -141,6 +149,10 @@ void TimeInit() {
// Nothing to do.
}
void TimeShutdown() {
// Nothing to do.
}
// The only intended use is to match the timings in VK_GOOGLE_display_timing
uint64_t time_now_raw() {
struct timespec tp;
@@ -210,6 +222,10 @@ void TimeInit() {
// Nothing to do.
}
void TimeShutdown() {
// Nothing to do.
}
static time_t start;
double time_now_d() {
+1
View File
@@ -2,6 +2,7 @@
#include <cstdint>
void TimeInit();
void TimeShutdown(); // Not really necessary to call, just for completeness.
// Seconds.
double time_now_d();
+13 -5
View File
@@ -186,6 +186,7 @@ static const CommandLineParam g_autoParams[] = {
{POFF(memReadAction), CmdParamType::Enum, "memread", '\0', "Set the action for memory read exceptions", CmdLineMode::Both, g_ExceptionActionValues, ARRAY_SIZE(g_ExceptionActionValues)},
{POFF(memWriteAction), CmdParamType::Enum, "memwrite", '\0', "Set the action for memory write exceptions", CmdLineMode::Both, g_ExceptionActionValues, ARRAY_SIZE(g_ExceptionActionValues)},
{POFF(breakAction), CmdParamType::Enum, "break", '\0', "Set the action for break exceptions", CmdLineMode::Both, g_ExceptionActionValues, ARRAY_SIZE(g_ExceptionActionValues)},
{POFF(verbose), CmdParamType::Bool, "verbose", '\0', "Enable verbose output", CmdLineMode::Both},
// TODO: At some point we should maybe simply expose all config settings to be set directly from the command line automatically?
};
@@ -214,7 +215,7 @@ int CommandLineOptions::PrintUsage(const char *progname, const char *situationTe
PRINT_STDOUT("Usage: %s [options] [FILE]\n\n", progname);
PRINT_STDOUT("Launches FILE (e.g. ISO image) if present.\n");
PRINT_STDOUT("Options (some of these are specific to SDL backend):\n");
PRINT_STDOUT("Options:\n");
PRINT_STDOUT(" -h, --help show this message and exit\n");
PRINT_STDOUT(" --version show version information and exit\n");
@@ -234,17 +235,24 @@ int CommandLineOptions::PrintUsage(const char *progname, const char *situationTe
// Skip mode-irrelevant parameters in help.
continue;
}
PRINT_STDOUT(" --%s%s%s\n", param.longName,
char key[25]{};
snprintf(key, ARRAY_SIZE(key), " --%s%s%s", param.longName,
param.shortName ? ", -" : "",
param.shortName ? std::string(1, param.shortName).c_str() : "");
// Fill key with spacing.
for (size_t j = strlen(key); j < ARRAY_SIZE(key) - 1; ++j) {
key[j] = ' ';
}
if (param.type == CmdParamType::Enum) {
PRINT_STDOUT("%s%s\n", key, param.docString);
PRINT_STDOUT(" options: ");
for (int i = 0; i < param.enumCount; ++i) {
PRINT_STDOUT("%s%s", param.enumValues[i], i + 1 < param.enumCount ? ", " : "");
}
PRINT_STDOUT("\n");
} else {
PRINT_STDOUT("%s%s\n", key, param.docString);
}
PRINT_STDOUT(" %s\n", param.docString);
}
// These are only available in SDL.
@@ -306,7 +314,7 @@ CommandLineParseResult CommandLineOptions::Parse(int argc, const char *argv[], C
logLevel = LogLevel::LVERBOSE;
break;
case 'h':
PrintUsage(argv[0], mode, nullptr);
PrintUsage(argv[0], nullptr);
return CommandLineParseResult::Exit;
// Legacy cpucore options.
case 'j':
@@ -352,7 +360,7 @@ CommandLineParseResult CommandLineOptions::Parse(int argc, const char *argv[], C
// We already incremented i.
continue;
} else if (equals(argv[i], "--help")) {
PrintUsage(argv[0], mode, nullptr);
PrintUsage(argv[0], nullptr);
return CommandLineParseResult::Exit;
} else if (equals(argv[i], "--version")) {
printf("%s\n", PPSSPP_GIT_VERSION);
+1 -1
View File
@@ -87,8 +87,8 @@ struct CommandLineOptions {
std::optional<bool> compare;
std::optional<bool> bench;
std::optional<bool> verbose;
std::optional<double> timeout;
std::optional<std::string> screenshotFilename;
std::optional<std::string> screenshotFilenameSave;
std::optional<double> timeout;
};
+1 -1
View File
@@ -99,7 +99,7 @@ std::string DefaultLangRegion() {
// Unfortunate default. There's no need to use bFirstRun, since this is only a default.
static std::string defaultLangRegion = "en_US";
std::string langRegion = System_GetProperty(SYSPROP_LANGREGION);
if (g_i18nrepo.IniExists(langRegion)) {
if (!langRegion.empty() && g_i18nrepo.IniExists(langRegion)) {
defaultLangRegion = langRegion;
} else if (langRegion.length() >= 3) {
// Don't give up. Let's try a fuzzy match - so nl_BE can match nl_NL.
+2 -1
View File
@@ -1310,7 +1310,8 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin
MainWindow::DestroyDebugWindows();
DialogManager::DestroyAll();
timeEndPeriod(1);
TimeShutdown();
UnRegisterCMPTMFApis();
+17 -37
View File
@@ -2,8 +2,12 @@
// Headless version of PPSSPP, for testing using http://code.google.com/p/pspautotests/ .
// See headless.txt.
// To build on non-windows systems, just run CMake in the SDL directory, it will build both a normal ppsspp and the headless version.
//
// Example command line to run a test in the VS debugger (useful to debug failures):
// > --root pspautotests/tests/../ --compare --timeout=5 --graphics=software pspautotests/tests/cpu/cpu_alu/cpu_alu.prx
// Example command line for taking screenshots from a frame dump:
// > -l --graphics=vulkan --screenshot-save=vt_ref.bmp "D:\PSP ISO\dump\Depth\11578 Virtua Tennis pause menu ULES00126_0002.zip" --resolution-scale=2
//
// NOTE: In MSVC, don't forget to set the working directory to $ProjectDir\.. in debug settings.
#include "ppsspp_config.h"
@@ -17,13 +21,11 @@
#include <algorithm>
#include "Common/Profiler/Profiler.h"
#include "Common/System/NativeApp.h"
#include "Common/System/Request.h"
#include "Common/System/System.h"
#include "Common/CommonWindows.h"
#if PPSSPP_PLATFORM(WINDOWS)
#include <timeapi.h>
#else
#include <csignal>
#endif
@@ -121,33 +123,18 @@ std::string NativeLoadSecret(std::string_view nameOfSecret) {
return "";
}
int printUsage(const char *progname, const char *reason) {
if (reason != NULL)
fprintf(stderr, "Error: %s\n\n", reason);
fprintf(stderr, "PPSSPP Headless\n");
fprintf(stderr, "This is primarily meant as a non-interactive test tool.\n\n");
fprintf(stderr, "Usage: %s file.elf... [options]\n\n", progname);
fprintf(stderr, "Options:\n");
int printUsage(const CommandLineOptions &options, const char *progname, const char *reason) {
options.PrintUsage(progname, reason);
fprintf(stderr, " -m, --mount umd.cso mount iso on umd1:\n");
fprintf(stderr, " -r, --root some/path mount path on host0: (elfs must be in here)\n");
fprintf(stderr, " -l, --log full log output, not just emulated printfs\n");
fprintf(stderr, " --debugger=PORT enable websocket debugger and break at start\n");
fprintf(stderr, " --graphics=BACKEND use a different gpu backend\n");
fprintf(stderr, " options: gles, software, directx9, etc.\n");
fprintf(stderr, " --screenshot=FILE compare against a screenshot\n");
fprintf(stderr, " --screenshot-save=FILE save rendered screenshot to a BMP file\n");
fprintf(stderr, " --max-mse=NUMBER maximum allowed MSE error for screenshot\n");
fprintf(stderr, " --timeout=SECONDS abort test it if takes longer than SECONDS\n");
fprintf(stderr, " -v, --verbose show the full passed/failed result\n");
fprintf(stderr, " -i use the interpreter\n");
fprintf(stderr, " --ir use ir interpreter\n");
fprintf(stderr, " -j use jit (default)\n");
fprintf(stderr, " -c, --compare compare with output in file.expected\n");
fprintf(stderr, " --bench run multiple times and output speed\n");
fprintf(stderr, "\nSee headless.txt for details.\n");
return 1;
}
@@ -260,9 +247,9 @@ void System_SendDebugScreenshot(const uint8_t *data, int width, int height) {
struct AutoTestOptions {
double timeout;
double maxScreenshotError;
bool compare : 1;
bool verbose : 1;
bool bench : 1;
bool compare;
bool verbose;
bool bench;
};
bool RunAutoTest(HeadlessHost *headlessHost, CoreParameter &coreParameter, const AutoTestOptions &opt) {
@@ -464,6 +451,7 @@ int main(int argc, const char* argv[]) {
testOptions.compare = cmdLineOptions.compare.value_or(false);
testOptions.bench = cmdLineOptions.bench.value_or(false);
testOptions.timeout = cmdLineOptions.timeout.value_or(std::numeric_limits<double>::infinity());
testOptions.verbose = cmdLineOptions.verbose.value_or(false);
bool fullLog = false;
const char *stateToLoad = 0;
@@ -488,32 +476,26 @@ int main(int argc, const char* argv[]) {
for (int i = 1; i < argc; i++) {
if (!strcmp(argv[i], "-m") || !strcmp(argv[i], "--mount")) {
if (++i >= argc)
return printUsage(argv[0], "Missing argument after -m");
return printUsage(cmdLineOptions, argv[0], "Missing argument after -m");
mountIso = argv[i];
}
else if (!strcmp(argv[i], "-l") || !strcmp(argv[i], "--log"))
fullLog = true;
else if (!strcmp(argv[i], "-o") || !strcmp(argv[i], "--odslog"))
outputDebugStringLog = true;
else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose"))
testOptions.verbose = true;
else if (!strncmp(argv[i], "--max-mse=", strlen("--max-mse=")) && strlen(argv[i]) > strlen("--max-mse="))
testOptions.maxScreenshotError = strtod(argv[i] + strlen("--max-mse="), nullptr);
else if (!strncmp(argv[i], "--state=", strlen("--state=")) && strlen(argv[i]) > strlen("--state="))
stateToLoad = argv[i] + strlen("--state=");
else if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "-h"))
return printUsage(argv[0], NULL);
return printUsage(cmdLineOptions, argv[0], NULL);
else if (!strcmp(argv[i], "--ignore")) {
if (++i >= argc)
return printUsage(argv[0], "Missing argument after --ignore");
return printUsage(cmdLineOptions, argv[0], "Missing argument after --ignore");
ignoredTests.push_back(argv[i]);
}
}
if (!fullLog) {
printf("Pass the -l flag to see full log output.\n");
}
for (const std::string &filename : cmdLineOptions.bootFilenames) {
AddToTestsByPath(&testFilenames, filename);
}
@@ -683,7 +665,7 @@ int main(int argc, const char* argv[]) {
AddToTestsByPath(&testFilenames, (g_Config.flash0Directory / "vsh/module/vshmain.prx").ToString());
}
if (testFilenames.empty()) {
return printUsage(argv[0], argc <= 1 ? NULL : "No executables specified");
return printUsage(cmdLineOptions, argv[0], argc <= 1 ? NULL : "No executables specified");
}
if (cmdLineOptions.debuggerPort.has_value()) {
@@ -754,12 +736,10 @@ int main(int argc, const char* argv[]) {
g_VFS.Clear();
g_logManager.Shutdown();
if (cmdLineOptions.debuggerPort.has_value())
if (cmdLineOptions.debuggerPort.has_value()) {
net::Shutdown();
#if PPSSPP_PLATFORM(WINDOWS)
timeEndPeriod(1);
#endif
}
TimeShutdown();
g_threadManager.Teardown();