Add audio debug stats dev tool. Increase audio buffer low watermark a bit, hopefully helping #7370.

This commit is contained in:
Henrik Rydgard 2015-01-29 12:55:49 +01:00
parent f3c3a2ddb4
commit 86cee103f3
9 changed files with 141 additions and 36 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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();

View File

@ -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);
}

View File

@ -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;

View File

@ -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);

View File

@ -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 {

View File

@ -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();

2
native

@ -1 +1 @@
Subproject commit 813c12e40fa241c3097b541de4cb68f16a45af53
Subproject commit 1467753c35eaf57be5610d756a3071e4badc1b69