rsx: Fix FIFO GET updates (Atomic FIFO exclusive fix)

This commit is contained in:
Elad
2026-10-02 04:56:21 +03:00
parent 982337435a
commit a3dc7e6af4
9 changed files with 89 additions and 46 deletions
+7 -4
View File
@@ -17,7 +17,7 @@ namespace rsx
// Write ref+get (get will be written again with the same value at command end)
auto& dma = *vm::_ptr<RsxDmaControl>(RSX(ctx)->dma_address);
dma.get.store(RSX(ctx)->fifo_ctrl->get_pos() + 4);
//dma.get.store(RSX(ctx)->fifo_ctrl->get_pos() + 4);
dma.ref.store(arg);
}
@@ -29,6 +29,9 @@ namespace rsx
// Syncronization point, may be associated with memory changes without actually changing addresses
RSX(ctx)->m_graphics_state |= rsx::pipeline_state::fragment_program_needs_rehash;
// Ensure atomic seq-cst memory ordering for FIFO GET update
atomic_fence_seq_cst();
const auto& sema = vm::_ref<RsxSemaphore>(addr);
const auto& atomic_sema = vm::_ref<atomic_t<RsxSemaphore>>(addr);
@@ -37,7 +40,7 @@ namespace rsx
// Flip semaphore doesnt need wake-up delay
if (addr != RSX(ctx)->label_addr + 0x10)
{
RSX(ctx)->flush_fifo();
//RSX(ctx)->flush_fifo();
RSX(ctx)->fifo_wake_delay(2);
}
@@ -45,7 +48,7 @@ namespace rsx
}
else
{
RSX(ctx)->flush_fifo();
//RSX(ctx)->flush_fifo();
}
u64 start = get_system_time();
@@ -134,7 +137,7 @@ namespace rsx
arg = 1;
}
util::write_gcm_label<false, true>(ctx, reg, addr, arg, RSX(ctx)->fifo_ctrl->get_pos() + 4);
util::write_gcm_label<false, true>(ctx, reg, addr, arg);
}
}
}
+3 -3
View File
@@ -757,11 +757,11 @@ namespace rsx
if (g_cfg.video.strict_rendering_mode) [[ unlikely ]]
{
util::write_gcm_label<true, true>(ctx, reg, addr, arg, RSX(ctx)->fifo_ctrl->get_pos() + 4);
util::write_gcm_label<true, true>(ctx, reg, addr, arg);
}
else
{
util::write_gcm_label<true, false>(ctx, reg, addr, arg, RSX(ctx)->fifo_ctrl->get_pos() + 4);
util::write_gcm_label<true, false>(ctx, reg, addr, arg);
}
}
@@ -786,7 +786,7 @@ namespace rsx
}
const u32 val = (arg & 0xff00ff00) | ((arg & 0xff) << 16) | ((arg >> 16) & 0xff);
util::write_gcm_label<true, true>(ctx, reg, addr, val, RSX(ctx)->fifo_ctrl->get_pos() + 4);
util::write_gcm_label<true, true>(ctx, reg, addr, val);
}
void sync(context* ctx, u32, u32)
+3 -3
View File
@@ -11,10 +11,10 @@ namespace rsx
namespace util
{
template <bool FlushDMA, bool FlushPipe>
static void write_gcm_label(context* ctx, u32 type, u32 address, u32 data, u32 post_fifo_get)
static void write_gcm_label(context* ctx, u32 type, u32 address, u32 data)
{
// Advance FIFO GET before writing labels
ensure(ctx->rsxthr->ctrl)->get.store(post_fifo_get);
// Ensure atomic seq-cst memory ordering for FIFO GET updates
atomic_fence_seq_cst();
const bool is_flip_sema = (address == (RSX(ctx)->label_addr + 0x10) || address == (RSX(ctx)->device_addr + 0x30));
if (!is_flip_sema)
+44 -27
View File
@@ -36,31 +36,39 @@ namespace rsx
void FIFO_control::sync_get() const
{
m_ctrl->get.release(m_internal_get);
if (m_fifo_pos - m_cache_addr <= m_cache_size)
{
// Atomic FIFO only path
m_ctrl->get.release(m_cache_addr + m_cache_size);
}
else
{
m_ctrl->get.release(m_fifo_pos);
}
}
void FIFO_control::restore_state(u32 cmd, u32 count)
void FIFO_control::restore_state(u32 cmd, u32 count, u32 position)
{
m_cmd = cmd;
m_command_inc = ((m_cmd & RSX_METHOD_NON_INCREMENT_CMD_MASK) == RSX_METHOD_NON_INCREMENT_CMD) ? 0 : 4;
m_remaining_commands = count;
m_internal_get = m_ctrl->get - 4;
m_args_ptr = m_iotable->get_addr(m_internal_get);
m_fifo_pos = position - 4;
m_args_ptr = m_iotable->get_addr(m_fifo_pos);
m_command_reg = (m_cmd & 0xffff) + m_command_inc * (((m_cmd >> 18) - count) & 0x7ff) - m_command_inc;
}
void FIFO_control::inc_get(bool wait)
{
m_internal_get += 4;
m_fifo_pos += 4;
if (wait && read_put<false>() == m_internal_get)
if (!m_cache_size && wait && read_put<false>() == m_fifo_pos)
{
// 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();
invalidate_cache();
while (read_put() == m_internal_get && !Emu.IsStopped())
while (read_put() == m_fifo_pos && !Emu.IsStopped())
{
m_thread->cpu_wait({});
}
@@ -200,6 +208,9 @@ namespace rsx
i = (i - 1) % 8;
}
}
// Update FIFO GET
sync_get();
}
const auto ret = read_from_ptr_unsafe<be_t<u32>>(+m_cache[0], addr - m_cache_addr);
@@ -210,15 +221,16 @@ namespace rsx
{
invalidate_cache();
if (spin_cmd && m_ctrl->get == get)
if (spin_cmd && m_fifo_pos == get)
{
m_memwatch_addr = get;
m_memwatch_cmp = spin_cmd;
m_ctrl->get.release(get);
return;
}
// Update ctrl registers
m_ctrl->get.release(m_internal_get = get);
m_ctrl->get.release(m_fifo_pos = get);
m_remaining_commands = 0;
}
@@ -234,7 +246,7 @@ namespace rsx
if (g_cfg.core.rsx_fifo_accuracy)
{
// Return a pointer to the cache storage with confined access
const u32 cache_offset_in_words = (m_internal_get - m_cache_addr) / 4;
const u32 cache_offset_in_words = (m_fifo_pos - m_cache_addr) / 4;
const u32 cache_size_in_words = m_cache_size / 4;
return {reinterpret_cast<const u32*>(&m_cache) + cache_offset_in_words, cache_size_in_words - cache_offset_in_words};
}
@@ -242,9 +254,9 @@ namespace rsx
// Return a raw pointer to contiguous memory
constexpr u32 _1M = 0x100000;
const u32 size = length_in_words * sizeof(u32);
const u32 from = m_iotable->get_addr(m_internal_get);
const u32 from = m_iotable->get_addr(m_fifo_pos);
for (u32 remaining = size, addr = m_internal_get, ptr = from; remaining > 0;)
for (u32 remaining = size, addr = m_fifo_pos, ptr = from; remaining > 0;)
{
const u32 next_block = utils::align(addr + 1, _1M);
const u32 available = (next_block - addr);
@@ -275,9 +287,11 @@ namespace rsx
bool ok{};
u32 arg = 0;
m_fifo_pos += 4;
if (g_cfg.core.rsx_fifo_accuracy) [[ unlikely ]]
{
std::tie(ok, arg) = fetch_u32(m_internal_get + 4);
std::tie(ok, arg) = fetch_u32(m_fifo_pos);
if (!ok)
{
@@ -285,14 +299,19 @@ namespace rsx
{
m_thread->recover_fifo();
}
else
{
m_fifo_pos -= 4;
}
return false;
}
}
else
{
if (m_internal_get + 4 == read_put<false>())
if (m_fifo_pos == read_put<false>())
{
m_fifo_pos -= 4;
return false;
}
@@ -300,8 +319,6 @@ namespace rsx
arg = vm::read32(m_args_ptr);
}
m_internal_get += 4;
m_command_reg += m_command_inc;
--m_remaining_commands;
@@ -310,7 +327,7 @@ namespace rsx
return true;
}
m_internal_get += 4;
m_fifo_pos += 4;
return false;
}
@@ -322,12 +339,12 @@ namespace rsx
{
m_command_reg += m_command_inc * count;
m_remaining_commands -= count;
m_internal_get += 4 * count;
m_fifo_pos += 4 * count;
m_args_ptr += 4 * count;
return true;
}
m_internal_get += 4 * m_remaining_commands;
m_fifo_pos += 4 * m_remaining_commands;
m_remaining_commands = 0;
return false;
}
@@ -348,7 +365,7 @@ namespace rsx
if (m_memwatch_addr)
{
if (m_internal_get == m_memwatch_addr)
if (m_fifo_pos == m_memwatch_addr)
{
if (const u32 addr = m_iotable->get_addr(m_memwatch_addr); addr + 1)
{
@@ -369,14 +386,14 @@ namespace rsx
{
const u32 put = read_put();
if (put == m_internal_get)
if (put == m_fifo_pos)
{
// Nothing to do
data.reg = FIFO_EMPTY;
return;
}
if (const u32 addr = m_iotable->get_addr(m_internal_get); addr + 1)
if (const u32 addr = m_iotable->get_addr(m_fifo_pos); addr + 1)
{
m_cmd = vm::read32(addr);
}
@@ -388,7 +405,7 @@ namespace rsx
}
else
{
if (auto [ok, arg] = fetch_u32(m_internal_get); ok)
if (auto [ok, arg] = fetch_u32(m_fifo_pos); ok)
{
m_cmd = arg;
}
@@ -421,7 +438,7 @@ namespace rsx
if (!count)
{
m_ctrl->get.release(m_internal_get += 4);
m_ctrl->get.release(m_fifo_pos += 4);
data.reg = FIFO_NOP;
return;
}
@@ -436,9 +453,9 @@ namespace rsx
if (g_cfg.core.rsx_fifo_accuracy)
{
m_internal_get += 4;
m_fifo_pos += 4;
auto [ok, arg] = fetch_u32(m_internal_get);
auto [ok, arg] = fetch_u32(m_fifo_pos);
if (!ok)
{
@@ -464,7 +481,7 @@ namespace rsx
inc_get(true); // Wait for data block to become available
// Validate the args ptr if the command attempts to read from it
m_args_ptr = m_iotable->get_addr(m_internal_get);
m_args_ptr = m_iotable->get_addr(m_fifo_pos);
if (m_args_ptr == umax) [[unlikely]]
{
// Optional recovery
+3 -3
View File
@@ -132,7 +132,7 @@ namespace rsx
mutable rsx::thread* m_thread;
RsxDmaControl* m_ctrl = nullptr;
const rsx::rsx_iomap_table* m_iotable;
u32 m_internal_get = 0;
u32 m_fifo_pos = 0;
u32 m_memwatch_addr = 0;
u32 m_memwatch_cmp = 0;
@@ -156,12 +156,12 @@ namespace rsx
std::pair<bool, u32> fetch_u32(u32 addr);
void invalidate_cache() { m_cache_size = 0; }
u32 get_pos() const { return m_internal_get; }
u32 get_pos() const { return m_fifo_pos; }
u32 last_cmd() const { return m_cmd; }
void sync_get() const;
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);
void restore_state(u32 cmd, u32 count, u32 position);
void inc_get(bool wait);
void set_get(u32 get, u32 spin_cmd = 0);
+19 -5
View File
@@ -811,11 +811,25 @@ namespace rsx
{
ar(u32{0});
}
ar(fifo_ctrl ? fifo_ctrl->get_pos() : 0);
}
else if (u32 count{ar})
else
{
restore_fifo_count = count;
ar(restore_fifo_cmd);
if (u32 count{ar})
{
restore_fifo_count = count;
ar(restore_fifo_cmd);
}
if (version >= 4)
{
ar(restore_fifo_position);
}
else
{
restore_fifo_position = vm::_ptr<RsxDmaControl>(dma_address)->get;
}
}
}
@@ -1141,9 +1155,9 @@ namespace rsx
vblank_count = 0;
if (restore_fifo_count)
if (serialized)
{
fifo_ctrl->restore_state(restore_fifo_cmd, restore_fifo_count);
fifo_ctrl->restore_state(restore_fifo_cmd, restore_fifo_count, restore_fifo_position);
}
if (!send_event(0, event_flags, 0))
+1
View File
@@ -133,6 +133,7 @@ namespace rsx
u32 saved_fifo_ret = RSX_CALL_STACK_EMPTY;
u32 restore_fifo_cmd = 0;
u32 restore_fifo_count = 0;
u32 restore_fifo_position = 0;
// Occlusion query
bool zcull_surface_active = false;
+1 -1
View File
@@ -58,7 +58,7 @@ namespace vm
namespace rsx
{
SERIALIZATION_VER(rsx, 10, 1, 2/*Pending flip*/, 3/*avconf scan_mode*/)
SERIALIZATION_VER(rsx, 10, 1, 2/*Pending flip*/, 3/*avconf scan_mode*/, 4/*FIFO Position*/)
}
namespace np
+8
View File
@@ -421,6 +421,14 @@ namespace utils
return static_cast<std::make_unsigned_t<std::common_type_t<T, U>>>((value + (align - 1)) & (T{0} - align));
}
// Align to power of 2 but also promotes already aligned values
template <typename T, typename U>
requires std::is_unsigned_v<T>
constexpr std::make_unsigned_t<std::common_type_t<T, U>> align_forced(T value, U align)
{
return static_cast<std::make_unsigned_t<std::common_type_t<T, U>>>((value | (align - 1)) + 1);
}
// General purpose aligned division, the result is rounded up not truncated
template <typename T>
requires std::is_unsigned_v<T>