From 6290ac6cbdf11f76e6957dc5d1da856e9055d3d2 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 9 Jul 2013 22:13:40 -0700 Subject: [PATCH 1/9] Sas: Process SAS on a separate thread. Remaining issues: * Need a config setting. * Locking for voices / config? * Locking for atrac interop? --- Core/HLE/sceSas.cpp | 110 ++++++++++++++++++++++++++++++++++++++----- Core/HW/SasAudio.cpp | 2 - 2 files changed, 97 insertions(+), 15 deletions(-) diff --git a/Core/HLE/sceSas.cpp b/Core/HLE/sceSas.cpp index d78130b5b8..2913f7a195 100644 --- a/Core/HLE/sceSas.cpp +++ b/Core/HLE/sceSas.cpp @@ -28,6 +28,11 @@ #include #include "base/basictypes.h" +#include "base/functional.h" +#include "base/mutex.h" +#include "profiler/profiler.h" +#include "thread/thread.h" +#include "thread/threadutil.h" #include "Common/Log.h" #include "Core/HLE/HLE.h" #include "Core/HLE/FunctionWrappers.h" @@ -69,8 +74,78 @@ enum { // No known games use more than one instance of Sas though. static SasInstance *sas = NULL; +enum SasThreadState { + DISABLED, + READY, + QUEUED, +}; +struct SasThreadParams { + u32 outAddr; + u32 inAddr; + int leftVol; + int rightVol; +}; + +static std::thread *sasThread; +static recursive_mutex sasWakeMutex; +static recursive_mutex sasDoneMutex; +static condition_variable sasWake; +static condition_variable sasDone; +static volatile int sasThreadState = SasThreadState::DISABLED; +static SasThreadParams sasThreadParams; + +int __SasThread() { + setCurrentThreadName("SAS"); + + lock_guard guard(sasWakeMutex); + while (sasThreadState != SasThreadState::DISABLED) { + sasWake.wait(sasWakeMutex); + if (sasThreadState == SasThreadState::QUEUED) { + sas->Mix(sasThreadParams.outAddr, sasThreadParams.inAddr, sasThreadParams.leftVol, sasThreadParams.rightVol); + + sasDoneMutex.lock(); + sasThreadState = SasThreadState::READY; + sasDone.notify_one(); + sasDoneMutex.unlock(); + } + } + return 0; +} + +static void __SasEnqueueMix(u32 outAddr, u32 inAddr = 0, int leftVol = 0, int rightVol = 0) { + if (sasThreadState == SasThreadState::DISABLED) { + // No thread, call it immediately. + sas->Mix(outAddr, inAddr, leftVol, rightVol); + return; + } + + if (sasThreadState == SasThreadState::QUEUED) { + // Wait for the queue to drain. + sasDoneMutex.lock(); + while (sasThreadState == SasThreadState::QUEUED) + sasDone.wait(sasDoneMutex); + sasDoneMutex.unlock(); + } + + // We're safe to write, since it can't be processing now anymore. + // No other thread enqueues. + sasThreadParams.outAddr = outAddr; + sasThreadParams.inAddr = inAddr; + sasThreadParams.leftVol = leftVol; + sasThreadParams.rightVol = rightVol; + + // And now, notify. + sasWakeMutex.lock(); + sasThreadState = SasThreadState::QUEUED; + sasWake.notify_one(); + sasWakeMutex.unlock(); +} + void __SasInit() { sas = new SasInstance(); + + sasThreadState = SasThreadState::READY; + sasThread = new std::thread(__SasThread); } void __SasDoState(PointerWrap &p) { @@ -82,6 +157,16 @@ void __SasDoState(PointerWrap &p) { } void __SasShutdown() { + if (sasThreadState != SasThreadState::DISABLED) { + sasWakeMutex.lock(); + sasThreadState = SasThreadState::DISABLED; + sasWake.notify_one(); + sasWakeMutex.unlock(); + sasThread->join(); + delete sasThread; + sasThread = nullptr; + } + delete sas; sas = 0; } @@ -135,34 +220,33 @@ static u32 sceSasGetEndFlag(u32 core) { // Runs the mixer static u32 _sceSasCore(u32 core, u32 outAddr) { + PROFILE_THIS_SCOPE("mixer"); + if (!Memory::IsValidAddress(outAddr)) { - ERROR_LOG_REPORT(SCESAS, "sceSasCore(%08x, %08x): invalid address", core, outAddr); - return ERROR_SAS_INVALID_PARAMETER; + return hleReportError(SCESAS, ERROR_SAS_INVALID_PARAMETER, "invalid address"); } - DEBUG_LOG(SCESAS, "sceSasCore(%08x, %08x)", core, outAddr); - sas->Mix(outAddr); + __SasEnqueueMix(outAddr); // Actual delay time seems to between 240 and 1000 us, based on grain and possibly other factors. - return hleDelayResult(0, "sas core", 240); + return hleLogSuccessI(SCESAS, hleDelayResult(0, "sas core", 240)); } // Another way of running the mixer, the inoutAddr should be both input and output static u32 _sceSasCoreWithMix(u32 core, u32 inoutAddr, int leftVolume, int rightVolume) { + PROFILE_THIS_SCOPE("mixer"); + if (!Memory::IsValidAddress(inoutAddr)) { - ERROR_LOG_REPORT(SCESAS, "sceSasCoreWithMix(%08x, %08x, %i, %i): invalid address", core, inoutAddr, leftVolume, rightVolume); - return ERROR_SAS_INVALID_PARAMETER; + return hleReportError(SCESAS, ERROR_SAS_INVALID_PARAMETER, "invalid address"); } if (sas->outputMode == PSP_SAS_OUTPUTMODE_RAW) { - ERROR_LOG_REPORT(SCESAS, "sceSasCoreWithMix(%08x, %08x, %i, %i): unsupported outputMode", core, inoutAddr, leftVolume, rightVolume); - return 0x80000004; + return hleReportError(SCESAS, 0x80000004, "unsupported outputMode"); } - - DEBUG_LOG(SCESAS, "sceSasCoreWithMix(%08x, %08x, %i, %i)", core, inoutAddr, leftVolume, rightVolume); - sas->Mix(inoutAddr, inoutAddr, leftVolume, rightVolume); + + __SasEnqueueMix(inoutAddr, inoutAddr, leftVolume, rightVolume); // Actual delay time seems to between 240 and 1000 us, based on grain and possibly other factors. - return hleDelayResult(0, "sas core", 240); + return hleLogSuccessI(SCESAS, hleDelayResult(0, "sas core", 240)); } static u32 sceSasSetVoice(u32 core, int voiceNum, u32 vagAddr, int size, int loop) { diff --git a/Core/HW/SasAudio.cpp b/Core/HW/SasAudio.cpp index 8948d82775..1d383a369a 100644 --- a/Core/HW/SasAudio.cpp +++ b/Core/HW/SasAudio.cpp @@ -558,8 +558,6 @@ void SasInstance::MixVoice(SasVoice &voice) { } void SasInstance::Mix(u32 outAddr, u32 inAddr, int leftVol, int rightVol) { - PROFILE_THIS_SCOPE("mixer"); - int voicesPlayingCount = 0; for (int v = 0; v < PSP_SAS_VOICES_MAX; v++) { From 0394319e899d1cd4a309e41e7d737d5d68b38e95 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 15 Nov 2015 11:02:57 -0800 Subject: [PATCH 2/9] Sas: Drain thread on savestate. --- Core/HLE/sceSas.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Core/HLE/sceSas.cpp b/Core/HLE/sceSas.cpp index 2913f7a195..b97437daf4 100644 --- a/Core/HLE/sceSas.cpp +++ b/Core/HLE/sceSas.cpp @@ -153,6 +153,14 @@ void __SasDoState(PointerWrap &p) { if (!s) return; + if (sasThreadState == SasThreadState::QUEUED) { + // Wait for the queue to drain. Don't want to save the wrong stuff. + sasDoneMutex.lock(); + while (sasThreadState == SasThreadState::QUEUED) + sasDone.wait(sasDoneMutex); + sasDoneMutex.unlock(); + } + p.DoClass(sas); } From fc8acc781eeb0d5ee0cfc5ab4ce11ca860542790 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 15 Nov 2015 11:03:16 -0800 Subject: [PATCH 3/9] Sas: Add config for SAS thread. --- Core/Config.cpp | 1 + Core/Config.h | 3 ++- Core/HLE/sceSas.cpp | 9 +++++++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index 23311584d6..5f9d41e973 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -333,6 +333,7 @@ static ConfigSetting generalSettings[] = { static ConfigSetting cpuSettings[] = { ReportedConfigSetting("Jit", &g_Config.bJit, &DefaultJit, true, true), ReportedConfigSetting("SeparateCPUThread", &g_Config.bSeparateCPUThread, false, true, true), + ReportedConfigSetting("SeparateSASThread", &g_Config.bSeparateSASThread, true, true, true), ReportedConfigSetting("SeparateIOThread", &g_Config.bSeparateIOThread, true, true, true), ReportedConfigSetting("IOTimingMethod", &g_Config.iIOTimingMethod, IOTIMING_FAST, true, true), ConfigSetting("FastMemoryAccess", &g_Config.bFastMemory, true, true, true), diff --git a/Core/Config.h b/Core/Config.h index a1ba518d44..74fe1a43f7 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -119,8 +119,9 @@ public: // Definitely cannot be changed while game is running. bool bSeparateCPUThread; - int iIOTimingMethod; + bool bSeparateSASThread; bool bSeparateIOThread; + int iIOTimingMethod; int iLockedCPUSpeed; bool bAutoSaveSymbolMap; bool bCacheFullIsoInRam; diff --git a/Core/HLE/sceSas.cpp b/Core/HLE/sceSas.cpp index b97437daf4..75a3c5548a 100644 --- a/Core/HLE/sceSas.cpp +++ b/Core/HLE/sceSas.cpp @@ -34,6 +34,7 @@ #include "thread/thread.h" #include "thread/threadutil.h" #include "Common/Log.h" +#include "Core/Config.h" #include "Core/HLE/HLE.h" #include "Core/HLE/FunctionWrappers.h" #include "Core/MIPS/MIPS.h" @@ -144,8 +145,12 @@ static void __SasEnqueueMix(u32 outAddr, u32 inAddr = 0, int leftVol = 0, int ri void __SasInit() { sas = new SasInstance(); - sasThreadState = SasThreadState::READY; - sasThread = new std::thread(__SasThread); + if (g_Config.bSeparateSASThread) { + sasThreadState = SasThreadState::READY; + sasThread = new std::thread(__SasThread); + } else { + sasThreadState = SasThreadState::DISABLED; + } } void __SasDoState(PointerWrap &p) { From 5a6c8b513c9e3d97d75cff7edba537b9d7a2aafd Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 15 Nov 2015 11:24:09 -0800 Subject: [PATCH 4/9] Sas: Drain thread before reading/writing SAS state. This should be the safest option, but I'm pretty sure some operations give a BUSY operation or have different behavior, actually. --- Core/HLE/sceSas.cpp | 42 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/Core/HLE/sceSas.cpp b/Core/HLE/sceSas.cpp index 75a3c5548a..7bca18bdd8 100644 --- a/Core/HLE/sceSas.cpp +++ b/Core/HLE/sceSas.cpp @@ -113,6 +113,13 @@ int __SasThread() { return 0; } +static void __SasDrain() { + sasDoneMutex.lock(); + while (sasThreadState == SasThreadState::QUEUED) + sasDone.wait(sasDoneMutex); + sasDoneMutex.unlock(); +} + static void __SasEnqueueMix(u32 outAddr, u32 inAddr = 0, int leftVol = 0, int rightVol = 0) { if (sasThreadState == SasThreadState::DISABLED) { // No thread, call it immediately. @@ -122,10 +129,7 @@ static void __SasEnqueueMix(u32 outAddr, u32 inAddr = 0, int leftVol = 0, int ri if (sasThreadState == SasThreadState::QUEUED) { // Wait for the queue to drain. - sasDoneMutex.lock(); - while (sasThreadState == SasThreadState::QUEUED) - sasDone.wait(sasDoneMutex); - sasDoneMutex.unlock(); + __SasDrain(); } // We're safe to write, since it can't be processing now anymore. @@ -160,10 +164,7 @@ void __SasDoState(PointerWrap &p) { if (sasThreadState == SasThreadState::QUEUED) { // Wait for the queue to drain. Don't want to save the wrong stuff. - sasDoneMutex.lock(); - while (sasThreadState == SasThreadState::QUEUED) - sasDone.wait(sasDoneMutex); - sasDoneMutex.unlock(); + __SasDrain(); } p.DoClass(sas); @@ -222,6 +223,7 @@ static u32 sceSasInit(u32 core, u32 grainSize, u32 maxVoices, u32 outputMode, u3 static u32 sceSasGetEndFlag(u32 core) { u32 endFlag = 0; + __SasDrain(); for (int i = 0; i < sas->maxVoices; i++) { if (!sas->voices[i].playing) endFlag |= (1 << i); @@ -285,6 +287,7 @@ static u32 sceSasSetVoice(u32 core, int voiceNum, u32 vagAddr, int size, int loo return 0; } + __SasDrain(); SasVoice &v = sas->voices[voiceNum]; if (v.type == VOICETYPE_ATRAC3) { return hleLogError(SCESAS, ERROR_SAS_ATRAC3_ALREADY_SET, "voice is already ATRAC3"); @@ -328,6 +331,7 @@ static u32 sceSasSetVoicePCM(u32 core, int voiceNum, u32 pcmAddr, int size, int return 0; } + __SasDrain(); SasVoice &v = sas->voices[voiceNum]; if (v.type == VOICETYPE_ATRAC3) { return hleLogError(SCESAS, ERROR_SAS_ATRAC3_ALREADY_SET, "voice is already ATRAC3"); @@ -349,6 +353,7 @@ static u32 sceSasSetVoicePCM(u32 core, int voiceNum, u32 pcmAddr, int size, int static u32 sceSasGetPauseFlag(u32 core) { u32 pauseFlag = 0; + __SasDrain(); for (int i = 0; i < sas->maxVoices; i++) { if (sas->voices[i].paused) pauseFlag |= (1 << i); @@ -361,6 +366,7 @@ static u32 sceSasGetPauseFlag(u32 core) { static u32 sceSasSetPause(u32 core, u32 voicebit, int pause) { DEBUG_LOG(SCESAS, "sceSasSetPause(%08x, %08x, %i)", core, voicebit, pause); + __SasDrain(); for (int i = 0; voicebit != 0; i++, voicebit >>= 1) { if (i < PSP_SAS_VOICES_MAX && i >= 0) { if ((voicebit & 1) != 0) @@ -383,6 +389,7 @@ static u32 sceSasSetVolume(u32 core, int voiceNum, int leftVol, int rightVol, in if (overVolume) return ERROR_SAS_INVALID_VOLUME; + __SasDrain(); SasVoice &v = sas->voices[voiceNum]; v.volumeLeft = leftVol; v.volumeRight = rightVol; @@ -402,6 +409,7 @@ static u32 sceSasSetPitch(u32 core, int voiceNum, int pitch) { } DEBUG_LOG(SCESAS, "sceSasSetPitch(%08x, %i, %i)", core, voiceNum, pitch); + __SasDrain(); SasVoice &v = sas->voices[voiceNum]; v.pitch = pitch; v.ChangedParams(false); @@ -416,6 +424,7 @@ static u32 sceSasSetKeyOn(u32 core, int voiceNum) { return ERROR_SAS_INVALID_VOICE; } + __SasDrain(); if (sas->voices[voiceNum].paused || sas->voices[voiceNum].on) { return ERROR_SAS_VOICE_PAUSED; } @@ -433,6 +442,7 @@ static u32 sceSasSetKeyOff(u32 core, int voiceNum) { } else { DEBUG_LOG(SCESAS, "sceSasSetKeyOff(%08x, %i)", core, voiceNum); + __SasDrain(); if (sas->voices[voiceNum].paused || !sas->voices[voiceNum].on) { return ERROR_SAS_VOICE_PAUSED; } @@ -455,6 +465,7 @@ static u32 sceSasSetNoise(u32 core, int voiceNum, int freq) { DEBUG_LOG(SCESAS, "sceSasSetNoise(%08x, %i, %i)", core, voiceNum, freq); + __SasDrain(); SasVoice &v = sas->voices[voiceNum]; v.type = VOICETYPE_NOISE; v.noiseFreq = freq; @@ -469,6 +480,7 @@ static u32 sceSasSetSL(u32 core, int voiceNum, int level) { } DEBUG_LOG(SCESAS, "sceSasSetSL(%08x, %i, %08x)", core, voiceNum, level); + __SasDrain(); SasVoice &v = sas->voices[voiceNum]; v.envelope.sustainLevel = level; return 0; @@ -488,6 +500,7 @@ static u32 sceSasSetADSR(u32 core, int voiceNum, int flag, int a, int d, int s, DEBUG_LOG(SCESAS, "0=sceSasSetADSR(%08x, %i, %i, %08x, %08x, %08x, %08x)", core, voiceNum, flag, a, d, s, r); + __SasDrain(); SasVoice &v = sas->voices[voiceNum]; if ((flag & 0x1) != 0) v.envelope.attackRate = a; if ((flag & 0x2) != 0) v.envelope.decayRate = d; @@ -533,6 +546,7 @@ static u32 sceSasSetADSRMode(u32 core, int voiceNum, int flag, int a, int d, int } DEBUG_LOG(SCESAS, "sceSasSetADSRMode(%08x, %i, %i, %08x, %08x, %08x, %08x)", core, voiceNum, flag, a, d, s, r); + __SasDrain(); SasVoice &v = sas->voices[voiceNum]; if ((flag & 0x1) != 0) v.envelope.attackType = a; if ((flag & 0x2) != 0) v.envelope.decayType = d; @@ -555,6 +569,7 @@ static u32 sceSasSetSimpleADSR(u32 core, int voiceNum, u32 ADSREnv1, u32 ADSREnv DEBUG_LOG(SCESAS, "sasSetSimpleADSR(%08x, %i, %08x, %08x)", core, voiceNum, ADSREnv1, ADSREnv2); + __SasDrain(); SasVoice &v = sas->voices[voiceNum]; v.envelope.SetSimpleEnvelope(ADSREnv1 & 0xFFFF, ADSREnv2 & 0xFFFF); return 0; @@ -566,6 +581,7 @@ static u32 sceSasGetEnvelopeHeight(u32 core, int voiceNum) { return ERROR_SAS_INVALID_VOICE; } + __SasDrain(); SasVoice &v = sas->voices[voiceNum]; int height = v.envelope.GetHeight(); DEBUG_LOG(SCESAS, "%i = sceSasGetEnvelopeHeight(%08x, %i)", height, core, voiceNum); @@ -577,6 +593,7 @@ static u32 sceSasRevType(u32 core, int type) { return hleLogError(SCESAS, ERROR_SAS_REV_INVALID_TYPE, "invalid type"); } + __SasDrain(); sas->SetWaveformEffectType(type); return hleLogSuccessI(SCESAS, 0); } @@ -589,6 +606,7 @@ static u32 sceSasRevParam(u32 core, int delay, int feedback) { return hleLogError(SCESAS, ERROR_SAS_REV_INVALID_FEEDBACK, "invalid feedback value"); } + __SasDrain(); sas->waveformEffect.delay = delay; sas->waveformEffect.feedback = feedback; return hleLogSuccessI(SCESAS, 0); @@ -599,12 +617,14 @@ static u32 sceSasRevEVOL(u32 core, u32 lv, u32 rv) { return hleReportDebug(SCESAS, ERROR_SAS_REV_INVALID_VOLUME, "invalid volume"); } + __SasDrain(); sas->waveformEffect.leftVol = lv; sas->waveformEffect.rightVol = rv; return hleLogSuccessI(SCESAS, 0); } static u32 sceSasRevVON(u32 core, int dry, int wet) { + __SasDrain(); sas->waveformEffect.isDryOn = dry != 0; sas->waveformEffect.isWetOn = wet != 0; return hleLogSuccessI(SCESAS, 0); @@ -617,6 +637,7 @@ static u32 sceSasGetGrain(u32 core) { static u32 sceSasSetGrain(u32 core, int grain) { INFO_LOG(SCESAS, "sceSasSetGrain(%08x, %i)", core, grain); + __SasDrain(); sas->SetGrainSize(grain); return 0; } @@ -632,6 +653,7 @@ static u32 sceSasSetOutputMode(u32 core, u32 outputMode) { return ERROR_SAS_INVALID_OUTPUT_MODE; } DEBUG_LOG(SCESAS, "sceSasSetOutputMode(%08x, %i)", core, outputMode); + __SasDrain(); sas->outputMode = outputMode; return 0; @@ -644,6 +666,7 @@ static u32 sceSasGetAllEnvelopeHeights(u32 core, u32 heightsAddr) { return ERROR_SAS_INVALID_PARAMETER; } + __SasDrain(); for (int i = 0; i < PSP_SAS_VOICES_MAX; i++) { int voiceHeight = sas->voices[i].envelope.GetHeight(); Memory::Write_U32(voiceHeight, heightsAddr + i * 4); @@ -667,6 +690,7 @@ static u32 __sceSasSetVoiceATRAC3(u32 core, int voiceNum, u32 atrac3Context) { return hleLogWarning(SCESAS, ERROR_SAS_INVALID_VOICE, "invalid voicenum"); } + __SasDrain(); SasVoice &v = sas->voices[voiceNum]; if (v.type == VOICETYPE_ATRAC3) { return hleLogError(SCESAS, ERROR_SAS_ATRAC3_ALREADY_SET, "voice is already ATRAC3"); @@ -686,6 +710,7 @@ static u32 __sceSasConcatenateATRAC3(u32 core, int voiceNum, u32 atrac3DataAddr, } DEBUG_LOG_REPORT(SCESAS, "__sceSasConcatenateATRAC3(%08x, %i, %08x, %i)", core, voiceNum, atrac3DataAddr, atrac3DataLength); + __SasDrain(); SasVoice &v = sas->voices[voiceNum]; if (Memory::IsValidAddress(atrac3DataAddr)) v.atrac3.addStreamData(atrac3DataAddr, atrac3DataLength); @@ -697,6 +722,7 @@ static u32 __sceSasUnsetATRAC3(u32 core, int voiceNum) { return hleLogWarning(SCESAS, ERROR_SAS_INVALID_VOICE, "invalid voicenum"); } + __SasDrain(); SasVoice &v = sas->voices[voiceNum]; if (v.type != VOICETYPE_ATRAC3) { return hleLogError(SCESAS, ERROR_SAS_ATRAC3_NOT_SET, "voice is not ATRAC3"); From 9d64f9f9f699394f1760d42afa002e97a136933f Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 15 Nov 2015 11:24:56 -0800 Subject: [PATCH 5/9] Fix compile errors when profiler is enabled. --- UI/ProfilerDraw.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/UI/ProfilerDraw.cpp b/UI/ProfilerDraw.cpp index 14c72733d5..9b4855b722 100644 --- a/UI/ProfilerDraw.cpp +++ b/UI/ProfilerDraw.cpp @@ -15,9 +15,12 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include #include +#include "gfx_es2/draw_buffer.h" #include "ui/ui_context.h" +#include "ui/view.h" #include "profiler/profiler.h" static const uint32_t nice_colors[] = { From 75290cfd566532298cb337489e3925bed73cb57d Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 15 Nov 2015 11:27:40 -0800 Subject: [PATCH 6/9] Sas: Enable SAS thread on multicore only. --- Core/Config.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index 5f9d41e973..3b5607fd65 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -330,10 +330,14 @@ static ConfigSetting generalSettings[] = { ConfigSetting(false), }; +static bool DefaultSasThread() { + return cpu_info.num_cores > 1; +} + static ConfigSetting cpuSettings[] = { ReportedConfigSetting("Jit", &g_Config.bJit, &DefaultJit, true, true), ReportedConfigSetting("SeparateCPUThread", &g_Config.bSeparateCPUThread, false, true, true), - ReportedConfigSetting("SeparateSASThread", &g_Config.bSeparateSASThread, true, true, true), + ReportedConfigSetting("SeparateSASThread", &g_Config.bSeparateSASThread, &DefaultSasThread, true, true), ReportedConfigSetting("SeparateIOThread", &g_Config.bSeparateIOThread, true, true, true), ReportedConfigSetting("IOTimingMethod", &g_Config.iIOTimingMethod, IOTIMING_FAST, true, true), ConfigSetting("FastMemoryAccess", &g_Config.bFastMemory, true, true, true), From 23441d12fbfc781a4f8d26bf354a84bddf97fa22 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 25 Nov 2015 18:23:50 -0800 Subject: [PATCH 7/9] Sas: Estimate time taken for mixing. Tests seem to show it's not affected by mhz. --- Core/HLE/sceSas.cpp | 6 ++---- Core/HW/SasAudio.cpp | 14 ++++++++++++++ Core/HW/SasAudio.h | 1 + 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Core/HLE/sceSas.cpp b/Core/HLE/sceSas.cpp index 7bca18bdd8..b8f01530fc 100644 --- a/Core/HLE/sceSas.cpp +++ b/Core/HLE/sceSas.cpp @@ -243,8 +243,7 @@ static u32 _sceSasCore(u32 core, u32 outAddr) { __SasEnqueueMix(outAddr); - // Actual delay time seems to between 240 and 1000 us, based on grain and possibly other factors. - return hleLogSuccessI(SCESAS, hleDelayResult(0, "sas core", 240)); + return hleLogSuccessI(SCESAS, hleDelayResult(0, "sas core", sas->EstimateMixUs())); } // Another way of running the mixer, the inoutAddr should be both input and output @@ -260,8 +259,7 @@ static u32 _sceSasCoreWithMix(u32 core, u32 inoutAddr, int leftVolume, int right __SasEnqueueMix(inoutAddr, inoutAddr, leftVolume, rightVolume); - // Actual delay time seems to between 240 and 1000 us, based on grain and possibly other factors. - return hleLogSuccessI(SCESAS, hleDelayResult(0, "sas core", 240)); + return hleLogSuccessI(SCESAS, hleDelayResult(0, "sas core", sas->EstimateMixUs())); } static u32 sceSasSetVoice(u32 core, int voiceNum, u32 vagAddr, int size, int loop) { diff --git a/Core/HW/SasAudio.cpp b/Core/HW/SasAudio.cpp index 1d383a369a..78baaf39ee 100644 --- a/Core/HW/SasAudio.cpp +++ b/Core/HW/SasAudio.cpp @@ -408,6 +408,20 @@ void SasInstance::SetGrainSize(int newGrainSize) { resampleBuffer = new s16[grainSize * 4 + 3]; } +int SasInstance::EstimateMixUs() { + int voicesPlayingCount = 0; + + for (int v = 0; v < PSP_SAS_VOICES_MAX; v++) { + SasVoice &voice = voices[v]; + if (!voice.playing || voice.paused) + continue; + voicesPlayingCount++; + } + + // Each voice costs extra time, and each byte of grain costs extra time. + return 20 + voicesPlayingCount * 68 + (grainSize * 60) / 100; +} + void SasVoice::ReadSamples(s16 *output, int numSamples) { // Read N samples into the resample buffer. Could do either PCM or VAG here. switch (type) { diff --git a/Core/HW/SasAudio.h b/Core/HW/SasAudio.h index 07257027ff..fd4283e2f6 100644 --- a/Core/HW/SasAudio.h +++ b/Core/HW/SasAudio.h @@ -278,6 +278,7 @@ public: void ClearGrainSize(); void SetGrainSize(int newGrainSize); int GetGrainSize() const { return grainSize; } + int EstimateMixUs(); int maxVoices; int sampleRate; From 546590106fb639fbc16a105ebcd72c31bbb54147 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 25 Nov 2015 18:38:10 -0800 Subject: [PATCH 8/9] Sas: Fail to mix when dispatch disabled. --- Core/HLE/sceSas.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Core/HLE/sceSas.cpp b/Core/HLE/sceSas.cpp index b8f01530fc..c8071f86d7 100644 --- a/Core/HLE/sceSas.cpp +++ b/Core/HLE/sceSas.cpp @@ -44,6 +44,7 @@ #include "Core/HLE/sceSas.h" #include "Core/HLE/sceKernel.h" +#include "Core/HLE/sceKernelThread.h" enum { ERROR_SAS_INVALID_GRAIN = 0x80420001, @@ -233,6 +234,11 @@ static u32 sceSasGetEndFlag(u32 core) { return endFlag; } +static int delaySasResult(int result) { + const int usec = sas->EstimateMixUs(); + return hleDelayResult(result, "sas core", usec); +} + // Runs the mixer static u32 _sceSasCore(u32 core, u32 outAddr) { PROFILE_THIS_SCOPE("mixer"); @@ -240,10 +246,13 @@ static u32 _sceSasCore(u32 core, u32 outAddr) { if (!Memory::IsValidAddress(outAddr)) { return hleReportError(SCESAS, ERROR_SAS_INVALID_PARAMETER, "invalid address"); } + if (!__KernelIsDispatchEnabled()) { + return hleLogError(SCESAS, SCE_KERNEL_ERROR_CAN_NOT_WAIT, "dispatch disabled"); + } __SasEnqueueMix(outAddr); - return hleLogSuccessI(SCESAS, hleDelayResult(0, "sas core", sas->EstimateMixUs())); + return hleLogSuccessI(SCESAS, delaySasResult(0)); } // Another way of running the mixer, the inoutAddr should be both input and output @@ -256,10 +265,13 @@ static u32 _sceSasCoreWithMix(u32 core, u32 inoutAddr, int leftVolume, int right if (sas->outputMode == PSP_SAS_OUTPUTMODE_RAW) { return hleReportError(SCESAS, 0x80000004, "unsupported outputMode"); } + if (!__KernelIsDispatchEnabled()) { + return hleLogError(SCESAS, SCE_KERNEL_ERROR_CAN_NOT_WAIT, "dispatch disabled"); + } __SasEnqueueMix(inoutAddr, inoutAddr, leftVolume, rightVolume); - return hleLogSuccessI(SCESAS, hleDelayResult(0, "sas core", sas->EstimateMixUs())); + return hleLogSuccessI(SCESAS, delaySasResult(0)); } static u32 sceSasSetVoice(u32 core, int voiceNum, u32 vagAddr, int size, int loop) { From f759a2a3fdb54c1af003a6e6c570ba887d202ff7 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 25 Nov 2015 18:43:35 -0800 Subject: [PATCH 9/9] Sas: Sync result when mix calling thread returns. --- Core/HLE/sceSas.cpp | 65 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 11 deletions(-) diff --git a/Core/HLE/sceSas.cpp b/Core/HLE/sceSas.cpp index c8071f86d7..5c4c71a92b 100644 --- a/Core/HLE/sceSas.cpp +++ b/Core/HLE/sceSas.cpp @@ -35,6 +35,7 @@ #include "thread/threadutil.h" #include "Common/Log.h" #include "Core/Config.h" +#include "Core/CoreTiming.h" #include "Core/HLE/HLE.h" #include "Core/HLE/FunctionWrappers.h" #include "Core/MIPS/MIPS.h" @@ -95,6 +96,7 @@ static condition_variable sasWake; static condition_variable sasDone; static volatile int sasThreadState = SasThreadState::DISABLED; static SasThreadParams sasThreadParams; +static int sasMixEvent = -1; int __SasThread() { setCurrentThreadName("SAS"); @@ -147,9 +149,42 @@ static void __SasEnqueueMix(u32 outAddr, u32 inAddr = 0, int leftVol = 0, int ri sasWakeMutex.unlock(); } +static void __SasDisableThread() { + if (sasThreadState != SasThreadState::DISABLED) { + sasWakeMutex.lock(); + sasThreadState = SasThreadState::DISABLED; + sasWake.notify_one(); + sasWakeMutex.unlock(); + sasThread->join(); + delete sasThread; + sasThread = nullptr; + } +} + +static void sasMixFinish(u64 userdata, int cycleslate) { + PROFILE_THIS_SCOPE("mixer"); + + u32 error; + SceUID threadID = (SceUID)userdata; + SceUID verify = __KernelGetWaitID(threadID, WAITTYPE_HLEDELAY, error); + u64 result = __KernelGetWaitValue(threadID, error); + + if (error == 0 && verify == 1) { + // Wait until it's actually complete before waking the thread. + __SasDrain(); + + __KernelResumeThreadFromWait(threadID, result); + __KernelReSchedule("woke from sas mix"); + } else { + WARN_LOG(HLE, "Someone else woke up SAS-blocked thread?"); + } +} + void __SasInit() { sas = new SasInstance(); + sasMixEvent = CoreTiming::RegisterEvent("SasMix", sasMixFinish); + if (g_Config.bSeparateSASThread) { sasThreadState = SasThreadState::READY; sasThread = new std::thread(__SasThread); @@ -159,7 +194,7 @@ void __SasInit() { } void __SasDoState(PointerWrap &p) { - auto s = p.Section("sceSas", 1); + auto s = p.Section("sceSas", 1, 2); if (!s) return; @@ -169,18 +204,18 @@ void __SasDoState(PointerWrap &p) { } p.DoClass(sas); + + if (s >= 2) { + p.Do(sasMixEvent); + CoreTiming::RestoreRegisterEvent(sasMixEvent, "SasMix", sasMixFinish); + } else { + sasMixEvent = -1; + __SasDisableThread(); + } } void __SasShutdown() { - if (sasThreadState != SasThreadState::DISABLED) { - sasWakeMutex.lock(); - sasThreadState = SasThreadState::DISABLED; - sasWake.notify_one(); - sasWakeMutex.unlock(); - sasThread->join(); - delete sasThread; - sasThread = nullptr; - } + __SasDisableThread(); delete sas; sas = 0; @@ -236,7 +271,15 @@ static u32 sceSasGetEndFlag(u32 core) { static int delaySasResult(int result) { const int usec = sas->EstimateMixUs(); - return hleDelayResult(result, "sas core", usec); + + // No event, fall back. + if (sasMixEvent == -1) { + return hleDelayResult(result, "sas core", usec); + } + + CoreTiming::ScheduleEvent(usToCycles(usec), sasMixEvent, __KernelGetCurThread()); + __KernelWaitCurThread(WAITTYPE_HLEDELAY, 1, result, 0, false, "sas core"); + return result; } // Runs the mixer