rsx: Restructure flip code and frame scoping

- Add an explicit frame scope marker tied in with the queue_prepare command
  Since queue_prepare is emitted at the end of a frame, it can be used as end-of-frame in games that emit this
- If this command is not emitted, fifo flatenner and frameskip will not work
This commit is contained in:
kd-11 2019-09-17 14:26:03 +03:00 committed by kd-11
parent c50119aefa
commit 2c76f47eec
3 changed files with 123 additions and 82 deletions

View File

@ -15,6 +15,9 @@ extern u64 get_timebased_time();
static shared_mutex s_rsxmem_mtx;
// Unknown error code returned by sys_rsx_context_attribute
constexpr unsigned SYS_RSX_CONTEXT_ATTRIBUTE_ERROR = -17u;
u64 rsxTimeStamp()
{
return get_timebased_time();
@ -82,7 +85,9 @@ error_code sys_rsx_context_allocate(vm::ptr<u32> context_id, vm::ptr<u64> lpar_d
auto rsx_cfg = g_fxo->get<lv2_rsx_config>();
if (!rsx_cfg->state)
{
return CELL_EINVAL;
}
*context_id = 0x55555555;
@ -96,7 +101,8 @@ error_code sys_rsx_context_allocate(vm::ptr<u32> context_id, vm::ptr<u64> lpar_d
for (int i = 0; i < 64; ++i)
reports.notify[i].timestamp = (u64)-1;
for (int i = 0; i < 256; ++i) {
for (int i = 0; i < 256; ++i)
{
reports.semaphore[i].val = 0x1337C0D3;
reports.semaphore[i].pad = 0x1337BABE;
reports.semaphore[i].timestamp = (u64)-1; // technically different but should be fine
@ -301,9 +307,9 @@ error_code sys_rsx_context_attribute(s32 context_id, u32 package_id, u64 a3, u64
// fyi -- u32 hardware_channel = (a4 >> 8) & 0xFF;
// sanity check, the head should have a 'queued' buffer on it, and it should have been previously 'queued'
u32 sanity_check = 0x40000000 & (1 << flip_idx);
const u32 sanity_check = 0x40000000 & (1 << flip_idx);
if ((driverInfo.head[a3].flipFlags & sanity_check) != sanity_check)
LOG_ERROR(RSX, "Display Flip Queued: Flipping non previously queued buffer 0x%x", a4);
LOG_ERROR(RSX, "Display Flip Queued: Flipping non previously queued buffer 0x%llx", a4);
}
else
{
@ -327,23 +333,33 @@ error_code sys_rsx_context_attribute(s32 context_id, u32 package_id, u64 a3, u64
break;
case 0x103: // Display Queue
{
driverInfo.head[a3].lastQueuedBufferId = a4;
driverInfo.head[a3].flipFlags |= 0x40000000 | (1 << a4);
if (a3 == 0)
sys_event_port_send(rsx_cfg->rsx_event_port, 0, (1 << 5), 0);
if (a3 == 1)
sys_event_port_send(rsx_cfg->rsx_event_port, 0, (1 << 6), 0);
break;
// NOTE: There currently seem to only be 2 active heads on PS3
verify(HERE), a3 < 2;
const u64 shift_offset = (a3 + 5);
sys_event_port_send(rsx_cfg->rsx_event_port, 0, (1ull << shift_offset), 0);
render->on_frame_end(a4);
}
break;
case 0x104: // Display buffer
{
u8 id = a3 & 0xFF;
u32 width = (a4 >> 32) & 0xFFFFFFFF;
u32 height = a4 & 0xFFFFFFFF;
u32 pitch = (a5 >> 32) & 0xFFFFFFFF;
u32 offset = a5 & 0xFFFFFFFF;
const u8 id = a3 & 0xFF;
if (id > 7)
return -17;
{
return SYS_RSX_CONTEXT_ATTRIBUTE_ERROR;
}
const u32 width = (a4 >> 32) & 0xFFFFFFFF;
const u32 height = a4 & 0xFFFFFFFF;
const u32 pitch = (a5 >> 32) & 0xFFFFFFFF;
const u32 offset = a5 & 0xFFFFFFFF;
render->display_buffers[id].width = width;
render->display_buffers[id].height = height;
render->display_buffers[id].pitch = pitch;
@ -370,7 +386,9 @@ error_code sys_rsx_context_attribute(s32 context_id, u32 package_id, u64 a3, u64
case 0x10a: // ? Involved in managing flip status through cellGcmResetFlipStatus
{
if (a3 > 7)
return -17;
{
return SYS_RSX_CONTEXT_ATTRIBUTE_ERROR;
}
u32 flipStatus = driverInfo.head[a3].flipFlags;
flipStatus = (flipStatus & a4) | a5;

View File

@ -2235,45 +2235,9 @@ namespace rsx
void thread::flip(int buffer, bool emu_flip)
{
if (!(async_flip_requested & flip_request::any))
if (async_flip_requested & flip_request::any)
{
// Flip is processed through inline FLIP command in the commandstream
// This is critical as it is a reliable end-of-frame marker
if (!g_cfg.video.disable_FIFO_reordering)
{
// Try to enable FIFO optimizations
// Only rarely useful for some games like RE4
m_flattener.evaluate_performance(m_draw_calls);
}
// Reset zcull ctrl
zcull_ctrl->set_active(this, false);
zcull_ctrl->clear(this);
if (zcull_ctrl->has_pending())
{
LOG_TRACE(RSX, "Dangling reports found, discarding...");
zcull_ctrl->sync(this);
}
if (g_cfg.video.frame_skip_enabled)
{
m_skip_frame_ctr++;
if (m_skip_frame_ctr == g_cfg.video.consequtive_frames_to_draw)
m_skip_frame_ctr = -g_cfg.video.consequtive_frames_to_skip;
skip_frame = (m_skip_frame_ctr < 0);
}
}
else
{
if (async_flip_requested & flip_request::emu_requested)
{
m_flattener.force_disable();
}
// Deferred flip
if (emu_flip)
{
async_flip_requested.clear(flip_request::emu_requested);
@ -2284,13 +2248,16 @@ namespace rsx
}
}
if (!skip_frame)
if (emu_flip)
{
// Reset counter
m_draw_calls = 0;
}
if (g_cfg.video.frame_skip_enabled)
{
skip_frame = (m_skip_frame_ctr < 0);
}
performance_counters.sampled_frames++;
m_draw_calls = 0;
performance_counters.sampled_frames++;
}
}
void thread::check_zcull_status(bool framebuffer_swap)
@ -2528,31 +2495,9 @@ namespace rsx
return performance_counters.approximate_load;
}
void thread::request_emu_flip(u32 buffer)
{
const bool is_rsxthr = std::this_thread::get_id() == m_rsx_thread;
// requested through command buffer
if (is_rsxthr)
{
// NOTE: The flip will clear any queued flip requests
handle_emu_flip(buffer);
}
else // requested 'manually' through ppu syscall
{
if (async_flip_requested & flip_request::emu_requested)
{
// ignore multiple requests until previous happens
return;
}
async_flip_buffer = buffer;
async_flip_requested |= flip_request::emu_requested;
}
}
void thread::handle_emu_flip(u32 buffer)
void thread::on_frame_end(u32 buffer, bool forced)
{
// Marks the end of a frame scope GPU-side
if (user_asked_for_frame_capture && !capture_current_frame)
{
capture_current_frame = true;
@ -2588,6 +2533,81 @@ namespace rsx
Emu.Pause();
}
// Reset zcull ctrl
zcull_ctrl->set_active(this, false);
zcull_ctrl->clear(this);
if (zcull_ctrl->has_pending())
{
LOG_TRACE(RSX, "Dangling reports found, discarding...");
zcull_ctrl->sync(this);
}
if (LIKELY(!forced))
{
if (!g_cfg.video.disable_FIFO_reordering)
{
// Try to enable FIFO optimizations
// Only rarely useful for some games like RE4
m_flattener.evaluate_performance(m_draw_calls);
}
if (g_cfg.video.frame_skip_enabled)
{
m_skip_frame_ctr++;
if (m_skip_frame_ctr == g_cfg.video.consequtive_frames_to_draw)
m_skip_frame_ctr = -g_cfg.video.consequtive_frames_to_skip;
}
}
else
{
if (!g_cfg.video.disable_FIFO_reordering)
{
// Flattener is unusable due to forced random flips
m_flattener.force_disable();
}
if (g_cfg.video.frame_skip_enabled)
{
LOG_ERROR(RSX, "Frame skip is not compatible with this application");
}
}
queued_flip_index = int(buffer);
}
void thread::request_emu_flip(u32 buffer)
{
const bool is_rsxthr = std::this_thread::get_id() == m_rsx_thread;
// requested through command buffer
if (is_rsxthr)
{
// NOTE: The flip will clear any queued flip requests
handle_emu_flip(buffer);
}
else // requested 'manually' through ppu syscall
{
if (async_flip_requested & flip_request::emu_requested)
{
// ignore multiple requests until previous happens
return;
}
async_flip_buffer = buffer;
async_flip_requested |= flip_request::emu_requested;
}
}
void thread::handle_emu_flip(u32 buffer)
{
if (queued_flip_index < 0)
{
// Frame was not queued before flipping
on_frame_end(buffer, true);
}
double limit = 0.;
switch (g_cfg.video.frame_limit)
{
@ -2634,6 +2654,7 @@ namespace rsx
last_flip_time = get_system_time() - 1000000;
flip_status = CELL_GCM_DISPLAY_FLIP_STATUS_DONE;
queued_flip_index = -1;
if (flip_handler)
{

View File

@ -558,6 +558,7 @@ namespace rsx
u64 start_rsx_time = 0;
u64 int_flip_index = 0;
u64 last_flip_time;
int queued_flip_index = -1;
vm::ptr<void(u32)> flip_handler = vm::null;
vm::ptr<void(u32)> user_handler = vm::null;
vm::ptr<void(u32)> vblank_handler = vm::null;
@ -607,6 +608,7 @@ namespace rsx
virtual void on_init_rsx() = 0;
virtual void on_init_thread() = 0;
virtual bool do_method(u32 /*cmd*/, u32 /*value*/) { return false; }
virtual void on_frame_end(u32 buffer, bool forced = false);
virtual void flip(int buffer, bool emu_flip = false) = 0;
virtual u64 timestamp();
virtual bool on_access_violation(u32 /*address*/, bool /*is_writing*/) { return false; }