[opus] proper limit and error code for opus decoder instances (#4384)

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

This is intended to fix Hades II save loading issue.

By analyzing panic dump it was found out that the error code was 0x160A. By decoding firmware it was found that error comes from 'nn::codec::InitializeHardwareOpusDecoder'.

Further decoding revealed that SDK tries to allocate multiple opus instances, up to the fw limit of 24 instances, and when it receives return error ResultOutOfOpusDecoders (385), a software fallback is gracefully used.

Implementing the limitation and the limit reached error code return makes saves load properly, multiple times in a row in both windows and android.

I still wonder why the game allocates too many parallel instances, but the current implementation is a real fw compliant solution, and whether the 24+ is real or some leak may be investigated later, or not.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4384
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
This commit is contained in:
xbzk
2026-09-10 22:29:35 +02:00
committed by crueter
parent e7a96c7907
commit 00a1c392e7
4 changed files with 46 additions and 1 deletions
+16
View File
@@ -8,6 +8,7 @@
#include "audio_core/opus/hardware_opus.h" #include "audio_core/opus/hardware_opus.h"
#include "audio_core/opus/parameters.h" #include "audio_core/opus/parameters.h"
#include "common/alignment.h" #include "common/alignment.h"
#include "common/scope_exit.h"
#include "common/swap.h" #include "common/swap.h"
#include "core/core.h" #include "core/core.h"
@@ -28,10 +29,18 @@ OpusDecoder::OpusDecoder(Core::System& system_, HardwareOpus& hardware_opus_)
OpusDecoder::~OpusDecoder() { OpusDecoder::~OpusDecoder() {
if (decode_object_initialized) { if (decode_object_initialized) {
hardware_opus.ShutdownDecodeObject(shared_buffer.data(), shared_buffer.size()); hardware_opus.ShutdownDecodeObject(shared_buffer.data(), shared_buffer.size());
hardware_opus.UnregisterDecoder(this);
} }
} }
Result OpusDecoder::Initialize(const OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size) { Result OpusDecoder::Initialize(const OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size) {
R_TRY(hardware_opus.RegisterDecoder(this));
SCOPE_EXIT {
if (!decode_object_initialized) {
hardware_opus.UnregisterDecoder(this);
}
};
auto frame_size{params.use_large_frame_size ? 5760 : 1920}; auto frame_size{params.use_large_frame_size ? 5760 : 1920};
shared_buffer.resize(transfer_memory_size); shared_buffer.resize(transfer_memory_size);
shared_memory_mapped = true; shared_memory_mapped = true;
@@ -61,6 +70,13 @@ Result OpusDecoder::Initialize(const OpusParametersEx& params, Kernel::KTransfer
} }
Result OpusDecoder::Initialize(const OpusMultiStreamParametersEx& params, Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size) { Result OpusDecoder::Initialize(const OpusMultiStreamParametersEx& params, Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size) {
R_TRY(hardware_opus.RegisterDecoder(this));
SCOPE_EXIT {
if (!decode_object_initialized) {
hardware_opus.UnregisterDecoder(this);
}
};
auto frame_size{params.use_large_frame_size ? 5760 : 1920}; auto frame_size{params.use_large_frame_size ? 5760 : 1920};
shared_buffer.resize(transfer_memory_size, 0); shared_buffer.resize(transfer_memory_size, 0);
shared_memory_mapped = true; shared_memory_mapped = true;
+20
View File
@@ -4,6 +4,7 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <array> #include <array>
#include "audio_core/audio_core.h" #include "audio_core/audio_core.h"
@@ -45,6 +46,25 @@ HardwareOpus::HardwareOpus(Core::System& system_)
opus_decoder.SetSharedMemory(shared_memory); opus_decoder.SetSharedMemory(shared_memory);
} }
Result HardwareOpus::RegisterDecoder(OpusDecoder* decoder) {
std::scoped_lock l{mutex};
const auto slot = std::ranges::find(decoders, nullptr);
if (slot == decoders.end()) {
R_THROW(ResultOutOfOpusDecoders);
}
*slot = decoder;
R_SUCCEED();
}
void HardwareOpus::UnregisterDecoder(OpusDecoder* decoder) {
std::scoped_lock l{mutex};
const auto slot = std::ranges::find(decoders, decoder);
if (slot == decoders.end()) {
return;
}
*slot = nullptr;
}
u32 HardwareOpus::GetWorkBufferSize(u32 channel) { u32 HardwareOpus::GetWorkBufferSize(u32 channel) {
if (!opus_decoder.IsRunning()) { if (!opus_decoder.IsRunning()) {
return 0; return 0;
+8
View File
@@ -1,8 +1,12 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include <array>
#include <mutex> #include <mutex>
#include <opus.h> #include <opus.h>
@@ -12,9 +16,12 @@
#include "core/hle/service/audio/errors.h" #include "core/hle/service/audio/errors.h"
namespace AudioCore::OpusDecoder { namespace AudioCore::OpusDecoder {
class OpusDecoder;
class HardwareOpus { class HardwareOpus {
public: public:
HardwareOpus(Core::System& system); HardwareOpus(Core::System& system);
Result RegisterDecoder(OpusDecoder* decoder);
void UnregisterDecoder(OpusDecoder* decoder);
u32 GetWorkBufferSize(u32 channel); u32 GetWorkBufferSize(u32 channel);
u32 GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count); u32 GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count);
@@ -39,6 +46,7 @@ public:
private: private:
Core::System& system; Core::System& system;
std::mutex mutex; std::mutex mutex;
std::array<OpusDecoder*, 24> decoders{};
ADSP::OpusDecoder::OpusDecoder& opus_decoder; ADSP::OpusDecoder::OpusDecoder& opus_decoder;
ADSP::OpusDecoder::SharedMemory shared_memory; ADSP::OpusDecoder::SharedMemory shared_memory;
}; };
+2 -1
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -33,6 +33,7 @@ constexpr Result ResultLibOpusInternalError{ErrorModule::HwOpus, 4};
constexpr Result ResultBufferTooSmall{ErrorModule::HwOpus, 3}; constexpr Result ResultBufferTooSmall{ErrorModule::HwOpus, 3};
constexpr Result ResultLibOpusBadArg{ErrorModule::HwOpus, 2}; constexpr Result ResultLibOpusBadArg{ErrorModule::HwOpus, 2};
constexpr Result ResultInvalidOpusDSPReturnCode{ErrorModule::HwOpus, 259}; constexpr Result ResultInvalidOpusDSPReturnCode{ErrorModule::HwOpus, 259};
constexpr Result ResultOutOfOpusDecoders{ErrorModule::HwOpus, 385};
constexpr Result ResultInvalidOpusSampleRate{ErrorModule::HwOpus, 1001}; constexpr Result ResultInvalidOpusSampleRate{ErrorModule::HwOpus, 1001};
constexpr Result ResultInvalidOpusChannelCount{ErrorModule::HwOpus, 1002}; constexpr Result ResultInvalidOpusChannelCount{ErrorModule::HwOpus, 1002};