diff --git a/Core/CmdLine.cpp b/Core/CmdLine.cpp index 7ea12e14a1..947a89cddb 100644 --- a/Core/CmdLine.cpp +++ b/Core/CmdLine.cpp @@ -256,6 +256,8 @@ static const CommandLineParam g_autoParams[] = { {POFF(timeoutWall), CmdParamType::Double, "timeout", '\0', "Alias for --timeout-wall", CmdLineMode::Headless}, {POFF(instance), CmdParamType::Int, "instance", '\0', "Run as instance N of several talking to each other over ad hoc (own 127.0.0.N address and MAC)", CmdLineMode::Headless}, {POFF(realtime), CmdParamType::Bool, "realtime", '\0', "Run at PSP speed instead of as fast as possible", CmdLineMode::Headless}, + {POFF(portOffset), CmdParamType::Int, "port-offset", '\0', "Add this to every ad hoc port on the host (default from the config, normally 10000)"}, + {POFF(speedPercent), CmdParamType::Int, "speed", '\0', "With --realtime, run at this percentage of PSP speed (alternate speed 1)", CmdLineMode::Headless}, {POFF(adhocServer), CmdParamType::Bool, "adhoc-server", '\0', "Run the built-in ad hoc server"}, {POFF(nickname), CmdParamType::String, "nickname", '\0', "The PSP's nickname (System settings), for this run only"}, {POFF(lagSync), CmdParamType::Bool, "lag-sync", '\0', "Keep emulated time in step with the wall clock every millisecond, not every frame (the Force real clock sync setting)"}, @@ -663,6 +665,19 @@ void CommandLineOptions::ApplyToConfig() const { g_logManager.DoNotSaveChannels(); } + if (portOffset.has_value()) { + g_Config.iPortOffset = portOffset.value(); + g_Config.DoNotSaveSetting(&g_Config.iPortOffset); + } + + if (speedPercent.has_value()) { + g_Config.iAltSpeedPercent1 = speedPercent.value(); + g_Config.DoNotSaveSetting(&g_Config.iAltSpeedPercent1); + // Ad hoc tests are what this is for, and speed control is otherwise off while connected. + g_Config.bAllowSpeedControlWhileConnected = true; + g_Config.DoNotSaveSetting(&g_Config.bAllowSpeedControlWhileConnected); + } + if (lockedCPUSpeed.has_value()) { g_Config.iLockedCPUSpeed = lockedCPUSpeed.value(); g_Config.DoNotSaveSetting(&g_Config.iLockedCPUSpeed); diff --git a/Core/CmdLine.h b/Core/CmdLine.h index cfb79b2be8..0ee8091eeb 100644 --- a/Core/CmdLine.h +++ b/Core/CmdLine.h @@ -199,6 +199,11 @@ struct CommandLineOptions { // Both are for ad hoc tests where instances talk to each other. std::optional instance; std::optional realtime; + // Headless with --realtime: run at this percentage of PSP speed, through alternate speed 1. + std::optional speedPercent; + // Added to every ad hoc port on the host. Back-to-back test runs vary it, so a connection doesn't + // collide with the previous run's, still in TCP TIME_WAIT. + std::optional portOffset; std::optional adhocServer; std::optional lagSync; std::optional nickname; diff --git a/headless/Headless.cpp b/headless/Headless.cpp index 5f4fd0ed4e..a05d52cde3 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -63,6 +63,7 @@ #include "Core/HLE/HLE.h" #include "Core/HLE/__sceAudio.h" #include "Core/HLE/sceCtrl.h" +#include "Core/HLE/sceDisplay.h" #include "Core/MIPS/MIPSTables.h" #include "Core/System.h" #include "Core/Util/PSARUnpack.h" @@ -665,7 +666,10 @@ static bool RunAutoTest(GraphicsContext *graphicsContext, CoreParameter &corePar // draw never flips, so the emulated clock would race ahead of instances it talks to. The JIT // cores only return at the end of a frame, so like the app, this paces once per frame. blockTicks = (int)usToCycles(1000); - const double ahead = emulatedElapsed - (time_now_d() - startTime); + // At an alternate speed (--speed), the emulated clock runs that much faster than the wall clock. + const int frameLimit = __DisplayGetFrameTimingLimit(); + const double speed = frameLimit > 0 ? frameLimit / 60.0 : 1.0; + const double ahead = (emulatedElapsed - (time_now_d() - startTime) * speed) / speed; // With --lag-sync, lag sync paces every emulated millisecond instead, and two pacers out of // phase would undo each other. if (ahead > 0.001 && !g_Config.bForceLagSync) { @@ -1345,6 +1349,9 @@ int main(int argc, const char* argv[]) { coreParameter.pixelHeight = 272 * coreParameter.renderScaleFactor; // Ad hoc tests between instances wait for each other over the real network, so they need PSP speed. coreParameter.fastForward = !cmdLineOptions.realtime.value_or(false); + if (cmdLineOptions.speedPercent.has_value()) { + coreParameter.fpsLimit = FPSLimit::CUSTOM1; + } #if PPSSPP_PLATFORM(IOS) // GetExeDirectory() knows nothing on iOS, but argv[0] is the executable inside the .app, diff --git a/test.py b/test.py index 198f24eb5c..47abe0a2ca 100755 --- a/test.py +++ b/test.py @@ -5,6 +5,7 @@ import sys import os import subprocess +import random import threading import glob import platform @@ -747,13 +748,17 @@ test_flags = { # The ad hoc tests create groups, so they need a server to log in to, and they wait for its replies # in real time. -ADHOC_FLAGS = ["--adhoc-server", "--realtime", "--timeout-wall=60"] +# They run at 8x PSP speed: timeouts and delays are in emulated time, and only the local network round +# trips stay real. +ADHOC_FLAGS = ["--adhoc-server", "--realtime", "--speed=" + os.environ.get("ADHOC_SPEED", "800"), "--timeout-wall=60"] def flags_for(test): if test in test_flags: return test_flags[test] if test.startswith("net/adhoc/"): - return ADHOC_FLAGS + # A new port offset each run: a connection the last run made with the same ports is still in TCP + # TIME_WAIT for a while, and connecting again fails. + return ADHOC_FLAGS + ["--port-offset=%d" % random.randrange(10000, 30000)] return None def is_pair_test(test): @@ -846,6 +851,7 @@ def run_pair_tests(test_list, args): # built-in ad hoc server and relay. cmdline = [sys.executable, os.path.join(os.path.dirname(os.path.abspath(__file__)), 'Tools', 'adhoc_multi.py'), '--headless', PPSSPP_EXE, '--relay'] cmdline.extend([i for i in args if i not in ['-g', '-m', '-b', '--teamcity']]) + cmdline.append("--speed=" + os.environ.get("ADHOC_SPEED", "800")) cmdline.extend(test_list) returncode = subprocess.call(cmdline) print("Ran " + ' '.join(cmdline))