mirror of
https://github.com/RPCS3/rpcs3.git
synced 2026-10-12 02:09:17 +00:00
rsx: Fix FIFO GET updates (Atomic FIFO exclusive fix)
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user