diff --git a/src/xenia/apu/xma_context.cc b/src/xenia/apu/xma_context.cc index d5042ad8d..02caa8095 100644 --- a/src/xenia/apu/xma_context.cc +++ b/src/xenia/apu/xma_context.cc @@ -98,10 +98,10 @@ int XmaContext::Setup(uint32_t id, Memory* memory, uint32_t guest_ptr) { return 0; } -void XmaContext::Work() { +bool XmaContext::Work() { std::lock_guard lock(lock_); if (!is_allocated() || !is_enabled()) { - return; + return false; } set_is_enabled(false); @@ -110,6 +110,7 @@ void XmaContext::Work() { XMA_CONTEXT_DATA data(context_ptr); DecodePackets(&data); data.Store(context_ptr); + return true; } void XmaContext::Enable() { diff --git a/src/xenia/apu/xma_context.h b/src/xenia/apu/xma_context.h index 6ae9197d3..68be68c73 100644 --- a/src/xenia/apu/xma_context.h +++ b/src/xenia/apu/xma_context.h @@ -147,7 +147,7 @@ class XmaContext { ~XmaContext(); int Setup(uint32_t id, Memory* memory, uint32_t guest_ptr); - void Work(); + bool Work(); void Enable(); bool Block(bool poll); diff --git a/src/xenia/apu/xma_decoder.cc b/src/xenia/apu/xma_decoder.cc index 778c952f1..8994ac8cb 100644 --- a/src/xenia/apu/xma_decoder.cc +++ b/src/xenia/apu/xma_decoder.cc @@ -118,6 +118,7 @@ X_STATUS XmaDecoder::Setup(kernel::KernelState* kernel_state) { context_bitmap_.Resize(kContextCount); worker_running_ = true; + work_event_ = xe::threading::Event::CreateAutoResetEvent(false); worker_thread_ = kernel::object_ref( new kernel::XHostThread(kernel_state, 128 * 1024, 0, [this]() { WorkerThreadMain(); @@ -131,11 +132,13 @@ X_STATUS XmaDecoder::Setup(kernel::KernelState* kernel_state) { } void XmaDecoder::WorkerThreadMain() { + uint32_t idle_loop_count = 0; while (worker_running_) { // Okay, let's loop through XMA contexts to find ones we need to decode! + bool did_work = false; for (uint32_t n = 0; n < kContextCount; n++) { XmaContext& context = contexts_[n]; - context.Work(); + did_work = context.Work() || did_work; // TODO: Need thread safety to do this. // Probably not too important though. @@ -148,19 +151,34 @@ void XmaDecoder::WorkerThreadMain() { resume_fence_.Wait(); } + if (!did_work) { + idle_loop_count++; + } else { + idle_loop_count = 0; + } + + if (idle_loop_count > 500) { + // Idle for an extended period. Introduce a 20ms wait. + xe::threading::Wait(work_event_.get(), false, + std::chrono::milliseconds(20)); + } + xe::threading::MaybeYield(); } } void XmaDecoder::Shutdown() { worker_running_ = false; - worker_fence_.Signal(); - worker_thread_.reset(); + work_event_->Set(); if (paused_) { Resume(); } + // Wait for work thread. + xe::threading::Wait(worker_thread_->thread(), false); + worker_thread_.reset(); + memory()->SystemHeapFree(registers_.context_array_ptr); } @@ -261,7 +279,7 @@ void XmaDecoder::WriteRegister(uint32_t addr, uint32_t value) { } // Signal the decoder thread to start processing. - worker_fence_.Signal(); + work_event_->Set(); } else if (r >= 0x1A40 && r <= 0x1A40 + 9 * 4) { // Context lock command. // This requests a lock by flagging the context. @@ -276,7 +294,7 @@ void XmaDecoder::WriteRegister(uint32_t addr, uint32_t value) { } // Signal the decoder thread to start processing. - worker_fence_.Signal(); + work_event_->Set(); } else if (r >= 0x1A80 && r <= 0x1A80 + 9 * 4) { // Context clear command. // This will reset the given hardware contexts. diff --git a/src/xenia/apu/xma_decoder.h b/src/xenia/apu/xma_decoder.h index 318a4dcb5..8312b820d 100644 --- a/src/xenia/apu/xma_decoder.h +++ b/src/xenia/apu/xma_decoder.h @@ -69,7 +69,7 @@ class XmaDecoder { std::atomic worker_running_ = {false}; kernel::object_ref worker_thread_; - xe::threading::Fence worker_fence_; + std::unique_ptr work_event_ = nullptr; bool paused_ = false; xe::threading::Fence pause_fence_; // Signaled when worker paused.