From 86cee103f332e0539085d5f170fe5e6b4f342986 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Thu, 29 Jan 2015 12:55:49 +0100 Subject: [PATCH] Add audio debug stats dev tool. Increase audio buffer low watermark a bit, hopefully helping #7370. --- Core/Config.h | 2 + Core/HLE/__sceAudio.cpp | 7 ++++ Core/HLE/__sceAudio.h | 11 +++++ Core/HW/StereoResampler.cpp | 45 ++++++++++++++++---- Core/HW/StereoResampler.h | 17 +++++++- UI/DevScreens.cpp | 8 ++++ UI/DevScreens.h | 1 + UI/EmuScreen.cpp | 84 +++++++++++++++++++++++++------------ native | 2 +- 9 files changed, 141 insertions(+), 36 deletions(-) diff --git a/Core/Config.h b/Core/Config.h index 32eb5731b9..107567a821 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -206,7 +206,9 @@ public: // UI bool bShowDebuggerOnLoad; int iShowFPSCounter; + bool bShowDebugStats; + bool bShowAudioDebug; //Analog stick tilting //the base x and y tilt. this inclination is treated as (0,0) and the tilt input diff --git a/Core/HLE/__sceAudio.cpp b/Core/HLE/__sceAudio.cpp index 4ce346f601..cb0e7a32fb 100644 --- a/Core/HLE/__sceAudio.cpp +++ b/Core/HLE/__sceAudio.cpp @@ -40,6 +40,7 @@ #include "Core/Util/AudioFormat.h" StereoResampler resampler; +AudioDebugStats g_AudioDebugStats; // Should be used to lock anything related to the outAudioQueue. // atomic locks are used on the lock. TODO: make this lock-free @@ -93,6 +94,7 @@ static void __AudioCPUMHzChange() { void __AudioInit() { + memset(&g_AudioDebugStats, 0, sizeof(g_AudioDebugStats)); mixFrequency = 44100; switch (g_Config.iAudioLatency) { @@ -371,3 +373,8 @@ int __AudioMix(short *outstereo, int numFrames, int sampleRate) { resampler.Mix(outstereo, numFrames, false, sampleRate); return numFrames; } + +const AudioDebugStats *__AudioGetDebugStats() { + resampler.GetAudioDebugStats(&g_AudioDebugStats); + return &g_AudioDebugStats; +} \ No newline at end of file diff --git a/Core/HLE/__sceAudio.h b/Core/HLE/__sceAudio.h index 9bbd9d9090..808929bb8b 100644 --- a/Core/HLE/__sceAudio.h +++ b/Core/HLE/__sceAudio.h @@ -19,6 +19,16 @@ #include "sceAudio.h" +struct AudioDebugStats { + int buffered; + int watermark; + int bufsize; + int underrunCount; + int overrunCount; + int instantSampleRate; + int lastPushSize; +}; + // Easy interface for sceAudio to write to, to keep the complexity in check. void __AudioInit(); @@ -33,3 +43,4 @@ void __AudioWakeThreads(AudioChannel &chan, int result, int step); void __AudioWakeThreads(AudioChannel &chan, int result); int __AudioMix(short *outstereo, int numSamples, int sampleRate); +const AudioDebugStats *__AudioGetDebugStats(); \ No newline at end of file diff --git a/Core/HW/StereoResampler.cpp b/Core/HW/StereoResampler.cpp index 7edf2a0372..5a9cd33312 100644 --- a/Core/HW/StereoResampler.cpp +++ b/Core/HW/StereoResampler.cpp @@ -25,6 +25,8 @@ #include "Common/MathUtil.h" #include "Common/Atomics.h" #include "Core/HW/StereoResampler.h" +#include "Core/HLE/__sceAudio.h" +#include "Core/System.h" #include "Globals.h" #ifdef _M_SSE @@ -94,7 +96,7 @@ unsigned int StereoResampler::MixerFifo::Mix(short* samples, unsigned int numSam if (offset > MAX_FREQ_SHIFT) offset = MAX_FREQ_SHIFT; if (offset < -MAX_FREQ_SHIFT) offset = -MAX_FREQ_SHIFT; - float aid_sample_rate = m_input_sample_rate + offset; + aid_sample_rate_ = m_input_sample_rate + offset; /* Hm? u32 framelimit = SConfig::GetInstance().m_Framelimit; @@ -102,7 +104,7 @@ unsigned int StereoResampler::MixerFifo::Mix(short* samples, unsigned int numSam aid_sample_rate = aid_sample_rate * (framelimit - 1) * 5 / 59.994; }*/ - const u32 ratio = (u32)(65536.0f * aid_sample_rate / (float)sample_rate); + const u32 ratio = (u32)(65536.0f * aid_sample_rate_ / (float)sample_rate); // TODO: consider a higher-quality resampling algorithm. // TODO: Add a fast path for 1:1. @@ -114,8 +116,8 @@ unsigned int StereoResampler::MixerFifo::Mix(short* samples, unsigned int numSam s16 r2 = m_buffer[(indexR2 + 1) & INDEX_MASK]; //next int sampleL = ((l1 << 16) + (l2 - l1) * (u16)m_frac) >> 16; int sampleR = ((r1 << 16) + (r2 - r1) * (u16)m_frac) >> 16; - samples[currentSample] = clamp_s16(sampleL); // Do we even need to clamp after interpolation? - samples[currentSample + 1] = clamp_s16(sampleR); + samples[currentSample] = sampleL; + samples[currentSample + 1] = sampleR; m_frac += ratio; indexR += 2 * (u16)(m_frac >> 16); m_frac &= 0xffff; @@ -123,6 +125,9 @@ unsigned int StereoResampler::MixerFifo::Mix(short* samples, unsigned int numSam int realSamples = currentSample; + if (currentSample < numSamples * 2) + underrunCount_++; + // Padding with the last value to reduce clicking short s[2]; s[0] = clamp_s16(m_buffer[(indexR - 1) & INDEX_MASK]); @@ -138,6 +143,7 @@ unsigned int StereoResampler::MixerFifo::Mix(short* samples, unsigned int numSam //if (realSamples != numSamples * 2) { // ILOG("Underrun! %i / %i", realSamples / 2, numSamples); //} + lastBufSize_ = (m_indexW - m_indexR) & INDEX_MASK; return realSamples / 2; } @@ -155,14 +161,20 @@ void StereoResampler::MixerFifo::PushSamples(const s32 *samples, unsigned int nu // needs to get updates to not deadlock. u32 indexW = Common::AtomicLoad(m_indexW); + u32 cap = MAX_SAMPLES * 2; + // If unthottling, no need to fill up the entire buffer, just screws up timing after releasing unthrottle. + if (PSP_CoreParameter().unthrottle) + cap = LOW_WATERMARK * 2; + // Check if we have enough free space // indexW == m_indexR results in empty buffer, so indexR must always be smaller than indexW - if (num_samples * 2 + ((indexW - Common::AtomicLoad(m_indexR)) & INDEX_MASK) >= MAX_SAMPLES * 2) + if (num_samples * 2 + ((indexW - Common::AtomicLoad(m_indexR)) & INDEX_MASK) >= cap) { + if (!PSP_CoreParameter().unthrottle) + overrunCount_++; + // TODO: "Timestretch" by doing a windowed overlap with existing buffer content? return; + } - // AyuanX: Actual re-sampling work has been moved to sound thread - // to alleviate the workload on main thread - // and we simply store raw data here to make fast mem copy int over_bytes = num_samples * 4 - (MAX_SAMPLES * 2 - (indexW & INDEX_MASK)) * sizeof(short); if (over_bytes > 0) { ClampBufferToS16(&m_buffer[indexW & INDEX_MASK], samples, (num_samples * 4 - over_bytes) / 2); @@ -172,6 +184,19 @@ void StereoResampler::MixerFifo::PushSamples(const s32 *samples, unsigned int nu } Common::AtomicAdd(m_indexW, num_samples * 2); + lastPushSize_ = num_samples; +} + +void StereoResampler::MixerFifo::GetAudioDebugStats(AudioDebugStats *stats) { + stats->buffered = lastBufSize_; + stats->underrunCount += underrunCount_; + underrunCount_ = 0; + stats->overrunCount += overrunCount_; + overrunCount_ = 0; + stats->watermark = LOW_WATERMARK; + stats->bufsize = MAX_SAMPLES * 2; + stats->instantSampleRate = aid_sample_rate_; + stats->lastPushSize = lastPushSize_; } void StereoResampler::PushSamples(const int *samples, unsigned int num_samples) { @@ -187,3 +212,7 @@ void StereoResampler::DoState(PointerWrap &p) { if (!s) return; } + +void StereoResampler::GetAudioDebugStats(AudioDebugStats *stats) { + m_dma_mixer.GetAudioDebugStats(stats); +} diff --git a/Core/HW/StereoResampler.h b/Core/HW/StereoResampler.h index 504eed1583..77b55c7e28 100644 --- a/Core/HW/StereoResampler.h +++ b/Core/HW/StereoResampler.h @@ -26,12 +26,14 @@ #include "Common/ChunkFile.h" #include "Common/CommonTypes.h" +struct AudioDebugStats; + // 16 bit Stereo #define MAX_SAMPLES (2*(1024 * 2)) // 2*64ms - had to double it for nVidia Shield which has huge buffers #define INDEX_MASK (MAX_SAMPLES * 2 - 1) -#define LOW_WATERMARK 1280 // 40 ms +#define LOW_WATERMARK 1680 // 40 ms #define MAX_FREQ_SHIFT 200 // per 32000 Hz #define CONTROL_FACTOR 0.2f // in freq_shift per fifo size offset #define CONTROL_AVG 32 @@ -56,7 +58,10 @@ public: void DoState(PointerWrap &p); + void GetAudioDebugStats(AudioDebugStats *stats); + protected: + // TODO: Unlike Dolphin we only mix one stream so this inner class can be merged into the outer one. class MixerFifo { public: MixerFifo(StereoResampler *mixer, unsigned sample_rate) @@ -66,6 +71,10 @@ protected: , m_indexR(0) , m_numLeftI(0.0f) , m_frac(0) + , underrunCount_(0) + , overrunCount_(0) + , aid_sample_rate_(0.0f) + , lastBufSize_(0) { memset(m_buffer, 0, sizeof(m_buffer)); } @@ -73,6 +82,7 @@ protected: unsigned int Mix(short* samples, unsigned int numSamples, bool consider_framelimit, int sample_rate); void SetInputSampleRate(unsigned int rate); void Clear(); + void GetAudioDebugStats(AudioDebugStats *stats); private: StereoResampler *m_mixer; @@ -82,6 +92,11 @@ protected: volatile u32 m_indexR; float m_numLeftI; u32 m_frac; + int underrunCount_; + int overrunCount_; + float aid_sample_rate_; + int lastBufSize_; + int lastPushSize_; }; MixerFifo m_dma_mixer; diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index f3132bf0c0..cd98a3c740 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -63,6 +63,7 @@ void DevMenu::CreatePopupContents(UI::ViewGroup *parent) { parent->Add(new Choice(de->T("Jit Compare")))->OnClick.Handle(this, &DevMenu::OnJitCompare); parent->Add(new Choice(de->T("Toggle Freeze")))->OnClick.Handle(this, &DevMenu::OnFreezeFrame); parent->Add(new Choice(de->T("Dump Frame GPU Commands")))->OnClick.Handle(this, &DevMenu::OnDumpFrame); + parent->Add(new Choice(de->T("Toggle Audio Debug")))->OnClick.Handle(this, &DevMenu::OnToggleAudioDebug); RingbufferLogListener *ring = LogManager::GetInstance()->GetRingbufferListener(); if (ring) { @@ -70,6 +71,12 @@ void DevMenu::CreatePopupContents(UI::ViewGroup *parent) { } } +UI::EventReturn DevMenu::OnToggleAudioDebug(UI::EventParams &e) { + g_Config.bShowAudioDebug = !g_Config.bShowAudioDebug; + return UI::EVENT_DONE; +} + + UI::EventReturn DevMenu::OnLogView(UI::EventParams &e) { UpdateUIState(UISTATE_PAUSEMENU); screenManager()->push(new LogScreen()); @@ -109,6 +116,7 @@ UI::EventReturn DevMenu::OnDumpFrame(UI::EventParams &e) { } void DevMenu::dialogFinished(const Screen *dialog, DialogResult result) { + UpdateUIState(UISTATE_INGAME); // Close when a subscreen got closed. // TODO: a bug in screenmanager causes this not to work here. // screenManager()->finishDialog(this, DR_OK); diff --git a/UI/DevScreens.h b/UI/DevScreens.h index d2bcc822cd..2ef2f3c33b 100644 --- a/UI/DevScreens.h +++ b/UI/DevScreens.h @@ -42,6 +42,7 @@ protected: UI::EventReturn OnFreezeFrame(UI::EventParams &e); UI::EventReturn OnDumpFrame(UI::EventParams &e); UI::EventReturn OnDeveloperTools(UI::EventParams &e); + UI::EventReturn OnToggleAudioDebug(UI::EventParams &e); }; class LogConfigScreen : public UIDialogScreenWithBackground { diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 224e38dd7e..bb2905eed4 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -47,6 +47,7 @@ #include "Core/Debugger/SymbolMap.h" #include "Core/SaveState.h" #include "Core/MIPS/MIPS.h" +#include "Core/HLE/__sceAudio.h" #include "UI/ui_atlas.h" #include "UI/OnScreenDisplay.h" @@ -681,6 +682,56 @@ void EmuScreen::checkPowerDown() { } } +static void DrawDebugStats(DrawBuffer *draw2d) { + char statbuf[4096] = { 0 }; + __DisplayGetDebugStats(statbuf, sizeof(statbuf)); + draw2d->SetFontScale(.7f, .7f); + draw2d->DrawText(UBUNTU24, statbuf, 11, 31, 0xc0000000, FLAG_DYNAMIC_ASCII); + draw2d->DrawText(UBUNTU24, statbuf, 10, 30, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); + draw2d->SetFontScale(1.0f, 1.0f); +} + +static void DrawAudioDebugStats(DrawBuffer *draw2d) { + char statbuf[1024] = { 0 }; + const AudioDebugStats *stats = __AudioGetDebugStats(); + snprintf(statbuf, sizeof(statbuf), + "Audio buffer: %d/%d (low watermark: %d)\n" + "Underruns: %d\n" + "Overruns: %d\n" + "Sample rate: %d\n" + "Push size: %d\n", + stats->buffered, stats->bufsize, stats->watermark, + stats->underrunCount, + stats->overrunCount, + stats->instantSampleRate, + stats->lastPushSize); + draw2d->SetFontScale(0.7f, 0.7f); + draw2d->DrawText(UBUNTU24, statbuf, 11, 31, 0xc0000000, FLAG_DYNAMIC_ASCII); + draw2d->DrawText(UBUNTU24, statbuf, 10, 30, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); + draw2d->SetFontScale(1.0f, 1.0f); +} + +static void DrawFPS(DrawBuffer *draw2d, const Bounds &bounds) { + float vps, fps, actual_fps; + __DisplayGetFPS(&vps, &fps, &actual_fps); + char fpsbuf[256]; + switch (g_Config.iShowFPSCounter) { + case 1: + snprintf(fpsbuf, sizeof(fpsbuf), "Speed: %0.1f%%", vps / (59.94f / 100.0f)); break; + case 2: + snprintf(fpsbuf, sizeof(fpsbuf), "FPS: %0.1f", actual_fps); break; + case 3: + snprintf(fpsbuf, sizeof(fpsbuf), "%0.0f/%0.0f (%0.1f%%)", actual_fps, fps, vps / (59.94f / 100.0f)); break; + default: + return; + } + + draw2d->SetFontScale(0.7f, 0.7f); + draw2d->DrawText(UBUNTU24, fpsbuf, bounds.x2() - 8, 12, 0xc0000000, ALIGN_TOPRIGHT | FLAG_DYNAMIC_ASCII); + draw2d->DrawText(UBUNTU24, fpsbuf, bounds.x2() - 10, 10, 0xFF3fFF3f, ALIGN_TOPRIGHT | FLAG_DYNAMIC_ASCII); + draw2d->SetFontScale(1.0f, 1.0f); +} + void EmuScreen::render() { if (invalid_) { // It's possible this might be set outside PSP_RunLoopFor(). @@ -725,7 +776,7 @@ void EmuScreen::render() { if (useBufferedRendering && g_Config.iGPUBackend == GPU_BACKEND_OPENGL) fbo_unbind(); - if (!osm.IsEmpty() || g_Config.bShowDebugStats || g_Config.iShowFPSCounter || g_Config.bShowTouchControls || g_Config.bShowDeveloperMenu) { + if (!osm.IsEmpty() || g_Config.bShowDebugStats || g_Config.iShowFPSCounter || g_Config.bShowTouchControls || g_Config.bShowDeveloperMenu || g_Config.bShowAudioDebug) { Thin3DContext *thin3d = screenManager()->getThin3DContext(); @@ -750,34 +801,15 @@ void EmuScreen::render() { } if (g_Config.bShowDebugStats) { - char statbuf[4096] = {0}; - __DisplayGetDebugStats(statbuf, sizeof(statbuf)); - draw2d->SetFontScale(.7f, .7f); - draw2d->DrawText(UBUNTU24, statbuf, 11, 11, 0xc0000000, FLAG_DYNAMIC_ASCII); - draw2d->DrawText(UBUNTU24, statbuf, 10, 10, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); - draw2d->SetFontScale(1.0f, 1.0f); + DrawDebugStats(draw2d); + } + + if (g_Config.bShowAudioDebug) { + DrawAudioDebugStats(draw2d); } if (g_Config.iShowFPSCounter) { - float vps, fps, actual_fps; - __DisplayGetFPS(&vps, &fps, &actual_fps); - char fpsbuf[256]; - switch (g_Config.iShowFPSCounter) { - case 1: - snprintf(fpsbuf, sizeof(fpsbuf), "Speed: %0.1f%%", vps / (59.94f / 100.0f)); break; - case 2: - snprintf(fpsbuf, sizeof(fpsbuf), "FPS: %0.1f", actual_fps); break; - case 3: - snprintf(fpsbuf, sizeof(fpsbuf), "%0.0f/%0.0f (%0.1f%%)", actual_fps, fps, vps / (59.94f / 100.0f)); break; - default: - return; - } - - const Bounds &bounds = screenManager()->getUIContext()->GetBounds(); - draw2d->SetFontScale(0.7f, 0.7f); - draw2d->DrawText(UBUNTU24, fpsbuf, bounds.x2() - 8, 12, 0xc0000000, ALIGN_TOPRIGHT | FLAG_DYNAMIC_ASCII); - draw2d->DrawText(UBUNTU24, fpsbuf, bounds.x2() - 10, 10, 0xFF3fFF3f, ALIGN_TOPRIGHT | FLAG_DYNAMIC_ASCII); - draw2d->SetFontScale(1.0f, 1.0f); + DrawFPS(draw2d, screenManager()->getUIContext()->GetBounds()); } screenManager()->getUIContext()->End(); diff --git a/native b/native index 813c12e40f..1467753c35 160000 --- a/native +++ b/native @@ -1 +1 @@ -Subproject commit 813c12e40fa241c3097b541de4cb68f16a45af53 +Subproject commit 1467753c35eaf57be5610d756a3071e4badc1b69