DSP/LLE: add multithread mode
This commit is contained in:
parent
fbad420240
commit
443f4b964d
|
@ -1 +1 @@
|
||||||
Subproject commit 343bad999d53279b5ed966db3f36e2d1c6f5d85b
|
Subproject commit fd97ef90bff46bd5c3163edfd32b4cb38964eebe
|
|
@ -3,13 +3,17 @@
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <atomic>
|
||||||
|
#include <thread>
|
||||||
#include <teakra/teakra.h>
|
#include <teakra/teakra.h>
|
||||||
#include "audio_core/lle/lle.h"
|
#include "audio_core/lle/lle.h"
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/bit_field.h"
|
#include "common/bit_field.h"
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
|
#include "common/thread.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
|
#include "core/hle/lock.h"
|
||||||
#include "core/hle/service/dsp/dsp_dsp.h"
|
#include "core/hle/service/dsp/dsp_dsp.h"
|
||||||
|
|
||||||
namespace AudioCore {
|
namespace AudioCore {
|
||||||
|
@ -117,11 +121,15 @@ static u8 PipeIndexToSlotIndex(u8 pipe_index, PipeDirection direction) {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DspLle::Impl final {
|
struct DspLle::Impl final {
|
||||||
Impl() {
|
Impl(bool multithread) : multithread(multithread) {
|
||||||
teakra_slice_event = Core::System::GetInstance().CoreTiming().RegisterEvent(
|
teakra_slice_event = Core::System::GetInstance().CoreTiming().RegisterEvent(
|
||||||
"DSP slice", [this](u64, int late) { TeakraSliceEvent(static_cast<u64>(late)); });
|
"DSP slice", [this](u64, int late) { TeakraSliceEvent(static_cast<u64>(late)); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~Impl() {
|
||||||
|
StopTeakraThread();
|
||||||
|
}
|
||||||
|
|
||||||
Teakra::Teakra teakra;
|
Teakra::Teakra teakra;
|
||||||
u16 pipe_base_waddr = 0;
|
u16 pipe_base_waddr = 0;
|
||||||
|
|
||||||
|
@ -129,13 +137,44 @@ struct DspLle::Impl final {
|
||||||
bool data_signaled = false;
|
bool data_signaled = false;
|
||||||
|
|
||||||
Core::TimingEventType* teakra_slice_event;
|
Core::TimingEventType* teakra_slice_event;
|
||||||
bool loaded = false;
|
std::atomic<bool> loaded = false;
|
||||||
|
|
||||||
|
const bool multithread;
|
||||||
|
std::thread teakra_thread;
|
||||||
|
Common::Barrier teakra_slice_barrier{2};
|
||||||
|
std::atomic<bool> stop_signal = false;
|
||||||
|
std::size_t stop_generation;
|
||||||
|
|
||||||
static constexpr u32 DspDataOffset = 0x40000;
|
static constexpr u32 DspDataOffset = 0x40000;
|
||||||
static constexpr u32 TeakraSlice = 20000;
|
static constexpr u32 TeakraSlice = 20000;
|
||||||
|
|
||||||
|
void TeakraThread() {
|
||||||
|
while (true) {
|
||||||
|
teakra.Run(TeakraSlice);
|
||||||
|
teakra_slice_barrier.Sync();
|
||||||
|
if (stop_signal) {
|
||||||
|
if (stop_generation == teakra_slice_barrier.Generation())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stop_signal = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StopTeakraThread() {
|
||||||
|
if (teakra_thread.joinable()) {
|
||||||
|
stop_generation = teakra_slice_barrier.Generation() + 1;
|
||||||
|
stop_signal = true;
|
||||||
|
teakra_slice_barrier.Sync();
|
||||||
|
teakra_thread.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void RunTeakraSlice() {
|
void RunTeakraSlice() {
|
||||||
teakra.Run(TeakraSlice);
|
if (multithread) {
|
||||||
|
teakra_slice_barrier.Sync();
|
||||||
|
} else {
|
||||||
|
teakra.Run(TeakraSlice);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TeakraSliceEvent(u64 late) {
|
void TeakraSliceEvent(u64 late) {
|
||||||
|
@ -288,6 +327,10 @@ struct DspLle::Impl final {
|
||||||
|
|
||||||
Core::System::GetInstance().CoreTiming().ScheduleEvent(TeakraSlice, teakra_slice_event, 0);
|
Core::System::GetInstance().CoreTiming().ScheduleEvent(TeakraSlice, teakra_slice_event, 0);
|
||||||
|
|
||||||
|
if (multithread) {
|
||||||
|
teakra_thread = std::thread(&Impl::TeakraThread, this);
|
||||||
|
}
|
||||||
|
|
||||||
// Wait for initialization
|
// Wait for initialization
|
||||||
if (dsp.recv_data_on_start) {
|
if (dsp.recv_data_on_start) {
|
||||||
for (u8 i = 0; i < 3; ++i) {
|
for (u8 i = 0; i < 3; ++i) {
|
||||||
|
@ -312,6 +355,8 @@ struct DspLle::Impl final {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loaded = false;
|
||||||
|
|
||||||
// Send finalization signal via command/reply register 2
|
// Send finalization signal via command/reply register 2
|
||||||
constexpr u16 FinalizeSignal = 0x8000;
|
constexpr u16 FinalizeSignal = 0x8000;
|
||||||
while (!teakra.SendDataIsEmpty(2))
|
while (!teakra.SendDataIsEmpty(2))
|
||||||
|
@ -326,7 +371,7 @@ struct DspLle::Impl final {
|
||||||
teakra.RecvData(2); // discard the value
|
teakra.RecvData(2); // discard the value
|
||||||
|
|
||||||
Core::System::GetInstance().CoreTiming().UnscheduleEvent(teakra_slice_event, 0);
|
Core::System::GetInstance().CoreTiming().UnscheduleEvent(teakra_slice_event, 0);
|
||||||
loaded = false;
|
StopTeakraThread();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -362,13 +407,21 @@ std::array<u8, Memory::DSP_RAM_SIZE>& DspLle::GetDspMemory() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DspLle::SetServiceToInterrupt(std::weak_ptr<Service::DSP::DSP_DSP> dsp) {
|
void DspLle::SetServiceToInterrupt(std::weak_ptr<Service::DSP::DSP_DSP> dsp) {
|
||||||
impl->teakra.SetRecvDataHandler(0, [dsp]() {
|
impl->teakra.SetRecvDataHandler(0, [this, dsp]() {
|
||||||
|
if (!impl->loaded)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::lock_guard lock(HLE::g_hle_lock);
|
||||||
if (auto locked = dsp.lock()) {
|
if (auto locked = dsp.lock()) {
|
||||||
locked->SignalInterrupt(Service::DSP::DSP_DSP::InterruptType::Zero,
|
locked->SignalInterrupt(Service::DSP::DSP_DSP::InterruptType::Zero,
|
||||||
static_cast<DspPipe>(0));
|
static_cast<DspPipe>(0));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
impl->teakra.SetRecvDataHandler(1, [dsp]() {
|
impl->teakra.SetRecvDataHandler(1, [this, dsp]() {
|
||||||
|
if (!impl->loaded)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::lock_guard lock(HLE::g_hle_lock);
|
||||||
if (auto locked = dsp.lock()) {
|
if (auto locked = dsp.lock()) {
|
||||||
locked->SignalInterrupt(Service::DSP::DSP_DSP::InterruptType::One,
|
locked->SignalInterrupt(Service::DSP::DSP_DSP::InterruptType::One,
|
||||||
static_cast<DspPipe>(0));
|
static_cast<DspPipe>(0));
|
||||||
|
@ -399,6 +452,7 @@ void DspLle::SetServiceToInterrupt(std::weak_ptr<Service::DSP::DSP_DSP> dsp) {
|
||||||
// pipe 0 is for debug. 3DS automatically drains this pipe and discards the data
|
// pipe 0 is for debug. 3DS automatically drains this pipe and discards the data
|
||||||
impl->ReadPipe(pipe, impl->GetPipeReadableSize(pipe));
|
impl->ReadPipe(pipe, impl->GetPipeReadableSize(pipe));
|
||||||
} else {
|
} else {
|
||||||
|
std::lock_guard lock(HLE::g_hle_lock);
|
||||||
if (auto locked = dsp.lock()) {
|
if (auto locked = dsp.lock()) {
|
||||||
locked->SignalInterrupt(Service::DSP::DSP_DSP::InterruptType::Pipe,
|
locked->SignalInterrupt(Service::DSP::DSP_DSP::InterruptType::Pipe,
|
||||||
static_cast<DspPipe>(pipe));
|
static_cast<DspPipe>(pipe));
|
||||||
|
@ -419,7 +473,8 @@ void DspLle::UnloadComponent() {
|
||||||
impl->UnloadComponent();
|
impl->UnloadComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
DspLle::DspLle(Memory::MemorySystem& memory) : impl(std::make_unique<Impl>()) {
|
DspLle::DspLle(Memory::MemorySystem& memory, bool multithread)
|
||||||
|
: impl(std::make_unique<Impl>(multithread)) {
|
||||||
Teakra::AHBMCallback ahbm;
|
Teakra::AHBMCallback ahbm;
|
||||||
ahbm.read8 = [&memory](u32 address) -> u8 {
|
ahbm.read8 = [&memory](u32 address) -> u8 {
|
||||||
return *memory.GetFCRAMPointer(address - Memory::FCRAM_PADDR);
|
return *memory.GetFCRAMPointer(address - Memory::FCRAM_PADDR);
|
||||||
|
|
|
@ -10,7 +10,7 @@ namespace AudioCore {
|
||||||
|
|
||||||
class DspLle final : public DspInterface {
|
class DspLle final : public DspInterface {
|
||||||
public:
|
public:
|
||||||
explicit DspLle(Memory::MemorySystem& memory);
|
explicit DspLle(Memory::MemorySystem& memory, bool multithread);
|
||||||
~DspLle() override;
|
~DspLle() override;
|
||||||
|
|
||||||
u16 RecvData(u32 register_number) override;
|
u16 RecvData(u32 register_number) override;
|
||||||
|
|
|
@ -156,6 +156,8 @@ void Config::ReadValues() {
|
||||||
|
|
||||||
// Audio
|
// Audio
|
||||||
Settings::values.enable_dsp_lle = sdl2_config->GetBoolean("Audio", "enable_dsp_lle", false);
|
Settings::values.enable_dsp_lle = sdl2_config->GetBoolean("Audio", "enable_dsp_lle", false);
|
||||||
|
Settings::values.enable_dsp_lle_multithread =
|
||||||
|
sdl2_config->GetBoolean("Audio", "enable_dsp_lle_multithread", false);
|
||||||
Settings::values.sink_id = sdl2_config->GetString("Audio", "output_engine", "auto");
|
Settings::values.sink_id = sdl2_config->GetString("Audio", "output_engine", "auto");
|
||||||
Settings::values.enable_audio_stretching =
|
Settings::values.enable_audio_stretching =
|
||||||
sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true);
|
sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true);
|
||||||
|
|
|
@ -171,6 +171,11 @@ swap_screen =
|
||||||
# 0 (default): No, 1: Yes
|
# 0 (default): No, 1: Yes
|
||||||
enable_dsp_lle =
|
enable_dsp_lle =
|
||||||
|
|
||||||
|
# Whether or not to run DSP LLE on a different thread
|
||||||
|
# 0 (default): No, 1: Yes
|
||||||
|
enable_dsp_lle_thread =
|
||||||
|
|
||||||
|
|
||||||
# Which audio output engine to use.
|
# Which audio output engine to use.
|
||||||
# auto (default): Auto-select, null: No audio output, sdl2: SDL2 (if available)
|
# auto (default): Auto-select, null: No audio output, sdl2: SDL2 (if available)
|
||||||
output_engine =
|
output_engine =
|
||||||
|
|
|
@ -137,6 +137,8 @@ void Config::ReadValues() {
|
||||||
|
|
||||||
qt_config->beginGroup("Audio");
|
qt_config->beginGroup("Audio");
|
||||||
Settings::values.enable_dsp_lle = ReadSetting("enable_dsp_lle", false).toBool();
|
Settings::values.enable_dsp_lle = ReadSetting("enable_dsp_lle", false).toBool();
|
||||||
|
Settings::values.enable_dsp_lle_multithread =
|
||||||
|
ReadSetting("enable_dsp_lle_multithread", false).toBool();
|
||||||
Settings::values.sink_id = ReadSetting("output_engine", "auto").toString().toStdString();
|
Settings::values.sink_id = ReadSetting("output_engine", "auto").toString().toStdString();
|
||||||
Settings::values.enable_audio_stretching =
|
Settings::values.enable_audio_stretching =
|
||||||
ReadSetting("enable_audio_stretching", true).toBool();
|
ReadSetting("enable_audio_stretching", true).toBool();
|
||||||
|
@ -417,6 +419,7 @@ void Config::SaveValues() {
|
||||||
|
|
||||||
qt_config->beginGroup("Audio");
|
qt_config->beginGroup("Audio");
|
||||||
WriteSetting("enable_dsp_lle", Settings::values.enable_dsp_lle, false);
|
WriteSetting("enable_dsp_lle", Settings::values.enable_dsp_lle, false);
|
||||||
|
WriteSetting("enable_dsp_lle_multithread", Settings::values.enable_dsp_lle_multithread, false);
|
||||||
WriteSetting("output_engine", QString::fromStdString(Settings::values.sink_id), "auto");
|
WriteSetting("output_engine", QString::fromStdString(Settings::values.sink_id), "auto");
|
||||||
WriteSetting("enable_audio_stretching", Settings::values.enable_audio_stretching, true);
|
WriteSetting("enable_audio_stretching", Settings::values.enable_audio_stretching, true);
|
||||||
WriteSetting("output_device", QString::fromStdString(Settings::values.audio_device_id), "auto");
|
WriteSetting("output_device", QString::fromStdString(Settings::values.audio_device_id), "auto");
|
||||||
|
|
|
@ -22,6 +22,7 @@ ConfigureAudio::ConfigureAudio(QWidget* parent)
|
||||||
|
|
||||||
ui->emulation_combo_box->addItem(tr("HLE (fast)"));
|
ui->emulation_combo_box->addItem(tr("HLE (fast)"));
|
||||||
ui->emulation_combo_box->addItem(tr("LLE (accurate)"));
|
ui->emulation_combo_box->addItem(tr("LLE (accurate)"));
|
||||||
|
ui->emulation_combo_box->addItem(tr("LLE multi-core"));
|
||||||
ui->emulation_combo_box->setEnabled(!Core::System::GetInstance().IsPoweredOn());
|
ui->emulation_combo_box->setEnabled(!Core::System::GetInstance().IsPoweredOn());
|
||||||
|
|
||||||
connect(ui->volume_slider, &QSlider::valueChanged, this,
|
connect(ui->volume_slider, &QSlider::valueChanged, this,
|
||||||
|
@ -47,7 +48,17 @@ void ConfigureAudio::setConfiguration() {
|
||||||
ui->volume_slider->setValue(Settings::values.volume * ui->volume_slider->maximum());
|
ui->volume_slider->setValue(Settings::values.volume * ui->volume_slider->maximum());
|
||||||
setVolumeIndicatorText(ui->volume_slider->sliderPosition());
|
setVolumeIndicatorText(ui->volume_slider->sliderPosition());
|
||||||
|
|
||||||
ui->emulation_combo_box->setCurrentIndex(Settings::values.enable_dsp_lle ? 1 : 0);
|
int selection;
|
||||||
|
if (Settings::values.enable_dsp_lle) {
|
||||||
|
if (Settings::values.enable_dsp_lle_multithread) {
|
||||||
|
selection = 2;
|
||||||
|
} else {
|
||||||
|
selection = 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
selection = 0;
|
||||||
|
}
|
||||||
|
ui->emulation_combo_box->setCurrentIndex(selection);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigureAudio::setOutputSinkFromSinkID() {
|
void ConfigureAudio::setOutputSinkFromSinkID() {
|
||||||
|
@ -92,7 +103,8 @@ void ConfigureAudio::applyConfiguration() {
|
||||||
.toStdString();
|
.toStdString();
|
||||||
Settings::values.volume =
|
Settings::values.volume =
|
||||||
static_cast<float>(ui->volume_slider->sliderPosition()) / ui->volume_slider->maximum();
|
static_cast<float>(ui->volume_slider->sliderPosition()) / ui->volume_slider->maximum();
|
||||||
Settings::values.enable_dsp_lle = ui->emulation_combo_box->currentIndex() == 1;
|
Settings::values.enable_dsp_lle = ui->emulation_combo_box->currentIndex() != 0;
|
||||||
|
Settings::values.enable_dsp_lle_multithread = ui->emulation_combo_box->currentIndex() == 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigureAudio::updateAudioDevices(int sink_index) {
|
void ConfigureAudio::updateAudioDevices(int sink_index) {
|
||||||
|
|
|
@ -190,7 +190,8 @@ System::ResultStatus System::Init(EmuWindow& emu_window, u32 system_mode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Settings::values.enable_dsp_lle) {
|
if (Settings::values.enable_dsp_lle) {
|
||||||
dsp_core = std::make_unique<AudioCore::DspLle>(*memory);
|
dsp_core = std::make_unique<AudioCore::DspLle>(*memory,
|
||||||
|
Settings::values.enable_dsp_lle_multithread);
|
||||||
} else {
|
} else {
|
||||||
dsp_core = std::make_unique<AudioCore::DspHle>(*memory);
|
dsp_core = std::make_unique<AudioCore::DspHle>(*memory);
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,6 +80,7 @@ void LogSettings() {
|
||||||
LogSetting("Layout_LayoutOption", static_cast<int>(Settings::values.layout_option));
|
LogSetting("Layout_LayoutOption", static_cast<int>(Settings::values.layout_option));
|
||||||
LogSetting("Layout_SwapScreen", Settings::values.swap_screen);
|
LogSetting("Layout_SwapScreen", Settings::values.swap_screen);
|
||||||
LogSetting("Audio_EnableDspLle", Settings::values.enable_dsp_lle);
|
LogSetting("Audio_EnableDspLle", Settings::values.enable_dsp_lle);
|
||||||
|
LogSetting("Audio_EnableDspLleMultithread", Settings::values.enable_dsp_lle_multithread);
|
||||||
LogSetting("Audio_OutputEngine", Settings::values.sink_id);
|
LogSetting("Audio_OutputEngine", Settings::values.sink_id);
|
||||||
LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching);
|
LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching);
|
||||||
LogSetting("Audio_OutputDevice", Settings::values.audio_device_id);
|
LogSetting("Audio_OutputDevice", Settings::values.audio_device_id);
|
||||||
|
|
|
@ -148,6 +148,7 @@ struct Values {
|
||||||
|
|
||||||
// Audio
|
// Audio
|
||||||
bool enable_dsp_lle;
|
bool enable_dsp_lle;
|
||||||
|
bool enable_dsp_lle_multithread;
|
||||||
std::string sink_id;
|
std::string sink_id;
|
||||||
bool enable_audio_stretching;
|
bool enable_audio_stretching;
|
||||||
std::string audio_device_id;
|
std::string audio_device_id;
|
||||||
|
|
Reference in New Issue