diff --git a/rpcs3/Emu/Cell/lv2/sys_rsx.cpp b/rpcs3/Emu/Cell/lv2/sys_rsx.cpp index aa4507811b..cb8ff1397b 100644 --- a/rpcs3/Emu/Cell/lv2/sys_rsx.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_rsx.cpp @@ -273,20 +273,50 @@ s32 sys_rsx_context_attribute(s32 context_id, u32 package_id, u64 a3, u64 a4, u6 break; case 0x102: // Display flip - driverInfo.head[a3].flipFlags |= 0x80000000; - driverInfo.head[a3].lastFlipTime = rsxTimeStamp(); // should rsxthread set this? - // lets give this a shot for giving bufferid back to gcm - driverInfo.head[a3].flipBufferId = driverInfo.head[a3].queuedBufferId; - // seems gcmSysWaitLabel uses this offset, so lets set it to 0 every flip - vm::_ref(render->label_addr + 0x10) = 0; - if (a3 == 0) - sys_event_port_send(m_sysrsx->rsx_event_port, 0, (1 << 3), 0); - if (a3 == 1) - sys_event_port_send(m_sysrsx->rsx_event_port, 0, (1 << 4), 0); - break; + { + u32 flip_idx = -1u; + + // high bit signifys grabbing a queued buffer + // otherwise it contains a display buffer offset + if ((a4 & 0x80000000) != 0) + { + // last half byte gives buffer, 0xf seems to trigger just last queued + u8 idx_check = a4 & 0xf; + if (idx_check > 7) + flip_idx = driverInfo.head[a3].lastQueuedBufferId; + else + flip_idx = idx_check; + + // 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); + if ((driverInfo.head[a3].flipFlags & sanity_check) != sanity_check) + LOG_ERROR(RSX, "Display Flip Queued: Flipping non previously queued buffer 0x%x", a4); + } + else + { + for (u32 i = 0; i < render->display_buffers_count; ++i) + { + if (render->display_buffers[i].offset == a4) + { + flip_idx = i; + break; + } + } + if (flip_idx == -1u) + { + LOG_ERROR(RSX, "Display Flip: Couldn't find display buffer offset, flipping 0. Offset: 0x%x", a4); + flip_idx = 0; + } + } + + render->request_emu_flip(flip_idx); + } + break; case 0x103: // Display Queue - driverInfo.head[a3].queuedBufferId = a4; + driverInfo.head[a3].lastQueuedBufferId = a4; driverInfo.head[a3].flipFlags |= 0x40000000 | (1 << a4); if (a3 == 0) sys_event_port_send(m_sysrsx->rsx_event_port, 0, (1 << 5), 0); @@ -475,4 +505,28 @@ s32 sys_rsx_attribute(u32 packageId, u32 a2, u32 a3, u32 a4, u32 a5) sys_rsx.warning("sys_rsx_attribute(packageId=0x%x, a2=0x%x, a3=0x%x, a4=0x%x, a5=0x%x)", packageId, a2, a3, a4, a5); return CELL_OK; -} \ No newline at end of file +} + +void sys_rsx_flip_event(u32 buffer) +{ + auto m_sysrsx = fxm::get(); + if (!m_sysrsx) + return; + + auto &driverInfo = vm::_ref(m_sysrsx->driverInfo); + const auto render = rsx::get_current_renderer(); + + // we only ever use head 1 for now + + driverInfo.head[1].flipFlags |= 0x80000000; + driverInfo.head[1].lastFlipTime = rsxTimeStamp(); // should rsxthread set this? + driverInfo.head[1].flipBufferId = buffer; + + // seems gcmSysWaitLabel uses this offset, so lets set it to 0 every flip + vm::_ref(render->label_addr + 0x10) = 0; + + //if (a3 == 0) + // sys_event_port_send(m_sysrsx->rsx_event_port, 0, (1 << 3), 0); + //if (a3 == 1) + sys_event_port_send(m_sysrsx->rsx_event_port, 0, (1 << 4), 0); +} diff --git a/rpcs3/Emu/Cell/lv2/sys_rsx.h b/rpcs3/Emu/Cell/lv2/sys_rsx.h index 61cb076b81..c40ced4879 100644 --- a/rpcs3/Emu/Cell/lv2/sys_rsx.h +++ b/rpcs3/Emu/Cell/lv2/sys_rsx.h @@ -21,9 +21,9 @@ struct RsxDriverInfo { be_t lastFlipTime; // 0x0 last flip time be_t flipFlags; // 0x8 flags to handle flip/queue - be_t unk1; // 0xC + be_t offset; // 0xC be_t flipBufferId; // 0x10 - be_t queuedBufferId; // 0x14 todo: this is definately not this variable but its 'unused' so im using it for queueId to pass to flip handler + be_t lastQueuedBufferId; // 0x14 todo: this is definately not this variable but its 'unused' so im using it for queueId to pass to flip handler be_t unk3; // 0x18 be_t unk6; // 0x18 possible low bits of time stamp? used in getlastVBlankTime be_t lastSecondVTime; // 0x20 last time for second vhandler freq @@ -101,7 +101,7 @@ struct RsxDisplayInfo struct SysRsxConfig { - be_t rsx_event_port{ 0 }; + u32 rsx_event_port{ 0 }; u32 driverInfo{ 0 }; u32 rsx_context_addr{ 0 }; }; @@ -118,4 +118,7 @@ s32 sys_rsx_context_iounmap(u32 context_id, u32 io, u32 size); s32 sys_rsx_context_attribute(s32 context_id, u32 package_id, u64 a3, u64 a4, u64 a5, u64 a6); s32 sys_rsx_device_map(vm::ptr dev_addr, vm::ptr a2, u32 dev_id); s32 sys_rsx_device_unmap(u32 dev_id); -s32 sys_rsx_attribute(u32 a1, u32 a2, u32 a3, u32 a4, u32 a5); \ No newline at end of file +s32 sys_rsx_attribute(u32 a1, u32 a2, u32 a3, u32 a4, u32 a5); + +// Custom 'RSX Events', not lv2 syscalls +void sys_rsx_flip_event(u32 buffer); diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.cpp b/rpcs3/Emu/RSX/GL/GLGSRender.cpp index e4b08c88a3..446fa4a3be 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.cpp +++ b/rpcs3/Emu/RSX/GL/GLGSRender.cpp @@ -1722,9 +1722,8 @@ void GLGSRender::do_local_task(rsx::FIFO_state state) if (m_overlay_manager) { - if (!in_begin_end && native_ui_flip_request.load()) + if (!in_begin_end && async_flip_requested.test_and_reset(flip_request::native_ui)) { - native_ui_flip_request.store(false); flip((s32)current_display_buffer); } } diff --git a/rpcs3/Emu/RSX/Overlays/overlays.cpp b/rpcs3/Emu/RSX/Overlays/overlays.cpp index 9c9e86012e..55325fb2d9 100644 --- a/rpcs3/Emu/RSX/Overlays/overlays.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlays.cpp @@ -35,7 +35,7 @@ namespace rsx const auto now = get_system_time() - 1000000; if ((now - rsxthr->last_flip_time) > min_refresh_duration_us) { - rsxthr->native_ui_flip_request.store(true); + rsxthr->async_flip_requested.test_and_set(rsx::thread::flip_request::native_ui); } } } diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index b65e3fc924..b7a4e2c0eb 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -18,6 +18,9 @@ #include "Utilities/GSL.h" #include "Utilities/StrUtil.h" +#include + +#include #include #include #include @@ -364,6 +367,11 @@ namespace rsx } } + void thread::on_spawn() + { + m_rsx_thread = std::this_thread::get_id(); + } + void thread::on_task() { if (supports_native_ui) @@ -1348,6 +1356,12 @@ namespace rsx void thread::do_local_task(FIFO_state state) { + if (test(async_flip_requested, flip_request::emu_requested)) + { + handle_emu_flip(async_flip_buffer); + async_flip_requested.test_and_reset(flip_request::emu_requested); + } + if (!in_begin_end && state != FIFO_state::lock_wait) { reader_lock lock(m_mtx_task); @@ -2790,6 +2804,139 @@ 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) + { + // async flip hasnt been handled before next requested...? + if (test(async_flip_requested, flip_request::emu_requested)) + { + handle_emu_flip(async_flip_buffer); + async_flip_requested.test_and_reset(flip_request::emu_requested); + } + handle_emu_flip(buffer); + } + else // requested 'manually' through ppu syscall + { + // ignore multiple requests until previous happens + if (test(async_flip_requested, flip_request::emu_requested)) + return; + + async_flip_buffer = buffer; + async_flip_requested.test_and_set(flip_request::emu_requested); + } + } + + void thread::handle_emu_flip(u32 buffer) + { + if (user_asked_for_frame_capture && !g_cfg.video.strict_rendering_mode) + { + // not dealing with non-strict rendering capture for now + user_asked_for_frame_capture = false; + LOG_FATAL(RSX, "RSX Capture: Capture only supported when ran with strict rendering mode."); + } + else if (user_asked_for_frame_capture && !capture_current_frame) + { + capture_current_frame = true; + user_asked_for_frame_capture = false; + frame_debug.reset(); + frame_capture.reset(); + + // random number just to jumpstart the size + frame_capture.replay_commands.reserve(8000); + + // capture first tile state with nop cmd + rsx::frame_capture_data::replay_command replay_cmd; + replay_cmd.rsx_command = std::make_pair(NV4097_NO_OPERATION, 0); + frame_capture.replay_commands.push_back(replay_cmd); + capture::capture_display_tile_state(this, frame_capture.replay_commands.back()); + } + else if (capture_current_frame) + { + capture_current_frame = false; + std::stringstream os; + cereal::BinaryOutputArchive archive(os); + const std::string& filePath = fs::get_config_dir() + "capture.rrc"; + archive(frame_capture); + { + // todo: 'dynamicly' create capture filename, also may want to compress this data? + fs::file f(filePath, fs::rewrite); + f.write(os.str()); + } + + LOG_SUCCESS(RSX, "capture successful: %s", filePath.c_str()); + + frame_capture.reset(); + Emu.Pause(); + } + + double limit = 0.; + switch (g_cfg.video.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 = fps_limit; break; // TODO + } + + if (limit) + { + const u64 time = get_system_time() - Emu.GetPauseTime() - start_rsx_time; + + if (int_flip_index == 0) + { + start_rsx_time = time; + } + else + { + // Convert limit to expected time value + double expected = int_flip_index * 1000000. / limit; + + while (time >= expected + 1000000. / limit) + { + expected = int_flip_index++ * 1000000. / limit; + } + + if (expected > time + 1000) + { + const auto delay_us = static_cast(expected - time); + std::this_thread::sleep_for(std::chrono::milliseconds{ delay_us / 1000 }); + performance_counters.idle_time += delay_us; + } + } + } + + int_flip_index++; + current_display_buffer = buffer; + flip(buffer); + // After each flip PS3 system is executing a routine that changes registers value to some default. + // Some game use this default state (SH3). + if (isHLE) + reset(); + + last_flip_time = get_system_time() - 1000000; + flip_status = CELL_GCM_DISPLAY_FLIP_STATUS_DONE; + + if (flip_handler) + { + intr_thread->cmd_list + ({ + { ppu_cmd::set_args, 1 }, u64{ 1 }, + { ppu_cmd::lle_call, flip_handler }, + { ppu_cmd::sleep, 0 } + }); + + intr_thread->notify(); + } + sys_rsx_flip_event(buffer); + } + + namespace reports { void ZCULL_control::set_enabled(class ::rsx::thread* ptimer, bool state) diff --git a/rpcs3/Emu/RSX/RSXThread.h b/rpcs3/Emu/RSX/RSXThread.h index 2089eb1bb8..1569f373d4 100644 --- a/rpcs3/Emu/RSX/RSXThread.h +++ b/rpcs3/Emu/RSX/RSXThread.h @@ -368,8 +368,16 @@ namespace rsx } performance_counters; - // Native UI interrupts - atomic_t native_ui_flip_request{ false }; + enum class flip_request : u32 + { + emu_requested, + native_ui, + + __bitset_enum_max + }; + + atomic_t> async_flip_requested{}; + u8 async_flip_buffer{ 0 }; GcmTileInfo tiles[limits::tiles_count]; GcmZcullInfo zculls[limits::zculls_count]; @@ -468,8 +476,7 @@ namespace rsx thread(); virtual ~thread(); - void handle_invalidated_memory_range(); - + virtual void on_spawn() override; virtual void on_task() override; virtual void on_exit() override; @@ -565,6 +572,8 @@ namespace rsx std::deque m_internal_tasks; void do_internal_task(); + void handle_emu_flip(u32 buffer); + void handle_invalidated_memory_range(); public: //std::future add_internal_task(std::function callback); @@ -646,6 +655,9 @@ namespace rsx tiled_region get_tiled_address(u32 offset, u32 location); GcmTileInfo *find_tile(u32 offset, u32 location); + // Emu App/Game flip, only immediately flips when called from rsxthread + void request_emu_flip(u32 buffer); + u32 ReadIO32(u32 addr); void WriteIO32(u32 addr, u32 value); diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index 9f63c8c910..8791ebddc7 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -827,7 +827,7 @@ bool VKGSRender::on_access_violation(u32 address, bool is_writing) if (result.num_flushable > 0) { - const bool is_rsxthr = std::this_thread::get_id() == rsx_thread; + const bool is_rsxthr = std::this_thread::get_id() == m_rsx_thread; bool has_queue_ref = false; u64 sync_timestamp = 0ull; @@ -1594,7 +1594,6 @@ void VKGSRender::on_init_thread() } GSRender::on_init_thread(); - rsx_thread = std::this_thread::get_id(); zcull_ctrl.reset(static_cast<::rsx::reports::ZCULL_control*>(this)); if (!supports_native_ui) @@ -2193,9 +2192,8 @@ void VKGSRender::do_local_task(rsx::FIFO_state state) if (m_overlay_manager) { - if (!in_begin_end && native_ui_flip_request.load()) + if (!in_begin_end && async_flip_requested.test_and_reset(flip_request::native_ui)) { - native_ui_flip_request.store(false); flush_command_queue(true); flip((s32)current_display_buffer); } diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.h b/rpcs3/Emu/RSX/VK/VKGSRender.h index 0ecd134361..9b5ebdfe2b 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.h +++ b/rpcs3/Emu/RSX/VK/VKGSRender.h @@ -364,7 +364,6 @@ private: shared_mutex m_flush_queue_mutex; flush_request_task m_flush_requests; - std::thread::id rsx_thread; std::atomic m_last_sync_event = { 0 }; bool render_pass_open = false; diff --git a/rpcs3/Emu/RSX/rsx_methods.cpp b/rpcs3/Emu/RSX/rsx_methods.cpp index cc8d973e05..f8c886b1ba 100644 --- a/rpcs3/Emu/RSX/rsx_methods.cpp +++ b/rpcs3/Emu/RSX/rsx_methods.cpp @@ -1,4 +1,4 @@ -#include "stdafx.h" +#include "stdafx.h" #include "rsx_methods.h" #include "RSXThread.h" #include "Emu/Memory/vm.h" @@ -9,9 +9,6 @@ #include "Emu/Cell/lv2/sys_rsx.h" #include "Capture/rsx_capture.h" -#include -#include - #include template <> @@ -1119,100 +1116,7 @@ namespace rsx void flip_command(thread* rsx, u32, u32 arg) { - if (user_asked_for_frame_capture && !rsx->capture_current_frame) - { - rsx->capture_current_frame = true; - user_asked_for_frame_capture = false; - frame_debug.reset(); - frame_capture.reset(); - - // random number just to jumpstart the size - frame_capture.replay_commands.reserve(8000); - - // capture first tile state with nop cmd - rsx::frame_capture_data::replay_command replay_cmd; - replay_cmd.rsx_command = std::make_pair(NV4097_NO_OPERATION, 0); - frame_capture.replay_commands.push_back(replay_cmd); - capture::capture_display_tile_state(rsx, frame_capture.replay_commands.back()); - } - else if (rsx->capture_current_frame) - { - rsx->capture_current_frame = false; - std::stringstream os; - cereal::BinaryOutputArchive archive(os); - const std::string& filePath = fs::get_config_dir() + "captures/" + Emu.GetTitleID() + "_" + date_time::current_time_narrow() +"_capture.rrc"; - archive(frame_capture); - { - // todo: 'dynamicly' create capture filename, also may want to compress this data? - fs::file f(filePath, fs::rewrite); - f.write(os.str()); - } - - LOG_SUCCESS(RSX, "capture successful: %s", filePath.c_str()); - - frame_capture.reset(); - Emu.Pause(); - } - - double limit = 0.; - switch (g_cfg.video.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 = rsx->fps_limit; break; // TODO - } - - if (limit) - { - const u64 time = get_system_time() - Emu.GetPauseTime() - rsx->start_rsx_time; - - if (rsx->int_flip_index == 0) - { - rsx->start_rsx_time = time; - } - else - { - // Convert limit to expected time value - double expected = rsx->int_flip_index * 1000000. / limit; - - while (time >= expected + 1000000. / limit) - { - expected = rsx->int_flip_index++ * 1000000. / limit; - } - - if (expected > time + 1000) - { - const auto delay_us = static_cast(expected - time); - std::this_thread::sleep_for(std::chrono::milliseconds{delay_us / 1000}); - rsx->performance_counters.idle_time += delay_us; - } - } - } - - rsx->int_flip_index++; - rsx->current_display_buffer = arg; - rsx->flip(arg); - // After each flip cellGcmSys is executing a routine that changes registers value to some default. - // Some game use this default state (SH3). - if (rsx->isHLE) rsx->reset(); - - rsx->last_flip_time = get_system_time() - 1000000; - rsx->flip_status = CELL_GCM_DISPLAY_FLIP_STATUS_DONE; - - if (rsx->flip_handler) - { - rsx->intr_thread->cmd_list - ({ - { ppu_cmd::set_args, 1 }, u64{1}, - { ppu_cmd::lle_call, rsx->flip_handler }, - { ppu_cmd::sleep, 0 } - }); - - rsx->intr_thread->notify(); - } + rsx->request_emu_flip(arg); } void user_command(thread* rsx, u32, u32 arg) @@ -1233,8 +1137,6 @@ namespace rsx namespace gcm { - // not entirely sure which one should actually do the flip, or if these should be handled separately, - // so for now lets flip in queue and just let the driver deal with it template struct driver_flip { @@ -1249,7 +1151,6 @@ namespace rsx { static void impl(thread* rsx, u32 _reg, u32 arg) { - flip_command(rsx, _reg, arg); sys_rsx_context_attribute(0x55555555, 0x103, index, arg, 0, 0); } };