diff --git a/src/xenia/cpu/processor.cc b/src/xenia/cpu/processor.cc index b96cbd6f2..ffb9f2462 100644 --- a/src/xenia/cpu/processor.cc +++ b/src/xenia/cpu/processor.cc @@ -144,6 +144,7 @@ bool Processor::Setup() { backend_ = std::move(backend); frontend_ = std::move(frontend); + // DEPRECATED: will be removed. interrupt_thread_state_ = new ThreadState(this, 0, ThreadStackType::kKernelStack, 0, 128 * 1024, 0); interrupt_thread_state_->set_name("Interrupt"); @@ -373,17 +374,13 @@ void Processor::LowerIrql(Irql old_value) { reinterpret_cast(&irql_)); } -uint64_t Processor::ExecuteInterrupt(uint32_t cpu, uint32_t address, - uint64_t args[], size_t arg_count) { +uint64_t Processor::ExecuteInterrupt(uint32_t address, uint64_t args[], + size_t arg_count) { SCOPE_profile_cpu_f("cpu"); // Acquire lock on interrupt thread (we can only dispatch one at a time). std::lock_guard lock(interrupt_thread_lock_); - // Set 0x10C(r13) to the current CPU ID. - xe::store_and_swap( - memory_->TranslateVirtual(interrupt_thread_block_ + 0x10C), cpu); - // Execute interrupt. uint64_t result = Execute(interrupt_thread_state_, address, args, arg_count); diff --git a/src/xenia/cpu/processor.h b/src/xenia/cpu/processor.h index f0860ba5f..104e49c75 100644 --- a/src/xenia/cpu/processor.h +++ b/src/xenia/cpu/processor.h @@ -75,7 +75,8 @@ class Processor { Irql RaiseIrql(Irql new_value); void LowerIrql(Irql old_value); - uint64_t ExecuteInterrupt(uint32_t cpu, uint32_t address, uint64_t args[], + // DEPRECATED: will be removed. + uint64_t ExecuteInterrupt(uint32_t address, uint64_t args[], size_t arg_count); private: diff --git a/src/xenia/gpu/gl4/command_processor.cc b/src/xenia/gpu/gl4/command_processor.cc index 1c6f83ca1..84eb3788a 100644 --- a/src/xenia/gpu/gl4/command_processor.cc +++ b/src/xenia/gpu/gl4/command_processor.cc @@ -164,7 +164,7 @@ void CommandProcessor::EndTracing() { void CommandProcessor::CallInThread(std::function fn) { if (pending_fns_.empty() && - worker_thread_.get() == kernel::XThread::GetCurrentThread()) { + kernel::XThread::IsInThread(worker_thread_.get())) { fn(); } else { pending_fns_.push(std::move(fn)); diff --git a/src/xenia/gpu/gl4/gl4_graphics_system.cc b/src/xenia/gpu/gl4/gl4_graphics_system.cc index f8a890201..072a2aa23 100644 --- a/src/xenia/gpu/gl4/gl4_graphics_system.cc +++ b/src/xenia/gpu/gl4/gl4_graphics_system.cc @@ -12,6 +12,7 @@ #include "xenia/base/logging.h" #include "xenia/base/threading.h" #include "xenia/cpu/processor.h" +#include "xenia/emulator.h" #include "xenia/gpu/gl4/gl4_gpu-private.h" #include "xenia/gpu/gl4/gl4_profiler_display.h" #include "xenia/gpu/gpu-private.h" @@ -24,7 +25,7 @@ namespace gl4 { extern "C" GLEWContext* glewGetContext(); GL4GraphicsSystem::GL4GraphicsSystem(Emulator* emulator) - : GraphicsSystem(emulator), timer_queue_(nullptr), vsync_timer_(nullptr) {} + : GraphicsSystem(emulator), worker_running_(false) {} GL4GraphicsSystem::~GL4GraphicsSystem() = default; @@ -80,15 +81,26 @@ X_STATUS GL4GraphicsSystem::Setup(cpu::Processor* processor, reinterpret_cast(MMIOWriteRegisterThunk)); // 60hz vsync timer. - DWORD timer_period = 16; - if (!FLAGS_vsync) { - // DANGER a value too low here will lead to starvation! - timer_period = 4; - } - timer_queue_ = CreateTimerQueue(); - CreateTimerQueueTimer(&vsync_timer_, timer_queue_, - (WAITORTIMERCALLBACK)VsyncCallbackThunk, this, 16, - timer_period, WT_EXECUTEINPERSISTENTTHREAD); + worker_running_ = true; + worker_thread_ = + kernel::object_ref(new kernel::XHostThread( + emulator()->kernel_state(), 128 * 1024, 0, [this]() { + uint64_t vsync_duration = FLAGS_vsync ? 16 : 1; + uint64_t last_frame_time = xe::threading::ticks(); + while (worker_running_) { + uint64_t current_time = xe::threading::ticks(); + uint64_t elapsed = (current_time - last_frame_time) / + (xe::threading::ticks_per_second() / 1000); + if (elapsed >= vsync_duration) { + MarkVblank(); + last_frame_time = current_time; + } + Sleep(1); + } + return 0; + })); + worker_thread_->set_name("GL4 Vsync"); + worker_thread_->Create(); if (FLAGS_trace_gpu_stream) { BeginTracing(); @@ -100,8 +112,9 @@ X_STATUS GL4GraphicsSystem::Setup(cpu::Processor* processor, void GL4GraphicsSystem::Shutdown() { EndTracing(); - DeleteTimerQueueTimer(timer_queue_, vsync_timer_, nullptr); - DeleteTimerQueue(timer_queue_); + worker_running_ = false; + worker_thread_->Wait(0, 0, 0, nullptr); + worker_thread_.reset(); command_processor_->Shutdown(); diff --git a/src/xenia/gpu/gl4/gl4_graphics_system.h b/src/xenia/gpu/gl4/gl4_graphics_system.h index 6eeec1310..5cab2e560 100644 --- a/src/xenia/gpu/gl4/gl4_graphics_system.h +++ b/src/xenia/gpu/gl4/gl4_graphics_system.h @@ -16,6 +16,7 @@ #include "xenia/gpu/gl4/wgl_control.h" #include "xenia/gpu/graphics_system.h" #include "xenia/gpu/register_file.h" +#include "xenia/kernel/objects/xthread.h" namespace xe { namespace gpu { @@ -60,16 +61,13 @@ class GL4GraphicsSystem : public GraphicsSystem { uint64_t value) { gs->WriteRegister(addr, value); } - static void __stdcall VsyncCallbackThunk(GL4GraphicsSystem* gs, BOOLEAN) { - gs->MarkVblank(); - } RegisterFile register_file_; std::unique_ptr command_processor_; std::unique_ptr control_; - HANDLE timer_queue_; - HANDLE vsync_timer_; + std::atomic worker_running_; + kernel::object_ref worker_thread_; }; } // namespace gl4 diff --git a/src/xenia/gpu/graphics_system.cc b/src/xenia/gpu/graphics_system.cc index 09d21838d..e36b75825 100644 --- a/src/xenia/gpu/graphics_system.cc +++ b/src/xenia/gpu/graphics_system.cc @@ -13,6 +13,7 @@ #include "xenia/base/math.h" #include "xenia/cpu/processor.h" #include "xenia/gpu/gpu-private.h" +#include "xenia/kernel/objects/xthread.h" namespace xe { namespace gpu { @@ -53,18 +54,21 @@ void GraphicsSystem::DispatchInterruptCallback(uint32_t source, uint32_t cpu) { return; } + auto thread = kernel::XThread::GetCurrentThread(); + assert_not_null(thread); + // Pick a CPU, if needed. We're going to guess 2. Because. if (cpu == 0xFFFFFFFF) { cpu = 2; } + thread->SetActiveCpu(cpu); // XELOGGPU("Dispatching GPU interrupt at %.8X w/ mode %d on cpu %d", // interrupt_callback_, source, cpu); - // NOTE: we may be executing in some random thread. uint64_t args[] = {source, interrupt_callback_data_}; - processor_->ExecuteInterrupt(cpu, interrupt_callback_, args, - xe::countof(args)); + processor_->Execute(thread->thread_state(), interrupt_callback_, args, + xe::countof(args)); } } // namespace gpu diff --git a/src/xenia/kernel/objects/xthread.cc b/src/xenia/kernel/objects/xthread.cc index 70d545313..a38fe5fb7 100644 --- a/src/xenia/kernel/objects/xthread.cc +++ b/src/xenia/kernel/objects/xthread.cc @@ -35,7 +35,6 @@ using namespace xe::cpu; uint32_t next_xthread_id = 0; thread_local XThread* current_thread_tls = nullptr; xe::mutex critical_region_; -XThread* shared_kernel_thread_ = 0; XThread::XThread(KernelState* kernel_state, uint32_t stack_size, uint32_t xapi_thread_startup, uint32_t start_address, @@ -97,18 +96,14 @@ XThread::~XThread() { } } +bool XThread::IsInThread(XThread* other) { + return current_thread_tls == other; +} + XThread* XThread::GetCurrentThread() { XThread* thread = current_thread_tls; if (!thread) { - // Assume this is some shared interrupt thread/etc. - XThread::EnterCriticalRegion(); - thread = shared_kernel_thread_; - if (!thread) { - thread = new XThread(KernelState::shared(), 256 * 1024, 0, 0, 0, 0); - shared_kernel_thread_ = thread; - current_thread_tls = thread; - } - XThread::LeaveCriticalRegion(); + assert_always("Attempting to use kernel stuff from a non-kernel thread"); } return thread; } @@ -496,7 +491,7 @@ void XThread::DeliverAPCs(void* data) { apc_address, thread->scratch_address_ + 0, thread->scratch_address_ + 4, thread->scratch_address_ + 8, thread->scratch_address_ + 12, }; - processor->ExecuteInterrupt(0, kernel_routine, kernel_args, + processor->ExecuteInterrupt(kernel_routine, kernel_args, xe::countof(kernel_args)); normal_routine = xe::load_and_swap(scratch_ptr + 0); normal_context = xe::load_and_swap(scratch_ptr + 4); @@ -509,7 +504,7 @@ void XThread::DeliverAPCs(void* data) { thread->UnlockApc(); // normal_routine(normal_context, system_arg1, system_arg2) uint64_t normal_args[] = {normal_context, system_arg1, system_arg2}; - processor->ExecuteInterrupt(0, normal_routine, normal_args, + processor->ExecuteInterrupt(normal_routine, normal_args, xe::countof(normal_args)); thread->LockApc(); } @@ -534,7 +529,7 @@ void XThread::RundownAPCs() { if (rundown_routine) { // rundown_routine(apc) uint64_t args[] = {apc_address}; - kernel_state()->processor()->ExecuteInterrupt(0, rundown_routine, args, + kernel_state()->processor()->ExecuteInterrupt(rundown_routine, args, xe::countof(args)); } } @@ -582,6 +577,11 @@ void XThread::SetAffinity(uint32_t affinity) { } } +void XThread::SetActiveCpu(uint32_t cpu_index) { + uint8_t* pcr = memory()->TranslateVirtual(pcr_address_); + xe::store_and_swap(pcr + 0x10C, cpu_index); +} + X_STATUS XThread::Resume(uint32_t* out_suspend_count) { DWORD result = ResumeThread(thread_handle_); if (result >= 0) { diff --git a/src/xenia/kernel/objects/xthread.h b/src/xenia/kernel/objects/xthread.h index 7dceb697a..b0fd97c2f 100644 --- a/src/xenia/kernel/objects/xthread.h +++ b/src/xenia/kernel/objects/xthread.h @@ -31,6 +31,7 @@ class XThread : public XObject { uint32_t start_context, uint32_t creation_flags); virtual ~XThread(); + static bool IsInThread(XThread* other); static XThread* GetCurrentThread(); static uint32_t GetCurrentThreadHandle(); static uint32_t GetCurrentThreadId(const uint8_t* pcr); @@ -61,6 +62,7 @@ class XThread : public XObject { int32_t QueryPriority(); void SetPriority(int32_t increment); void SetAffinity(uint32_t affinity); + void SetActiveCpu(uint32_t cpu_index); X_STATUS Resume(uint32_t* out_suspend_count = nullptr); X_STATUS Suspend(uint32_t* out_suspend_count); diff --git a/src/xenia/kernel/xboxkrnl_threading.cc b/src/xenia/kernel/xboxkrnl_threading.cc index b25035952..0246833e4 100644 --- a/src/xenia/kernel/xboxkrnl_threading.cc +++ b/src/xenia/kernel/xboxkrnl_threading.cc @@ -990,6 +990,7 @@ SHIM_CALL KfAcquireSpinLock_shim(PPCContext* ppc_state, KernelState* state) { while (!xe::atomic_cas(0, 1, lock)) { // Spin! // TODO(benvanik): error on deadlock? + YieldProcessor(); } // Raise IRQL to DISPATCH.