-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
rsx: PS3 Native frame limiter improvements, add Infinite frame limiter #12052
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -595,6 +595,31 @@ namespace rsx | |
} | ||
} | ||
|
||
void thread::post_vblank_event(u64 post_event_time) | ||
{ | ||
vblank_count++; | ||
|
||
if (isHLE) | ||
{ | ||
if (auto ptr = vblank_handler) | ||
{ | ||
intr_thread->cmd_list | ||
({ | ||
{ ppu_cmd::set_args, 1 }, u64{1}, | ||
{ ppu_cmd::lle_call, ptr }, | ||
{ ppu_cmd::sleep, 0 } | ||
}); | ||
|
||
intr_thread->cmd_notify++; | ||
intr_thread->cmd_notify.notify_one(); | ||
} | ||
} | ||
else | ||
{ | ||
sys_rsx_context_attribute(0x55555555, 0xFED, 1, get_guest_system_time(post_event_time), 0, 0); | ||
} | ||
} | ||
|
||
void thread::on_task() | ||
{ | ||
g_tls_log_prefix = [] | ||
|
@@ -686,7 +711,6 @@ namespace rsx | |
{ | ||
{ | ||
local_vblank_count++; | ||
vblank_count++; | ||
|
||
if (local_vblank_count == vblank_rate) | ||
{ | ||
|
@@ -701,25 +725,7 @@ namespace rsx | |
vblank_period = 1'000'000 + u64{g_cfg.video.vblank_ntsc.get()} * 1000; | ||
} | ||
|
||
if (isHLE) | ||
{ | ||
if (auto ptr = vblank_handler) | ||
{ | ||
intr_thread->cmd_list | ||
({ | ||
{ ppu_cmd::set_args, 1 }, u64{1}, | ||
{ ppu_cmd::lle_call, ptr }, | ||
{ ppu_cmd::sleep, 0 } | ||
}); | ||
|
||
intr_thread->cmd_notify++; | ||
intr_thread->cmd_notify.notify_one(); | ||
} | ||
} | ||
else | ||
{ | ||
sys_rsx_context_attribute(0x55555555, 0xFED, 1, get_guest_system_time(post_event_time), 0, 0); | ||
} | ||
post_vblank_event(post_event_time); | ||
} | ||
} | ||
else if (wait_sleep) | ||
|
@@ -3203,15 +3209,17 @@ namespace rsx | |
} | ||
|
||
double limit = 0.; | ||
switch (g_disable_frame_limit ? frame_limit_type::none : g_cfg.video.frame_limit) | ||
const auto frame_limit = g_disable_frame_limit ? frame_limit_type::none : g_cfg.video.frame_limit; | ||
|
||
switch (frame_limit) | ||
{ | ||
case frame_limit_type::none: limit = 0.; break; | ||
case frame_limit_type::_59_94: limit = 59.94; break; | ||
case frame_limit_type::_50: limit = 50.; break; | ||
case frame_limit_type::_60: limit = 60.; break; | ||
case frame_limit_type::_30: limit = 30.; break; | ||
case frame_limit_type::_auto: limit = static_cast<double>(g_cfg.video.vblank_rate); break; | ||
case frame_limit_type::_ps3: limit = 0.; break; | ||
case frame_limit_type::infinite: limit = 0.; break; | ||
default: | ||
break; | ||
} | ||
|
@@ -3246,22 +3254,32 @@ namespace rsx | |
performance_counters.idle_time += delay_us; | ||
} | ||
} | ||
|
||
flip_notification_count = 1; | ||
} | ||
else if (wait_for_flip_sema) | ||
else if (frame_limit == frame_limit_type::_ps3) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I'm reading this correctly the logic here is essentially "do not flip until a fresh vblank signal is received". You have the vblank signal as the flip release and this is an acquire operation here. Two problems:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm quite confident DEVICE 0x30 is something to do with the queue command and not vblank, as it's inserted by gcm for all display sync options and occurs before semaphore wait by cellGcmSetFlipWithWaitLabel. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From the trace above, we can see there are 2 signals:
Client:
My guess here is that 0x0 is signaled by the hardware vblank system, 0x30 just acknowledges that a head has been enqueued. So, your PR addresses this by having the system use vblank instead of enqueue signal. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For simplicity I have omitted the 0x10 logic, I have no idea where that signal is for. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No I have HLEd the game this is a cellGcmSetFlipWithWaitLabel call with 0 address. It doesn't appear in other games which uses other flip functions. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Which is named _cellGcmSetFlipCommandWithWaitLabel on firmware. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So for "normal" flip the 0x0 var is absent? That simplifies the design then and makes the 0x30_ack a wait for vblank. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 0x30 has nothing to do with vblank, it appears in all sync modes even with VSYNC turned off and besides the whole purpose of _cellGcmSetFlipCommandWithWaitLabel is to time the flip very closely to what CELL decides. So if 0x30 is a vblank semaphore it defies all logic as to why the function exists in the first place. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems my point is getting lost in translation, that doesn't matter it's just an ack, whether its from vblank or not. I'm not happy with the implementation, but if we're being honest, you're unlikely to restructure it, so I'll merge and change it later when I have time. |
||
{ | ||
const auto& value = vm::_ref<RsxSemaphore>(device_addr + 0x30).val; | ||
if (value != flip_sema_wait_val) | ||
bool exit = false; | ||
|
||
if (vblank_at_flip == umax) | ||
{ | ||
vblank_at_flip = +vblank_count; | ||
flip_notification_count = 1; | ||
exit = true; | ||
} | ||
|
||
if (requested_vsync && (exit || vblank_at_flip == vblank_count)) | ||
{ | ||
// Not yet signaled, handle it later | ||
async_flip_requested |= flip_request::emu_requested; | ||
async_flip_buffer = buffer; | ||
return; | ||
} | ||
|
||
wait_for_flip_sema = false; | ||
vblank_at_flip = umax; | ||
} | ||
|
||
int_flip_index++; | ||
int_flip_index += flip_notification_count; | ||
|
||
current_display_buffer = buffer; | ||
m_queued_flip.emu_flip = true; | ||
|
@@ -3274,23 +3292,26 @@ namespace rsx | |
flip_status = CELL_GCM_DISPLAY_FLIP_STATUS_DONE; | ||
m_queued_flip.in_progress = false; | ||
|
||
if (!isHLE) | ||
while (flip_notification_count--) | ||
{ | ||
sys_rsx_context_attribute(0x55555555, 0xFEC, buffer, 0, 0, 0); | ||
return; | ||
} | ||
if (!isHLE) | ||
{ | ||
sys_rsx_context_attribute(0x55555555, 0xFEC, buffer, 0, 0, 0); | ||
continue; | ||
} | ||
|
||
if (auto ptr = flip_handler) | ||
{ | ||
intr_thread->cmd_list | ||
({ | ||
{ ppu_cmd::set_args, 1 }, u64{ 1 }, | ||
{ ppu_cmd::lle_call, ptr }, | ||
{ ppu_cmd::sleep, 0 } | ||
}); | ||
if (auto ptr = flip_handler) | ||
{ | ||
intr_thread->cmd_list | ||
({ | ||
{ ppu_cmd::set_args, 1 }, u64{ 1 }, | ||
{ ppu_cmd::lle_call, ptr }, | ||
{ ppu_cmd::sleep, 0 } | ||
}); | ||
|
||
intr_thread->cmd_notify++; | ||
intr_thread->cmd_notify.notify_one(); | ||
intr_thread->cmd_notify++; | ||
intr_thread->cmd_notify.notify_one(); | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where is the counterpart to disable this? We know PS3 had engines that had tearing when fps dropped below a threshold (e.g cryengine 2)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see sometimes in logs that the games reset it afterward and after cellVideoOutConfigure, I will add to it as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, it is unlikely that they are calling videoOutConfigure during gameplay as that is a heavy operation. It is more likely to be toggled using this method. Is it known what happens if a5 is 0?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm assuming that games also react to the unused CellVideoOutCallback. (maybe it's called when the display is changed)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's controlled by CellGcmSetFlipMode. You can choose 3 modes there, 1 for vsync, 1 for scanline sync and the last mode for all out anarchy. We only need to set vsync to engage when the first mode is chosen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've checked and no game uses cellVideoOutRegisterCallback.