tests/audio_core: add sanity test cases for LLE vs HLE
This commit is contained in:
parent
8cada619b3
commit
5311c939a2
@ -123,7 +123,6 @@ struct BinaryMessage {
|
|||||||
EncodeAACResponse encode_aac_response;
|
EncodeAACResponse encode_aac_response;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(sizeof(BinaryMessage) == 32, "Unexpected struct size for BinaryMessage");
|
static_assert(sizeof(BinaryMessage) == 32, "Unexpected struct size for BinaryMessage");
|
||||||
|
|
||||||
enum_le<DecoderSampleRate> GetSampleRateEnum(u32 sample_rate);
|
enum_le<DecoderSampleRate> GetSampleRateEnum(u32 sample_rate);
|
||||||
|
@ -468,6 +468,14 @@ void DspLle::SetServiceToInterrupt(std::weak_ptr<Service::DSP::DSP_DSP> dsp) {
|
|||||||
impl->teakra.SetSemaphoreHandler([ProcessPipeEvent]() { ProcessPipeEvent(false); });
|
impl->teakra.SetSemaphoreHandler([ProcessPipeEvent]() { ProcessPipeEvent(false); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DspLle::SetSemaphoreHandler(std::function<void()> handler) {
|
||||||
|
impl->teakra.SetSemaphoreHandler(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DspLle::SetRecvDataHandler(u8 index, std::function<void()> handler) {
|
||||||
|
impl->teakra.SetRecvDataHandler(index, handler);
|
||||||
|
}
|
||||||
|
|
||||||
void DspLle::LoadComponent(const std::vector<u8>& buffer) {
|
void DspLle::LoadComponent(const std::vector<u8>& buffer) {
|
||||||
impl->LoadComponent(buffer);
|
impl->LoadComponent(buffer);
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,9 @@ public:
|
|||||||
|
|
||||||
void SetServiceToInterrupt(std::weak_ptr<Service::DSP::DSP_DSP> dsp) override;
|
void SetServiceToInterrupt(std::weak_ptr<Service::DSP::DSP_DSP> dsp) override;
|
||||||
|
|
||||||
|
void SetSemaphoreHandler(std::function<void()> handler);
|
||||||
|
void SetRecvDataHandler(u8 index, std::function<void()> handler);
|
||||||
|
|
||||||
void LoadComponent(const std::vector<u8>& buffer) override;
|
void LoadComponent(const std::vector<u8>& buffer) override;
|
||||||
void UnloadComponent() override;
|
void UnloadComponent() override;
|
||||||
|
|
||||||
|
@ -11,6 +11,8 @@ add_executable(tests
|
|||||||
core/memory/memory.cpp
|
core/memory/memory.cpp
|
||||||
core/memory/vm_manager.cpp
|
core/memory/vm_manager.cpp
|
||||||
precompiled_headers.h
|
precompiled_headers.h
|
||||||
|
audio_core/hle/hle.cpp
|
||||||
|
audio_core/lle/lle.cpp
|
||||||
audio_core/audio_fixures.h
|
audio_core/audio_fixures.h
|
||||||
audio_core/decoder_tests.cpp
|
audio_core/decoder_tests.cpp
|
||||||
video_core/shader/shader_jit_x64_compiler.cpp
|
video_core/shader/shader_jit_x64_compiler.cpp
|
||||||
|
143
src/tests/audio_core/hle/hle.cpp
Normal file
143
src/tests/audio_core/hle/hle.cpp
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
// Copyright 2023 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
#include "audio_core/hle/decoder.h"
|
||||||
|
#include "audio_core/hle/hle.h"
|
||||||
|
#include "audio_core/lle/lle.h"
|
||||||
|
#include "common/common_paths.h"
|
||||||
|
#include "core/core_timing.h"
|
||||||
|
#include "core/memory.h"
|
||||||
|
|
||||||
|
TEST_CASE("DSP LLE vs HLE", "[audio_core][hle]") {
|
||||||
|
Memory::MemorySystem hle_memory;
|
||||||
|
Core::Timing hle_core_timing(1, 100);
|
||||||
|
|
||||||
|
Memory::MemorySystem lle_memory;
|
||||||
|
Core::Timing lle_core_timing(1, 100);
|
||||||
|
|
||||||
|
AudioCore::DspHle hle(hle_memory, hle_core_timing);
|
||||||
|
AudioCore::DspLle lle(lle_memory, lle_core_timing, true);
|
||||||
|
|
||||||
|
// Initialiase LLE
|
||||||
|
{
|
||||||
|
FileUtil::SetUserPath();
|
||||||
|
// see tests/audio_core/lle/lle.cpp for details on dspaudio.cdc
|
||||||
|
std::string firm_filepath =
|
||||||
|
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "3ds" DIR_SEP "dspaudio.cdc";
|
||||||
|
|
||||||
|
if (!FileUtil::Exists(firm_filepath)) {
|
||||||
|
SKIP("Test requires dspaudio.cdc");
|
||||||
|
}
|
||||||
|
|
||||||
|
FileUtil::IOFile firm_file(firm_filepath, "rb");
|
||||||
|
|
||||||
|
std::vector<u8> firm_file_buf(firm_file.GetSize());
|
||||||
|
firm_file.ReadArray(firm_file_buf.data(), firm_file_buf.size());
|
||||||
|
lle.LoadComponent(firm_file_buf);
|
||||||
|
lle.SetSemaphoreHandler([&lle]() {
|
||||||
|
u16 slot = lle.RecvData(2);
|
||||||
|
u16 side = slot % 2;
|
||||||
|
u16 pipe = slot / 2;
|
||||||
|
fmt::print("SetSemaphoreHandler slot={}\n", slot);
|
||||||
|
if (pipe > 15)
|
||||||
|
return;
|
||||||
|
if (side != 0)
|
||||||
|
return;
|
||||||
|
if (pipe == 0) {
|
||||||
|
// pipe 0 is for debug. 3DS automatically drains this pipe and discards the
|
||||||
|
// data
|
||||||
|
lle.PipeRead(static_cast<AudioCore::DspPipe>(pipe),
|
||||||
|
lle.GetPipeReadableSize(static_cast<AudioCore::DspPipe>(pipe)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
lle.SetRecvDataHandler(0, []() { fmt::print("SetRecvDataHandler 0\n"); });
|
||||||
|
lle.SetRecvDataHandler(1, []() { fmt::print("SetRecvDataHandler 1\n"); });
|
||||||
|
lle.SetRecvDataHandler(2, []() { fmt::print("SetRecvDataHandler 2\n"); });
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Initialise Audio Pipe") {
|
||||||
|
std::vector<u8> buffer(4, 0);
|
||||||
|
buffer[0] = 0;
|
||||||
|
|
||||||
|
// LLE
|
||||||
|
{
|
||||||
|
lle.PipeWrite(AudioCore::DspPipe::Audio, buffer);
|
||||||
|
lle.SetSemaphore(0x4000);
|
||||||
|
|
||||||
|
// todo: wait for interrupt
|
||||||
|
do {
|
||||||
|
lle_core_timing.GetTimer(0)->AddTicks(lle_core_timing.GetTimer(0)->GetDowncount());
|
||||||
|
lle_core_timing.GetTimer(0)->Advance();
|
||||||
|
lle_core_timing.GetTimer(0)->SetNextSlice();
|
||||||
|
} while (lle.GetPipeReadableSize(AudioCore::DspPipe::Audio) == 0);
|
||||||
|
|
||||||
|
REQUIRE(lle.GetPipeReadableSize(AudioCore::DspPipe::Audio) >= 32);
|
||||||
|
}
|
||||||
|
std::vector<u8> lle_read_buffer;
|
||||||
|
lle_read_buffer = lle.PipeRead(AudioCore::DspPipe::Audio, 2);
|
||||||
|
u16 lle_size;
|
||||||
|
memcpy(&lle_size, lle_read_buffer.data(), sizeof(lle_size));
|
||||||
|
lle_read_buffer = lle.PipeRead(AudioCore::DspPipe::Audio, lle_size * 2);
|
||||||
|
|
||||||
|
// HLE
|
||||||
|
{
|
||||||
|
hle.PipeWrite(AudioCore::DspPipe::Audio, buffer);
|
||||||
|
REQUIRE(hle.GetPipeReadableSize(AudioCore::DspPipe::Audio) >= 32);
|
||||||
|
}
|
||||||
|
std::vector<u8> hle_read_buffer(32);
|
||||||
|
hle_read_buffer = hle.PipeRead(AudioCore::DspPipe::Audio, 2);
|
||||||
|
u16 hle_size;
|
||||||
|
memcpy(&hle_size, hle_read_buffer.data(), sizeof(hle_size));
|
||||||
|
hle_read_buffer = hle.PipeRead(AudioCore::DspPipe::Audio, hle_size * 2);
|
||||||
|
|
||||||
|
REQUIRE(hle_size == lle_size);
|
||||||
|
REQUIRE(hle_read_buffer == lle_read_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Initialise Binary Pipe") {
|
||||||
|
std::vector<u8> buffer(32, 0);
|
||||||
|
AudioCore::HLE::BinaryMessage& request =
|
||||||
|
*reinterpret_cast<AudioCore::HLE::BinaryMessage*>(buffer.data());
|
||||||
|
|
||||||
|
request.header.codec = AudioCore::HLE::DecoderCodec::DecodeAAC;
|
||||||
|
request.header.cmd = AudioCore::HLE::DecoderCommand::Init;
|
||||||
|
|
||||||
|
// Values used by Pokemon X
|
||||||
|
request.header.result = static_cast<AudioCore::HLE::ResultStatus>(3);
|
||||||
|
request.decode_aac_init.unknown1 = 1;
|
||||||
|
request.decode_aac_init.unknown2 = 0xFFFF'FFFF;
|
||||||
|
request.decode_aac_init.unknown3 = 1;
|
||||||
|
request.decode_aac_init.unknown4 = 0;
|
||||||
|
request.decode_aac_init.unknown5 = 1;
|
||||||
|
request.decode_aac_init.unknown6 = 0x20;
|
||||||
|
|
||||||
|
// LLE
|
||||||
|
lle.PipeWrite(AudioCore::DspPipe::Binary, buffer);
|
||||||
|
lle.SetSemaphore(0x4000);
|
||||||
|
|
||||||
|
// todo: wait for interrupt
|
||||||
|
do {
|
||||||
|
lle_core_timing.GetTimer(0)->AddTicks(lle_core_timing.GetTimer(0)->GetDowncount());
|
||||||
|
lle_core_timing.GetTimer(0)->Advance();
|
||||||
|
lle_core_timing.GetTimer(0)->SetNextSlice();
|
||||||
|
} while (lle.GetPipeReadableSize(AudioCore::DspPipe::Binary) == 0);
|
||||||
|
|
||||||
|
REQUIRE(lle.GetPipeReadableSize(AudioCore::DspPipe::Binary) >= 32);
|
||||||
|
|
||||||
|
std::vector<u8> lle_read_buffer = lle.PipeRead(AudioCore::DspPipe::Binary, 32);
|
||||||
|
AudioCore::HLE::BinaryMessage& resp =
|
||||||
|
*reinterpret_cast<AudioCore::HLE::BinaryMessage*>(lle_read_buffer.data());
|
||||||
|
CHECK(resp.header.result == AudioCore::HLE::ResultStatus::Success);
|
||||||
|
|
||||||
|
// HLE
|
||||||
|
{
|
||||||
|
hle.PipeWrite(AudioCore::DspPipe::Binary, buffer);
|
||||||
|
REQUIRE(hle.GetPipeReadableSize(AudioCore::DspPipe::Binary) >= 32);
|
||||||
|
}
|
||||||
|
std::vector<u8> hle_read_buffer = hle.PipeRead(AudioCore::DspPipe::Binary, 32);
|
||||||
|
|
||||||
|
REQUIRE(hle_read_buffer == lle_read_buffer);
|
||||||
|
}
|
||||||
|
}
|
110
src/tests/audio_core/lle/lle.cpp
Normal file
110
src/tests/audio_core/lle/lle.cpp
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
// Copyright 2023 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
#include "audio_core/hle/decoder.h"
|
||||||
|
#include "audio_core/lle/lle.h"
|
||||||
|
#include "common/common_paths.h"
|
||||||
|
#include "core/core_timing.h"
|
||||||
|
#include "core/memory.h"
|
||||||
|
|
||||||
|
TEST_CASE("DSP LLE Sanity", "[audio_core][lle]") {
|
||||||
|
Memory::MemorySystem memory;
|
||||||
|
Core::Timing core_timing(1, 100);
|
||||||
|
|
||||||
|
AudioCore::DspLle lle(memory, core_timing, true);
|
||||||
|
{
|
||||||
|
FileUtil::SetUserPath();
|
||||||
|
// dspaudio.cdc can be dumped from Pokemon X & Y, It can be found in the romfs at
|
||||||
|
// "rom:/sound/dspaudio.cdc".
|
||||||
|
// One could also extract the firmware from the 3DS sound app using a modified version of
|
||||||
|
// https://github.com/zoogie/DSP1.
|
||||||
|
std::string firm_filepath =
|
||||||
|
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "3ds" DIR_SEP "dspaudio.cdc";
|
||||||
|
|
||||||
|
if (!FileUtil::Exists(firm_filepath)) {
|
||||||
|
SKIP("Test requires dspaudio.cdc");
|
||||||
|
}
|
||||||
|
|
||||||
|
FileUtil::IOFile firm_file(firm_filepath, "rb");
|
||||||
|
|
||||||
|
std::vector<u8> firm_file_buf(firm_file.GetSize());
|
||||||
|
firm_file.ReadArray(firm_file_buf.data(), firm_file_buf.size());
|
||||||
|
lle.LoadComponent(firm_file_buf);
|
||||||
|
}
|
||||||
|
lle.SetSemaphoreHandler([&lle]() {
|
||||||
|
u16 slot = lle.RecvData(2);
|
||||||
|
u16 side = slot % 2;
|
||||||
|
u16 pipe = slot / 2;
|
||||||
|
fmt::print("SetSemaphoreHandler slot={}\n", slot);
|
||||||
|
if (pipe > 15)
|
||||||
|
return;
|
||||||
|
if (side != 0)
|
||||||
|
return;
|
||||||
|
if (pipe == 0) {
|
||||||
|
// pipe 0 is for debug. 3DS automatically drains this pipe and discards the
|
||||||
|
// data
|
||||||
|
lle.PipeRead(static_cast<AudioCore::DspPipe>(pipe),
|
||||||
|
lle.GetPipeReadableSize(static_cast<AudioCore::DspPipe>(pipe)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
lle.SetRecvDataHandler(0, []() { fmt::print("SetRecvDataHandler 0\n"); });
|
||||||
|
lle.SetRecvDataHandler(1, []() { fmt::print("SetRecvDataHandler 1\n"); });
|
||||||
|
lle.SetRecvDataHandler(2, []() { fmt::print("SetRecvDataHandler 2\n"); });
|
||||||
|
SECTION("Initialise Audio Pipe") {
|
||||||
|
std::vector<u8> buffer(4, 0);
|
||||||
|
buffer[0] = 0;
|
||||||
|
|
||||||
|
lle.PipeWrite(AudioCore::DspPipe::Audio, buffer);
|
||||||
|
lle.SetSemaphore(0x4000);
|
||||||
|
|
||||||
|
// todo: wait for interrupt
|
||||||
|
do {
|
||||||
|
core_timing.GetTimer(0)->AddTicks(core_timing.GetTimer(0)->GetDowncount());
|
||||||
|
core_timing.GetTimer(0)->Advance();
|
||||||
|
core_timing.GetTimer(0)->SetNextSlice();
|
||||||
|
} while (lle.GetPipeReadableSize(AudioCore::DspPipe::Audio) == 0);
|
||||||
|
|
||||||
|
REQUIRE(lle.GetPipeReadableSize(AudioCore::DspPipe::Audio) >= 32);
|
||||||
|
|
||||||
|
buffer = lle.PipeRead(AudioCore::DspPipe::Audio, 2);
|
||||||
|
u16 size;
|
||||||
|
memcpy(&size, buffer.data(), sizeof(size));
|
||||||
|
// see AudioCore::DspHle::Impl::AudioPipeWriteStructAddresses()
|
||||||
|
REQUIRE(size * 2 == 30);
|
||||||
|
}
|
||||||
|
SECTION("Initialise EncodeAAC - Binary Pipe") {
|
||||||
|
std::vector<u8> buffer(32, 0);
|
||||||
|
AudioCore::HLE::BinaryMessage& request =
|
||||||
|
*reinterpret_cast<AudioCore::HLE::BinaryMessage*>(buffer.data());
|
||||||
|
|
||||||
|
request.header.codec = AudioCore::HLE::DecoderCodec::EncodeAAC;
|
||||||
|
request.header.cmd = AudioCore::HLE::DecoderCommand::Init;
|
||||||
|
|
||||||
|
// Values used by the 3DS sound app.
|
||||||
|
request.encode_aac_init.unknown1 = 1;
|
||||||
|
request.encode_aac_init.sample_rate = static_cast<AudioCore::HLE::DecoderSampleRate>(5);
|
||||||
|
request.encode_aac_init.unknown3 = 2;
|
||||||
|
request.encode_aac_init.unknown4 = 0;
|
||||||
|
request.encode_aac_init.unknown5 = rand();
|
||||||
|
request.encode_aac_init.unknown6 = rand();
|
||||||
|
lle.PipeWrite(AudioCore::DspPipe::Binary, buffer);
|
||||||
|
lle.SetSemaphore(0x4000);
|
||||||
|
|
||||||
|
// todo: wait for interrupt
|
||||||
|
do {
|
||||||
|
core_timing.GetTimer(0)->AddTicks(core_timing.GetTimer(0)->GetDowncount());
|
||||||
|
core_timing.GetTimer(0)->Advance();
|
||||||
|
core_timing.GetTimer(0)->SetNextSlice();
|
||||||
|
} while (lle.GetPipeReadableSize(AudioCore::DspPipe::Binary) == 0);
|
||||||
|
|
||||||
|
REQUIRE(lle.GetPipeReadableSize(AudioCore::DspPipe::Binary) >= 32);
|
||||||
|
|
||||||
|
buffer = lle.PipeRead(AudioCore::DspPipe::Binary, 32);
|
||||||
|
AudioCore::HLE::BinaryMessage& resp =
|
||||||
|
*reinterpret_cast<AudioCore::HLE::BinaryMessage*>(buffer.data());
|
||||||
|
REQUIRE(resp.header.result == AudioCore::HLE::ResultStatus::Success);
|
||||||
|
REQUIRE(resp.data == request.data);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user