mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-07 14:45:41 +12:00
Better DSP scheduling
This commit is contained in:
parent
f58354af06
commit
33eb096ef8
8 changed files with 44 additions and 27 deletions
|
@ -7,20 +7,30 @@
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
#include "logger.hpp"
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
#include "scheduler.hpp"
|
||||||
|
|
||||||
// The DSP core must have access to the DSP service to be able to trigger interrupts properly
|
// The DSP core must have access to the DSP service to be able to trigger interrupts properly
|
||||||
class DSPService;
|
class DSPService;
|
||||||
|
|
||||||
namespace Audio {
|
namespace Audio {
|
||||||
|
// There are 160 stereo samples in 1 audio frame, so 320 samples total
|
||||||
|
static constexpr u64 samplesInFrame = 160;
|
||||||
|
// 1 frame = 4096 DSP cycles = 8192 ARM11 cycles
|
||||||
|
static constexpr u64 cyclesPerFrame = samplesInFrame * 8192;
|
||||||
|
// For LLE DSP cores, we run the DSP for N cycles at a time, every N*2 arm11 cycles since the ARM11 runs twice as fast
|
||||||
|
static constexpr u64 lleSlice = 16384;
|
||||||
|
|
||||||
class DSPCore {
|
class DSPCore {
|
||||||
protected:
|
protected:
|
||||||
Memory& mem;
|
Memory& mem;
|
||||||
|
Scheduler& scheduler;
|
||||||
DSPService& dspService;
|
DSPService& dspService;
|
||||||
|
|
||||||
MAKE_LOG_FUNCTION(log, dspLogger)
|
MAKE_LOG_FUNCTION(log, dspLogger)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum class Type { Null, Teakra };
|
enum class Type { Null, Teakra };
|
||||||
DSPCore(Memory& mem, DSPService& dspService) : mem(mem), dspService(dspService) {}
|
DSPCore(Memory& mem, Scheduler& scheduler, DSPService& dspService) : mem(mem), scheduler(scheduler), dspService(dspService) {}
|
||||||
|
|
||||||
virtual void reset() = 0;
|
virtual void reset() = 0;
|
||||||
virtual void runAudioFrame() = 0;
|
virtual void runAudioFrame() = 0;
|
||||||
|
@ -36,5 +46,5 @@ namespace Audio {
|
||||||
virtual void setSemaphoreMask(u16 value) = 0;
|
virtual void setSemaphoreMask(u16 value) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<DSPCore> makeDSPCore(DSPCore::Type type, Memory& mem, DSPService& dspService);
|
std::unique_ptr<DSPCore> makeDSPCore(DSPCore::Type type, Memory& mem, Scheduler& scheduler, DSPService& dspService);
|
||||||
} // namespace Audio
|
} // namespace Audio
|
|
@ -20,7 +20,7 @@ namespace Audio {
|
||||||
void resetAudioPipe();
|
void resetAudioPipe();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
NullDSP(Memory& mem, DSPService& dspService) : DSPCore(mem, dspService) {}
|
NullDSP(Memory& mem, Scheduler& scheduler, DSPService& dspService) : DSPCore(mem, scheduler, dspService) {}
|
||||||
|
|
||||||
void reset() override;
|
void reset() override;
|
||||||
void runAudioFrame() override {}
|
void runAudioFrame() override {}
|
||||||
|
|
|
@ -65,14 +65,22 @@ namespace Audio {
|
||||||
bool signalledData;
|
bool signalledData;
|
||||||
bool signalledSemaphore;
|
bool signalledSemaphore;
|
||||||
|
|
||||||
|
// Run 1 slice of DSP instructions
|
||||||
|
void runSlice() {
|
||||||
|
if (running) {
|
||||||
|
teakra.Run(Audio::lleSlice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TeakraDSP(Memory& mem, DSPService& dspService);
|
TeakraDSP(Memory& mem, Scheduler& scheduler, DSPService& dspService);
|
||||||
|
|
||||||
void reset() override;
|
void reset() override;
|
||||||
|
|
||||||
|
// Run 1 slice of DSP instructions and schedule the next audio frame
|
||||||
void runAudioFrame() override {
|
void runAudioFrame() override {
|
||||||
if (running) {
|
runSlice();
|
||||||
teakra.Run(16384);
|
scheduler.addEvent(Scheduler::EventType::RunDSP, scheduler.currentTimestamp + Audio::lleSlice * 2);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u8* getDspMemory() override { return teakra.GetDspMemory().data(); }
|
u8* getDspMemory() override { return teakra.GetDspMemory().data(); }
|
||||||
|
|
|
@ -50,7 +50,6 @@ struct Scheduler {
|
||||||
// Clear any pending events
|
// Clear any pending events
|
||||||
events.clear();
|
events.clear();
|
||||||
addEvent(Scheduler::EventType::VBlank, arm11Clock / 60);
|
addEvent(Scheduler::EventType::VBlank, arm11Clock / 60);
|
||||||
addEvent(Scheduler::EventType::RunDSP, 16384 * 2);
|
|
||||||
|
|
||||||
// Add a dummy event to always keep the scheduler non-empty
|
// Add a dummy event to always keep the scheduler non-empty
|
||||||
addEvent(EventType::Panic, std::numeric_limits<u64>::max());
|
addEvent(EventType::Panic, std::numeric_limits<u64>::max());
|
||||||
|
|
|
@ -3,16 +3,16 @@
|
||||||
#include "audio/null_core.hpp"
|
#include "audio/null_core.hpp"
|
||||||
#include "audio/teakra_core.hpp"
|
#include "audio/teakra_core.hpp"
|
||||||
|
|
||||||
std::unique_ptr<Audio::DSPCore> Audio::makeDSPCore(DSPCore::Type type, Memory& mem, DSPService& dspService) {
|
std::unique_ptr<Audio::DSPCore> Audio::makeDSPCore(DSPCore::Type type, Memory& mem, Scheduler& scheduler, DSPService& dspService) {
|
||||||
std::unique_ptr<DSPCore> core;
|
std::unique_ptr<DSPCore> core;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case DSPCore::Type::Null: core = std::make_unique<NullDSP>(mem, dspService); break;
|
case DSPCore::Type::Null: core = std::make_unique<NullDSP>(mem, scheduler, dspService); break;
|
||||||
case DSPCore::Type::Teakra: core = std::make_unique<TeakraDSP>(mem, dspService); break;
|
case DSPCore::Type::Teakra: core = std::make_unique<TeakraDSP>(mem, scheduler, dspService); break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Helpers::warn("Invalid DSP core selected!");
|
Helpers::warn("Invalid DSP core selected!");
|
||||||
core = std::make_unique<NullDSP>(mem, dspService);
|
core = std::make_unique<NullDSP>(mem, scheduler, dspService);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,8 @@ struct Dsp1 {
|
||||||
Segment segments[10];
|
Segment segments[10];
|
||||||
};
|
};
|
||||||
|
|
||||||
TeakraDSP::TeakraDSP(Memory& mem, DSPService& dspService) : DSPCore(mem, dspService), pipeBaseAddr(0), running(false) {
|
TeakraDSP::TeakraDSP(Memory& mem, Scheduler& scheduler, DSPService& dspService)
|
||||||
|
: DSPCore(mem, scheduler, dspService), pipeBaseAddr(0), running(false) {
|
||||||
// Set up callbacks for Teakra
|
// Set up callbacks for Teakra
|
||||||
Teakra::AHBMCallback ahbm;
|
Teakra::AHBMCallback ahbm;
|
||||||
|
|
||||||
|
@ -117,7 +118,6 @@ void TeakraDSP::reset() {
|
||||||
|
|
||||||
// https://github.com/citra-emu/citra/blob/master/src/audio_core/lle/lle.cpp
|
// https://github.com/citra-emu/citra/blob/master/src/audio_core/lle/lle.cpp
|
||||||
void TeakraDSP::writeProcessPipe(u32 channel, u32 size, u32 buffer) {
|
void TeakraDSP::writeProcessPipe(u32 channel, u32 size, u32 buffer) {
|
||||||
Helpers::warn("Teakra: Write process pipe");
|
|
||||||
size &= 0xffff;
|
size &= 0xffff;
|
||||||
|
|
||||||
PipeStatus status = getPipeStatus(channel, PipeDirection::CPUtoDSP);
|
PipeStatus status = getPipeStatus(channel, PipeDirection::CPUtoDSP);
|
||||||
|
@ -167,7 +167,7 @@ void TeakraDSP::writeProcessPipe(u32 channel, u32 size, u32 buffer) {
|
||||||
if (needUpdate) {
|
if (needUpdate) {
|
||||||
updatePipeStatus(status);
|
updatePipeStatus(status);
|
||||||
while (!teakra.SendDataIsEmpty(2)) {
|
while (!teakra.SendDataIsEmpty(2)) {
|
||||||
runAudioFrame();
|
runSlice();
|
||||||
}
|
}
|
||||||
|
|
||||||
teakra.SendData(2, status.slot);
|
teakra.SendData(2, status.slot);
|
||||||
|
@ -175,7 +175,6 @@ void TeakraDSP::writeProcessPipe(u32 channel, u32 size, u32 buffer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<u8> TeakraDSP::readPipe(u32 channel, u32 peer, u32 size, u32 buffer) {
|
std::vector<u8> TeakraDSP::readPipe(u32 channel, u32 peer, u32 size, u32 buffer) {
|
||||||
Helpers::warn("Teakra: Read pipe");
|
|
||||||
size &= 0xffff;
|
size &= 0xffff;
|
||||||
|
|
||||||
PipeStatus status = getPipeStatus(channel, PipeDirection::DSPtoCPU);
|
PipeStatus status = getPipeStatus(channel, PipeDirection::DSPtoCPU);
|
||||||
|
@ -216,7 +215,7 @@ std::vector<u8> TeakraDSP::readPipe(u32 channel, u32 peer, u32 size, u32 buffer)
|
||||||
if (needUpdate) {
|
if (needUpdate) {
|
||||||
updatePipeStatus(status);
|
updatePipeStatus(status);
|
||||||
while (!teakra.SendDataIsEmpty(2)) {
|
while (!teakra.SendDataIsEmpty(2)) {
|
||||||
runAudioFrame();
|
runSlice();
|
||||||
}
|
}
|
||||||
|
|
||||||
teakra.SendData(2, status.slot);
|
teakra.SendData(2, status.slot);
|
||||||
|
@ -273,20 +272,20 @@ void TeakraDSP::loadComponent(std::vector<u8>& data, u32 programMask, u32 dataMa
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
do {
|
do {
|
||||||
while (!teakra.RecvDataIsReady(i)) {
|
while (!teakra.RecvDataIsReady(i)) {
|
||||||
runAudioFrame();
|
runSlice();
|
||||||
}
|
}
|
||||||
} while (teakra.RecvData(i) != 1);
|
} while (teakra.RecvData(i) != 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("DSP::LoadComponent: Semaphore value: %X\n", teakra.GetSemaphore());
|
|
||||||
|
|
||||||
// Retrieve the pipe base address
|
// Retrieve the pipe base address
|
||||||
while (!teakra.RecvDataIsReady(2)) {
|
while (!teakra.RecvDataIsReady(2)) {
|
||||||
runAudioFrame();
|
runSlice();
|
||||||
}
|
}
|
||||||
|
|
||||||
pipeBaseAddr = teakra.RecvData(2);
|
pipeBaseAddr = teakra.RecvData(2);
|
||||||
|
|
||||||
|
// Schedule next DSP event
|
||||||
|
scheduler.addEvent(Scheduler::EventType::RunDSP, scheduler.currentTimestamp + Audio::lleSlice * 2);
|
||||||
loaded = true;
|
loaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,17 +295,19 @@ void TeakraDSP::unloadComponent() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
loaded = false;
|
loaded = false;
|
||||||
|
// Stop scheduling DSP events
|
||||||
|
scheduler.removeEvent(Scheduler::EventType::RunDSP);
|
||||||
|
|
||||||
// Wait for SEND2 to be ready, then send the shutdown command to the DSP
|
// Wait for SEND2 to be ready, then send the shutdown command to the DSP
|
||||||
while (!teakra.SendDataIsEmpty(2)) {
|
while (!teakra.SendDataIsEmpty(2)) {
|
||||||
runAudioFrame();
|
runSlice();
|
||||||
}
|
}
|
||||||
|
|
||||||
teakra.SendData(2, 0x8000);
|
teakra.SendData(2, 0x8000);
|
||||||
|
|
||||||
// Wait for shutdown to be acknowledged
|
// Wait for shutdown to be acknowledged
|
||||||
while (!teakra.RecvDataIsReady(2)) {
|
while (!teakra.RecvDataIsReady(2)) {
|
||||||
runAudioFrame();
|
runSlice();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the value and discard it, completing shutdown
|
// Read the value and discard it, completing shutdown
|
||||||
|
|
|
@ -125,7 +125,7 @@ void GPUService::registerInterruptRelayQueue(u32 messagePointer) {
|
||||||
void GPUService::requestInterrupt(GPUInterrupt type) {
|
void GPUService::requestInterrupt(GPUInterrupt type) {
|
||||||
// HACK: Signal DSP events on GPU interrupt for now until we have the DSP since games need DSP events
|
// HACK: Signal DSP events on GPU interrupt for now until we have the DSP since games need DSP events
|
||||||
// Maybe there's a better alternative?
|
// Maybe there's a better alternative?
|
||||||
// kernel.signalDSPEvents();
|
//kernel.signalDSPEvents();
|
||||||
|
|
||||||
if (sharedMem == nullptr) [[unlikely]] { // Shared memory hasn't been set up yet
|
if (sharedMem == nullptr) [[unlikely]] { // Shared memory hasn't been set up yet
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -26,7 +26,7 @@ Emulator::Emulator()
|
||||||
{
|
{
|
||||||
DSPService& dspService = kernel.getServiceManager().getDSP();
|
DSPService& dspService = kernel.getServiceManager().getDSP();
|
||||||
|
|
||||||
dsp = Audio::makeDSPCore(Audio::DSPCore::Type::Teakra, memory, dspService);
|
dsp = Audio::makeDSPCore(Audio::DSPCore::Type::Teakra, memory, scheduler, dspService);
|
||||||
dspService.setDSPCore(dsp.get());
|
dspService.setDSPCore(dsp.get());
|
||||||
|
|
||||||
#ifdef PANDA3DS_ENABLE_DISCORD_RPC
|
#ifdef PANDA3DS_ENABLE_DISCORD_RPC
|
||||||
|
@ -151,7 +151,6 @@ void Emulator::pollScheduler() {
|
||||||
|
|
||||||
case Scheduler::EventType::UpdateTimers: kernel.pollTimers(); break;
|
case Scheduler::EventType::UpdateTimers: kernel.pollTimers(); break;
|
||||||
case Scheduler::EventType::RunDSP: {
|
case Scheduler::EventType::RunDSP: {
|
||||||
scheduler.addEvent(Scheduler::EventType::RunDSP, time + 16384 * 2);
|
|
||||||
dsp->runAudioFrame();
|
dsp->runAudioFrame();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue