mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-14 15:00:30 +02:00
Dialog: Follow the firmware's thread priorities in init/shutdown
On a PSP, dialog init and shutdown happen partly at the accessThread priority and partly at the graphicsThread priority, one phase after the other. Model that with one helper thread that switches priority per phase, and let starting it reschedule normally instead of disabling interrupts. A caller with worse priority than both now sees shutdown complete inside ShutdownStart, as on hardware. NFL Street 3 (graphics 17, access 19, caller 111) calls the next InitStart right after ShutdownStart and used to loop forever on 'A save request is already running' (#19957). The utility pspautotests, where the caller has better priority, are unchanged. Also logs the dialog thread priorities at debug level. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
24e44cb5dd
commit
20970d5fad
3 files changed
+50
-12
No files matched your search
@@ -27,6 +27,7 @@
|
||||
#include "Core/CoreTiming.h"
|
||||
#include "Core/Dialog/PSPDialog.h"
|
||||
#include "Core/HLE/sceCtrl.h"
|
||||
#include "Core/HLE/sceKernelThread.h"
|
||||
#include "Core/HLE/scePower.h"
|
||||
#include "Core/HLE/sceUtility.h"
|
||||
#include "Core/MemMapHelpers.h"
|
||||
@@ -66,6 +67,14 @@ const char *UtilityDialogStatusToString(PSPDialog::DialogStatus status) {
|
||||
void PSPDialog::InitCommon() {
|
||||
UpdateCommon();
|
||||
|
||||
if (const pspUtilityDialogCommon *common = GetCommonParam()) {
|
||||
// How these compare with the calling thread decides what the game sees from GetStatus right
|
||||
// after InitStart/ShutdownStart.
|
||||
DEBUG_LOG(Log::sceUtility, "%s thread priorities: graphics=%d access=%d font=%d sound=%d (caller %d)",
|
||||
UtilityDialogTypeToString(DialogType()), common->graphicsThread, common->accessThread,
|
||||
common->fontThread, common->soundThread, KernelCurThreadPriority());
|
||||
}
|
||||
|
||||
if (GetCommonParam() && GetCommonParam()->language != GetPSPLanguage()) {
|
||||
WARN_LOG(Log::sceUtility, "Game requested language %d, ignoring and using user language", GetCommonParam()->language);
|
||||
}
|
||||
@@ -162,7 +171,7 @@ void PSPDialog::ChangeStatusInit(int delayUs) {
|
||||
|
||||
auto params = GetCommonParam();
|
||||
if (params)
|
||||
UtilityDialogInitialize(DialogType(), delayUs, params->accessThread);
|
||||
UtilityDialogInitialize(DialogType(), delayUs, params->accessThread, params->graphicsThread);
|
||||
else
|
||||
ChangeStatus(SCE_UTILITY_STATUS_RUNNING, delayUs);
|
||||
}
|
||||
@@ -174,7 +183,7 @@ void PSPDialog::ChangeStatusShutdown(int delayUs) {
|
||||
|
||||
auto params = GetCommonParam();
|
||||
if (params && !skipDialogShutdown)
|
||||
UtilityDialogShutdown(DialogType(), delayUs, params->accessThread);
|
||||
UtilityDialogShutdown(DialogType(), delayUs, params->accessThread, params->graphicsThread);
|
||||
else
|
||||
ChangeStatus(SCE_UTILITY_STATUS_NONE, delayUs);
|
||||
}
|
||||
|
||||
+37
-8
@@ -600,8 +600,21 @@ void __UtilityShutdown() {
|
||||
delete npSigninDialog;
|
||||
}
|
||||
|
||||
void UtilityDialogInitialize(UtilityDialogType type, int delayUs, int priority) {
|
||||
// On a PSP, dialog init and shutdown happen partly at the accessThread priority and partly at the
|
||||
// graphicsThread priority, one phase after the other. One helper thread that switches priority per
|
||||
// phase reproduces who gets to run when - e.g. a game thread with worse priority than both doesn't
|
||||
// run again until the dialog has finished starting up or shutting down.
|
||||
static bool ValidThreadPriority(int priority) {
|
||||
return priority >= 0x08 && priority <= 0x77;
|
||||
}
|
||||
|
||||
static int PhasePriority(int priority, int fallback) {
|
||||
return ValidThreadPriority(priority) ? priority : fallback;
|
||||
}
|
||||
|
||||
void UtilityDialogInitialize(UtilityDialogType type, int delayUs, int accessPriority, int graphicsPriority) {
|
||||
int partDelay = delayUs / 4;
|
||||
const int dialogPriority = PhasePriority(graphicsPriority, accessPriority);
|
||||
const u32_le insts[] = {
|
||||
// Make sure we don't discard/deadbeef a0.
|
||||
(u32_le)MIPS_MAKE_ORI(MIPS_REG_S0, MIPS_REG_A0, 0),
|
||||
@@ -611,12 +624,18 @@ void UtilityDialogInitialize(UtilityDialogType type, int delayUs, int priority)
|
||||
(u32_le)MIPS_MAKE_ORI(MIPS_REG_A2, MIPS_REG_ZERO, 0),
|
||||
(u32_le)MIPS_MAKE_SYSCALL("sceSuspendForUser", "sceKernelVolatileMemLock"),
|
||||
|
||||
// Loading, at accessThread priority.
|
||||
(u32_le)MIPS_MAKE_ORI(MIPS_REG_A0, MIPS_REG_S0, 0),
|
||||
(u32_le)MIPS_MAKE_SYSCALL("sceUtility", "__UtilityWorkUs"),
|
||||
(u32_le)MIPS_MAKE_ORI(MIPS_REG_A0, MIPS_REG_S0, 0),
|
||||
(u32_le)MIPS_MAKE_SYSCALL("sceUtility", "__UtilityWorkUs"),
|
||||
(u32_le)MIPS_MAKE_ORI(MIPS_REG_A0, MIPS_REG_S0, 0),
|
||||
(u32_le)MIPS_MAKE_SYSCALL("sceUtility", "__UtilityWorkUs"),
|
||||
|
||||
// Setting up the dialog, at graphicsThread priority, then the status goes to RUNNING.
|
||||
(u32_le)MIPS_MAKE_ORI(MIPS_REG_A0, MIPS_REG_ZERO, 0),
|
||||
(u32_le)MIPS_MAKE_ORI(MIPS_REG_A1, MIPS_REG_ZERO, dialogPriority),
|
||||
(u32_le)MIPS_MAKE_SYSCALL("ThreadManForUser", "sceKernelChangeThreadPriority"),
|
||||
(u32_le)MIPS_MAKE_ORI(MIPS_REG_A0, MIPS_REG_S0, 0),
|
||||
(u32_le)MIPS_MAKE_SYSCALL("sceUtility", "__UtilityWorkUs"),
|
||||
|
||||
@@ -626,22 +645,34 @@ void UtilityDialogInitialize(UtilityDialogType type, int delayUs, int priority)
|
||||
};
|
||||
|
||||
CleanupDialogThreads(true);
|
||||
accessThread = new HLEHelperThread("ScePafJob", insts, (uint32_t)ARRAY_SIZE(insts), priority, 0x200);
|
||||
accessThread = new HLEHelperThread("ScePafJob", insts, (uint32_t)ARRAY_SIZE(insts), accessPriority, 0x200);
|
||||
accessThread->Start(partDelay, 0);
|
||||
accessThreadFinished = false;
|
||||
accessThreadState = "initializing";
|
||||
}
|
||||
|
||||
void UtilityDialogShutdown(UtilityDialogType type, int delayUs, int priority) {
|
||||
void UtilityDialogShutdown(UtilityDialogType type, int delayUs, int accessPriority, int graphicsPriority) {
|
||||
// Break it up so better-priority rescheduling happens.
|
||||
// The windows aren't this regular, but close.
|
||||
int partDelay = delayUs / 4;
|
||||
const int dialogPriority = PhasePriority(graphicsPriority, accessPriority);
|
||||
const u32_le insts[] = {
|
||||
// Make sure we don't discard/deadbeef 'em.
|
||||
(u32_le)MIPS_MAKE_ORI(MIPS_REG_S0, MIPS_REG_A0, 0),
|
||||
|
||||
// Tearing down the dialog, at graphicsThread priority.
|
||||
(u32_le)MIPS_MAKE_ORI(MIPS_REG_A0, MIPS_REG_ZERO, 0),
|
||||
(u32_le)MIPS_MAKE_ORI(MIPS_REG_A1, MIPS_REG_ZERO, dialogPriority),
|
||||
(u32_le)MIPS_MAKE_SYSCALL("ThreadManForUser", "sceKernelChangeThreadPriority"),
|
||||
(u32_le)MIPS_MAKE_ORI(MIPS_REG_A0, MIPS_REG_S0, 0),
|
||||
(u32_le)MIPS_MAKE_SYSCALL("sceUtility", "__UtilityWorkUs"),
|
||||
(u32_le)MIPS_MAKE_ORI(MIPS_REG_A0, MIPS_REG_S0, 0),
|
||||
(u32_le)MIPS_MAKE_SYSCALL("sceUtility", "__UtilityWorkUs"),
|
||||
|
||||
// Cleaning up at accessThread priority, then the status goes to NONE.
|
||||
(u32_le)MIPS_MAKE_ORI(MIPS_REG_A0, MIPS_REG_ZERO, 0),
|
||||
(u32_le)MIPS_MAKE_ORI(MIPS_REG_A1, MIPS_REG_ZERO, accessPriority),
|
||||
(u32_le)MIPS_MAKE_SYSCALL("ThreadManForUser", "sceKernelChangeThreadPriority"),
|
||||
(u32_le)MIPS_MAKE_ORI(MIPS_REG_A0, MIPS_REG_S0, 0),
|
||||
(u32_le)MIPS_MAKE_SYSCALL("sceUtility", "__UtilityWorkUs"),
|
||||
(u32_le)MIPS_MAKE_ORI(MIPS_REG_A0, MIPS_REG_S0, 0),
|
||||
@@ -652,15 +683,13 @@ void UtilityDialogShutdown(UtilityDialogType type, int delayUs, int priority) {
|
||||
(u32_le)MIPS_MAKE_SYSCALL("sceUtility", "__UtilityFinishDialog"),
|
||||
};
|
||||
|
||||
// Starting the thread reschedules normally, so a caller with worse priority than both phases sees
|
||||
// NONE by the time ShutdownStart returns.
|
||||
CleanupDialogThreads(true);
|
||||
bool prevInterrupts = __InterruptsEnabled();
|
||||
__DisableInterrupts();
|
||||
accessThread = new HLEHelperThread("ScePafJob", insts, (uint32_t)ARRAY_SIZE(insts), priority, 0x200);
|
||||
accessThread = new HLEHelperThread("ScePafJob", insts, (uint32_t)ARRAY_SIZE(insts), accessPriority, 0x200);
|
||||
accessThread->Start(partDelay, 0);
|
||||
accessThreadFinished = false;
|
||||
accessThreadState = "shutting down";
|
||||
if (prevInterrupts)
|
||||
__EnableInterrupts();
|
||||
}
|
||||
|
||||
static int UtilityWorkUs(int us) {
|
||||
|
||||
@@ -123,8 +123,8 @@ void __UtilityInit();
|
||||
void __UtilityDoState(PointerWrap &p);
|
||||
void __UtilityShutdown();
|
||||
|
||||
void UtilityDialogInitialize(UtilityDialogType type, int delayUs, int priority);
|
||||
void UtilityDialogShutdown(UtilityDialogType type, int delayUs, int priority);
|
||||
void UtilityDialogInitialize(UtilityDialogType type, int delayUs, int accessPriority, int graphicsPriority);
|
||||
void UtilityDialogShutdown(UtilityDialogType type, int delayUs, int accessPriority, int graphicsPriority);
|
||||
|
||||
typedef void (*ModuleLoadCallback)(int state, u32 loadAddr, u32 loadSize);
|
||||
|
||||
|
||||
Reference in new issue
Block a user