2018-09-24 15:03:25 +02:00
|
|
|
|
#include "stdafx.h"
|
|
|
|
|
|
|
|
|
|
|
|
#include "RSXFIFO.h"
|
|
|
|
|
|
#include "RSXThread.h"
|
|
|
|
|
|
#include "Capture/rsx_capture.h"
|
|
|
|
|
|
|
|
|
|
|
|
namespace rsx
|
|
|
|
|
|
{
|
|
|
|
|
|
namespace FIFO
|
|
|
|
|
|
{
|
|
|
|
|
|
FIFO_control::FIFO_control(::rsx::thread* pctrl)
|
|
|
|
|
|
{
|
|
|
|
|
|
m_ctrl = pctrl->ctrl;
|
2020-01-16 21:40:47 +01:00
|
|
|
|
m_iotable = &pctrl->iomap_table;
|
2018-10-31 10:24:43 +01:00
|
|
|
|
}
|
2018-09-24 15:03:25 +02:00
|
|
|
|
|
2019-01-15 13:37:26 +01:00
|
|
|
|
void FIFO_control::inc_get(bool wait)
|
2018-12-07 05:53:30 +01:00
|
|
|
|
{
|
|
|
|
|
|
m_internal_get += 4;
|
|
|
|
|
|
|
2019-09-29 19:23:52 +02:00
|
|
|
|
if (wait && read_put<false>() == m_internal_get)
|
2018-12-07 05:53:30 +01:00
|
|
|
|
{
|
2019-01-15 13:37:26 +01:00
|
|
|
|
// 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();
|
|
|
|
|
|
|
2019-09-28 09:31:06 +02:00
|
|
|
|
while (read_put() == m_internal_get && !Emu.IsStopped())
|
2018-12-07 05:53:30 +01:00
|
|
|
|
{
|
2019-01-15 13:37:26 +01:00
|
|
|
|
std::this_thread::yield();
|
2018-12-07 05:53:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-09-29 19:23:52 +02:00
|
|
|
|
template <bool full>
|
2019-10-14 20:26:31 +02:00
|
|
|
|
inline u32 FIFO_control::read_put()
|
2018-10-31 10:24:43 +01:00
|
|
|
|
{
|
2019-09-29 19:23:52 +02:00
|
|
|
|
if constexpr (!full)
|
|
|
|
|
|
{
|
|
|
|
|
|
return m_ctrl->put & ~3;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2020-02-05 08:00:08 +01:00
|
|
|
|
if (u32 put = m_ctrl->put; (put & 3) == 0) [[likely]]
|
2019-10-14 20:26:31 +02:00
|
|
|
|
{
|
|
|
|
|
|
return put;
|
|
|
|
|
|
}
|
2019-10-28 21:16:27 +01:00
|
|
|
|
|
|
|
|
|
|
return m_ctrl->put.and_fetch(~3);
|
2019-09-29 19:23:52 +02:00
|
|
|
|
}
|
2019-09-28 09:31:06 +02:00
|
|
|
|
}
|
2018-09-24 15:03:25 +02:00
|
|
|
|
|
2018-12-09 13:50:51 +01:00
|
|
|
|
void FIFO_control::set_get(u32 get)
|
2018-10-31 10:24:43 +01:00
|
|
|
|
{
|
|
|
|
|
|
if (m_ctrl->get == get)
|
2018-09-24 15:03:25 +02:00
|
|
|
|
{
|
2020-01-16 21:40:47 +01:00
|
|
|
|
if (const u32 addr = m_iotable->get_addr(m_memwatch_addr); addr + 1)
|
2018-09-24 15:03:25 +02:00
|
|
|
|
{
|
2018-12-09 13:50:51 +01:00
|
|
|
|
m_memwatch_addr = get;
|
|
|
|
|
|
m_memwatch_cmp = vm::read32(addr);
|
2018-10-31 10:24:43 +01:00
|
|
|
|
}
|
2018-09-24 15:03:25 +02:00
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-10-31 10:24:43 +01:00
|
|
|
|
// Update ctrl registers
|
2019-03-14 17:30:19 +01:00
|
|
|
|
m_ctrl->get.release(m_internal_get = get);
|
2019-01-15 13:37:26 +01:00
|
|
|
|
m_remaining_commands = 0;
|
2018-09-24 15:03:25 +02:00
|
|
|
|
|
2018-10-31 10:24:43 +01:00
|
|
|
|
// Clear memwatch spinner
|
|
|
|
|
|
m_memwatch_addr = 0;
|
|
|
|
|
|
}
|
2018-09-24 15:03:25 +02:00
|
|
|
|
|
2018-12-09 13:50:51 +01:00
|
|
|
|
bool FIFO_control::read_unsafe(register_pair& data)
|
2018-10-31 10:24:43 +01:00
|
|
|
|
{
|
|
|
|
|
|
// Fast read with no processing, only safe inside a PACKET_BEGIN+count block
|
2019-01-15 13:37:26 +01:00
|
|
|
|
if (m_remaining_commands &&
|
2019-09-29 19:23:52 +02:00
|
|
|
|
m_internal_get != read_put<false>())
|
2018-11-07 10:34:03 +01:00
|
|
|
|
{
|
|
|
|
|
|
m_command_reg += m_command_inc;
|
|
|
|
|
|
m_args_ptr += 4;
|
2019-01-15 13:37:26 +01:00
|
|
|
|
m_remaining_commands--;
|
|
|
|
|
|
m_internal_get += 4;
|
2018-10-31 10:24:43 +01:00
|
|
|
|
|
2018-12-09 13:50:51 +01:00
|
|
|
|
data.set(m_command_reg, vm::read32(m_args_ptr));
|
|
|
|
|
|
return true;
|
2018-11-07 10:34:03 +01:00
|
|
|
|
}
|
2018-12-09 13:50:51 +01:00
|
|
|
|
|
|
|
|
|
|
return false;
|
2018-09-24 15:03:25 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-10-09 19:45:24 +02:00
|
|
|
|
void FIFO_control::abort()
|
|
|
|
|
|
{
|
|
|
|
|
|
m_remaining_commands = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-10-31 10:24:43 +01:00
|
|
|
|
void FIFO_control::read(register_pair& data)
|
2018-09-24 15:03:25 +02:00
|
|
|
|
{
|
2019-09-28 09:31:06 +02:00
|
|
|
|
const u32 put = read_put();
|
2018-10-31 10:24:43 +01:00
|
|
|
|
m_internal_get = m_ctrl->get;
|
|
|
|
|
|
|
|
|
|
|
|
if (put == m_internal_get)
|
2018-10-01 22:05:51 +02:00
|
|
|
|
{
|
2018-10-31 10:24:43 +01:00
|
|
|
|
// Nothing to do
|
|
|
|
|
|
data.reg = FIFO_EMPTY;
|
2018-10-01 22:05:51 +02:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-01-15 13:37:26 +01:00
|
|
|
|
if (m_remaining_commands && read_unsafe(data))
|
|
|
|
|
|
{
|
|
|
|
|
|
// Previous block aborted to wait for PUT pointer
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-10-31 10:24:43 +01:00
|
|
|
|
if (m_memwatch_addr)
|
2018-10-01 22:05:51 +02:00
|
|
|
|
{
|
2018-10-31 10:24:43 +01:00
|
|
|
|
if (m_internal_get == m_memwatch_addr)
|
2018-10-01 22:05:51 +02:00
|
|
|
|
{
|
2020-01-16 21:40:47 +01:00
|
|
|
|
if (const u32 addr = m_iotable->get_addr(m_memwatch_addr); addr + 1)
|
2018-10-01 22:05:51 +02:00
|
|
|
|
{
|
2018-10-31 10:24:43 +01:00
|
|
|
|
if (vm::read32(addr) == m_memwatch_cmp)
|
2018-10-01 22:05:51 +02:00
|
|
|
|
{
|
2018-10-31 10:24:43 +01:00
|
|
|
|
// Still spinning in place
|
|
|
|
|
|
data.reg = FIFO_EMPTY;
|
|
|
|
|
|
return;
|
2018-10-01 22:05:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-10-31 10:24:43 +01:00
|
|
|
|
m_memwatch_addr = 0;
|
|
|
|
|
|
m_memwatch_cmp = 0;
|
|
|
|
|
|
}
|
2018-09-24 15:03:25 +02:00
|
|
|
|
|
2020-01-16 21:40:47 +01:00
|
|
|
|
if (const u32 addr = m_iotable->get_addr(m_internal_get); addr + 1)
|
2018-09-24 15:03:25 +02:00
|
|
|
|
{
|
2019-10-19 21:53:54 +02:00
|
|
|
|
m_cmd = vm::read32(addr);
|
2018-10-31 10:24:43 +01:00
|
|
|
|
}
|
|
|
|
|
|
else
|
2018-10-01 22:05:51 +02:00
|
|
|
|
{
|
2018-10-31 10:24:43 +01:00
|
|
|
|
// TODO: Optional recovery
|
|
|
|
|
|
data.reg = FIFO_ERROR;
|
|
|
|
|
|
return;
|
2018-10-01 22:05:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-02-05 08:00:08 +01:00
|
|
|
|
if (m_cmd & RSX_METHOD_NON_METHOD_CMD_MASK) [[unlikely]]
|
2018-10-01 22:05:51 +02:00
|
|
|
|
{
|
2019-10-19 21:53:54 +02:00
|
|
|
|
if ((m_cmd & RSX_METHOD_OLD_JUMP_CMD_MASK) == RSX_METHOD_OLD_JUMP_CMD ||
|
|
|
|
|
|
(m_cmd & RSX_METHOD_NEW_JUMP_CMD_MASK) == RSX_METHOD_NEW_JUMP_CMD ||
|
|
|
|
|
|
(m_cmd & RSX_METHOD_CALL_CMD_MASK) == RSX_METHOD_CALL_CMD ||
|
|
|
|
|
|
(m_cmd & RSX_METHOD_RETURN_MASK) == RSX_METHOD_RETURN_CMD)
|
2018-10-01 22:05:51 +02:00
|
|
|
|
{
|
2018-10-31 10:24:43 +01:00
|
|
|
|
// Flow control, stop reading
|
2019-10-19 21:53:54 +02:00
|
|
|
|
data.reg = m_cmd;
|
2018-10-31 10:24:43 +01:00
|
|
|
|
return;
|
2018-10-01 22:05:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-10-31 10:24:43 +01:00
|
|
|
|
// Malformed command, optional recovery
|
|
|
|
|
|
data.reg = FIFO_ERROR;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2018-10-01 22:05:51 +02:00
|
|
|
|
|
2018-10-31 10:24:43 +01:00
|
|
|
|
// Validate the args ptr if the command attempts to read from it
|
2020-01-16 21:40:47 +01:00
|
|
|
|
m_args_ptr = m_iotable->get_addr(m_internal_get + 4);
|
2020-02-19 18:03:59 +01:00
|
|
|
|
if (m_args_ptr == umax) [[unlikely]]
|
2018-10-31 10:24:43 +01:00
|
|
|
|
{
|
|
|
|
|
|
// Optional recovery
|
|
|
|
|
|
data.reg = FIFO_ERROR;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2018-10-01 22:05:51 +02:00
|
|
|
|
|
2018-10-31 10:24:43 +01:00
|
|
|
|
verify(HERE), !m_remaining_commands;
|
2019-10-19 21:53:54 +02:00
|
|
|
|
const u32 count = (m_cmd >> 18) & 0x7ff;
|
2018-10-01 22:05:51 +02:00
|
|
|
|
|
2018-12-07 05:53:30 +01:00
|
|
|
|
if (!count)
|
2018-10-31 10:24:43 +01:00
|
|
|
|
{
|
2019-03-14 17:30:19 +01:00
|
|
|
|
m_ctrl->get.release(m_internal_get + 4);
|
2018-12-07 05:53:30 +01:00
|
|
|
|
data.reg = FIFO_NOP;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2018-10-01 22:05:51 +02:00
|
|
|
|
|
2018-12-07 05:53:30 +01:00
|
|
|
|
if (count > 1)
|
|
|
|
|
|
{
|
2018-10-31 10:24:43 +01:00
|
|
|
|
// Set up readback parameters
|
2019-10-19 21:53:54 +02:00
|
|
|
|
m_command_reg = m_cmd & 0xfffc;
|
|
|
|
|
|
m_command_inc = ((m_cmd & RSX_METHOD_NON_INCREMENT_CMD_MASK) == RSX_METHOD_NON_INCREMENT_CMD) ? 0 : 4;
|
2018-10-31 10:24:43 +01:00
|
|
|
|
m_remaining_commands = count - 1;
|
2018-10-01 22:05:51 +02:00
|
|
|
|
}
|
2018-12-07 05:53:30 +01:00
|
|
|
|
|
2019-01-15 13:37:26 +01:00
|
|
|
|
inc_get(true); // Wait for data block to become available
|
2018-12-07 05:53:30 +01:00
|
|
|
|
m_internal_get += 4;
|
|
|
|
|
|
|
2019-10-19 21:53:54 +02:00
|
|
|
|
data.set(m_cmd & 0xfffc, vm::read32(m_args_ptr));
|
2018-09-24 15:03:25 +02:00
|
|
|
|
}
|
2018-11-07 10:34:03 +01:00
|
|
|
|
|
2018-12-02 13:41:05 +01:00
|
|
|
|
void flattening_helper::reset(bool _enabled)
|
2018-11-30 12:39:15 +01:00
|
|
|
|
{
|
2018-12-02 13:41:05 +01:00
|
|
|
|
enabled = _enabled;
|
2018-11-30 12:39:15 +01:00
|
|
|
|
num_collapsed = 0;
|
2018-12-02 13:41:05 +01:00
|
|
|
|
begin_end_ctr = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void flattening_helper::force_disable()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (enabled)
|
|
|
|
|
|
{
|
2020-02-01 09:07:25 +01:00
|
|
|
|
rsx_log.warning("FIFO optimizations have been disabled as the application is not compatible with per-frame analysis");
|
2018-12-02 13:41:05 +01:00
|
|
|
|
|
|
|
|
|
|
reset(false);
|
|
|
|
|
|
fifo_hint = optimization_hint::application_not_compatible;
|
|
|
|
|
|
}
|
2018-11-30 12:39:15 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-11-07 10:34:03 +01:00
|
|
|
|
void flattening_helper::evaluate_performance(u32 total_draw_count)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!enabled)
|
|
|
|
|
|
{
|
2018-12-02 13:41:05 +01:00
|
|
|
|
if (fifo_hint == optimization_hint::application_not_compatible)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Not compatible, do nothing
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-11-07 10:34:03 +01:00
|
|
|
|
if (total_draw_count <= 2000)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Low draw call pressure
|
|
|
|
|
|
fifo_hint = optimization_hint::load_low;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (fifo_hint == optimization_hint::load_unoptimizable)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Nope, wait for stats to change
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (enabled)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Currently activated. Check if there is any benefit
|
|
|
|
|
|
if (num_collapsed < 500)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Not worth it, disable
|
|
|
|
|
|
enabled = false;
|
|
|
|
|
|
fifo_hint = load_unoptimizable;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
u32 real_total = total_draw_count + num_collapsed;
|
|
|
|
|
|
if (real_total <= 2000)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Low total number of draws submitted, no need to keep trying for now
|
|
|
|
|
|
enabled = false;
|
|
|
|
|
|
fifo_hint = load_low;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-12-02 13:41:05 +01:00
|
|
|
|
reset(enabled);
|
2018-11-07 10:34:03 +01:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// Not enabled, check if we should try enabling
|
|
|
|
|
|
verify(HERE), total_draw_count > 2000;
|
|
|
|
|
|
if (fifo_hint != load_unoptimizable)
|
|
|
|
|
|
{
|
|
|
|
|
|
// If its set to unoptimizable, we already tried and it did not work
|
|
|
|
|
|
// If it resets to load low (usually after some kind of loading screen) we can try again
|
2018-12-02 13:41:05 +01:00
|
|
|
|
verify("Incorrect initial state" HERE), begin_end_ctr == 0, num_collapsed == 0;
|
2018-11-07 10:34:03 +01:00
|
|
|
|
enabled = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
flatten_op flattening_helper::test(register_pair& command)
|
|
|
|
|
|
{
|
2018-12-02 14:22:05 +01:00
|
|
|
|
u32 flush_cmd = ~0u;
|
2018-11-07 10:34:03 +01:00
|
|
|
|
switch (const u32 reg = (command.reg >> 2))
|
|
|
|
|
|
{
|
|
|
|
|
|
case NV4097_SET_BEGIN_END:
|
|
|
|
|
|
{
|
|
|
|
|
|
begin_end_ctr ^= 1;
|
|
|
|
|
|
|
|
|
|
|
|
if (command.value)
|
|
|
|
|
|
{
|
|
|
|
|
|
// This is a BEGIN call
|
2020-02-05 08:00:08 +01:00
|
|
|
|
if (!deferred_primitive) [[likely]]
|
2018-11-07 10:34:03 +01:00
|
|
|
|
{
|
|
|
|
|
|
// New primitive block
|
|
|
|
|
|
deferred_primitive = command.value;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (deferred_primitive == command.value)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Same primitive can be chanined; do nothing
|
|
|
|
|
|
command.reg = FIFO_DISABLED_COMMAND;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// Primitive command has changed!
|
|
|
|
|
|
// Flush
|
|
|
|
|
|
flush_cmd = command.value;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (deferred_primitive)
|
|
|
|
|
|
{
|
|
|
|
|
|
command.reg = FIFO_DRAW_BARRIER;
|
|
|
|
|
|
draw_count++;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2020-02-01 09:07:25 +01:00
|
|
|
|
rsx_log.error("Fifo flattener misalignment, disable FIFO reordering and report to developers");
|
2018-12-02 13:41:05 +01:00
|
|
|
|
begin_end_ctr = 0;
|
|
|
|
|
|
flush_cmd = 0u;
|
2018-11-07 10:34:03 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
case NV4097_DRAW_ARRAYS:
|
|
|
|
|
|
case NV4097_DRAW_INDEX_ARRAY:
|
|
|
|
|
|
{
|
|
|
|
|
|
// TODO: Check type
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
default:
|
|
|
|
|
|
{
|
2020-02-05 08:00:08 +01:00
|
|
|
|
if (draw_count) [[unlikely]]
|
2018-11-07 10:34:03 +01:00
|
|
|
|
{
|
2020-02-05 08:00:08 +01:00
|
|
|
|
if (m_register_properties[reg] & register_props::always_ignore) [[unlikely]]
|
2018-11-07 10:34:03 +01:00
|
|
|
|
{
|
|
|
|
|
|
// Always ignore
|
|
|
|
|
|
command.reg = FIFO_DISABLED_COMMAND;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// Flush
|
|
|
|
|
|
flush_cmd = (begin_end_ctr) ? deferred_primitive : 0u;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// Nothing to do
|
|
|
|
|
|
return NOTHING;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-12-02 14:22:05 +01:00
|
|
|
|
if (flush_cmd != ~0u)
|
2018-11-07 10:34:03 +01:00
|
|
|
|
{
|
|
|
|
|
|
num_collapsed += draw_count? (draw_count - 1) : 0;
|
|
|
|
|
|
draw_count = 0;
|
|
|
|
|
|
deferred_primitive = flush_cmd;
|
|
|
|
|
|
|
|
|
|
|
|
return (begin_end_ctr == 1)? EMIT_BARRIER : EMIT_END;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return NOTHING;
|
|
|
|
|
|
}
|
2018-09-24 15:03:25 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void thread::run_FIFO()
|
|
|
|
|
|
{
|
2018-10-31 10:24:43 +01:00
|
|
|
|
FIFO::register_pair command;
|
|
|
|
|
|
fifo_ctrl->read(command);
|
2018-09-24 15:03:25 +02:00
|
|
|
|
const auto cmd = command.reg;
|
|
|
|
|
|
|
2020-02-05 08:00:08 +01:00
|
|
|
|
if (cmd & (0xffff0000 | RSX_METHOD_NON_METHOD_CMD_MASK)) [[unlikely]]
|
2018-10-01 22:05:51 +02:00
|
|
|
|
{
|
2018-11-09 15:57:07 +01:00
|
|
|
|
// Check for special FIFO commands
|
|
|
|
|
|
switch (cmd)
|
|
|
|
|
|
{
|
2018-12-07 05:53:30 +01:00
|
|
|
|
case FIFO::FIFO_NOP:
|
|
|
|
|
|
{
|
|
|
|
|
|
if (performance_counters.state == FIFO_state::running)
|
|
|
|
|
|
{
|
|
|
|
|
|
performance_counters.FIFO_idle_timestamp = get_system_time();
|
|
|
|
|
|
performance_counters.state = FIFO_state::nop;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2018-11-09 15:57:07 +01:00
|
|
|
|
case FIFO::FIFO_EMPTY:
|
|
|
|
|
|
{
|
|
|
|
|
|
if (performance_counters.state == FIFO_state::running)
|
|
|
|
|
|
{
|
|
|
|
|
|
performance_counters.FIFO_idle_timestamp = get_system_time();
|
|
|
|
|
|
performance_counters.state = FIFO_state::empty;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
std::this_thread::yield();
|
|
|
|
|
|
}
|
2018-10-01 22:05:51 +02:00
|
|
|
|
|
2018-11-09 15:57:07 +01:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
case FIFO::FIFO_BUSY:
|
2018-09-24 15:03:25 +02:00
|
|
|
|
{
|
2018-11-09 15:57:07 +01:00
|
|
|
|
// Do something else
|
|
|
|
|
|
return;
|
2018-09-24 15:03:25 +02:00
|
|
|
|
}
|
2018-11-09 15:57:07 +01:00
|
|
|
|
case FIFO::FIFO_ERROR:
|
2018-10-31 10:24:43 +01:00
|
|
|
|
{
|
2020-02-11 06:00:30 +01:00
|
|
|
|
rsx_log.error("FIFO error: possible desync event (last cmd = 0x%x)", get_fifo_cmd());
|
2019-10-09 19:45:24 +02:00
|
|
|
|
recover_fifo();
|
2018-11-09 15:57:07 +01:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2018-10-31 10:24:43 +01:00
|
|
|
|
}
|
2018-09-24 15:03:25 +02:00
|
|
|
|
|
2018-11-09 15:57:07 +01:00
|
|
|
|
// Check for flow control
|
2018-10-31 10:24:43 +01:00
|
|
|
|
if ((cmd & RSX_METHOD_OLD_JUMP_CMD_MASK) == RSX_METHOD_OLD_JUMP_CMD)
|
2018-09-24 15:03:25 +02:00
|
|
|
|
{
|
2018-12-14 14:37:05 +01:00
|
|
|
|
const u32 offs = cmd & RSX_METHOD_OLD_JUMP_OFFSET_MASK;
|
2018-12-09 13:50:51 +01:00
|
|
|
|
if (offs == fifo_ctrl->get_pos())
|
2018-09-24 15:03:25 +02:00
|
|
|
|
{
|
2018-10-31 10:24:43 +01:00
|
|
|
|
//Jump to self. Often preceded by NOP
|
|
|
|
|
|
if (performance_counters.state == FIFO_state::running)
|
|
|
|
|
|
{
|
|
|
|
|
|
performance_counters.FIFO_idle_timestamp = get_system_time();
|
2018-12-07 05:53:30 +01:00
|
|
|
|
sync_point_request = true;
|
2018-10-31 10:24:43 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
performance_counters.state = FIFO_state::spinning;
|
2018-09-24 15:03:25 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-02-01 09:07:25 +01:00
|
|
|
|
//rsx_log.warning("rsx jump(0x%x) #addr=0x%x, cmd=0x%x, get=0x%x, put=0x%x", offs, m_ioAddress + get, cmd, get, put);
|
2018-12-09 13:50:51 +01:00
|
|
|
|
fifo_ctrl->set_get(offs);
|
2018-10-31 10:24:43 +01:00
|
|
|
|
return;
|
2018-09-24 15:03:25 +02:00
|
|
|
|
}
|
2018-10-31 10:24:43 +01:00
|
|
|
|
if ((cmd & RSX_METHOD_NEW_JUMP_CMD_MASK) == RSX_METHOD_NEW_JUMP_CMD)
|
2018-09-24 15:03:25 +02:00
|
|
|
|
{
|
2018-12-14 14:37:05 +01:00
|
|
|
|
const u32 offs = cmd & RSX_METHOD_NEW_JUMP_OFFSET_MASK;
|
2018-12-09 13:50:51 +01:00
|
|
|
|
if (offs == fifo_ctrl->get_pos())
|
2018-09-24 15:03:25 +02:00
|
|
|
|
{
|
2018-10-31 10:24:43 +01:00
|
|
|
|
//Jump to self. Often preceded by NOP
|
|
|
|
|
|
if (performance_counters.state == FIFO_state::running)
|
|
|
|
|
|
{
|
|
|
|
|
|
performance_counters.FIFO_idle_timestamp = get_system_time();
|
2018-12-07 05:53:30 +01:00
|
|
|
|
sync_point_request = true;
|
2018-10-31 10:24:43 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
performance_counters.state = FIFO_state::spinning;
|
2018-09-24 15:03:25 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-02-01 09:07:25 +01:00
|
|
|
|
//rsx_log.warning("rsx jump(0x%x) #addr=0x%x, cmd=0x%x, get=0x%x, put=0x%x", offs, m_ioAddress + get, cmd, get, put);
|
2018-12-09 13:50:51 +01:00
|
|
|
|
fifo_ctrl->set_get(offs);
|
2018-10-31 10:24:43 +01:00
|
|
|
|
return;
|
2018-09-24 15:03:25 +02:00
|
|
|
|
}
|
2018-10-31 10:24:43 +01:00
|
|
|
|
if ((cmd & RSX_METHOD_CALL_CMD_MASK) == RSX_METHOD_CALL_CMD)
|
2018-09-24 15:03:25 +02:00
|
|
|
|
{
|
2019-09-28 09:29:16 +02:00
|
|
|
|
if (fifo_ret_addr != RSX_CALL_STACK_EMPTY)
|
2018-10-31 10:24:43 +01:00
|
|
|
|
{
|
|
|
|
|
|
// Only one layer is allowed in the call stack.
|
2020-02-11 06:00:30 +01:00
|
|
|
|
rsx_log.error("FIFO: CALL found inside a subroutine (last cmd = 0x%x)", get_fifo_cmd());
|
|
|
|
|
|
recover_fifo();
|
2018-10-31 10:24:43 +01:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-12-14 14:37:05 +01:00
|
|
|
|
const u32 offs = cmd & RSX_METHOD_CALL_OFFSET_MASK;
|
2019-09-28 09:29:16 +02:00
|
|
|
|
fifo_ret_addr = fifo_ctrl->get_pos() + 4;
|
2018-10-31 10:24:43 +01:00
|
|
|
|
fifo_ctrl->set_get(offs);
|
2018-09-24 15:03:25 +02:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2018-10-31 10:24:43 +01:00
|
|
|
|
if ((cmd & RSX_METHOD_RETURN_MASK) == RSX_METHOD_RETURN_CMD)
|
2018-09-24 15:03:25 +02:00
|
|
|
|
{
|
2019-09-28 09:29:16 +02:00
|
|
|
|
if (fifo_ret_addr == RSX_CALL_STACK_EMPTY)
|
2018-10-31 10:24:43 +01:00
|
|
|
|
{
|
2020-02-11 06:00:30 +01:00
|
|
|
|
rsx_log.error("FIFO: RET found without corresponding CALL (last cmd = 0x%x)", get_fifo_cmd());
|
|
|
|
|
|
recover_fifo();
|
2018-10-31 10:24:43 +01:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-09-28 09:29:16 +02:00
|
|
|
|
fifo_ctrl->set_get(std::exchange(fifo_ret_addr, RSX_CALL_STACK_EMPTY));
|
2018-09-24 15:03:25 +02:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-11-09 15:57:07 +01:00
|
|
|
|
// If we reached here, this is likely an error
|
|
|
|
|
|
fmt::throw_exception("Unexpected command 0x%x" HERE, cmd);
|
2018-09-24 15:03:25 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-01-07 21:22:30 +01:00
|
|
|
|
if (const auto state = performance_counters.state;
|
|
|
|
|
|
state != FIFO_state::running)
|
2018-09-24 15:03:25 +02:00
|
|
|
|
{
|
2020-01-07 21:22:30 +01:00
|
|
|
|
performance_counters.state = FIFO_state::running;
|
2018-09-24 15:03:25 +02:00
|
|
|
|
|
2020-01-07 21:22:30 +01:00
|
|
|
|
// 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)
|
2018-09-24 15:03:25 +02:00
|
|
|
|
{
|
2020-01-07 21:22:30 +01:00
|
|
|
|
fifo_wake_delay();
|
2018-09-24 15:03:25 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-01-07 21:22:30 +01:00
|
|
|
|
// Update performance counters with time spent in idle mode
|
|
|
|
|
|
performance_counters.idle_time += (get_system_time() - performance_counters.FIFO_idle_timestamp);
|
2018-09-24 15:03:25 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-12-09 13:50:51 +01:00
|
|
|
|
do
|
2018-09-24 15:03:25 +02:00
|
|
|
|
{
|
2020-02-05 08:00:08 +01:00
|
|
|
|
if (capture_current_frame) [[unlikely]]
|
2018-12-28 17:02:20 +01:00
|
|
|
|
{
|
|
|
|
|
|
const u32 reg = (command.reg & 0xfffc) >> 2;
|
|
|
|
|
|
const u32 value = command.value;
|
|
|
|
|
|
|
2019-06-08 09:22:42 +02:00
|
|
|
|
frame_debug.command_queue.emplace_back(reg, value);
|
2018-12-28 17:02:20 +01:00
|
|
|
|
|
|
|
|
|
|
if (!(reg == NV406E_SET_REFERENCE || reg == NV406E_SEMAPHORE_RELEASE || reg == NV406E_SEMAPHORE_ACQUIRE))
|
|
|
|
|
|
{
|
|
|
|
|
|
// todo: handle nv406e methods better?, do we care about call/jumps?
|
|
|
|
|
|
rsx::frame_capture_data::replay_command replay_cmd;
|
|
|
|
|
|
replay_cmd.rsx_command = std::make_pair((reg << 2) | (1u << 18), value);
|
|
|
|
|
|
|
|
|
|
|
|
frame_capture.replay_commands.push_back(replay_cmd);
|
|
|
|
|
|
auto it = frame_capture.replay_commands.back();
|
|
|
|
|
|
|
|
|
|
|
|
switch (reg)
|
|
|
|
|
|
{
|
|
|
|
|
|
case NV3089_IMAGE_IN:
|
|
|
|
|
|
capture::capture_image_in(this, it);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case NV0039_BUFFER_NOTIFY:
|
|
|
|
|
|
capture::capture_buffer_notify(this, it);
|
|
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-02-05 08:00:08 +01:00
|
|
|
|
if (m_flattener.is_enabled()) [[unlikely]]
|
2018-11-07 10:34:03 +01:00
|
|
|
|
{
|
|
|
|
|
|
switch(m_flattener.test(command))
|
|
|
|
|
|
{
|
|
|
|
|
|
case FIFO::NOTHING:
|
|
|
|
|
|
{
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
case FIFO::EMIT_END:
|
|
|
|
|
|
{
|
|
|
|
|
|
// Emit end command to close existing scope
|
|
|
|
|
|
//verify(HERE), in_begin_end;
|
|
|
|
|
|
methods[NV4097_SET_BEGIN_END](this, NV4097_SET_BEGIN_END, 0);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
case FIFO::EMIT_BARRIER:
|
|
|
|
|
|
{
|
|
|
|
|
|
//verify(HERE), in_begin_end;
|
|
|
|
|
|
methods[NV4097_SET_BEGIN_END](this, NV4097_SET_BEGIN_END, 0);
|
|
|
|
|
|
methods[NV4097_SET_BEGIN_END](this, NV4097_SET_BEGIN_END, m_flattener.get_primitive());
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
default:
|
|
|
|
|
|
{
|
|
|
|
|
|
fmt::throw_exception("Unreachable" HERE);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (command.reg == FIFO::FIFO_DISABLED_COMMAND)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Optimized away
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-08-17 12:17:37 +02:00
|
|
|
|
const u32 reg = (command.reg & 0xffff) >> 2;
|
2018-11-07 10:34:03 +01:00
|
|
|
|
const u32 value = command.value;
|
|
|
|
|
|
|
2018-09-24 15:03:25 +02:00
|
|
|
|
method_registers.decode(reg, value);
|
|
|
|
|
|
|
|
|
|
|
|
if (auto method = methods[reg])
|
|
|
|
|
|
{
|
|
|
|
|
|
method(this, reg, value);
|
2019-10-09 19:45:24 +02:00
|
|
|
|
|
|
|
|
|
|
if (invalid_command_interrupt_raised)
|
|
|
|
|
|
{
|
|
|
|
|
|
fifo_ctrl->abort();
|
|
|
|
|
|
recover_fifo();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2018-09-24 15:03:25 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2018-12-09 13:50:51 +01:00
|
|
|
|
while (fifo_ctrl->read_unsafe(command));
|
2018-12-07 05:53:30 +01:00
|
|
|
|
|
|
|
|
|
|
fifo_ctrl->sync_get();
|
2018-09-24 15:03:25 +02:00
|
|
|
|
}
|
2019-10-14 20:26:31 +02:00
|
|
|
|
}
|