diff --git a/src/xenia/apu/audio_system.cc b/src/xenia/apu/audio_system.cc index a1a4f2aeb..f37084ce1 100644 --- a/src/xenia/apu/audio_system.cc +++ b/src/xenia/apu/audio_system.cc @@ -301,6 +301,8 @@ void AudioSystem::Pause() { // Kind of a hack, but it works. shutdown_event_->Set(); pause_fence_.Wait(); + + xma_decoder_->Pause(); } void AudioSystem::Resume() { @@ -310,6 +312,8 @@ void AudioSystem::Resume() { paused_ = false; resume_event_->Set(); + + xma_decoder_->Resume(); } } // namespace apu diff --git a/src/xenia/apu/xma_decoder.cc b/src/xenia/apu/xma_decoder.cc index 15fc85f78..778c952f1 100644 --- a/src/xenia/apu/xma_decoder.cc +++ b/src/xenia/apu/xma_decoder.cc @@ -115,6 +115,7 @@ X_STATUS XmaDecoder::Setup(kernel::KernelState* kernel_state) { } } registers_.next_context = 1; + context_bitmap_.Resize(kContextCount); worker_running_ = true; worker_thread_ = kernel::object_ref( @@ -141,6 +142,12 @@ void XmaDecoder::WorkerThreadMain() { // registers_.current_context = n; // registers_.next_context = (n + 1) % kContextCount; } + + if (paused_) { + pause_fence_.Signal(); + resume_fence_.Wait(); + } + xe::threading::MaybeYield(); } } @@ -150,6 +157,10 @@ void XmaDecoder::Shutdown() { worker_fence_.Signal(); worker_thread_.reset(); + if (paused_) { + Resume(); + } + memory()->SystemHeapFree(registers_.context_array_ptr); } @@ -164,32 +175,29 @@ int XmaDecoder::GetContextId(uint32_t guest_ptr) { } uint32_t XmaDecoder::AllocateContext() { - std::lock_guard lock(lock_); - - for (uint32_t n = 0; n < kContextCount; n++) { - XmaContext& context = contexts_[n]; - if (!context.is_allocated()) { - context.set_is_allocated(true); - return context.guest_ptr(); - } + size_t index = context_bitmap_.Acquire(); + if (index == -1) { + // Out of contexts. + return 0; } - return 0; + XmaContext& context = contexts_[index]; + assert_false(context.is_allocated()); + context.set_is_allocated(true); + return context.guest_ptr(); } void XmaDecoder::ReleaseContext(uint32_t guest_ptr) { - std::lock_guard lock(lock_); - auto context_id = GetContextId(guest_ptr); assert_true(context_id >= 0); XmaContext& context = contexts_[context_id]; + assert_true(context.is_allocated()); context.Release(); + context_bitmap_.Release(context_id); } bool XmaDecoder::BlockOnContext(uint32_t guest_ptr, bool poll) { - std::lock_guard lock(lock_); - auto context_id = GetContextId(guest_ptr); assert_true(context_id >= 0); @@ -283,5 +291,23 @@ void XmaDecoder::WriteRegister(uint32_t addr, uint32_t value) { } } +void XmaDecoder::Pause() { + if (paused_) { + return; + } + paused_ = true; + + pause_fence_.Wait(); +} + +void XmaDecoder::Resume() { + if (!paused_) { + return; + } + paused_ = false; + + resume_fence_.Signal(); +} + } // namespace apu } // namespace xe diff --git a/src/xenia/apu/xma_decoder.h b/src/xenia/apu/xma_decoder.h index 5c3c4d69d..318a4dcb5 100644 --- a/src/xenia/apu/xma_decoder.h +++ b/src/xenia/apu/xma_decoder.h @@ -15,6 +15,7 @@ #include #include "xenia/apu/xma_context.h" +#include "xenia/base/bit_map.h" #include "xenia/kernel/xthread.h" #include "xenia/xbox.h" @@ -43,6 +44,10 @@ class XmaDecoder { uint32_t ReadRegister(uint32_t addr); void WriteRegister(uint32_t addr, uint32_t value); + bool is_paused() const { return paused_; } + void Pause(); + void Resume(); + protected: int GetContextId(uint32_t guest_ptr); @@ -66,7 +71,9 @@ class XmaDecoder { kernel::object_ref worker_thread_; xe::threading::Fence worker_fence_; - std::mutex lock_; + bool paused_ = false; + xe::threading::Fence pause_fence_; // Signaled when worker paused. + xe::threading::Fence resume_fence_; // Signaled when resume requested. // Stored little endian, accessed through 0x7FEA.... union { @@ -91,6 +98,7 @@ class XmaDecoder { static const uint32_t kContextCount = 320; XmaContext contexts_[kContextCount]; + BitMap context_bitmap_; uint32_t context_data_first_ptr_ = 0; uint32_t context_data_last_ptr_ = 0;