mirror of
https://github.com/ARMSX2/ARMSX3.git
synced 2026-08-24 16:58:52 -07:00
Stop paying per-packet and per-argument costs in the FIFO loop
Three changes to the same loop, all measured against 36728 packets and 48737 dispatches per frame in Sonic '06. GET is published on a bounded lag rather than every packet. It is a release store into guest DMA memory, and get shares a 64-byte line with put which the guest PPU writes from another CPU cluster, so each publish was a cross-cluster coherence miss. The guest reads GET to size its free ring space and is far ahead of us here, FIFO stalls measuring 0.1 a frame, so lag is invisible to it. Anything that can idle or block publishes immediately: the put wait in inc_get, the NOP path, and set_get. The FIFO accuracy setting is snapshotted once per packet instead of being read per argument. Reading it goes through a seq_cst atomic load, which on ARM64 is an ldar the compiler cannot hoist out of the loop. The again poll is relaxed. That flag is only ever set by this thread, by the handler invoked immediately before, so sequential consistency buys nothing and cost another ldar per dispatch.
This commit is contained in:
@@ -37,6 +37,21 @@ namespace rsx
|
||||
|
||||
void FIFO_control::sync_get() const
|
||||
{
|
||||
// Every 8th packet. The guest reads GET to work out how much ring space is
|
||||
// free, and it is far ahead of us here (FIFO stalls measure 0.1/frame), so a
|
||||
// bounded lag is invisible to it. Anything that can idle or block publishes
|
||||
// immediately via sync_get_force so a waiting producer is never held up.
|
||||
if (++m_get_sync_counter & 7)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_ctrl->get.release(m_internal_get);
|
||||
}
|
||||
|
||||
void FIFO_control::sync_get_force() const
|
||||
{
|
||||
m_get_sync_counter = 0;
|
||||
m_ctrl->get.release(m_internal_get);
|
||||
}
|
||||
|
||||
@@ -58,7 +73,7 @@ namespace rsx
|
||||
{
|
||||
// NOTE: Only supposed to be invoked to wait for a single arg on command[0] (4 bytes)
|
||||
// Wait for put to allow us to procceed execution
|
||||
sync_get();
|
||||
sync_get_force();
|
||||
invalidate_cache();
|
||||
|
||||
while (read_put() == m_internal_get && !Emu.IsStopped())
|
||||
@@ -257,7 +272,7 @@ namespace rsx
|
||||
return {};
|
||||
}
|
||||
|
||||
if (g_cfg.core.rsx_fifo_accuracy)
|
||||
if (m_accurate_fetch)
|
||||
{
|
||||
// Return a pointer to the cache storage with confined access
|
||||
const u32 cache_offset_in_words = (m_internal_get - m_cache_addr) / 4;
|
||||
@@ -365,6 +380,8 @@ namespace rsx
|
||||
|
||||
void FIFO_control::read(register_pair& data)
|
||||
{
|
||||
m_accurate_fetch = !!g_cfg.core.rsx_fifo_accuracy;
|
||||
|
||||
if (m_remaining_commands)
|
||||
{
|
||||
// Previous block aborted to wait for PUT pointer
|
||||
@@ -391,7 +408,7 @@ namespace rsx
|
||||
m_memwatch_cmp = 0;
|
||||
}
|
||||
|
||||
if (!g_cfg.core.rsx_fifo_accuracy) [[ likely ]]
|
||||
if (!m_accurate_fetch) [[ likely ]]
|
||||
{
|
||||
const u32 put = read_put();
|
||||
|
||||
@@ -684,6 +701,9 @@ namespace rsx
|
||||
performance_counters.state = FIFO::state::nop;
|
||||
}
|
||||
|
||||
// Going idle: publish GET now rather than carrying up to seven packets of
|
||||
// lag into a period where the producer may be waiting on ring space.
|
||||
fifo_ctrl->sync_get_force();
|
||||
return;
|
||||
}
|
||||
case FIFO::FIFO_EMPTY:
|
||||
@@ -925,7 +945,9 @@ namespace rsx
|
||||
{
|
||||
method(m_ctx, reg, value);
|
||||
|
||||
if (state & cpu_flag::again)
|
||||
// Relaxed: `again` is only set by this thread, by the handler just called.
|
||||
// The seq_cst default is an ldar on ARM64 for no benefit here.
|
||||
if (state.observe() & cpu_flag::again)
|
||||
{
|
||||
m_ctx->register_state->decode(reg, m_ctx->register_state->latch);
|
||||
break;
|
||||
|
||||
@@ -190,7 +190,18 @@ namespace rsx
|
||||
|
||||
u32 get_pos() const { return m_internal_get; }
|
||||
u32 last_cmd() const { return m_cmd; }
|
||||
// Publishing GET is a release store into guest DMA memory, and `get` shares a
|
||||
// 64-byte line with `put` which the guest PPU writes from another CPU cluster. At
|
||||
// ~36700 packets/frame that is a cross-cluster coherence miss per packet. Bounded
|
||||
// lag instead, with sync_get_force on every path that can idle or block.
|
||||
void sync_get() const;
|
||||
void sync_get_force() const;
|
||||
mutable u32 m_get_sync_counter = 0;
|
||||
|
||||
// Snapshot of g_cfg.core.rsx_fifo_accuracy, refreshed once per packet. Reading the
|
||||
// config goes through a seq_cst atomic load, an ldar on ARM64 the compiler cannot
|
||||
// hoist, and it was consulted once per FIFO argument.
|
||||
bool m_accurate_fetch = false;
|
||||
std::span<const u32> get_current_arg_ptr(u32 length_in_words) const;
|
||||
u32 get_remaining_args_count() const { return m_remaining_commands; }
|
||||
void restore_state(u32 cmd, u32 count);
|
||||
|
||||
Reference in New Issue
Block a user