diff --git a/rpcs3/Emu/RSX/RSXFIFO.cpp b/rpcs3/Emu/RSX/RSXFIFO.cpp index 25d190cb7e..87add68d35 100644 --- a/rpcs3/Emu/RSX/RSXFIFO.cpp +++ b/rpcs3/Emu/RSX/RSXFIFO.cpp @@ -477,20 +477,21 @@ namespace rsx fmt::throw_exception("Unexpected command 0x%x" HERE, cmd); } - if (performance_counters.state != FIFO_state::running) + if (const auto state = performance_counters.state; + state != FIFO_state::running) { - //Update performance counters with time spent in idle mode - performance_counters.idle_time += (get_system_time() - performance_counters.FIFO_idle_timestamp); + performance_counters.state = FIFO_state::running; - if (performance_counters.state == FIFO_state::spinning) + // Hack: Delay FIFO wake-up according to setting + // NOTE: The typical spin setup is a NOP followed by a jump-to-self + // NOTE: There is a small delay when the jump address is dynamically edited by cell + if (state != FIFO_state::nop) { - //TODO: Properly simulate FIFO wake delay. - //NOTE: The typical spin setup is a NOP followed by a jump-to-self - //NOTE: There is a small delay when the jump address is dynamically edited by cell - busy_wait(3000); + fifo_wake_delay(); } - performance_counters.state = FIFO_state::running; + // Update performance counters with time spent in idle mode + performance_counters.idle_time += (get_system_time() - performance_counters.FIFO_idle_timestamp); } do diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index e4b741f938..16f89b7b3a 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -2273,6 +2273,61 @@ namespace rsx } } + void thread::fifo_wake_delay(u64 div) + { + // TODO: Nanoseconds accuracy + u64 remaining = g_cfg.video.driver_wakeup_delay; + + if (!remaining) + { + return; + } + + // Some cases do not need full delay + remaining = ::aligned_div(remaining, div); + const u64 until = get_system_time() + remaining; + + while (true) + { +#ifdef __linux__ + // NOTE: Assumption that timer initialization has succeeded + u64 host_min_quantum = remaining <= 1000 ? 10 : 50; +#else + // Host scheduler quantum for windows (worst case) + // NOTE: On ps3 this function has very high accuracy + constexpr u64 host_min_quantum = 500; +#endif + if (remaining >= host_min_quantum) + { +#ifdef __linux__ + // Do not wait for the last quantum to avoid loss of accuracy + thread_ctrl::wait_for(remaining - ((remaining % host_min_quantum) + host_min_quantum), false); +#else + // Wait on multiple of min quantum for large durations to avoid overloading low thread cpus + thread_ctrl::wait_for(remaining - (remaining % host_min_quantum), false); +#endif + } + // TODO: Determine best value for yield delay + else if (remaining >= host_min_quantum / 2) + { + std::this_thread::yield(); + } + else + { + busy_wait(100); + } + + const u64 current = get_system_time(); + + if (current >= until) + { + break; + } + + remaining = until - current; + } + } + u32 thread::get_fifo_cmd() { // Last fifo cmd for logging and utility diff --git a/rpcs3/Emu/RSX/RSXThread.h b/rpcs3/Emu/RSX/RSXThread.h index f935f0af80..21036eef0a 100644 --- a/rpcs3/Emu/RSX/RSXThread.h +++ b/rpcs3/Emu/RSX/RSXThread.h @@ -607,6 +607,7 @@ namespace rsx atomic_t external_interrupt_ack{ false }; void flush_fifo(); void recover_fifo(); + void fifo_wake_delay(u64 div = 1); u32 get_fifo_cmd(); // Performance approximation counters diff --git a/rpcs3/Emu/RSX/rsx_methods.cpp b/rpcs3/Emu/RSX/rsx_methods.cpp index d30e8be3d1..90fdd74f36 100644 --- a/rpcs3/Emu/RSX/rsx_methods.cpp +++ b/rpcs3/Emu/RSX/rsx_methods.cpp @@ -76,9 +76,23 @@ namespace rsx const auto& sema = vm::_ref>(addr); // TODO: Remove vblank semaphore hack - if (sema == arg || addr == rsx->device_addr + 0x30) return; + if (addr == rsx->device_addr + 0x30) return; - rsx->flush_fifo(); + if (sema == arg) + { + // Flip semaphore doesnt need wake-up delay + if (addr != rsx->label_addr + 0x10) + { + rsx->flush_fifo(); + rsx->fifo_wake_delay(2); + } + + return; + } + else + { + rsx->flush_fifo(); + } u64 start = get_system_time(); while (sema != arg) @@ -113,6 +127,7 @@ namespace rsx std::this_thread::yield(); } + rsx->fifo_wake_delay(); rsx->performance_counters.idle_time += (get_system_time() - start); } diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index 065f728388..cd1601fb28 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -502,7 +502,8 @@ struct cfg_root : cfg::node cfg::_int<50, 800> resolution_scale_percent{this, "Resolution Scale", 100}; cfg::_int<0, 16> anisotropic_level_override{this, "Anisotropic Filter Override", 0}; cfg::_int<1, 1024> min_scalable_dimension{this, "Minimum Scalable Dimension", 16}; - cfg::_int<0, 30000000> driver_recovery_timeout{this, "Driver Recovery Timeout", 1000000}; + cfg::_int<0, 30000000> driver_recovery_timeout{this, "Driver Recovery Timeout", 1000000, true}; + cfg::_int<0, 16667> driver_wakeup_delay{this, "Driver Wake-Up Delay", 1, true}; cfg::_int<1, 500> vblank_rate{this, "Vblank Rate", 60}; // Changing this from 60 may affect game speed in unexpected ways struct node_vk : cfg::node