Switching vsync to custom thread.

This commit is contained in:
Ben Vanik 2015-05-25 21:12:27 -07:00
parent e3ddcd44e7
commit 41cee3d337
9 changed files with 57 additions and 41 deletions

View File

@ -144,6 +144,7 @@ bool Processor::Setup() {
backend_ = std::move(backend); backend_ = std::move(backend);
frontend_ = std::move(frontend); frontend_ = std::move(frontend);
// DEPRECATED: will be removed.
interrupt_thread_state_ = interrupt_thread_state_ =
new ThreadState(this, 0, ThreadStackType::kKernelStack, 0, 128 * 1024, 0); new ThreadState(this, 0, ThreadStackType::kKernelStack, 0, 128 * 1024, 0);
interrupt_thread_state_->set_name("Interrupt"); interrupt_thread_state_->set_name("Interrupt");
@ -373,17 +374,13 @@ void Processor::LowerIrql(Irql old_value) {
reinterpret_cast<volatile uint32_t*>(&irql_)); reinterpret_cast<volatile uint32_t*>(&irql_));
} }
uint64_t Processor::ExecuteInterrupt(uint32_t cpu, uint32_t address, uint64_t Processor::ExecuteInterrupt(uint32_t address, uint64_t args[],
uint64_t args[], size_t arg_count) { size_t arg_count) {
SCOPE_profile_cpu_f("cpu"); SCOPE_profile_cpu_f("cpu");
// Acquire lock on interrupt thread (we can only dispatch one at a time). // Acquire lock on interrupt thread (we can only dispatch one at a time).
std::lock_guard<xe::mutex> lock(interrupt_thread_lock_); std::lock_guard<xe::mutex> lock(interrupt_thread_lock_);
// Set 0x10C(r13) to the current CPU ID.
xe::store_and_swap<uint8_t>(
memory_->TranslateVirtual(interrupt_thread_block_ + 0x10C), cpu);
// Execute interrupt. // Execute interrupt.
uint64_t result = Execute(interrupt_thread_state_, address, args, arg_count); uint64_t result = Execute(interrupt_thread_state_, address, args, arg_count);

View File

@ -75,7 +75,8 @@ class Processor {
Irql RaiseIrql(Irql new_value); Irql RaiseIrql(Irql new_value);
void LowerIrql(Irql old_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); size_t arg_count);
private: private:

View File

@ -164,7 +164,7 @@ void CommandProcessor::EndTracing() {
void CommandProcessor::CallInThread(std::function<void()> fn) { void CommandProcessor::CallInThread(std::function<void()> fn) {
if (pending_fns_.empty() && if (pending_fns_.empty() &&
worker_thread_.get() == kernel::XThread::GetCurrentThread()) { kernel::XThread::IsInThread(worker_thread_.get())) {
fn(); fn();
} else { } else {
pending_fns_.push(std::move(fn)); pending_fns_.push(std::move(fn));

View File

@ -12,6 +12,7 @@
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
#include "xenia/base/threading.h" #include "xenia/base/threading.h"
#include "xenia/cpu/processor.h" #include "xenia/cpu/processor.h"
#include "xenia/emulator.h"
#include "xenia/gpu/gl4/gl4_gpu-private.h" #include "xenia/gpu/gl4/gl4_gpu-private.h"
#include "xenia/gpu/gl4/gl4_profiler_display.h" #include "xenia/gpu/gl4/gl4_profiler_display.h"
#include "xenia/gpu/gpu-private.h" #include "xenia/gpu/gpu-private.h"
@ -24,7 +25,7 @@ namespace gl4 {
extern "C" GLEWContext* glewGetContext(); extern "C" GLEWContext* glewGetContext();
GL4GraphicsSystem::GL4GraphicsSystem(Emulator* emulator) GL4GraphicsSystem::GL4GraphicsSystem(Emulator* emulator)
: GraphicsSystem(emulator), timer_queue_(nullptr), vsync_timer_(nullptr) {} : GraphicsSystem(emulator), worker_running_(false) {}
GL4GraphicsSystem::~GL4GraphicsSystem() = default; GL4GraphicsSystem::~GL4GraphicsSystem() = default;
@ -80,15 +81,26 @@ X_STATUS GL4GraphicsSystem::Setup(cpu::Processor* processor,
reinterpret_cast<cpu::MMIOWriteCallback>(MMIOWriteRegisterThunk)); reinterpret_cast<cpu::MMIOWriteCallback>(MMIOWriteRegisterThunk));
// 60hz vsync timer. // 60hz vsync timer.
DWORD timer_period = 16; worker_running_ = true;
if (!FLAGS_vsync) { worker_thread_ =
// DANGER a value too low here will lead to starvation! kernel::object_ref<kernel::XHostThread>(new kernel::XHostThread(
timer_period = 4; emulator()->kernel_state(), 128 * 1024, 0, [this]() {
} uint64_t vsync_duration = FLAGS_vsync ? 16 : 1;
timer_queue_ = CreateTimerQueue(); uint64_t last_frame_time = xe::threading::ticks();
CreateTimerQueueTimer(&vsync_timer_, timer_queue_, while (worker_running_) {
(WAITORTIMERCALLBACK)VsyncCallbackThunk, this, 16, uint64_t current_time = xe::threading::ticks();
timer_period, WT_EXECUTEINPERSISTENTTHREAD); 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) { if (FLAGS_trace_gpu_stream) {
BeginTracing(); BeginTracing();
@ -100,8 +112,9 @@ X_STATUS GL4GraphicsSystem::Setup(cpu::Processor* processor,
void GL4GraphicsSystem::Shutdown() { void GL4GraphicsSystem::Shutdown() {
EndTracing(); EndTracing();
DeleteTimerQueueTimer(timer_queue_, vsync_timer_, nullptr); worker_running_ = false;
DeleteTimerQueue(timer_queue_); worker_thread_->Wait(0, 0, 0, nullptr);
worker_thread_.reset();
command_processor_->Shutdown(); command_processor_->Shutdown();

View File

@ -16,6 +16,7 @@
#include "xenia/gpu/gl4/wgl_control.h" #include "xenia/gpu/gl4/wgl_control.h"
#include "xenia/gpu/graphics_system.h" #include "xenia/gpu/graphics_system.h"
#include "xenia/gpu/register_file.h" #include "xenia/gpu/register_file.h"
#include "xenia/kernel/objects/xthread.h"
namespace xe { namespace xe {
namespace gpu { namespace gpu {
@ -60,16 +61,13 @@ class GL4GraphicsSystem : public GraphicsSystem {
uint64_t value) { uint64_t value) {
gs->WriteRegister(addr, value); gs->WriteRegister(addr, value);
} }
static void __stdcall VsyncCallbackThunk(GL4GraphicsSystem* gs, BOOLEAN) {
gs->MarkVblank();
}
RegisterFile register_file_; RegisterFile register_file_;
std::unique_ptr<CommandProcessor> command_processor_; std::unique_ptr<CommandProcessor> command_processor_;
std::unique_ptr<WGLControl> control_; std::unique_ptr<WGLControl> control_;
HANDLE timer_queue_; std::atomic<bool> worker_running_;
HANDLE vsync_timer_; kernel::object_ref<kernel::XHostThread> worker_thread_;
}; };
} // namespace gl4 } // namespace gl4

View File

@ -13,6 +13,7 @@
#include "xenia/base/math.h" #include "xenia/base/math.h"
#include "xenia/cpu/processor.h" #include "xenia/cpu/processor.h"
#include "xenia/gpu/gpu-private.h" #include "xenia/gpu/gpu-private.h"
#include "xenia/kernel/objects/xthread.h"
namespace xe { namespace xe {
namespace gpu { namespace gpu {
@ -53,18 +54,21 @@ void GraphicsSystem::DispatchInterruptCallback(uint32_t source, uint32_t cpu) {
return; return;
} }
auto thread = kernel::XThread::GetCurrentThread();
assert_not_null(thread);
// Pick a CPU, if needed. We're going to guess 2. Because. // Pick a CPU, if needed. We're going to guess 2. Because.
if (cpu == 0xFFFFFFFF) { if (cpu == 0xFFFFFFFF) {
cpu = 2; cpu = 2;
} }
thread->SetActiveCpu(cpu);
// XELOGGPU("Dispatching GPU interrupt at %.8X w/ mode %d on cpu %d", // XELOGGPU("Dispatching GPU interrupt at %.8X w/ mode %d on cpu %d",
// interrupt_callback_, source, cpu); // interrupt_callback_, source, cpu);
// NOTE: we may be executing in some random thread.
uint64_t args[] = {source, interrupt_callback_data_}; uint64_t args[] = {source, interrupt_callback_data_};
processor_->ExecuteInterrupt(cpu, interrupt_callback_, args, processor_->Execute(thread->thread_state(), interrupt_callback_, args,
xe::countof(args)); xe::countof(args));
} }
} // namespace gpu } // namespace gpu

View File

@ -35,7 +35,6 @@ using namespace xe::cpu;
uint32_t next_xthread_id = 0; uint32_t next_xthread_id = 0;
thread_local XThread* current_thread_tls = nullptr; thread_local XThread* current_thread_tls = nullptr;
xe::mutex critical_region_; xe::mutex critical_region_;
XThread* shared_kernel_thread_ = 0;
XThread::XThread(KernelState* kernel_state, uint32_t stack_size, XThread::XThread(KernelState* kernel_state, uint32_t stack_size,
uint32_t xapi_thread_startup, uint32_t start_address, 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* XThread::GetCurrentThread() {
XThread* thread = current_thread_tls; XThread* thread = current_thread_tls;
if (!thread) { if (!thread) {
// Assume this is some shared interrupt thread/etc. assert_always("Attempting to use kernel stuff from a non-kernel thread");
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();
} }
return thread; return thread;
} }
@ -496,7 +491,7 @@ void XThread::DeliverAPCs(void* data) {
apc_address, thread->scratch_address_ + 0, thread->scratch_address_ + 4, apc_address, thread->scratch_address_ + 0, thread->scratch_address_ + 4,
thread->scratch_address_ + 8, thread->scratch_address_ + 12, 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)); xe::countof(kernel_args));
normal_routine = xe::load_and_swap<uint32_t>(scratch_ptr + 0); normal_routine = xe::load_and_swap<uint32_t>(scratch_ptr + 0);
normal_context = xe::load_and_swap<uint32_t>(scratch_ptr + 4); normal_context = xe::load_and_swap<uint32_t>(scratch_ptr + 4);
@ -509,7 +504,7 @@ void XThread::DeliverAPCs(void* data) {
thread->UnlockApc(); thread->UnlockApc();
// normal_routine(normal_context, system_arg1, system_arg2) // normal_routine(normal_context, system_arg1, system_arg2)
uint64_t normal_args[] = {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)); xe::countof(normal_args));
thread->LockApc(); thread->LockApc();
} }
@ -534,7 +529,7 @@ void XThread::RundownAPCs() {
if (rundown_routine) { if (rundown_routine) {
// rundown_routine(apc) // rundown_routine(apc)
uint64_t args[] = {apc_address}; uint64_t args[] = {apc_address};
kernel_state()->processor()->ExecuteInterrupt(0, rundown_routine, args, kernel_state()->processor()->ExecuteInterrupt(rundown_routine, args,
xe::countof(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<uint8_t>(pcr + 0x10C, cpu_index);
}
X_STATUS XThread::Resume(uint32_t* out_suspend_count) { X_STATUS XThread::Resume(uint32_t* out_suspend_count) {
DWORD result = ResumeThread(thread_handle_); DWORD result = ResumeThread(thread_handle_);
if (result >= 0) { if (result >= 0) {

View File

@ -31,6 +31,7 @@ class XThread : public XObject {
uint32_t start_context, uint32_t creation_flags); uint32_t start_context, uint32_t creation_flags);
virtual ~XThread(); virtual ~XThread();
static bool IsInThread(XThread* other);
static XThread* GetCurrentThread(); static XThread* GetCurrentThread();
static uint32_t GetCurrentThreadHandle(); static uint32_t GetCurrentThreadHandle();
static uint32_t GetCurrentThreadId(const uint8_t* pcr); static uint32_t GetCurrentThreadId(const uint8_t* pcr);
@ -61,6 +62,7 @@ class XThread : public XObject {
int32_t QueryPriority(); int32_t QueryPriority();
void SetPriority(int32_t increment); void SetPriority(int32_t increment);
void SetAffinity(uint32_t affinity); void SetAffinity(uint32_t affinity);
void SetActiveCpu(uint32_t cpu_index);
X_STATUS Resume(uint32_t* out_suspend_count = nullptr); X_STATUS Resume(uint32_t* out_suspend_count = nullptr);
X_STATUS Suspend(uint32_t* out_suspend_count); X_STATUS Suspend(uint32_t* out_suspend_count);

View File

@ -990,6 +990,7 @@ SHIM_CALL KfAcquireSpinLock_shim(PPCContext* ppc_state, KernelState* state) {
while (!xe::atomic_cas(0, 1, lock)) { while (!xe::atomic_cas(0, 1, lock)) {
// Spin! // Spin!
// TODO(benvanik): error on deadlock? // TODO(benvanik): error on deadlock?
YieldProcessor();
} }
// Raise IRQL to DISPATCH. // Raise IRQL to DISPATCH.